Skip to content

Commit 15c8bf8

Browse files
Merge pull request #43 from zeixcom/feature/docs-data-flow
docs(data-flow): simplify todo-app
2 parents 8b0594b + 08251cf commit 15c8bf8

20 files changed

+531
-590
lines changed

docs-src/components/todo-app/todo-app.css

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,66 @@ todo-app {
22
display: flex;
33
flex-direction: column;
44
gap: var(--space-l);
5+
container-type: inline-size;
6+
7+
& form {
8+
display: flex;
9+
flex-direction: column;
10+
align-items: flex-start;
11+
gap: var(--space-m);
12+
justify-content: space-between;
13+
}
14+
15+
& ol {
16+
display: flex;
17+
flex-direction: column;
18+
gap: var(--space-m);
19+
list-style: none;
20+
margin: 0;
21+
padding: 0;
22+
23+
& li {
24+
display: flex;
25+
justify-content: space-between;
26+
gap: var(--space-m);
27+
margin: 0;
28+
padding: 0;
29+
}
30+
31+
&[filter="completed"] li:not(:has([checked])) {
32+
display: none;
33+
}
34+
35+
&[filter="active"] li:has([checked]) {
36+
display: none;
37+
}
38+
}
539

640
& footer {
741
display: grid;
842
grid-template-columns: 1fr 1fr 1fr;
943
align-items: center;
1044
gap: var(--space-m);
1145
margin: 0;
46+
47+
.todo-count {
48+
justify-self: start;
49+
50+
& p {
51+
font-size: var(--font-size-s);
52+
margin: 0;
53+
}
54+
}
1255

1356
.clear-completed {
1457
justify-self: end;
1558
}
1659
}
1760
}
61+
62+
@container (width > 32rem) {
63+
todo-app form {
64+
flex-direction: row;
65+
align-items: flex-end;
66+
}
67+
}

docs-src/components/todo-app/todo-app.html

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
1-
<<todo-app>
2-
<todo-form>
3-
<form action="#">
4-
<input-field>
5-
<label for="add-todo">What needs to be done?</label>
6-
<div class="row">
7-
<div class="group auto">
8-
<input id="add-todo" type="text" value="" required>
9-
</div>
1+
<todo-app>
2+
<form action="#">
3+
<input-field>
4+
<label for="add-todo">What needs to be done?</label>
5+
<div class="row">
6+
<div class="group auto">
7+
<input id="add-todo" type="text" value="" required>
108
</div>
11-
</input-field>
12-
<input-button class="submit">
13-
<button type="submit" class="primary" disabled>Add Todo</button>
9+
</div>
10+
</input-field>
11+
<input-button class="submit">
12+
<button type="submit" class="primary" disabled>Add Todo</button>
13+
</input-button>
14+
</form>
15+
<ol filter="all"></ol>
16+
<template>
17+
<li>
18+
<input-checkbox class="todo">
19+
<label>
20+
<input type="checkbox" class="visually-hidden" />
21+
<span></span>
22+
</label>
23+
</input-checkbox>
24+
<input-button class="delete">
25+
<button type="button">Delete</button>
1426
</input-button>
15-
</form>
16-
</todo-form>
17-
<todo-list filter="all">
18-
<ol></ol>
19-
<template>
20-
<li>
21-
<input-checkbox class="todo">
22-
<label>
23-
<input type="checkbox" class="visually-hidden" />
24-
<span></span>
25-
</label>
26-
</input-checkbox>
27-
<input-button>
28-
<button type="button">Delete</button>
29-
</input-button>
30-
</li>
31-
</template>
32-
</todo-list>
27+
</li>
28+
</template>
3329
<footer>
34-
<todo-count>
30+
<div class="todo-count">
3531
<p class="all-done">Well done, all done!</p>
36-
<p class="remaining"><span></span> tasks left</p>
37-
</todo-count>
32+
<p class="remaining">
33+
<span class="count"></span>
34+
<span class="singular">task</span>
35+
<span class="plural">tasks</span>
36+
remaining
37+
</p>
38+
</div>
3839
<input-radiogroup value="all" class="split-button">
3940
<fieldset>
4041
<legend class="visually-hidden">Filter</legend>
Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,90 @@
1-
import { UIElement } from "@zeix/ui-element"
2-
import type { TodoCountObject, TodoList } from "../todo-list/todo-list"
1+
import { setAttribute, setProperty, setText, UIElement } from "@zeix/ui-element"
32
import type { InputRadiogroup } from "../input-radiogroup/input-radiogroup"
4-
import type { AddTodoEvent } from "../todo-form/todo-form"
3+
import type { InputField } from "../input-field/input-field"
4+
import type { InputCheckbox } from "../input-checkbox/input-checkbox"
55

