jac-client 0.2.0__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 (72) hide show
  1. jac_client/docs/README.md +659 -0
  2. jac_client/docs/advanced-state.md +1266 -0
  3. jac_client/docs/assets/pipe_line.png +0 -0
  4. jac_client/docs/guide-example/intro.md +117 -0
  5. jac_client/docs/guide-example/step-01-setup.md +260 -0
  6. jac_client/docs/guide-example/step-02-components.md +416 -0
  7. jac_client/docs/guide-example/step-03-styling.md +478 -0
  8. jac_client/docs/guide-example/step-04-todo-ui.md +477 -0
  9. jac_client/docs/guide-example/step-05-local-state.md +530 -0
  10. jac_client/docs/guide-example/step-06-events.md +750 -0
  11. jac_client/docs/guide-example/step-07-effects.md +469 -0
  12. jac_client/docs/guide-example/step-08-walkers.md +534 -0
  13. jac_client/docs/guide-example/step-09-authentication.md +586 -0
  14. jac_client/docs/guide-example/step-10-routing.md +540 -0
  15. jac_client/docs/guide-example/step-11-final.md +964 -0
  16. jac_client/docs/imports.md +1142 -0
  17. jac_client/docs/lifecycle-hooks.md +774 -0
  18. jac_client/docs/routing.md +660 -0
  19. jac_client/examples/basic/.babelrc +9 -0
  20. jac_client/examples/basic/README.md +16 -0
  21. jac_client/examples/basic/app.jac +16 -0
  22. jac_client/examples/basic/package.json +27 -0
  23. jac_client/examples/basic/vite.config.js +28 -0
  24. jac_client/examples/basic-auth/.babelrc +9 -0
  25. jac_client/examples/basic-auth/README.md +16 -0
  26. jac_client/examples/basic-auth/app.jac +308 -0
  27. jac_client/examples/basic-auth/package.json +27 -0
  28. jac_client/examples/basic-auth/vite.config.js +28 -0
  29. jac_client/examples/basic-auth-with-router/.babelrc +9 -0
  30. jac_client/examples/basic-auth-with-router/README.md +60 -0
  31. jac_client/examples/basic-auth-with-router/app.jac +464 -0
  32. jac_client/examples/basic-auth-with-router/package.json +28 -0
  33. jac_client/examples/basic-auth-with-router/vite.config.js +28 -0
  34. jac_client/examples/basic-full-stack/.babelrc +9 -0
  35. jac_client/examples/basic-full-stack/README.md +18 -0
  36. jac_client/examples/basic-full-stack/app.jac +320 -0
  37. jac_client/examples/basic-full-stack/package.json +28 -0
  38. jac_client/examples/basic-full-stack/vite.config.js +28 -0
  39. jac_client/examples/full-stack-with-auth/.babelrc +9 -0
  40. jac_client/examples/full-stack-with-auth/README.md +16 -0
  41. jac_client/examples/full-stack-with-auth/app.jac +735 -0
  42. jac_client/examples/full-stack-with-auth/package.json +28 -0
  43. jac_client/examples/full-stack-with-auth/vite.config.js +30 -0
  44. jac_client/examples/little-x/app.jac +615 -0
  45. jac_client/examples/little-x/package.json +23 -0
  46. jac_client/examples/little-x/submit-button.jac +8 -0
  47. jac_client/examples/with-router/.babelrc +9 -0
  48. jac_client/examples/with-router/README.md +17 -0
  49. jac_client/examples/with-router/app.jac +323 -0
  50. jac_client/examples/with-router/package.json +28 -0
  51. jac_client/examples/with-router/vite.config.js +28 -0
  52. jac_client/plugin/cli.py +239 -0
  53. jac_client/plugin/client.py +89 -0
  54. jac_client/plugin/client_runtime.jac +234 -0
  55. jac_client/plugin/vite_client_bundle.py +355 -0
  56. jac_client/tests/__init__.py +2 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +18 -0
  58. jac_client/tests/fixtures/client_app_with_antd/app.jac +28 -0
  59. jac_client/tests/fixtures/js_import/app.jac +30 -0
  60. jac_client/tests/fixtures/js_import/utils.js +22 -0
  61. jac_client/tests/fixtures/package-lock.json +329 -0
  62. jac_client/tests/fixtures/package.json +11 -0
  63. jac_client/tests/fixtures/relative_import/app.jac +13 -0
  64. jac_client/tests/fixtures/relative_import/button.jac +6 -0
  65. jac_client/tests/fixtures/spawn_test/app.jac +133 -0
  66. jac_client/tests/fixtures/test_fragments_spread/app.jac +53 -0
  67. jac_client/tests/test_cl.py +476 -0
  68. jac_client/tests/test_create_jac_app.py +139 -0
  69. jac_client-0.2.0.dist-info/METADATA +182 -0
  70. jac_client-0.2.0.dist-info/RECORD +72 -0
  71. jac_client-0.2.0.dist-info/WHEEL +4 -0
  72. jac_client-0.2.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,540 @@
