Skip to content

Commit eb7e57a

Browse files
authored
Add new devtools demo (#759)
1 parent c7c3218 commit eb7e57a

File tree

4 files changed

+149
-1
lines changed

4 files changed

+149
-1
lines changed

docs/demos/devtools.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.new-todo {
2+
display: flex;
3+
align-items: center;
4+
gap: 8px;
5+
margin-bottom: 8px;
6+
}
7+
8+
.todo-item {
9+
display: flex;
10+
align-items: center;
11+
gap: 8px;
12+
margin-bottom: 4px;
13+
}

docs/demos/devtools.tsx

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { useSignal } from "@preact/signals";
2+
import { Show, For } from "@preact/signals/utils";
3+
import { computed, signal } from "@preact/signals-core";
4+
import "./devtools.css";
5+
6+
type TodoModel = {
7+
id: number;
8+
get text(): string;
9+
get done(): boolean;
10+
toggle(): void;
11+
updateText(newText: string): void;
12+
};
13+
14+
const createTodoModel = (id: number, input: string): TodoModel => {
15+
const text = signal(input, { name: `todo-${id}-text` });
16+
const done = signal(false, { name: `todo-${id}-done` });
17+
18+
return {
19+
id,
20+
get text() {
21+
return text.value;
22+
},
23+
get done() {
24+
return done.value;
25+
},
26+
toggle() {
27+
done.value = !done.value;
28+
},
29+
updateText(newText: string) {
30+
text.value = newText;
31+
},
32+
};
33+
};
34+
35+
const todosModel = (() => {
36+
const todos = signal<TodoModel[]>(
37+
[
38+
createTodoModel(1, "Learn Preact Signals"),
39+
createTodoModel(2, "Build something fun"),
40+
],
41+
{ name: "todos-list" }
42+
);
43+
44+
const allDone = computed(
45+
() => todos.value.length > 0 && todos.value.every(t => t.done),
46+
{ name: "all-done" }
47+
);
48+
49+
return {
50+
todos,
51+
allDone,
52+
add(text: string) {
53+
todos.value = [...todos.value, createTodoModel(Date.now(), text)];
54+
},
55+
};
56+
})();
57+
58+
export default function DevToolsDemo() {
59+
return (
60+
<div>
61+
<h1>DevTools Demo</h1>
62+
<main>
63+
<TodosList />
64+
</main>
65+
</div>
66+
);
67+
}
68+
69+
function TodosList() {
70+
const newTodoText = useSignal("", { name: "new-todo-text" });
71+
72+
return (
73+
<div>
74+
<h2>Todos</h2>
75+
<div class="new-todo">
76+
<label>
77+
New Todo:{" "}
78+
<input
79+
type="text"
80+
value={newTodoText.value}
81+
onInput={e => (newTodoText.value = e.currentTarget.value)}
82+
/>
83+
</label>
84+
<button
85+
onClick={() => {
86+
if (newTodoText.value.trim() !== "") {
87+
todosModel.add(newTodoText.value.trim());
88+
newTodoText.value = "";
89+
}
90+
}}
91+
>
92+
Add
93+
</button>
94+
</div>
95+
<Show when={todosModel.allDone}>
96+
<p>All todos are done! 🎉</p>
97+
</Show>
98+
<ul>
99+
<For each={todosModel.todos}>
100+
{todo => <TodoItem key={todo.id} todo={todo} />}
101+
</For>
102+
</ul>
103+
</div>
104+
);
105+
}
106+
107+
function TodoItem({ todo }: { todo: TodoModel }) {
108+
const isEditing = useSignal(false, { name: `todo-${todo.id}-isEditing` });
109+
return (
110+
<li class="todo-item">
111+
<input
112+
type="checkbox"
113+
checked={todo.done}
114+
onChange={() => todo.toggle()}
115+
/>
116+
<Show when={isEditing} fallback={<p>{todo.text}</p>}>
117+
<input
118+
type="text"
119+
value={todo.text}
120+
onInput={e => todo.updateText(e.currentTarget.value)}
121+
/>
122+
</Show>
123+
<button onClick={() => (isEditing.value = !isEditing.value)}>
124+
Toggle Edit
125+
</button>
126+
</li>
127+
);
128+
}

docs/demos/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const demos = {
1313
Sum,
1414
GlobalCounter,
1515
DuelingCounters,
16+
Devtools: lazy(() => import("./devtools")),
1617
Nesting: lazy(() => import("./nesting")),
1718
Animation: lazy(() => import("./animation")),
1819
Bench: lazy(() => import("./bench")),

docs/vite.config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ function packages(prod: boolean) {
1616
if (pkg.private) continue;
1717
const entry = prod ? "." : pkg.source;
1818
alias[pkg.name] = resolve(root, name, entry);
19+
console.log(`alias: ${pkg.name} -> ${alias[pkg.name]}`);
1920
}
2021
return alias;
2122
}
2223

24+
// @ts-expect-error
2325
export default defineConfig(env => ({
2426
plugins: [
2527
process.env.DEBUG
@@ -68,8 +70,12 @@ export default defineConfig(env => ({
6870
// one, as expected. I'm working around this by just mainly aliasing
6971
// the package that needs to be resolved.
7072
"@preact/signals-react/runtime": join(root, "react/runtime"),
73+
"@preact/signals": join(root, "preact"),
7174
}
72-
: packages(false),
75+
: {
76+
"@preact/signals/utils": join(root, "preact/utils/src"),
77+
...packages(false),
78+
},
7379
},
7480
}));
7581

0 commit comments

Comments
 (0)