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