Skip to content

Commit 2024d6b

Browse files
committed
feat(add-on): legend state
1 parent d8d8f40 commit 2024d6b

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { observable } from "@legendapp/state";
2+
import { ObservablePersistLocalStorage } from "@legendapp/state/persist-plugins/local-storage";
3+
import { syncObservable } from "@legendapp/state/sync";
4+
5+
type Todo = {
6+
id: number;
7+
text: string;
8+
completed: boolean;
9+
};
10+
// Create an observable
11+
export const todos$ = observable<Todo[]>([]);
12+
13+
// Persist the observable
14+
syncObservable(todos$, {
15+
persist: {
16+
name: "demo-legend-state-todos",
17+
plugin: ObservablePersistLocalStorage,
18+
},
19+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/react-router'
2+
import { todos$ } from "@/lib/demo-legend-state";
3+
import { For, useObservable } from "@legendapp/state/react";
4+
import { $React } from "@legendapp/state/react-web";
5+
<% if (codeRouter) { %>
6+
import type { RootRoute } from '@tanstack/react-router'
7+
<% } else { %>
8+
export const Route = createFileRoute('/demo/legend-state')({
9+
component: LegendStateDemo,
10+
})
11+
<% } %>
12+
13+
function TodoForm() {
14+
const todoText$ = useObservable("");
15+
const handleSubmit = (e: React.FormEvent) => {
16+
e.preventDefault();
17+
if (todoText$.peek().trim() === "") return;
18+
todos$.push({ id: Date.now(), text: todoText$.peek().trim(), completed: false });
19+
todoText$.set("");
20+
};
21+
return (
22+
<form onSubmit={handleSubmit}>
23+
<$React.input
24+
placeholder="Add a todo"
25+
type="text"
26+
$value={todoText$}
27+
className="bg-white/10 rounded-lg px-4 py-2 outline-none border border-white/20 hover:border-white/40 focus:border-white/60 transition-colors duration-200 placeholder-white/40 w-full"
28+
/>
29+
</form>
30+
);
31+
}
32+
function TodoList() {
33+
return (
34+
<ul className="space-y-4">
35+
<For each={todos$} optimized>
36+
{(todo) => (
37+
<li
38+
key={todo.id.get()}
39+
className="inline-flex items-center gap-2 justify-between w-full"
40+
>
41+
<span className={todo.completed.get() ? "line-through" : ""}>
42+
{todo.text.get()}
43+
</span>
44+
<span className="flex items-center gap-2 justify-center">
45+
<$React.input
46+
type="checkbox"
47+
$checked={todo.completed}
48+
className="w-6 h-6 text-blue-600 bg-gray-100 border-gray-300 rounded-sm focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
49+
/>
50+
<button
51+
type="button"
52+
onClick={() =>
53+
todos$.splice(
54+
todos$.findIndex((t) => t.id.get() === todo.id.get()),
55+
1,
56+
)
57+
}
58+
className="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-xs px-5 py-2.5 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900"
59+
>
60+
Remove
61+
</button>
62+
</span>
63+
</li>
64+
)}
65+
</For>
66+
</ul>
67+
);
68+
}
69+
70+
function LegendStateDemo() {
71+
return (
72+
<div
73+
className="min-h-[calc(100vh-32px)] text-white p-8 flex items-center justify-center w-full h-full"
74+
style={{
75+
backgroundImage:
76+
"radial-gradient(50% 50% at 80% 80%, #f4a460 0%, #8b4513 70%, #1a0f0a 100%)",
77+
}}
78+
>
79+
<div className="bg-white/10 backdrop-blur-lg rounded-xl p-8 shadow-lg flex flex-col gap-4 text-3xl w-5xl">
80+
<h1 className="text-4xl font-bold mb-5">Legend State Example</h1>
81+
<TodoForm />
82+
<TodoList />
83+
</div>
84+
</div>
85+
);
86+
}
87+
88+
89+
<% if (codeRouter) { %>
90+
export default (parentRoute: RootRoute) => createRoute({
91+
path: '/demo/legend-state',
92+
component: LegendStateDemo,
93+
getParentRoute: () => parentRoute,
94+
})
95+
<% } %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "Legend State",
3+
"description": "A state manager with built-in sync engine to persist state locally or remotely",
4+
"phase": "add-on",
5+
"templates": ["file-router", "code-router"],
6+
"link": "https://legendapp.com/open-source/state/v3/",
7+
"routes": [
8+
{
9+
"url": "/demo/legend-state",
10+
"name": "Legend State"
11+
}
12+
]
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"@legendapp/state": "3.0.0-beta.30"
4+
}
5+
}

0 commit comments

Comments
 (0)