Add tmpl.js
This commit is contained in:
parent
99fc54eea9
commit
747db9602d
@ -1349,6 +1349,13 @@ pub async fn health() -> HttpResponse {
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
|
||||
#[get("/tmpl.js")]
|
||||
async fn tmpl() -> HttpResponse {
|
||||
HttpResponse::Ok()
|
||||
.append_header(("Content-Type", "application/javascript"))
|
||||
.body(include_str!("../templates/tmpl.js").to_string())
|
||||
}
|
||||
|
||||
pub fn configure(config: &mut actix_web::web::ServiceConfig) {
|
||||
tmp_cleanup();
|
||||
|
||||
@ -1374,5 +1381,6 @@ pub fn configure(config: &mut actix_web::web::ServiceConfig) {
|
||||
.service(no_ads)
|
||||
.service(no_sellers)
|
||||
.service(health)
|
||||
.service(tmpl)
|
||||
.service(Files::new("/assets", "./assets"));
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/styles.css">
|
||||
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
|
||||
<script type="module" src="/tmpl.js"></script>
|
||||
<title>Cooked</title>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
@ -42,6 +42,10 @@
|
||||
<textarea id="ingredients" name="ingredients" class="textarea-without-view" required>{{ingredients}}</textarea>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
{% include "recipies/ingredients-list.jinja" %}
|
||||
|
||||
|
||||
<div class="w-full flex gap-4 flex-wrap">
|
||||
{% for ingredient in known_ingredients %}
|
||||
<label class="border rounded-lg p-2 styled-checkbox cursor-pointer">
|
||||
@ -49,6 +53,9 @@
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
{% include "recipies/ingredients-list.jinja" %}
|
||||
</div>
|
||||
|
||||
<details class="w-full collapse collapse-arrow">
|
||||
<summary class="text-base font-semibold collapse-title">Ingeredients should be listed as list of AMOUNT UNIT INGEREDIENT. Example:</summary>
|
||||
|
35
cooked/templates/recipies/ingredients-list.jinja
Normal file
35
cooked/templates/recipies/ingredients-list.jinja
Normal file
@ -0,0 +1,35 @@
|
||||
<div id="ingredients-list" class="tmpl-wrapper flex flex-col gap-4">
|
||||
<label for="ingredient" class="flex gap-4">
|
||||
<div class="flex gap-2 w-full">
|
||||
<input name="ingredient[][qty]" placeholder="Quantity" type="number" class="input border border-solid border-gray-200 rounded-lg" required/>
|
||||
<input name="ingredient[][unit]" placeholder="Unit" class="input border border-solid border-gray-200 rounded-lg"/>
|
||||
<input name="ingredient[][name]" placeholder="Name" class="input border border-solid border-gray-200 rounded-lg" required/>
|
||||
<input name="ingredient[][sidenote]" placeholder="Sidenote" class="input border border-solid border-gray-200 rounded-lg"/>
|
||||
|
||||
<button class="input border border-solid border-gray-200 rounded-lg tmpl-adder tmpl-swap-before" data-tmpl-swap="#remove">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<template class="tmpl-row">
|
||||
<label for="ingredient" class="flex gap-4 tmpl-row">
|
||||
<div class="flex gap-2 w-full">
|
||||
<input name="ingredient[][qty]" placeholder="Quantity" type="number" class="input border border-solid border-gray-200 rounded-lg" required/>
|
||||
<input name="ingredient[][unit]" placeholder="Unit" class="input border border-solid border-gray-200 rounded-lg"/>
|
||||
<input name="ingredient[][name]" placeholder="Name" class="input border border-solid border-gray-200 rounded-lg" required/>
|
||||
<input name="ingredient[][sidenote]" placeholder="Sidenote" class="input border border-solid border-gray-200 rounded-lg"/>
|
||||
|
||||
<button class="input border border-solid border-gray-200 rounded-lg tmpl-adder tmpl-swap-before" data-tmpl-swap="#remove">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<template class="tmpl-swap" id="remove">
|
||||
<button class="input border border-solid border-gray-200 rounded-lg tmpl-wrapper-rm" data-tmpl-wrapper-rm="label:has(div > button.tmpl-wrapper-rm)">
|
||||
-
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
75
cooked/templates/tmpl.js
Normal file
75
cooked/templates/tmpl.js
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* <template> framework
|
||||
*
|
||||
* Usage:
|
||||
* ```html
|
||||
* <div class="tmpl-wrapper">
|
||||
* <input class="tmpl-adder" />
|
||||
* <template>
|
||||
* <div>Some content to add</div>
|
||||
* </template>
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
|
||||
const setup_wrapper = (wrapper) => {
|
||||
const template = wrapper.querySelector("template.tmpl-row");
|
||||
const adder = wrapper.querySelector(".tmpl-adder");
|
||||
const receiver = wrapper.querySelector(".tmpl-receiver") || wrapper;
|
||||
|
||||
wrapper.addEventListener("click", (ev) => {
|
||||
if (!ev.target.classList.contains('tmpl-adder')) return;
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const content = template.content.cloneNode(true);
|
||||
template.dispatchEvent(new CustomEvent('tmpl:cloned', { detail: { content } }));
|
||||
template.dispatchEvent(new CustomEvent('tmpl:before-append', { detail: { content, receiver } }));
|
||||
|
||||
Array.from(wrapper.querySelectorAll('.tmpl-rm-before')).forEach(el => el.remove());
|
||||
|
||||
receiver.appendChild(content);
|
||||
template.dispatchEvent(new CustomEvent('tmpl:after-append', { detail: { content, receiver } }));
|
||||
|
||||
Array.from(wrapper.querySelectorAll('.tmpl-rm-after')).forEach(el => el.remove());
|
||||
});
|
||||
|
||||
wrapper.addEventListener('click', ev => {
|
||||
if (!ev.target.classList.contains('tmpl-wrapper-rm')) return;
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const selector = ev.target.dataset.tmplWrapperRm;
|
||||
if (!selector) return;
|
||||
|
||||
const child = wrapper.querySelector(selector);
|
||||
if (!child) return;
|
||||
child.remove();
|
||||
});
|
||||
|
||||
wrapper.addEventListener('click', ev => {
|
||||
if (!ev.target.classList.contains('tmpl-swap-before')) return;
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const current = ev.target;
|
||||
const parent = current.parentElement;
|
||||
if (!parent) return;
|
||||
const selector = ev.target.dataset.tmplSwap;
|
||||
if (!selector) return;
|
||||
const swapTemplate = wrapper.querySelector(`template${selector}`);
|
||||
if (!swapTemplate) return;
|
||||
const newNode = swapTemplate.content.cloneNode(true);
|
||||
parent.replaceChild(newNode, current);
|
||||
});
|
||||
};
|
||||
|
||||
const mount = () => {
|
||||
Array.from(document.body.querySelectorAll('.tmpl-wrapper') || []).forEach(wrapper => {
|
||||
setup_wrapper(wrapper);
|
||||
});
|
||||
};
|
||||
document.addEventListener("DOMContentLoaded", mount);
|
Loading…
Reference in New Issue
Block a user