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
package/dist/index.iife.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v7.
|
|
2
|
+
* snice v7.3.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.
|
|
@@ -233,7 +233,7 @@ var Snice = (function (exports) {
|
|
|
233
233
|
*/
|
|
234
234
|
class Context {
|
|
235
235
|
constructor(context = {}, placards = [], currentRoute = '', routeParams = {}, fetcher) {
|
|
236
|
-
// Stores the SET of wrapped-method names per
|
|
236
|
+
// Stores the SET of wrapped-method names per subscriber so that classes with
|
|
237
237
|
// multiple @context() handlers register every method, not just the last one.
|
|
238
238
|
this[_a] = new WeakMap();
|
|
239
239
|
this[_b] = new Set();
|
|
@@ -257,7 +257,7 @@ var Snice = (function (exports) {
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
/**
|
|
260
|
-
* Register an element to receive context updates
|
|
260
|
+
* Register an element or controller to receive context updates
|
|
261
261
|
* @internal Used by @context decorator
|
|
262
262
|
*/
|
|
263
263
|
[(_a = REGISTERED_ELEMENTS, _b = REGISTERED_ELEMENTS_SET, _c = IS_UPDATING, CONTEXT_REGISTER)](element, methodName) {
|
|
@@ -271,10 +271,10 @@ var Snice = (function (exports) {
|
|
|
271
271
|
this[REGISTERED_ELEMENTS_SET].add(element);
|
|
272
272
|
}
|
|
273
273
|
/**
|
|
274
|
-
* Unregister
|
|
274
|
+
* Unregister a subscriber from context updates. With a methodName,
|
|
275
275
|
* removes only that one handler (so a `once` handler tearing itself down
|
|
276
276
|
* doesn't kill the element's other @context handlers); without one, removes
|
|
277
|
-
* the
|
|
277
|
+
* the subscriber entirely (used on disconnect/detach).
|
|
278
278
|
* @internal Used by @context decorator cleanup
|
|
279
279
|
*/
|
|
280
280
|
[CONTEXT_UNREGISTER](element, methodName) {
|
|
@@ -315,7 +315,7 @@ var Snice = (function (exports) {
|
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
317
|
this[IS_UPDATING] = true;
|
|
318
|
-
// Notify all registered
|
|
318
|
+
// Notify all registered subscribers by calling EVERY registered method
|
|
319
319
|
// name on each. Classes with multiple @context() methods get all fired.
|
|
320
320
|
const elementsSet = this[REGISTERED_ELEMENTS_SET];
|
|
321
321
|
const elementsMap = this[REGISTERED_ELEMENTS];
|
|
@@ -337,7 +337,7 @@ var Snice = (function (exports) {
|
|
|
337
337
|
this[IS_UPDATING] = false;
|
|
338
338
|
}
|
|
339
339
|
/**
|
|
340
|
-
* Notify a specific
|
|
340
|
+
* Notify a specific subscriber of the current context state
|
|
341
341
|
* @internal Used by @context decorator
|
|
342
342
|
*/
|
|
343
343
|
[CONTEXT_NOTIFY_ELEMENT](element) {
|
|
@@ -346,7 +346,12 @@ var Snice = (function (exports) {
|
|
|
346
346
|
return;
|
|
347
347
|
for (const methodName of names) {
|
|
348
348
|
if (typeof element[methodName] === 'function') {
|
|
349
|
-
|
|
349
|
+
try {
|
|
350
|
+
element[methodName](this);
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
console.error(`Error calling @context method ${methodName}:`, error);
|
|
354
|
+
}
|
|
350
355
|
}
|
|
351
356
|
}
|
|
352
357
|
}
|
|
@@ -678,11 +683,17 @@ var Snice = (function (exports) {
|
|
|
678
683
|
*/
|
|
679
684
|
function createDebounced(fn, delay) {
|
|
680
685
|
let timer = null;
|
|
681
|
-
|
|
686
|
+
const debounced = function (...args) {
|
|
682
687
|
if (timer !== null)
|
|
683
688
|
clearTimeout(timer);
|
|
684
689
|
timer = setTimeout(() => { fn.apply(this, args); timer = null; }, delay);
|
|
685
690
|
};
|
|
691
|
+
debounced.cancel = () => {
|
|
692
|
+
if (timer !== null)
|
|
693
|
+
clearTimeout(timer);
|
|
694
|
+
timer = null;
|
|
695
|
+
};
|
|
696
|
+
return debounced;
|
|
686
697
|
}
|
|
687
698
|
/**
|
|
688
699
|
* Create a throttled version of a function (leading + trailing edge)
|
|
@@ -1139,7 +1150,7 @@ var Snice = (function (exports) {
|
|
|
1139
1150
|
* boundary. Public callers use provideContext(), which also activates the
|
|
1140
1151
|
* daemon instances declared by the context.
|
|
1141
1152
|
*/
|
|
1142
|
-
function installContextProvider(root, context) {
|
|
1153
|
+
function installContextProvider(root, context, fetch, navigation) {
|
|
1143
1154
|
if (!root || typeof root.addEventListener !== 'function' || typeof root.removeEventListener !== 'function') {
|
|
1144
1155
|
throw new TypeError('provideContext() requires an EventTarget root.');
|
|
1145
1156
|
}
|
|
@@ -1151,9 +1162,26 @@ var Snice = (function (exports) {
|
|
|
1151
1162
|
}
|
|
1152
1163
|
const listener = (event) => {
|
|
1153
1164
|
const request = event;
|
|
1154
|
-
if (!request.detail
|
|
1165
|
+
if (!request.detail)
|
|
1166
|
+
return;
|
|
1167
|
+
if (request.detail.kind === 'application') {
|
|
1168
|
+
if (request.detail.context !== undefined)
|
|
1169
|
+
return;
|
|
1170
|
+
request.detail.context = context;
|
|
1171
|
+
}
|
|
1172
|
+
else if (request.detail.kind === 'fetch') {
|
|
1173
|
+
if (!fetch || request.detail.fetch !== undefined)
|
|
1174
|
+
return;
|
|
1175
|
+
request.detail.fetch = fetch;
|
|
1176
|
+
}
|
|
1177
|
+
else if (request.detail.kind === 'navigation') {
|
|
1178
|
+
if (!navigation || request.detail.navigation !== undefined)
|
|
1179
|
+
return;
|
|
1180
|
+
request.detail.navigation = navigation;
|
|
1181
|
+
}
|
|
1182
|
+
else {
|
|
1155
1183
|
return;
|
|
1156
|
-
|
|
1184
|
+
}
|
|
1157
1185
|
request.preventDefault();
|
|
1158
1186
|
request.stopPropagation();
|
|
1159
1187
|
};
|
|
@@ -1183,7 +1211,7 @@ var Snice = (function (exports) {
|
|
|
1183
1211
|
}
|
|
1184
1212
|
const target = contextTarget(source);
|
|
1185
1213
|
if (target) {
|
|
1186
|
-
const detail = {};
|
|
1214
|
+
const detail = { kind: 'application' };
|
|
1187
1215
|
target.dispatchEvent(new CustomEvent(CONTEXT_REQUEST_EVENT, {
|
|
1188
1216
|
bubbles: true,
|
|
1189
1217
|
composed: true,
|
|
@@ -1202,6 +1230,39 @@ var Snice = (function (exports) {
|
|
|
1202
1230
|
}
|
|
1203
1231
|
return undefined;
|
|
1204
1232
|
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Resolve the fetch function supplied by the nearest context provider.
|
|
1235
|
+
* Router providers expose their ContextAwareFetcher-bound function here;
|
|
1236
|
+
* explicit providers may supply the same dependency for non-router apps and
|
|
1237
|
+
* tests without adding a reserved property to AppContext.
|
|
1238
|
+
*/
|
|
1239
|
+
function getContextFetch(source) {
|
|
1240
|
+
const target = contextTarget(source);
|
|
1241
|
+
if (!target)
|
|
1242
|
+
return undefined;
|
|
1243
|
+
const detail = { kind: 'fetch' };
|
|
1244
|
+
target.dispatchEvent(new CustomEvent(CONTEXT_REQUEST_EVENT, {
|
|
1245
|
+
bubbles: true,
|
|
1246
|
+
composed: true,
|
|
1247
|
+
cancelable: true,
|
|
1248
|
+
detail,
|
|
1249
|
+
}));
|
|
1250
|
+
return detail.fetch;
|
|
1251
|
+
}
|
|
1252
|
+
/** @internal Resolve the router Context visible to an element or controller. */
|
|
1253
|
+
function getNavigationContext(source) {
|
|
1254
|
+
const target = contextTarget(source);
|
|
1255
|
+
if (!target)
|
|
1256
|
+
return undefined;
|
|
1257
|
+
const detail = { kind: 'navigation' };
|
|
1258
|
+
target.dispatchEvent(new CustomEvent(CONTEXT_REQUEST_EVENT, {
|
|
1259
|
+
bubbles: true,
|
|
1260
|
+
composed: true,
|
|
1261
|
+
cancelable: true,
|
|
1262
|
+
detail,
|
|
1263
|
+
}));
|
|
1264
|
+
return detail.navigation;
|
|
1265
|
+
}
|
|
1205
1266
|
|
|
1206
1267
|
function isDaemonInstance(value) {
|
|
1207
1268
|
return !!value
|
|
@@ -1865,12 +1926,13 @@ var Snice = (function (exports) {
|
|
|
1865
1926
|
}
|
|
1866
1927
|
// Initialize cleanup object if needed
|
|
1867
1928
|
if (!instance[CLEANUP]) {
|
|
1868
|
-
instance[CLEANUP] = { events: [], channels: [], observers: [] };
|
|
1929
|
+
instance[CLEANUP] = { events: [], eventCancels: [], channels: [], observers: [] };
|
|
1869
1930
|
}
|
|
1870
|
-
|
|
1931
|
+
if (!instance[CLEANUP].events)
|
|
1871
1932
|
instance[CLEANUP].events = [];
|
|
1872
|
-
|
|
1873
|
-
|
|
1933
|
+
if (!instance[CLEANUP].eventCancels)
|
|
1934
|
+
instance[CLEANUP].eventCancels = [];
|
|
1935
|
+
if (instance[CLEANUP].events.length > 0 || instance[CLEANUP].eventCancels.length > 0) {
|
|
1874
1936
|
// Events already set up - clean them up first to avoid duplicates
|
|
1875
1937
|
cleanupEventHandlers(instance);
|
|
1876
1938
|
}
|
|
@@ -1900,7 +1962,9 @@ var Snice = (function (exports) {
|
|
|
1900
1962
|
warnIfModifierMisuse(handler.eventName);
|
|
1901
1963
|
// Apply debounce (takes precedence over throttle)
|
|
1902
1964
|
if (handlerOptions.debounce && handlerOptions.debounce > 0) {
|
|
1903
|
-
|
|
1965
|
+
const debounced = createDebounced(boundMethod, handlerOptions.debounce);
|
|
1966
|
+
instance[CLEANUP].eventCancels.push(() => debounced.cancel());
|
|
1967
|
+
boundMethod = debounced;
|
|
1904
1968
|
}
|
|
1905
1969
|
else if (handlerOptions.throttle && handlerOptions.throttle > 0) {
|
|
1906
1970
|
boundMethod = throttle$1(boundMethod, handlerOptions.throttle);
|
|
@@ -2073,12 +2137,190 @@ var Snice = (function (exports) {
|
|
|
2073
2137
|
function cleanupEventHandlers(instance) {
|
|
2074
2138
|
if (!instance[CLEANUP]?.events)
|
|
2075
2139
|
return;
|
|
2140
|
+
for (const cancel of instance[CLEANUP].eventCancels ?? [])
|
|
2141
|
+
cancel();
|
|
2142
|
+
instance[CLEANUP].eventCancels = [];
|
|
2076
2143
|
for (const { target, eventName, handler, options } of instance[CLEANUP].events) {
|
|
2077
2144
|
target.removeEventListener(eventName, handler, options);
|
|
2078
2145
|
}
|
|
2079
2146
|
instance[CLEANUP].events = [];
|
|
2080
2147
|
}
|
|
2081
2148
|
|
|
2149
|
+
/**
|
|
2150
|
+
* @context decorator for receiving router context updates
|
|
2151
|
+
*/
|
|
2152
|
+
const CONTEXT_HANDLERS = getSymbol('context-handlers');
|
|
2153
|
+
/**
|
|
2154
|
+
* @context decorator for receiving router context updates
|
|
2155
|
+
*
|
|
2156
|
+
* @example
|
|
2157
|
+
* ```typescript
|
|
2158
|
+
* @element('my-layout')
|
|
2159
|
+
* class MyLayout extends HTMLElement {
|
|
2160
|
+
* @context
|
|
2161
|
+
* handleContext(ctx: Context) {
|
|
2162
|
+
* this.renderNav(ctx.placards, ctx.currentRoute);
|
|
2163
|
+
* }
|
|
2164
|
+
*
|
|
2165
|
+
* @context({ debounce: 300 })
|
|
2166
|
+
* handleContextDebounced(ctx: Context) {
|
|
2167
|
+
* // Called after 300ms of no updates
|
|
2168
|
+
* }
|
|
2169
|
+
* }
|
|
2170
|
+
* ```
|
|
2171
|
+
*/
|
|
2172
|
+
function context$1(options = {}) {
|
|
2173
|
+
return function (originalMethod, context) {
|
|
2174
|
+
const methodName = context.name;
|
|
2175
|
+
context.addInitializer(function () {
|
|
2176
|
+
const constructor = this.constructor;
|
|
2177
|
+
// hasOwnProperty guards so subclasses don't mutate parent state via
|
|
2178
|
+
// the prototype chain.
|
|
2179
|
+
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_METHODS)) {
|
|
2180
|
+
constructor[CONTEXT_METHODS] = new Set();
|
|
2181
|
+
}
|
|
2182
|
+
if (constructor[CONTEXT_METHODS].has(originalMethod))
|
|
2183
|
+
return;
|
|
2184
|
+
constructor[CONTEXT_METHODS].add(originalMethod);
|
|
2185
|
+
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_HANDLERS)) {
|
|
2186
|
+
const inherited = constructor[CONTEXT_HANDLERS];
|
|
2187
|
+
constructor[CONTEXT_HANDLERS] = inherited ? [...inherited] : [];
|
|
2188
|
+
}
|
|
2189
|
+
constructor[CONTEXT_HANDLERS].push({
|
|
2190
|
+
methodName,
|
|
2191
|
+
method: originalMethod,
|
|
2192
|
+
options,
|
|
2193
|
+
});
|
|
2194
|
+
});
|
|
2195
|
+
return originalMethod;
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Set up context handlers for an element or attached controller.
|
|
2200
|
+
* Called automatically by the corresponding managed lifecycle.
|
|
2201
|
+
*/
|
|
2202
|
+
function setupContextHandler(element, catchUp = false) {
|
|
2203
|
+
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2204
|
+
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
// Get the Context instance from the router
|
|
2208
|
+
const ctx = element[CONTEXT_HANDLER] ?? getNavigationContext(element);
|
|
2209
|
+
if (!ctx) {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
// Store the Context instance for cleanup
|
|
2213
|
+
element[NAVIGATION_CONTEXT_INSTANCE] = ctx;
|
|
2214
|
+
// A controller can finish attaching after the Router has already announced
|
|
2215
|
+
// the current navigation. Track whether an update arrives during this turn;
|
|
2216
|
+
// if none does, catch it up once from the Context's current state.
|
|
2217
|
+
let receivedContextUpdate = false;
|
|
2218
|
+
// Register each handler with the Context
|
|
2219
|
+
for (const handler of handlers) {
|
|
2220
|
+
const { methodName, method, options } = handler;
|
|
2221
|
+
const wrappedMethodName = `__wrapped_${methodName}`;
|
|
2222
|
+
// Create wrapped method with timing controls
|
|
2223
|
+
element[wrappedMethodName] = function (context) {
|
|
2224
|
+
receivedContextUpdate = true;
|
|
2225
|
+
// `once` is tracked PER method (not a single element-level flag) so
|
|
2226
|
+
// multiple once handlers on the same element each fire.
|
|
2227
|
+
const called = element[CONTEXT_CALLED] ||
|
|
2228
|
+
(element[CONTEXT_CALLED] = new Set());
|
|
2229
|
+
if (options.once && called.has(methodName)) {
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
const callMethod = () => {
|
|
2233
|
+
method.call(element, context);
|
|
2234
|
+
// Handle once option
|
|
2235
|
+
if (options.once) {
|
|
2236
|
+
called.add(methodName);
|
|
2237
|
+
// Unregister ONLY this handler, not the whole element — otherwise the
|
|
2238
|
+
// element's other @context handlers would stop receiving updates.
|
|
2239
|
+
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2240
|
+
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2241
|
+
ctx[CONTEXT_UNREGISTER](element, wrappedMethodName);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
};
|
|
2245
|
+
// Per-handler timer slot to avoid debounce/throttle handlers on the same
|
|
2246
|
+
// element overwriting each other's state.
|
|
2247
|
+
const timerSlot = element[CONTEXT_TIMER] ||
|
|
2248
|
+
(element[CONTEXT_TIMER] = {});
|
|
2249
|
+
const timerKey = methodName;
|
|
2250
|
+
if (options.debounce) {
|
|
2251
|
+
clearTimeout(timerSlot[timerKey]?.timeout);
|
|
2252
|
+
timerSlot[timerKey] = {
|
|
2253
|
+
timeout: setTimeout(callMethod, options.debounce),
|
|
2254
|
+
};
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
if (options.throttle) {
|
|
2258
|
+
const now = Date.now();
|
|
2259
|
+
const lastCall = timerSlot[timerKey]?.lastCall || 0;
|
|
2260
|
+
if (now - lastCall >= options.throttle) {
|
|
2261
|
+
timerSlot[timerKey] = { lastCall: now };
|
|
2262
|
+
callMethod();
|
|
2263
|
+
}
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
callMethod();
|
|
2267
|
+
};
|
|
2268
|
+
// Register with the Context using the wrapped method name
|
|
2269
|
+
if (typeof ctx[CONTEXT_REGISTER] === 'function') {
|
|
2270
|
+
ctx[CONTEXT_REGISTER](element, wrappedMethodName);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
if (catchUp && typeof ctx[CONTEXT_NOTIFY_ELEMENT] === 'function') {
|
|
2274
|
+
return new Promise((resolve) => {
|
|
2275
|
+
const notifyIfNeeded = () => {
|
|
2276
|
+
try {
|
|
2277
|
+
if (!receivedContextUpdate
|
|
2278
|
+
&& element[NAVIGATION_CONTEXT_INSTANCE] === ctx) {
|
|
2279
|
+
ctx[CONTEXT_NOTIFY_ELEMENT](element);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
finally {
|
|
2283
|
+
resolve();
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
if (catchUp === 'task')
|
|
2287
|
+
setTimeout(notifyIfNeeded, 0);
|
|
2288
|
+
else
|
|
2289
|
+
queueMicrotask(notifyIfNeeded);
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Clean up context handlers for an element or attached controller.
|
|
2295
|
+
*/
|
|
2296
|
+
function cleanupContextHandler(element) {
|
|
2297
|
+
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2298
|
+
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
// Clear any pending debounce timers (per-handler slots)
|
|
2302
|
+
const timerSlot = element[CONTEXT_TIMER];
|
|
2303
|
+
if (timerSlot && typeof timerSlot === 'object') {
|
|
2304
|
+
for (const key of Object.keys(timerSlot)) {
|
|
2305
|
+
if (timerSlot[key]?.timeout)
|
|
2306
|
+
clearTimeout(timerSlot[key].timeout);
|
|
2307
|
+
}
|
|
2308
|
+
delete element[CONTEXT_TIMER];
|
|
2309
|
+
}
|
|
2310
|
+
for (const handler of handlers) {
|
|
2311
|
+
// Clean up wrapped method
|
|
2312
|
+
const wrappedMethodName = `__wrapped_${handler.methodName}`;
|
|
2313
|
+
delete element[wrappedMethodName];
|
|
2314
|
+
}
|
|
2315
|
+
// Unregister from Context if available
|
|
2316
|
+
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2317
|
+
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2318
|
+
ctx[CONTEXT_UNREGISTER](element);
|
|
2319
|
+
}
|
|
2320
|
+
delete element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2321
|
+
delete element[CONTEXT_CALLED];
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2082
2324
|
// Controller-scoped cleanup registry
|
|
2083
2325
|
class ControllerScope {
|
|
2084
2326
|
constructor() {
|
|
@@ -2412,6 +2654,10 @@ var Snice = (function (exports) {
|
|
|
2412
2654
|
await scope.runOperation(async () => {
|
|
2413
2655
|
await controllerInstance.attach(element);
|
|
2414
2656
|
});
|
|
2657
|
+
// @context has the same managed lifecycle on controllers as @on,
|
|
2658
|
+
// @observe and @respond. Catch up controllers whose async attach completed
|
|
2659
|
+
// after the Router announced the current navigation.
|
|
2660
|
+
await setupContextHandler(controllerInstance, 'microtask');
|
|
2415
2661
|
// Setup @observe observers for controller
|
|
2416
2662
|
setupObservers(controllerInstance, element);
|
|
2417
2663
|
// Setup @channel handlers for controller
|
|
@@ -2477,6 +2723,8 @@ var Snice = (function (exports) {
|
|
|
2477
2723
|
cleanupResponseHandlers(controllerInstance);
|
|
2478
2724
|
// Cleanup @on event handlers for controller
|
|
2479
2725
|
cleanupEventHandlers(controllerInstance);
|
|
2726
|
+
// Cleanup @context handlers for controller
|
|
2727
|
+
cleanupContextHandler(controllerInstance);
|
|
2480
2728
|
// Cleanup the controller scope
|
|
2481
2729
|
if (scope) {
|
|
2482
2730
|
await scope.cleanup();
|
|
@@ -2610,158 +2858,6 @@ var Snice = (function (exports) {
|
|
|
2610
2858
|
globalThis.sniceNativeControllerObserver = observer;
|
|
2611
2859
|
}
|
|
2612
2860
|
|
|
2613
|
-
/**
|
|
2614
|
-
* @context decorator for receiving router context updates
|
|
2615
|
-
*/
|
|
2616
|
-
const CONTEXT_HANDLERS = getSymbol('context-handlers');
|
|
2617
|
-
/**
|
|
2618
|
-
* @context decorator for receiving router context updates
|
|
2619
|
-
*
|
|
2620
|
-
* @example
|
|
2621
|
-
* ```typescript
|
|
2622
|
-
* @element('my-layout')
|
|
2623
|
-
* class MyLayout extends HTMLElement {
|
|
2624
|
-
* @context
|
|
2625
|
-
* handleContext(ctx: Context) {
|
|
2626
|
-
* this.renderNav(ctx.placards, ctx.currentRoute);
|
|
2627
|
-
* }
|
|
2628
|
-
*
|
|
2629
|
-
* @context({ debounce: 300 })
|
|
2630
|
-
* handleContextDebounced(ctx: Context) {
|
|
2631
|
-
* // Called after 300ms of no updates
|
|
2632
|
-
* }
|
|
2633
|
-
* }
|
|
2634
|
-
* ```
|
|
2635
|
-
*/
|
|
2636
|
-
function context$1(options = {}) {
|
|
2637
|
-
return function (originalMethod, context) {
|
|
2638
|
-
const methodName = context.name;
|
|
2639
|
-
context.addInitializer(function () {
|
|
2640
|
-
const constructor = this.constructor;
|
|
2641
|
-
// hasOwnProperty guards so subclasses don't mutate parent state via
|
|
2642
|
-
// the prototype chain.
|
|
2643
|
-
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_METHODS)) {
|
|
2644
|
-
constructor[CONTEXT_METHODS] = new Set();
|
|
2645
|
-
}
|
|
2646
|
-
if (constructor[CONTEXT_METHODS].has(originalMethod))
|
|
2647
|
-
return;
|
|
2648
|
-
constructor[CONTEXT_METHODS].add(originalMethod);
|
|
2649
|
-
if (!Object.prototype.hasOwnProperty.call(constructor, CONTEXT_HANDLERS)) {
|
|
2650
|
-
const inherited = constructor[CONTEXT_HANDLERS];
|
|
2651
|
-
constructor[CONTEXT_HANDLERS] = inherited ? [...inherited] : [];
|
|
2652
|
-
}
|
|
2653
|
-
constructor[CONTEXT_HANDLERS].push({
|
|
2654
|
-
methodName,
|
|
2655
|
-
method: originalMethod,
|
|
2656
|
-
options,
|
|
2657
|
-
});
|
|
2658
|
-
});
|
|
2659
|
-
return originalMethod;
|
|
2660
|
-
};
|
|
2661
|
-
}
|
|
2662
|
-
/**
|
|
2663
|
-
* Setup context handler for an element instance
|
|
2664
|
-
* Called automatically during element connection
|
|
2665
|
-
*/
|
|
2666
|
-
function setupContextHandler(element) {
|
|
2667
|
-
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2668
|
-
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2669
|
-
return;
|
|
2670
|
-
}
|
|
2671
|
-
// Get the Context instance from the router
|
|
2672
|
-
const ctx = element[CONTEXT_HANDLER];
|
|
2673
|
-
if (!ctx) {
|
|
2674
|
-
return;
|
|
2675
|
-
}
|
|
2676
|
-
// Store the Context instance for cleanup
|
|
2677
|
-
element[NAVIGATION_CONTEXT_INSTANCE] = ctx;
|
|
2678
|
-
// Register each handler with the Context
|
|
2679
|
-
for (const handler of handlers) {
|
|
2680
|
-
const { methodName, method, options } = handler;
|
|
2681
|
-
const wrappedMethodName = `__wrapped_${methodName}`;
|
|
2682
|
-
// Create wrapped method with timing controls
|
|
2683
|
-
element[wrappedMethodName] = function (context) {
|
|
2684
|
-
// `once` is tracked PER method (not a single element-level flag) so
|
|
2685
|
-
// multiple once handlers on the same element each fire.
|
|
2686
|
-
const called = element[CONTEXT_CALLED] ||
|
|
2687
|
-
(element[CONTEXT_CALLED] = new Set());
|
|
2688
|
-
if (options.once && called.has(methodName)) {
|
|
2689
|
-
return;
|
|
2690
|
-
}
|
|
2691
|
-
const callMethod = () => {
|
|
2692
|
-
method.call(element, context);
|
|
2693
|
-
// Handle once option
|
|
2694
|
-
if (options.once) {
|
|
2695
|
-
called.add(methodName);
|
|
2696
|
-
// Unregister ONLY this handler, not the whole element — otherwise the
|
|
2697
|
-
// element's other @context handlers would stop receiving updates.
|
|
2698
|
-
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2699
|
-
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2700
|
-
ctx[CONTEXT_UNREGISTER](element, wrappedMethodName);
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
};
|
|
2704
|
-
// Per-handler timer slot to avoid debounce/throttle handlers on the same
|
|
2705
|
-
// element overwriting each other's state.
|
|
2706
|
-
const timerSlot = element[CONTEXT_TIMER] ||
|
|
2707
|
-
(element[CONTEXT_TIMER] = {});
|
|
2708
|
-
const timerKey = methodName;
|
|
2709
|
-
if (options.debounce) {
|
|
2710
|
-
clearTimeout(timerSlot[timerKey]?.timeout);
|
|
2711
|
-
timerSlot[timerKey] = {
|
|
2712
|
-
timeout: setTimeout(callMethod, options.debounce),
|
|
2713
|
-
};
|
|
2714
|
-
return;
|
|
2715
|
-
}
|
|
2716
|
-
if (options.throttle) {
|
|
2717
|
-
const now = Date.now();
|
|
2718
|
-
const lastCall = timerSlot[timerKey]?.lastCall || 0;
|
|
2719
|
-
if (now - lastCall >= options.throttle) {
|
|
2720
|
-
timerSlot[timerKey] = { lastCall: now };
|
|
2721
|
-
callMethod();
|
|
2722
|
-
}
|
|
2723
|
-
return;
|
|
2724
|
-
}
|
|
2725
|
-
callMethod();
|
|
2726
|
-
};
|
|
2727
|
-
// Register with the Context using the wrapped method name
|
|
2728
|
-
if (typeof ctx[CONTEXT_REGISTER] === 'function') {
|
|
2729
|
-
ctx[CONTEXT_REGISTER](element, wrappedMethodName);
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
/**
|
|
2734
|
-
* Cleanup context handler for an element instance
|
|
2735
|
-
* Called automatically during element disconnection
|
|
2736
|
-
*/
|
|
2737
|
-
function cleanupContextHandler(element) {
|
|
2738
|
-
const handlers = element.constructor[CONTEXT_HANDLERS];
|
|
2739
|
-
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
2740
|
-
return;
|
|
2741
|
-
}
|
|
2742
|
-
// Clear any pending debounce timers (per-handler slots)
|
|
2743
|
-
const timerSlot = element[CONTEXT_TIMER];
|
|
2744
|
-
if (timerSlot && typeof timerSlot === 'object') {
|
|
2745
|
-
for (const key of Object.keys(timerSlot)) {
|
|
2746
|
-
if (timerSlot[key]?.timeout)
|
|
2747
|
-
clearTimeout(timerSlot[key].timeout);
|
|
2748
|
-
}
|
|
2749
|
-
delete element[CONTEXT_TIMER];
|
|
2750
|
-
}
|
|
2751
|
-
for (const handler of handlers) {
|
|
2752
|
-
// Clean up wrapped method
|
|
2753
|
-
const wrappedMethodName = `__wrapped_${handler.methodName}`;
|
|
2754
|
-
delete element[wrappedMethodName];
|
|
2755
|
-
}
|
|
2756
|
-
// Unregister from Context if available
|
|
2757
|
-
const ctx = element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2758
|
-
if (ctx && typeof ctx[CONTEXT_UNREGISTER] === 'function') {
|
|
2759
|
-
ctx[CONTEXT_UNREGISTER](element);
|
|
2760
|
-
}
|
|
2761
|
-
delete element[NAVIGATION_CONTEXT_INSTANCE];
|
|
2762
|
-
delete element[CONTEXT_CALLED];
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
2861
|
/**
|
|
2766
2862
|
* Template system for Snice v3.0.0
|
|
2767
2863
|
* Provides html`` and css`` tagged template processors with differential rendering
|
|
@@ -2997,14 +3093,32 @@ var Snice = (function (exports) {
|
|
|
2997
3093
|
function markPreUpgradePropertyBinding(element, propertyName) {
|
|
2998
3094
|
const tagName = element.localName;
|
|
2999
3095
|
if (!tagName?.includes('-'))
|
|
3000
|
-
return;
|
|
3096
|
+
return false;
|
|
3001
3097
|
const registry = element.ownerDocument?.defaultView?.customElements ?? globalThis.customElements;
|
|
3002
3098
|
if (registry?.get(tagName))
|
|
3003
|
-
return;
|
|
3099
|
+
return false;
|
|
3004
3100
|
const target = element;
|
|
3005
3101
|
if (!target[PRE_UPGRADE_PROPERTY_BINDINGS])
|
|
3006
3102
|
target[PRE_UPGRADE_PROPERTY_BINDINGS] = new Set();
|
|
3007
3103
|
target[PRE_UPGRADE_PROPERTY_BINDINGS].add(propertyName);
|
|
3104
|
+
return true;
|
|
3105
|
+
}
|
|
3106
|
+
function commitPropertyValue(element, propertyName, value) {
|
|
3107
|
+
if (!markPreUpgradePropertyBinding(element, propertyName)) {
|
|
3108
|
+
element[propertyName] = value;
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
// An undefined custom element can inherit a native IDL setter with the same
|
|
3112
|
+
// name. Assignment would invoke that setter (for example HTMLElement.role),
|
|
3113
|
+
// stringify structured data into an attribute, and leave no own value for
|
|
3114
|
+
// @property's upgrade adoption. Park every pre-upgrade binding as the own,
|
|
3115
|
+
// configurable value that the initializer already knows how to replay.
|
|
3116
|
+
Object.defineProperty(element, propertyName, {
|
|
3117
|
+
configurable: true,
|
|
3118
|
+
enumerable: true,
|
|
3119
|
+
writable: true,
|
|
3120
|
+
value,
|
|
3121
|
+
});
|
|
3008
3122
|
}
|
|
3009
3123
|
// noChange sentinel - preserves the currently committed value
|
|
3010
3124
|
const noChange = Symbol.for('snice:no-change');
|
|
@@ -4641,15 +4755,13 @@ var Snice = (function (exports) {
|
|
|
4641
4755
|
commitProperties(next) {
|
|
4642
4756
|
for (const key of Object.keys(this.committed)) {
|
|
4643
4757
|
if (!Object.prototype.hasOwnProperty.call(next, key)) {
|
|
4644
|
-
|
|
4645
|
-
this.element[key] = undefined;
|
|
4758
|
+
commitPropertyValue(this.element, key, undefined);
|
|
4646
4759
|
}
|
|
4647
4760
|
}
|
|
4648
4761
|
for (const [key, value] of Object.entries(next)) {
|
|
4649
4762
|
if (Object.is(this.committed[key], value))
|
|
4650
4763
|
continue;
|
|
4651
|
-
|
|
4652
|
-
this.element[key] = value === nothing ? undefined : value;
|
|
4764
|
+
commitPropertyValue(this.element, key, value === nothing ? undefined : value);
|
|
4653
4765
|
}
|
|
4654
4766
|
}
|
|
4655
4767
|
commitAttributes(next) {
|
|
@@ -4776,8 +4888,7 @@ var Snice = (function (exports) {
|
|
|
4776
4888
|
return;
|
|
4777
4889
|
}
|
|
4778
4890
|
this._committedValue = value;
|
|
4779
|
-
|
|
4780
|
-
this.element[this.name] = value === nothing ? undefined : value;
|
|
4891
|
+
commitPropertyValue(this.element, this.name, value === nothing ? undefined : value);
|
|
4781
4892
|
}
|
|
4782
4893
|
clear() {
|
|
4783
4894
|
// This part is being destroyed with its owning template. The element is
|
|
@@ -6809,7 +6920,7 @@ var Snice = (function (exports) {
|
|
|
6809
6920
|
// Re-establish handlers that get cleaned up on disconnect
|
|
6810
6921
|
setupEventHandlers(this, this);
|
|
6811
6922
|
setupResponseHandlers(this, this);
|
|
6812
|
-
setupContextHandler(this);
|
|
6923
|
+
void setupContextHandler(this, 'task');
|
|
6813
6924
|
// Re-establish observers that get cleaned up on disconnect
|
|
6814
6925
|
try {
|
|
6815
6926
|
setupObservers(this, this);
|
|
@@ -6934,7 +7045,7 @@ var Snice = (function (exports) {
|
|
|
6934
7045
|
}
|
|
6935
7046
|
setupEventHandlers(this, this);
|
|
6936
7047
|
setupResponseHandlers(this, this);
|
|
6937
|
-
setupContextHandler(this);
|
|
7048
|
+
void setupContextHandler(this, 'task');
|
|
6938
7049
|
this[INITIALIZED] = true;
|
|
6939
7050
|
// One-time (per class) sanity check: a @watch name with no matching
|
|
6940
7051
|
// @property never fires — surface the typo instead of staying silent.
|
|
@@ -8639,10 +8750,11 @@ var Snice = (function (exports) {
|
|
|
8639
8750
|
*
|
|
8640
8751
|
* Daemon instances must already exist in context.daemons. The returned cleanup
|
|
8641
8752
|
* removes the provider and deactivates framework communication for daemons no
|
|
8642
|
-
* longer provided by any context.
|
|
8753
|
+
* longer provided by any context. An optional fetch function is exposed on the
|
|
8754
|
+
* same boundary through getContextFetch() without modifying the context object.
|
|
8643
8755
|
*/
|
|
8644
|
-
function
|
|
8645
|
-
const releaseProvider = installContextProvider(root, context);
|
|
8756
|
+
function provideContextBoundary(root, context, options, navigation) {
|
|
8757
|
+
const releaseProvider = installContextProvider(root, context, options.fetch, navigation);
|
|
8646
8758
|
let releaseDaemons;
|
|
8647
8759
|
try {
|
|
8648
8760
|
releaseDaemons = bindContextDaemons(context);
|
|
@@ -8660,6 +8772,15 @@ var Snice = (function (exports) {
|
|
|
8660
8772
|
releaseDaemons?.();
|
|
8661
8773
|
};
|
|
8662
8774
|
}
|
|
8775
|
+
function provideContext(root, context, options = {}) {
|
|
8776
|
+
return provideContextBoundary(root, context, options);
|
|
8777
|
+
}
|
|
8778
|
+
/** @internal Router-owned provider including live navigation state. */
|
|
8779
|
+
function provideRouterContext(root, navigation) {
|
|
8780
|
+
return provideContextBoundary(root, navigation.application, {
|
|
8781
|
+
fetch: navigation.fetch,
|
|
8782
|
+
}, navigation);
|
|
8783
|
+
}
|
|
8663
8784
|
|
|
8664
8785
|
// Router historically allows a later Router instance to take over the same
|
|
8665
8786
|
// target. Track only Router-owned provider releases so that handoff remains
|
|
@@ -8683,6 +8804,7 @@ var Snice = (function (exports) {
|
|
|
8683
8804
|
const win = options.window ?? window;
|
|
8684
8805
|
const doc = options.document ?? document;
|
|
8685
8806
|
const routes = [];
|
|
8807
|
+
let nextRegistrationOrder = 0;
|
|
8686
8808
|
let is_sorted = false;
|
|
8687
8809
|
let placards = []; // Store collected placards
|
|
8688
8810
|
let _404; // the 404 page
|
|
@@ -8702,7 +8824,7 @@ var Snice = (function (exports) {
|
|
|
8702
8824
|
return;
|
|
8703
8825
|
releaseContextProvider?.();
|
|
8704
8826
|
routerContextProviders.get(target)?.();
|
|
8705
|
-
const releaseProvidedContext =
|
|
8827
|
+
const releaseProvidedContext = provideRouterContext(target, navigationContext);
|
|
8706
8828
|
let released = false;
|
|
8707
8829
|
const release = () => {
|
|
8708
8830
|
if (released)
|
|
@@ -8773,7 +8895,11 @@ var Snice = (function (exports) {
|
|
|
8773
8895
|
win.customElements.define(pageOptions.tag, constructor);
|
|
8774
8896
|
}
|
|
8775
8897
|
// Register the routes with guards, layout, and placard
|
|
8776
|
-
pageOptions.routes.forEach(route =>
|
|
8898
|
+
pageOptions.routes.forEach(route => {
|
|
8899
|
+
const path = typeof route === 'string' ? route : route.path;
|
|
8900
|
+
const order = typeof route === 'string' ? undefined : route.order;
|
|
8901
|
+
register(path, pageOptions.tag, pageOptions.transition, pageOptions.guards, pageOptions.layout, pageOptions.placard, order);
|
|
8902
|
+
});
|
|
8777
8903
|
return constructor;
|
|
8778
8904
|
};
|
|
8779
8905
|
}
|
|
@@ -8781,11 +8907,24 @@ var Snice = (function (exports) {
|
|
|
8781
8907
|
* Registers a new route with the router.
|
|
8782
8908
|
* @param {string} route - The route path.
|
|
8783
8909
|
* @param {string} tag - The custom element tag associated with the route.
|
|
8910
|
+
* @param {number} order - Optional lower-first tie-break after specificity.
|
|
8784
8911
|
* @example
|
|
8785
8912
|
* register('/custom-route', 'custom-element');
|
|
8786
8913
|
*/
|
|
8787
|
-
function register(route, tag, transition, guards, layout, placard) {
|
|
8788
|
-
|
|
8914
|
+
function register(route, tag, transition, guards, layout, placard, order = 0) {
|
|
8915
|
+
if (!Number.isFinite(order)) {
|
|
8916
|
+
throw new TypeError(`Route order for "${route}" must be a finite number.`);
|
|
8917
|
+
}
|
|
8918
|
+
routes.push({
|
|
8919
|
+
route: new Route(route),
|
|
8920
|
+
tag,
|
|
8921
|
+
transition,
|
|
8922
|
+
guards,
|
|
8923
|
+
layout,
|
|
8924
|
+
placard,
|
|
8925
|
+
order,
|
|
8926
|
+
registrationOrder: nextRegistrationOrder++
|
|
8927
|
+
});
|
|
8789
8928
|
is_sorted = false;
|
|
8790
8929
|
switch (route) {
|
|
8791
8930
|
case '/404':
|
|
@@ -8846,7 +8985,9 @@ var Snice = (function (exports) {
|
|
|
8846
8985
|
ensureContextProvider(target);
|
|
8847
8986
|
const needsSorting = !is_sorted;
|
|
8848
8987
|
if (needsSorting) {
|
|
8849
|
-
routes.sort((a, b) => routeSpecificity(b.route.spec) - routeSpecificity(a.route.spec)
|
|
8988
|
+
routes.sort((a, b) => routeSpecificity(b.route.spec) - routeSpecificity(a.route.spec)
|
|
8989
|
+
|| a.order - b.order
|
|
8990
|
+
|| a.registrationOrder - b.registrationOrder);
|
|
8850
8991
|
is_sorted = true;
|
|
8851
8992
|
}
|
|
8852
8993
|
// Collect placards from registered routes
|
|
@@ -9597,6 +9738,7 @@ var Snice = (function (exports) {
|
|
|
9597
9738
|
exports.escapeHtml = escapeHtml;
|
|
9598
9739
|
exports.getBodyScrollLockCount = getBodyScrollLockCount;
|
|
9599
9740
|
exports.getContext = getContext;
|
|
9741
|
+
exports.getContextFetch = getContextFetch;
|
|
9600
9742
|
exports.getController = getController;
|
|
9601
9743
|
exports.getSymbol = getSymbol;
|
|
9602
9744
|
exports.html = html;
|