snice 7.5.0 → 7.7.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/adapters/react/SniceProvider.js +1 -1
- package/adapters/react/SniceRouter.js +1 -1
- package/adapters/react/avatar.d.ts +1 -0
- package/adapters/react/avatar.d.ts.map +1 -1
- package/adapters/react/avatar.js +1 -1
- package/adapters/react/avatar.js.map +1 -1
- package/adapters/react/avatar.tsx +2 -1
- package/adapters/react/calendar.d.ts +4 -0
- package/adapters/react/calendar.d.ts.map +1 -1
- package/adapters/react/calendar.js +1 -1
- package/adapters/react/calendar.js.map +1 -1
- package/adapters/react/calendar.tsx +5 -1
- package/adapters/react/matchRoute.js +68 -2
- package/adapters/react/matchRoute.js.map +1 -1
- package/adapters/react/route-match.d.ts +7 -0
- package/adapters/react/useRequestHandler.js +1 -1
- package/bin/analyzer-contracts.js +60 -2
- package/bin/project-analyzer.js +1262 -118
- package/bin/snice.js +95 -5
- package/custom-elements.json +112 -0
- package/dist/cdn/accordion/snice-accordion.js +1 -1
- package/dist/cdn/action-bar/snice-action-bar.js +1 -1
- package/dist/cdn/activity-feed/snice-activity-feed.js +1 -1
- package/dist/cdn/alert/snice-alert.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
- package/dist/cdn/approval-flow/snice-approval-flow.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
- package/dist/cdn/availability/snice-availability.js +1 -1
- package/dist/cdn/avatar/README.md +1 -1
- package/dist/cdn/avatar/snice-avatar.js +9 -3
- package/dist/cdn/avatar/snice-avatar.js.map +1 -1
- package/dist/cdn/avatar/snice-avatar.min.js +2 -2
- package/dist/cdn/avatar/snice-avatar.min.js.map +1 -1
- package/dist/cdn/avatar-group/snice-avatar-group.js +1 -1
- package/dist/cdn/badge/snice-badge.js +1 -1
- package/dist/cdn/banner/snice-banner.js +1 -1
- package/dist/cdn/binpack/snice-binpack.js +1 -1
- package/dist/cdn/book/snice-book.js +1 -1
- package/dist/cdn/booking/snice-booking.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
- package/dist/cdn/button/snice-button.js +2 -2
- package/dist/cdn/button/snice-button.js.map +1 -1
- package/dist/cdn/button/snice-button.min.js +1 -1
- package/dist/cdn/button/snice-button.min.js.map +1 -1
- package/dist/cdn/calendar/README.md +2 -2
- package/dist/cdn/calendar/snice-calendar.js +562 -40
- package/dist/cdn/calendar/snice-calendar.js.map +1 -1
- package/dist/cdn/calendar/snice-calendar.min.js +28 -1
- package/dist/cdn/calendar/snice-calendar.min.js.map +1 -1
- package/dist/cdn/camera/snice-camera.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.js +1 -1
- package/dist/cdn/card/snice-card.js +1 -1
- package/dist/cdn/carousel/snice-carousel.js +1 -1
- package/dist/cdn/cart/snice-cart.js +1 -1
- package/dist/cdn/chart/snice-chart.js +1 -1
- package/dist/cdn/chat/snice-chat.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.js +1 -1
- package/dist/cdn/chip/snice-chip.js +1 -1
- package/dist/cdn/code-block/snice-code-block.js +1 -1
- package/dist/cdn/color-display/snice-color-display.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.js +1 -1
- package/dist/cdn/comments/snice-comments.js +1 -1
- package/dist/cdn/countdown/snice-countdown.js +1 -1
- package/dist/cdn/cropper/snice-cropper.js +1 -1
- package/dist/cdn/data-card/snice-data-card.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.js +1 -1
- package/dist/cdn/date-range-picker/snice-date-range-picker.js +1 -1
- package/dist/cdn/date-time-picker/snice-date-time-picker.js +1 -1
- package/dist/cdn/diff/snice-diff.js +1 -1
- package/dist/cdn/divider/snice-divider.js +1 -1
- package/dist/cdn/doc/snice-doc.js +1 -1
- package/dist/cdn/draw/snice-draw.js +1 -1
- package/dist/cdn/drawer/snice-drawer.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.js +1 -1
- package/dist/cdn/estimate/snice-estimate.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.js +1 -1
- package/dist/cdn/flow/snice-flow.js +1 -1
- package/dist/cdn/form-layout/snice-form-layout.js +1 -1
- package/dist/cdn/funnel/snice-funnel.js +1 -1
- package/dist/cdn/gantt/snice-gantt.js +1 -1
- package/dist/cdn/gauge/snice-gauge.js +1 -1
- package/dist/cdn/grid/snice-grid.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.js +1 -1
- package/dist/cdn/image/snice-image.js +1 -1
- package/dist/cdn/input/snice-input.js +1 -1
- package/dist/cdn/invoice/snice-invoice.js +1 -1
- package/dist/cdn/kanban/snice-kanban.js +1 -1
- package/dist/cdn/key-value/snice-key-value.js +1 -1
- package/dist/cdn/kpi/snice-kpi.js +1 -1
- package/dist/cdn/layout/snice-layout.js +1 -1
- package/dist/cdn/leaderboard/snice-leaderboard.js +1 -1
- package/dist/cdn/link/snice-link.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.js +1 -1
- package/dist/cdn/list/snice-list.js +1 -1
- package/dist/cdn/location/snice-location.js +1 -1
- package/dist/cdn/login/README.md +1 -1
- package/dist/cdn/login/snice-login.js +2 -2
- package/dist/cdn/login/snice-login.js.map +1 -1
- package/dist/cdn/login/snice-login.min.js +1 -1
- package/dist/cdn/login/snice-login.min.js.map +1 -1
- package/dist/cdn/map/snice-map.js +1 -1
- package/dist/cdn/markdown/snice-markdown.js +1 -1
- package/dist/cdn/masonry/snice-masonry.js +1 -1
- package/dist/cdn/menu/snice-menu.js +1 -1
- package/dist/cdn/message-strip/snice-message-strip.js +1 -1
- package/dist/cdn/modal/snice-modal.js +1 -1
- package/dist/cdn/music-player/snice-music-player.js +1 -1
- package/dist/cdn/nav/snice-nav.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.js +1 -1
- package/dist/cdn/order-tracker/snice-order-tracker.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.js +1 -1
- package/dist/cdn/pagination/snice-pagination.js +1 -1
- package/dist/cdn/paint/snice-paint.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +1 -1
- package/dist/cdn/permission-matrix/snice-permission-matrix.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.js +1 -1
- package/dist/cdn/popover/snice-popover.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
- package/dist/cdn/product-card/snice-product-card.js +1 -1
- package/dist/cdn/progress/snice-progress.js +1 -1
- package/dist/cdn/progress-ring/snice-progress-ring.js +1 -1
- package/dist/cdn/qr-code/snice-qr-code.js +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
- package/dist/cdn/radio/snice-radio.js +1 -1
- package/dist/cdn/range-slider/snice-range-slider.js +1 -1
- package/dist/cdn/rating/snice-rating.js +1 -1
- package/dist/cdn/receipt/snice-receipt.js +1 -1
- package/dist/cdn/recipe/snice-recipe.js +1 -1
- package/dist/cdn/runtime/README.md +2 -2
- package/dist/cdn/runtime/snice-runtime.esm.js +706 -267
- package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.js +706 -267
- package/dist/cdn/runtime/snice-runtime.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
- package/dist/cdn/sankey/snice-sankey.js +1 -1
- package/dist/cdn/segmented-control/snice-segmented-control.js +1 -1
- package/dist/cdn/select/snice-select.js +3 -1
- package/dist/cdn/select/snice-select.js.map +1 -1
- package/dist/cdn/select/snice-select.min.js +2 -2
- package/dist/cdn/select/snice-select.min.js.map +1 -1
- package/dist/cdn/skeleton/snice-skeleton.js +1 -1
- package/dist/cdn/slider/snice-slider.js +1 -1
- package/dist/cdn/sortable/snice-sortable.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.js +1 -1
- package/dist/cdn/spinner/snice-spinner.js +1 -1
- package/dist/cdn/split-button/snice-split-button.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.js +1 -1
- package/dist/cdn/stat-group/snice-stat-group.js +1 -1
- package/dist/cdn/step-input/snice-step-input.js +1 -1
- package/dist/cdn/stepper/snice-stepper.js +1 -1
- package/dist/cdn/switch/README.md +1 -1
- package/dist/cdn/switch/snice-switch.js +8 -4
- package/dist/cdn/switch/snice-switch.js.map +1 -1
- package/dist/cdn/switch/snice-switch.min.js +8 -4
- package/dist/cdn/switch/snice-switch.min.js.map +1 -1
- package/dist/cdn/table/README.md +2 -2
- package/dist/cdn/table/snice-table.esm.js +77 -5
- package/dist/cdn/table/snice-table.esm.js.map +1 -1
- package/dist/cdn/table/snice-table.esm.min.js +68 -47
- package/dist/cdn/table/snice-table.esm.min.js.map +1 -1
- package/dist/cdn/table/snice-table.js +76 -4
- package/dist/cdn/table/snice-table.js.map +1 -1
- package/dist/cdn/table/snice-table.min.js +53 -32
- package/dist/cdn/table/snice-table.min.js.map +1 -1
- package/dist/cdn/tabs/snice-tabs.js +1 -1
- package/dist/cdn/tag/snice-tag.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.js +1 -1
- package/dist/cdn/terminal/snice-terminal.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.js +1 -1
- package/dist/cdn/textarea/snice-textarea.js +1 -1
- package/dist/cdn/time-picker/snice-time-picker.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
- package/dist/cdn/timeline/snice-timeline.js +1 -1
- package/dist/cdn/timer/snice-timer.js +1 -1
- package/dist/cdn/toast/snice-toast.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.js +1 -1
- package/dist/cdn/tree/snice-tree.js +1 -1
- package/dist/cdn/treemap/snice-treemap.js +1 -1
- package/dist/cdn/user-card/snice-user-card.js +1 -1
- package/dist/cdn/video-player/snice-video-player.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.js +1 -1
- package/dist/cdn/weather/snice-weather.js +1 -1
- package/dist/cdn/work-order/snice-work-order.js +1 -1
- package/dist/components/avatar/snice-avatar.d.ts +1 -0
- package/dist/components/avatar/snice-avatar.js +8 -2
- package/dist/components/avatar/snice-avatar.js.map +1 -1
- package/dist/components/avatar/snice-avatar.types.d.ts +2 -0
- package/dist/components/button/snice-button.js +1 -1
- package/dist/components/button/snice-button.js.map +1 -1
- package/dist/components/calendar/snice-calendar.d.ts +38 -1
- package/dist/components/calendar/snice-calendar.js +390 -40
- package/dist/components/calendar/snice-calendar.js.map +1 -1
- package/dist/components/calendar/snice-calendar.types.d.ts +63 -0
- package/dist/components/select/snice-select.js +2 -0
- package/dist/components/select/snice-select.js.map +1 -1
- package/dist/components/switch/snice-switch.js +7 -3
- package/dist/components/switch/snice-switch.js.map +1 -1
- package/dist/components/table/snice-table.d.ts +9 -0
- package/dist/components/table/snice-table.js +72 -2
- package/dist/components/table/snice-table.js.map +1 -1
- package/dist/components/table/table-keyboard.js +1 -1
- package/dist/components/table/table-keyboard.js.map +1 -1
- package/dist/index.cjs +703 -264
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.esm.js +703 -264
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +703 -264
- package/dist/index.iife.js.map +1 -1
- package/dist/parts.d.ts +14 -4
- package/dist/react/SniceProvider.js +1 -1
- package/dist/react/SniceRouter.js +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/matchRoute.js +68 -2
- package/dist/react/matchRoute.js.map +1 -1
- package/dist/react/route-match.d.ts +7 -0
- package/dist/react/useRequestHandler.js +1 -1
- package/dist/render-errors.d.ts +9 -0
- package/dist/route-match.d.ts +7 -0
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/template-marker.d.ts +5 -0
- package/dist/testing-dom.cjs +1 -1
- package/dist/testing-dom.esm.js +1 -1
- package/dist/testing.d.ts +3 -0
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/dispatch-options.d.ts +5 -4
- package/dist/types/event-timing.d.ts +7 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/on-options.d.ts +21 -6
- package/dist/utils.d.ts +3 -0
- package/docs/ai/DEVELOPMENT.md +1 -1
- package/docs/ai/api.md +7 -2
- package/docs/ai/cli.md +14 -0
- package/docs/ai/components/avatar.md +1 -0
- package/docs/ai/components/calendar.md +24 -1
- package/docs/ai/components/switch.md +3 -0
- package/docs/ai/components/table.md +1 -1
- package/docs/ai/decorators.md +3 -2
- package/docs/ai/events.md +45 -8
- package/docs/ai/rendering.md +7 -0
- package/docs/ai/routing.md +6 -1
- package/docs/ai/testing.md +17 -0
- package/docs/cli.md +19 -0
- package/docs/components/avatar.md +1 -0
- package/docs/components/calendar.md +112 -2
- package/docs/components/switch.md +7 -1
- package/docs/components/table.md +2 -1
- package/docs/events.md +90 -10
- package/docs/rendering.md +10 -0
- package/docs/routing.md +22 -1
- package/docs/testing.md +19 -0
- package/package.json +2 -2
- package/vscode.html-custom-data.json +28 -0
package/dist/parts.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { TemplateResult } from './template';
|
|
2
|
+
import { type RenderHostIdentity } from './render-errors';
|
|
3
|
+
type PropertyBindingRegistry = Pick<CustomElementRegistry, 'get' | 'whenDefined'>;
|
|
2
4
|
export declare const noChange: unique symbol;
|
|
3
5
|
export type NoChange = typeof noChange;
|
|
4
6
|
declare const LIVE_MARKER: unique symbol;
|
|
@@ -45,18 +47,21 @@ interface TemplatePart {
|
|
|
45
47
|
* Instance of a rendered template
|
|
46
48
|
*/
|
|
47
49
|
export declare class TemplateInstance {
|
|
50
|
+
#private;
|
|
48
51
|
template: Template;
|
|
49
52
|
strings: TemplateStringsArray;
|
|
50
53
|
parts: Part[];
|
|
51
54
|
fragment: DocumentFragment | null;
|
|
52
55
|
private conditionalParts;
|
|
53
56
|
private regularParts;
|
|
54
|
-
constructor(result: TemplateResult);
|
|
57
|
+
constructor(result: TemplateResult, renderIdentity?: RenderHostIdentity | null, customElementRegistry?: PropertyBindingRegistry | null);
|
|
55
58
|
/**
|
|
56
59
|
* Check if this instance uses the same template strings
|
|
57
60
|
* (template identity is based on the strings array reference)
|
|
58
61
|
*/
|
|
59
62
|
isSameTemplate(strings: TemplateStringsArray): boolean;
|
|
63
|
+
/** Internal attribution value; intentionally contains no live renderer host. */
|
|
64
|
+
getRenderHostIdentity(): RenderHostIdentity | null;
|
|
60
65
|
renderFragment(): DocumentFragment;
|
|
61
66
|
render(values: readonly any[]): DocumentFragment;
|
|
62
67
|
update(values: readonly any[]): void;
|
|
@@ -85,6 +90,7 @@ export declare abstract class Part {
|
|
|
85
90
|
* - Reuses child NodeParts when rendering arrays
|
|
86
91
|
*/
|
|
87
92
|
export declare class NodePart extends Part {
|
|
93
|
+
#private;
|
|
88
94
|
readonly type: "node";
|
|
89
95
|
private startNode;
|
|
90
96
|
private endNode;
|
|
@@ -98,7 +104,8 @@ export declare class NodePart extends Part {
|
|
|
98
104
|
private _asyncCompleted;
|
|
99
105
|
private _asyncPaused;
|
|
100
106
|
private _committingAsyncValue;
|
|
101
|
-
constructor(startNode: Comment, endNode: Comment);
|
|
107
|
+
constructor(startNode: Comment, endNode: Comment, renderIdentity?: RenderHostIdentity | null, customElementRegistry?: PropertyBindingRegistry | null);
|
|
108
|
+
private _withRenderContext;
|
|
102
109
|
commit(value: any): void;
|
|
103
110
|
/**
|
|
104
111
|
* Commit a primitive value as text
|
|
@@ -209,13 +216,14 @@ export declare class StylePart extends Part {
|
|
|
209
216
|
}
|
|
210
217
|
/** Named spreads: ...props, ...attrs, and ...events. */
|
|
211
218
|
export declare class SpreadPart extends Part {
|
|
219
|
+
#private;
|
|
212
220
|
readonly type: "spread";
|
|
213
221
|
element: Element;
|
|
214
222
|
readonly name: string;
|
|
215
223
|
private committed;
|
|
216
224
|
private listeners;
|
|
217
225
|
private consumedOnce;
|
|
218
|
-
constructor(element: Element, name: string);
|
|
226
|
+
constructor(element: Element, name: string, customElementRegistry?: PropertyBindingRegistry | null);
|
|
219
227
|
commit(value: unknown): void;
|
|
220
228
|
private commitProperties;
|
|
221
229
|
private commitAttributes;
|
|
@@ -231,12 +239,14 @@ export declare class SpreadPart extends Part {
|
|
|
231
239
|
* PropertyPart handles property bindings (.property=${value})
|
|
232
240
|
*/
|
|
233
241
|
export declare class PropertyPart extends Part {
|
|
242
|
+
#private;
|
|
234
243
|
readonly type: "property";
|
|
235
244
|
element: Element;
|
|
236
245
|
readonly name: string;
|
|
237
246
|
private _committedValue;
|
|
238
|
-
constructor(element: Element, name: string);
|
|
247
|
+
constructor(element: Element, name: string, customElementRegistry?: PropertyBindingRegistry | null);
|
|
239
248
|
commit(value: unknown): void;
|
|
249
|
+
reconnected(): void;
|
|
240
250
|
clear(): void;
|
|
241
251
|
}
|
|
242
252
|
/**
|
package/dist/react/index.js
CHANGED
package/dist/react/matchRoute.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v7.
|
|
2
|
+
* snice v7.6.0
|
|
3
3
|
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
4
4
|
* (c) 2024
|
|
5
5
|
* Released under the MIT License.
|
|
@@ -42,6 +42,72 @@ function routeSpecificity(spec) {
|
|
|
42
42
|
return score;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
const EMPTY_QUERY_MARKER = '__snice_empty_query_value__';
|
|
46
|
+
const QUERY_PARAM_PATTERN = /=:[\w-]+\)?$/;
|
|
47
|
+
function containsDecodedMarker(url, marker) {
|
|
48
|
+
if (url.includes(marker))
|
|
49
|
+
return true;
|
|
50
|
+
const queryIndex = url.indexOf('?');
|
|
51
|
+
if (queryIndex === -1)
|
|
52
|
+
return false;
|
|
53
|
+
return url.slice(queryIndex + 1).split('&').some(part => {
|
|
54
|
+
try {
|
|
55
|
+
return decodeURIComponent(part).includes(marker);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function emptyQueryMarker(route, url) {
|
|
63
|
+
let marker = EMPTY_QUERY_MARKER;
|
|
64
|
+
while (route.spec.includes(marker) || containsDecodedMarker(url, marker)) {
|
|
65
|
+
marker += '_';
|
|
66
|
+
}
|
|
67
|
+
return marker;
|
|
68
|
+
}
|
|
69
|
+
function fillEmptyQueryParams(route, url, marker) {
|
|
70
|
+
const urlQueryIndex = url.indexOf('?');
|
|
71
|
+
const routeQueryIndex = route.spec.indexOf('?');
|
|
72
|
+
if (urlQueryIndex === -1 || routeQueryIndex === -1)
|
|
73
|
+
return null;
|
|
74
|
+
const urlParts = url.slice(urlQueryIndex + 1).split('&');
|
|
75
|
+
const routeParts = route.spec.slice(routeQueryIndex + 1).split('&');
|
|
76
|
+
let changed = false;
|
|
77
|
+
const normalizedParts = urlParts.map((part, index) => {
|
|
78
|
+
if (part.endsWith('=') && QUERY_PARAM_PATTERN.test(routeParts[index] ?? '')) {
|
|
79
|
+
changed = true;
|
|
80
|
+
return `${part}${marker}`;
|
|
81
|
+
}
|
|
82
|
+
return part;
|
|
83
|
+
});
|
|
84
|
+
if (!changed)
|
|
85
|
+
return null;
|
|
86
|
+
return `${url.slice(0, urlQueryIndex + 1)}${normalizedParts.join('&')}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Match through pica-route while preserving legal empty query parameter values.
|
|
90
|
+
* pica-route 1.1.2 rejects an empty param and therefore discards the whole route.
|
|
91
|
+
* Keep this compatibility shim in sync with packages/core/src/route-match.ts.
|
|
92
|
+
*/
|
|
93
|
+
function matchRoute(route, url) {
|
|
94
|
+
const directMatch = route.match(url);
|
|
95
|
+
if (directMatch !== false)
|
|
96
|
+
return directMatch;
|
|
97
|
+
const marker = emptyQueryMarker(route, url);
|
|
98
|
+
const normalizedUrl = fillEmptyQueryParams(route, url, marker);
|
|
99
|
+
if (normalizedUrl === null)
|
|
100
|
+
return false;
|
|
101
|
+
const params = route.match(normalizedUrl);
|
|
102
|
+
if (params === false)
|
|
103
|
+
return false;
|
|
104
|
+
for (const name of Object.keys(params)) {
|
|
105
|
+
if (params[name] === marker)
|
|
106
|
+
params[name] = '';
|
|
107
|
+
}
|
|
108
|
+
return params;
|
|
109
|
+
}
|
|
110
|
+
|
|
45
111
|
/**
|
|
46
112
|
* Match a URL path against an array of route configs.
|
|
47
113
|
* Uses pica-route — same matching as vanilla Snice's Router.
|
|
@@ -62,7 +128,7 @@ function matchRoutes(routes, pathname) {
|
|
|
62
128
|
|| a.registrationOrder - b.registrationOrder);
|
|
63
129
|
for (const route of sorted) {
|
|
64
130
|
const matcher = new Route(route.path);
|
|
65
|
-
const params = matcher
|
|
131
|
+
const params = matchRoute(matcher, pathname);
|
|
66
132
|
if (params !== false) {
|
|
67
133
|
return {
|
|
68
134
|
index: route.index,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matchRoute.js","sources":["../../src/react/route-specificity.ts","../../src/react/matchRoute.ts"],"sourcesContent":["/**\n * Route specificity scorer for the React binding.\n *\n * IMPORTANT: keep this in exact sync with `src/route-specificity.ts`. The React\n * integration is bundled in isolation (rootDir: src/react) and cannot import\n * across that boundary, so the scorer is mirrored here. The `matchRoutes`\n * resolution tests exercise this copy, so behavioral drift is caught.\n *\n * Scoring is per-segment by ROLE, not string length: a static segment outweighs\n * a dynamic (`:param`) segment, which outweighs a wildcard (`*` / `*name`). So\n * `/post/new` beats `/post/:id`, and a splat always ranks last. Sort callers\n * descending by this score (highest = most specific = first).\n */\nconst STATIC_SEGMENT = 10;\nconst DYNAMIC_SEGMENT = 3;\nconst WILDCARD_SEGMENT = -2;\n\nexport function routeSpecificity(spec: string): number {\n const segments = spec.split('/').filter(Boolean);\n // Base on segment count so a deeper path outranks a shallower one on ties.\n let score = segments.length;\n\n for (const segment of segments) {\n if (segment.startsWith('*')) {\n score += WILDCARD_SEGMENT;\n } else if (segment.startsWith(':')) {\n score += DYNAMIC_SEGMENT;\n } else {\n score += STATIC_SEGMENT;\n }\n }\n\n return score;\n}\n","import { Route, type RouteParams } from 'pica-route';\nimport { routeSpecificity } from './route-specificity';\n\nexport interface RouteConfig {\n path: string;\n index: number;\n /** Lower values win when route specificity ties. Defaults to 0. */\n order?: number;\n}\n\nexport interface MatchResult {\n index: number;\n params: RouteParams;\n path: string;\n}\n\n/**\n * Match a URL path against an array of route configs.\n * Uses pica-route — same matching as vanilla Snice's Router.\n * Routes are sorted by per-segment specificity (static > dynamic > wildcard),\n * then optional lower-first order, then declaration order.\n */\nexport function matchRoutes(routes: RouteConfig[], pathname: string): MatchResult | null {\n for (const route of routes) {\n if (route.order !== undefined && !Number.isFinite(route.order)) {\n throw new TypeError(`Route order for \"${route.path}\" must be a finite number.`);\n }\n }\n // Sort by specificity (most specific first), same model as the vanilla Router.\n const sorted = routes\n .map((route, registrationOrder) => ({ ...route, registrationOrder }))\n .sort((a, b) =>\n routeSpecificity(b.path) - routeSpecificity(a.path)\n || (a.order ?? 0) - (b.order ?? 0)\n || a.registrationOrder - b.registrationOrder\n );\n\n for (const route of sorted) {\n const matcher = new Route(route.path);\n const params = matcher
|
|
1
|
+
{"version":3,"file":"matchRoute.js","sources":["../../src/react/route-specificity.ts","../../src/react/route-match.ts","../../src/react/matchRoute.ts"],"sourcesContent":["/**\n * Route specificity scorer for the React binding.\n *\n * IMPORTANT: keep this in exact sync with `src/route-specificity.ts`. The React\n * integration is bundled in isolation (rootDir: src/react) and cannot import\n * across that boundary, so the scorer is mirrored here. The `matchRoutes`\n * resolution tests exercise this copy, so behavioral drift is caught.\n *\n * Scoring is per-segment by ROLE, not string length: a static segment outweighs\n * a dynamic (`:param`) segment, which outweighs a wildcard (`*` / `*name`). So\n * `/post/new` beats `/post/:id`, and a splat always ranks last. Sort callers\n * descending by this score (highest = most specific = first).\n */\nconst STATIC_SEGMENT = 10;\nconst DYNAMIC_SEGMENT = 3;\nconst WILDCARD_SEGMENT = -2;\n\nexport function routeSpecificity(spec: string): number {\n const segments = spec.split('/').filter(Boolean);\n // Base on segment count so a deeper path outranks a shallower one on ties.\n let score = segments.length;\n\n for (const segment of segments) {\n if (segment.startsWith('*')) {\n score += WILDCARD_SEGMENT;\n } else if (segment.startsWith(':')) {\n score += DYNAMIC_SEGMENT;\n } else {\n score += STATIC_SEGMENT;\n }\n }\n\n return score;\n}\n","import type { Route, RouteParams } from 'pica-route';\n\nconst EMPTY_QUERY_MARKER = '__snice_empty_query_value__';\nconst QUERY_PARAM_PATTERN = /=:[\\w-]+\\)?$/;\n\nfunction containsDecodedMarker(url: string, marker: string): boolean {\n if (url.includes(marker)) return true;\n\n const queryIndex = url.indexOf('?');\n if (queryIndex === -1) return false;\n\n return url.slice(queryIndex + 1).split('&').some(part => {\n try {\n return decodeURIComponent(part).includes(marker);\n } catch {\n return false;\n }\n });\n}\n\nfunction emptyQueryMarker(route: Route, url: string): string {\n let marker = EMPTY_QUERY_MARKER;\n while (route.spec.includes(marker) || containsDecodedMarker(url, marker)) {\n marker += '_';\n }\n return marker;\n}\n\nfunction fillEmptyQueryParams(route: Route, url: string, marker: string): string | null {\n const urlQueryIndex = url.indexOf('?');\n const routeQueryIndex = route.spec.indexOf('?');\n if (urlQueryIndex === -1 || routeQueryIndex === -1) return null;\n\n const urlParts = url.slice(urlQueryIndex + 1).split('&');\n const routeParts = route.spec.slice(routeQueryIndex + 1).split('&');\n let changed = false;\n\n const normalizedParts = urlParts.map((part, index) => {\n if (part.endsWith('=') && QUERY_PARAM_PATTERN.test(routeParts[index] ?? '')) {\n changed = true;\n return `${part}${marker}`;\n }\n return part;\n });\n\n if (!changed) return null;\n return `${url.slice(0, urlQueryIndex + 1)}${normalizedParts.join('&')}`;\n}\n\n/**\n * Match through pica-route while preserving legal empty query parameter values.\n * pica-route 1.1.2 rejects an empty param and therefore discards the whole route.\n * Keep this compatibility shim in sync with packages/core/src/route-match.ts.\n */\nexport function matchRoute(route: Route, url: string): RouteParams | false {\n const directMatch = route.match(url);\n if (directMatch !== false) return directMatch;\n\n const marker = emptyQueryMarker(route, url);\n const normalizedUrl = fillEmptyQueryParams(route, url, marker);\n if (normalizedUrl === null) return false;\n\n const params = route.match(normalizedUrl);\n if (params === false) return false;\n\n for (const name of Object.keys(params)) {\n if (params[name] === marker) params[name] = '';\n }\n return params;\n}\n","import { Route, type RouteParams } from 'pica-route';\nimport { routeSpecificity } from './route-specificity';\nimport { matchRoute } from './route-match';\n\nexport interface RouteConfig {\n path: string;\n index: number;\n /** Lower values win when route specificity ties. Defaults to 0. */\n order?: number;\n}\n\nexport interface MatchResult {\n index: number;\n params: RouteParams;\n path: string;\n}\n\n/**\n * Match a URL path against an array of route configs.\n * Uses pica-route — same matching as vanilla Snice's Router.\n * Routes are sorted by per-segment specificity (static > dynamic > wildcard),\n * then optional lower-first order, then declaration order.\n */\nexport function matchRoutes(routes: RouteConfig[], pathname: string): MatchResult | null {\n for (const route of routes) {\n if (route.order !== undefined && !Number.isFinite(route.order)) {\n throw new TypeError(`Route order for \"${route.path}\" must be a finite number.`);\n }\n }\n // Sort by specificity (most specific first), same model as the vanilla Router.\n const sorted = routes\n .map((route, registrationOrder) => ({ ...route, registrationOrder }))\n .sort((a, b) =>\n routeSpecificity(b.path) - routeSpecificity(a.path)\n || (a.order ?? 0) - (b.order ?? 0)\n || a.registrationOrder - b.registrationOrder\n );\n\n for (const route of sorted) {\n const matcher = new Route(route.path);\n const params = matchRoute(matcher, pathname);\n if (params !== false) {\n return {\n index: route.index,\n params: params as RouteParams,\n path: route.path,\n };\n }\n }\n\n return null;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Route, RouteParams } from 'pica-route';
|
|
2
|
+
/**
|
|
3
|
+
* Match through pica-route while preserving legal empty query parameter values.
|
|
4
|
+
* pica-route 1.1.2 rejects an empty param and therefore discards the whole route.
|
|
5
|
+
* Keep this compatibility shim in sync with packages/core/src/route-match.ts.
|
|
6
|
+
*/
|
|
7
|
+
export declare function matchRoute(route: Route, url: string): RouteParams | false;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Sanitized render attribution. It contains no host, node, constructor, or prototype. */
|
|
2
|
+
export interface RenderHostIdentity {
|
|
3
|
+
readonly label: string;
|
|
4
|
+
readonly marker: symbol;
|
|
5
|
+
}
|
|
6
|
+
export declare function registerRenderHostIdentity(constructor: unknown, tagName: string): void;
|
|
7
|
+
/** Capture once at the render boundary; failures fall back without masking the render error. */
|
|
8
|
+
export declare function captureRenderHostIdentity(host: HTMLElement): RenderHostIdentity;
|
|
9
|
+
export declare function contextualizeRenderError(identity: RenderHostIdentity, error: unknown): Error;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Route, RouteParams } from 'pica-route';
|
|
2
|
+
/**
|
|
3
|
+
* Match through pica-route while preserving legal empty query parameter values.
|
|
4
|
+
* pica-route 1.1.2 rejects an empty param and therefore discards the whole route.
|
|
5
|
+
* Keep this compatibility shim in sync with packages/react/src/route-match.ts.
|
|
6
|
+
*/
|
|
7
|
+
export declare function matchRoute(route: Route, url: string): RouteParams | false;
|
package/dist/symbols.cjs
CHANGED
package/dist/symbols.esm.js
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const templateMarker: string;
|
|
2
|
+
export declare const templateMarkerMatch: string;
|
|
3
|
+
export declare const templateNodeMarker: string;
|
|
4
|
+
export declare const templateMarkerRegex: RegExp;
|
|
5
|
+
export declare function hasTemplateMarker(value: string | null): boolean;
|
package/dist/testing-dom.cjs
CHANGED
package/dist/testing-dom.esm.js
CHANGED
package/dist/testing.d.ts
CHANGED
|
@@ -2,3 +2,6 @@ export * from './index';
|
|
|
2
2
|
export { registerControllerCleanup, getControllerScope, attachController, detachController, getController, cleanupNativeElementControllers } from './controller';
|
|
3
3
|
export { CONTROLLER_KEY, CONTROLLER_OPERATIONS, CONTROLLER_NAME_KEY, CONTROLLER_ID, PROPERTIES_INITIALIZED, PROPERTY_VALUES, PROPERTY_WATCHERS, EXPLICITLY_SET_PROPERTIES, READY_PROMISE, READY_RESOLVE, READY_HANDLERS, DISPOSE_HANDLERS, PARTS, PART_TIMERS, ROUTER_CONTEXT, CONTEXT_REQUEST_HANDLER, PAGE_TRANSITION, CREATED_AT, IS_ELEMENT_CLASS, IS_CONTROLLER_CLASS, IS_CONTROLLER_INSTANCE, IS_DAEMON_CLASS, IS_DAEMON_INSTANCE, DAEMON_STATE, NATIVE_CONTROLLER, DIRECT_CONTROLLER, PENDING_CONTROLLER_BINDING, CLEANUP, ON_HANDLERS, DISPATCH_TIMERS, CHANNEL_HANDLERS, OBSERVERS } from './symbols';
|
|
4
4
|
export { parseAttributeValue, detectType } from './utils';
|
|
5
|
+
export { TemplateInstance } from './parts';
|
|
6
|
+
export { captureRenderHostIdentity } from './render-errors';
|
|
7
|
+
export { renderElementNow } from './render';
|
package/dist/transitions.cjs
CHANGED
package/dist/transitions.esm.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { OnScope } from './on-options';
|
|
2
|
+
import type { EventTiming } from './event-timing';
|
|
2
3
|
export interface DispatchOptions extends EventInit {
|
|
3
4
|
/**
|
|
4
5
|
* Whether to dispatch even if the method returns undefined (default: true)
|
|
5
6
|
*/
|
|
6
7
|
dispatchOnUndefined?: boolean;
|
|
7
|
-
/** Debounce
|
|
8
|
-
debounce?:
|
|
9
|
-
/** Throttle
|
|
10
|
-
throttle?:
|
|
8
|
+
/** Debounce by milliseconds or resolve milliseconds from this instance */
|
|
9
|
+
debounce?: EventTiming;
|
|
10
|
+
/** Throttle by milliseconds or resolve milliseconds from this instance */
|
|
11
|
+
throttle?: EventTiming;
|
|
11
12
|
/**
|
|
12
13
|
* Where to dispatch the event. Default is the host element.
|
|
13
14
|
* Use `'global'` to dispatch on `document`, a selector for an ancestor
|
package/dist/types/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './page-options';
|
|
|
13
13
|
export * from './router-instance';
|
|
14
14
|
export * from './on-options';
|
|
15
15
|
export * from './dispatch-options';
|
|
16
|
+
export * from './event-timing';
|
|
16
17
|
export * from './i-controller';
|
|
17
18
|
export * from './transition';
|
|
18
19
|
export * from './observe-options';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EventTiming } from './event-timing';
|
|
1
2
|
/**
|
|
2
3
|
* Resolver for the EventTarget where the listener is attached.
|
|
3
4
|
* - `'global'`: attach to `document` (cross-cutting events)
|
|
@@ -9,7 +10,9 @@ export type OnScope = 'global' | string | EventTarget | ((this: HTMLElement) =>
|
|
|
9
10
|
export interface OnOptions {
|
|
10
11
|
/** Use capture phase instead of bubble phase */
|
|
11
12
|
capture?: boolean;
|
|
12
|
-
/**
|
|
13
|
+
/** Run the handler exactly once, then remove every listener it registered.
|
|
14
|
+
* Events that don't match the delegation selector or key filter don't
|
|
15
|
+
* consume it. */
|
|
13
16
|
once?: boolean;
|
|
14
17
|
/** Passive listener (can't preventDefault) */
|
|
15
18
|
passive?: boolean;
|
|
@@ -17,12 +20,24 @@ export interface OnOptions {
|
|
|
17
20
|
preventDefault?: boolean;
|
|
18
21
|
/** Automatically call stopPropagation on the event */
|
|
19
22
|
stopPropagation?: boolean;
|
|
20
|
-
/** Debounce
|
|
21
|
-
debounce?:
|
|
22
|
-
/** Throttle
|
|
23
|
-
throttle?:
|
|
24
|
-
/** CSS selector
|
|
23
|
+
/** Debounce by milliseconds or resolve milliseconds from this instance */
|
|
24
|
+
debounce?: EventTiming;
|
|
25
|
+
/** Throttle by milliseconds or resolve milliseconds from this instance */
|
|
26
|
+
throttle?: EventTiming;
|
|
27
|
+
/** CSS selector for event delegation; equivalent to the positional selector argument */
|
|
25
28
|
target?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Listen in the light DOM (the host element and its light-DOM children).
|
|
31
|
+
* Same tree toggle as @query. Defaults to true; combine with `shadow` to
|
|
32
|
+
* pick light, shadow, or both. Ignored when `scope`/`daemon` is set.
|
|
33
|
+
*/
|
|
34
|
+
light?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Listen in the shadow tree (the component's shadow root).
|
|
37
|
+
* Same tree toggle as @query. Defaults to true; combine with `light` to
|
|
38
|
+
* pick light, shadow, or both. Ignored when `scope`/`daemon` is set.
|
|
39
|
+
*/
|
|
40
|
+
shadow?: boolean;
|
|
26
41
|
/**
|
|
27
42
|
* Where to attach the listener. Default is the host element.
|
|
28
43
|
* Use `'global'` for document-wide events, a selector for an ancestor
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PropertyOptions } from './types';
|
|
2
|
+
import type { EventTiming } from './types/event-timing';
|
|
2
3
|
/**
|
|
3
4
|
* Detects the type constructor from an initial value
|
|
4
5
|
* @param initialValue - The default value assigned to a property field (e.g., `name = "default"` -> "default")
|
|
@@ -108,6 +109,8 @@ export declare function createDebounced<T extends (...args: any[]) => any>(fn: T
|
|
|
108
109
|
* Create a throttled version of a function (leading + trailing edge)
|
|
109
110
|
*/
|
|
110
111
|
export declare function createThrottled<T extends (...args: any[]) => any>(fn: T, delay: number): T;
|
|
112
|
+
/** Resolve and validate an @on/@dispatch debounce or throttle interval. */
|
|
113
|
+
export declare function resolveEventTiming(instance: object, timing: EventTiming | undefined, decorator: '@on' | '@dispatch', option: 'debounce' | 'throttle'): number | undefined;
|
|
111
114
|
/**
|
|
112
115
|
* Resolve a scope option to a concrete EventTarget.
|
|
113
116
|
*
|
package/docs/ai/DEVELOPMENT.md
CHANGED
|
@@ -140,7 +140,7 @@ All CDN builds use the shared runtime (external `snice` imports). Load `snice-ru
|
|
|
140
140
|
- IIFE: `.js` + `.min.js`
|
|
141
141
|
- Sourcemaps + README
|
|
142
142
|
|
|
143
|
-
**Size:** Runtime ~
|
|
143
|
+
**Size:** Runtime ~34KB gzip, components ~1-111KB each
|
|
144
144
|
|
|
145
145
|
**Features:**
|
|
146
146
|
- Shared runtime (external snice imports)
|
package/docs/ai/api.md
CHANGED
|
@@ -114,6 +114,8 @@ html`
|
|
|
114
114
|
@on(event: string | string[], selector?: string, options?: OnOptions)
|
|
115
115
|
// Works in elements + controllers
|
|
116
116
|
// Options: { debounce?, throttle?, preventDefault?, stopPropagation?, once?, capture?, passive?, target?, scope?, daemon? }
|
|
117
|
+
// debounce/throttle: number | function called with decorated instance as `this`
|
|
118
|
+
// @on resolves timing functions at listener setup/reconnect; finite non-negative ms required
|
|
117
119
|
// target: CSS selector for shadow DOM event delegation
|
|
118
120
|
// scope: 'global' | selector | EventTarget | () => EventTarget | null — redirects listener attachment
|
|
119
121
|
// Keyboard: 'keydown:Enter', 'keydown.escape', 'keydown:ctrl+s', 'keydown:~Space'
|
|
@@ -122,6 +124,8 @@ html`
|
|
|
122
124
|
@dispatch(eventName: string, options?: { debounce?, throttle?, dispatchOnUndefined?, scope?, daemon?, ...EventInit })
|
|
123
125
|
// Fires CustomEvent after method, detail = return value
|
|
124
126
|
// Supports async methods (dispatches after promise resolves)
|
|
127
|
+
// Timing functions resolve against the decorated instance per invocation
|
|
128
|
+
// Teardown cancels queued and unresolved-async dispatch work
|
|
125
129
|
// dispatchOnUndefined: false — skips when return is undefined; omitted/true still dispatches
|
|
126
130
|
// scope: 'global' | selector | EventTarget | () => EventTarget | null — redirects dispatch target
|
|
127
131
|
```
|
|
@@ -367,8 +371,9 @@ Full references: [rendering.md](rendering.md) for renderer structure and [bindin
|
|
|
367
371
|
```typescript
|
|
368
372
|
interface TemplateResult { readonly _$litType$: number; }
|
|
369
373
|
interface CSSResult { cssText: string; }
|
|
370
|
-
|
|
371
|
-
interface
|
|
374
|
+
type EventTiming = number | ((this: any) => number)
|
|
375
|
+
interface OnOptions { debounce?: EventTiming, throttle?: EventTiming, preventDefault?, stopPropagation?, once?, capture?, passive?, target?, scope? }
|
|
376
|
+
interface DispatchOptions extends EventInit { debounce?: EventTiming, throttle?: EventTiming, dispatchOnUndefined?, scope? }
|
|
372
377
|
type OnScope = 'global' | string | EventTarget | ((this: HTMLElement) => EventTarget | null)
|
|
373
378
|
interface RenderOptions { debounce?, throttle?, once?, sync?, differential? }
|
|
374
379
|
interface PropertyOptions { type?, attribute?, reflect?, deep?, converter?, hasChanged? }
|
package/docs/ai/cli.md
CHANGED
|
@@ -46,6 +46,20 @@ npx snice validate # source analyzer only
|
|
|
46
46
|
- deep imports that were never released package paths, e.g. `snice/decorators`
|
|
47
47
|
- Router missing `target`, `type`, or a project-wide `initialize()` call
|
|
48
48
|
- a routed class combining `@page` with redundant `@element`
|
|
49
|
+
- route path/query `:param` or named `*splat` without a reachable attribute target,
|
|
50
|
+
including `attribute: false` and mismatched aliases
|
|
51
|
+
(`snice/route-param-has-no-binding-target`, non-blocking warning)
|
|
52
|
+
- controller `@context()` handlers that start load/reload/refresh/fetch work
|
|
53
|
+
without `{ once: true }`, a first-delivery guard, or an update diff
|
|
54
|
+
(`snice/unguarded-context-load`, non-blocking warning)
|
|
55
|
+
|
|
56
|
+
Route-param inheritance follows proven local declarations, direct relative
|
|
57
|
+
re-exports, and named or namespace Snice imports. Missing-target findings are
|
|
58
|
+
deferred for unresolved/ambiguous bases or dynamic route/attribute contracts;
|
|
59
|
+
locally visible disabled or mismatched properties are still reported.
|
|
60
|
+
The rule stays warning-level because the dependency-free static lexer cannot
|
|
61
|
+
model every valid JavaScript/TypeScript grammar edge; verify the Router
|
|
62
|
+
attribute channel, but this finding alone does not fail `validate` or `check`.
|
|
49
63
|
|
|
50
64
|
Non-blocking architecture suggestions:
|
|
51
65
|
- `@page` -> `src/pages/`
|
|
@@ -18,6 +18,7 @@ alt: string = ''; // Alt text (falls back to `name`)
|
|
|
18
18
|
name: string = ''; // User's name (initials + color generation)
|
|
19
19
|
size: 'xs'|'small'|'medium'|'large'|'xl'|'xxl' = 'medium';
|
|
20
20
|
shape: 'circle'|'square'|'rounded' = 'circle';
|
|
21
|
+
loading: 'lazy'|'eager' = 'lazy'; // img loading strategy
|
|
21
22
|
fallbackColor: string = '#ffffff'; // attr: fallback-color — text color for initials
|
|
22
23
|
fallbackBackground: string = ''; // attr: fallback-background — overrides auto-color
|
|
23
24
|
```
|
|
@@ -15,6 +15,8 @@ highlightToday: boolean = true; // attribute: highlight-today
|
|
|
15
15
|
showWeekNumbers: boolean = false; // attribute: show-week-numbers
|
|
16
16
|
firstDayOfWeek: number = 0; // attribute: first-day-of-week (0=Sun, 1=Mon)
|
|
17
17
|
locale: string = 'en-US';
|
|
18
|
+
noDaySelect: boolean = false; // attribute: no-day-select — display-only: day clicks don't select/highlight or fire calendar-change; event bars stay interactive
|
|
19
|
+
cellSizing: 'square'|'stretch' = 'square'; // attribute: cell-sizing — square: cells as tall as the column is wide; stretch: rows collapse to content + event-lane reservation
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
## Methods
|
|
@@ -48,8 +50,29 @@ locale: string = 'en-US';
|
|
|
48
50
|
import 'snice/components/calendar/snice-calendar';
|
|
49
51
|
|
|
50
52
|
calendar.events = [
|
|
51
|
-
{ id: 1, title: 'Meeting', start: new Date(), color: '#2196f3' }
|
|
53
|
+
{ id: 1, title: 'Meeting', start: new Date(), color: '#2196f3' },
|
|
54
|
+
{ id: 2, title: 'Conf', start: '2026-06-04', end: '2026-06-10' } // ranged
|
|
52
55
|
];
|
|
56
|
+
// Ranged events render as continuous stripes: one bar per week row, chopped
|
|
57
|
+
// at week boundaries (squared corners on the continuing side, title repeats).
|
|
58
|
+
// Concurrent events stack into lanes (start asc, longer first on ties);
|
|
59
|
+
// max 3 lanes, deeper stacks collapse to a per-day "+N more" chip.
|
|
60
|
+
// Bars: part="event-bar", click -> calendar-event-click.
|
|
61
|
+
// Styling: color -> background; avatar?: string | {src?,name?,alt?} ->
|
|
62
|
+
// <snice-avatar> on each bar (part="event-avatar"; name -> initials fallback);
|
|
63
|
+
// className?: string -> added to bar classes AND part list, so
|
|
64
|
+
// ::part(<className>) themes specific events from outside.
|
|
65
|
+
// Tooltips: event.tooltip?: string (static), or on the element:
|
|
66
|
+
// eventTooltip: (event) => string | Node | Promise<string | Node> // lazy/rich; wins over event.tooltip
|
|
67
|
+
// Stale async results discarded on pointer-leave. Overlay part="event-tooltip".
|
|
68
|
+
// Popovers (click-to-open, interactive; STRICT per-event opt-in):
|
|
69
|
+
// event.popover?: boolean | string | Node | (() => Node)
|
|
70
|
+
// Resolution: inline content -> element eventPopover(event) => string|Node|Promise
|
|
71
|
+
// -> @request('calendar/event-popover') {event} (answer with @respond) -> warn+close.
|
|
72
|
+
// role="dialog" part="event-popover"; Escape/outside-click dismiss, focus returns
|
|
73
|
+
// to bar; loading state while pending; stale results discarded.
|
|
74
|
+
// Bars with popover: role="button" tabindex=0, Enter/Space opens.
|
|
75
|
+
// calendar-event-click still fires. closeEventPopover() closes programmatically.
|
|
53
76
|
|
|
54
77
|
calendar.addEventListener('calendar-change', (e) => {
|
|
55
78
|
console.log('Selected:', e.detail.value);
|
|
@@ -58,6 +58,9 @@ readonly labels: NodeList | null;
|
|
|
58
58
|
- `thumb` - Switch thumb
|
|
59
59
|
- `spinner` - Loading spinner
|
|
60
60
|
- `label` - Label text
|
|
61
|
+
- `label-on` / `label-off` - State text inside the track (render when set)
|
|
62
|
+
|
|
63
|
+
State labels: track auto-sizes to the widest label (medium/large; small hides them); checked thumb travel follows actual track width, so `::part(track)` width overrides stay aligned.
|
|
61
64
|
|
|
62
65
|
## Basic Usage
|
|
63
66
|
|
|
@@ -52,7 +52,7 @@ interface ColumnDefinition {
|
|
|
52
52
|
key:string; label:string; type?:ColumnType; align?:'left'|'center'|'right'; width?:string; flex?:number; minWidth?:number; maxWidth?:number;
|
|
53
53
|
sortable?:boolean; filterable?:boolean; resizable?:boolean; reorderable?:boolean; hideable?:boolean; pinnable?:boolean; pinned?:'left'|'right'|false;
|
|
54
54
|
editable?:boolean; exportable?:boolean; editorType?:'text'|'number'|'date'|'boolean'|'select'; selectOptions?:{value:string;label:string}[];
|
|
55
|
-
formatter?:(value:any,row?:any)=>string; valueGetter?:(value:any,row:any)=>any; valueFormatter?:(value:any,row:any)=>string;
|
|
55
|
+
formatter?:(value:any,row?:any)=>string; valueGetter?:(value:any,row:any)=>any; /* runs for display, sort, aggregation */ valueFormatter?:(value:any,row:any)=>string;
|
|
56
56
|
valueParser?:(value:string,row:any)=>any; valueSetter?:(value:any,row:any)=>any; sortComparator?:(a:any,b:any,direction:'asc'|'desc')=>number;
|
|
57
57
|
colSpan?:number|((value:any,row:any)=>number); aggregate?:Aggregator; renderCell?:(value:any,row:any,column:ColumnDefinition)=>HTMLElement|string;
|
|
58
58
|
renderEditor?:(value:any,row:any,column:ColumnDefinition,commit:(v:any)=>void,cancel:()=>void)=>HTMLElement;
|
package/docs/ai/decorators.md
CHANGED
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
- `@queryAll('selector', { light? })` - NodeList
|
|
32
32
|
|
|
33
33
|
## Events
|
|
34
|
-
- `@on('event', 'selector?', options?)` - Delegation, auto-bound. Options: `{ capture?, once?, passive?, preventDefault?, stopPropagation?, debounce
|
|
35
|
-
- `@dispatch('event-name', options?)` - Emit CustomEvent, detail = return value. Supports async. Options: `{ debounce
|
|
34
|
+
- `@on('event', 'selector?', options?)` - Delegation, auto-bound. Options: `{ capture?, once?, passive?, preventDefault?, stopPropagation?, debounce?: EventTiming, throttle?: EventTiming, scope?, daemon? }`
|
|
35
|
+
- `@dispatch('event-name', options?)` - Emit CustomEvent, detail = return value. Supports async. Options: `{ debounce?: EventTiming, throttle?: EventTiming, dispatchOnUndefined?, scope?, daemon?, ...EventInit }`
|
|
36
|
+
- `EventTiming = number | ((this: any) => number)` - Per-instance resolver. `@on` resolves at listener setup; `@dispatch` resolves per invocation. Must return finite non-negative ms; `0` disables.
|
|
36
37
|
|
|
37
38
|
### scope (on both `@on` and `@dispatch`)
|
|
38
39
|
|