jac-client 0.2.3__py3-none-any.whl → 0.2.4__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 (176) hide show
  1. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  2. jac_client/tests/conftest.py +281 -0
  3. jac_client/tests/test_cli.py +755 -0
  4. jac_client/tests/test_it.py +347 -67
  5. {jac_client-0.2.3.dist-info → jac_client-0.2.4.dist-info}/METADATA +28 -30
  6. jac_client-0.2.4.dist-info/RECORD +10 -0
  7. {jac_client-0.2.3.dist-info → jac_client-0.2.4.dist-info}/WHEEL +2 -1
  8. jac_client-0.2.4.dist-info/entry_points.txt +4 -0
  9. jac_client-0.2.4.dist-info/top_level.txt +1 -0
  10. jac_client/docs/README.md +0 -689
  11. jac_client/docs/advanced-state.md +0 -1265
  12. jac_client/docs/asset-serving/intro.md +0 -209
  13. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  14. jac_client/docs/assets/pipe_line.png +0 -0
  15. jac_client/docs/file-system/app.jac.md +0 -121
  16. jac_client/docs/file-system/backend-frontend.md +0 -217
  17. jac_client/docs/file-system/intro.md +0 -72
  18. jac_client/docs/file-system/nested-imports.md +0 -348
  19. jac_client/docs/guide-example/intro.md +0 -115
  20. jac_client/docs/guide-example/step-01-setup.md +0 -270
  21. jac_client/docs/guide-example/step-02-components.md +0 -416
  22. jac_client/docs/guide-example/step-03-styling.md +0 -478
  23. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  24. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  25. jac_client/docs/guide-example/step-06-events.md +0 -749
  26. jac_client/docs/guide-example/step-07-effects.md +0 -468
  27. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  28. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  29. jac_client/docs/guide-example/step-10-routing.md +0 -539
  30. jac_client/docs/guide-example/step-11-final.md +0 -963
  31. jac_client/docs/imports.md +0 -1141
  32. jac_client/docs/lifecycle-hooks.md +0 -773
  33. jac_client/docs/routing.md +0 -659
  34. jac_client/docs/styling/intro.md +0 -249
  35. jac_client/docs/styling/js-styling.md +0 -367
  36. jac_client/docs/styling/material-ui.md +0 -341
  37. jac_client/docs/styling/pure-css.md +0 -299
  38. jac_client/docs/styling/sass.md +0 -403
  39. jac_client/docs/styling/styled-components.md +0 -395
  40. jac_client/docs/styling/tailwind.md +0 -298
  41. jac_client/examples/all-in-one/.babelrc +0 -9
  42. jac_client/examples/all-in-one/README.md +0 -16
  43. jac_client/examples/all-in-one/app.jac +0 -426
  44. jac_client/examples/all-in-one/assets/burger.png +0 -0
  45. jac_client/examples/all-in-one/button.jac +0 -7
  46. jac_client/examples/all-in-one/components/button.jac +0 -7
  47. jac_client/examples/all-in-one/package.json +0 -29
  48. jac_client/examples/all-in-one/styles.css +0 -26
  49. jac_client/examples/all-in-one/vite.config.js +0 -28
  50. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  51. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  52. jac_client/examples/asset-serving/css-with-image/app.jac +0 -88
  53. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  54. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  55. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  56. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  57. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  58. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  59. jac_client/examples/asset-serving/image-asset/app.jac +0 -55
  60. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  61. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  62. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  63. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  64. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  65. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  66. jac_client/examples/asset-serving/import-alias/app.jac +0 -111
  67. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  68. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  69. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  70. jac_client/examples/basic/.babelrc +0 -9
  71. jac_client/examples/basic/README.md +0 -16
  72. jac_client/examples/basic/app.jac +0 -21
  73. jac_client/examples/basic/package.json +0 -27
  74. jac_client/examples/basic/vite.config.js +0 -27
  75. jac_client/examples/basic-auth/.babelrc +0 -9
  76. jac_client/examples/basic-auth/README.md +0 -16
  77. jac_client/examples/basic-auth/app.jac +0 -308
  78. jac_client/examples/basic-auth/package.json +0 -27
  79. jac_client/examples/basic-auth/vite.config.js +0 -27
  80. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  81. jac_client/examples/basic-auth-with-router/README.md +0 -60
  82. jac_client/examples/basic-auth-with-router/app.jac +0 -464
  83. jac_client/examples/basic-auth-with-router/package.json +0 -28
  84. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  85. jac_client/examples/basic-full-stack/.babelrc +0 -9
  86. jac_client/examples/basic-full-stack/README.md +0 -18
  87. jac_client/examples/basic-full-stack/app.jac +0 -320
  88. jac_client/examples/basic-full-stack/package.json +0 -28
  89. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  90. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  91. jac_client/examples/css-styling/js-styling/README.md +0 -183
  92. jac_client/examples/css-styling/js-styling/app.jac +0 -84
  93. jac_client/examples/css-styling/js-styling/package.json +0 -28
  94. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  95. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  96. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  97. jac_client/examples/css-styling/material-ui/README.md +0 -16
  98. jac_client/examples/css-styling/material-ui/app.jac +0 -122
  99. jac_client/examples/css-styling/material-ui/package.json +0 -32
  100. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  101. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  102. jac_client/examples/css-styling/pure-css/README.md +0 -16
  103. jac_client/examples/css-styling/pure-css/app.jac +0 -64
  104. jac_client/examples/css-styling/pure-css/package.json +0 -28
  105. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  106. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  107. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  108. jac_client/examples/css-styling/sass-example/README.md +0 -16
  109. jac_client/examples/css-styling/sass-example/app.jac +0 -64
  110. jac_client/examples/css-styling/sass-example/package.json +0 -29
  111. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  112. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  113. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  114. jac_client/examples/css-styling/styled-components/README.md +0 -16
  115. jac_client/examples/css-styling/styled-components/app.jac +0 -71
  116. jac_client/examples/css-styling/styled-components/package.json +0 -29
  117. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  118. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  119. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  120. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  121. jac_client/examples/css-styling/tailwind-example/app.jac +0 -63
  122. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  123. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  124. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  125. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  126. jac_client/examples/full-stack-with-auth/README.md +0 -16
  127. jac_client/examples/full-stack-with-auth/app.jac +0 -722
  128. jac_client/examples/full-stack-with-auth/package.json +0 -28
  129. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  130. jac_client/examples/little-x/app.jac +0 -724
  131. jac_client/examples/little-x/package.json +0 -23
  132. jac_client/examples/little-x/submit-button.jac +0 -8
  133. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  134. jac_client/examples/nested-folders/nested-advance/ButtonRoot.jac +0 -11
  135. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  136. jac_client/examples/nested-folders/nested-advance/app.jac +0 -35
  137. jac_client/examples/nested-folders/nested-advance/level1/ButtonSecondL.jac +0 -19
  138. jac_client/examples/nested-folders/nested-advance/level1/Card.jac +0 -43
  139. jac_client/examples/nested-folders/nested-advance/level1/level2/ButtonThirdL.jac +0 -25
  140. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  141. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  142. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  143. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  144. jac_client/examples/nested-folders/nested-basic/app.jac +0 -13
  145. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  146. jac_client/examples/nested-folders/nested-basic/button.jac +0 -7
  147. jac_client/examples/nested-folders/nested-basic/components/button.jac +0 -7
  148. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  149. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  150. jac_client/examples/with-router/.babelrc +0 -9
  151. jac_client/examples/with-router/README.md +0 -17
  152. jac_client/examples/with-router/app.jac +0 -323
  153. jac_client/examples/with-router/package.json +0 -28
  154. jac_client/examples/with-router/vite.config.js +0 -27
  155. jac_client/plugin/cli.py +0 -244
  156. jac_client/plugin/client.py +0 -152
  157. jac_client/plugin/client_runtime.jac +0 -234
  158. jac_client/plugin/vite_client_bundle.py +0 -503
  159. jac_client/tests/fixtures/basic-app/app.jac +0 -23
  160. jac_client/tests/fixtures/cl_file/app.cl.jac +0 -48
  161. jac_client/tests/fixtures/cl_file/app.jac +0 -15
  162. jac_client/tests/fixtures/client_app_with_antd/app.jac +0 -34
  163. jac_client/tests/fixtures/js_import/app.jac +0 -34
  164. jac_client/tests/fixtures/js_import/utils.js +0 -21
  165. jac_client/tests/fixtures/package-lock.json +0 -329
  166. jac_client/tests/fixtures/package.json +0 -11
  167. jac_client/tests/fixtures/relative_import/app.jac +0 -11
  168. jac_client/tests/fixtures/relative_import/button.jac +0 -7
  169. jac_client/tests/fixtures/spawn_test/app.jac +0 -129
  170. jac_client/tests/fixtures/test_fragments_spread/app.jac +0 -67
  171. jac_client/tests/test_asset_examples.py +0 -322
  172. jac_client/tests/test_cl.py +0 -530
  173. jac_client/tests/test_create_jac_app.py +0 -131
  174. jac_client/tests/test_nested_file.py +0 -374
  175. jac_client-0.2.3.dist-info/RECORD +0 -171
  176. jac_client-0.2.3.dist-info/entry_points.txt +0 -4
