jac-client 0.1.0__py3-none-any.whl → 0.2.1__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/docs/README.md +232 -172
- jac_client/docs/advanced-state.md +1012 -452
- jac_client/docs/asset-serving/intro.md +209 -0
- jac_client/docs/assets/pipe_line-v2.svg +32 -0
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/file-system/intro.md +90 -0
- jac_client/docs/guide-example/intro.md +117 -0
- jac_client/docs/guide-example/step-01-setup.md +260 -0
- jac_client/docs/guide-example/step-02-components.md +416 -0
- jac_client/docs/guide-example/step-03-styling.md +478 -0
- jac_client/docs/guide-example/step-04-todo-ui.md +477 -0
- jac_client/docs/guide-example/step-05-local-state.md +530 -0
- jac_client/docs/guide-example/step-06-events.md +750 -0
- jac_client/docs/guide-example/step-07-effects.md +469 -0
- jac_client/docs/guide-example/step-08-walkers.md +534 -0
- jac_client/docs/guide-example/step-09-authentication.md +586 -0
- jac_client/docs/guide-example/step-10-routing.md +540 -0
- jac_client/docs/guide-example/step-11-final.md +964 -0
- jac_client/docs/imports.md +538 -46
- jac_client/docs/lifecycle-hooks.md +517 -297
- jac_client/docs/routing.md +487 -357
- jac_client/docs/styling/intro.md +250 -0
- jac_client/docs/styling/js-styling.md +373 -0
- jac_client/docs/styling/material-ui.md +346 -0
- jac_client/docs/styling/pure-css.md +305 -0
- jac_client/docs/styling/sass.md +409 -0
- jac_client/docs/styling/styled-components.md +401 -0
- jac_client/docs/styling/tailwind.md +303 -0
- jac_client/examples/asset-serving/css-with-image/.babelrc +9 -0
- jac_client/examples/asset-serving/css-with-image/README.md +91 -0
- jac_client/examples/asset-serving/css-with-image/app.jac +67 -0
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +28 -0
- jac_client/examples/asset-serving/css-with-image/styles.css +27 -0
- jac_client/examples/asset-serving/css-with-image/vite.config.js +29 -0
- jac_client/examples/asset-serving/image-asset/.babelrc +9 -0
- jac_client/examples/asset-serving/image-asset/README.md +119 -0
- jac_client/examples/asset-serving/image-asset/app.jac +43 -0
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +28 -0
- jac_client/examples/asset-serving/image-asset/styles.css +27 -0
- jac_client/examples/asset-serving/image-asset/vite.config.js +29 -0
- jac_client/examples/asset-serving/import-alias/.babelrc +9 -0
- jac_client/examples/asset-serving/import-alias/README.md +83 -0
- jac_client/examples/asset-serving/import-alias/app.jac +57 -0
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +28 -0
- jac_client/examples/asset-serving/import-alias/vite.config.js +29 -0
- jac_client/examples/basic/.babelrc +9 -0
- jac_client/examples/basic/README.md +16 -0
- jac_client/examples/basic/app.jac +16 -0
- jac_client/examples/basic/package.json +27 -0
- jac_client/examples/basic/vite.config.js +28 -0
- jac_client/examples/basic-auth/.babelrc +9 -0
- jac_client/examples/basic-auth/README.md +16 -0
- jac_client/examples/basic-auth/app.jac +308 -0
- jac_client/examples/basic-auth/package.json +27 -0
- jac_client/examples/basic-auth/vite.config.js +28 -0
- jac_client/examples/basic-auth-with-router/.babelrc +9 -0
- jac_client/examples/basic-auth-with-router/README.md +60 -0
- jac_client/examples/basic-auth-with-router/app.jac +464 -0
- jac_client/examples/basic-auth-with-router/package.json +28 -0
- jac_client/examples/basic-auth-with-router/vite.config.js +28 -0
- jac_client/examples/basic-full-stack/.babelrc +9 -0
- jac_client/examples/basic-full-stack/README.md +18 -0
- jac_client/examples/basic-full-stack/app.jac +320 -0
- jac_client/examples/basic-full-stack/package.json +28 -0
- jac_client/examples/basic-full-stack/vite.config.js +28 -0
- jac_client/examples/css-styling/js-styling/.babelrc +9 -0
- jac_client/examples/css-styling/js-styling/README.md +183 -0
- jac_client/examples/css-styling/js-styling/app.jac +63 -0
- jac_client/examples/css-styling/js-styling/package.json +28 -0
- jac_client/examples/css-styling/js-styling/styles.js +100 -0
- jac_client/examples/css-styling/js-styling/vite.config.js +28 -0
- jac_client/examples/css-styling/material-ui/.babelrc +9 -0
- jac_client/examples/css-styling/material-ui/README.md +16 -0
- jac_client/examples/css-styling/material-ui/app.jac +82 -0
- jac_client/examples/css-styling/material-ui/package.json +32 -0
- jac_client/examples/css-styling/material-ui/vite.config.js +28 -0
- jac_client/examples/css-styling/pure-css/.babelrc +9 -0
- jac_client/examples/css-styling/pure-css/README.md +16 -0
- jac_client/examples/css-styling/pure-css/app.jac +63 -0
- jac_client/examples/css-styling/pure-css/package.json +28 -0
- jac_client/examples/css-styling/pure-css/styles.css +112 -0
- jac_client/examples/css-styling/pure-css/vite.config.js +28 -0
- jac_client/examples/css-styling/sass-example/.babelrc +9 -0
- jac_client/examples/css-styling/sass-example/README.md +16 -0
- jac_client/examples/css-styling/sass-example/app.jac +63 -0
- jac_client/examples/css-styling/sass-example/package.json +29 -0
- jac_client/examples/css-styling/sass-example/styles.scss +158 -0
- jac_client/examples/css-styling/sass-example/vite.config.js +28 -0
- jac_client/examples/css-styling/styled-components/.babelrc +9 -0
- jac_client/examples/css-styling/styled-components/README.md +16 -0
- jac_client/examples/css-styling/styled-components/app.jac +66 -0
- jac_client/examples/css-styling/styled-components/package.json +29 -0
- jac_client/examples/css-styling/styled-components/styled.js +91 -0
- jac_client/examples/css-styling/styled-components/vite.config.js +28 -0
- jac_client/examples/css-styling/tailwind-example/.babelrc +9 -0
- jac_client/examples/css-styling/tailwind-example/README.md +16 -0
- jac_client/examples/css-styling/tailwind-example/app.jac +64 -0
- jac_client/examples/css-styling/tailwind-example/global.css +1 -0
- jac_client/examples/css-styling/tailwind-example/package.json +30 -0
- jac_client/examples/css-styling/tailwind-example/vite.config.js +30 -0
- jac_client/examples/full-stack-with-auth/.babelrc +9 -0
- jac_client/examples/full-stack-with-auth/README.md +16 -0
- jac_client/examples/full-stack-with-auth/app.jac +735 -0
- jac_client/examples/full-stack-with-auth/package.json +28 -0
- jac_client/examples/full-stack-with-auth/vite.config.js +30 -0
- jac_client/examples/with-router/.babelrc +9 -0
- jac_client/examples/with-router/README.md +17 -0
- jac_client/examples/with-router/app.jac +323 -0
- jac_client/examples/with-router/package.json +28 -0
- jac_client/examples/with-router/vite.config.js +28 -0
- jac_client/plugin/cli.py +95 -179
- jac_client/plugin/client.py +111 -2
- jac_client/plugin/client_runtime.jac +183 -890
- jac_client/plugin/vite_client_bundle.py +185 -205
- jac_client/tests/__init__.py +0 -1
- jac_client/tests/fixtures/{client_app.jac → basic-app/app.jac} +1 -1
- jac_client/tests/fixtures/cl_file/app.cl.jac +38 -0
- jac_client/tests/fixtures/cl_file/app.jac +15 -0
- jac_client/tests/fixtures/{client_app_with_antd.jac → client_app_with_antd/app.jac} +7 -0
- jac_client/tests/fixtures/{js_import.jac → js_import/app.jac} +2 -2
- jac_client/tests/fixtures/{relative_import.jac → relative_import/app.jac} +1 -1
- jac_client/tests/fixtures/{button.jac → relative_import/button.jac} +2 -2
- jac_client/tests/fixtures/spawn_test/app.jac +133 -0
- jac_client/tests/fixtures/{test_fragments_spread.jac → test_fragments_spread/app.jac} +11 -2
- jac_client/tests/test_asset_examples.py +339 -0
- jac_client/tests/test_cl.py +345 -151
- jac_client/tests/test_create_jac_app.py +41 -45
- {jac_client-0.1.0.dist-info → jac_client-0.2.1.dist-info}/METADATA +72 -16
- jac_client-0.2.1.dist-info/RECORD +140 -0
- jac_client/examples/little-x/package-lock.json +0 -2840
- jac_client/examples/todo-app/README.md +0 -82
- jac_client/examples/todo-app/app.jac +0 -683
- jac_client/examples/todo-app/package-lock.json +0 -999
- jac_client/examples/todo-app/package.json +0 -22
- jac_client-0.1.0.dist-info/RECORD +0 -33
- /jac_client/tests/fixtures/{utils.js → js_import/utils.js} +0 -0
- {jac_client-0.1.0.dist-info → jac_client-0.2.1.dist-info}/WHEEL +0 -0
- {jac_client-0.1.0.dist-info → jac_client-0.2.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# Step 2: First Component
|
|
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 create your first reusable component - a **TodoItem** that displays a single todo.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🏗️ Part 1: Building the App
|
|
10
|
+
|
|
11
|
+
### Step 2.1: Create a TodoItem Component
|
|
12
|
+
|
|
13
|
+
Update your `app.jac`:
|
|
14
|
+
|
|
15
|
+
```jac
|
|
16
|
+
cl {
|
|
17
|
+
# A component to display a single todo
|
|
18
|
+
def TodoItem(props: any) -> any {
|
|
19
|
+
return <div>
|
|
20
|
+
<input type="checkbox" />
|
|
21
|
+
<span>Learn Jac basics</span>
|
|
22
|
+
<button>Delete</button>
|
|
23
|
+
</div>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def app() -> any {
|
|
27
|
+
return <div>
|
|
28
|
+
<h1>My Todos</h1>
|
|
29
|
+
<TodoItem />
|
|
30
|
+
</div>;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Try it!** Save and refresh your browser. You should see a todo item with a checkbox, text, and delete button.
|
|
36
|
+
|
|
37
|
+
### Step 2.2: Make It Reusable with Props
|
|
38
|
+
|
|
39
|
+
Now let's make the TodoItem display different text:
|
|
40
|
+
|
|
41
|
+
```jac
|
|
42
|
+
cl {
|
|
43
|
+
# Component that accepts data via props
|
|
44
|
+
def TodoItem(props: any) -> any {
|
|
45
|
+
return <div>
|
|
46
|
+
<input type="checkbox" checked={props.done} />
|
|
47
|
+
<span>{props.text}</span>
|
|
48
|
+
<button>Delete</button>
|
|
49
|
+
</div>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def app() -> any {
|
|
53
|
+
return <div>
|
|
54
|
+
<h1>My Todos</h1>
|
|
55
|
+
<TodoItem text="Learn Jac basics" done={false} />
|
|
56
|
+
<TodoItem text="Build a todo app" done={false} />
|
|
57
|
+
<TodoItem text="Deploy to production" done={true} />
|
|
58
|
+
</div>;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Try it!** You should now see three different todos. Notice how the third one has the checkbox checked!
|
|
64
|
+
|
|
65
|
+
### Step 2.3: Create Multiple Components
|
|
66
|
+
|
|
67
|
+
Let's add more components to organize our app:
|
|
68
|
+
|
|
69
|
+
```jac
|
|
70
|
+
cl {
|
|
71
|
+
# Component 1: TodoInput (input field + Add button)
|
|
72
|
+
def TodoInput(props: any) -> any {
|
|
73
|
+
return <div>
|
|
74
|
+
<input type="text" placeholder="What needs to be done?" />
|
|
75
|
+
<button>Add</button>
|
|
76
|
+
</div>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Component 2: TodoFilters (filter buttons)
|
|
80
|
+
def TodoFilters(props: any) -> any {
|
|
81
|
+
return <div>
|
|
82
|
+
<button>All</button>
|
|
83
|
+
<button>Active</button>
|
|
84
|
+
<button>Completed</button>
|
|
85
|
+
</div>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Component 3: TodoItem (single todo)
|
|
89
|
+
def TodoItem(props: any) -> any {
|
|
90
|
+
return <div>
|
|
91
|
+
<input type="checkbox" checked={props.done} />
|
|
92
|
+
<span>{props.text}</span>
|
|
93
|
+
<button>Delete</button>
|
|
94
|
+
</div>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Component 4: TodoList (list of todos)
|
|
98
|
+
def TodoList(props: any) -> any {
|
|
99
|
+
return <div>
|
|
100
|
+
<TodoItem text="Learn Jac basics" done={true} />
|
|
101
|
+
<TodoItem text="Build a todo app" done={false} />
|
|
102
|
+
<TodoItem text="Deploy to production" done={false} />
|
|
103
|
+
</div>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Main app - combines all components
|
|
107
|
+
def app() -> any {
|
|
108
|
+
return <div>
|
|
109
|
+
<h1>My Todos</h1>
|
|
110
|
+
<TodoInput />
|
|
111
|
+
<TodoFilters />
|
|
112
|
+
<TodoList />
|
|
113
|
+
</div>;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Try it!** Your app now has a clear structure with separate components.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
**⏭️ Want to skip the theory?** Jump to [Step 3: Styling](./step-03-styling.md)
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 💡 Part 2: Understanding the Concepts
|
|
127
|
+
|
|
128
|
+
### What is a Component?
|
|
129
|
+
|
|
130
|
+
A component is a **function that returns UI (JSX)**.
|
|
131
|
+
|
|
132
|
+
Think of components like Python functions:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
# Python - returns text
|
|
136
|
+
def greet_user(name):
|
|
137
|
+
return f"Hello, {name}!"
|
|
138
|
+
|
|
139
|
+
print(greet_user("Alice")) # Hello, Alice!
|
|
140
|
+
print(greet_user("Bob")) # Hello, Bob!
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```jac
|
|
144
|
+
# Jac - returns UI
|
|
145
|
+
def TodoItem(props: any) -> any {
|
|
146
|
+
return <div>{props.text}</div>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Usage
|
|
150
|
+
<TodoItem text="Learn Jac" />
|
|
151
|
+
<TodoItem text="Build app" />
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Why Use Components?
|
|
155
|
+
|
|
156
|
+
**1. Reusability** - Write once, use many times
|
|
157
|
+
|
|
158
|
+
```jac
|
|
159
|
+
<TodoItem text="Task 1" done={false} />
|
|
160
|
+
<TodoItem text="Task 2" done={true} />
|
|
161
|
+
<TodoItem text="Task 3" done={false} />
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**2. Organization** - Break complex UI into manageable pieces
|
|
165
|
+
|
|
166
|
+
```jac
|
|
167
|
+
app
|
|
168
|
+
├── TodoInput
|
|
169
|
+
├── TodoFilters
|
|
170
|
+
└── TodoList
|
|
171
|
+
├── TodoItem
|
|
172
|
+
├── TodoItem
|
|
173
|
+
└── TodoItem
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**3. Maintainability** - Easy to find and fix bugs
|
|
177
|
+
|
|
178
|
+
If there's a bug in how todos display, you know to check `TodoItem`.
|
|
179
|
+
|
|
180
|
+
### Component Naming Rules
|
|
181
|
+
|
|
182
|
+
**1. Use PascalCase** (first letter capitalized)
|
|
183
|
+
|
|
184
|
+
```jac
|
|
185
|
+
# ✅ Correct
|
|
186
|
+
def TodoItem() -> any { ... }
|
|
187
|
+
def UserProfile() -> any { ... }
|
|
188
|
+
def NavigationBar() -> any { ... }
|
|
189
|
+
|
|
190
|
+
# ❌ Wrong
|
|
191
|
+
def todoItem() -> any { ... } # camelCase
|
|
192
|
+
def user_profile() -> any { ... } # snake_case
|
|
193
|
+
def navigation-bar() -> any { ... } # kebab-case
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**2. Name describes what it does**
|
|
197
|
+
|
|
198
|
+
```jac
|
|
199
|
+
# ✅ Good names
|
|
200
|
+
def TodoItem() -> any { ... }
|
|
201
|
+
def LoginForm() -> any { ... }
|
|
202
|
+
def ProductCard() -> any { ... }
|
|
203
|
+
|
|
204
|
+
# ❌ Bad names
|
|
205
|
+
def Component1() -> any { ... }
|
|
206
|
+
def Thing() -> any { ... }
|
|
207
|
+
def X() -> any { ... }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Understanding Props
|
|
211
|
+
|
|
212
|
+
**Props** = "Properties" = Data passed to a component
|
|
213
|
+
|
|
214
|
+
```jac
|
|
215
|
+
# Passing props (like function arguments)
|
|
216
|
+
<TodoItem text="Learn Jac" done={false} />
|
|
217
|
+
|
|
218
|
+
# Receiving props (in the component)
|
|
219
|
+
def TodoItem(props: any) -> any {
|
|
220
|
+
let text = props.text; # "Learn Jac"
|
|
221
|
+
let done = props.done; # false
|
|
222
|
+
return <div>{text}</div>;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Important**: In React (which Jac uses), components receive props as a **single object**, not individual parameters.
|
|
227
|
+
|
|
228
|
+
```jac
|
|
229
|
+
# ✅ Correct way
|
|
230
|
+
def TodoItem(props: any) -> any {
|
|
231
|
+
let text = props.text;
|
|
232
|
+
let done = props.done;
|
|
233
|
+
# ...
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# ❌ Wrong way (won't work)
|
|
237
|
+
def TodoItem(text: str, done: bool) -> any {
|
|
238
|
+
# This doesn't work in React!
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Accessing Props
|
|
243
|
+
|
|
244
|
+
Three ways to access props:
|
|
245
|
+
|
|
246
|
+
**Method 1: Direct access**
|
|
247
|
+
|
|
248
|
+
```jac
|
|
249
|
+
def TodoItem(props: any) -> any {
|
|
250
|
+
return <span>{props.text}</span>;
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Method 2: Extract to variables**
|
|
255
|
+
|
|
256
|
+
```jac
|
|
257
|
+
def TodoItem(props: any) -> any {
|
|
258
|
+
let text = props.text;
|
|
259
|
+
let done = props.done;
|
|
260
|
+
return <span>{text}</span>;
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Method 3: Dictionary access (explicit)**
|
|
265
|
+
|
|
266
|
+
```jac
|
|
267
|
+
def TodoItem(props: any) -> any {
|
|
268
|
+
let text = props["text"];
|
|
269
|
+
let done = props["done"];
|
|
270
|
+
return <span>{text}</span>;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
All three ways work! Use whichever feels clearest to you.
|
|
275
|
+
|
|
276
|
+
### Composing Components
|
|
277
|
+
|
|
278
|
+
You can nest components inside other components:
|
|
279
|
+
|
|
280
|
+
```jac
|
|
281
|
+
# TodoList uses TodoItem
|
|
282
|
+
def TodoList() -> any {
|
|
283
|
+
return <div>
|
|
284
|
+
<TodoItem text="Task 1" done={false} />
|
|
285
|
+
<TodoItem text="Task 2" done={true} />
|
|
286
|
+
</div>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# App uses TodoList
|
|
290
|
+
def app() -> any {
|
|
291
|
+
return <div>
|
|
292
|
+
<h1>My Todos</h1>
|
|
293
|
+
<TodoList />
|
|
294
|
+
</div>;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This creates a hierarchy:
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
app
|
|
302
|
+
└── TodoList
|
|
303
|
+
├── TodoItem
|
|
304
|
+
└── TodoItem
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Using JSX in Props
|
|
308
|
+
|
|
309
|
+
You can pass any value as props:
|
|
310
|
+
|
|
311
|
+
```jac
|
|
312
|
+
# String
|
|
313
|
+
<TodoItem text="Hello" />
|
|
314
|
+
|
|
315
|
+
# Number
|
|
316
|
+
<TodoItem count={42} />
|
|
317
|
+
|
|
318
|
+
# Boolean
|
|
319
|
+
<TodoItem done={true} />
|
|
320
|
+
|
|
321
|
+
# Variable
|
|
322
|
+
let myText = "Learn Jac";
|
|
323
|
+
<TodoItem text={myText} />
|
|
324
|
+
|
|
325
|
+
# Expression
|
|
326
|
+
<TodoItem priority={5 + 3} />
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## ✅ What You've Learned
|
|
332
|
+
|
|
333
|
+
- ✅ Components are functions that return UI
|
|
334
|
+
- ✅ How to create a component
|
|
335
|
+
- ✅ PascalCase naming convention
|
|
336
|
+
- ✅ Passing data to components with props
|
|
337
|
+
- ✅ Receiving props as a single object
|
|
338
|
+
- ✅ Composing components (nesting)
|
|
339
|
+
- ✅ Organizing app into multiple components
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 🐛 Common Issues
|
|
344
|
+
|
|
345
|
+
### Issue: Component not showing up
|
|
346
|
+
|
|
347
|
+
**Check:**
|
|
348
|
+
- Is the name in PascalCase? `TodoItem` not `todoItem`
|
|
349
|
+
- Did you use `<TodoItem />` (with angle brackets)?
|
|
350
|
+
- Does it have a `return` statement?
|
|
351
|
+
|
|
352
|
+
### Issue: "object with keys {text, done}"
|
|
353
|
+
|
|
354
|
+
**Cause**: Using individual parameters instead of props object
|
|
355
|
+
|
|
356
|
+
```jac
|
|
357
|
+
# ❌ Wrong
|
|
358
|
+
def TodoItem(text: str, done: bool) -> any {
|
|
359
|
+
# ...
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# ✅ Correct
|
|
363
|
+
def TodoItem(props: any) -> any {
|
|
364
|
+
let text = props.text;
|
|
365
|
+
# ...
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Issue: Props are undefined
|
|
370
|
+
|
|
371
|
+
**Check:**
|
|
372
|
+
- Did you pass the props when using the component?
|
|
373
|
+
- Are the prop names spelled the same in both places?
|
|
374
|
+
|
|
375
|
+
```jac
|
|
376
|
+
# Passing props
|
|
377
|
+
<TodoItem text="Learn" done={false} />
|
|
378
|
+
|
|
379
|
+
# Receiving props (names must match!)
|
|
380
|
+
def TodoItem(props: any) -> any {
|
|
381
|
+
props.text # "Learn"
|
|
382
|
+
props.done # false
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 🎯 Quick Exercise
|
|
389
|
+
|
|
390
|
+
Try adding a new component:
|
|
391
|
+
|
|
392
|
+
```jac
|
|
393
|
+
def TodoStats(props: any) -> any {
|
|
394
|
+
return <div>
|
|
395
|
+
<p>Total: {props.total}</p>
|
|
396
|
+
<p>Completed: {props.completed}</p>
|
|
397
|
+
</div>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
# Use it in app
|
|
401
|
+
def app() -> any {
|
|
402
|
+
return <div>
|
|
403
|
+
<h1>My Todos</h1>
|
|
404
|
+
<TodoStats total={3} completed={1} />
|
|
405
|
+
# ... rest of your components
|
|
406
|
+
</div>;
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## ➡️ Next Step
|
|
413
|
+
|
|
414
|
+
Great! You can now create and organize components. But they look plain. Let's make them beautiful with **styling**!
|
|
415
|
+
|
|
416
|
+
👉 **[Continue to Step 3: Styling](./step-03-styling.md)**
|