jac-client 0.2.6__py3-none-any.whl → 0.2.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jac_client/examples/all-in-one/{src/button.jac → button.jac} +4 -3
- jac_client/examples/all-in-one/components/CategoryFilter.jac +47 -0
- jac_client/examples/all-in-one/components/Header.jac +17 -0
- jac_client/examples/all-in-one/components/ProfitOverview.jac +64 -0
- jac_client/examples/all-in-one/components/Summary.jac +76 -0
- jac_client/examples/all-in-one/components/TransactionForm.jac +188 -0
- jac_client/examples/all-in-one/components/TransactionItem.jac +62 -0
- jac_client/examples/all-in-one/components/TransactionList.jac +44 -0
- jac_client/examples/all-in-one/components/button.jac +8 -0
- jac_client/examples/all-in-one/components/navigation.jac +126 -0
- jac_client/examples/all-in-one/constants/categories.jac +36 -0
- jac_client/examples/all-in-one/constants/clients.jac +12 -0
- jac_client/examples/all-in-one/context/BudgetContext.jac +31 -0
- jac_client/examples/all-in-one/hooks/useBudget.jac +122 -0
- jac_client/examples/all-in-one/hooks/useLocalStorage.jac +37 -0
- jac_client/examples/all-in-one/main.jac +542 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +140 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +157 -0
- jac_client/examples/all-in-one/pages/LandingPage.jac +124 -0
- jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
- jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
- jac_client/examples/all-in-one/pages/loginPage.jac +127 -0
- jac_client/examples/all-in-one/pages/nestedDemo.jac +54 -0
- jac_client/examples/all-in-one/pages/notFound.jac +18 -0
- jac_client/examples/all-in-one/pages/signupPage.jac +127 -0
- jac_client/examples/all-in-one/utils/formatters.jac +49 -0
- jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
- jac_client/examples/asset-serving/image-asset/main.jac +56 -0
- jac_client/examples/asset-serving/import-alias/main.jac +109 -0
- jac_client/examples/basic/main.jac +23 -0
- jac_client/examples/basic-auth/main.jac +363 -0
- jac_client/examples/basic-auth-with-router/main.jac +451 -0
- jac_client/examples/basic-full-stack/main.jac +362 -0
- jac_client/examples/css-styling/js-styling/main.jac +63 -0
- jac_client/examples/css-styling/material-ui/main.jac +122 -0
- jac_client/examples/css-styling/pure-css/main.jac +55 -0
- jac_client/examples/css-styling/sass-example/main.jac +55 -0
- jac_client/examples/css-styling/styled-components/main.jac +62 -0
- jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
- jac_client/examples/full-stack-with-auth/main.jac +696 -0
- jac_client/examples/little-x/main.jac +681 -0
- jac_client/examples/little-x/src/submit-button.jac +15 -14
- jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
- jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
- jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
- jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
- jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
- jac_client/examples/ts-support/main.jac +35 -0
- jac_client/examples/with-router/main.jac +286 -0
- jac_client/plugin/cli.jac +507 -470
- jac_client/plugin/client.jac +30 -12
- jac_client/plugin/client_runtime.cl.jac +25 -15
- jac_client/plugin/impl/client.impl.jac +126 -26
- jac_client/plugin/impl/client_runtime.impl.jac +182 -10
- jac_client/plugin/plugin_config.jac +216 -34
- jac_client/plugin/src/__init__.jac +0 -2
- jac_client/plugin/src/compiler.jac +2 -2
- jac_client/plugin/src/config_loader.jac +1 -0
- jac_client/plugin/src/desktop_config.jac +31 -0
- jac_client/plugin/src/impl/compiler.impl.jac +99 -30
- jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
- jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
- jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
- jac_client/plugin/src/impl/vite_bundler.impl.jac +384 -144
- jac_client/plugin/src/package_installer.jac +1 -1
- jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
- jac_client/plugin/src/targets/desktop_target.jac +37 -0
- jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
- jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
- jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
- jac_client/plugin/src/targets/register.jac +21 -0
- jac_client/plugin/src/targets/registry.jac +87 -0
- jac_client/plugin/src/targets/web_target.jac +35 -0
- jac_client/plugin/src/vite_bundler.jac +15 -1
- jac_client/plugin/utils/__init__.jac +3 -0
- jac_client/plugin/utils/bun_installer.jac +16 -0
- jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
- jac_client/templates/client.jacpack +72 -0
- jac_client/templates/fullstack.jacpack +61 -0
- jac_client/tests/conftest.py +110 -52
- jac_client/tests/fixtures/spawn_test/app.jac +64 -70
- jac_client/tests/fixtures/with-ts/app.jac +28 -28
- jac_client/tests/test_cli.py +280 -113
- jac_client/tests/test_e2e.py +232 -0
- jac_client/tests/test_helpers.py +58 -0
- jac_client/tests/test_it.py +325 -154
- jac_client/tests/test_it_desktop.py +891 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/METADATA +20 -11
- jac_client-0.2.11.dist-info/RECORD +113 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
- jac_client/examples/all-in-one/src/app.jac +0 -841
- jac_client/examples/all-in-one/src/components/button.jac +0 -7
- jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
- jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
- jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
- jac_client/examples/basic/src/app.jac +0 -21
- jac_client/examples/basic-auth/src/app.jac +0 -377
- jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
- jac_client/examples/basic-full-stack/src/app.jac +0 -365
- jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
- jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
- jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
- jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
- jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
- jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
- jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
- jac_client/examples/little-x/src/app.jac +0 -719
- jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
- jac_client/examples/ts-support/src/app.jac +0 -35
- jac_client/examples/with-router/src/app.jac +0 -323
- jac_client/plugin/src/babel_processor.jac +0 -18
- jac_client/plugin/src/impl/babel_processor.impl.jac +0 -84
- jac_client-0.2.6.dist-info/RECORD +0 -74
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Todo App
|
|
2
|
+
node Todo {
|
|
3
|
+
has text: str,
|
|
4
|
+
done: bool = False;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
walker create_todo {
|
|
8
|
+
has text: str;
|
|
9
|
+
|
|
10
|
+
can create with `root entry {
|
|
11
|
+
new_todo = here ++> Todo(text=self.text);
|
|
12
|
+
report new_todo;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
walker toggle_todo {
|
|
17
|
+
can toggle with Todo entry {
|
|
18
|
+
here.done = not here.done;
|
|
19
|
+
report here;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
walker read_todos {
|
|
24
|
+
can read with `root entry {
|
|
25
|
+
visit [-->(`?Todo)];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
can report_todos with exit {
|
|
29
|
+
report here;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cl {
|
|
34
|
+
def:pub app -> any {
|
|
35
|
+
has todos: list = [],
|
|
36
|
+
inputValue: str = "",
|
|
37
|
+
filter: str = "all";
|
|
38
|
+
|
|
39
|
+
async can with entry {
|
|
40
|
+
result = root spawn read_todos();
|
|
41
|
+
todos = result.reports;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Add a new todo
|
|
45
|
+
async def addTodo -> None {
|
|
46
|
+
if not inputValue.trim() {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
newTodo = {"id": Date.now(), "text": inputValue.trim(), "done": False};
|
|
50
|
+
response = root spawn create_todo(text=inputValue.trim());
|
|
51
|
+
newTodos = todos.concat([response.reports[0][0]]);
|
|
52
|
+
todos = newTodos;
|
|
53
|
+
inputValue = "";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Toggle todo completion status
|
|
57
|
+
async def toggleTodo(id: any) -> None {
|
|
58
|
+
console.log("toggleTodo", id);
|
|
59
|
+
id spawn toggle_todo();
|
|
60
|
+
todos = todos.map(
|
|
61
|
+
lambda todo: any -> any { if todo._jac_id == id {
|
|
62
|
+
updatedTodo = {
|
|
63
|
+
"_jac_id": todo._jac_id,
|
|
64
|
+
"text": todo.text,
|
|
65
|
+
"done": not todo.done,
|
|
66
|
+
"id": todo.id
|
|
67
|
+
};
|
|
68
|
+
return updatedTodo;
|
|
69
|
+
}return todo; }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Delete a todo
|
|
74
|
+
def deleteTodo(id: any) -> None {
|
|
75
|
+
todos = todos.filter(lambda todo: any -> bool { return todo.id != id; });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Clear all completed todos
|
|
79
|
+
def clearCompleted -> None {
|
|
80
|
+
todos = todos.filter(lambda todo: any -> bool { return not todo.done; });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Filter todos based on current filter
|
|
84
|
+
def getFilteredTodos -> list {
|
|
85
|
+
if filter == "active" {
|
|
86
|
+
return todos.filter(
|
|
87
|
+
lambda todo: any -> bool { return not todo.done; }
|
|
88
|
+
);
|
|
89
|
+
} elif filter == "completed" {
|
|
90
|
+
return todos.filter(lambda todo: any -> bool { return todo.done; });
|
|
91
|
+
}
|
|
92
|
+
return todos;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Count active todos
|
|
96
|
+
activeCount = todos.filter(lambda todo: any -> bool { return not todo.done; }).length;
|
|
97
|
+
|
|
98
|
+
filteredTodos = getFilteredTodos();
|
|
99
|
+
|
|
100
|
+
return
|
|
101
|
+
<div
|
|
102
|
+
style={{
|
|
103
|
+
"maxWidth": "600px",
|
|
104
|
+
"margin": "40px auto",
|
|
105
|
+
"padding": "24px",
|
|
106
|
+
"fontFamily": "system-ui, -apple-system, sans-serif",
|
|
107
|
+
"background": "#f9fafb",
|
|
108
|
+
"minHeight": "100vh"
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<h1
|
|
112
|
+
style={{
|
|
113
|
+
"textAlign": "center",
|
|
114
|
+
"color": "#1f2937",
|
|
115
|
+
"marginBottom": "32px",
|
|
116
|
+
"fontSize": "2.5rem",
|
|
117
|
+
"fontWeight": "700"
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
📝 My Todo App
|
|
121
|
+
</h1>
|
|
122
|
+
# Add todo form
|
|
123
|
+
<div
|
|
124
|
+
style={{
|
|
125
|
+
"display": "flex",
|
|
126
|
+
"gap": "8px",
|
|
127
|
+
"marginBottom": "24px",
|
|
128
|
+
"background": "#ffffff",
|
|
129
|
+
"padding": "16px",
|
|
130
|
+
"borderRadius": "12px",
|
|
131
|
+
"boxShadow": "0 1px 3px rgba(0,0,0,0.1)"
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
<input
|
|
135
|
+
type="text"
|
|
136
|
+
value={inputValue}
|
|
137
|
+
onChange={lambda e: any -> None { inputValue = e.target.value;}}
|
|
138
|
+
onKeyPress={lambda e: any -> None { if e.key == "Enter" {
|
|
139
|
+
addTodo();
|
|
140
|
+
}}}
|
|
141
|
+
placeholder="What needs to be done?"
|
|
142
|
+
style={{
|
|
143
|
+
"flex": "1",
|
|
144
|
+
"padding": "12px 16px",
|
|
145
|
+
"border": "1px solid #e5e7eb",
|
|
146
|
+
"borderRadius": "8px",
|
|
147
|
+
"fontSize": "16px",
|
|
148
|
+
"outline": "none"
|
|
149
|
+
}}
|
|
150
|
+
/>
|
|
151
|
+
<button
|
|
152
|
+
onClick={addTodo}
|
|
153
|
+
style={{
|
|
154
|
+
"padding": "12px 24px",
|
|
155
|
+
"background": "#3b82f6",
|
|
156
|
+
"color": "#ffffff",
|
|
157
|
+
"border": "none",
|
|
158
|
+
"borderRadius": "8px",
|
|
159
|
+
"fontSize": "16px",
|
|
160
|
+
"fontWeight": "600",
|
|
161
|
+
"cursor": "pointer",
|
|
162
|
+
"transition": "background 0.2s"
|
|
163
|
+
}}
|
|
164
|
+
>
|
|
165
|
+
Add
|
|
166
|
+
</button>
|
|
167
|
+
</div>
|
|
168
|
+
# Filter buttons
|
|
169
|
+
<div
|
|
170
|
+
style={{
|
|
171
|
+
"display": "flex",
|
|
172
|
+
"gap": "8px",
|
|
173
|
+
"marginBottom": "24px",
|
|
174
|
+
"justifyContent": "center"
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<button
|
|
178
|
+
onClick={lambda -> None { filter = "all";}}
|
|
179
|
+
style={{
|
|
180
|
+
"padding": "8px 16px",
|
|
181
|
+
"background": ("#3b82f6" if filter == "all" else "#ffffff"),
|
|
182
|
+
"color": ("#ffffff" if filter == "all" else "#3b82f6"),
|
|
183
|
+
"border": "1px solid #3b82f6",
|
|
184
|
+
"borderRadius": "6px",
|
|
185
|
+
"fontSize": "14px",
|
|
186
|
+
"fontWeight": "600",
|
|
187
|
+
"cursor": "pointer"
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
All
|
|
191
|
+
</button>
|
|
192
|
+
<button
|
|
193
|
+
onClick={lambda -> None { filter = "active";}}
|
|
194
|
+
style={{
|
|
195
|
+
"padding": "8px 16px",
|
|
196
|
+
"background": (
|
|
197
|
+
"#3b82f6" if filter == "active" else "#ffffff"
|
|
198
|
+
),
|
|
199
|
+
"color": ("#ffffff" if filter == "active" else "#3b82f6"),
|
|
200
|
+
"border": "1px solid #3b82f6",
|
|
201
|
+
"borderRadius": "6px",
|
|
202
|
+
"fontSize": "14px",
|
|
203
|
+
"fontWeight": "600",
|
|
204
|
+
"cursor": "pointer"
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
Active
|
|
208
|
+
</button>
|
|
209
|
+
<button
|
|
210
|
+
onClick={lambda -> None { filter = "completed";}}
|
|
211
|
+
style={{
|
|
212
|
+
"padding": "8px 16px",
|
|
213
|
+
"background": (
|
|
214
|
+
"#3b82f6" if filter == "completed" else "#ffffff"
|
|
215
|
+
),
|
|
216
|
+
"color": (
|
|
217
|
+
"#ffffff" if filter == "completed" else "#3b82f6"
|
|
218
|
+
),
|
|
219
|
+
"border": "1px solid #3b82f6",
|
|
220
|
+
"borderRadius": "6px",
|
|
221
|
+
"fontSize": "14px",
|
|
222
|
+
"fontWeight": "600",
|
|
223
|
+
"cursor": "pointer"
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
Completed
|
|
227
|
+
</button>
|
|
228
|
+
</div>
|
|
229
|
+
# Todo list
|
|
230
|
+
<div
|
|
231
|
+
style={{
|
|
232
|
+
"background": "#ffffff",
|
|
233
|
+
"borderRadius": "12px",
|
|
234
|
+
"boxShadow": "0 1px 3px rgba(0,0,0,0.1)",
|
|
235
|
+
"overflow": "hidden"
|
|
236
|
+
}}
|
|
237
|
+
>
|
|
238
|
+
{(
|
|
239
|
+
<div
|
|
240
|
+
style={{
|
|
241
|
+
"padding": "40px",
|
|
242
|
+
"textAlign": "center",
|
|
243
|
+
"color": "#9ca3af"
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
{(
|
|
247
|
+
"No todos yet. Add one above!"
|
|
248
|
+
if filter == "all"
|
|
249
|
+
else (
|
|
250
|
+
"No active todos!"
|
|
251
|
+
if filter == "active"
|
|
252
|
+
else "No completed todos!"
|
|
253
|
+
)
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
)
|
|
257
|
+
if filteredTodos.length == 0
|
|
258
|
+
else (
|
|
259
|
+
filteredTodos.map(
|
|
260
|
+
lambda todo: any -> any { return
|
|
261
|
+
<div
|
|
262
|
+
key={todo._jac_id}
|
|
263
|
+
style={{
|
|
264
|
+
"display": "flex",
|
|
265
|
+
"alignItems": "center",
|
|
266
|
+
"gap": "12px",
|
|
267
|
+
"padding": "16px",
|
|
268
|
+
"borderBottom": "1px solid #e5e7eb",
|
|
269
|
+
"transition": "background 0.2s"
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
<input
|
|
273
|
+
type="checkbox"
|
|
274
|
+
checked={todo.done}
|
|
275
|
+
onChange={lambda -> None { toggleTodo(
|
|
276
|
+
todo._jac_id
|
|
277
|
+
);}}
|
|
278
|
+
style={{
|
|
279
|
+
"width": "20px",
|
|
280
|
+
"height": "20px",
|
|
281
|
+
"cursor": "pointer"
|
|
282
|
+
}}
|
|
283
|
+
/>
|
|
284
|
+
<span
|
|
285
|
+
style={{
|
|
286
|
+
"flex": "1",
|
|
287
|
+
"textDecoration": (
|
|
288
|
+
"line-through" if todo.done else "none"
|
|
289
|
+
),
|
|
290
|
+
"color": (
|
|
291
|
+
"#9ca3af" if todo.done else "#1f2937"
|
|
292
|
+
),
|
|
293
|
+
"fontSize": "16px"
|
|
294
|
+
}}
|
|
295
|
+
>
|
|
296
|
+
{todo.text}
|
|
297
|
+
</span>
|
|
298
|
+
<button
|
|
299
|
+
onClick={lambda -> None { deleteTodo(
|
|
300
|
+
todo.__jac_id
|
|
301
|
+
);}}
|
|
302
|
+
style={{
|
|
303
|
+
"padding": "6px 12px",
|
|
304
|
+
"background": "#ef4444",
|
|
305
|
+
"color": "#ffffff",
|
|
306
|
+
"border": "none",
|
|
307
|
+
"borderRadius": "6px",
|
|
308
|
+
"fontSize": "14px",
|
|
309
|
+
"fontWeight": "500",
|
|
310
|
+
"cursor": "pointer",
|
|
311
|
+
"transition": "background 0.2s"
|
|
312
|
+
}}
|
|
313
|
+
>
|
|
314
|
+
Delete
|
|
315
|
+
</button>
|
|
316
|
+
</div>; }
|
|
317
|
+
)
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
# Stats and clear completed button
|
|
321
|
+
{(
|
|
322
|
+
<div
|
|
323
|
+
style={{
|
|
324
|
+
"display": "flex",
|
|
325
|
+
"justifyContent": "space-between",
|
|
326
|
+
"alignItems": "center",
|
|
327
|
+
"marginTop": "24px",
|
|
328
|
+
"padding": "16px",
|
|
329
|
+
"background": "#ffffff",
|
|
330
|
+
"borderRadius": "12px",
|
|
331
|
+
"boxShadow": "0 1px 3px rgba(0,0,0,0.1)"
|
|
332
|
+
}}
|
|
333
|
+
>
|
|
334
|
+
<span style={{"color": "#6b7280", "fontSize": "14px"}}>
|
|
335
|
+
{activeCount}{"item" if activeCount == 1 else "items"}left
|
|
336
|
+
</span>
|
|
337
|
+
{(
|
|
338
|
+
<button
|
|
339
|
+
onClick={clearCompleted}
|
|
340
|
+
style={{
|
|
341
|
+
"padding": "8px 16px",
|
|
342
|
+
"background": "#ef4444",
|
|
343
|
+
"color": "#ffffff",
|
|
344
|
+
"border": "none",
|
|
345
|
+
"borderRadius": "6px",
|
|
346
|
+
"fontSize": "14px",
|
|
347
|
+
"fontWeight": "600",
|
|
348
|
+
"cursor": "pointer"
|
|
349
|
+
}}
|
|
350
|
+
>
|
|
351
|
+
Clear Completed
|
|
352
|
+
</button>
|
|
353
|
+
)
|
|
354
|
+
if todos.some(lambda todo: any -> bool { return todo.done; })
|
|
355
|
+
else None}
|
|
356
|
+
</div>
|
|
357
|
+
)
|
|
358
|
+
if todos.length > 0
|
|
359
|
+
else None}
|
|
360
|
+
</div>;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import from .styles { default as styles }
|
|
3
|
+
|
|
4
|
+
cl {
|
|
5
|
+
def:pub app -> any {
|
|
6
|
+
has count: int = 0;
|
|
7
|
+
|
|
8
|
+
can with count entry {
|
|
9
|
+
console.log("Count changed: ", count);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
handleIncrement = lambda e: any -> None { count = count + 1;};
|
|
13
|
+
|
|
14
|
+
handleDecrement = lambda e: any -> None { count = count - 1;};
|
|
15
|
+
|
|
16
|
+
handleReset = lambda e: any -> None { count = 0;};
|
|
17
|
+
|
|
18
|
+
countStyle = styles.countDisplayZero
|
|
19
|
+
if count == 0
|
|
20
|
+
else (
|
|
21
|
+
styles.countDisplayPositive if count > 0 else styles.countDisplayNegative
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return
|
|
25
|
+
<div style={styles.container}>
|
|
26
|
+
<div style={styles.card}>
|
|
27
|
+
<h1 style={styles.title}>
|
|
28
|
+
Counter Application
|
|
29
|
+
</h1>
|
|
30
|
+
<div style={styles.divider}></div>
|
|
31
|
+
<div style={styles.counterSection}>
|
|
32
|
+
<div style={styles.label}>
|
|
33
|
+
Current Count
|
|
34
|
+
</div>
|
|
35
|
+
<div style={countStyle}>
|
|
36
|
+
{count}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div style={styles.divider}></div>
|
|
40
|
+
<div style={styles.buttonGroup}>
|
|
41
|
+
<button
|
|
42
|
+
style={styles.buttonDecrement}
|
|
43
|
+
onClick={handleDecrement}
|
|
44
|
+
>
|
|
45
|
+
-
|
|
46
|
+
</button>
|
|
47
|
+
<button style={styles.buttonReset} onClick={handleReset}>
|
|
48
|
+
↻
|
|
49
|
+
</button>
|
|
50
|
+
<button
|
|
51
|
+
style={styles.buttonIncrement}
|
|
52
|
+
onClick={handleIncrement}
|
|
53
|
+
>
|
|
54
|
+
+
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
<div style={styles.hint}>
|
|
58
|
+
Click the buttons to increment, decrement, or reset the counter
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
cl import from '@mui/material/Button' { default as Button }
|
|
2
|
+
cl import from '@mui/material/Card' { default as Card }
|
|
3
|
+
cl import from '@mui/material/CardContent' { default as CardContent }
|
|
4
|
+
cl import from '@mui/material/CardActions' { default as CardActions }
|
|
5
|
+
cl import from '@mui/material/Typography' { default as Typography }
|
|
6
|
+
cl import from '@mui/material/IconButton' { default as IconButton }
|
|
7
|
+
cl import from '@mui/material/Box' { default as Box }
|
|
8
|
+
cl import from '@mui/material/Divider' { default as Divider }
|
|
9
|
+
cl import from '@mui/icons-material/Add' { default as AddIcon }
|
|
10
|
+
cl import from '@mui/icons-material/Remove' { default as RemoveIcon }
|
|
11
|
+
cl import from '@mui/icons-material/Refresh' { default as RefreshIcon }
|
|
12
|
+
|
|
13
|
+
cl {
|
|
14
|
+
def:pub app -> any {
|
|
15
|
+
has count: int = 0;
|
|
16
|
+
|
|
17
|
+
can with count entry {
|
|
18
|
+
console.log("Count changed: ", count);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
handleIncrement = lambda e: any -> None { count = count + 1;};
|
|
22
|
+
|
|
23
|
+
handleDecrement = lambda e: any -> None { count = count - 1;};
|
|
24
|
+
|
|
25
|
+
handleReset = lambda e: any -> None { count = 0;};
|
|
26
|
+
|
|
27
|
+
return
|
|
28
|
+
<Box
|
|
29
|
+
sx={{
|
|
30
|
+
"display": "flex",
|
|
31
|
+
"justifyContent": "center",
|
|
32
|
+
"alignItems": "center",
|
|
33
|
+
"minHeight": "100vh",
|
|
34
|
+
"backgroundColor": "#f5f5f5"
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<Card sx={{"minWidth": 400, "boxShadow": 3}}>
|
|
38
|
+
<CardContent>
|
|
39
|
+
<Typography
|
|
40
|
+
variant="h4"
|
|
41
|
+
component="div"
|
|
42
|
+
gutterBottom
|
|
43
|
+
sx={{"textAlign": "center", "color": "primary.main"}}
|
|
44
|
+
>
|
|
45
|
+
Counter Application
|
|
46
|
+
</Typography>
|
|
47
|
+
<Divider sx={{"my": 2}} />
|
|
48
|
+
<Box
|
|
49
|
+
sx={{
|
|
50
|
+
"display": "flex",
|
|
51
|
+
"flexDirection": "column",
|
|
52
|
+
"alignItems": "center",
|
|
53
|
+
"py": 3
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<Typography
|
|
57
|
+
variant="h6"
|
|
58
|
+
color="text.secondary"
|
|
59
|
+
gutterBottom
|
|
60
|
+
>
|
|
61
|
+
Current Count
|
|
62
|
+
</Typography>
|
|
63
|
+
<Typography
|
|
64
|
+
variant="h1"
|
|
65
|
+
component="div"
|
|
66
|
+
sx={{
|
|
67
|
+
"fontWeight": "bold",
|
|
68
|
+
"color": "success.main"
|
|
69
|
+
if count > 0
|
|
70
|
+
else "error.main" if count < 0 else "text.primary"
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{count}
|
|
74
|
+
</Typography>
|
|
75
|
+
</Box>
|
|
76
|
+
</CardContent>
|
|
77
|
+
<Divider />
|
|
78
|
+
<CardActions
|
|
79
|
+
sx={{
|
|
80
|
+
"display": "flex",
|
|
81
|
+
"justifyContent": "space-around",
|
|
82
|
+
"p": 2
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<IconButton
|
|
86
|
+
color="error"
|
|
87
|
+
size="large"
|
|
88
|
+
onClick={handleDecrement}
|
|
89
|
+
sx={{"border": "2px solid", "borderColor": "error.main"}}
|
|
90
|
+
>
|
|
91
|
+
<RemoveIcon />
|
|
92
|
+
</IconButton>
|
|
93
|
+
<IconButton
|
|
94
|
+
color="primary"
|
|
95
|
+
size="large"
|
|
96
|
+
onClick={handleReset}
|
|
97
|
+
sx={{"border": "2px solid", "borderColor": "primary.main"}}
|
|
98
|
+
>
|
|
99
|
+
<RefreshIcon />
|
|
100
|
+
</IconButton>
|
|
101
|
+
<IconButton
|
|
102
|
+
color="success"
|
|
103
|
+
size="large"
|
|
104
|
+
onClick={handleIncrement}
|
|
105
|
+
sx={{"border": "2px solid", "borderColor": "success.main"}}
|
|
106
|
+
>
|
|
107
|
+
<AddIcon />
|
|
108
|
+
</IconButton>
|
|
109
|
+
</CardActions>
|
|
110
|
+
<Box sx={{"px": 3, "pb": 2}}>
|
|
111
|
+
<Typography
|
|
112
|
+
variant="caption"
|
|
113
|
+
color="text.secondary"
|
|
114
|
+
sx={{"display": "block", "textAlign": "center"}}
|
|
115
|
+
>
|
|
116
|
+
Click the buttons to increment, decrement, or reset the counter
|
|
117
|
+
</Typography>
|
|
118
|
+
</Box>
|
|
119
|
+
</Card>
|
|
120
|
+
</Box>;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import ".styles.css";
|
|
3
|
+
|
|
4
|
+
cl {
|
|
5
|
+
def:pub app -> any {
|
|
6
|
+
has count: int = 0;
|
|
7
|
+
|
|
8
|
+
can with count entry {
|
|
9
|
+
console.log("Count changed: ", count);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
handleIncrement = lambda e: any -> None { count = count + 1;};
|
|
13
|
+
|
|
14
|
+
handleDecrement = lambda e: any -> None { count = count - 1;};
|
|
15
|
+
|
|
16
|
+
handleReset = lambda e: any -> None { count = 0;};
|
|
17
|
+
|
|
18
|
+
countClass = "countDisplay " + (
|
|
19
|
+
"positive" if count > 0 else "negative" if count < 0 else "zero"
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return
|
|
23
|
+
<div className="container">
|
|
24
|
+
<div className="card">
|
|
25
|
+
<h1 className="title">
|
|
26
|
+
Counter Application
|
|
27
|
+
</h1>
|
|
28
|
+
<div className="divider"></div>
|
|
29
|
+
<div className="counterSection">
|
|
30
|
+
<div className="label">
|
|
31
|
+
Current Count
|
|
32
|
+
</div>
|
|
33
|
+
<div className={countClass}>
|
|
34
|
+
{count}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="divider"></div>
|
|
38
|
+
<div className="buttonGroup">
|
|
39
|
+
<button className="btn btnDecrement" onClick={handleDecrement}>
|
|
40
|
+
-
|
|
41
|
+
</button>
|
|
42
|
+
<button className="btn btnReset" onClick={handleReset}>
|
|
43
|
+
↻
|
|
44
|
+
</button>
|
|
45
|
+
<button className="btn btnIncrement" onClick={handleIncrement}>
|
|
46
|
+
+
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="hint">
|
|
50
|
+
Click the buttons to increment, decrement, or reset the counter
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import ".styles.scss";
|
|
3
|
+
|
|
4
|
+
cl {
|
|
5
|
+
def:pub app -> any {
|
|
6
|
+
has count: int = 0;
|
|
7
|
+
|
|
8
|
+
can with count entry {
|
|
9
|
+
console.log("Count changed: ", count);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
handleIncrement = lambda e: any -> None { count = count + 1;};
|
|
13
|
+
|
|
14
|
+
handleDecrement = lambda e: any -> None { count = count - 1;};
|
|
15
|
+
|
|
16
|
+
handleReset = lambda e: any -> None { count = 0;};
|
|
17
|
+
|
|
18
|
+
countClass = "countDisplay " + (
|
|
19
|
+
"positive" if count > 0 else "negative" if count < 0 else "zero"
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return
|
|
23
|
+
<div className="container">
|
|
24
|
+
<div className="card">
|
|
25
|
+
<h1 className="title">
|
|
26
|
+
Counter Application
|
|
27
|
+
</h1>
|
|
28
|
+
<div className="divider"></div>
|
|
29
|
+
<div className="counterSection">
|
|
30
|
+
<div className="label">
|
|
31
|
+
Current Count
|
|
32
|
+
</div>
|
|
33
|
+
<div className={countClass}>
|
|
34
|
+
{count}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="divider"></div>
|
|
38
|
+
<div className="buttonGroup">
|
|
39
|
+
<button className="btn btnDecrement" onClick={handleDecrement}>
|
|
40
|
+
-
|
|
41
|
+
</button>
|
|
42
|
+
<button className="btn btnReset" onClick={handleReset}>
|
|
43
|
+
↻
|
|
44
|
+
</button>
|
|
45
|
+
<button className="btn btnIncrement" onClick={handleIncrement}>
|
|
46
|
+
+
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="hint">
|
|
50
|
+
Click the buttons to increment, decrement, or reset the counter
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>;
|
|
54
|
+
}
|
|
55
|
+
}
|