1
+ # Step 10: Adding Routing
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 add multiple pages to your app so users can navigate between login, signup, and todos!
6
+
7
+ ---
8
+
9
+ ## 🏗️ Part 1: Building the App
10
+
11
+ ### Step 10.1: Import Routing Components
12
+
13
+ Update your imports:
14
+
15
+ ```jac
16
+ cl import from react {useState, useEffect}
17
+ cl import from "@jac-client/utils" {
18
+ Router,
19
+ Routes,
20
+ Route,
21
+ Link,
22
+ Navigate,
23
+ useNavigate,
24
+ jacSignup,
25
+ jacLogin,
26
+ jacLogout,
27
+ jacIsLoggedIn
28
+ }
29
+
30
+ cl {
31
+ # ... your components
32
+ }
33
+ ```
34
+
35
+ ### Step 10.2: Add Navigation Links to Login Page
36
+
37
+ Update your `LoginPage` to include a link to signup:
38
+
39
+ ```jac
40
+ # In LoginPage, replace the last <p> tag with:
41
+ <p style={{
42
+ "textAlign": "center",
43
+ "marginTop": "12px",
44
+ "fontSize": "14px"
45
+ }}>
46
+ Need an account? <Link to="/signup">Sign up</Link>
47
+ </p>
48
+ ```
49
+
50
+ ### Step 10.3: Add Navigation Links to Signup Page
51
+
52
+ Update your `SignupPage`:
53
+
54
+ ```jac
55
+ # In SignupPage, replace the last <p> tag with:
56
+ <p style={{
57
+ "textAlign": "center",
58
+ "marginTop": "12px",
59
+ "fontSize": "14px"
60
+ }}>
61
+ Have an account? <Link to="/login">Login</Link>
62
+ </p>
63
+ ```
64
+
65
+ ### Step 10.4: Add Navigation After Login/Signup
66
+
67
+ Update the login handler to navigate to todos after successful login:
68
+
69
+ ```jac
70
+ # In LoginPage
71
+ async def handleLogin(e: any) -> None {
72
+ e.preventDefault();
73
+ setError("");
74
+
75
+ if not username or not password {
76
+ setError("Please fill in all fields");
77
+ return;
78
+ }
79
+
80
+ success = await jacLogin(username, password);
81
+ if success {
82
+ window.location.href = "/page/app#/todos"; # Navigate to todos
83
+ } else {
84
+ setError("Invalid credentials");
85
+ }
86
+ }
87
+ ```
88
+
89
+ Update the signup handler:
90
+
91
+ ```jac
92
+ # In SignupPage
93
+ async def handleSignup(e: any) -> None {
94
+ e.preventDefault();
95
+ setError("");
96
+
97
+ if not username or not password {
98
+ setError("Please fill in all fields");
99
+ return;
100
+ }
101
+
102
+ result = await jacSignup(username, password);
103
+ if result["success"] {
104
+ window.location.href = "/page/app#/todos"; # Navigate to todos
105
+ } else {
106
+ setError(result["error"] if result["error"] else "Signup failed");
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### Step 10.5: Create a Navigation Component
112
+
113
+ Add this component to show navigation at the top:
114
+
115
+ ```jac
116
+ def Navigation() -> any {
117
+ let isLoggedIn = jacIsLoggedIn();
118
+
119
+ if isLoggedIn {
120
+ return <nav style={{
121
+ "padding": "12px 24px",
122
+ "background": "#3b82f6",
123
+ "color": "#ffffff",
124
+ "display": "flex",
125
+ "justifyContent": "space-between"
126
+ }}>
127
+ <div style={{"fontWeight": "600"}}>Todo App</div>
128
+ <div style={{"display": "flex", "gap": "16px"}}>
129
+ <Link to="/todos" style={{
130
+ "color": "#ffffff",
131
+ "textDecoration": "none"
132
+ }}>
133
+ Todos
134
+ </Link>
135
+ <button
136
+ onClick={lambda e: any -> None {
137
+ e.preventDefault();
138
+ jacLogout();
139
+ window.location.href = "/page/app#/login";
140
+ }}
141
+ style={{
142
+ "background": "none",
143
+ "color": "#ffffff",
144
+ "border": "1px solid #ffffff",
145
+ "padding": "2px 10px",
146
+ "borderRadius": "4px",
147
+ "cursor": "pointer"
148
+ }}
149
+ >
150
+ Logout
151
+ </button>
152
+ </div>
153
+ </nav>;
154
+ }
155
+
156
+ return <nav style={{
157
+ "padding": "12px 24px",
158
+ "background": "#3b82f6",
159
+ "color": "#ffffff",
160
+ "display": "flex",
161
+ "justifyContent": "space-between"
162
+ }}>
163
+ <div style={{"fontWeight": "600"}}>Todo App</div>
164
+ <div style={{"display": "flex", "gap": "16px"}}>
165
+ <Link to="/login" style={{
166
+ "color": "#ffffff",
167
+ "textDecoration": "none"
168
+ }}>
169
+ Login
170
+ </Link>
171
+ <Link to="/signup" style={{
172
+ "color": "#ffffff",
173
+ "textDecoration": "none"
174
+ }}>
175
+ Sign Up
176
+ </Link>
177
+ </div>
178
+ </nav>;
179
+ }
180
+ ```
181
+
182
+ ### Step 10.6: Update TodosPage to Redirect if Not Logged In
183
+
184
+ Update your `TodosPage`:
185
+
186
+ ```jac
187
+ def TodosPage() -> any {
188
+ # Redirect to login if not logged in
189
+ if not jacIsLoggedIn() {
190
+ return <Navigate to="/login" />;
191
+ }
192
+
193
+ # ... all your existing todo code
194
+ }
195
+ ```
196
+
197
+ ### Step 10.7: Create a Home Page
198
+
199
+ Add this simple home page:
200
+
201
+ ```jac
202
+ def HomePage() -> any {
203
+ if jacIsLoggedIn() {
204
+ return <Navigate to="/todos" />;
205
+ }
206
+ return <Navigate to="/login" />;
207
+ }
208
+ ```
209
+
210
+ ### Step 10.8: Set Up Router in app()
211
+
212
+ Now, update your `app` function to use the router:
213
+
214
+ ```jac
215
+ def app() -> any {
216
+ return <Router>
217
+ <div style={{"fontFamily": "system-ui, sans-serif"}}>
218
+ <Navigation />
219
+ <Routes>
220
+ <Route path="/" element={<HomePage />} />
221
+ <Route path="/login" element={<LoginPage />} />
222
+ <Route path="/signup" element={<SignupPage />} />
223
+ <Route path="/todos" element={<TodosPage />} />
224
+ </Routes>
225
+ </div>
226
+ </Router>;
227
+ }
228
+ ```
229
+
230
+ **Try it!**
231
+
232
+ 1. Go to `http://localhost:8000/page/app` - you'll be redirected to login
233
+ 2. Click "Sign up" - goes to signup page
234
+ 3. Create an account - redirects to todos
235
+ 4. Click "Logout" - redirects to login
236
+ 5. Try manually going to `/page/app#/todos` while logged out - redirects to login!
237
+
238
+ ---
239
+
240
+ **⏭️ Want to skip the theory?** Jump to [Step 11: Final Integration](./step-11-final.md)
241
+
242
+ ---
243
+
244
+ ## 💡 Part 2: Understanding the Concepts
245
+
246
+ ### What is Routing?
247
+
248
+ Routing = Different URLs show different pages
249
+
250
+ **Traditional websites:**
251
+ - `/login` → Server sends login.html
252
+ - `/todos` → Server sends todos.html
253
+ - Every click = full page reload
254
+
255
+ **Single-Page Apps (SPAs):**
256
+ - `/login` → JavaScript shows login component
257
+ - `/todos` → JavaScript shows todos component
258
+ - No page reload = instant and smooth!
259
+
260
+ ### Router Components
261
+
262
+ ```jac
263
+ <Router> # Container for all routing
264
+ <Routes> # Groups route definitions
265
+ <Route /> # Defines one route
266
+ </Routes>
267
+ </Router>
268
+ ```
269
+
270
+ **Think of it like:**
271
+ ```python
272
+ # Python routing (Flask)
273
+ @app.route("/login")
274
+ def login():
275
+ return render_template("login.html")
276
+
277
+ # Jac routing
278
+ <Route path="/login" element={<LoginPage />} />
279
+ ```
280
+
281
+ ### The Router Setup
282
+
283
+ ```jac
284
+ <Router>
285
+ <div>
286
+ <Navigation /> # Shows on all pages
287
+ <Routes>
288
+ <Route path="/" element={<HomePage />} />
289
+ <Route path="/login" element={<LoginPage />} />
290
+ </Routes>
291
+ </div>
292
+ </Router>
293
+ ```
294
+
295
+ **What happens:**
296
+ - `Router` manages the current URL
297
+ - `Navigation` is always visible
298
+ - `Routes` shows ONE matching route
299
+ - Components render based on URL
300
+
301
+ ### Route Definitions
302
+
303
+ ```jac
304
+ <Route path="/login" element={<LoginPage />} />
305
+ ```
306
+
307
+ **Breakdown:**
308
+ - `path="/login"` - URL pattern to match
309
+ - `element={<LoginPage />}` - Component to show (must be JSX!)
310
+
311
+ **Important:** Use `element={<Component />}` not `element={Component}`
312
+
313
+ ### Link Component
314
+
315
+ ```jac
316
+ <Link to="/login">Go to Login</Link>
317
+ ```
318
+
319
+ **vs regular anchor tag:**
320
+
321
+ ```jac
322
+ # ❌ Wrong - causes page reload
323
+ <a href="/login">Go to Login</a>
324
+
325
+ # ✅ Correct - no page reload
326
+ <Link to="/login">Go to Login</Link>
327
+ ```
328
+
329
+ `Link` updates the URL without reloading the page!
330
+
331
+ ### Navigate Component
332
+
333
+ ```jac
334
+ def TodosPage() -> any {
335
+ if not jacIsLoggedIn() {
336
+ return <Navigate to="/login" />;
337
+ }
338
+ // Show todos
339
+ }
340
+ ```
341
+
342
+ **Purpose:** Redirect programmatically
343
+
344
+ **When to use:**
345
+ - Protecting routes (if not logged in, redirect)
346
+ - After form submission
347
+ - Conditional redirects
348
+
349
+ ### useNavigate Hook
350
+
351
+ ```jac
352
+ let navigate = useNavigate();
353
+
354
+ async def handleLogin() -> None {
355
+ let success = await jacLogin(username, password);
356
+ if success {
357
+ navigate("/todos"); # Navigate programmatically
358
+ }
359
+ }
360
+ ```
361
+
362
+ **Use when:** You need to navigate from JavaScript code (not from JSX)
363
+
364
+ ### URL Structure in Jac
365
+
366
+ ```
367
+ http://localhost:8000/page/app#/todos
368
+ ^^^^^^^ ^^^^^^
369
+ Jac app Route
370
+ ```
371
+
372
+ - `/page/app` - Your Jac app
373
+ - `#/todos` - Client-side route (hash routing)
374
+
375
+ ### Protected Routes Pattern
376
+
377
+ ```jac
378
+ def ProtectedPage() -> any {
379
+ if not jacIsLoggedIn() {
380
+ return <Navigate to="/login" />;
381
+ }
382
+
383
+ # User is authenticated, show page
384
+ return <div>Protected content</div>;
385
+ }
386
+ ```
387
+
388
+ This pattern:
389
+ 1. Checks authentication
390
+ 2. Redirects if not logged in
391
+ 3. Shows content if logged in
392
+
393
+ ### Conditional Navigation
394
+
395
+ ```jac
396
+ def Navigation() -> any {
397
+ let isLoggedIn = jacIsLoggedIn();
398
+
399
+ if isLoggedIn {
400
+ return <nav>
401
+ <Link to="/todos">Todos</Link>
402
+ <button onClick={logout}>Logout</button>
403
+ </nav>;
404
+ }
405
+
406
+ return <nav>
407
+ <Link to="/login">Login</Link>
408
+ <Link to="/signup">Sign Up</Link>
409
+ </nav>;
410
+ }
411
+ ```
412
+
413
+ Shows different links based on login status!
414
+
415
+ ### Route Matching
416
+
417
+ Routes are matched **in order**:
418
+
419
+ ```jac
420
+ <Routes>
421
+ <Route path="/" element={<HomePage />} />
422
+ <Route path="/login" element={<LoginPage />} />
423
+ <Route path="/todos" element={<TodosPage />} />
424
+ </Routes>
425
+ ```
426
+
427
+ - URL `/` → Shows `HomePage`
428
+ - URL `/login` → Shows `LoginPage`
429
+ - URL `/todos` → Shows `TodosPage`
430
+ - URL `/other` → Shows nothing (we could add a 404 page)
431
+
432
+ ### Common Routing Patterns
433
+
434
+ **Pattern 1: Auto-redirect based on auth**
435
+
436
+ ```jac
437
+ def HomePage() -> any {
438
+ if jacIsLoggedIn() {
439
+ return <Navigate to="/todos" />;
440
+ }
441
+ return <Navigate to="/login" />;
442
+ }
443
+ ```
444
+
445
+ **Pattern 2: Logout and redirect**
446
+
447
+ ```jac
448
+ def handleLogout() -> None {
449
+ jacLogout();
450
+ navigate("/login");
451
+ }
452
+ ```
453
+
454
+ **Pattern 3: Conditional links**
455
+
456
+ ```jac
457
+ {(
458
+ <Link to="/dashboard">Dashboard</Link>
459
+ ) if jacIsLoggedIn() else (
460
+ <Link to="/login">Login</Link>
461
+ )}
462
+ ```
463
+
464
+ ---
465
+
466
+ ## ✅ What You've Learned
467
+
468
+ - ✅ What client-side routing is
469
+ - ✅ Setting up Router, Routes, and Route
470
+ - ✅ Creating navigation with Link
471
+ - ✅ Programmatic navigation with Navigate
472
+ - ✅ Protected routes with authentication checks
473
+ - ✅ Conditional navigation based on auth status
474
+ - ✅ Common routing patterns
475
+
476
+ ---
477
+
478
+ ## 🐛 Common Issues
479
+
480
+ ### Issue: Links don't work
481
+
482
+ **Check:**
483
+ - Is everything wrapped in `<Router>`?
484
+ - Are you using `<Link>` not `<a>`?
485
+ - Is the `to` prop correct? `to="/login"` not `href="/login"`
486
+
487
+ ### Issue: Page reloads when clicking links
488
+
489
+ **Cause:** Using `<a href="">` instead of `<Link to="">`
490
+
491
+ ```jac
492
+ # ❌ Wrong
493
+ <a href="/login">Login</a>
494
+
495
+ # ✅ Correct
496
+ <Link to="/login">Login</Link>
497
+ ```
498
+
499
+ ### Issue: Navigate not working
500
+
501
+ **Check:**
502
+ - Is `<Navigate>` inside a component rendered by `<Route>`?
503
+ - Is it wrapped in `<Router>`?
504
+
505
+ ### Issue: Can't access protected page when logged in
506
+
507
+ **Check:**
508
+ - Is `jacIsLoggedIn()` returning true?
509
+ - Did you successfully login?
510
+ - Check browser console for errors
511
+
512
+ ---
513
+
514
+ ## 🎯 Quick Exercise
515
+
516
+ Try adding a 404 page for unknown routes:
517
+
518
+ ```jac
519
+ def NotFoundPage() -> any {
520
+ return <div style={{
521
+ "textAlign": "center",
522
+ "padding": "50px"
523
+ }}>
524
+ <h1>404 - Page Not Found</h1>
525
+ <Link to="/">Go Home</Link>
526
+ </div>;
527
+ }
528
+
529
+ # Add as last route (catches everything else)
530
+ <Route path="*" element={<NotFoundPage />} />
531
+ ```
532
+
533
+ ---
534
+
535
+ ## ➡️ Next Step
536
+
537
+ Congratulations! You've learned all the key concepts. Now let's put everything together into the **complete, final app**!
538
+
539
+ 👉 **[Continue to Step 11: Final Integration](./step-11-final.md)**
540
+