jac-client 0.2.2__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} +7 -7
- 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.2.dist-info → jac_client-0.2.6.dist-info}/METADATA +30 -24
- jac_client-0.2.6.dist-info/RECORD +74 -0
- {jac_client-0.2.2.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.2.dist-info/RECORD +0 -171
- jac_client-0.2.2.dist-info/entry_points.txt +0 -4
jac_client/docs/routing.md
DELETED
|
@@ -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!
|