66
export class TodoApp extends UIElement {
77
connectedCallback() {
8-
const todoList = this.querySelector<TodoList>('todo-list')
8+
const input = this.querySelector<InputField>('input-field')
99

10-
// Event listener on own element
11-
this.self.on('add-todo', (e: Event) => {
12-
todoList?.addItem((e as AddTodoEvent).detail)
13-
})
10+
// State signal 'tasks': State<InputCheckbox[]>
11+
this.#updateTasks()
12+
13+
// Derived signals
14+
this.set('total', () =>
15+
this.get<InputCheckbox[]>('tasks')!.length
16+
)
17+
this.set('completed', () =>
18+
this.get<InputCheckbox[]>('tasks')!.filter(el => el.get('checked')).length
19+
)
20+
this.set('active', () =>
21+
this.get<number>('total')! - this.get<number>('completed')!
22+
)
23+
this.set('filter', () =>
24+
this.querySelector<InputRadiogroup>('input-radiogroup')?.get<string>('value') ?? 'all'
25+
)
1426

15-
// Coordinate todo-count
16-
this.first('todo-count').pass({
17-
active: () => (todoList?.get('count') as TodoCountObject).active
27+
this.first('form').on('submit', (e: Event) => {
28+
e.preventDefault()
29+
30+
// Wait for microtask to ensure the input field value is updated
31+
queueMicrotask(() => {
32+
const value = input?.get<string>('value')?.trim()
33+
if (value) {
34+
const ol = this.querySelector('ol')
35+
const fragment = this.querySelector('template')
36+
?.content.cloneNode(true) as DocumentFragment
37+
const span = fragment.querySelector('span')
38+
if (ol && fragment && span) {
39+
span.textContent = value
40+
ol.appendChild(fragment)
41+
}
42+
this.#updateTasks()
43+
input?.clear()
44+
}
45+
})
1846
})
1947

20-
// Coordinate todo-list
21-
this.first('todo-list').pass({
22-
filter: () => this.querySelector<InputRadiogroup>('input-radiogroup')?.get('value')
48+
// Coordinate .submit button
49+
this.first('.submit').pass({
50+
disabled: () => input?.get('empty')
2351
})
2452

53+
// Event handler and effect on ol element
54+
this.first('ol')
55+
.on('click', (e: Event) => {
56+
const el = e.target as HTMLElement
57+
if (el.localName === 'button') {
58+
el.closest('li')?.remove()
59+
this.#updateTasks()
60+
}
61+
})
62+
.sync(setAttribute('filter'))
63+
64+
// Effects on .todo-count elements
65+
this.first('.count').sync(setText('active'))
66+
const setAriaHidden = (fn: (n: number) => boolean) =>
67+
setProperty('ariaHidden', () => fn(this.get<number>('active')!))
68+
this.first('.singular').sync(setAriaHidden(n => n > 1))
69+
this.first('.plural').sync(setAriaHidden(n => n === 1))
70+
this.first('.remaining').sync(setAriaHidden(n => !n))
71+
this.first('.all-done').sync(setAriaHidden(n => !!n))
72+
2573
// Coordinate .clear-completed button
2674
this.first('.clear-completed')
27-
.on('click', () => todoList?.clearCompleted())
75+
.on('click', () => {
76+
this.get<InputCheckbox[]>('tasks')!
77+
.filter(el => el.get('checked'))
78+
.forEach(el => el.parentElement?.remove())
79+
this.#updateTasks()
80+
})
2881
.pass({
29-
disabled: () => !(todoList?.get('count') as TodoCountObject).completed
82+
disabled: () => !(this.get('completed'))
3083
})
3184
}
85+
86+
#updateTasks() {
87+
this.set('tasks', Array.from(this.querySelectorAll('input-checkbox')))
88+
}
3289
}
3390
TodoApp.define('todo-app')

docs-src/components/todo-count/todo-count.css

Lines changed: 0 additions & 8 deletions
This file was deleted.

docs-src/components/todo-count/todo-count.html

Lines changed: 0 additions & 9 deletions
This file was deleted.

docs-src/components/todo-count/todo-count.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

docs-src/components/todo-form/todo-form.css

Lines changed: 0 additions & 19 deletions
This file was deleted.

docs-src/components/todo-form/todo-form.html

Lines changed: 0 additions & 15 deletions
This file was deleted.

docs-src/components/todo-form/todo-form.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

docs-src/components/todo-list/todo-list.css

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)