jac-client 0.2.8__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.
Files changed (119) hide show
  1. jac_client/examples/all-in-one/button.jac +4 -3
  2. jac_client/examples/all-in-one/components/CategoryFilter.jac +36 -24
  3. jac_client/examples/all-in-one/components/Header.jac +12 -8
  4. jac_client/examples/all-in-one/components/ProfitOverview.jac +49 -35
  5. jac_client/examples/all-in-one/components/Summary.jac +59 -36
  6. jac_client/examples/all-in-one/components/TransactionForm.jac +142 -112
  7. jac_client/examples/all-in-one/components/TransactionItem.jac +37 -30
  8. jac_client/examples/all-in-one/components/TransactionList.jac +33 -26
  9. jac_client/examples/all-in-one/components/button.jac +4 -3
  10. jac_client/examples/all-in-one/components/navigation.jac +111 -117
  11. jac_client/examples/all-in-one/constants/categories.jac +23 -24
  12. jac_client/examples/all-in-one/constants/clients.jac +7 -8
  13. jac_client/examples/all-in-one/context/BudgetContext.jac +9 -6
  14. jac_client/examples/all-in-one/hooks/useBudget.jac +18 -12
  15. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +14 -13
  16. jac_client/examples/all-in-one/main.jac +542 -0
  17. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +26 -12
  18. jac_client/examples/all-in-one/pages/FeaturesTest.jac +43 -12
  19. jac_client/examples/all-in-one/pages/LandingPage.jac +113 -90
  20. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
  21. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
  22. jac_client/examples/all-in-one/pages/loginPage.jac +114 -119
  23. jac_client/examples/all-in-one/pages/nestedDemo.jac +44 -51
  24. jac_client/examples/all-in-one/pages/notFound.jac +15 -21
  25. jac_client/examples/all-in-one/pages/signupPage.jac +113 -119
  26. jac_client/examples/all-in-one/utils/formatters.jac +5 -8
  27. jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
  28. jac_client/examples/asset-serving/image-asset/main.jac +56 -0
  29. jac_client/examples/asset-serving/import-alias/main.jac +109 -0
  30. jac_client/examples/basic/main.jac +23 -0
  31. jac_client/examples/basic-auth/main.jac +363 -0
  32. jac_client/examples/basic-auth-with-router/main.jac +451 -0
  33. jac_client/examples/basic-full-stack/main.jac +362 -0
  34. jac_client/examples/css-styling/js-styling/main.jac +63 -0
  35. jac_client/examples/css-styling/material-ui/main.jac +122 -0
  36. jac_client/examples/css-styling/pure-css/main.jac +55 -0
  37. jac_client/examples/css-styling/sass-example/main.jac +55 -0
  38. jac_client/examples/css-styling/styled-components/main.jac +62 -0
  39. jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
  40. jac_client/examples/full-stack-with-auth/main.jac +696 -0
  41. jac_client/examples/little-x/main.jac +681 -0
  42. jac_client/examples/little-x/src/submit-button.jac +15 -14
  43. jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
  44. jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
  45. jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
  46. jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
  47. jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
  48. jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
  49. jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
  50. jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
  51. jac_client/examples/ts-support/main.jac +35 -0
  52. jac_client/examples/with-router/main.jac +286 -0
  53. jac_client/plugin/cli.jac +491 -411
  54. jac_client/plugin/client.jac +25 -0
  55. jac_client/plugin/client_runtime.cl.jac +10 -4
  56. jac_client/plugin/impl/client.impl.jac +96 -55
  57. jac_client/plugin/impl/client_runtime.impl.jac +155 -1
  58. jac_client/plugin/plugin_config.jac +211 -29
  59. jac_client/plugin/src/__init__.jac +0 -2
  60. jac_client/plugin/src/compiler.jac +0 -1
  61. jac_client/plugin/src/config_loader.jac +1 -0
  62. jac_client/plugin/src/desktop_config.jac +31 -0
  63. jac_client/plugin/src/impl/compiler.impl.jac +49 -17
  64. jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
  65. jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
  66. jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
  67. jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
  68. jac_client/plugin/src/impl/vite_bundler.impl.jac +191 -64
  69. jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
  70. jac_client/plugin/src/targets/desktop_target.jac +37 -0
  71. jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
  72. jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
  73. jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
  74. jac_client/plugin/src/targets/register.jac +21 -0
  75. jac_client/plugin/src/targets/registry.jac +87 -0
  76. jac_client/plugin/src/targets/web_target.jac +35 -0
  77. jac_client/plugin/src/vite_bundler.jac +6 -0
  78. jac_client/plugin/utils/__init__.jac +3 -0
  79. jac_client/plugin/utils/bun_installer.jac +16 -0
  80. jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
  81. jac_client/templates/client.jacpack +72 -0
  82. jac_client/templates/fullstack.jacpack +61 -0
  83. jac_client/tests/conftest.py +103 -47
  84. jac_client/tests/fixtures/spawn_test/app.jac +49 -52
  85. jac_client/tests/fixtures/with-ts/app.jac +27 -27
  86. jac_client/tests/test_cli.py +182 -71
  87. jac_client/tests/test_e2e.py +232 -0
  88. jac_client/tests/test_helpers.py +58 -0
  89. jac_client/tests/test_it.py +91 -135
  90. jac_client/tests/test_it_desktop.py +891 -0
  91. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/METADATA +6 -6
  92. jac_client-0.2.11.dist-info/RECORD +113 -0
  93. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
  94. jac_client/examples/all-in-one/app.jac +0 -573
  95. jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +0 -70
  96. jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +0 -552
  97. jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
  98. jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
  99. jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
  100. jac_client/examples/basic/src/app.jac +0 -21
  101. jac_client/examples/basic-auth/src/app.jac +0 -371
  102. jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
  103. jac_client/examples/basic-full-stack/src/app.jac +0 -359
  104. jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
  105. jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
  106. jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
  107. jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
  108. jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
  109. jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
  110. jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
  111. jac_client/examples/little-x/src/app.jac +0 -719
  112. jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
  113. jac_client/examples/ts-support/src/app.jac +0 -35
  114. jac_client/examples/with-router/src/app.jac +0 -323
  115. jac_client/plugin/src/babel_processor.jac +0 -18
  116. jac_client/plugin/src/impl/babel_processor.impl.jac +0 -89
  117. jac_client-0.2.8.dist-info/RECORD +0 -97
  118. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
  119. {jac_client-0.2.8.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
+ }