diff --git a/cooked/src/routes.rs b/cooked/src/routes.rs
index fcd76e7..6032704 100644
--- a/cooked/src/routes.rs
+++ b/cooked/src/routes.rs
@@ -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"));
}
diff --git a/cooked/templates/base.jinja b/cooked/templates/base.jinja
index 683a6cb..ff727f1 100644
--- a/cooked/templates/base.jinja
+++ b/cooked/templates/base.jinja
@@ -6,6 +6,7 @@
+
Cooked
{% block head %}{% endblock %}
diff --git a/cooked/templates/recipies/create_form.jinja b/cooked/templates/recipies/create_form.jinja
index de58ed2..a52694c 100644
--- a/cooked/templates/recipies/create_form.jinja
+++ b/cooked/templates/recipies/create_form.jinja
@@ -42,6 +42,10 @@
+
+ {% include "recipies/ingredients-list.jinja" %}
+
+
{% for ingredient in known_ingredients %}
@@ -49,6 +53,9 @@
{% endfor %}
+
+ {% include "recipies/ingredients-list.jinja" %}
+
Ingeredients should be listed as list of AMOUNT UNIT INGEREDIENT. Example:
diff --git a/cooked/templates/recipies/ingredients-list.jinja b/cooked/templates/recipies/ingredients-list.jinja
new file mode 100644
index 0000000..ea07f66
--- /dev/null
+++ b/cooked/templates/recipies/ingredients-list.jinja
@@ -0,0 +1,35 @@
+
diff --git a/cooked/templates/tmpl.js b/cooked/templates/tmpl.js
new file mode 100644
index 0000000..384010a
--- /dev/null
+++ b/cooked/templates/tmpl.js
@@ -0,0 +1,75 @@
+/**
+ * framework
+ *
+ * Usage:
+ * ```html
+ *
+ *
+ *
+ * Some content to add
+ *
+ *
+ * ```
+*/
+
+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);