mount-observer 0.1.12 → 0.1.13
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/DefineCustomElementHandler.js +99 -99
- package/ElementMountExtension.js +183 -8
- package/ElementMountExtension.ts +218 -11
- package/EnhanceMountedElementHandler.js +96 -96
- package/Events.js +18 -18
- package/Events.ts +6 -6
- package/EvtRt.js +16 -14
- package/EvtRt.ts +18 -15
- package/MountObserver.js +186 -71
- package/MountObserver.ts +207 -85
- package/README.md +1345 -151
- package/RegistryMountCoordinator.js +125 -0
- package/RegistryMountCoordinator.ts +181 -0
- package/connectionMonitor.js +1 -1
- package/connectionMonitor.ts +1 -1
- package/{getRootRegistryContainer.js → getRegistryRoot.js} +1 -1
- package/{getRootRegistryContainer.ts → getRegistryRoot.ts} +1 -1
- package/index.js +15 -10
- package/index.ts +15 -10
- package/mediaQuery.js +1 -1
- package/mediaQuery.ts +1 -1
- package/observedRootHas.js +87 -87
- package/package.json +67 -61
- package/playwright.config.ts +1 -0
- package/rootSizeObserver.js +1 -1
- package/rootSizeObserver.ts +1 -1
- package/upShadowSearch.js +64 -0
- package/upShadowSearch.ts +62 -0
- package/DefineCustomElementHandler.ts +0 -117
- package/EnhanceMountedElementHandler.ts +0 -111
package/MountObserver.ts
CHANGED
|
@@ -21,9 +21,10 @@ import {
|
|
|
21
21
|
type MutationCallback
|
|
22
22
|
} from './SharedMutationObserver.js';
|
|
23
23
|
import { withScopePerimeter } from './withScopePerimeter.js';
|
|
24
|
+
import { getRegistryRoot } from './getRegistryRoot.js';
|
|
24
25
|
import type { assignTentatively as AssignTentativelyType } from 'assign-gingerly/assignTentatively.js';
|
|
25
26
|
|
|
26
|
-
export class MountObserver extends EventTarget implements IMountObserver {
|
|
27
|
+
export class MountObserver<TKeys extends string = string> extends EventTarget implements IMountObserver {
|
|
27
28
|
// Static registry for registered handlers
|
|
28
29
|
static #handlerRegistry = new Map<string, Constructor>();
|
|
29
30
|
|
|
@@ -38,6 +39,7 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
38
39
|
#options: MountObserverOptions;
|
|
39
40
|
#abortController: AbortController;
|
|
40
41
|
#modules: any[] = [];
|
|
42
|
+
#configFromPromise: Promise<void> | undefined;
|
|
41
43
|
#mountedElements: WeakDual<Element> = {
|
|
42
44
|
weakSet: new WeakSet(),
|
|
43
45
|
setWeak: new Set()
|
|
@@ -62,6 +64,7 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
62
64
|
#assignTentatively: typeof AssignTentativelyType | undefined;
|
|
63
65
|
#elementNotifiers = new WeakMap<Element, EventTarget>();
|
|
64
66
|
#notifierMountedElements = new WeakSet<Element>();
|
|
67
|
+
#subObservers: Map<string, MountObserver> | undefined;
|
|
65
68
|
|
|
66
69
|
#mergeHandlerDefaults(config: MountConfig): MountConfig {
|
|
67
70
|
const doValue = config.do;
|
|
@@ -94,7 +97,7 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
94
97
|
return { ...handlerDefaults, ...config };
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
constructor(config: MountConfig
|
|
100
|
+
constructor(config: MountConfig<TKeys>, options: MountObserverOptions = {}) {
|
|
98
101
|
super();
|
|
99
102
|
|
|
100
103
|
// Merge handler defaults if do is a string reference
|
|
@@ -105,8 +108,8 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
105
108
|
this.#abortController = new AbortController();
|
|
106
109
|
|
|
107
110
|
const {
|
|
108
|
-
assignOnMount, assignOnDismount, stageOnMount, do: doValue,
|
|
109
|
-
import: imp
|
|
111
|
+
assignOnMount, assignOnDismount, stageOnMount, do: doValue, loadingEagerness,
|
|
112
|
+
import: imp, configFrom
|
|
110
113
|
} = mergedConfig;
|
|
111
114
|
// Make a copy of assignOnMount config using structuredClone
|
|
112
115
|
if (assignOnMount !== undefined) {
|
|
@@ -130,9 +133,9 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
130
133
|
this.#validateDoHandlers();
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
//
|
|
134
|
-
if (
|
|
135
|
-
this.#
|
|
136
|
+
// Load configFrom modules if specified
|
|
137
|
+
if (configFrom !== undefined) {
|
|
138
|
+
this.#configFromPromise = this.#loadConfigFrom();
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
// Start loading imports if eager
|
|
@@ -155,35 +158,87 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Loads configuration from external modules specified in configFrom property.
|
|
164
|
+
* Merges multiple configs left-to-right, with inline config taking final precedence.
|
|
165
|
+
*/
|
|
166
|
+
async #loadConfigFrom(): Promise<void> {
|
|
167
|
+
const { configFrom } = this.#init;
|
|
168
|
+
if (!configFrom) return;
|
|
169
|
+
|
|
170
|
+
// Normalize to array
|
|
171
|
+
const configPaths = Array.isArray(configFrom) ? configFrom : [configFrom];
|
|
172
|
+
|
|
173
|
+
// Check for duplicates
|
|
174
|
+
const pathSet = new Set<string>();
|
|
175
|
+
for (const path of configPaths) {
|
|
176
|
+
if (pathSet.has(path)) {
|
|
177
|
+
throw new Error(`Duplicate configFrom module: '${path}'`);
|
|
178
|
+
}
|
|
179
|
+
pathSet.add(path);
|
|
162
180
|
}
|
|
163
181
|
|
|
164
|
-
//
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
// Load all modules
|
|
183
|
+
const loadedConfigs: MountConfig[] = [];
|
|
184
|
+
for (const path of configPaths) {
|
|
185
|
+
try {
|
|
186
|
+
const module = await import(path);
|
|
168
187
|
|
|
169
|
-
|
|
170
|
-
|
|
188
|
+
if (!module.mountConfig) {
|
|
189
|
+
throw new Error(`Module '${path}' does not export 'mountConfig'`);
|
|
190
|
+
}
|
|
171
191
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
192
|
+
if (typeof module.mountConfig !== 'object' || module.mountConfig === null) {
|
|
193
|
+
throw new Error(`Module '${path}' exports invalid mountConfig: must be an object`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
loadedConfigs.push(module.mountConfig);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
// Re-throw with better context if it's not already our error
|
|
199
|
+
if (error instanceof Error && !error.message.includes(path)) {
|
|
200
|
+
throw new Error(`Failed to load config from '${path}': ${error.message}`);
|
|
201
|
+
}
|
|
202
|
+
throw error;
|
|
177
203
|
}
|
|
204
|
+
}
|
|
178
205
|
|
|
179
|
-
|
|
206
|
+
// Merge configs: loaded configs first (left-to-right), then inline config
|
|
207
|
+
// Save the original inline config
|
|
208
|
+
const inlineConfig = { ...this.#init };
|
|
180
209
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
210
|
+
// Start with empty object, merge all loaded configs, then merge inline
|
|
211
|
+
let mergedConfig: MountConfig = {};
|
|
212
|
+
for (const loadedConfig of loadedConfigs) {
|
|
213
|
+
mergedConfig = Object.assign(mergedConfig, loadedConfig);
|
|
185
214
|
}
|
|
215
|
+
|
|
216
|
+
// Inline config takes final precedence
|
|
217
|
+
mergedConfig = Object.assign(mergedConfig, inlineConfig);
|
|
218
|
+
|
|
219
|
+
// Update the init config with merged result
|
|
220
|
+
this.#init = mergedConfig;
|
|
186
221
|
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Creates and initializes sub-observers from the `with` property.
|
|
225
|
+
* Each sub-observer observes the same root node as the parent.
|
|
226
|
+
* Sub-observers are stored in #subObservers Map for lifecycle management.
|
|
227
|
+
*/
|
|
228
|
+
async #createSubObservers(rootNode: Node): Promise<void> {
|
|
229
|
+
const withConfig = this.#init.with;
|
|
230
|
+
if (!withConfig) return;
|
|
231
|
+
|
|
232
|
+
this.#subObservers = new Map();
|
|
233
|
+
|
|
234
|
+
for (const [key, subConfig] of Object.entries(withConfig)) {
|
|
235
|
+
const subObserver = new MountObserver(subConfig as MountConfig);
|
|
236
|
+
this.#subObservers.set(key, subObserver);
|
|
237
|
+
await subObserver.observe(rootNode);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
187
242
|
|
|
188
243
|
|
|
189
244
|
async #setupMediaQuery(): Promise<void> {
|
|
@@ -267,6 +322,17 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
267
322
|
return this.#abortController.signal;
|
|
268
323
|
}
|
|
269
324
|
|
|
325
|
+
get mountedElements(): Element[] {
|
|
326
|
+
const elements: Element[] = [];
|
|
327
|
+
for (const ref of this.#mountedElements.setWeak) {
|
|
328
|
+
const element = ref.deref();
|
|
329
|
+
if (element !== undefined) {
|
|
330
|
+
elements.push(element);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return elements;
|
|
334
|
+
}
|
|
335
|
+
|
|
270
336
|
getNotifier(element: Element): EventTarget {
|
|
271
337
|
// Return cached notifier if it exists
|
|
272
338
|
let notifier = this.#elementNotifiers.get(element);
|
|
@@ -280,10 +346,29 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
280
346
|
return notifier;
|
|
281
347
|
}
|
|
282
348
|
|
|
283
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Begins observing elements within the provided node.
|
|
351
|
+
*
|
|
352
|
+
* @param observedNode - The node to observe for matching elements. This is the root
|
|
353
|
+
* of the observation scope where the mutation observer will be
|
|
354
|
+
* registered. All matching elements within this node (and its
|
|
355
|
+
* descendants) will trigger mount callbacks.
|
|
356
|
+
*
|
|
357
|
+
* Common values:
|
|
358
|
+
* - `document` - Observe the entire document
|
|
359
|
+
* - `element` - Observe a specific subtree
|
|
360
|
+
* - `shadowRoot` - Observe within a shadow DOM
|
|
361
|
+
*/
|
|
362
|
+
async observe(observedNode: Node): Promise<void> {
|
|
284
363
|
if (this.#rootNode) {
|
|
285
364
|
throw new Error('Already observing');
|
|
286
365
|
}
|
|
366
|
+
|
|
367
|
+
// Wait for configFrom loading to complete if it was started
|
|
368
|
+
if (this.#configFromPromise) {
|
|
369
|
+
await this.#configFromPromise;
|
|
370
|
+
}
|
|
371
|
+
|
|
287
372
|
if(this.#asgMtSource || this.#asgDisMtSource){
|
|
288
373
|
await import('assign-gingerly/object-extension.js');
|
|
289
374
|
}
|
|
@@ -292,7 +377,10 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
292
377
|
this.#assignTentatively = assignTentatively;
|
|
293
378
|
}
|
|
294
379
|
|
|
295
|
-
this.#rootNode = new WeakRef(
|
|
380
|
+
this.#rootNode = new WeakRef(observedNode);
|
|
381
|
+
|
|
382
|
+
// Create sub-observers from `with` property
|
|
383
|
+
await this.#createSubObservers(observedNode);
|
|
296
384
|
|
|
297
385
|
// Set up media query if specified (needs rootNode to be set first)
|
|
298
386
|
if (this.#init.withMediaMatching) {
|
|
@@ -321,7 +409,7 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
321
409
|
|
|
322
410
|
// Process existing elements only if all conditions match
|
|
323
411
|
if (this.#mediaMatches && this.#rootSizeMatches && this.#connectionMatches) {
|
|
324
|
-
this.#processNode(
|
|
412
|
+
this.#processNode(observedNode);
|
|
325
413
|
}
|
|
326
414
|
|
|
327
415
|
// Create mutation callback
|
|
@@ -353,12 +441,21 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
353
441
|
};
|
|
354
442
|
|
|
355
443
|
// Register with shared mutation observer
|
|
356
|
-
registerSharedObserver(
|
|
444
|
+
registerSharedObserver(observedNode, this.#mutationCallback, observerConfig);
|
|
357
445
|
}
|
|
358
446
|
|
|
359
447
|
disconnect(): void {
|
|
360
448
|
const rootNode = this.#rootNode?.deref();
|
|
361
449
|
|
|
450
|
+
// Disconnect all sub-observers first (recursive)
|
|
451
|
+
if (this.#subObservers) {
|
|
452
|
+
for (const subObserver of this.#subObservers.values()) {
|
|
453
|
+
subObserver.disconnect();
|
|
454
|
+
}
|
|
455
|
+
this.#subObservers.clear();
|
|
456
|
+
this.#subObservers = undefined;
|
|
457
|
+
}
|
|
458
|
+
|
|
362
459
|
// Unregister from shared mutation observer
|
|
363
460
|
if (rootNode && this.#mutationCallback) {
|
|
364
461
|
unregisterSharedObserver(rootNode, this.#mutationCallback);
|
|
@@ -403,26 +500,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
403
500
|
this.#modules = await loadImports(this.#init.import);
|
|
404
501
|
this.#importsLoaded = true;
|
|
405
502
|
|
|
406
|
-
// Validate referenced whereInstanceOf if reference is specified
|
|
407
|
-
if (this.#init.reference !== undefined) {
|
|
408
|
-
const references = arr(this.#init.reference);
|
|
409
|
-
|
|
410
|
-
for (const index of references) {
|
|
411
|
-
const module = this.#modules[index];
|
|
412
|
-
if (module && module.whereInstanceOf !== undefined) {
|
|
413
|
-
// Validate that it's a Constructor or array of Constructors
|
|
414
|
-
const whereInstanceOf = module.whereInstanceOf;
|
|
415
|
-
const constructors = arr(whereInstanceOf);
|
|
416
|
-
|
|
417
|
-
for (const constructor of constructors) {
|
|
418
|
-
if (typeof constructor !== 'function') {
|
|
419
|
-
throw new Error(`Referenced module at index ${index} exports invalid whereInstanceOf: must be a Constructor or array of Constructors`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
503
|
this.dispatchEvent(new LoadEvent(this.#modules, this.#init));
|
|
427
504
|
}
|
|
428
505
|
|
|
@@ -445,7 +522,8 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
445
522
|
const root = node as DocumentFragment;
|
|
446
523
|
|
|
447
524
|
// Get all elements matching the CSS selector first
|
|
448
|
-
root.querySelectorAll(this.#init.matching)
|
|
525
|
+
const matches = root.querySelectorAll(this.#init.matching);
|
|
526
|
+
matches.forEach(child => {
|
|
449
527
|
// If intersection observer is active, start observing the element
|
|
450
528
|
if (this.#intersectionObserver) {
|
|
451
529
|
this.#intersectionObserver.observe(child);
|
|
@@ -468,9 +546,22 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
468
546
|
return false;
|
|
469
547
|
}
|
|
470
548
|
|
|
549
|
+
// Check that element's customElementRegistry matches root node's registry
|
|
550
|
+
const rootNode = this.#rootNode?.deref();
|
|
551
|
+
if (rootNode) {
|
|
552
|
+
const registriesMatch = (rootNode as any).customElementRegistry === (element as any).customElementRegistry;
|
|
553
|
+
|
|
554
|
+
// If whereDifferentCustomElementRegistry is true, exclude matching registries
|
|
555
|
+
if (this.#init.whereDifferentCustomElementRegistry) {
|
|
556
|
+
if (registriesMatch) return false;
|
|
557
|
+
} else {
|
|
558
|
+
// Default behavior: exclude non-matching registries
|
|
559
|
+
if (!registriesMatch) return false;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
471
563
|
// Check withScopePerimeter condition if specified (donut hole scoping)
|
|
472
564
|
if (this.#init.withScopePerimeter) {
|
|
473
|
-
const rootNode = this.#rootNode?.deref();
|
|
474
565
|
if (!rootNode || !withScopePerimeter(rootNode, element, this.#init.withScopePerimeter)) {
|
|
475
566
|
return false;
|
|
476
567
|
}
|
|
@@ -493,22 +584,14 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
493
584
|
}
|
|
494
585
|
}
|
|
495
586
|
|
|
496
|
-
// Check
|
|
497
|
-
if (this.#
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
// Element must be an instance of at least one constructor (OR logic within this module)
|
|
506
|
-
const matchesInstanceOf = constructors.some((constructor: Constructor) => element instanceof constructor);
|
|
507
|
-
|
|
508
|
-
if (!matchesInstanceOf) {
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
587
|
+
// Check whereLocalNameMatches condition if specified
|
|
588
|
+
if (this.#init.whereLocalNameMatches) {
|
|
589
|
+
const pattern = typeof this.#init.whereLocalNameMatches === 'string'
|
|
590
|
+
? new RegExp(this.#init.whereLocalNameMatches)
|
|
591
|
+
: this.#init.whereLocalNameMatches;
|
|
592
|
+
|
|
593
|
+
if (!pattern.test(element.localName)) {
|
|
594
|
+
return false;
|
|
512
595
|
}
|
|
513
596
|
}
|
|
514
597
|
|
|
@@ -541,12 +624,55 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
541
624
|
return;
|
|
542
625
|
}
|
|
543
626
|
|
|
544
|
-
const context: MountContext = {
|
|
627
|
+
const context: MountContext<TKeys> = {
|
|
545
628
|
modules: this.#modules,
|
|
546
629
|
observer: this,
|
|
547
630
|
rootNode,
|
|
548
|
-
|
|
631
|
+
mountConfig: this.#init,
|
|
549
632
|
};
|
|
633
|
+
|
|
634
|
+
// Add withObservers if sub-observers exist
|
|
635
|
+
if (this.#subObservers && this.#subObservers.size > 0) {
|
|
636
|
+
context.withObservers = {} as {[K in TKeys]: IMountObserver};
|
|
637
|
+
for (const [key, subObserver] of this.#subObservers.entries()) {
|
|
638
|
+
(context.withObservers as any)[key] = subObserver;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Check shouldMount condition if specified (final gate before mounting)
|
|
643
|
+
if (this.#init.shouldMount) {
|
|
644
|
+
try {
|
|
645
|
+
const shouldMount = this.#init.shouldMount(element, context);
|
|
646
|
+
if (!shouldMount) {
|
|
647
|
+
// shouldMount returned false - don't mount this element
|
|
648
|
+
// Remove from processed set so it can be re-evaluated later
|
|
649
|
+
this.#processedDoForElement.delete(element);
|
|
650
|
+
// Remove from mounted elements tracking
|
|
651
|
+
this.#mountedElements.weakSet.delete(element);
|
|
652
|
+
for (const ref of this.#mountedElements.setWeak) {
|
|
653
|
+
if (ref.deref() === element) {
|
|
654
|
+
this.#mountedElements.setWeak.delete(ref);
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
} catch (error) {
|
|
661
|
+
// shouldMount threw an error - treat as false and log
|
|
662
|
+
console.error('shouldMount check failed:', error);
|
|
663
|
+
// Remove from processed set so it can be re-evaluated later
|
|
664
|
+
this.#processedDoForElement.delete(element);
|
|
665
|
+
// Remove from mounted elements tracking
|
|
666
|
+
this.#mountedElements.weakSet.delete(element);
|
|
667
|
+
for (const ref of this.#mountedElements.setWeak) {
|
|
668
|
+
if (ref.deref() === element) {
|
|
669
|
+
this.#mountedElements.setWeak.delete(ref);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
550
676
|
|
|
551
677
|
// Apply assignGingerly if specified
|
|
552
678
|
if (this.#asgMtSource) {
|
|
@@ -581,18 +707,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
581
707
|
}
|
|
582
708
|
}
|
|
583
709
|
|
|
584
|
-
// Call referenced do functions from imported modules
|
|
585
|
-
if (this.#init.reference !== undefined) {
|
|
586
|
-
const references = arr(this.#init.reference);
|
|
587
|
-
|
|
588
|
-
for (const index of references) {
|
|
589
|
-
const module = this.#modules[index];
|
|
590
|
-
if (module && typeof module.do === 'function') {
|
|
591
|
-
module.do(element, context);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
710
|
// Dispatch mount event
|
|
597
711
|
const mountEvent = new MountEvent(element, this.#modules, this.#init, context);
|
|
598
712
|
this.dispatchEvent(mountEvent);
|
|
@@ -683,12 +797,20 @@ export class MountObserver extends EventTarget implements IMountObserver {
|
|
|
683
797
|
return;
|
|
684
798
|
}
|
|
685
799
|
|
|
686
|
-
const context: MountContext = {
|
|
800
|
+
const context: MountContext<TKeys> = {
|
|
687
801
|
modules: this.#modules,
|
|
688
802
|
observer: this,
|
|
689
803
|
rootNode,
|
|
690
|
-
|
|
804
|
+
mountConfig: this.#init,
|
|
691
805
|
};
|
|
806
|
+
|
|
807
|
+
// Add withObservers if sub-observers exist
|
|
808
|
+
if (this.#subObservers && this.#subObservers.size > 0) {
|
|
809
|
+
context.withObservers = {} as {[K in TKeys]: IMountObserver};
|
|
810
|
+
for (const [key, subObserver] of this.#subObservers.entries()) {
|
|
811
|
+
(context.withObservers as any)[key] = subObserver;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
692
814
|
|
|
693
815
|
|
|
694
816
|
// Dispatch dismount event
|