mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-04-03 20:35:53 +02:00
🔤 ⚡
This commit is contained in:
parent
8756dabc55
commit
9251e70015
@ -26,28 +26,23 @@ function Todos(){
|
||||
});
|
||||
});
|
||||
|
||||
// Setup hash change listener
|
||||
window.addEventListener("hashchange", () => {
|
||||
const hash = location.hash.replace("#", "") || "all";
|
||||
S.action(pageS, "set", /** @type {"all"|"active"|"completed"} */(hash));
|
||||
});
|
||||
|
||||
/** @type {ddeElementAddon<HTMLInputElement>} */
|
||||
const onToggleAll = on("change", event => {
|
||||
const checked = /** @type {HTMLInputElement} */ (event.target).checked;
|
||||
S.action(todosS, "completeAll", checked);
|
||||
});
|
||||
const formNewTodo = "newTodo";
|
||||
/** @type {ddeElementAddon<HTMLFormElement>} */
|
||||
const onSubmitNewTodo = on("submit", event => {
|
||||
event.preventDefault();
|
||||
const input = /** @type {HTMLInputElement} */(
|
||||
/** @type {HTMLFormElement} */(event.target).elements.namedItem("newTodo")
|
||||
/** @type {HTMLFormElement} */(event.target).elements.namedItem(formNewTodo)
|
||||
);
|
||||
const title = input.value.trim();
|
||||
if (title) {
|
||||
S.action(todosS, "add", title);
|
||||
input.value = "";
|
||||
}
|
||||
if (!title) return;
|
||||
|
||||
S.action(todosS, "add", title);
|
||||
input.value = "";
|
||||
});
|
||||
const onClearCompleted = on("click", () => S.action(todosS, "clearCompleted"));
|
||||
const onDelete = on("todo:delete", ev =>
|
||||
@ -61,15 +56,16 @@ function Todos(){
|
||||
el("form", null, onSubmitNewTodo).append(
|
||||
el("input", {
|
||||
className: "new-todo",
|
||||
name: "newTodo",
|
||||
name: formNewTodo,
|
||||
placeholder: "What needs to be done?",
|
||||
autocomplete: "off",
|
||||
autofocus: true
|
||||
})
|
||||
)
|
||||
),
|
||||
S.el(todosS, todos => todos.length
|
||||
? el("main", { className: "main" }).append(
|
||||
S.el(todosS, todos => !todos.length
|
||||
? el()
|
||||
: el("main", { className: "main" }).append(
|
||||
el("input", {
|
||||
id: "toggle-all",
|
||||
className: "toggle-all",
|
||||
@ -82,50 +78,40 @@ function Todos(){
|
||||
)
|
||||
)
|
||||
)
|
||||
: el()
|
||||
),
|
||||
S.el(todosS, todos => memo(todos.length, length=> length
|
||||
? el("footer", { className: "footer" }).append(
|
||||
S.el(todosS, todos => !todos.length
|
||||
? el()
|
||||
: el("footer", { className: "footer" }).append(
|
||||
el("span", { className: "todo-count" }).append(
|
||||
S.el(S(() => todosS.get().filter(todo => !todo.completed).length),
|
||||
length=> el("strong").append(
|
||||
length + " ",
|
||||
length === 1 ? "item left" : "items left"
|
||||
noOfLeft(todos)
|
||||
),
|
||||
memo("filters", ()=>
|
||||
el("ul", { className: "filters" }).append(
|
||||
...[ "All", "Active", "Completed" ].map(textContent =>
|
||||
el("li").append(
|
||||
el("a", {
|
||||
textContent,
|
||||
classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
|
||||
href: `#${textContent.toLowerCase()}`
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
el("ul", { className: "filters" }).append(
|
||||
el("li").append(
|
||||
el("a", {
|
||||
textContent: "All",
|
||||
className: S(()=> pageS.get() === "all" ? "selected" : ""),
|
||||
href: "#"
|
||||
}),
|
||||
),
|
||||
el("li").append(
|
||||
el("a", {
|
||||
textContent: "Active",
|
||||
className: S(()=> pageS.get() === "active" ? "selected" : ""),
|
||||
href: "#active"
|
||||
}),
|
||||
),
|
||||
el("li").append(
|
||||
el("a", {
|
||||
textContent: "Completed",
|
||||
className: S(()=> pageS.get() === "completed" ? "selected" : ""),
|
||||
href: "#completed"
|
||||
}),
|
||||
)
|
||||
),
|
||||
S.el(S(() => todosS.get().some(todo => todo.completed)),
|
||||
hasTodosCompleted=> hasTodosCompleted
|
||||
? el("button", { textContent: "Clear completed", className: "clear-completed" }, onClearCompleted)
|
||||
: el()
|
||||
)
|
||||
!todos.some(todo => todo.completed)
|
||||
? el()
|
||||
: el("button", { textContent: "Clear completed", className: "clear-completed" }, onClearCompleted)
|
||||
)
|
||||
: el()
|
||||
))
|
||||
)
|
||||
);
|
||||
/** @param {Todo[]} todos */
|
||||
function noOfLeft(todos){
|
||||
const { length }= todos.filter(todo => !todo.completed);
|
||||
return el("strong").append(
|
||||
length + " ",
|
||||
length === 1 ? "item left" : "items left"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,10 +163,11 @@ function TodoItem({ id, title, completed }) {
|
||||
}
|
||||
isEditing.set(false);
|
||||
});
|
||||
const formEdit = "edit";
|
||||
/** @type {ddeElementAddon<HTMLFormElement>} */
|
||||
const onSubmitEdit = on("submit", event => {
|
||||
event.preventDefault();
|
||||
const input = /** @type {HTMLFormElement} */(event.target).elements.namedItem("edit");
|
||||
const input = /** @type {HTMLFormElement} */(event.target).elements.namedItem(formEdit);
|
||||
const value = /** @type {HTMLInputElement} */(input).value.trim();
|
||||
if (value) {
|
||||
dispatchEdit({ id, title: value });
|
||||
@ -209,16 +196,15 @@ function TodoItem({ id, title, completed }) {
|
||||
el("label", { textContent: title }, onStartEdit),
|
||||
el("button", { className: "destroy" }, onDelete)
|
||||
),
|
||||
S.el(isEditing, editing => editing
|
||||
? el("form", null, onSubmitEdit).append(
|
||||
S.el(isEditing, editing => !editing
|
||||
? el()
|
||||
: el("form", null, onSubmitEdit).append(
|
||||
el("input", {
|
||||
className: "edit",
|
||||
name: "edit",
|
||||
name: formEdit,
|
||||
value: title,
|
||||
"data-id": id
|
||||
}, onBlurEdit, onKeyDown, addFocus)
|
||||
)
|
||||
: el()
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -354,7 +340,7 @@ function todosSignal(){
|
||||
*/
|
||||
function routerSignal(signal){
|
||||
const initial = location.hash.replace("#", "") || "all";
|
||||
return signal(initial, {
|
||||
const out = signal(initial, {
|
||||
/**
|
||||
* Set the current route
|
||||
* @param {"all"|"active"|"completed"} hash - The route to set
|
||||
@ -364,6 +350,14 @@ function routerSignal(signal){
|
||||
this.value = hash;
|
||||
}
|
||||
});
|
||||
|
||||
// Setup hash change listener
|
||||
window.addEventListener("hashchange", () => {
|
||||
const hash = location.hash.replace("#", "") || "all";
|
||||
S.action(out, "set", /** @type {"all"|"active"|"completed"} */(hash));
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,6 +177,13 @@ export function page({ pkg, info }){
|
||||
clearCompleted() {
|
||||
this.value = this.value.filter(todo => !todo.completed);
|
||||
},
|
||||
/**
|
||||
* Mark all todos as completed or active
|
||||
* @param {boolean} state - Whether to mark todos as completed or active
|
||||
*/
|
||||
completeAll(state = true) {
|
||||
this.value.forEach(todo => todo.completed = state);
|
||||
},
|
||||
/**
|
||||
* Handle cleanup when signal is cleared
|
||||
*/
|
||||
@ -237,8 +244,32 @@ export function page({ pkg, info }){
|
||||
The derived signal automatically recalculates whenever either the todos list or the current filter changes,
|
||||
ensuring the UI always shows the correct filtered todos.
|
||||
`),
|
||||
|
||||
el("h4", t`2. Toggle All Functionality`),
|
||||
el(code, { content: `
|
||||
/** @type {ddeElementAddon<HTMLInputElement>} */
|
||||
const onToggleAll = on("change", event => {
|
||||
const checked = /** @type {HTMLInputElement} */ (event.target).checked;
|
||||
S.action(todosS, "completeAll", checked);
|
||||
});
|
||||
|
||||
// Using the toggle-all functionality in the UI
|
||||
el("input", {
|
||||
id: "toggle-all",
|
||||
className: "toggle-all",
|
||||
type: "checkbox"
|
||||
}, onToggleAll),
|
||||
el("label", { htmlFor: "toggle-all", title: "Mark all as complete" }),
|
||||
`, page_id }),
|
||||
|
||||
el("p").append(T`
|
||||
The "toggle all" checkbox allows users to mark all todos as completed or active. When the checkbox
|
||||
is toggled, it calls the ${el("code", "completeAll")} action on the todos signal, passing the current
|
||||
checked state. This is a good example of how signals and actions can be used to manage application
|
||||
state in a clean, declarative way.
|
||||
`),
|
||||
|
||||
el("h4", t`2. Local Component State`),
|
||||
el("h4", t`3. Local Component State`),
|
||||
el(code, { content: `
|
||||
function TodoItem({ id, title, completed }) {
|
||||
const { host }= scope;
|
||||
@ -273,7 +304,7 @@ export function page({ pkg, info }){
|
||||
UI feedback while still communicating changes to the parent via events.
|
||||
`),
|
||||
|
||||
el("h4", t`3. Reactive Properties`),
|
||||
el("h4", t`4. Reactive Properties`),
|
||||
el(code, { content: `
|
||||
// Dynamic class attributes
|
||||
el("a", {
|
||||
|
Loading…
x
Reference in New Issue
Block a user