snice 7.2.0 → 7.4.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/.agents/skills/snice/SKILL.md +36 -13
- package/README.md +11 -6
- package/adapters/react/SniceProvider.js +1 -1
- package/adapters/react/SniceRouter.d.ts +2 -0
- package/adapters/react/SniceRouter.js +3 -2
- package/adapters/react/SniceRouter.js.map +1 -1
- package/adapters/react/matchRoute.d.ts +4 -1
- package/adapters/react/matchRoute.js +13 -3
- package/adapters/react/matchRoute.js.map +1 -1
- package/adapters/react/useRequestHandler.js +1 -1
- package/bin/analyzer-contracts.js +4 -1
- package/bin/project-analyzer.js +521 -20
- package/bin/snice.js +77 -13
- package/bin/templates/AI_GUIDANCE.md +6 -1
- package/bin/templates/default/README.md +1 -1
- package/bin/templates/default/src/components/app-header.ts +11 -16
- package/bin/templates/default/src/components/notification-badge.ts +5 -14
- package/bin/templates/default/src/components/search-bar.ts +21 -65
- package/bin/templates/default/src/pages/dashboard.ts +15 -11
- package/bin/templates/default/src/pages/data.ts +10 -13
- package/bin/templates/default/src/pages/notifications.ts +4 -9
- package/bin/templates/default/src/pages/profile.ts +2 -8
- package/bin/templates/default/src/pages/settings.ts +16 -16
- package/dist/app-context.d.ts +10 -10
- 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/snice-avatar.js +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 +1 -1
- package/dist/cdn/calendar/snice-calendar.js +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/snice-login.js +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 +333 -192
- 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 +333 -191
- 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 +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/snice-switch.js +1 -1
- package/dist/cdn/table/snice-table.esm.js +1 -1
- package/dist/cdn/table/snice-table.js +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/pagination/snice-pagination.types.d.ts +5 -0
- package/dist/context-provider.d.ts +11 -1
- package/dist/context.d.ts +5 -6
- package/dist/index.cjs +330 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +330 -189
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +330 -188
- package/dist/index.iife.js.map +1 -1
- package/dist/react/SniceProvider.js +1 -1
- package/dist/react/SniceRouter.d.ts +2 -0
- package/dist/react/SniceRouter.js +3 -2
- package/dist/react/SniceRouter.js.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/matchRoute.d.ts +4 -1
- package/dist/react/matchRoute.js +13 -3
- package/dist/react/matchRoute.js.map +1 -1
- package/dist/react/useRequestHandler.js +1 -1
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/testing-dom.cjs +48 -0
- package/dist/testing-dom.cjs.map +1 -0
- package/dist/testing-dom.d.ts +23 -0
- package/dist/testing-dom.esm.js +45 -0
- package/dist/testing-dom.esm.js.map +1 -0
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/context.d.ts +8 -8
- package/dist/types/page-options.d.ts +15 -1
- package/dist/types/router-instance.d.ts +2 -1
- package/dist/utils.d.ts +3 -1
- package/docs/ai/README.md +6 -4
- package/docs/ai/api.md +13 -2
- package/docs/ai/architecture.md +28 -11
- package/docs/ai/bindings.md +1 -1
- package/docs/ai/cli.md +26 -1
- package/docs/ai/components/badge.md +4 -0
- package/docs/ai/components/pagination.md +4 -2
- package/docs/ai/components/segmented-control.md +5 -1
- package/docs/ai/controllers.md +41 -12
- package/docs/ai/decorators.md +3 -3
- package/docs/ai/events.md +8 -2
- package/docs/ai/fetcher.md +32 -1
- package/docs/ai/lifecycle.md +4 -1
- package/docs/ai/patterns.md +9 -4
- package/docs/ai/react-integration.md +1 -0
- package/docs/ai/request-response.md +20 -6
- package/docs/ai/routing.md +33 -4
- package/docs/ai/testing.md +71 -0
- package/docs/bindings.md +1 -1
- package/docs/cli.md +27 -2
- package/docs/components/badge.md +4 -0
- package/docs/components/pagination.md +8 -5
- package/docs/components/segmented-control.md +5 -1
- package/docs/controllers.md +46 -15
- package/docs/events.md +11 -2
- package/docs/fetcher.md +32 -1
- package/docs/lifecycle.md +5 -1
- package/docs/react-integration.md +1 -0
- package/docs/request-response.md +25 -7
- package/docs/routing.md +39 -3
- package/docs/testing.md +71 -0
- package/package.json +6 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v7.
|
|
2
|
+
* snice v7.4.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.
|
|
6
6
|
*/
|
|
7
7
|
/*!
|
|
8
|
-
* snice v7.
|
|
8
|
+
* snice v7.3.0
|
|
9
9
|
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
10
10
|
* (c) 2024
|
|
11
11
|
* Released under the MIT License.
|
|
@@ -236,7 +236,7 @@ let contextIdCounter = 0;
|
|
|
236
236
|
*/
|
|
237
237
|
class Context {
|
|
238
238
|
constructor(context = {}, placards = [], currentRoute = '', routeParams = {}, fetcher) {
|
|
239
|
-
// Stores the SET of wrapped-method names per
|
|
239
|
+
// Stores the SET of wrapped-method names per subscriber so that classes with
|
|
240
240
|
// multiple @context() handlers register every method, not just the last one.
|
|
241
241
|
this[_a] = new WeakMap();
|
|
242
242
|
this[_b] = new Set();
|
|
@@ -260,7 +260,7 @@ class Context {
|
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
/**
|
|
263
|
-
* Register an element to receive context updates
|
|
263
|
+
* Register an element or controller to receive context updates
|
|
264
264
|
* @internal Used by @context decorator
|
|
265
265
|
*/
|
|
266
266
|
[(_a = REGISTERED_ELEMENTS, _b = REGISTERED_ELEMENTS_SET, _c = IS_UPDATING, CONTEXT_REGISTER)](element, methodName) {
|
|
@@ -274,10 +274,10 @@ class Context {
|
|
|
274
274
|
this[REGISTERED_ELEMENTS_SET].add(element);
|
|
275
275
|
}
|
|
276
276
|
/**
|
|
277
|
-
* Unregister
|
|
277
|
+
* Unregister a subscriber from context updates. With a methodName,
|
|
278
278
|
* removes only that one handler (so a `once` handler tearing itself down
|
|
279
279
|
* doesn't kill the element's other @context handlers); without one, removes
|
|
280
|
-
* the
|
|
280
|
+
* the subscriber entirely (used on disconnect/detach).
|
|
281
281
|
* @internal Used by @context decorator cleanup
|
|
282
282
|
*/
|
|
283
283
|
[CONTEXT_UNREGISTER](element, methodName) {
|
|
@@ -318,7 +318,7 @@ class Context {
|
|
|
318
318
|
return;
|
|
319
319
|
}
|
|
320
320
|
this[IS_UPDATING] = true;
|
|
321
|
-
// Notify all registered
|
|
321
|
+
// Notify all registered subscribers by calling EVERY registered method
|
|
322
322
|
// name on each. Classes with multiple @context() methods get all fired.
|
|
323
323
|
const elementsSet = this[REGISTERED_ELEMENTS_SET];
|
|
324
324
|
const elementsMap = this[REGISTERED_ELEMENTS];
|
|
@@ -340,7 +340,7 @@ class Context {
|
|
|
340
340
|
this[IS_UPDATING] = false;
|
|
341
341
|
}
|
|
342
342
|
/**
|
|
343
|
-
* Notify a specific
|
|
343
|
+
* Notify a specific subscriber of the current context state
|
|
344
344
|
* @internal Used by @context decorator
|
|
345
345
|
*/
|
|
346
346
|
[CONTEXT_NOTIFY_ELEMENT](element) {
|
|
@@ -349,7 +349,12 @@ class Context {
|
|
|
349
349
|
return;
|
|
350
350
|
for (const methodName of names) {
|
|
351
351
|
if (typeof element[methodName] === 'function') {
|
|
352
|
-
|
|
352
|
+
try {
|
|
353
|
+
element[methodName](this);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
console.error(`Error calling @context method ${methodName}:`, error);
|
|
357
|
+
}
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
}
|
|
@@ -681,11 +686,17 @@ function invokeImmediateWatchers(instance, constructor) {
|
|
|
681
686
|
*/
|
|
682
687
|
function createDebounced(fn, delay) {
|
|
683
688
|
let timer = null;
|
|
684
|
-
|
|
689
|
+
const debounced = function (...args) {
|
|
685
690
|
if (timer !== null)
|
|
686
691
|
clearTimeout(timer);
|
|
687
692
|
timer = setTimeout(() => { fn.apply(this, args); timer = null; }, delay);
|
|
688
693
|
};
|
|
694
|
+
debounced.cancel = () => {
|
|
695
|
+
if (timer !== null)
|
|
696
|
+
clearTimeout(timer);
|
|
697
|
+
timer = null;
|
|
698
|
+
};
|
|
699
|
+
return debounced;
|
|
689
700
|
}
|
|
690
701
|
/**
|
|
691
702
|
* Create a throttled version of a function (leading + trailing edge)
|
|
@@ -1142,7 +1153,7 @@ function contextTarget(source) {
|
|
|
1142
1153
|
* boundary. Public callers use provideContext(), which also activates the
|
|
1143
1154
|
* daemon instances declared by the context.
|
|
1144
1155
|
*/
|
|
1145
|
-
function installContextProvider(root, context) {
|
|
1156
|
+
function installContextProvider(root, context, fetch, navigation) {
|
|
1146
1157
|
if (!root || typeof root.addEventListener !== 'function' || typeof root.removeEventListener !== 'function') {
|
|
1147
1158
|
throw new TypeError('provideContext() requires an EventTarget root.');
|
|
1148
1159
|
}
|
|
@@ -1154,9 +1165,26 @@ function installContextProvider(root, context) {
|
|
|
1154
1165
|
}
|
|
1155
1166
|
const listener = (event) => {
|
|
1156
1167
|
const request = event;
|
|
1157
|
-
if (!request.detail
|
|
1168
|
+
if (!request.detail)
|
|
1169
|
+
return;
|
|
1170
|
+
if (request.detail.kind === 'application') {
|
|
1171
|
+
if (request.detail.context !== undefined)
|
|
1172
|
+
return;
|
|
1173
|
+
request.detail.context = context;
|
|
1174
|
+
}
|
|
1175
|
+
else if (request.detail.kind === 'fetch') {
|
|
1176
|
+
if (!fetch || request.detail.fetch !== undefined)
|
|
1177
|
+
return;
|
|
1178
|
+
request.detail.fetch = fetch;
|
|
1179
|
+
}
|
|
1180
|
+
else if (request.detail.kind === 'navigation') {
|
|
1181
|
+
if (!navigation || request.detail.navigation !== undefined)
|
|
1182
|
+
return;
|
|
1183
|
+
request.detail.navigation = navigation;
|
|
1184
|
+
}
|
|
1185
|
+
else {
|
|
1158
1186
|
return;
|
|
1159
|
-
|
|
1187
|
+
}
|
|
1160
1188
|
request.preventDefault();
|
|
1161
1189
|
request.stopPropagation();
|
|
1162
1190
|
};
|
|
@@ -1186,7 +1214,7 @@ function getContext(source) {
|
|
|
1186
1214
|
}
|
|
1187
1215
|
const target = contextTarget(source);
|
|
1188
1216
|
if (target) {
|
|
1189
|
-
const detail = {};
|
|
1217
|
+
const detail = { kind: 'application' };
|
|
1190
1218
|
target.dispatchEvent(new CustomEvent(CONTEXT_REQUEST_EVENT, {
|
|
1191
1219
|
bubbles: true,
|
|
1192
1220
|
composed: true,
|
|
@@ -1205,6 +1233,39 @@ function getContext(source) {
|
|
|
1205
1233
|
}
|
|
1206
1234
|
return undefined;
|
|
1207
1235
|
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Resolve the fetch function supplied by the nearest context provider.
|
|
1238
|
+
* Router providers expose their ContextAwareFetcher-bound function here;
|
|
1239
|
+
* explicit providers may supply the same dependency for non-router apps and
|
|
1240
|
+
* tests without adding a reserved property to AppContext.
|
|
1241
|
+
*/
|
|
1242
|
+
function getContextFetch(source) {
|
|
1243
|
+
const target = contextTarget(source);
|
|
1244
|
+
if (!target)
|
|
1245
|
+
return undefined;
|
|
1246
|
+
const detail = { kind: 'fetch' };
|
|
1247
|
+
target.dispatchEvent(new CustomEvent(CONTEXT_REQUEST_EVENT, {
|
|
1248
|
+
bubbles: true,
|
|
1249
|
+
composed: true,
|
|
1250
|
+
cancelable: true,
|
|
1251
|
+
detail,
|
|
1252
|
+
}));
|
|
1253
|
+
return detail.fetch;
|
|
1254
|
+
}
|
|
1255
|
+
/** @internal Resolve the router Context visible to an element or controller. */
|
|
1256
|
+
function getNavigationContext(source) {
|
|
1257
|
+
const target = contextTarget(source);
|
|
1258
|
+
if (!target)
|
|
1259
|
+
return undefined;
|
|
1260
|
+
const detail = { kind: 'navigation' };
|
|
1261
|
+
target.dispatchEvent(new CustomEvent(CONTEXT_REQUEST_EVENT, {
|
|
1262
|
+
bubbles: true,
|
|
1263
|
+
composed: true,
|
|
1264
|
+
cancelable: true,
|
|
1265
|
+
detail,
|
|
1266
|
+
}));
|
|
1267
|
+
return detail.navigation;
|
|
1268
|
+
}
|
|
1208
1269
|
|
|
1209
1270
|
function isDaemonInstance(value) {
|
|
1210
1271
|
return !!value
|
|
@@ -1868,12 +1929,13 @@ function setupEventHandlers(instance, targetElement) {
|
|
|
1868
1929
|
}
|
|
1869
1930
|
// Initialize cleanup object if needed
|
|
1870
1931
|
if (!instance[CLEANUP]) {
|
|
1871
|
-
instance[CLEANUP] = { events: [], channels: [], observers: [] };
|
|
1932
|
+
instance[CLEANUP] = { events: [], eventCancels: [], channels: [], observers: [] };
|
|
1872
1933
|
}
|
|
1873
|
-
|
|
1934
|
+
if (!instance[CLEANUP].events)
|
|
1874
1935
|
instance[CLEANUP].events = [];
|
|
1875
|
-
|
|
1876
|
-
|
|
1936
|
+
if (!instance[CLEANUP].eventCancels)
|
|
1937
|
+
instance[CLEANUP].eventCancels = [];
|
|
1938
|
+
if (instance[CLEANUP].events.length > 0 || instance[CLEANUP].eventCancels.length > 0) {
|
|
1877
1939
|
// Events already set up - clean them up first to avoid duplicates
|
|
1878
1940
|
cleanupEventHandlers(instance);
|
|
1879
1941
|
}
|
|
@@ -1903,7 +1965,9 @@ function setupEventHandlers(instance, targetElement) {
|
|
|
1903
1965
|
warnIfModifierMisuse(handler.eventName);
|
|
1904
1966
|
// Apply debounce (takes precedence over throttle)
|
|
1905
1967
|
if (handlerOptions.debounce && handlerOptions.debounce > 0) {
|
|
1906
|
-
|
|
1968
|
+
const debounced = createDebounced(boundMethod, handlerOptions.debounce);
|
|
1969
|
+
instance[CLEANUP].eventCancels.push(() => debounced.cancel());
|
|
1970
|
+
boundMethod = debounced;
|
|
1907
1971
|
}
|
|
1908
1972
|
else if (handlerOptions.throttle && handlerOptions.throttle > 0) {
|
|
1909
1973
|
boundMethod = throttle$1(boundMethod, handlerOptions.throttle);
|
|
@@ -2076,12 +2140,190 @@ function setupEventHandlers(instance, targetElement) {
|
|
|
2076
2140
|
function cleanupEventHandlers(instance) {
|
|
2077
2141
|
if (!instance[CLEANUP]?.events)
|
|
2078
2142
|
return;
|
|
2143
|
+
for (const cancel of instance[CLEANUP].eventCancels ?? [])
|
|
2144
|
+
cancel();
|
|
2145
|
+
instance[CLEANUP].eventCancels = [];
|
|
2079
2146
|
for (const { target, eventName, handler, options } of instance[CLEANUP].events) {
|
|
2080
2147
|
target.removeEventListener(eventName, handler, options);
|
|
2081
2148
|
}
|
|
2082
2149
|
instance[CLEANUP].events = [];
|
|
2083
2150
|
}
|
|
2084
2151
|
|
|
2152
|
+
/**
|
|
2153
|
+
* @context decorator for receiving router context updates
|
|
2154
|
+
*/
|
|
2155
|
+
const CONTEXT_HANDLERS = getSymbol('context-handlers');
|
|
2156
|
+
/**
|
|
2157
|
+
* @context decorator for receiving router context updates
|
|
2158
|
+
*
|
|
2159
|
+
* @example
|
|
2160
|
+
* ```typescript
|
|
2161
|
+
* @element('my-layout')
|
|
2162
|
+
* class MyLayout extends HTMLElement {
|
|
2163
|
+
* @context
|
|
2164
|
+
* handleContext(ctx: Context) {
|
|
2165
|
+
* this.renderNav(ctx.placards, ctx.currentRoute);
|
|
2166
|
+
* }
|
|
2167
|
+
*
|
|
2168
|
+
* @context({ debounce: 300 })
|
|
2169
|
+
* handleContextDebounced(ctx: Context) {
|
|
2170
|
+
* // Called after 300ms of no updates
|
|
2171
|
+
* }
|
|
2172
|
+
* }
|
|
2173
|
+
* ```
|
|
2174
|
+
*/
|
|
2175
|
+
function context$1(options = {}) {
|
|
2176
|
+
return function (originalMethod, context) {
|
|
2177
|
+
const methodName = context.name;
|
|
2178
|
+
context.addInitializer(function () {
|
|
2179
|
+
const constructor = this.constructor;
|
|
2180
|
+
// hasOwnProperty guards so subclasses don't mutate parent state via
|
|
2181
|
+
// the prototype chain.
|
|
2182
|
+
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_METHODS)) {
|
|
2183
|
+
constructor[CONTEXT_METHODS] = new Set();
|
|
2184
|
+
}
|
|
2185
|
+
if (constructor[CONTEXT_METHODS].has(originalMethod))
|
|
2186
|
+
return;
|
|
2187
|
+
constructor[CONTEXT_METHODS].add(originalMethod);
|
|
2188
|
+
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_HANDLERS)) {
|
|
2189
|
+
const inherited = constructor[CONTEXT_HANDLERS];
|
|
2190
|
+
constructor[CONTEXT_HANDLERS] = inherited ? [...inherited] : [];
|
|
2191
|
+
}
|
|
2192
|
+
constructor[CONTEXT_HANDLERS].push({
|
|
2193
|
+
methodName,
|
|
2194
|
+
method: originalMethod,
|
|
2195
|
+
options,
|
|
2196
|
+
});
|
|
2197
|
+
});
|
|
2198
|
+
return originalMethod;
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Set up context handlers for an element or attached controller.
|
|
2203
|
+
* Called automatically by the corresponding managed lifecycle.
|
|
2204
|
+
*/
|
|
2205
|
+
function setupContextHandler(element, catchUp = false) {
|
|
2206
|
+
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2207
|
+
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2210
|
+
// Get the Context instance from the router
|
|
2211
|
+
const ctx = element[CONTEXT_HANDLER] ?? getNavigationContext(element);
|
|
2212
|
+
if (!ctx) {
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
// Store the Context instance for cleanup
|
|
2216
|
+
element[NAVIGATION_CONTEXT_INSTANCE] = ctx;
|
|
2217
|
+
// A controller can finish attaching after the Router has already announced
|
|
2218
|
+
// the current navigation. Track whether an update arrives during this turn;
|
|
2219
|
+
// if none does, catch it up once from the Context's current state.
|
|
2220
|
+
let receivedContextUpdate = false;
|
|
2221
|
+
// Register each handler with the Context
|
|
2222
|
+
for (const handler of handlers) {
|
|
2223
|
+
const { methodName, method, options } = handler;
|
|
2224
|
+
const wrappedMethodName = `__wrapped_${methodName}`;
|
|
2225
|
+
// Create wrapped method with timing controls
|
|
2226
|
+
element[wrappedMethodName] = function (context) {
|
|
2227
|
+
receivedContextUpdate = true;
|
|
2228
|
+
// `once` is tracked PER method (not a single element-level flag) so
|
|
2229
|
+
// multiple once handlers on the same element each fire.
|
|
2230
|
+
const called = element[CONTEXT_CALLED] ||
|
|
2231
|
+
(element[CONTEXT_CALLED] = new Set());
|
|
2232
|
+
if (options.once && called.has(methodName)) {
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2235
|
+
const callMethod = () => {
|
|
2236
|
+
method.call(element, context);
|
|
2237
|
+
// Handle once option
|
|
2238
|
+
if (options.once) {
|
|
2239
|
+
called.add(methodName);
|
|
2240
|
+
// Unregister ONLY this handler, not the whole element — otherwise the
|
|
2241
|
+
// element's other @context handlers would stop receiving updates.
|
|
2242
|
+
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2243
|
+
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2244
|
+
ctx[CONTEXT_UNREGISTER](element, wrappedMethodName);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
// Per-handler timer slot to avoid debounce/throttle handlers on the same
|
|
2249
|
+
// element overwriting each other's state.
|
|
2250
|
+
const timerSlot = element[CONTEXT_TIMER] ||
|
|
2251
|
+
(element[CONTEXT_TIMER] = {});
|
|
2252
|
+
const timerKey = methodName;
|
|
2253
|
+
if (options.debounce) {
|
|
2254
|
+
clearTimeout(timerSlot[timerKey]?.timeout);
|
|
2255
|
+
timerSlot[timerKey] = {
|
|
2256
|
+
timeout: setTimeout(callMethod, options.debounce),
|
|
2257
|
+
};
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
if (options.throttle) {
|
|
2261
|
+
const now = Date.now();
|
|
2262
|
+
const lastCall = timerSlot[timerKey]?.lastCall || 0;
|
|
2263
|
+
if (now - lastCall >= options.throttle) {
|
|
2264
|
+
timerSlot[timerKey] = { lastCall: now };
|
|
2265
|
+
callMethod();
|
|
2266
|
+
}
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
callMethod();
|
|
2270
|
+
};
|
|
2271
|
+
// Register with the Context using the wrapped method name
|
|
2272
|
+
if (typeof ctx[CONTEXT_REGISTER] === 'function') {
|
|
2273
|
+
ctx[CONTEXT_REGISTER](element, wrappedMethodName);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
if (catchUp && typeof ctx[CONTEXT_NOTIFY_ELEMENT] === 'function') {
|
|
2277
|
+
return new Promise((resolve) => {
|
|
2278
|
+
const notifyIfNeeded = () => {
|
|
2279
|
+
try {
|
|
2280
|
+
if (!receivedContextUpdate
|
|
2281
|
+
&& element[NAVIGATION_CONTEXT_INSTANCE] === ctx) {
|
|
2282
|
+
ctx[CONTEXT_NOTIFY_ELEMENT](element);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
finally {
|
|
2286
|
+
resolve();
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
if (catchUp === 'task')
|
|
2290
|
+
setTimeout(notifyIfNeeded, 0);
|
|
2291
|
+
else
|
|
2292
|
+
queueMicrotask(notifyIfNeeded);
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Clean up context handlers for an element or attached controller.
|
|
2298
|
+
*/
|
|
2299
|
+
function cleanupContextHandler(element) {
|
|
2300
|
+
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2301
|
+
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
// Clear any pending debounce timers (per-handler slots)
|
|
2305
|
+
const timerSlot = element[CONTEXT_TIMER];
|
|
2306
|
+
if (timerSlot && typeof timerSlot === 'object') {
|
|
2307
|
+
for (const key of Object.keys(timerSlot)) {
|
|
2308
|
+
if (timerSlot[key]?.timeout)
|
|
2309
|
+
clearTimeout(timerSlot[key].timeout);
|
|
2310
|
+
}
|
|
2311
|
+
delete element[CONTEXT_TIMER];
|
|
2312
|
+
}
|
|
2313
|
+
for (const handler of handlers) {
|
|
2314
|
+
// Clean up wrapped method
|
|
2315
|
+
const wrappedMethodName = `__wrapped_${handler.methodName}`;
|
|
2316
|
+
delete element[wrappedMethodName];
|
|
2317
|
+
}
|
|
2318
|
+
// Unregister from Context if available
|
|
2319
|
+
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2320
|
+
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2321
|
+
ctx[CONTEXT_UNREGISTER](element);
|
|
2322
|
+
}
|
|
2323
|
+
delete element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2324
|
+
delete element[CONTEXT_CALLED];
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2085
2327
|
// Controller-scoped cleanup registry
|
|
2086
2328
|
class ControllerScope {
|
|
2087
2329
|
constructor() {
|
|
@@ -2415,6 +2657,10 @@ async function attachController(element, controller) {
|
|
|
2415
2657
|
await scope.runOperation(async () => {
|
|
2416
2658
|
await controllerInstance.attach(element);
|
|
2417
2659
|
});
|
|
2660
|
+
// @context has the same managed lifecycle on controllers as @on,
|
|
2661
|
+
// @observe and @respond. Catch up controllers whose async attach completed
|
|
2662
|
+
// after the Router announced the current navigation.
|
|
2663
|
+
await setupContextHandler(controllerInstance, 'microtask');
|
|
2418
2664
|
// Setup @observe observers for controller
|
|
2419
2665
|
setupObservers(controllerInstance, element);
|
|
2420
2666
|
// Setup @channel handlers for controller
|
|
@@ -2480,6 +2726,8 @@ async function detachController(element) {
|
|
|
2480
2726
|
cleanupResponseHandlers(controllerInstance);
|
|
2481
2727
|
// Cleanup @on event handlers for controller
|
|
2482
2728
|
cleanupEventHandlers(controllerInstance);
|
|
2729
|
+
// Cleanup @context handlers for controller
|
|
2730
|
+
cleanupContextHandler(controllerInstance);
|
|
2483
2731
|
// Cleanup the controller scope
|
|
2484
2732
|
if (scope) {
|
|
2485
2733
|
await scope.cleanup();
|
|
@@ -2613,158 +2861,6 @@ function useNativeElementControllers() {
|
|
|
2613
2861
|
globalThis.sniceNativeControllerObserver = observer;
|
|
2614
2862
|
}
|
|
2615
2863
|
|
|
2616
|
-
/**
|
|
2617
|
-
* @context decorator for receiving router context updates
|
|
2618
|
-
*/
|
|
2619
|
-
const CONTEXT_HANDLERS = getSymbol('context-handlers');
|
|
2620
|
-
/**
|
|
2621
|
-
* @context decorator for receiving router context updates
|
|
2622
|
-
*
|
|
2623
|
-
* @example
|
|
2624
|
-
* ```typescript
|
|
2625
|
-
* @element('my-layout')
|
|
2626
|
-
* class MyLayout extends HTMLElement {
|
|
2627
|
-
* @context
|
|
2628
|
-
* handleContext(ctx: Context) {
|
|
2629
|
-
* this.renderNav(ctx.placards, ctx.currentRoute);
|
|
2630
|
-
* }
|
|
2631
|
-
*
|
|
2632
|
-
* @context({ debounce: 300 })
|
|
2633
|
-
* handleContextDebounced(ctx: Context) {
|
|
2634
|
-
* // Called after 300ms of no updates
|
|
2635
|
-
* }
|
|
2636
|
-
* }
|
|
2637
|
-
* ```
|
|
2638
|
-
*/
|
|
2639
|
-
function context$1(options = {}) {
|
|
2640
|
-
return function (originalMethod, context) {
|
|
2641
|
-
const methodName = context.name;
|
|
2642
|
-
context.addInitializer(function () {
|
|
2643
|
-
const constructor = this.constructor;
|
|
2644
|
-
// hasOwnProperty guards so subclasses don't mutate parent state via
|
|
2645
|
-
// the prototype chain.
|
|
2646
|
-
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_METHODS)) {
|
|
2647
|
-
constructor[CONTEXT_METHODS] = new Set();
|
|
2648
|
-
}
|
|
2649
|
-
if (constructor[CONTEXT_METHODS].has(originalMethod))
|
|
2650
|
-
return;
|
|
2651
|
-
constructor[CONTEXT_METHODS].add(originalMethod);
|
|
2652
|
-
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_HANDLERS)) {
|
|
2653
|
-
const inherited = constructor[CONTEXT_HANDLERS];
|
|
2654
|
-
constructor[CONTEXT_HANDLERS] = inherited ? [...inherited] : [];
|
|
2655
|
-
}
|
|
2656
|
-
constructor[CONTEXT_HANDLERS].push({
|
|
2657
|
-
methodName,
|
|
2658
|
-
method: originalMethod,
|
|
2659
|
-
options,
|
|
2660
|
-
});
|
|
2661
|
-
});
|
|
2662
|
-
return originalMethod;
|
|
2663
|
-
};
|
|
2664
|
-
}
|
|
2665
|
-
/**
|
|
2666
|
-
* Setup context handler for an element instance
|
|
2667
|
-
* Called automatically during element connection
|
|
2668
|
-
*/
|
|
2669
|
-
function setupContextHandler(element) {
|
|
2670
|
-
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2671
|
-
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2672
|
-
return;
|
|
2673
|
-
}
|
|
2674
|
-
// Get the Context instance from the router
|
|
2675
|
-
const ctx = element[CONTEXT_HANDLER];
|
|
2676
|
-
if (!ctx) {
|
|
2677
|
-
return;
|
|
2678
|
-
}
|
|
2679
|
-
// Store the Context instance for cleanup
|
|
2680
|
-
element[NAVIGATION_CONTEXT_INSTANCE] = ctx;
|
|
2681
|
-
// Register each handler with the Context
|
|
2682
|
-
for (const handler of handlers) {
|
|
2683
|
-
const { methodName, method, options } = handler;
|
|
2684
|
-
const wrappedMethodName = `__wrapped_${methodName}`;
|
|
2685
|
-
// Create wrapped method with timing controls
|
|
2686
|
-
element[wrappedMethodName] = function (context) {
|
|
2687
|
-
// `once` is tracked PER method (not a single element-level flag) so
|
|
2688
|
-
// multiple once handlers on the same element each fire.
|
|
2689
|
-
const called = element[CONTEXT_CALLED] ||
|
|
2690
|
-
(element[CONTEXT_CALLED] = new Set());
|
|
2691
|
-
if (options.once && called.has(methodName)) {
|
|
2692
|
-
return;
|
|
2693
|
-
}
|
|
2694
|
-
const callMethod = () => {
|
|
2695
|
-
method.call(element, context);
|
|
2696
|
-
// Handle once option
|
|
2697
|
-
if (options.once) {
|
|
2698
|
-
called.add(methodName);
|
|
2699
|
-
// Unregister ONLY this handler, not the whole element — otherwise the
|
|
2700
|
-
// element's other @context handlers would stop receiving updates.
|
|
2701
|
-
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2702
|
-
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2703
|
-
ctx[CONTEXT_UNREGISTER](element, wrappedMethodName);
|
|
2704
|
-
}
|
|
2705
|
-
}
|
|
2706
|
-
};
|
|
2707
|
-
// Per-handler timer slot to avoid debounce/throttle handlers on the same
|
|
2708
|
-
// element overwriting each other's state.
|
|
2709
|
-
const timerSlot = element[CONTEXT_TIMER] ||
|
|
2710
|
-
(element[CONTEXT_TIMER] = {});
|
|
2711
|
-
const timerKey = methodName;
|
|
2712
|
-
if (options.debounce) {
|
|
2713
|
-
clearTimeout(timerSlot[timerKey]?.timeout);
|
|
2714
|
-
timerSlot[timerKey] = {
|
|
2715
|
-
timeout: setTimeout(callMethod, options.debounce),
|
|
2716
|
-
};
|
|
2717
|
-
return;
|
|
2718
|
-
}
|
|
2719
|
-
if (options.throttle) {
|
|
2720
|
-
const now = Date.now();
|
|
2721
|
-
const lastCall = timerSlot[timerKey]?.lastCall || 0;
|
|
2722
|
-
if (now - lastCall >= options.throttle) {
|
|
2723
|
-
timerSlot[timerKey] = { lastCall: now };
|
|
2724
|
-
callMethod();
|
|
2725
|
-
}
|
|
2726
|
-
return;
|
|
2727
|
-
}
|
|
2728
|
-
callMethod();
|
|
2729
|
-
};
|
|
2730
|
-
// Register with the Context using the wrapped method name
|
|
2731
|
-
if (typeof ctx[CONTEXT_REGISTER] === 'function') {
|
|
2732
|
-
ctx[CONTEXT_REGISTER](element, wrappedMethodName);
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
/**
|
|
2737
|
-
* Cleanup context handler for an element instance
|
|
2738
|
-
* Called automatically during element disconnection
|
|
2739
|
-
*/
|
|
2740
|
-
function cleanupContextHandler(element) {
|
|
2741
|
-
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2742
|
-
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2743
|
-
return;
|
|
2744
|
-
}
|
|
2745
|
-
// Clear any pending debounce timers (per-handler slots)
|
|
2746
|
-
const timerSlot = element[CONTEXT_TIMER];
|
|
2747
|
-
if (timerSlot && typeof timerSlot === 'object') {
|
|
2748
|
-
for (const key of Object.keys(timerSlot)) {
|
|
2749
|
-
if (timerSlot[key]?.timeout)
|
|
2750
|
-
clearTimeout(timerSlot[key].timeout);
|
|
2751
|
-
}
|
|
2752
|
-
delete element[CONTEXT_TIMER];
|
|
2753
|
-
}
|
|
2754
|
-
for (const handler of handlers) {
|
|
2755
|
-
// Clean up wrapped method
|
|
2756
|
-
const wrappedMethodName = `__wrapped_${handler.methodName}`;
|
|
2757
|
-
delete element[wrappedMethodName];
|
|
2758
|
-
}
|
|
2759
|
-
// Unregister from Context if available
|
|
2760
|
-
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2761
|
-
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2762
|
-
ctx[CONTEXT_UNREGISTER](element);
|
|
2763
|
-
}
|
|
2764
|
-
delete element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2765
|
-
delete element[CONTEXT_CALLED];
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
2864
|
/**
|
|
2769
2865
|
* Template system for Snice v3.0.0
|
|
2770
2866
|
* Provides html`` and css`` tagged template processors with differential rendering
|
|
@@ -3000,14 +3096,32 @@ const NOT_COMMITTED = Symbol('not-committed');
|
|
|
3000
3096
|
function markPreUpgradePropertyBinding(element, propertyName) {
|
|
3001
3097
|
const tagName = element.localName;
|
|
3002
3098
|
if (!tagName?.includes('-'))
|
|
3003
|
-
return;
|
|
3099
|
+
return false;
|
|
3004
3100
|
const registry = element.ownerDocument?.defaultView?.customElements ?? globalThis.customElements;
|
|
3005
3101
|
if (registry?.get(tagName))
|
|
3006
|
-
return;
|
|
3102
|
+
return false;
|
|
3007
3103
|
const target = element;
|
|
3008
3104
|
if (!target[PRE_UPGRADE_PROPERTY_BINDINGS])
|
|
3009
3105
|
target[PRE_UPGRADE_PROPERTY_BINDINGS] = new Set();
|
|
3010
3106
|
target[PRE_UPGRADE_PROPERTY_BINDINGS].add(propertyName);
|
|
3107
|
+
return true;
|
|
3108
|
+
}
|
|
3109
|
+
function commitPropertyValue(element, propertyName, value) {
|
|
3110
|
+
if (!markPreUpgradePropertyBinding(element, propertyName)) {
|
|
3111
|
+
element[propertyName] = value;
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
// An undefined custom element can inherit a native IDL setter with the same
|
|
3115
|
+
// name. Assignment would invoke that setter (for example HTMLElement.role),
|
|
3116
|
+
// stringify structured data into an attribute, and leave no own value for
|
|
3117
|
+
// @property's upgrade adoption. Park every pre-upgrade binding as the own,
|
|
3118
|
+
// configurable value that the initializer already knows how to replay.
|
|
3119
|
+
Object.defineProperty(element, propertyName, {
|
|
3120
|
+
configurable: true,
|
|
3121
|
+
enumerable: true,
|
|
3122
|
+
writable: true,
|
|
3123
|
+
value,
|
|
3124
|
+
});
|
|
3011
3125
|
}
|
|
3012
3126
|
// noChange sentinel - preserves the currently committed value
|
|
3013
3127
|
const noChange = Symbol.for('snice:no-change');
|
|
@@ -4644,15 +4758,13 @@ class SpreadPart extends Part {
|
|
|
4644
4758
|
commitProperties(next) {
|
|
4645
4759
|
for (const key of Object.keys(this.committed)) {
|
|
4646
4760
|
if (!Object.prototype.hasOwnProperty.call(next, key)) {
|
|
4647
|
-
|
|
4648
|
-
this.element[key] = undefined;
|
|
4761
|
+
commitPropertyValue(this.element, key, undefined);
|
|
4649
4762
|
}
|
|
4650
4763
|
}
|
|
4651
4764
|
for (const [key, value] of Object.entries(next)) {
|
|
4652
4765
|
if (Object.is(this.committed[key], value))
|
|
4653
4766
|
continue;
|
|
4654
|
-
|
|
4655
|
-
this.element[key] = value === nothing ? undefined : value;
|
|
4767
|
+
commitPropertyValue(this.element, key, value === nothing ? undefined : value);
|
|
4656
4768
|
}
|
|
4657
4769
|
}
|
|
4658
4770
|
commitAttributes(next) {
|
|
@@ -4779,8 +4891,7 @@ class PropertyPart extends Part {
|
|
|
4779
4891
|
return;
|
|
4780
4892
|
}
|
|
4781
4893
|
this._committedValue = value;
|
|
4782
|
-
|
|
4783
|
-
this.element[this.name] = value === nothing ? undefined : value;
|
|
4894
|
+
commitPropertyValue(this.element, this.name, value === nothing ? undefined : value);
|
|
4784
4895
|
}
|
|
4785
4896
|
clear() {
|
|
4786
4897
|
// This part is being destroyed with its owning template. The element is
|
|
@@ -6812,7 +6923,7 @@ function applyElementFunctionality(constructor) {
|
|
|
6812
6923
|
// Re-establish handlers that get cleaned up on disconnect
|
|
6813
6924
|
setupEventHandlers(this, this);
|
|
6814
6925
|
setupResponseHandlers(this, this);
|
|
6815
|
-
setupContextHandler(this);
|
|
6926
|
+
void setupContextHandler(this, 'task');
|
|
6816
6927
|
// Re-establish observers that get cleaned up on disconnect
|
|
6817
6928
|
try {
|
|
6818
6929
|
setupObservers(this, this);
|
|
@@ -6937,7 +7048,7 @@ function applyElementFunctionality(constructor) {
|
|
|
6937
7048
|
}
|
|
6938
7049
|
setupEventHandlers(this, this);
|
|
6939
7050
|
setupResponseHandlers(this, this);
|
|
6940
|
-
setupContextHandler(this);
|
|
7051
|
+
void setupContextHandler(this, 'task');
|
|
6941
7052
|
this[INITIALIZED] = true;
|
|
6942
7053
|
// One-time (per class) sanity check: a @watch name with no matching
|
|
6943
7054
|
// @property never fires — surface the typo instead of staying silent.
|
|
@@ -8642,10 +8753,11 @@ function bindContextDaemons(context) {
|
|
|
8642
8753
|
*
|
|
8643
8754
|
* Daemon instances must already exist in context.daemons. The returned cleanup
|
|
8644
8755
|
* removes the provider and deactivates framework communication for daemons no
|
|
8645
|
-
* longer provided by any context.
|
|
8756
|
+
* longer provided by any context. An optional fetch function is exposed on the
|
|
8757
|
+
* same boundary through getContextFetch() without modifying the context object.
|
|
8646
8758
|
*/
|
|
8647
|
-
function
|
|
8648
|
-
const releaseProvider = installContextProvider(root, context);
|
|
8759
|
+
function provideContextBoundary(root, context, options, navigation) {
|
|
8760
|
+
const releaseProvider = installContextProvider(root, context, options.fetch, navigation);
|
|
8649
8761
|
let releaseDaemons;
|
|
8650
8762
|
try {
|
|
8651
8763
|
releaseDaemons = bindContextDaemons(context);
|
|
@@ -8663,6 +8775,15 @@ function provideContext(root, context) {
|
|
|
8663
8775
|
releaseDaemons?.();
|
|
8664
8776
|
};
|
|
8665
8777
|
}
|
|
8778
|
+
function provideContext(root, context, options = {}) {
|
|
8779
|
+
return provideContextBoundary(root, context, options);
|
|
8780
|
+
}
|
|
8781
|
+
/** @internal Router-owned provider including live navigation state. */
|
|
8782
|
+
function provideRouterContext(root, navigation) {
|
|
8783
|
+
return provideContextBoundary(root, navigation.application, {
|
|
8784
|
+
fetch: navigation.fetch,
|
|
8785
|
+
}, navigation);
|
|
8786
|
+
}
|
|
8666
8787
|
|
|
8667
8788
|
// Router historically allows a later Router instance to take over the same
|
|
8668
8789
|
// target. Track only Router-owned provider releases so that handoff remains
|
|
@@ -8686,6 +8807,7 @@ function Router(options) {
|
|
|
8686
8807
|
const win = options.window ?? window;
|
|
8687
8808
|
const doc = options.document ?? document;
|
|
8688
8809
|
const routes = [];
|
|
8810
|
+
let nextRegistrationOrder = 0;
|
|
8689
8811
|
let is_sorted = false;
|
|
8690
8812
|
let placards = []; // Store collected placards
|
|
8691
8813
|
let _404; // the 404 page
|
|
@@ -8705,7 +8827,7 @@ function Router(options) {
|
|
|
8705
8827
|
return;
|
|
8706
8828
|
releaseContextProvider?.();
|
|
8707
8829
|
routerContextProviders.get(target)?.();
|
|
8708
|
-
const releaseProvidedContext =
|
|
8830
|
+
const releaseProvidedContext = provideRouterContext(target, navigationContext);
|
|
8709
8831
|
let released = false;
|
|
8710
8832
|
const release = () => {
|
|
8711
8833
|
if (released)
|
|
@@ -8776,7 +8898,11 @@ function Router(options) {
|
|
|
8776
8898
|
win.customElements.define(pageOptions.tag, constructor);
|
|
8777
8899
|
}
|
|
8778
8900
|
// Register the routes with guards, layout, and placard
|
|
8779
|
-
pageOptions.routes.forEach(route =>
|
|
8901
|
+
pageOptions.routes.forEach(route => {
|
|
8902
|
+
const path = typeof route === 'string' ? route : route.path;
|
|
8903
|
+
const order = typeof route === 'string' ? undefined : route.order;
|
|
8904
|
+
register(path, pageOptions.tag, pageOptions.transition, pageOptions.guards, pageOptions.layout, pageOptions.placard, order);
|
|
8905
|
+
});
|
|
8780
8906
|
return constructor;
|
|
8781
8907
|
};
|
|
8782
8908
|
}
|
|
@@ -8784,11 +8910,24 @@ function Router(options) {
|
|
|
8784
8910
|
* Registers a new route with the router.
|
|
8785
8911
|
* @param {string} route - The route path.
|
|
8786
8912
|
* @param {string} tag - The custom element tag associated with the route.
|
|
8913
|
+
* @param {number} order - Optional lower-first tie-break after specificity.
|
|
8787
8914
|
* @example
|
|
8788
8915
|
* register('/custom-route', 'custom-element');
|
|
8789
8916
|
*/
|
|
8790
|
-
function register(route, tag, transition, guards, layout, placard) {
|
|
8791
|
-
|
|
8917
|
+
function register(route, tag, transition, guards, layout, placard, order = 0) {
|
|
8918
|
+
if (!Number.isFinite(order)) {
|
|
8919
|
+
throw new TypeError(`Route order for "${route}" must be a finite number.`);
|
|
8920
|
+
}
|
|
8921
|
+
routes.push({
|
|
8922
|
+
route: new Route(route),
|
|
8923
|
+
tag,
|
|
8924
|
+
transition,
|
|
8925
|
+
guards,
|
|
8926
|
+
layout,
|
|
8927
|
+
placard,
|
|
8928
|
+
order,
|
|
8929
|
+
registrationOrder: nextRegistrationOrder++
|
|
8930
|
+
});
|
|
8792
8931
|
is_sorted = false;
|
|
8793
8932
|
switch (route) {
|
|
8794
8933
|
case '/404':
|
|
@@ -8849,7 +8988,9 @@ function Router(options) {
|
|
|
8849
8988
|
ensureContextProvider(target);
|
|
8850
8989
|
const needsSorting = !is_sorted;
|
|
8851
8990
|
if (needsSorting) {
|
|
8852
|
-
routes.sort((a, b) => routeSpecificity(b.route.spec) - routeSpecificity(a.route.spec)
|
|
8991
|
+
routes.sort((a, b) => routeSpecificity(b.route.spec) - routeSpecificity(a.route.spec)
|
|
8992
|
+
|| a.order - b.order
|
|
8993
|
+
|| a.registrationOrder - b.registrationOrder);
|
|
8853
8994
|
is_sorted = true;
|
|
8854
8995
|
}
|
|
8855
8996
|
// Collect placards from registered routes
|
|
@@ -9570,7 +9711,7 @@ if (typeof document !== 'undefined') {
|
|
|
9570
9711
|
}
|
|
9571
9712
|
|
|
9572
9713
|
/*!
|
|
9573
|
-
* snice v7.
|
|
9714
|
+
* snice v7.3.0
|
|
9574
9715
|
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
9575
9716
|
* (c) 2024
|
|
9576
9717
|
* Released under the MIT License.
|
|
@@ -9592,7 +9733,7 @@ if (!globalThis.snice) {
|
|
|
9592
9733
|
globalThis.snice.pendingControllerAttachments ??= new Map();
|
|
9593
9734
|
|
|
9594
9735
|
/*!
|
|
9595
|
-
* snice v7.
|
|
9736
|
+
* snice v7.3.0
|
|
9596
9737
|
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
9597
9738
|
* (c) 2024
|
|
9598
9739
|
* Released under the MIT License.
|
|
@@ -9610,5 +9751,5 @@ var TransitionMode;
|
|
|
9610
9751
|
TransitionMode["SIMULTANEOUS"] = "simultaneous";
|
|
9611
9752
|
})(TransitionMode || (TransitionMode = {}));
|
|
9612
9753
|
|
|
9613
|
-
export { Context, ContextAwareFetcher, IS_CONTROLLER_INSTANCE, Router, SimpleArray, SniceElement, TransitionMode$1 as TransitionMode, adopted, applyElementFunctionality, attachController, classMap, clearDebounceTimers, clearMemoizeCache, clearThrottleTimers, context$1 as context, context as contextProperty, controller, createRequestHandler, css, daemon, debounce, detachController, dispatch, dispose, element, escapeAttr, escapeHtml, getBodyScrollLockCount, getContext, getController, getSymbol, html, isSafeUrl, layout, live, lockBodyScroll, memoize, moved, noChange, nothing, observe, on, once, parseDuration, property, provideContext, query, queryAll, ready, reconnect, render, repeat, request, resetOnce, respond, setDisableElementReadyWarnings, setStrictRenderErrors, state, styleMap, styles, svg, throttle, trackRenders, unlockBodyScroll, unsafeHTML, useNativeElementControllers, waitForAllCustomElements, waitForElementDefined, waitForElementReady, watch };
|
|
9754
|
+
export { Context, ContextAwareFetcher, IS_CONTROLLER_INSTANCE, Router, SimpleArray, SniceElement, TransitionMode$1 as TransitionMode, adopted, applyElementFunctionality, attachController, classMap, clearDebounceTimers, clearMemoizeCache, clearThrottleTimers, context$1 as context, context as contextProperty, controller, createRequestHandler, css, daemon, debounce, detachController, dispatch, dispose, element, escapeAttr, escapeHtml, getBodyScrollLockCount, getContext, getContextFetch, getController, getSymbol, html, isSafeUrl, layout, live, lockBodyScroll, memoize, moved, noChange, nothing, observe, on, once, parseDuration, property, provideContext, query, queryAll, ready, reconnect, render, repeat, request, resetOnce, respond, setDisableElementReadyWarnings, setStrictRenderErrors, state, styleMap, styles, svg, throttle, trackRenders, unlockBodyScroll, unsafeHTML, useNativeElementControllers, waitForAllCustomElements, waitForElementDefined, waitForElementReady, watch };
|
|
9614
9755
|
//# sourceMappingURL=snice-runtime.esm.js.map
|