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
jac_client/docs/imports.md
CHANGED
|
@@ -6,6 +6,7 @@ Learn how to import third-party libraries, other Jac files, and JavaScript modul
|
|
|
6
6
|
|
|
7
7
|
## 📚 Table of Contents
|
|
8
8
|
|
|
9
|
+
- [Importing Jac-Client Utilities](#importing-jac-client-utilities)
|
|
9
10
|
- [Working with Third-Party Node Modules](#working-with-third-party-node-modules)
|
|
10
11
|
- [Installing Packages](#installing-packages)
|
|
11
12
|
- [Importing Third-Party Libraries](#importing-third-party-libraries)
|
|
@@ -15,6 +16,450 @@ Learn how to import third-party libraries, other Jac files, and JavaScript modul
|
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
19
|
+
## Importing Jac-Client Utilities
|
|
20
|
+
|
|
21
|
+
Jac-Client provides built-in utilities for authentication, backend communication, and routing through the `@jac-client/utils` package.
|
|
22
|
+
|
|
23
|
+
### Available Utilities
|
|
24
|
+
|
|
25
|
+
```jac
|
|
26
|
+
cl import from '@jac-client/utils' {
|
|
27
|
+
jacSpawn, # Call backend walkers
|
|
28
|
+
jacLogin, # Login user
|
|
29
|
+
jacSignup, # Register new user
|
|
30
|
+
jacLogout, # Logout user
|
|
31
|
+
jacIsLoggedIn, # Check if user is logged in
|
|
32
|
+
navigate, # Navigate to routes
|
|
33
|
+
Link, # Link component for routing
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Backend Communication
|
|
38
|
+
|
|
39
|
+
#### `jacSpawn` - Call Backend Walkers
|
|
40
|
+
|
|
41
|
+
The `jacSpawn` function lets you call backend walkers from the frontend:
|
|
42
|
+
|
|
43
|
+
```jac
|
|
44
|
+
cl import from react { useState, useEffect }
|
|
45
|
+
cl import from '@jac-client/utils' { jacSpawn }
|
|
46
|
+
|
|
47
|
+
cl {
|
|
48
|
+
def TodoApp() -> any {
|
|
49
|
+
let [todos, setTodos] = useState([]);
|
|
50
|
+
|
|
51
|
+
useEffect(lambda -> None {
|
|
52
|
+
async def loadTodos() -> None {
|
|
53
|
+
# Call backend walker
|
|
54
|
+
result = await jacSpawn("read_todos", "", {});
|
|
55
|
+
setTodos(result.reports);
|
|
56
|
+
}
|
|
57
|
+
loadTodos();
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
async def addTodo(text: str) -> None {
|
|
61
|
+
# Call walker with parameters
|
|
62
|
+
new_todo = await jacSpawn("create_todo", "", {"text": text});
|
|
63
|
+
setTodos(todos.concat([new_todo]));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return <div>{/* UI */}</div>;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Signature:**
|
|
72
|
+
```jac
|
|
73
|
+
jacSpawn(walker_name: str, node_id: str, params: dict) -> any
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- `walker_name`: Name of the backend walker to call
|
|
77
|
+
- `node_id`: Target node ID (use `""` for root)
|
|
78
|
+
- `params`: Dictionary of parameters to pass to the walker
|
|
79
|
+
|
|
80
|
+
### Authentication Functions
|
|
81
|
+
|
|
82
|
+
#### `jacLogin` - User Login
|
|
83
|
+
|
|
84
|
+
```jac
|
|
85
|
+
cl import from '@jac-client/utils' { jacLogin, navigate }
|
|
86
|
+
|
|
87
|
+
cl {
|
|
88
|
+
def LoginForm() -> any {
|
|
89
|
+
async def handleLogin(e: any) -> None {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
username = document.getElementById("username").value;
|
|
92
|
+
password = document.getElementById("password").value;
|
|
93
|
+
|
|
94
|
+
success = await jacLogin(username, password);
|
|
95
|
+
|
|
96
|
+
if success {
|
|
97
|
+
navigate("/dashboard");
|
|
98
|
+
} else {
|
|
99
|
+
alert("Login failed");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return <form onSubmit={handleLogin}>
|
|
104
|
+
<input id="username" type="text" placeholder="Username" />
|
|
105
|
+
<input id="password" type="password" placeholder="Password" />
|
|
106
|
+
<button type="submit">Login</button>
|
|
107
|
+
</form>;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### `jacSignup` - User Registration
|
|
113
|
+
|
|
114
|
+
```jac
|
|
115
|
+
cl import from '@jac-client/utils' { jacSignup, navigate }
|
|
116
|
+
|
|
117
|
+
cl {
|
|
118
|
+
def SignupForm() -> any {
|
|
119
|
+
async def handleSignup(e: any) -> None {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
username = document.getElementById("username").value;
|
|
122
|
+
password = document.getElementById("password").value;
|
|
123
|
+
|
|
124
|
+
result = await jacSignup(username, password);
|
|
125
|
+
|
|
126
|
+
if result.success {
|
|
127
|
+
alert("Account created successfully!");
|
|
128
|
+
navigate("/login");
|
|
129
|
+
} else {
|
|
130
|
+
alert(result.error or "Signup failed");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return <form onSubmit={handleSignup}>
|
|
135
|
+
<input id="username" type="text" placeholder="Username" />
|
|
136
|
+
<input id="password" type="password" placeholder="Password" />
|
|
137
|
+
<button type="submit">Sign Up</button>
|
|
138
|
+
</form>;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `jacLogout` - User Logout
|
|
144
|
+
|
|
145
|
+
```jac
|
|
146
|
+
cl import from '@jac-client/utils' { jacLogout, navigate }
|
|
147
|
+
|
|
148
|
+
cl {
|
|
149
|
+
def Header() -> any {
|
|
150
|
+
def handleLogout() -> None {
|
|
151
|
+
jacLogout();
|
|
152
|
+
navigate("/login");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return <header>
|
|
156
|
+
<button onClick={handleLogout}>Logout</button>
|
|
157
|
+
</header>;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### `jacIsLoggedIn` - Check Authentication Status
|
|
163
|
+
|
|
164
|
+
```jac
|
|
165
|
+
cl import from '@jac-client/utils' { jacIsLoggedIn, navigate }
|
|
166
|
+
|
|
167
|
+
cl {
|
|
168
|
+
def ProtectedPage() -> any {
|
|
169
|
+
if not jacIsLoggedIn() {
|
|
170
|
+
navigate("/login");
|
|
171
|
+
return <div>Redirecting...</div>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return <div>
|
|
175
|
+
<h1>Protected Content</h1>
|
|
176
|
+
<p>Only logged-in users can see this!</p>
|
|
177
|
+
</div>;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Routing Functions
|
|
183
|
+
|
|
184
|
+
#### `navigate` - Programmatic Navigation
|
|
185
|
+
|
|
186
|
+
```jac
|
|
187
|
+
cl import from '@jac-client/utils' { navigate }
|
|
188
|
+
|
|
189
|
+
cl {
|
|
190
|
+
def MyComponent() -> any {
|
|
191
|
+
def goToHome() -> None {
|
|
192
|
+
navigate("/");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
def goToProfile() -> None {
|
|
196
|
+
navigate("/profile");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return <div>
|
|
200
|
+
<button onClick={goToHome}>Go Home</button>
|
|
201
|
+
<button onClick={goToProfile}>Go to Profile</button>
|
|
202
|
+
</div>;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `Link` - Declarative Navigation
|
|
208
|
+
|
|
209
|
+
```jac
|
|
210
|
+
cl import from '@jac-client/utils' { Link }
|
|
211
|
+
|
|
212
|
+
cl {
|
|
213
|
+
def Navigation() -> any {
|
|
214
|
+
return <nav>
|
|
215
|
+
<Link href="/">Home</Link>
|
|
216
|
+
<Link href="/about">About</Link>
|
|
217
|
+
<Link href="/contact">Contact</Link>
|
|
218
|
+
</nav>;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### `initRouter` - Initialize Router
|
|
224
|
+
|
|
225
|
+
```jac
|
|
226
|
+
cl import from '@jac-client/utils' { initRouter, jacIsLoggedIn }
|
|
227
|
+
|
|
228
|
+
cl {
|
|
229
|
+
def App() -> any {
|
|
230
|
+
# Define routes
|
|
231
|
+
routes = [
|
|
232
|
+
{
|
|
233
|
+
"path": "/",
|
|
234
|
+
"component": lambda -> any { return <HomePage />; },
|
|
235
|
+
"guard": None
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"path": "/dashboard",
|
|
239
|
+
"component": lambda -> any { return <Dashboard />; },
|
|
240
|
+
"guard": jacIsLoggedIn # Require authentication
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"path": "/login",
|
|
244
|
+
"component": lambda -> any { return <LoginPage />; },
|
|
245
|
+
"guard": None
|
|
246
|
+
}
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
# Initialize router with default route
|
|
250
|
+
router = initRouter(routes, "/");
|
|
251
|
+
|
|
252
|
+
return <div>
|
|
253
|
+
<Navigation />
|
|
254
|
+
{router.render()}
|
|
255
|
+
</div>;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Complete Authentication Example
|
|
261
|
+
|
|
262
|
+
```jac
|
|
263
|
+
cl import from react { useState }
|
|
264
|
+
cl import from '@jac-client/utils' {
|
|
265
|
+
jacLogin,
|
|
266
|
+
jacSignup,
|
|
267
|
+
jacLogout,
|
|
268
|
+
jacIsLoggedIn,
|
|
269
|
+
navigate,
|
|
270
|
+
Link,
|
|
271
|
+
initRouter
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
cl {
|
|
275
|
+
def LoginPage() -> any {
|
|
276
|
+
let [error, setError] = useState("");
|
|
277
|
+
|
|
278
|
+
async def handleLogin(e: any) -> None {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
username = document.getElementById("username").value;
|
|
281
|
+
password = document.getElementById("password").value;
|
|
282
|
+
|
|
283
|
+
success = await jacLogin(username, password);
|
|
284
|
+
|
|
285
|
+
if success {
|
|
286
|
+
navigate("/dashboard");
|
|
287
|
+
} else {
|
|
288
|
+
setError("Invalid credentials");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return <div style={{"maxWidth": "400px", "margin": "50px auto"}}>
|
|
293
|
+
<h1>Login</h1>
|
|
294
|
+
{error and <p style={{"color": "red"}}>{error}</p>}
|
|
295
|
+
<form onSubmit={handleLogin}>
|
|
296
|
+
<input
|
|
297
|
+
id="username"
|
|
298
|
+
type="text"
|
|
299
|
+
placeholder="Username"
|
|
300
|
+
style={{"width": "100%", "padding": "10px", "marginBottom": "10px"}}
|
|
301
|
+
/>
|
|
302
|
+
<input
|
|
303
|
+
id="password"
|
|
304
|
+
type="password"
|
|
305
|
+
placeholder="Password"
|
|
306
|
+
style={{"width": "100%", "padding": "10px", "marginBottom": "10px"}}
|
|
307
|
+
/>
|
|
308
|
+
<button type="submit" style={{"width": "100%", "padding": "10px"}}>
|
|
309
|
+
Login
|
|
310
|
+
</button>
|
|
311
|
+
</form>
|
|
312
|
+
<p>
|
|
313
|
+
Don't have an account? <Link href="/signup">Sign up</Link>
|
|
314
|
+
</p>
|
|
315
|
+
</div>;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
def Dashboard() -> any {
|
|
319
|
+
if not jacIsLoggedIn() {
|
|
320
|
+
navigate("/login");
|
|
321
|
+
return <div>Redirecting...</div>;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
def handleLogout() -> None {
|
|
325
|
+
jacLogout();
|
|
326
|
+
navigate("/login");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return <div style={{"padding": "20px"}}>
|
|
330
|
+
<h1>Dashboard</h1>
|
|
331
|
+
<p>Welcome! You are logged in.</p>
|
|
332
|
+
<button onClick={handleLogout}>Logout</button>
|
|
333
|
+
</div>;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
def App() -> any {
|
|
337
|
+
routes = [
|
|
338
|
+
{"path": "/login", "component": lambda -> any { return LoginPage(); }, "guard": None},
|
|
339
|
+
{"path": "/dashboard", "component": lambda -> any { return Dashboard(); }, "guard": jacIsLoggedIn}
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
router = initRouter(routes, "/login");
|
|
343
|
+
|
|
344
|
+
return <div>
|
|
345
|
+
{router.render()}
|
|
346
|
+
</div>;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Common Patterns
|
|
352
|
+
|
|
353
|
+
#### Pattern 1: Protected Route with Loading State
|
|
354
|
+
|
|
355
|
+
```jac
|
|
356
|
+
cl import from react { useState, useEffect }
|
|
357
|
+
cl import from '@jac-client/utils' { jacIsLoggedIn, jacSpawn, navigate }
|
|
358
|
+
|
|
359
|
+
cl {
|
|
360
|
+
def ProtectedDashboard() -> any {
|
|
361
|
+
let [user, setUser] = useState(None);
|
|
362
|
+
let [loading, setLoading] = useState(True);
|
|
363
|
+
|
|
364
|
+
useEffect(lambda -> None {
|
|
365
|
+
if not jacIsLoggedIn() {
|
|
366
|
+
navigate("/login");
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async def loadUserData() -> None {
|
|
371
|
+
result = await jacSpawn("get_user_profile", "", {});
|
|
372
|
+
setUser(result);
|
|
373
|
+
setLoading(False);
|
|
374
|
+
}
|
|
375
|
+
loadUserData();
|
|
376
|
+
}, []);
|
|
377
|
+
|
|
378
|
+
if loading { return <div>Loading...</div>; }
|
|
379
|
+
|
|
380
|
+
return <div>
|
|
381
|
+
<h1>Welcome, {user.name}!</h1>
|
|
382
|
+
</div>;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### Pattern 2: Form with Backend Integration
|
|
388
|
+
|
|
389
|
+
```jac
|
|
390
|
+
cl import from react { useState }
|
|
391
|
+
cl import from '@jac-client/utils' { jacSpawn }
|
|
392
|
+
|
|
393
|
+
cl {
|
|
394
|
+
def CreateTodoForm() -> any {
|
|
395
|
+
let [text, setText] = useState("");
|
|
396
|
+
let [loading, setLoading] = useState(False);
|
|
397
|
+
|
|
398
|
+
async def handleSubmit(e: any) -> None {
|
|
399
|
+
e.preventDefault();
|
|
400
|
+
if not text.trim() { return; }
|
|
401
|
+
|
|
402
|
+
setLoading(True);
|
|
403
|
+
try {
|
|
404
|
+
await jacSpawn("create_todo", "", {"text": text});
|
|
405
|
+
setText(""); # Clear form
|
|
406
|
+
alert("Todo created!");
|
|
407
|
+
} catch (err) {
|
|
408
|
+
alert("Failed to create todo");
|
|
409
|
+
} finally {
|
|
410
|
+
setLoading(False);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return <form onSubmit={handleSubmit}>
|
|
415
|
+
<input
|
|
416
|
+
value={text}
|
|
417
|
+
onChange={lambda e: any -> None { setText(e.target.value); }}
|
|
418
|
+
placeholder="Enter todo..."
|
|
419
|
+
disabled={loading}
|
|
420
|
+
/>
|
|
421
|
+
<button type="submit" disabled={loading}>
|
|
422
|
+
{"Creating..." if loading else "Add Todo"}
|
|
423
|
+
</button>
|
|
424
|
+
</form>;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### Pattern 3: Navigation with Auth Check
|
|
430
|
+
|
|
431
|
+
```jac
|
|
432
|
+
cl import from '@jac-client/utils' { Link, jacIsLoggedIn, jacLogout, navigate }
|
|
433
|
+
|
|
434
|
+
cl {
|
|
435
|
+
def Navigation() -> any {
|
|
436
|
+
isLoggedIn = jacIsLoggedIn();
|
|
437
|
+
|
|
438
|
+
def handleLogout() -> None {
|
|
439
|
+
jacLogout();
|
|
440
|
+
navigate("/");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return <nav style={{"padding": "10px", "background": "#f5f5f5"}}>
|
|
444
|
+
<Link href="/">Home</Link>
|
|
445
|
+
{isLoggedIn ? (
|
|
446
|
+
<>
|
|
447
|
+
<Link href="/dashboard">Dashboard</Link>
|
|
448
|
+
<button onClick={handleLogout}>Logout</button>
|
|
449
|
+
</>
|
|
450
|
+
) : (
|
|
451
|
+
<>
|
|
452
|
+
<Link href="/login">Login</Link>
|
|
453
|
+
<Link href="/signup">Sign Up</Link>
|
|
454
|
+
</>
|
|
455
|
+
)}
|
|
456
|
+
</nav>;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
18
463
|
## Working with Third-Party Node Modules
|
|
19
464
|
|
|
20
465
|
Jac supports importing any npm package that's compatible with ES modules. This includes popular libraries like React UI frameworks, utility libraries, and more.
|
|
@@ -121,75 +566,122 @@ npm install antd
|
|
|
121
566
|
```jac
|
|
122
567
|
"""Importing Ant Design components."""
|
|
123
568
|
|
|
124
|
-
cl import from antd {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
569
|
+
cl import from antd { Button, Input, Card, Typography, Space }
|
|
570
|
+
|
|
571
|
+
cl {
|
|
572
|
+
def MyApp() -> any {
|
|
573
|
+
return <div>
|
|
574
|
+
<Card title="Welcome" style={{"maxWidth": "400px", "margin": "50px auto"}}>
|
|
575
|
+
<Card.Meta title="Hello" description="Welcome to Jac!" />
|
|
576
|
+
<Space direction="vertical" style={{"width": "100%"}}>
|
|
577
|
+
<Input placeholder="Enter text..." />
|
|
578
|
+
<Button type="primary" style={{"width": "100%"}}>Submit</Button>
|
|
579
|
+
<Button color="default" variant="dashed">Dashed</Button>
|
|
580
|
+
<Button color="default" variant="filled">Filled</Button>
|
|
581
|
+
<Button color="default" variant="text">Text</Button>
|
|
582
|
+
<Button color="default" variant="link">Link</Button>
|
|
583
|
+
</Space>
|
|
137
584
|
</Card>
|
|
138
|
-
</
|
|
139
|
-
|
|
140
|
-
}
|
|
585
|
+
</div>;
|
|
586
|
+
}
|
|
141
587
|
|
|
142
|
-
|
|
143
|
-
|
|
588
|
+
def jac_app() -> any {
|
|
589
|
+
return MyApp();
|
|
590
|
+
}
|
|
144
591
|
}
|
|
145
592
|
```
|
|
146
593
|
|
|
147
|
-
### Example: Importing React
|
|
594
|
+
### Example: Importing React Hooks
|
|
595
|
+
|
|
596
|
+
React hooks can be imported and used directly in Jac:
|
|
148
597
|
|
|
149
598
|
```bash
|
|
150
|
-
#
|
|
151
|
-
npm install react
|
|
599
|
+
# React is typically included by default, but if needed:
|
|
600
|
+
npm install react
|
|
152
601
|
```
|
|
153
602
|
|
|
154
603
|
```jac
|
|
155
|
-
"""
|
|
156
|
-
|
|
157
|
-
cl import from
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
<
|
|
168
|
-
|
|
604
|
+
"""Using React hooks in Jac."""
|
|
605
|
+
|
|
606
|
+
cl import from react { useState, useEffect }
|
|
607
|
+
|
|
608
|
+
cl {
|
|
609
|
+
def Counter() -> any {
|
|
610
|
+
let [count, setCount] = useState(0);
|
|
611
|
+
|
|
612
|
+
useEffect(lambda -> None {
|
|
613
|
+
console.log("Count: ", count);
|
|
614
|
+
}, [count]);
|
|
615
|
+
|
|
616
|
+
return <div>
|
|
617
|
+
<h1>Count: {count}</h1>
|
|
618
|
+
<button onClick={lambda e: any -> None {
|
|
619
|
+
setCount(count + 1);
|
|
620
|
+
}}>
|
|
621
|
+
Increment
|
|
622
|
+
</button>
|
|
623
|
+
</div>;
|
|
624
|
+
}
|
|
169
625
|
}
|
|
170
626
|
```
|
|
171
627
|
|
|
172
628
|
### Example: Importing Utility Libraries
|
|
173
629
|
|
|
630
|
+
Lodash is a popular utility library with many helpful functions:
|
|
631
|
+
|
|
174
632
|
```bash
|
|
175
|
-
# Install
|
|
176
|
-
npm install
|
|
633
|
+
# Install lodash
|
|
634
|
+
npm install lodash
|
|
177
635
|
```
|
|
178
636
|
|
|
179
637
|
```jac
|
|
180
|
-
"""Importing
|
|
638
|
+
"""Importing lodash utilities."""
|
|
181
639
|
|
|
182
|
-
cl import from
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
640
|
+
cl import from lodash { * as _ }
|
|
641
|
+
|
|
642
|
+
cl {
|
|
643
|
+
def RandomQuoteCard() -> any {
|
|
644
|
+
suggestions = ['good luck', 'have fun', 'enjoy the ride'];
|
|
645
|
+
randomSuggestion = _.sample(suggestions); # Pick random item
|
|
187
646
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
647
|
+
return <div>
|
|
648
|
+
<h2>{randomSuggestion}</h2>
|
|
649
|
+
<p>Powered by Lodash!</p>
|
|
650
|
+
</div>;
|
|
651
|
+
}
|
|
191
652
|
}
|
|
653
|
+
```
|
|
192
654
|
|
|
655
|
+
### Example: Importing Specialized Libraries
|
|
656
|
+
|
|
657
|
+
You can import specialized libraries like pluralize or animation libraries:
|
|
658
|
+
|
|
659
|
+
```bash
|
|
660
|
+
# Install packages
|
|
661
|
+
npm install pluralize
|
|
662
|
+
npm install react-animated-components
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
```jac
|
|
666
|
+
"""Importing specialized libraries."""
|
|
667
|
+
|
|
668
|
+
cl import from pluralize { default as pluralize }
|
|
669
|
+
cl import from 'react-animated-components' { Rotate }
|
|
670
|
+
|
|
671
|
+
cl {
|
|
672
|
+
def AnimatedDemo() -> any {
|
|
673
|
+
word = "tweet";
|
|
674
|
+
count = 5;
|
|
675
|
+
pluralWord = pluralize(word, count);
|
|
676
|
+
|
|
677
|
+
return <div>
|
|
678
|
+
<h1>{count} {pluralWord}</h1>
|
|
679
|
+
<Rotate>
|
|
680
|
+
<span style={{"fontSize": "48px"}}>😂</span>
|
|
681
|
+
</Rotate>
|
|
682
|
+
</div>;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
193
685
|
```
|
|
194
686
|
|
|
195
687
|
### Example: Importing Multiple Components
|