snice 4.13.0 → 4.14.0
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/dist/cdn/accordion/snice-accordion.js +1 -1
- package/dist/cdn/accordion/snice-accordion.min.js +1 -1
- package/dist/cdn/alert/snice-alert.js +1 -1
- package/dist/cdn/alert/snice-alert.min.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.min.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.min.js +1 -1
- package/dist/cdn/avatar/snice-avatar.js +1 -1
- package/dist/cdn/avatar/snice-avatar.min.js +1 -1
- package/dist/cdn/badge/snice-badge.js +1 -1
- package/dist/cdn/badge/snice-badge.min.js +1 -1
- package/dist/cdn/banner/snice-banner.js +1 -1
- package/dist/cdn/banner/snice-banner.min.js +1 -1
- package/dist/cdn/book/snice-book.js +1 -1
- package/dist/cdn/book/snice-book.min.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.min.js +1 -1
- package/dist/cdn/button/snice-button.js +1 -1
- package/dist/cdn/button/snice-button.min.js +1 -1
- package/dist/cdn/calendar/snice-calendar.js +1 -1
- package/dist/cdn/calendar/snice-calendar.min.js +1 -1
- package/dist/cdn/camera/snice-camera.js +1 -1
- package/dist/cdn/camera/snice-camera.min.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.min.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.min.js +1 -1
- package/dist/cdn/card/snice-card.js +1 -1
- package/dist/cdn/card/snice-card.min.js +1 -1
- package/dist/cdn/carousel/snice-carousel.js +1 -1
- package/dist/cdn/carousel/snice-carousel.min.js +1 -1
- package/dist/cdn/chart/snice-chart.js +1 -1
- package/dist/cdn/chart/snice-chart.min.js +1 -1
- package/dist/cdn/chat/snice-chat.js +1 -1
- package/dist/cdn/chat/snice-chat.min.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.min.js +1 -1
- package/dist/cdn/chip/snice-chip.js +1 -1
- package/dist/cdn/chip/snice-chip.min.js +1 -1
- package/dist/cdn/code-block/snice-code-block.js +2 -2
- package/dist/cdn/code-block/snice-code-block.js.map +1 -1
- package/dist/cdn/code-block/snice-code-block.min.js +2 -2
- package/dist/cdn/code-block/snice-code-block.min.js.map +1 -1
- package/dist/cdn/color-display/snice-color-display.js +1 -1
- package/dist/cdn/color-display/snice-color-display.min.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.min.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.min.js +1 -1
- package/dist/cdn/comments/snice-comments.js +1 -1
- package/dist/cdn/comments/snice-comments.min.js +1 -1
- package/dist/cdn/countdown/snice-countdown.js +1 -1
- package/dist/cdn/countdown/snice-countdown.min.js +1 -1
- package/dist/cdn/cropper/snice-cropper.js +1 -1
- package/dist/cdn/cropper/snice-cropper.min.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.min.js +1 -1
- package/dist/cdn/diff/snice-diff.js +1 -1
- package/dist/cdn/diff/snice-diff.min.js +1 -1
- package/dist/cdn/divider/snice-divider.js +1 -1
- package/dist/cdn/divider/snice-divider.min.js +1 -1
- package/dist/cdn/doc/snice-doc.js +1 -1
- package/dist/cdn/doc/snice-doc.min.js +1 -1
- package/dist/cdn/draw/snice-draw.js +1 -1
- package/dist/cdn/draw/snice-draw.min.js +1 -1
- package/dist/cdn/drawer/snice-drawer.js +1 -1
- package/dist/cdn/drawer/snice-drawer.min.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.min.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.min.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.min.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.min.js +1 -1
- package/dist/cdn/flow/snice-flow.js +1 -1
- package/dist/cdn/flow/snice-flow.min.js +1 -1
- package/dist/cdn/funnel/snice-funnel.js +1 -1
- package/dist/cdn/funnel/snice-funnel.min.js +1 -1
- package/dist/cdn/gantt/snice-gantt.js +1 -1
- package/dist/cdn/gantt/snice-gantt.min.js +1 -1
- package/dist/cdn/gauge/snice-gauge.js +1 -1
- package/dist/cdn/gauge/snice-gauge.min.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.min.js +1 -1
- package/dist/cdn/image/snice-image.js +1 -1
- package/dist/cdn/image/snice-image.min.js +1 -1
- package/dist/cdn/input/snice-input.js +1 -1
- package/dist/cdn/input/snice-input.min.js +1 -1
- package/dist/cdn/kanban/snice-kanban.js +1 -1
- package/dist/cdn/kanban/snice-kanban.min.js +1 -1
- package/dist/cdn/kpi/snice-kpi.js +1 -1
- package/dist/cdn/kpi/snice-kpi.min.js +1 -1
- package/dist/cdn/layout/snice-layout.js +1 -1
- package/dist/cdn/layout/snice-layout.min.js +1 -1
- package/dist/cdn/link/snice-link.js +1 -1
- package/dist/cdn/link/snice-link.min.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.min.js +1 -1
- package/dist/cdn/list/snice-list.js +1 -1
- package/dist/cdn/list/snice-list.min.js +1 -1
- package/dist/cdn/location/snice-location.js +1 -1
- package/dist/cdn/location/snice-location.min.js +1 -1
- package/dist/cdn/login/snice-login.js +1 -1
- package/dist/cdn/login/snice-login.min.js +1 -1
- package/dist/cdn/map/snice-map.js +1 -1
- package/dist/cdn/map/snice-map.min.js +1 -1
- package/dist/cdn/markdown/snice-markdown.js +1 -1
- package/dist/cdn/markdown/snice-markdown.min.js +1 -1
- package/dist/cdn/masonry/snice-masonry.js +1 -1
- package/dist/cdn/masonry/snice-masonry.min.js +1 -1
- package/dist/cdn/menu/snice-menu.js +1 -1
- package/dist/cdn/menu/snice-menu.min.js +1 -1
- package/dist/cdn/modal/snice-modal.js +1 -1
- package/dist/cdn/modal/snice-modal.min.js +1 -1
- package/dist/cdn/music-player/snice-music-player.js +1 -1
- package/dist/cdn/music-player/snice-music-player.min.js +1 -1
- package/dist/cdn/nav/snice-nav.js +1 -1
- package/dist/cdn/nav/snice-nav.min.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.min.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.min.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.min.js +1 -1
- package/dist/cdn/pagination/snice-pagination.js +1 -1
- package/dist/cdn/pagination/snice-pagination.min.js +1 -1
- package/dist/cdn/paint/snice-paint.js +1 -1
- package/dist/cdn/paint/snice-paint.min.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.min.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.min.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.min.js +1 -1
- package/dist/cdn/progress/snice-progress.js +1 -1
- package/dist/cdn/progress/snice-progress.min.js +1 -1
- package/dist/cdn/qr-code/README.md +2 -2
- package/dist/cdn/qr-code/snice-qr-code.js +149 -20
- package/dist/cdn/qr-code/snice-qr-code.js.map +1 -1
- package/dist/cdn/qr-code/snice-qr-code.min.js +3 -3
- package/dist/cdn/qr-code/snice-qr-code.min.js.map +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.min.js +1 -1
- package/dist/cdn/radio/snice-radio.js +1 -1
- package/dist/cdn/radio/snice-radio.min.js +1 -1
- package/dist/cdn/rating/snice-rating.js +1 -1
- package/dist/cdn/rating/snice-rating.min.js +1 -1
- package/dist/cdn/recipe/snice-recipe.js +1 -1
- package/dist/cdn/recipe/snice-recipe.min.js +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.js +4 -4
- package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js +3 -3
- package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.js +4 -4
- package/dist/cdn/runtime/snice-runtime.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js +3 -3
- package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
- package/dist/cdn/sankey/snice-sankey.js +1 -1
- package/dist/cdn/sankey/snice-sankey.min.js +1 -1
- package/dist/cdn/select/snice-select.js +1 -1
- package/dist/cdn/select/snice-select.min.js +1 -1
- package/dist/cdn/skeleton/snice-skeleton.js +1 -1
- package/dist/cdn/skeleton/snice-skeleton.min.js +1 -1
- package/dist/cdn/slider/snice-slider.js +2 -2
- package/dist/cdn/slider/snice-slider.js.map +1 -1
- package/dist/cdn/slider/snice-slider.min.js +5 -5
- package/dist/cdn/slider/snice-slider.min.js.map +1 -1
- package/dist/cdn/sortable/snice-sortable.js +1 -1
- package/dist/cdn/sortable/snice-sortable.min.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.min.js +1 -1
- package/dist/cdn/spinner/snice-spinner.js +1 -1
- package/dist/cdn/spinner/snice-spinner.min.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.min.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.min.js +1 -1
- package/dist/cdn/spreadsheet/snice-spreadsheet.js +1 -1
- package/dist/cdn/spreadsheet/snice-spreadsheet.min.js +1 -1
- package/dist/cdn/stepper/snice-stepper.js +1 -1
- package/dist/cdn/stepper/snice-stepper.min.js +1 -1
- package/dist/cdn/switch/snice-switch.js +1 -1
- package/dist/cdn/switch/snice-switch.min.js +1 -1
- package/dist/cdn/table/snice-table.js +1 -1
- package/dist/cdn/table/snice-table.min.js +1 -1
- package/dist/cdn/tabs/snice-tabs.js +1 -1
- package/dist/cdn/tabs/snice-tabs.min.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.min.js +1 -1
- package/dist/cdn/terminal/snice-terminal.js +1 -1
- package/dist/cdn/terminal/snice-terminal.min.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.min.js +1 -1
- package/dist/cdn/textarea/snice-textarea.js +1 -1
- package/dist/cdn/textarea/snice-textarea.min.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.min.js +1 -1
- package/dist/cdn/timeline/snice-timeline.js +1 -1
- package/dist/cdn/timeline/snice-timeline.min.js +1 -1
- package/dist/cdn/timer/snice-timer.js +1 -1
- package/dist/cdn/timer/snice-timer.min.js +1 -1
- package/dist/cdn/toast/snice-toast.js +1 -1
- package/dist/cdn/toast/snice-toast.min.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.min.js +1 -1
- package/dist/cdn/tree/snice-tree.js +1 -1
- package/dist/cdn/tree/snice-tree.min.js +1 -1
- package/dist/cdn/treemap/snice-treemap.js +1 -1
- package/dist/cdn/treemap/snice-treemap.min.js +1 -1
- package/dist/cdn/video-player/snice-video-player.js +1 -1
- package/dist/cdn/video-player/snice-video-player.min.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.min.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.min.js +1 -1
- package/dist/cdn/weather/snice-weather.js +1 -1
- package/dist/cdn/weather/snice-weather.min.js +1 -1
- package/dist/components/code-block/snice-code-block.js +1 -1
- package/dist/components/code-block/snice-code-block.js.map +1 -1
- package/dist/components/qr-code/qrcode.d.ts +1 -0
- package/dist/components/qr-code/qrcode.js +16 -8
- package/dist/components/qr-code/qrcode.js.map +1 -1
- package/dist/components/qr-code/snice-qr-code.d.ts +5 -2
- package/dist/components/qr-code/snice-qr-code.js +132 -11
- package/dist/components/qr-code/snice-qr-code.js.map +1 -1
- package/dist/components/qr-code/snice-qr-code.types.d.ts +3 -2
- package/dist/components/slider/snice-slider.js +1 -1
- package/dist/components/slider/snice-slider.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.esm.js +2 -2
- package/dist/index.iife.js +2 -2
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/request-options.d.ts +1 -1
- package/docs/ai/api.md +1 -1
- package/docs/ai/components/code-block.md +1 -1
- package/docs/ai/decorators.md +2 -2
- package/docs/ai/patterns.md +17 -6
- package/docs/code-block.md +1 -3
- package/docs/controllers.md +98 -391
- package/docs/elements.md +131 -117
- package/docs/events.md +74 -83
- package/docs/fetcher.md +64 -76
- package/docs/observe.md +13 -33
- package/docs/placards.md +6 -16
- package/docs/request-response.md +171 -693
- package/docs/routing.md +67 -136
- package/package.json +1 -1
- package/docs/migration-v2-to-v3.md +0 -569
package/docs/routing.md
CHANGED
|
@@ -32,14 +32,14 @@ const { page, initialize, navigate } = router;
|
|
|
32
32
|
### Router Options
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
-
interface RouterOptions
|
|
35
|
+
interface RouterOptions {
|
|
36
36
|
target: string; // Target element selector
|
|
37
37
|
type: 'hash' | 'pushstate'; // Routing type
|
|
38
38
|
window?: Window; // Override window object (for testing)
|
|
39
39
|
document?: Document; // Override document object (for testing)
|
|
40
40
|
transition?: Transition; // Global transition config
|
|
41
41
|
layout?: string; // Default layout for all pages
|
|
42
|
-
context?:
|
|
42
|
+
context?: any; // Router context object (shared state)
|
|
43
43
|
fetcher?: Fetcher; // Optional fetch middleware (see docs/fetcher.md)
|
|
44
44
|
}
|
|
45
45
|
```
|
|
@@ -76,7 +76,8 @@ const { page, initialize } = Router({
|
|
|
76
76
|
### Basic Page
|
|
77
77
|
|
|
78
78
|
```typescript
|
|
79
|
-
import {
|
|
79
|
+
import { render, html, styles, css } from 'snice';
|
|
80
|
+
import { page } from './router'; // page comes from Router(), not from 'snice'
|
|
80
81
|
|
|
81
82
|
@page({ tag: 'home-page', routes: ['/'] })
|
|
82
83
|
class HomePage extends HTMLElement {
|
|
@@ -120,7 +121,8 @@ class HomePage extends HTMLElement {
|
|
|
120
121
|
The `@context()` decorator is a **method decorator** that receives context updates from the router. The method is called whenever navigation occurs, with a Context object containing application state and navigation data.
|
|
121
122
|
|
|
122
123
|
```typescript
|
|
123
|
-
import {
|
|
124
|
+
import { context, render, html, Context } from 'snice';
|
|
125
|
+
import { page } from './router';
|
|
124
126
|
|
|
125
127
|
@page({ tag: 'profile-page', routes: ['/profile'] })
|
|
126
128
|
class ProfilePage extends HTMLElement {
|
|
@@ -208,7 +210,7 @@ interface Context {
|
|
|
208
210
|
application: AppContext; // Your router context (e.g., { user, theme, config })
|
|
209
211
|
navigation: {
|
|
210
212
|
placards: Placard[]; // All page placards
|
|
211
|
-
route: string; // Current
|
|
213
|
+
route: string; // Current path (e.g. '/users/123')
|
|
212
214
|
params: Record<string, string>; // Route parameters
|
|
213
215
|
};
|
|
214
216
|
fetch: typeof globalThis.fetch; // Fetch function with middleware support
|
|
@@ -273,12 +275,13 @@ class SettingsPage extends HTMLElement {
|
|
|
273
275
|
### @page Decorator Options
|
|
274
276
|
|
|
275
277
|
```typescript
|
|
276
|
-
interface PageOptions
|
|
278
|
+
interface PageOptions {
|
|
277
279
|
tag: string; // Custom element tag name
|
|
278
280
|
routes: string[]; // Route patterns
|
|
279
281
|
transition?: Transition; // Page-specific transition
|
|
280
|
-
guards?: Guard
|
|
281
|
-
|
|
282
|
+
guards?: Guard | Guard[]; // Route guards
|
|
283
|
+
layout?: string | false; // Layout tag, or false to disable
|
|
284
|
+
placard?: Placard | ((ctx: AppContext) => Placard); // Page metadata
|
|
282
285
|
}
|
|
283
286
|
```
|
|
284
287
|
|
|
@@ -455,25 +458,20 @@ class CommentPage extends HTMLElement {
|
|
|
455
458
|
|
|
456
459
|
### Query Parameters
|
|
457
460
|
|
|
458
|
-
|
|
461
|
+
Define query parameters directly in the route pattern — they are extracted as route params automatically:
|
|
459
462
|
|
|
460
463
|
```typescript
|
|
461
464
|
@page({
|
|
462
465
|
tag: 'search-page',
|
|
463
|
-
routes: ['/search']
|
|
466
|
+
routes: ['/search?q=:query']
|
|
464
467
|
})
|
|
465
468
|
class SearchPage extends HTMLElement {
|
|
466
469
|
@property()
|
|
467
470
|
query = '';
|
|
468
471
|
|
|
469
|
-
@
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
@ready()
|
|
473
|
-
parseQueryParams() {
|
|
474
|
-
const params = new URLSearchParams(window.location.search);
|
|
475
|
-
this.query = params.get('q') || '';
|
|
476
|
-
this.page = parseInt(params.get('page') || '1');
|
|
472
|
+
@context()
|
|
473
|
+
handleContext(ctx: Context) {
|
|
474
|
+
this.query = ctx.navigation.params.query || '';
|
|
477
475
|
}
|
|
478
476
|
|
|
479
477
|
@render()
|
|
@@ -481,7 +479,6 @@ class SearchPage extends HTMLElement {
|
|
|
481
479
|
return html`
|
|
482
480
|
<div>
|
|
483
481
|
<h1>Search Results for: ${this.query}</h1>
|
|
484
|
-
<p>Page: ${this.page}</p>
|
|
485
482
|
</div>
|
|
486
483
|
`;
|
|
487
484
|
}
|
|
@@ -493,7 +490,7 @@ class SearchPage extends HTMLElement {
|
|
|
493
490
|
### Global Transitions
|
|
494
491
|
|
|
495
492
|
```typescript
|
|
496
|
-
import { fadeTransition } from 'snice';
|
|
493
|
+
import { fadeTransition } from 'snice/transitions';
|
|
497
494
|
|
|
498
495
|
const router = Router({
|
|
499
496
|
target: '#app',
|
|
@@ -505,7 +502,7 @@ const router = Router({
|
|
|
505
502
|
### Page-Specific Transitions
|
|
506
503
|
|
|
507
504
|
```typescript
|
|
508
|
-
import { slideTransition } from 'snice';
|
|
505
|
+
import { slideTransition } from 'snice/transitions';
|
|
509
506
|
|
|
510
507
|
@page({
|
|
511
508
|
tag: 'about-page',
|
|
@@ -526,50 +523,36 @@ class AboutPage extends HTMLElement {
|
|
|
526
523
|
import {
|
|
527
524
|
fadeTransition,
|
|
528
525
|
slideTransition,
|
|
529
|
-
slideLeftTransition,
|
|
530
526
|
slideRightTransition,
|
|
531
527
|
slideUpTransition,
|
|
532
528
|
slideDownTransition,
|
|
533
|
-
scaleTransition
|
|
534
|
-
|
|
529
|
+
scaleTransition,
|
|
530
|
+
rotateTransition,
|
|
531
|
+
flipTransition,
|
|
532
|
+
zoomTransition,
|
|
533
|
+
noneTransition
|
|
534
|
+
} from 'snice/transitions';
|
|
535
535
|
```
|
|
536
536
|
|
|
537
537
|
### Custom Transitions
|
|
538
538
|
|
|
539
|
+
Transitions use inline CSS property strings for `out` (leaving) and `in` (entering) states:
|
|
540
|
+
|
|
539
541
|
```typescript
|
|
540
|
-
import { Transition } from 'snice';
|
|
542
|
+
import { Transition } from 'snice/transitions';
|
|
541
543
|
|
|
542
544
|
const customTransition: Transition = {
|
|
543
545
|
name: 'custom',
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
546
|
+
outDuration: 300,
|
|
547
|
+
inDuration: 300,
|
|
548
|
+
out: 'opacity: 0; transform: translateY(-20px)',
|
|
549
|
+
in: 'opacity: 1; transform: translateY(0)',
|
|
550
|
+
mode: 'sequential' // or 'simultaneous'
|
|
549
551
|
};
|
|
550
|
-
|
|
551
|
-
// CSS for custom transition
|
|
552
|
-
/*
|
|
553
|
-
.page-enter {
|
|
554
|
-
opacity: 0;
|
|
555
|
-
transform: translateY(20px);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
.page-enter-active {
|
|
559
|
-
transition: all 500ms ease-out;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
.page-leave {
|
|
563
|
-
opacity: 1;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
.page-leave-active {
|
|
567
|
-
opacity: 0;
|
|
568
|
-
transition: all 500ms ease-in;
|
|
569
|
-
}
|
|
570
|
-
*/
|
|
571
552
|
```
|
|
572
553
|
|
|
554
|
+
The `out` string is the end state of the leaving page. The `in` string is the end state of the entering page (which starts invisible and transitions to this state).
|
|
555
|
+
|
|
573
556
|
## Route Guards
|
|
574
557
|
|
|
575
558
|
Guards protect routes and can redirect unauthorized access:
|
|
@@ -579,7 +562,7 @@ Guards protect routes and can redirect unauthorized access:
|
|
|
579
562
|
```typescript
|
|
580
563
|
import { Guard } from 'snice';
|
|
581
564
|
|
|
582
|
-
const isAuthenticated: Guard<AppContext> = (ctx) => {
|
|
565
|
+
const isAuthenticated: Guard<AppContext> = (ctx, params) => {
|
|
583
566
|
return ctx.getUser() !== null;
|
|
584
567
|
};
|
|
585
568
|
|
|
@@ -599,7 +582,7 @@ class DashboardPage extends HTMLElement {
|
|
|
599
582
|
### Multiple Guards
|
|
600
583
|
|
|
601
584
|
```typescript
|
|
602
|
-
const hasAdminRole: Guard<AppContext> = (ctx) => {
|
|
585
|
+
const hasAdminRole: Guard<AppContext> = (ctx, params) => {
|
|
603
586
|
const user = ctx.getUser();
|
|
604
587
|
return user?.role === 'admin';
|
|
605
588
|
};
|
|
@@ -619,33 +602,26 @@ class AdminPage extends HTMLElement {
|
|
|
619
602
|
|
|
620
603
|
### Guard with Redirect
|
|
621
604
|
|
|
605
|
+
When a guard returns `false`, the router renders the `/403` page (if registered) or a default 403 message. Guards can also trigger side effects like redirects:
|
|
606
|
+
|
|
622
607
|
```typescript
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
// Redirect to login page
|
|
628
|
-
setTimeout(() => {
|
|
629
|
-
window.location.hash = '#/login';
|
|
630
|
-
}, 0);
|
|
608
|
+
const isAuthenticated: Guard<AppContext> = (ctx, params) => {
|
|
609
|
+
if (!ctx.getUser()) {
|
|
610
|
+
window.location.hash = '#/login';
|
|
611
|
+
return false;
|
|
631
612
|
}
|
|
632
|
-
|
|
633
|
-
return isAuth;
|
|
613
|
+
return true;
|
|
634
614
|
};
|
|
635
615
|
```
|
|
636
616
|
|
|
637
|
-
###
|
|
617
|
+
### Permission Guard
|
|
618
|
+
|
|
619
|
+
Guards are synchronous — pre-load permissions into context before navigating:
|
|
638
620
|
|
|
639
621
|
```typescript
|
|
640
|
-
const
|
|
622
|
+
const hasAdminAccess: Guard<AppContext> = (ctx, params) => {
|
|
641
623
|
const user = ctx.getUser();
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
// Check with API
|
|
645
|
-
const response = await fetch(`/api/permissions/${user.id}`);
|
|
646
|
-
const permissions = await response.json();
|
|
647
|
-
|
|
648
|
-
return permissions.includes('access_dashboard');
|
|
624
|
+
return user?.role === 'admin';
|
|
649
625
|
};
|
|
650
626
|
```
|
|
651
627
|
|
|
@@ -781,7 +757,7 @@ const router = Router({
|
|
|
781
757
|
@page({
|
|
782
758
|
tag: 'fullscreen-page',
|
|
783
759
|
routes: ['/fullscreen'],
|
|
784
|
-
layout:
|
|
760
|
+
layout: false // Disable layout for this page
|
|
785
761
|
})
|
|
786
762
|
class FullscreenPage extends HTMLElement {
|
|
787
763
|
@render()
|
|
@@ -985,38 +961,12 @@ class NotFoundPage extends HTMLElement {
|
|
|
985
961
|
@render()
|
|
986
962
|
renderContent() {
|
|
987
963
|
return html`
|
|
988
|
-
<div
|
|
964
|
+
<div>
|
|
989
965
|
<h1>404 - Page Not Found</h1>
|
|
990
|
-
<p>The page you're looking for doesn't exist.</p>
|
|
991
966
|
<a href="#/">Go Home</a>
|
|
992
967
|
</div>
|
|
993
968
|
`;
|
|
994
969
|
}
|
|
995
|
-
|
|
996
|
-
@styles()
|
|
997
|
-
errorStyles() {
|
|
998
|
-
return css`
|
|
999
|
-
.not-found {
|
|
1000
|
-
text-align: center;
|
|
1001
|
-
padding: 4rem;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
h1 {
|
|
1005
|
-
color: #e74c3c;
|
|
1006
|
-
font-size: 3rem;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
a {
|
|
1010
|
-
display: inline-block;
|
|
1011
|
-
margin-top: 2rem;
|
|
1012
|
-
padding: 0.5rem 2rem;
|
|
1013
|
-
background: #3498db;
|
|
1014
|
-
color: white;
|
|
1015
|
-
text-decoration: none;
|
|
1016
|
-
border-radius: 4px;
|
|
1017
|
-
}
|
|
1018
|
-
`;
|
|
1019
|
-
}
|
|
1020
970
|
}
|
|
1021
971
|
```
|
|
1022
972
|
|
|
@@ -1029,11 +979,6 @@ class AppContext {
|
|
|
1029
979
|
|
|
1030
980
|
setUser(user: User | null) {
|
|
1031
981
|
this.user = user;
|
|
1032
|
-
|
|
1033
|
-
// Redirect if logged out
|
|
1034
|
-
if (!user && window.location.hash.includes('/dashboard')) {
|
|
1035
|
-
window.location.hash = '#/login';
|
|
1036
|
-
}
|
|
1037
982
|
}
|
|
1038
983
|
|
|
1039
984
|
getUser() {
|
|
@@ -1045,8 +990,8 @@ class AppContext {
|
|
|
1045
990
|
}
|
|
1046
991
|
}
|
|
1047
992
|
|
|
1048
|
-
// Auth guard
|
|
1049
|
-
const isAuthenticated: Guard<AppContext> = (ctx) => {
|
|
993
|
+
// Auth guard — redirect to login if not authenticated
|
|
994
|
+
const isAuthenticated: Guard<AppContext> = (ctx, params) => {
|
|
1050
995
|
if (!ctx.isAuthenticated()) {
|
|
1051
996
|
window.location.hash = '#/login';
|
|
1052
997
|
return false;
|
|
@@ -1126,37 +1071,24 @@ class LoginPage extends HTMLElement {
|
|
|
1126
1071
|
}
|
|
1127
1072
|
```
|
|
1128
1073
|
|
|
1129
|
-
## Best Practices
|
|
1130
|
-
|
|
1131
|
-
1. **Use semantic routes**: `/users/123` instead of `/page?id=123`
|
|
1132
|
-
2. **Leverage route parameters**: Automatically mapped to properties
|
|
1133
|
-
3. **Use guards for protection**: Keep auth logic separate from pages
|
|
1134
|
-
4. **Implement transitions**: Smooth user experience between pages
|
|
1135
|
-
5. **Use layouts efficiently**: Share common UI without duplication
|
|
1136
|
-
6. **Handle 404s**: Always include a catch-all route
|
|
1137
|
-
7. **Use context for shared state**: Avoid prop drilling
|
|
1138
|
-
8. **Lazy load when needed**: Improve initial load time
|
|
1139
|
-
9. **Type your guards**: Use TypeScript generics for context
|
|
1140
|
-
10. **Test navigation**: Ensure all routes work correctly
|
|
1141
1074
|
|
|
1142
1075
|
## Router API Reference
|
|
1143
1076
|
|
|
1144
1077
|
### Router()
|
|
1145
1078
|
|
|
1146
1079
|
```typescript
|
|
1147
|
-
function Router
|
|
1148
|
-
page:
|
|
1149
|
-
navigate: (path: string) => void;
|
|
1080
|
+
function Router(options: RouterOptions): {
|
|
1081
|
+
page: (pageOptions: PageOptions) => ClassDecorator;
|
|
1150
1082
|
initialize: () => void;
|
|
1151
|
-
|
|
1152
|
-
|
|
1083
|
+
navigate: (path: string) => Promise<void>;
|
|
1084
|
+
register: (route: string, tag: string, transition?: Transition, guards?: Guard | Guard[]) => void;
|
|
1153
1085
|
}
|
|
1154
1086
|
```
|
|
1155
1087
|
|
|
1156
1088
|
### navigate()
|
|
1157
1089
|
|
|
1158
1090
|
```typescript
|
|
1159
|
-
navigate(path: string): void
|
|
1091
|
+
navigate(path: string): Promise<void>
|
|
1160
1092
|
```
|
|
1161
1093
|
|
|
1162
1094
|
Navigates to the specified path. Uses hash (#) or pushstate depending on router type.
|
|
@@ -1169,18 +1101,17 @@ initialize(): void
|
|
|
1169
1101
|
|
|
1170
1102
|
Initializes the router and starts listening for route changes. Must be called after all pages are defined.
|
|
1171
1103
|
|
|
1172
|
-
###
|
|
1173
|
-
|
|
1174
|
-
```typescript
|
|
1175
|
-
getCurrentRoute(): string
|
|
1176
|
-
```
|
|
1177
|
-
|
|
1178
|
-
Returns the current route path.
|
|
1179
|
-
|
|
1180
|
-
### getRouteParams()
|
|
1104
|
+
### register()
|
|
1181
1105
|
|
|
1182
1106
|
```typescript
|
|
1183
|
-
|
|
1107
|
+
register(
|
|
1108
|
+
route: string,
|
|
1109
|
+
tag: string,
|
|
1110
|
+
transition?: Transition,
|
|
1111
|
+
guards?: Guard | Guard[],
|
|
1112
|
+
layout?: string | false,
|
|
1113
|
+
placard?: Placard | ((ctx: AppContext) => Placard)
|
|
1114
|
+
): void
|
|
1184
1115
|
```
|
|
1185
1116
|
|
|
1186
|
-
|
|
1117
|
+
Manually register a route without using the `@page` decorator.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snice",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Imperative TypeScript framework for building vanilla web components with decorators, differential rendering, routing, and controllers. No virtual DOM, no build complexity.",
|
|
6
6
|
"main": "dist/index.cjs",
|