sigpro 1.0.14 → 1.2.39
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.
- package/Readme.md +164 -1008
- package/dist/sigpro.editor.js +1 -0
- package/dist/sigpro.grid.js +78 -0
- package/dist/sigpro.js +1 -0
- package/dist/sigpro.ui.css +2 -0
- package/dist/sigpro.ui.js +1 -0
- package/dist/sigpro.utils.js +1 -0
- package/dist/sigpro.vite.js +4 -0
- package/package.json +64 -14
- package/sigpro.d.ts +395 -0
- package/.github/workflows/publish.yml +0 -25
- package/bun.lock +0 -385
- package/docs/404.html +0 -22
- package/docs/api/components.html +0 -595
- package/docs/api/effects.html +0 -787
- package/docs/api/fetch.html +0 -873
- package/docs/api/pages.html +0 -405
- package/docs/api/quick.html +0 -217
- package/docs/api/routing.html +0 -628
- package/docs/api/signals.html +0 -683
- package/docs/api/storage.html +0 -820
- package/docs/assets/api_components.md.BlFwj17l.js +0 -571
- package/docs/assets/api_components.md.BlFwj17l.lean.js +0 -1
- package/docs/assets/api_effects.md.Br_yStBS.js +0 -763
- package/docs/assets/api_effects.md.Br_yStBS.lean.js +0 -1
- package/docs/assets/api_fetch.md.DQLBJSoq.js +0 -849
- package/docs/assets/api_fetch.md.DQLBJSoq.lean.js +0 -1
- package/docs/assets/api_pages.md.BP19nHXw.js +0 -381
- package/docs/assets/api_pages.md.BP19nHXw.lean.js +0 -1
- package/docs/assets/api_quick.md.BDS3ttnt.js +0 -193
- package/docs/assets/api_quick.md.BDS3ttnt.lean.js +0 -1
- package/docs/assets/api_routing.md.7SNAZXtp.js +0 -604
- package/docs/assets/api_routing.md.7SNAZXtp.lean.js +0 -1
- package/docs/assets/api_signals.md.CrW68-BA.js +0 -659
- package/docs/assets/api_signals.md.CrW68-BA.lean.js +0 -1
- package/docs/assets/api_storage.md.COEWBXHk.js +0 -796
- package/docs/assets/api_storage.md.COEWBXHk.lean.js +0 -1
- package/docs/assets/app.DtmzNmNl.js +0 -1
- package/docs/assets/chunks/framework.C8AWLET_.js +0 -19
- package/docs/assets/chunks/theme.yfWKMLQM.js +0 -1
- package/docs/assets/guide_getting-started.md.BeQpK3vd.js +0 -172
- package/docs/assets/guide_getting-started.md.BeQpK3vd.lean.js +0 -1
- package/docs/assets/guide_why.md.DXchYMN-.js +0 -23
- package/docs/assets/guide_why.md.DXchYMN-.lean.js +0 -1
- package/docs/assets/index.md.uvMJmU4o.js +0 -1
- package/docs/assets/index.md.uvMJmU4o.lean.js +0 -1
- package/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/assets/style.DJRheFKp.css +0 -1
- package/docs/assets/ui_intro.md.gZ21GFqo.js +0 -1
- package/docs/assets/ui_intro.md.gZ21GFqo.lean.js +0 -1
- package/docs/assets/vite_plugin.md.gDWEi8f0.js +0 -225
- package/docs/assets/vite_plugin.md.gDWEi8f0.lean.js +0 -1
- package/docs/guide/getting-started.html +0 -196
- package/docs/guide/why.html +0 -47
- package/docs/hashmap.json +0 -1
- package/docs/index.html +0 -25
- package/docs/logo.svg +0 -118
- package/docs/ui/intro.html +0 -25
- package/docs/vite/plugin.html +0 -249
- package/docs/vp-icons.css +0 -1
- package/index.js +0 -3
- package/packages/docs/.vitepress/cache/deps/@theme_index.js +0 -275
- package/packages/docs/.vitepress/cache/deps/@theme_index.js.map +0 -7
- package/packages/docs/.vitepress/cache/deps/_metadata.json +0 -40
- package/packages/docs/.vitepress/cache/deps/chunk-3S55Y3P7.js +0 -12951
- package/packages/docs/.vitepress/cache/deps/chunk-3S55Y3P7.js.map +0 -7
- package/packages/docs/.vitepress/cache/deps/chunk-RLEUDPPB.js +0 -9719
- package/packages/docs/.vitepress/cache/deps/chunk-RLEUDPPB.js.map +0 -7
- package/packages/docs/.vitepress/cache/deps/package.json +0 -3
- package/packages/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +0 -4505
- package/packages/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +0 -7
- package/packages/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +0 -583
- package/packages/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +0 -7
- package/packages/docs/.vitepress/cache/deps/vue.js +0 -347
- package/packages/docs/.vitepress/cache/deps/vue.js.map +0 -7
- package/packages/docs/.vitepress/config.js +0 -68
- package/packages/docs/api/components.md +0 -760
- package/packages/docs/api/effects.md +0 -1039
- package/packages/docs/api/fetch.md +0 -998
- package/packages/docs/api/pages.md +0 -497
- package/packages/docs/api/quick.md +0 -436
- package/packages/docs/api/routing.md +0 -784
- package/packages/docs/api/signals.md +0 -899
- package/packages/docs/api/storage.md +0 -952
- package/packages/docs/guide/getting-started.md +0 -308
- package/packages/docs/guide/why.md +0 -135
- package/packages/docs/index.md +0 -84
- package/packages/docs/logo.svg +0 -118
- package/packages/docs/public/logo.svg +0 -118
- package/packages/docs/ui/intro.md +0 -16
- package/packages/docs/vite/plugin.md +0 -423
- package/packages/sigpro/plugin.js +0 -91
- package/packages/sigpro/plugin.min.js +0 -1
- package/packages/sigpro/sigpro.js +0 -631
- package/packages/sigpro/sigpro.min.js +0 -1
- package/vite.config.js +0 -24
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
# Pages API 📄
|
|
2
|
-
|
|
3
|
-
Pages in SigPro are special components designed for route-based navigation with **automatic cleanup**. When you navigate away from a page, all signals, effects, and event listeners created within that page are automatically cleaned up - no memory leaks, no manual cleanup needed.
|
|
4
|
-
|
|
5
|
-
## `$.page(setupFunction)`
|
|
6
|
-
|
|
7
|
-
Creates a page with automatic cleanup of all signals and effects when navigated away.
|
|
8
|
-
|
|
9
|
-
```javascript
|
|
10
|
-
import { $, html } from 'sigpro';
|
|
11
|
-
|
|
12
|
-
export default $.page(() => {
|
|
13
|
-
// All signals and effects created here
|
|
14
|
-
// will be automatically cleaned up on navigation
|
|
15
|
-
const count = $(0);
|
|
16
|
-
|
|
17
|
-
$.effect(() => {
|
|
18
|
-
console.log(`Count: ${count()}`);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
return html`
|
|
22
|
-
<div>
|
|
23
|
-
<h1>My Page</h1>
|
|
24
|
-
<p>Count: ${count}</p>
|
|
25
|
-
<button @click=${() => count(c => c + 1)}>+</button>
|
|
26
|
-
</div>
|
|
27
|
-
`;
|
|
28
|
-
});
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## 📋 API Reference
|
|
32
|
-
|
|
33
|
-
| Parameter | Type | Description |
|
|
34
|
-
|-----------|------|-------------|
|
|
35
|
-
| `setupFunction` | `Function` | Function that returns the page content. Receives context object with `params` and `onUnmount` |
|
|
36
|
-
|
|
37
|
-
### Context Object Properties
|
|
38
|
-
|
|
39
|
-
| Property | Type | Description |
|
|
40
|
-
|----------|------|-------------|
|
|
41
|
-
| `params` | `Object` | Route parameters passed to the page |
|
|
42
|
-
| `onUnmount` | `Function` | Register cleanup callbacks (alternative to automatic cleanup) |
|
|
43
|
-
|
|
44
|
-
## 🎯 Basic Usage
|
|
45
|
-
|
|
46
|
-
### Simple Page
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
// pages/home.js
|
|
50
|
-
import { $, html } from 'sigpro';
|
|
51
|
-
|
|
52
|
-
export default $.page(() => {
|
|
53
|
-
const title = $('Welcome to SigPro');
|
|
54
|
-
|
|
55
|
-
return html`
|
|
56
|
-
<div class="home-page">
|
|
57
|
-
<h1>${title}</h1>
|
|
58
|
-
<p>This page will clean itself up when you navigate away.</p>
|
|
59
|
-
</div>
|
|
60
|
-
`;
|
|
61
|
-
});
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Page with Route Parameters
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
// pages/user.js
|
|
68
|
-
import { $, html } from 'sigpro';
|
|
69
|
-
|
|
70
|
-
export default $.page(({ params }) => {
|
|
71
|
-
// Access route parameters
|
|
72
|
-
const userId = params.id;
|
|
73
|
-
const userData = $(null);
|
|
74
|
-
const loading = $(false);
|
|
75
|
-
|
|
76
|
-
// Auto-cleaned effect
|
|
77
|
-
$.effect(() => {
|
|
78
|
-
loading(true);
|
|
79
|
-
$.fetch(`/api/users/${userId}`, null, loading)
|
|
80
|
-
.then(data => userData(data));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
return html`
|
|
84
|
-
<div>
|
|
85
|
-
${() => loading() ? html`
|
|
86
|
-
<div class="spinner">Loading...</div>
|
|
87
|
-
` : html`
|
|
88
|
-
<h1>User Profile: ${userData()?.name}</h1>
|
|
89
|
-
<p>Email: ${userData()?.email}</p>
|
|
90
|
-
`}
|
|
91
|
-
</div>
|
|
92
|
-
`;
|
|
93
|
-
});
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## 🧹 Automatic Cleanup
|
|
97
|
-
|
|
98
|
-
The magic of `$.page` is automatic cleanup. Everything created inside the page is tracked and cleaned up:
|
|
99
|
-
|
|
100
|
-
```javascript
|
|
101
|
-
export default $.page(() => {
|
|
102
|
-
// ✅ Signals are auto-cleaned
|
|
103
|
-
const count = $(0);
|
|
104
|
-
const user = $(null);
|
|
105
|
-
|
|
106
|
-
// ✅ Effects are auto-cleaned
|
|
107
|
-
$.effect(() => {
|
|
108
|
-
document.title = `Count: ${count()}`;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// ✅ Event listeners are auto-cleaned
|
|
112
|
-
window.addEventListener('resize', handleResize);
|
|
113
|
-
|
|
114
|
-
// ✅ Intervals and timeouts are auto-cleaned
|
|
115
|
-
const interval = setInterval(() => {
|
|
116
|
-
refreshData();
|
|
117
|
-
}, 5000);
|
|
118
|
-
|
|
119
|
-
return html`<div>Page content</div>`;
|
|
120
|
-
});
|
|
121
|
-
// When navigating away: all signals, effects, listeners, intervals STOP
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## 📝 Manual Cleanup with `onUnmount`
|
|
125
|
-
|
|
126
|
-
Sometimes you need custom cleanup logic. Use `onUnmount` for that:
|
|
127
|
-
|
|
128
|
-
```javascript
|
|
129
|
-
export default $.page(({ onUnmount }) => {
|
|
130
|
-
// WebSocket connection
|
|
131
|
-
const socket = new WebSocket('wss://api.example.com');
|
|
132
|
-
|
|
133
|
-
socket.onmessage = (event) => {
|
|
134
|
-
updateData(JSON.parse(event.data));
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// Manual cleanup
|
|
138
|
-
onUnmount(() => {
|
|
139
|
-
socket.close();
|
|
140
|
-
console.log('WebSocket closed');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
return html`<div>Real-time updates</div>`;
|
|
144
|
-
});
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## 🔄 Integration with Router
|
|
148
|
-
|
|
149
|
-
Pages are designed to work seamlessly with `$.router`:
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
import { $, html } from 'sigpro';
|
|
153
|
-
import HomePage from './pages/Home.js';
|
|
154
|
-
import UserPage from './pages/User.js';
|
|
155
|
-
import SettingsPage from './pages/Settings.js';
|
|
156
|
-
|
|
157
|
-
const routes = [
|
|
158
|
-
{ path: '/', component: HomePage },
|
|
159
|
-
{ path: '/user/:id', component: UserPage },
|
|
160
|
-
{ path: '/settings', component: SettingsPage },
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
// Mount router
|
|
164
|
-
document.body.appendChild($.router(routes));
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## 💡 Practical Examples
|
|
168
|
-
|
|
169
|
-
### Example 1: Data Fetching Page
|
|
170
|
-
|
|
171
|
-
```javascript
|
|
172
|
-
// pages/posts.js
|
|
173
|
-
export default $.page(({ params }) => {
|
|
174
|
-
const posts = $([]);
|
|
175
|
-
const loading = $(true);
|
|
176
|
-
const error = $(null);
|
|
177
|
-
|
|
178
|
-
$.effect(() => {
|
|
179
|
-
fetch('/api/posts')
|
|
180
|
-
.then(res => res.json())
|
|
181
|
-
.then(data => {
|
|
182
|
-
posts(data);
|
|
183
|
-
loading(false);
|
|
184
|
-
})
|
|
185
|
-
.catch(err => {
|
|
186
|
-
error(err.message);
|
|
187
|
-
loading(false);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
return html`
|
|
192
|
-
<div class="posts-page">
|
|
193
|
-
<h1>Blog Posts</h1>
|
|
194
|
-
|
|
195
|
-
${() => loading() ? html`
|
|
196
|
-
<div class="loading">Loading posts...</div>
|
|
197
|
-
` : error() ? html`
|
|
198
|
-
<div class="error">Error: ${error()}</div>
|
|
199
|
-
` : html`
|
|
200
|
-
<div class="posts-grid">
|
|
201
|
-
${posts().map(post => html`
|
|
202
|
-
<article class="post-card">
|
|
203
|
-
<h2>${post.title}</h2>
|
|
204
|
-
<p>${post.excerpt}</p>
|
|
205
|
-
<a href="#/post/${post.id}">Read more</a>
|
|
206
|
-
</article>
|
|
207
|
-
`)}
|
|
208
|
-
</div>
|
|
209
|
-
`}
|
|
210
|
-
</div>
|
|
211
|
-
`;
|
|
212
|
-
});
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Example 2: Real-time Dashboard
|
|
216
|
-
|
|
217
|
-
```javascript
|
|
218
|
-
// pages/dashboard.js
|
|
219
|
-
export default $.page(({ onUnmount }) => {
|
|
220
|
-
const metrics = $({
|
|
221
|
-
cpu: 0,
|
|
222
|
-
memory: 0,
|
|
223
|
-
requests: 0
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Auto-refresh data
|
|
227
|
-
const refreshInterval = setInterval(async () => {
|
|
228
|
-
const data = await $.fetch('/api/metrics');
|
|
229
|
-
if (data) metrics(data);
|
|
230
|
-
}, 5000);
|
|
231
|
-
|
|
232
|
-
// Manual cleanup for interval
|
|
233
|
-
onUnmount(() => clearInterval(refreshInterval));
|
|
234
|
-
|
|
235
|
-
// Live clock
|
|
236
|
-
const currentTime = $(new Date());
|
|
237
|
-
const clockInterval = setInterval(() => {
|
|
238
|
-
currentTime(new Date());
|
|
239
|
-
}, 1000);
|
|
240
|
-
|
|
241
|
-
onUnmount(() => clearInterval(clockInterval));
|
|
242
|
-
|
|
243
|
-
return html`
|
|
244
|
-
<div class="dashboard">
|
|
245
|
-
<h1>System Dashboard</h1>
|
|
246
|
-
|
|
247
|
-
<div class="time">
|
|
248
|
-
Last updated: ${() => currentTime().toLocaleTimeString()}
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<div class="metrics-grid">
|
|
252
|
-
<div class="metric-card">
|
|
253
|
-
<h3>CPU Usage</h3>
|
|
254
|
-
<p class="metric-value">${() => metrics().cpu}%</p>
|
|
255
|
-
</div>
|
|
256
|
-
<div class="metric-card">
|
|
257
|
-
<h3>Memory Usage</h3>
|
|
258
|
-
<p class="metric-value">${() => metrics().memory}%</p>
|
|
259
|
-
</div>
|
|
260
|
-
<div class="metric-card">
|
|
261
|
-
<h3>Requests/min</h3>
|
|
262
|
-
<p class="metric-value">${() => metrics().requests}</p>
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
`;
|
|
267
|
-
});
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Example 3: Multi-step Form
|
|
271
|
-
|
|
272
|
-
```javascript
|
|
273
|
-
// pages/checkout.js
|
|
274
|
-
export default $.page(({ onUnmount }) => {
|
|
275
|
-
const step = $(1);
|
|
276
|
-
const formData = $({
|
|
277
|
-
email: '',
|
|
278
|
-
address: '',
|
|
279
|
-
payment: ''
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// Warn user before leaving
|
|
283
|
-
const handleBeforeUnload = (e) => {
|
|
284
|
-
if (step() < 3) {
|
|
285
|
-
e.preventDefault();
|
|
286
|
-
e.returnValue = '';
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
291
|
-
onUnmount(() => {
|
|
292
|
-
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
const nextStep = () => step(s => Math.min(s + 1, 3));
|
|
296
|
-
const prevStep = () => step(s => Math.max(s - 1, 1));
|
|
297
|
-
|
|
298
|
-
return html`
|
|
299
|
-
<div class="checkout">
|
|
300
|
-
<h1>Checkout - Step ${step} of 3</h1>
|
|
301
|
-
|
|
302
|
-
${() => {
|
|
303
|
-
switch(step()) {
|
|
304
|
-
case 1:
|
|
305
|
-
return html`
|
|
306
|
-
<div class="step">
|
|
307
|
-
<h2>Email</h2>
|
|
308
|
-
<input
|
|
309
|
-
type="email"
|
|
310
|
-
:value=${() => formData().email}
|
|
311
|
-
@input=${(e) => formData({...formData(), email: e.target.value})}
|
|
312
|
-
/>
|
|
313
|
-
</div>
|
|
314
|
-
`;
|
|
315
|
-
case 2:
|
|
316
|
-
return html`
|
|
317
|
-
<div class="step">
|
|
318
|
-
<h2>Address</h2>
|
|
319
|
-
<textarea
|
|
320
|
-
:value=${() => formData().address}
|
|
321
|
-
@input=${(e) => formData({...formData(), address: e.target.value})}
|
|
322
|
-
></textarea>
|
|
323
|
-
</div>
|
|
324
|
-
`;
|
|
325
|
-
case 3:
|
|
326
|
-
return html`
|
|
327
|
-
<div class="step">
|
|
328
|
-
<h2>Payment</h2>
|
|
329
|
-
<input
|
|
330
|
-
type="text"
|
|
331
|
-
placeholder="Card number"
|
|
332
|
-
:value=${() => formData().payment}
|
|
333
|
-
@input=${(e) => formData({...formData(), payment: e.target.value})}
|
|
334
|
-
/>
|
|
335
|
-
</div>
|
|
336
|
-
`;
|
|
337
|
-
}
|
|
338
|
-
}}
|
|
339
|
-
|
|
340
|
-
<div class="buttons">
|
|
341
|
-
${() => step() > 1 ? html`
|
|
342
|
-
<button @click=${prevStep}>Previous</button>
|
|
343
|
-
` : ''}
|
|
344
|
-
|
|
345
|
-
${() => step() < 3 ? html`
|
|
346
|
-
<button @click=${nextStep}>Next</button>
|
|
347
|
-
` : html`
|
|
348
|
-
<button @click=${submitOrder}>Place Order</button>
|
|
349
|
-
`}
|
|
350
|
-
</div>
|
|
351
|
-
</div>
|
|
352
|
-
`;
|
|
353
|
-
});
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
### Example 4: Page with Tabs
|
|
357
|
-
|
|
358
|
-
```javascript
|
|
359
|
-
// pages/profile.js
|
|
360
|
-
export default $.page(({ params }) => {
|
|
361
|
-
const activeTab = $('overview');
|
|
362
|
-
const userData = $(null);
|
|
363
|
-
|
|
364
|
-
// Load user data
|
|
365
|
-
$.effect(() => {
|
|
366
|
-
$.fetch(`/api/users/${params.id}`)
|
|
367
|
-
.then(data => userData(data));
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
const tabs = {
|
|
371
|
-
overview: () => html`
|
|
372
|
-
<div>
|
|
373
|
-
<h3>Overview</h3>
|
|
374
|
-
<p>Username: ${userData()?.username}</p>
|
|
375
|
-
<p>Member since: ${userData()?.joined}</p>
|
|
376
|
-
</div>
|
|
377
|
-
`,
|
|
378
|
-
posts: () => html`
|
|
379
|
-
<div>
|
|
380
|
-
<h3>Posts</h3>
|
|
381
|
-
${userData()?.posts.map(post => html`
|
|
382
|
-
<div class="post">${post.title}</div>
|
|
383
|
-
`)}
|
|
384
|
-
</div>
|
|
385
|
-
`,
|
|
386
|
-
settings: () => html`
|
|
387
|
-
<div>
|
|
388
|
-
<h3>Settings</h3>
|
|
389
|
-
<label>
|
|
390
|
-
<input type="checkbox" :checked=${userData()?.emailNotifications} />
|
|
391
|
-
Email notifications
|
|
392
|
-
</label>
|
|
393
|
-
</div>
|
|
394
|
-
`
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
return html`
|
|
398
|
-
<div class="profile-page">
|
|
399
|
-
<h1>${() => userData()?.name}</h1>
|
|
400
|
-
|
|
401
|
-
<div class="tabs">
|
|
402
|
-
${Object.keys(tabs).map(tab => html`
|
|
403
|
-
<button
|
|
404
|
-
class:active=${() => activeTab() === tab}
|
|
405
|
-
@click=${() => activeTab(tab)}
|
|
406
|
-
>
|
|
407
|
-
${tab.charAt(0).toUpperCase() + tab.slice(1)}
|
|
408
|
-
</button>
|
|
409
|
-
`)}
|
|
410
|
-
</div>
|
|
411
|
-
|
|
412
|
-
<div class="tab-content">
|
|
413
|
-
${() => tabs[activeTab()]()}
|
|
414
|
-
</div>
|
|
415
|
-
</div>
|
|
416
|
-
`;
|
|
417
|
-
});
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
## 🎯 Advanced Patterns
|
|
421
|
-
|
|
422
|
-
### Page with Nested Routes
|
|
423
|
-
|
|
424
|
-
```javascript
|
|
425
|
-
// pages/settings/index.js
|
|
426
|
-
export default $.page(({ params }) => {
|
|
427
|
-
const section = params.section || 'general';
|
|
428
|
-
|
|
429
|
-
const sections = {
|
|
430
|
-
general: () => import('./general.js').then(m => m.default),
|
|
431
|
-
security: () => import('./security.js').then(m => m.default),
|
|
432
|
-
notifications: () => import('./notifications.js').then(m => m.default)
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
const currentSection = $(null);
|
|
436
|
-
|
|
437
|
-
$.effect(() => {
|
|
438
|
-
sections[section]().then(comp => currentSection(comp));
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
return html`
|
|
442
|
-
<div class="settings">
|
|
443
|
-
<nav>
|
|
444
|
-
<a href="#/settings/general">General</a>
|
|
445
|
-
<a href="#/settings/security">Security</a>
|
|
446
|
-
<a href="#/settings/notifications">Notifications</a>
|
|
447
|
-
</nav>
|
|
448
|
-
|
|
449
|
-
<div class="content">
|
|
450
|
-
${currentSection}
|
|
451
|
-
</div>
|
|
452
|
-
</div>
|
|
453
|
-
`;
|
|
454
|
-
});
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
### Page with Authentication
|
|
458
|
-
|
|
459
|
-
```javascript
|
|
460
|
-
// pages/dashboard.js
|
|
461
|
-
export default $.page(({ onUnmount }) => {
|
|
462
|
-
const isAuthenticated = $(false);
|
|
463
|
-
const authCheck = $.effect(() => {
|
|
464
|
-
const token = localStorage.getItem('token');
|
|
465
|
-
isAuthenticated(!!token);
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
// Redirect if not authenticated
|
|
469
|
-
$.effect(() => {
|
|
470
|
-
if (!isAuthenticated()) {
|
|
471
|
-
$.router.go('/login');
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
return html`
|
|
476
|
-
<div class="dashboard">
|
|
477
|
-
<h1>Protected Dashboard</h1>
|
|
478
|
-
<!-- Protected content -->
|
|
479
|
-
</div>
|
|
480
|
-
`;
|
|
481
|
-
});
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
## 📊 Summary
|
|
485
|
-
|
|
486
|
-
| Feature | Description |
|
|
487
|
-
|---------|-------------|
|
|
488
|
-
| **Automatic Cleanup** | All signals, effects, and resources auto-cleaned on navigation |
|
|
489
|
-
| **Memory Safe** | No memory leaks, even with complex nested effects |
|
|
490
|
-
| **Router Integration** | Designed to work perfectly with `$.router` |
|
|
491
|
-
| **Parameters** | Access route parameters via `params` object |
|
|
492
|
-
| **Manual Cleanup** | `onUnmount` for custom cleanup needs |
|
|
493
|
-
| **Zero Configuration** | Just wrap your page in `$.page()` and it works |
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
> **Pro Tip:** Always wrap route-based views in `$.page()` to ensure proper cleanup. This prevents memory leaks and ensures your app stays performant even after many navigation changes.
|