jac-client 0.2.3__py3-none-any.whl → 0.2.6__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/assets/workers/worker.py +5 -0
- jac_client/examples/all-in-one/src/app.jac +841 -0
- jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
- jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
- jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
- jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
- jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +3 -3
- jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/basic-auth/src/app.jac +377 -0
- jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
- jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
- jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
- jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
- jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
- jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
- jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
- jac_client/examples/little-x/src/submit-button.jac +16 -0
- jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
- jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
- jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
- jac_client/examples/ts-support/src/app.jac +35 -0
- jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
- jac_client/plugin/cli.jac +547 -0
- jac_client/plugin/client.jac +52 -0
- jac_client/plugin/client_runtime.cl.jac +38 -0
- jac_client/plugin/impl/client.impl.jac +134 -0
- jac_client/plugin/impl/client_runtime.impl.jac +177 -0
- jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
- jac_client/plugin/plugin_config.jac +195 -0
- jac_client/plugin/src/__init__.jac +20 -0
- jac_client/plugin/src/asset_processor.jac +33 -0
- jac_client/plugin/src/babel_processor.jac +18 -0
- jac_client/plugin/src/compiler.jac +66 -0
- jac_client/plugin/src/config_loader.jac +32 -0
- jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
- jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
- jac_client/plugin/src/impl/compiler.impl.jac +251 -0
- jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
- jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
- jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
- jac_client/plugin/src/import_processor.jac +19 -0
- jac_client/plugin/src/jac_to_js.jac +35 -0
- jac_client/plugin/src/package_installer.jac +26 -0
- jac_client/plugin/src/vite_bundler.jac +36 -0
- jac_client/plugin/vite_client_bundle.jac +31 -0
- jac_client/tests/conftest.py +281 -0
- jac_client/tests/fixtures/basic-app/app.jac +2 -2
- jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
- jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
- jac_client/tests/fixtures/js_import/app.jac +5 -5
- jac_client/tests/fixtures/spawn_test/app.jac +7 -7
- jac_client/tests/fixtures/with-ts/app.jac +35 -0
- jac_client/tests/test_cli.py +755 -0
- jac_client/tests/test_it.py +347 -67
- {jac_client-0.2.3.dist-info → jac_client-0.2.6.dist-info}/METADATA +28 -30
- jac_client-0.2.6.dist-info/RECORD +74 -0
- {jac_client-0.2.3.dist-info → jac_client-0.2.6.dist-info}/WHEEL +2 -1
- jac_client-0.2.6.dist-info/entry_points.txt +4 -0
- jac_client-0.2.6.dist-info/top_level.txt +1 -0
- jac_client/docs/README.md +0 -689
- jac_client/docs/advanced-state.md +0 -1265
- jac_client/docs/asset-serving/intro.md +0 -209
- jac_client/docs/assets/pipe_line-v2.svg +0 -32
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/file-system/app.jac.md +0 -121
- jac_client/docs/file-system/backend-frontend.md +0 -217
- jac_client/docs/file-system/intro.md +0 -72
- jac_client/docs/file-system/nested-imports.md +0 -348
- jac_client/docs/guide-example/intro.md +0 -115
- jac_client/docs/guide-example/step-01-setup.md +0 -270
- jac_client/docs/guide-example/step-02-components.md +0 -416
- jac_client/docs/guide-example/step-03-styling.md +0 -478
- jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
- jac_client/docs/guide-example/step-05-local-state.md +0 -530
- jac_client/docs/guide-example/step-06-events.md +0 -749
- jac_client/docs/guide-example/step-07-effects.md +0 -468
- jac_client/docs/guide-example/step-08-walkers.md +0 -534
- jac_client/docs/guide-example/step-09-authentication.md +0 -586
- jac_client/docs/guide-example/step-10-routing.md +0 -539
- jac_client/docs/guide-example/step-11-final.md +0 -963
- jac_client/docs/imports.md +0 -1141
- jac_client/docs/lifecycle-hooks.md +0 -773
- jac_client/docs/routing.md +0 -659
- jac_client/docs/styling/intro.md +0 -249
- jac_client/docs/styling/js-styling.md +0 -367
- jac_client/docs/styling/material-ui.md +0 -341
- jac_client/docs/styling/pure-css.md +0 -299
- jac_client/docs/styling/sass.md +0 -403
- jac_client/docs/styling/styled-components.md +0 -395
- jac_client/docs/styling/tailwind.md +0 -298
- jac_client/examples/all-in-one/.babelrc +0 -9
- jac_client/examples/all-in-one/README.md +0 -16
- jac_client/examples/all-in-one/app.jac +0 -426
- jac_client/examples/all-in-one/assets/burger.png +0 -0
- jac_client/examples/all-in-one/package.json +0 -29
- jac_client/examples/all-in-one/styles.css +0 -26
- jac_client/examples/all-in-one/vite.config.js +0 -28
- jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
- jac_client/examples/asset-serving/css-with-image/README.md +0 -91
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +0 -28
- jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
- jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
- jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
- jac_client/examples/asset-serving/image-asset/README.md +0 -119
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +0 -28
- jac_client/examples/asset-serving/image-asset/styles.css +0 -26
- jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
- jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
- jac_client/examples/asset-serving/import-alias/README.md +0 -83
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +0 -28
- jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
- jac_client/examples/basic/.babelrc +0 -9
- jac_client/examples/basic/README.md +0 -16
- jac_client/examples/basic/package.json +0 -27
- jac_client/examples/basic/vite.config.js +0 -27
- jac_client/examples/basic-auth/.babelrc +0 -9
- jac_client/examples/basic-auth/README.md +0 -16
- jac_client/examples/basic-auth/app.jac +0 -308
- jac_client/examples/basic-auth/package.json +0 -27
- jac_client/examples/basic-auth/vite.config.js +0 -27
- jac_client/examples/basic-auth-with-router/.babelrc +0 -9
- jac_client/examples/basic-auth-with-router/README.md +0 -60
- jac_client/examples/basic-auth-with-router/package.json +0 -28
- jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
- jac_client/examples/basic-full-stack/.babelrc +0 -9
- jac_client/examples/basic-full-stack/README.md +0 -18
- jac_client/examples/basic-full-stack/package.json +0 -28
- jac_client/examples/basic-full-stack/vite.config.js +0 -27
- jac_client/examples/css-styling/js-styling/.babelrc +0 -9
- jac_client/examples/css-styling/js-styling/README.md +0 -183
- jac_client/examples/css-styling/js-styling/package.json +0 -28
- jac_client/examples/css-styling/js-styling/styles.js +0 -100
- jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
- jac_client/examples/css-styling/material-ui/.babelrc +0 -9
- jac_client/examples/css-styling/material-ui/README.md +0 -16
- jac_client/examples/css-styling/material-ui/package.json +0 -32
- jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
- jac_client/examples/css-styling/pure-css/.babelrc +0 -9
- jac_client/examples/css-styling/pure-css/README.md +0 -16
- jac_client/examples/css-styling/pure-css/package.json +0 -28
- jac_client/examples/css-styling/pure-css/styles.css +0 -111
- jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
- jac_client/examples/css-styling/sass-example/.babelrc +0 -9
- jac_client/examples/css-styling/sass-example/README.md +0 -16
- jac_client/examples/css-styling/sass-example/package.json +0 -29
- jac_client/examples/css-styling/sass-example/styles.scss +0 -153
- jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
- jac_client/examples/css-styling/styled-components/.babelrc +0 -9
- jac_client/examples/css-styling/styled-components/README.md +0 -16
- jac_client/examples/css-styling/styled-components/package.json +0 -29
- jac_client/examples/css-styling/styled-components/styled.js +0 -90
- jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
- jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
- jac_client/examples/css-styling/tailwind-example/README.md +0 -16
- jac_client/examples/css-styling/tailwind-example/global.css +0 -1
- jac_client/examples/css-styling/tailwind-example/package.json +0 -30
- jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
- jac_client/examples/full-stack-with-auth/.babelrc +0 -9
- jac_client/examples/full-stack-with-auth/README.md +0 -16
- jac_client/examples/full-stack-with-auth/package.json +0 -28
- jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
- jac_client/examples/little-x/package.json +0 -23
- jac_client/examples/little-x/submit-button.jac +0 -8
- jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-advance/README.md +0 -77
- jac_client/examples/nested-folders/nested-advance/package.json +0 -29
- jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
- jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-basic/README.md +0 -183
- jac_client/examples/nested-folders/nested-basic/app.js +0 -7
- jac_client/examples/nested-folders/nested-basic/package.json +0 -28
- jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
- jac_client/examples/with-router/.babelrc +0 -9
- jac_client/examples/with-router/README.md +0 -17
- jac_client/examples/with-router/package.json +0 -28
- jac_client/examples/with-router/vite.config.js +0 -27
- jac_client/plugin/cli.py +0 -244
- jac_client/plugin/client.py +0 -152
- jac_client/plugin/client_runtime.jac +0 -234
- jac_client/plugin/vite_client_bundle.py +0 -503
- jac_client/tests/fixtures/js_import/utils.js +0 -21
- jac_client/tests/fixtures/package-lock.json +0 -329
- jac_client/tests/fixtures/package.json +0 -11
- jac_client/tests/test_asset_examples.py +0 -322
- jac_client/tests/test_cl.py +0 -530
- jac_client/tests/test_create_jac_app.py +0 -131
- jac_client/tests/test_nested_file.py +0 -374
- jac_client-0.2.3.dist-info/RECORD +0 -171
- jac_client-0.2.3.dist-info/entry_points.txt +0 -4
|
@@ -1,477 +0,0 @@
|
|
|
1
|
-
# Step 4: Building the Complete Todo UI
|
|
2
|
-
|
|
3
|
-
> ** Quick Tip:** Each step has two parts. **Part 1** shows you what to build. **Part 2** explains why it works. Want to just build? Skip all Part 2 sections!
|
|
4
|
-
|
|
5
|
-
In this step, you'll put all your components together to create the full todo application interface!
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Part 1: Building the App
|
|
10
|
-
|
|
11
|
-
### Step 4.1: Complete App with All Components
|
|
12
|
-
|
|
13
|
-
Let's build the complete UI. Replace your entire `app.jac` with:
|
|
14
|
-
|
|
15
|
-
```jac
|
|
16
|
-
cl {
|
|
17
|
-
# Component 1: Todo Input
|
|
18
|
-
def TodoInput(props: any) -> any {
|
|
19
|
-
return <div style={{
|
|
20
|
-
"display": "flex",
|
|
21
|
-
"gap": "8px",
|
|
22
|
-
"marginBottom": "16px"
|
|
23
|
-
}}>
|
|
24
|
-
<input
|
|
25
|
-
type="text"
|
|
26
|
-
placeholder="What needs to be done?"
|
|
27
|
-
style={{
|
|
28
|
-
"flex": "1",
|
|
29
|
-
"padding": "8px",
|
|
30
|
-
"border": "1px solid #ddd",
|
|
31
|
-
"borderRadius": "4px"
|
|
32
|
-
}}
|
|
33
|
-
/>
|
|
34
|
-
<button style={{
|
|
35
|
-
"padding": "8px 16px",
|
|
36
|
-
"background": "#3b82f6",
|
|
37
|
-
"color": "#ffffff",
|
|
38
|
-
"border": "none",
|
|
39
|
-
"borderRadius": "4px",
|
|
40
|
-
"cursor": "pointer",
|
|
41
|
-
"fontWeight": "600"
|
|
42
|
-
}}>
|
|
43
|
-
Add
|
|
44
|
-
</button>
|
|
45
|
-
</div>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# Component 2: Filter Buttons
|
|
49
|
-
def TodoFilters(props: any) -> any {
|
|
50
|
-
return <div style={{
|
|
51
|
-
"display": "flex",
|
|
52
|
-
"gap": "8px",
|
|
53
|
-
"marginBottom": "16px"
|
|
54
|
-
}}>
|
|
55
|
-
<button style={{
|
|
56
|
-
"padding": "6px 12px",
|
|
57
|
-
"background": "#3b82f6",
|
|
58
|
-
"color": "#ffffff",
|
|
59
|
-
"border": "none",
|
|
60
|
-
"borderRadius": "4px",
|
|
61
|
-
"cursor": "pointer",
|
|
62
|
-
"fontSize": "14px"
|
|
63
|
-
}}>
|
|
64
|
-
All
|
|
65
|
-
</button>
|
|
66
|
-
<button style={{
|
|
67
|
-
"padding": "6px 12px",
|
|
68
|
-
"background": "#e5e7eb",
|
|
69
|
-
"color": "#000000",
|
|
70
|
-
"border": "none",
|
|
71
|
-
"borderRadius": "4px",
|
|
72
|
-
"cursor": "pointer",
|
|
73
|
-
"fontSize": "14px"
|
|
74
|
-
}}>
|
|
75
|
-
Active
|
|
76
|
-
</button>
|
|
77
|
-
<button style={{
|
|
78
|
-
"padding": "6px 12px",
|
|
79
|
-
"background": "#e5e7eb",
|
|
80
|
-
"color": "#000000",
|
|
81
|
-
"border": "none",
|
|
82
|
-
"borderRadius": "4px",
|
|
83
|
-
"cursor": "pointer",
|
|
84
|
-
"fontSize": "14px"
|
|
85
|
-
}}>
|
|
86
|
-
Completed
|
|
87
|
-
</button>
|
|
88
|
-
</div>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# Component 3: Single Todo Item
|
|
92
|
-
def TodoItem(props: any) -> any {
|
|
93
|
-
return <div style={{
|
|
94
|
-
"display": "flex",
|
|
95
|
-
"alignItems": "center",
|
|
96
|
-
"gap": "10px",
|
|
97
|
-
"padding": "10px",
|
|
98
|
-
"borderBottom": "1px solid #e5e7eb"
|
|
99
|
-
}}>
|
|
100
|
-
<input
|
|
101
|
-
type="checkbox"
|
|
102
|
-
checked={props.done}
|
|
103
|
-
style={{"cursor": "pointer"}}
|
|
104
|
-
/>
|
|
105
|
-
<span style={{
|
|
106
|
-
"flex": "1",
|
|
107
|
-
"textDecoration": ("line-through" if props.done else "none"),
|
|
108
|
-
"color": ("#999" if props.done else "#000")
|
|
109
|
-
}}>
|
|
110
|
-
{props.text}
|
|
111
|
-
</span>
|
|
112
|
-
<button style={{
|
|
113
|
-
"padding": "4px 8px",
|
|
114
|
-
"background": "#ef4444",
|
|
115
|
-
"color": "#ffffff",
|
|
116
|
-
"border": "none",
|
|
117
|
-
"borderRadius": "4px",
|
|
118
|
-
"cursor": "pointer",
|
|
119
|
-
"fontSize": "12px"
|
|
120
|
-
}}>
|
|
121
|
-
Delete
|
|
122
|
-
</button>
|
|
123
|
-
</div>;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
# Component 4: Todo List (renders multiple TodoItems)
|
|
127
|
-
def TodoList(props: any) -> any {
|
|
128
|
-
return <div>
|
|
129
|
-
<TodoItem text="Learn Jac basics" done={true} />
|
|
130
|
-
<TodoItem text="Build a todo app" done={false} />
|
|
131
|
-
<TodoItem text="Deploy to production" done={false} />
|
|
132
|
-
</div>;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
# Main App
|
|
136
|
-
def app() -> any {
|
|
137
|
-
return <div style={{
|
|
138
|
-
"maxWidth": "600px",
|
|
139
|
-
"margin": "20px auto",
|
|
140
|
-
"padding": "20px",
|
|
141
|
-
"background": "#ffffff",
|
|
142
|
-
"borderRadius": "8px",
|
|
143
|
-
"boxShadow": "0 2px 4px rgba(0,0,0,0.1)"
|
|
144
|
-
}}>
|
|
145
|
-
<h1 style={{"marginBottom": "20px"}}>My Todos</h1>
|
|
146
|
-
<TodoInput />
|
|
147
|
-
<TodoFilters />
|
|
148
|
-
<TodoList />
|
|
149
|
-
|
|
150
|
-
# Stats footer
|
|
151
|
-
<div style={{
|
|
152
|
-
"marginTop": "16px",
|
|
153
|
-
"padding": "10px",
|
|
154
|
-
"background": "#f9fafb",
|
|
155
|
-
"borderRadius": "4px",
|
|
156
|
-
"fontSize": "14px",
|
|
157
|
-
"color": "#666"
|
|
158
|
-
}}>
|
|
159
|
-
2 items left
|
|
160
|
-
</div>
|
|
161
|
-
</div>;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Try it!** You should now see a complete todo application interface!
|
|
167
|
-
|
|
168
|
-
It looks like a real app, but clicking buttons won't do anything yet (we'll add that next).
|
|
169
|
-
|
|
170
|
-
### Step 4.2: Add Empty State
|
|
171
|
-
|
|
172
|
-
What if there are no todos? Let's handle that:
|
|
173
|
-
|
|
174
|
-
```jac
|
|
175
|
-
cl {
|
|
176
|
-
# ... (keep all previous components)
|
|
177
|
-
|
|
178
|
-
# Updated TodoList with empty state
|
|
179
|
-
def TodoList(props: any) -> any {
|
|
180
|
-
# For now, we'll manually control this
|
|
181
|
-
let hasTodos = true; # Change to false to see empty state
|
|
182
|
-
|
|
183
|
-
if not hasTodos {
|
|
184
|
-
return <div style={{
|
|
185
|
-
"padding": "20px",
|
|
186
|
-
"textAlign": "center",
|
|
187
|
-
"color": "#999"
|
|
188
|
-
}}>
|
|
189
|
-
No todos yet. Add one above!
|
|
190
|
-
</div>;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return <div>
|
|
194
|
-
<TodoItem text="Learn Jac basics" done={true} />
|
|
195
|
-
<TodoItem text="Build a todo app" done={false} />
|
|
196
|
-
<TodoItem text="Deploy to production" done={false} />
|
|
197
|
-
</div>;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
# ... (rest of the code stays the same)
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
**Try it!** Change `hasTodos = true` to `hasTodos = false` and see the empty state message.
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
**⏭ Want to skip the theory?** Jump to [Step 5: Local State](./step-05-local-state.md)
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## Part 2: Understanding the Concepts
|
|
213
|
-
|
|
214
|
-
### Component Hierarchy
|
|
215
|
-
|
|
216
|
-
Your app now has a clear structure:
|
|
217
|
-
|
|
218
|
-
```
|
|
219
|
-
app (main container)
|
|
220
|
-
├── h1 (title)
|
|
221
|
-
├── TodoInput (input field + button)
|
|
222
|
-
├── TodoFilters (All/Active/Completed buttons)
|
|
223
|
-
├── TodoList (container)
|
|
224
|
-
│ ├── TodoItem (Learn Jac)
|
|
225
|
-
│ ├── TodoItem (Build app)
|
|
226
|
-
│ └── TodoItem (Deploy)
|
|
227
|
-
└── div (stats footer)
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### Container Component Pattern
|
|
231
|
-
|
|
232
|
-
`TodoList` is a **container component** - it manages and renders multiple child components:
|
|
233
|
-
|
|
234
|
-
```jac
|
|
235
|
-
def TodoList() -> any {
|
|
236
|
-
return <div>
|
|
237
|
-
<TodoItem text="Task 1" done={false} />
|
|
238
|
-
<TodoItem text="Task 2" done={true} />
|
|
239
|
-
<TodoItem text="Task 3" done={false} />
|
|
240
|
-
</div>;
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
This pattern makes it easy to:
|
|
245
|
-
- Add/remove todos (just add/remove `<TodoItem>` components)
|
|
246
|
-
- Style the list container separately from items
|
|
247
|
-
- Handle empty states
|
|
248
|
-
|
|
249
|
-
### Props Flow (Top-Down)
|
|
250
|
-
|
|
251
|
-
Data flows **down** from parent to child through props:
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
app
|
|
255
|
-
└─> passes nothing to TodoList
|
|
256
|
-
└─> passes {text, done} to each TodoItem
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Right now, data is hard-coded. Later, we'll use **state** to make it dynamic.
|
|
260
|
-
|
|
261
|
-
### Conditional Rendering
|
|
262
|
-
|
|
263
|
-
We used a simple `if` statement to show/hide content:
|
|
264
|
-
|
|
265
|
-
```jac
|
|
266
|
-
if not hasTodos {
|
|
267
|
-
return <div>No todos yet!</div>;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return <div>
|
|
271
|
-
# Show todos
|
|
272
|
-
</div>;
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
This is called **conditional rendering** - showing different UI based on conditions.
|
|
276
|
-
|
|
277
|
-
**Other ways to do this:**
|
|
278
|
-
|
|
279
|
-
**Method 1: If/Else in component**
|
|
280
|
-
|
|
281
|
-
```jac
|
|
282
|
-
def TodoList() -> any {
|
|
283
|
-
if hasTodos {
|
|
284
|
-
return <div>Show todos</div>;
|
|
285
|
-
} else {
|
|
286
|
-
return <div>Empty state</div>;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
**Method 2: Ternary operator in JSX**
|
|
292
|
-
|
|
293
|
-
```jac
|
|
294
|
-
return <div>
|
|
295
|
-
{(
|
|
296
|
-
<div>Show todos</div>
|
|
297
|
-
) if hasTodos else (
|
|
298
|
-
<div>Empty state</div>
|
|
299
|
-
)}
|
|
300
|
-
</div>;
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
**Method 3: And operator (&&)**
|
|
304
|
-
|
|
305
|
-
```jac
|
|
306
|
-
return <div>
|
|
307
|
-
{(<div>Empty state</div>) if not hasTodos else None}
|
|
308
|
-
# Shows only when hasTodos is false
|
|
309
|
-
</div>;
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Use whichever feels clearest to you!
|
|
313
|
-
|
|
314
|
-
### Layout Strategy
|
|
315
|
-
|
|
316
|
-
Our app uses a centered card layout:
|
|
317
|
-
|
|
318
|
-
```jac
|
|
319
|
-
<div style={{
|
|
320
|
-
"maxWidth": "600px", # Don't get too wide
|
|
321
|
-
"margin": "20px auto", # Center horizontally
|
|
322
|
-
"padding": "20px", # Inner spacing
|
|
323
|
-
"background": "#ffffff", # White card
|
|
324
|
-
"borderRadius": "8px", # Rounded corners
|
|
325
|
-
"boxShadow": "0 2px 4px rgba(0,0,0,0.1)" # Subtle shadow
|
|
326
|
-
}}>
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
This creates a "card" effect that looks modern and professional.
|
|
330
|
-
|
|
331
|
-
### Spacing Between Components
|
|
332
|
-
|
|
333
|
-
We use `marginBottom` to add space between components:
|
|
334
|
-
|
|
335
|
-
```jac
|
|
336
|
-
<h1 style={{"marginBottom": "20px"}}>My Todos</h1>
|
|
337
|
-
# 20px gap here
|
|
338
|
-
<TodoInput /> # Has marginBottom: "16px" in its style
|
|
339
|
-
# 16px gap here
|
|
340
|
-
<TodoFilters /> # Has marginBottom: "16px" in its style
|
|
341
|
-
# 16px gap here
|
|
342
|
-
<TodoList />
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
This creates consistent vertical rhythm in your design.
|
|
346
|
-
|
|
347
|
-
### Color Scheme
|
|
348
|
-
|
|
349
|
-
Our app uses a consistent color palette:
|
|
350
|
-
|
|
351
|
-
```jac
|
|
352
|
-
{
|
|
353
|
-
"primary": "#3b82f6", # Blue (buttons, accents)
|
|
354
|
-
"danger": "#ef4444", # Red (delete button)
|
|
355
|
-
"background": "#ffffff", # White (main background)
|
|
356
|
-
"lightGray": "#f9fafb", # Light gray (stats footer)
|
|
357
|
-
"border": "#e5e7eb", # Gray border
|
|
358
|
-
"text": "#000", # Black text
|
|
359
|
-
"textMuted": "#999", # Gray text (completed todos)
|
|
360
|
-
"textLight": "#666" # Medium gray (stats)
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
Using consistent colors makes your app look polished!
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
## What You've Learned
|
|
369
|
-
|
|
370
|
-
- Building a complete UI by composing components
|
|
371
|
-
- Component hierarchy and organization
|
|
372
|
-
- Container components that render lists
|
|
373
|
-
- Conditional rendering for empty states
|
|
374
|
-
- Centered card layout pattern
|
|
375
|
-
- Consistent spacing and colors
|
|
376
|
-
- Props flow from parent to child
|
|
377
|
-
|
|
378
|
-
---
|
|
379
|
-
|
|
380
|
-
## Common Issues
|
|
381
|
-
|
|
382
|
-
### Issue: Components overlapping
|
|
383
|
-
|
|
384
|
-
**Solution**: Check that each component has proper margins/padding:
|
|
385
|
-
|
|
386
|
-
```jac
|
|
387
|
-
<TodoInput /> # Add marginBottom
|
|
388
|
-
<TodoFilters /> # Add marginBottom
|
|
389
|
-
<TodoList />
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### Issue: Layout looks broken
|
|
393
|
-
|
|
394
|
-
**Check:**
|
|
395
|
-
- Is `maxWidth` set on the container?
|
|
396
|
-
- Is `margin: "0 auto"` used for centering?
|
|
397
|
-
- Does the container have `padding`?
|
|
398
|
-
|
|
399
|
-
### Issue: Empty state not showing
|
|
400
|
-
|
|
401
|
-
**Check**: Make sure you're returning ONLY the empty state when there are no todos:
|
|
402
|
-
|
|
403
|
-
```jac
|
|
404
|
-
if not hasTodos {
|
|
405
|
-
return <div>Empty state</div>; # Return here, don't continue
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return <div>Show todos</div>; # This only runs if hasTodos is true
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
---
|
|
412
|
-
|
|
413
|
-
## Quick Exercise
|
|
414
|
-
|
|
415
|
-
Try customizing your app:
|
|
416
|
-
|
|
417
|
-
**1. Change the color scheme:**
|
|
418
|
-
|
|
419
|
-
```jac
|
|
420
|
-
# Change primary color from blue to purple
|
|
421
|
-
"background": "#8b5cf6" # Instead of "#3b82f6"
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
**2. Add more mock todos:**
|
|
425
|
-
|
|
426
|
-
```jac
|
|
427
|
-
def TodoList() -> any {
|
|
428
|
-
return <div>
|
|
429
|
-
<TodoItem text="Task 1" done={false} />
|
|
430
|
-
<TodoItem text="Task 2" done={true} />
|
|
431
|
-
<TodoItem text="Task 3" done={false} />
|
|
432
|
-
<TodoItem text="Task 4" done={false} />
|
|
433
|
-
<TodoItem text="Task 5" done={true} />
|
|
434
|
-
</div>;
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
**3. Add a header:**
|
|
439
|
-
|
|
440
|
-
```jac
|
|
441
|
-
def app() -> any {
|
|
442
|
-
return <div>
|
|
443
|
-
# Add a header
|
|
444
|
-
<div style={{
|
|
445
|
-
"textAlign": "center",
|
|
446
|
-
"padding": "20px",
|
|
447
|
-
"background": "#3b82f6",
|
|
448
|
-
"color": "white",
|
|
449
|
-
"marginBottom": "20px"
|
|
450
|
-
}}>
|
|
451
|
-
<h1 style={{"margin": "0"}}> Todo App</h1>
|
|
452
|
-
<p style={{"margin": "5px 0 0 0"}}>Stay organized!</p>
|
|
453
|
-
</div>
|
|
454
|
-
|
|
455
|
-
# Main content
|
|
456
|
-
<div style={{
|
|
457
|
-
"maxWidth": "600px",
|
|
458
|
-
"margin": "0 auto",
|
|
459
|
-
"padding": "20px"
|
|
460
|
-
}}>
|
|
461
|
-
<TodoInput />
|
|
462
|
-
<TodoFilters />
|
|
463
|
-
<TodoList />
|
|
464
|
-
</div>
|
|
465
|
-
</div>;
|
|
466
|
-
}
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
---
|
|
470
|
-
|
|
471
|
-
## Next Step
|
|
472
|
-
|
|
473
|
-
Excellent! Your UI is complete and looks great. But it's all static - clicking buttons does nothing!
|
|
474
|
-
|
|
475
|
-
In the next step, we'll add **state** to make your app interactive!
|
|
476
|
-
|
|
477
|
-
**[Continue to Step 5: Local State](./step-05-local-state.md)**
|