@@ -1,659 +0,0 @@
1
- # Routing in Jac: Building Multi-Page Applications
2
-
3
- Learn how to create multi-page applications with client-side routing using Jac's declarative routing API.
4
-
5
- ---
6
-
7
- ## Table of Contents
8
-
9
- - [What is Routing?](#what-is-routing)
10
- - [Getting Started](#getting-started)
11
- - [Basic Routing Setup](#basic-routing-setup)
12
- - [Route Components](#route-components)
13
- - [Navigation with Link](#navigation-with-link)
14
- - [Programmatic Navigation with useNavigate](#programmatic-navigation-with-usenavigate)
15
- - [URL Parameters with useParams](#url-parameters-with-useparams)
16
- - [Current Location with useLocation](#current-location-with-uselocation)
17
- - [Protected Routes Pattern](#protected-routes-pattern)
18
- - [Complete Examples](#complete-examples)
19
- - [Best Practices](#best-practices)
20
-
21
- ---
22
-
23
- ## What is Routing?
24
-
25
- Routing allows you to create multi-page applications where different URLs display different components without page refreshes.
26
-
27
- **Key Benefits:**
28
- - **Single Page Application (SPA)**: No page refreshes when navigating
29
- - **Declarative Syntax**: Define routes using JSX components
30
- - **URL Parameters**: Dynamic routes with params like `/user/:id`
31
- - **Browser History**: Back/forward buttons work automatically
32
- - **Hash-based URLs**: Uses `#/path` for maximum compatibility
33
- - **Battle-tested**: Built on industry-standard routing technology
34
-
35
- ---
36
-
37
- ## Getting Started
38
-
39
- Import routing components from `@jac-client/utils`:
40
-
41
- ```jac
42
- cl import from "@jac-client/utils" {
43
- Router,
44
- Routes,
45
- Route,
46
- Link,
47
- Navigate,
48
- useNavigate,
49
- useLocation,
50
- useParams
51
- }
52
- ```
53
-
54
- **Core Components:**
55
- - **`<Router>`**: Container that wraps your entire application
56
- - **`<Routes>`**: Groups multiple routes together
57
- - **`<Route>`**: Defines a single route with path and element
58
- - **`<Link>`**: Navigation links that don't refresh the page
59
- - **`<Navigate>`**: Component for conditional redirects
60
-
61
- **Hooks:**
62
- - **`useNavigate()`**: Get navigate function for programmatic navigation
63
- - **`useLocation()`**: Access current location and pathname
64
- - **`useParams()`**: Access URL parameters from dynamic routes
65
-
66
- ---
67
-
68
- ## Basic Routing Setup
69
-
70
- ### Simple Three-Page App
71
-
72
- ```jac
73
- cl import from react { useState, useEffect }
74
- cl import from "@jac-client/utils" { Router, Routes, Route, Link }
75
-
76
- cl {
77
- # Page Components
78
- def Home() -> any {
79
- return <div>
80
- <h1> Home Page</h1>
81
- <p>Welcome to the home page!</p>
82
- </div>;
83
- }
84
-
85
- def About() -> any {
86
- return <div>
87
- <h1>ℹ About Page</h1>
88
- <p>Learn more about our application.</p>
89
- </div>;
90
- }
91
-
92
- def Contact() -> any {
93
- return <div>
94
- <h1> Contact Page</h1>
95
- <p>Email: contact@example.com</p>
96
- </div>;
97
- }
98
-
99
- # Main App with React Router
100
- def app() -> any {
101
- return <Router>
102
- <div>
103
- <nav>
104
- <Link to="/">Home</Link>
105
- {" | "}
106
- <Link to="/about">About</Link>
107
- {" | "}
108
- <Link to="/contact">Contact</Link>
109
- </nav>
110
- <Routes>
111
- <Route path="/" element={<Home />} />
112
- <Route path="/about" element={<About />} />
113
- <Route path="/contact" element={<Contact />} />
114
- </Routes>
115
- </div>
116
- </Router>;
117
- }
118
- }
119
- ```
120
-
121
- **How It Works:**
122
- 1. **`<Router>`** wraps your entire app and manages routing state
123
- 2. **`<Routes>`** contains all your route definitions
124
- 3. **`<Route>`** maps a URL path to an element (note: `element={<Component />}`)
125
- 4. **`<Link>`** creates clickable navigation links
126
- 5. URLs will be hash-based: `#/`, `#/about`, `#/contact`
127
-
128
- **Key Points:**
129
- - Use `element={<Home />}` to render components
130
- - No configuration needed - just wrap and go
131
- - Hash-based URLs work everywhere
132
-
133
- ---
134
-
135
- ## Route Components
136
-
137
- ### Router Component
138
-
139
- The `<Router>` component is the top-level container for your app:
140
-
141
- ```jac
142
- <Router>
143
- {/* Your app content */}
144
- </Router>
145
- ```
146
-
147
- **Features:**
148
- - Hash-based URLs (e.g., `#/about`, `#/contact`)
149
- - No props needed - it just works!
150
- - Manages routing state automatically
151
- - Works in any environment
152
-
153
- ### Routes Component
154
-
155
- The `<Routes>` component groups multiple routes:
156
-
157
- ```jac
158
- <Routes>
159
- <Route path="/" element={<Home />} />
160
- <Route path="/about" element={<About />} />
161
- <Route path="/contact" element={<Contact />} />
162
- </Routes>
163
- ```
164
-
165
- ### Route Component
166
-
167
- Each `<Route>` defines a single route:
168
-
169
- ```jac
170
- <Route path="/todos" element={<TodoList />} />
171
- ```
172
-
173
- **Props:**
174
- - **`path`**: The URL path (must start with `/`)
175
- - **`element`**: The JSX element to render (note: call the component with `<>`)
176
- - **`index`**: Boolean for index routes (optional)
177
-
178
- **Important:** Use `element={<Component />}` not `component={Component}`
179
-
180
- ### Example: Index Routes
181
-
182
- ```jac
183
- <Routes>
184
- <Route index element={<Home />} /> {/* Matches parent route */}
185
- <Route path="/about" element={<About />} />
186
- </Routes>
187
- ```
188
-
189
- ---
190
-
191
- ## Navigation with Link
192
-
193
- ### The Link Component
194
-
195
- The `<Link>` component creates clickable navigation links:
196
-
197
- ```jac
198
- <Link to="/about">About Us</Link>
199
- ```
200
-
201
- **Props:**
202
- - **`to`**: The destination path (e.g., `"/"`, `"/about"`)
203
- - **`style`**: Optional CSS styles for the link
204
- - **`className`**: Optional CSS class name
205
-
206
- ### Basic Navigation
207
-
208
- ```jac
209
- cl import from "@jac-client/utils" { Router, Routes, Route, Link }
210
-
211
- cl {
212
- def Navigation() -> any {
213
- return <nav style={{"padding": "1rem", "backgroundColor": "#f0f0f0"}}>
214
- <Link to="/">Home</Link>
215
- {" | "}
216
- <Link to="/about">About</Link>
217
- {" | "}
218
- <Link to="/contact">Contact</Link>
219
- </nav>;
220
- }
221
- }
222
- ```
223
-
224
- ### Active Link Styling with useLocation
225
-
226
- ```jac
227
- cl import from "@jac-client/utils" { Link, useLocation }
228
-
229
- cl {
230
- def Navigation() -> any {
231
- let location = useLocation();
232
-
233
- def linkStyle(path: str) -> dict {
234
- isActive = location.pathname == path;
235
- return {
236
- "padding": "0.5rem 1rem",
237
- "textDecoration": "none",
238
- "color": "#0066cc" if isActive else "#333",
239
- "fontWeight": "bold" if isActive else "normal",
240
- "backgroundColor": "#e3f2fd" if isActive else "transparent",
241
- "borderRadius": "4px"
242
- };
243
- }
244
-
245
- return <nav style={{"display": "flex", "gap": "1rem", "padding": "1rem"}}>
246
- <Link to="/" style={linkStyle("/")}>Home</Link>
247
- <Link to="/about" style={linkStyle("/about")}>About</Link>
248
- <Link to="/contact" style={linkStyle("/contact")}>Contact</Link>
249
- </nav>;
250
- }
251
- }
252
- ```
253
-
254
- ### Link Component Features
255
-
256
- - **No Page Refresh**: Navigation happens without reloading the page
257
- - **Client-Side Routing**: Fast transitions between pages
258
- - **Browser History**: Works with browser back/forward buttons
259
- - **Styling Support**: Can be styled like any other element
260
- - **Battle-tested**: Reliable, production-ready navigation
261
-
262
- ---
263
-
264
- ## Programmatic Navigation with useNavigate
265
-
266
- For programmatic navigation (e.g., after form submission), use the `useNavigate()` hook:
267
-
268
- ```jac
269
- cl import from "@jac-client/utils" { useNavigate }
270
-
271
- cl {
272
- def LoginForm() -> any {
273
- let [username, setUsername] = useState("");
274
- let [password, setPassword] = useState("");
275
- let navigate = useNavigate();
276
-
277
- async def handleLogin(e: any) -> None {
278
- e.preventDefault();
279
- success = await jacLogin(username, password);
280
- if success {
281
- navigate("/dashboard"); # Navigate after successful login
282
- } else {
283
- alert("Login failed");
284
- }
285
- }
286
-
287
- return <form onSubmit={handleLogin}>
288
- <input
289
- type="text"
290
- value={username}
291
- onChange={lambda e: any -> None { setUsername(e.target.value); }}
292
- placeholder="Username"
293
- />
294
- <input
295
- type="password"
296
- value={password}
297
- onChange={lambda e: any -> None { setPassword(e.target.value); }}
298
- placeholder="Password"
299
- />
300
- <button type="submit">Login</button>
301
- </form>;
302
- }
303
- }
304
- ```
305
-
306
- **useNavigate() Features:**
307
- - **Hook-based API**: Modern React pattern
308
- - **Type-safe**: Works seamlessly with TypeScript/Jac types
309
- - **Replace option**: Use `navigate("/path", { replace: true })` to replace history entry
310
-
311
- **Common Use Cases:**
312
- - After form submission
313
- - After authentication
314
- - Conditional navigation based on logic
315
- - In button onClick handlers
316
- - Redirects after API calls
317
-
318
- ---
319
-
320
- ## URL Parameters with useParams
321
-
322
- Access dynamic URL parameters using the `useParams()` hook:
323
-
324
- ```jac
325
- cl import from "@jac-client/utils" { useParams, Link }
326
-
327
- cl {
328
- def UserProfile() -> any {
329
- let params = useParams();
330
- let userId = params.id;
331
-
332
- return <div>
333
- <h1>User Profile</h1>
334
- <p>Viewing profile for user ID: {userId}</p>
335
- <Link to="/">Back to Home</Link>
336
- </div>;
337
- }
338
-
339
- def app() -> any {
340
- return <Router>
341
- <Routes>
342
- <Route path="/" element={<Home />} />
343
- <Route path="/user/:id" element={<UserProfile />} />
344
- </Routes>
345
- </Router>;
346
- }
347
- }
348
- ```
349
-
350
- **URL Pattern Examples:**
351
- - `/user/:id` → Access via `params.id`
352
- - `/posts/:postId/comments/:commentId` → Access via `params.postId` and `params.commentId`
353
- - `/products/:category/:productId` → Multiple parameters
354
-
355
- ---
356
-
357
- ## Current Location with useLocation
358
-
359
- Access the current location object using `useLocation()`:
360
-
361
- ```jac
362
- cl import from "@jac-client/utils" { useLocation }
363
-
364
- cl {
365
- def CurrentPath() -> any {
366
- let location = useLocation();
367
-
368
- return <div>
369
- <p>Current pathname: {location.pathname}</p>
370
- <p>Current hash: {location.hash}</p>
371
- <p>Search params: {location.search}</p>
372
- </div>;
373
- }
374
- }
375
- ```
376
-
377
- **Location Object Properties:**
378
- - **`pathname`**: Current path (e.g., `/about`)
379
- - **`search`**: Query string (e.g., `?page=2`)
380
- - **`hash`**: URL hash (e.g., `#section1`)
381
- - **`state`**: Location state passed via navigate
382
-
383
- ---
384
-
385
- ## Protected Routes Pattern
386
-
387
- Use the `<Navigate>` component to protect routes that require authentication:
388
-
389
- ```jac
390
- cl import from "@jac-client/utils" { Navigate, useNavigate }
391
-
392
- cl {
393
- def Dashboard() -> any {
394
- # Check if user is logged in
395
- if not jacIsLoggedIn() {
396
- return <Navigate to="/login" />;
397
- }
398
-
399
- return <div>
400
- <h1> Dashboard</h1>
401
- <p>Welcome! You are logged in.</p>
402
- <p>This is protected content only visible to authenticated users.</p>
403
- </div>;
404
- }
405
-
406
- def LoginPage() -> any {
407
- let navigate = useNavigate();
408
-
409
- async def handleLogin(e: any) -> None {
410
- e.preventDefault();
411
- success = await jacLogin(username, password);
412
- if success {
413
- navigate("/dashboard");
414
- }
415
- }
416
-
417
- return <form onSubmit={handleLogin}>
418
- <h2>Login</h2>
419
- <input type="text" placeholder="Username" />
420
- <input type="password" placeholder="Password" />
421
- <button type="submit">Login</button>
422
- </form>;
423
- }
424
-
425
- def app() -> any {
426
- return <Router>
427
- <Routes>
428
- <Route path="/" element={<HomePage />} />
429
- <Route path="/login" element={<LoginPage />} />
430
- <Route path="/dashboard" element={<Dashboard />} />
431
- </Routes>
432
- </Router>;
433
- }
434
- }
435
- ```
436
-
437
- **Protected Route Pattern:**
438
- 1. Check authentication at the start of the component
439
- 2. Return `<Navigate to="/login" />` if not authenticated
440
- 3. Return protected content if authenticated
441
- 4. Use `useNavigate()` to redirect after successful login
442
-
443
- ---
444
-
445
- ## Complete Examples
446
-
447
- ### Example 1: Simple Multi-Page App
448
-
449
- ```jac
450
- cl import from react { useState, useEffect }
451
- cl import from "@jac-client/utils" { Router, Routes, Route, Link, useLocation }
452
-
453
- cl {
454
- def Navigation() -> any {
455
- let location = useLocation();
456
-
457
- def linkStyle(path: str) -> dict {
458
- isActive = location.pathname == path;
459
- return {
460
- "padding": "0.5rem 1rem",
461
- "textDecoration": "none",
462
- "color": "#0066cc" if isActive else "#333",
463
- "fontWeight": "bold" if isActive else "normal"
464
- };
465
- }
466
-
467
- return <nav style={{"padding": "1rem", "backgroundColor": "#f0f0f0"}}>
468
- <Link to="/" style={linkStyle("/")}>Home</Link>
469
- {" | "}
470
- <Link to="/about" style={linkStyle("/about")}>About</Link>
471
- {" | "}
472
- <Link to="/contact" style={linkStyle("/contact")}>Contact</Link>
473
- </nav>;
474
- }
475
-
476
- def Home() -> any {
477
- return <div>
478
- <h1> Home Page</h1>
479
- <p>Welcome to the home page!</p>
480
- </div>;
481
- }
482
-
483
- def About() -> any {
484
- return <div>
485
- <h1>ℹ About Page</h1>
486
- <p>Learn more about our application.</p>
487
- </div>;
488
- }
489
-
490
- def Contact() -> any {
491
- return <div>
492
- <h1> Contact Page</h1>
493
- <p>Email: contact@example.com</p>
494
- </div>;
495
- }
496
-
497
- def app() -> any {
498
- return <Router>
499
- <div>
500
- <Navigation />
501
- <div style={{"padding": "2rem"}}>
502
- <Routes>
503
- <Route path="/" element={<Home />} />
504
- <Route path="/about" element={<About />} />
505
- <Route path="/contact" element={<Contact />} />
506
- </Routes>
507
- </div>
508
- </div>
509
- </Router>;
510
- }
511
- }
512
- ```
513
-
514
- ### Example 2: App with URL Parameters
515
-
516
- ```jac
517
- cl import from "@jac-client/utils" { Router, Routes, Route, Link, useParams }
518
-
519
- cl {
520
- def UserList() -> any {
521
- users = ["Alice", "Bob", "Charlie"];
522
-
523
- return <div>
524
- <h1> User List</h1>
525
- {users.map(lambda user: any -> any {
526
- return <div key={user}>
527
- <Link to={"/user/" + user}>{user}</Link>
528
- </div>;
529
- })}
530
- </div>;
531
- }
532
-
533
- def UserProfile() -> any {
534
- let params = useParams();
535
- let username = params.id;
536
-
537
- return <div>
538
- <h1> Profile: {username}</h1>
539
- <p>Viewing profile for {username}</p>
540
- <Link to="/">← Back to User List</Link>
541
- </div>;
542
- }
543
-
544
- def app() -> any {
545
- return <Router>
546
- <Routes>
547
- <Route path="/" element={<UserList />} />
548
- <Route path="/user/:id" element={<UserProfile />} />
549
- </Routes>
550
- </Router>;
551
- }
552
- }
553
- ```
554
-
555
- ---
556
-
557
- ## Best Practices
558
-
559
- ### 1. **Use Correct Route Syntax**
560
- ```jac
561
- # CORRECT - Use element prop with JSX
562
- <Route path="/" element={<Home />} />
563
-
564
- # WRONG - Don't pass component without JSX
565
- <Route path="/" component={Home} />
566
- ```
567
-
568
- ### 2. **Import All Needed Components**
569
- ```jac
570
- cl import from "@jac-client/utils" {
571
- Router,
572
- Routes,
573
- Route,
574
- Link,
575
- Navigate,
576
- useNavigate,
577
- useLocation,
578
- useParams
579
- }
580
- ```
581
-
582
- ### 3. **Use Hooks for Navigation**
583
- ```jac
584
- # CORRECT - Use useNavigate hook
585
- def MyComponent() -> any {
586
- let navigate = useNavigate();
587
- navigate("/dashboard");
588
- }
589
-
590
- # OLD - Global navigate() function (still works for backward compatibility)
591
- navigate("/dashboard");
592
- ```
593
-
594
- ### 4. **Protected Routes Pattern**
595
- ```jac
596
- # CORRECT - Check auth in component
597
- def ProtectedPage() -> any {
598
- if not jacIsLoggedIn() {
599
- return <Navigate to="/login" />;
600
- }
601
- return <div>Protected content</div>;
602
- }
603
- ```
604
-
605
- ### 5. **Use Link for Navigation**
606
- ```jac
607
- # CORRECT - Use Link component
608
- <Link to="/about">About</Link>
609
-
610
- # WRONG - Regular anchor tags cause page reload
611
- <a href="#/about">About</a>
612
- ```
613
-
614
- ### 6. **Dynamic Routes with Parameters**
615
- ```jac
616
- # Define route with parameter
617
- <Route path="/user/:id" element={<UserProfile />} />
618
-
619
- # Access parameter in component
620
- def UserProfile() -> any {
621
- let params = useParams();
622
- let userId = params.id;
623
- return <div>User: {userId}</div>;
624
- }
625
- ```
626
-
627
- ### 7. **Active Link Styling**
628
- ```jac
629
- def Navigation() -> any {
630
- let location = useLocation();
631
-
632
- def isActive(path: str) -> bool {
633
- return location.pathname == path;
634
- }
635
-
636
- return <nav>
637
- <Link
638
- to="/"
639
- style={{"fontWeight": "bold" if isActive("/") else "normal"}}
640
- >
641
- Home
642
- </Link>
643
- </nav>;
644
- }
645
- ```
646
-
647
- ---
648
-
649
- ## Summary
650
-
651
- - **Simple & Declarative**: Use `<Router>`, `<Routes>`, `<Route>` components
652
- - **Hash-based URLs**: Uses `#/path` for maximum compatibility
653
- - **Modern Hooks**: `useNavigate()`, `useLocation()`, `useParams()`
654
- - **Protected Routes**: Use `<Navigate>` component for redirects
655
- - **URL Parameters**: Dynamic routes with `:param` syntax
656
- - **No Configuration**: Just wrap your app in `<Router>` and start routing!
657
- - **Production-ready**: Battle-tested routing for real applications
658
-
659
- Routing in Jac is simple, powerful, and production-ready!