mount-observer 0.1.36 → 0.1.38

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.
@@ -1,73 +1,73 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupElementIntersection = setupElementIntersection;
4
- exports.isElementIntersecting = isElementIntersecting;
5
- var Events_js_1 = require("./Events.js");
6
- function setupElementIntersection(init, rootNodeRef, mountedElements, modules, observer, matchesSelector, handleMatch) {
7
- var whereElementIntersectsWith = init.whereElementIntersectsWith;
8
- if (!whereElementIntersectsWith) {
9
- throw new Error('whereElementIntersectsWith is required');
10
- }
11
- // Track which elements are currently intersecting
12
- var intersectingElements = new WeakSet();
13
- // Create IntersectionObserver with the provided options
14
- var intersectionObserver = new IntersectionObserver(function (entries) {
15
- for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
16
- var entry = entries_1[_i];
17
- var element = entry.target;
18
- if (entry.isIntersecting) {
19
- // Element is now intersecting
20
- intersectingElements.add(element);
21
- // Check if element matches all other conditions and mount if so
22
- if (matchesSelector(element)) {
23
- handleMatch(element);
24
- }
25
- }
26
- else {
27
- // Element is no longer intersecting
28
- intersectingElements.delete(element);
29
- // Dismount if it was mounted
30
- if (mountedElements.weakSet.has(element)) {
31
- dismountElement(element);
32
- }
33
- }
34
- }
35
- }, whereElementIntersectsWith);
36
- function dismountElement(element) {
37
- // Remove from mounted elements
38
- mountedElements.weakSet.delete(element);
39
- for (var _i = 0, _a = mountedElements.setWeak; _i < _a.length; _i++) {
40
- var ref = _a[_i];
41
- if (ref.deref() === element) {
42
- mountedElements.setWeak.delete(ref);
43
- break;
44
- }
45
- }
46
- // Dispatch dismount event
47
- observer.dispatchEvent(new Events_js_1.DismountEvent(element, 'intersection-failed', init));
48
- }
49
- function observeElement(element) {
50
- intersectionObserver.observe(element);
51
- }
52
- return {
53
- intersectionObserver: intersectionObserver,
54
- observeElement: observeElement,
55
- cleanup: function () {
56
- intersectionObserver.disconnect();
57
- }
58
- };
59
- }
60
- /**
61
- * Check if an element is currently intersecting
62
- * This is called from #matchesSelector to determine if intersection condition is met
63
- */
64
- function isElementIntersecting(element, intersectionObserver) {
65
- // If no intersection observer is set up, consider all elements as intersecting
66
- if (!intersectionObserver) {
67
- return true;
68
- }
69
- // When intersection observer is active, we can't synchronously determine intersection state
70
- // The element will be observed and the callback will handle mounting when it intersects
71
- // Return false here to prevent immediate mounting
72
- return false;
73
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupElementIntersection = setupElementIntersection;
4
+ exports.isElementIntersecting = isElementIntersecting;
5
+ var Events_js_1 = require("./Events.js");
6
+ function setupElementIntersection(init, rootNodeRef, mountedElements, modules, observer, matchesSelector, handleMatch) {
7
+ var whereElementIntersectsWith = init.whereElementIntersectsWith;
8
+ if (!whereElementIntersectsWith) {
9
+ throw new Error('whereElementIntersectsWith is required');
10
+ }
11
+ // Track which elements are currently intersecting
12
+ var intersectingElements = new WeakSet();
13
+ // Create IntersectionObserver with the provided options
14
+ var intersectionObserver = new IntersectionObserver(function (entries) {
15
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
16
+ var entry = entries_1[_i];
17
+ var element = entry.target;
18
+ if (entry.isIntersecting) {
19
+ // Element is now intersecting
20
+ intersectingElements.add(element);
21
+ // Check if element matches all other conditions and mount if so
22
+ if (matchesSelector(element)) {
23
+ handleMatch(element);
24
+ }
25
+ }
26
+ else {
27
+ // Element is no longer intersecting
28
+ intersectingElements.delete(element);
29
+ // Dismount if it was mounted
30
+ if (mountedElements.weakSet.has(element)) {
31
+ dismountElement(element);
32
+ }
33
+ }
34
+ }
35
+ }, whereElementIntersectsWith);
36
+ function dismountElement(element) {
37
+ // Remove from mounted elements
38
+ mountedElements.weakSet.delete(element);
39
+ for (var _i = 0, _a = mountedElements.setWeak; _i < _a.length; _i++) {
40
+ var ref = _a[_i];
41
+ if (ref.deref() === element) {
42
+ mountedElements.setWeak.delete(ref);
43
+ break;
44
+ }
45
+ }
46
+ // Dispatch dismount event
47
+ observer.dispatchEvent(new Events_js_1.DismountEvent(element, 'intersection-failed', init));
48
+ }
49
+ function observeElement(element) {
50
+ intersectionObserver.observe(element);
51
+ }
52
+ return {
53
+ intersectionObserver: intersectionObserver,
54
+ observeElement: observeElement,
55
+ cleanup: function () {
56
+ intersectionObserver.disconnect();
57
+ }
58
+ };
59
+ }
60
+ /**
61
+ * Check if an element is currently intersecting
62
+ * This is called from #matchesSelector to determine if intersection condition is met
63
+ */
64
+ function isElementIntersecting(element, intersectionObserver) {
65
+ // If no intersection observer is set up, consider all elements as intersecting
66
+ if (!intersectionObserver) {
67
+ return true;
68
+ }
69
+ // When intersection observer is active, we can't synchronously determine intersection state
70
+ // The element will be observed and the callback will handle mounting when it intersects
71
+ // Return false here to prevent immediate mounting
72
+ return false;
73
+ }
package/index.js CHANGED
@@ -12,6 +12,7 @@ export { MountObserverScriptHandler } from './handlers/MountObserverScript.js';
12
12
  export { EMCScriptHandler } from './handlers/EMCScript.js';
13
13
  export { HoistTemplateHandler } from './handlers/HoistTemplate.js';
14
14
  export { HTMLIncludeHandler } from './handlers/HTMLInclude.js';
15
+ export { CedeScriptHandler } from './handlers/CedeScript.js';
15
16
  export { upShadowSearch } from './upShadowSearch.js';
16
17
  export { mountEventName, dismountEventName, disconnectEventName, loadEventName, mediamatchEventName, mediaunmatchEventName, addedScriptElementEventName } from './Events.js';
17
18
  // Register built-in handlers
@@ -24,3 +25,4 @@ import './handlers/MountObserverScript.js';
24
25
  import './handlers/EMCScript.js';
25
26
  import './handlers/HoistTemplate.js';
26
27
  import './handlers/HTMLInclude.js';
28
+ import './handlers/CedeScript.js';
package/index.ts CHANGED
@@ -12,6 +12,7 @@ export { MountObserverScriptHandler } from './handlers/MountObserverScript.js';
12
12
  export { EMCScriptHandler } from './handlers/EMCScript.js';
13
13
  export { HoistTemplateHandler } from './handlers/HoistTemplate.js';
14
14
  export { HTMLIncludeHandler } from './handlers/HTMLInclude.js';
15
+ export { CedeScriptHandler } from './handlers/CedeScript.js';
15
16
  export { upShadowSearch } from './upShadowSearch.js';
16
17
  export type {
17
18
  MountConfig,
@@ -44,3 +45,4 @@ import './handlers/MountObserverScript.js';
44
45
  import './handlers/EMCScript.js';
45
46
  import './handlers/HoistTemplate.js';
46
47
  import './handlers/HTMLInclude.js';
48
+ import './handlers/CedeScript.js';
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
7
7
  "dependencies": {
8
- "assign-gingerly": "0.0.38",
8
+ "assign-gingerly": "0.0.51",
9
9
  "id-generation": "0.0.4"
10
10
  },
11
11
  "devDependencies": {
12
- "@playwright/test": "1.59.1",
12
+ "@playwright/test": "1.60.0",
13
13
  "spa-ssi": "0.0.27"
14
14
  },
15
15
  "exports": {
@@ -179,13 +179,18 @@ export interface AttrConfig<T = unknown, TParserConfig = unknown> {
179
179
  // * Defaults to true (initial read only)
180
180
  // */
181
181
  // initialOnly?: boolean;
182
+
183
+ /**
184
+ * Should make sure it is added to static observedAttribrutes
185
+ */
186
+ sourceOfTruth?: boolean;
182
187
  }
183
188
 
184
189
  export type AttrPatterns<T = any> = {
185
190
  /**
186
191
  * Base prefix for attribute names
187
192
  */
188
- base: string;
193
+ base?: string;
189
194
 
190
195
  /**
191
196
  * Configuration for the base pattern
@@ -321,3 +326,151 @@ export interface ElementEnhancement{
321
326
  dispose(registryItem: EnhancementConfig | string | symbol): void;
322
327
  whenResolved(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): Promise<any>;
323
328
  }
329
+
330
+ // =============================================================================
331
+ // Custom Element Features types
332
+ // =============================================================================
333
+
334
+ /**
335
+ * Context passed to feature spawn constructors
336
+ */
337
+ export interface FeatureSpawnContext {
338
+ /** The feature key (e.g., 'photoTaker') */
339
+ key: string;
340
+ /** The SupportedFeatureConfig from static supportedFeatures */
341
+ optIn: SupportedFeatureConfig;
342
+ /** The FeatureConfig from assignFeatures */
343
+ injection: FeatureConfig;
344
+ /** The features registry reference */
345
+ featuresRegistry: FeaturesRegistry;
346
+ /** Shared context from the host element (via getSharedContext callback) */
347
+ shared?: any;
348
+ }
349
+
350
+ /**
351
+ * Configuration for a supported feature slot declared via static supportedFeatures
352
+ */
353
+ export interface SupportedFeatureConfig {
354
+ /**
355
+ * Optional fallback class (or async spawner) to use if no implementation is injected.
356
+ */
357
+ fallbackSpawn?:
358
+ | { new(hostElement: any, ctx: FeatureSpawnContext, initVals?: any): any }
359
+ | (() => Promise<{ new(hostElement: any, ctx: FeatureSpawnContext, initVals?: any): any }>);
360
+
361
+ /**
362
+ * Optional runtime shape validation for the spawned instance.
363
+ * Return true if the instance is valid, false to throw.
364
+ */
365
+ validateShape?: (spawnedInstance: any) => boolean;
366
+
367
+ /**
368
+ * Optional callback to provide shared context (e.g., ElementInternals, private state)
369
+ * to the feature at construction time.
370
+ */
371
+ getSharedContext?: (instance: any) => any;
372
+
373
+ /**
374
+ * Lifecycle callbacks that this feature requires.
375
+ * Serves as the default — the consumer can add more via FeatureConfig.callbackForwarding.
376
+ */
377
+ callbackForwarding?: string[];
378
+ }
379
+
380
+ /**
381
+ * Class-level configuration for the features system.
382
+ * Declared as `static featuresConfig` on the class.
383
+ */
384
+ export interface FeaturesClassConfig {
385
+ /**
386
+ * Lifecycle method configuration.
387
+ * true = install 'whenFeatureReady' method.
388
+ * Object = custom method name.
389
+ */
390
+ lifecycleKeys?: true | {
391
+ whenFeatureReady?: string;
392
+ };
393
+ }
394
+
395
+ /**
396
+ * Configuration for a feature passed to assignFeatures.
397
+ */
398
+ export interface FeatureConfig {
399
+ /**
400
+ * The class to instantiate, or an async function returning one.
401
+ */
402
+ spawn?:
403
+ | { new(hostElement: any, ctx: FeatureSpawnContext, initVals?: any): any }
404
+ | (() => Promise<{ new(hostElement: any, ctx: FeatureSpawnContext, initVals?: any): any }>);
405
+
406
+ /** Attribute patterns for parsing element attributes into initVals. */
407
+ withAttrs?: AttrPatterns<any>;
408
+
409
+ /** Pass-through custom configuration data (accessible via ctx.injection.customData). */
410
+ customData?: any;
411
+
412
+ /** Lifecycle callbacks to forward to this feature. */
413
+ callbackForwarding?: string[];
414
+ }
415
+
416
+ export type SupportedFeaturesMap = Record<string, SupportedFeatureConfig>;
417
+ export type FeatureConfigsMap = Record<string, FeatureConfig>;
418
+
419
+ /**
420
+ * Registry for feature configs, keyed by constructor.
421
+ */
422
+ export declare class FeaturesRegistry {
423
+ has(ctr: Function): boolean;
424
+ get(ctr: Function): Map<string, FeatureConfig> | undefined;
425
+ set(ctr: Function, key: string, config: FeatureConfig): void;
426
+ hasKey(ctr: Function, key: string): boolean;
427
+ }
428
+
429
+ /**
430
+ * A suggestion from one feature to another.
431
+ */
432
+ export interface FeatureInfoSuggestion {
433
+ from: Function;
434
+ withAttrs?: any;
435
+ customData?: any;
436
+ }
437
+
438
+ /**
439
+ * Core assignFeatures function.
440
+ */
441
+ export declare function assignFeatures(
442
+ ctr: Function,
443
+ features: FeatureConfigsMap,
444
+ featuresRegistry: FeaturesRegistry
445
+ ): Promise<void> | undefined;
446
+
447
+ /**
448
+ * Captures own-properties that shadow feature getters.
449
+ */
450
+ export declare function captureFeatureInitVals(instance: any): void;
451
+
452
+ /**
453
+ * Suggest configuration to another feature during registration.
454
+ */
455
+ export declare function suggestFeatureInfo(
456
+ fromFeatureCtr: Function,
457
+ toFeatureSymbol: symbol,
458
+ featureInfo: { withAttrs?: any; customData?: any },
459
+ targetClass: Function
460
+ ): void;
461
+
462
+ /**
463
+ * Retrieve suggestions made to a feature by other features.
464
+ */
465
+ export declare function getFeatureInfoSuggestions(
466
+ toFeatureSymbol: symbol,
467
+ targetClass: Function
468
+ ): FeatureInfoSuggestion[];
469
+
470
+ /**
471
+ * Base class for nested feature containers.
472
+ */
473
+ export declare class PropertyBag {
474
+ customElementRegistry: any;
475
+ constructor(hostElement: any, ctx?: FeatureSpawnContext, initVals?: any);
476
+ }
@@ -0,0 +1,80 @@
1
+ import { SpawnContext } from "../assign-gingerly/types";
2
+
3
+ /**
4
+ * Shared context passed via getSharedContext — contains everything
5
+ * the Reflector needs to self-activate on spawn.
6
+ */
7
+ export interface ReflectorSharedContext {
8
+ /** The ElementInternals instance for custom state manipulation */
9
+ internals: ElementInternals;
10
+ /** The EventTarget the host dispatches property change events on */
11
+ hostPropagator: EventTarget;
12
+ }
13
+
14
+ /**
15
+ * Context passed to the Reflector feature constructor
16
+ */
17
+ export interface FeatureSpawnContext extends SpawnContext {
18
+ key: string;
19
+ optIn: any;
20
+ injection: any;
21
+ featuresRegistry: any;
22
+ shared?: ReflectorSharedContext;
23
+ }
24
+
25
+ /**
26
+ * A single custom state export rule parsed from --custom-state-exports
27
+ */
28
+ export interface CustomStateRule {
29
+ /** The custom state name to toggle (e.g. "disabled", "alertTypeIndicatesSuccess") */
30
+ stateName: string;
31
+ /** The property name to observe */
32
+ propName: string;
33
+ /** The condition type */
34
+ conditionType: 'boolean' | 'equals' | 'contains' | 'endsWith' | 'modulo' | 'lessThan' | 'greaterThan' | 'lessThanOrEqual' | 'greaterThanOrEqual';
35
+ /** The comparison value (for non-boolean conditions) */
36
+ compareValue?: string | number;
37
+ /** The modulo divisor (for modulo conditions) */
38
+ moduloDivisor?: number;
39
+ }
40
+
41
+ /**
42
+ * Properties that the Reflector feature exposes.
43
+ * With callbackForwarding, hostPropagator is provided via getSharedContext
44
+ * and the feature self-activates — but the setter remains for manual use cases.
45
+ */
46
+ export interface ReflectorProps {
47
+ /**
48
+ * The EventTarget that the host element uses to propagate property change events.
49
+ * Provided via getSharedContext. Also settable post-spawn for reconnection.
50
+ */
51
+ hostPropagator: EventTarget | null;
52
+ }
53
+
54
+ /**
55
+ * Internal state
56
+ */
57
+ export interface AllProps extends ReflectorProps {
58
+ /**
59
+ * WeakRef to the host custom element
60
+ */
61
+ hostRef: WeakRef<HTMLElement> | null;
62
+
63
+ /**
64
+ * The ElementInternals instance from the host (for custom state manipulation)
65
+ */
66
+ internals: ElementInternals | null;
67
+
68
+ /**
69
+ * Parsed custom state rules from --custom-state-exports CSS property
70
+ */
71
+ rules: CustomStateRule[];
72
+
73
+ /**
74
+ * AbortController for cleaning up event listeners
75
+ */
76
+ abortController: AbortController | null;
77
+ }
78
+
79
+ export type AP = AllProps;
80
+ export type PAP = Partial<AP>;
@@ -1,4 +1,4 @@
1
- import { ElementEnhancementGateway } from "../assign-gingerly/types";
1
+ import { ElementEnhancementGateway, SpawnContext } from "../assign-gingerly/types";
2
2
  import { StatementsResult } from "../nested-regex-groups/types";
3
3
 
4
4
  export interface EndUserProps{
@@ -19,7 +19,7 @@ export type ProPAP = Promise<PAP>
19
19
  export interface Actions{
20
20
  hydrate(self: AP & Actions): ProPAP;
21
21
  handleEvent(self: AP, event: Event, incParameters: IncParameters): void;
22
- init(self: AP, enhancedElement: Element, initVals: PAP): Promise<void>
22
+ init(self: AP, enhancedElement: Element, ctx: SpawnContext, initVals: PAP): Promise<void>;
23
23
  }
24
24
 
25
25
  export type asOptions =
@@ -0,0 +1,28 @@
1
+ import { ElementEnhancementGateway, SpawnContext, IAssignGingerlyOptions } from "../assign-gingerly/types";
2
+
3
+ export interface MergeParameters {
4
+ assign: Record<string, any>;
5
+ on?: string;
6
+ options?: IAssignGingerlyOptions;
7
+ targetElementId?: string;
8
+ }
9
+
10
+ export interface EndUserProps {
11
+ mergeParamSets: MergeParameters | MergeParameters[];
12
+ }
13
+
14
+ export interface AllProps extends EndUserProps {
15
+ enhancedElement: Element & ElementEnhancementGateway;
16
+ resolved: boolean;
17
+ }
18
+
19
+ export type AP = AllProps;
20
+
21
+ export type PAP = Partial<AP>;
22
+
23
+ export type ProPAP = Promise<PAP>;
24
+
25
+ export interface Actions {
26
+ init(self: AP, enhancedElement: Element, ctx: SpawnContext, initVals: PAP): Promise<void>;
27
+ hydrate(self: AP): ProPAP;
28
+ }
@@ -1,4 +1,4 @@
1
- import { ElementEnhancementGateway } from "../assign-gingerly/types";
1
+ import { ElementEnhancementGateway, SpawnContext } from "../assign-gingerly/types";
2
2
  import { StatementsResult } from "../nested-regex-groups/types";
3
3
 
4
4
  export interface EndUserProps{}
@@ -16,7 +16,7 @@ export type PAP = Partial<AP>;
16
16
  export type ProPAP = Promise<PAP>
17
17
 
18
18
  export interface Actions{
19
- init(self: AllProps, enhancedElement: Element, initVals: PAP): void;
19
+ init(self: AllProps, enhancedElement: Element, ctx: SpawnContext, initVals: PAP): Promise<void>;
20
20
  hydrate(self: AP): ProPAP;
21
21
  handleEvent(self: AP, e: Event, parsedStatement: TogglingParameters): void;
22
22
  }
@@ -24,4 +24,8 @@ export interface Actions{
24
24
  export interface TogglingParameters {
25
25
  prop?: string | null;
26
26
  localEventType?: string;
27
+ targetSpecifier: {
28
+ prop?: string;
29
+ targetElementId?: string;
30
+ };
27
31
  }
@@ -0,0 +1,102 @@
1
+ import { SpawnContext } from "../assign-gingerly/types";
2
+
3
+ /**
4
+ * Shared context passed via getSharedContext — contains the
5
+ * ElementInternals instance needed for form control APIs.
6
+ */
7
+ export interface FaceUpSharedContext {
8
+ /** The ElementInternals instance for form control APIs */
9
+ internals: ElementInternals;
10
+ }
11
+
12
+ /**
13
+ * Context passed to the FaceUp feature constructor
14
+ */
15
+ export interface FeatureSpawnContext extends SpawnContext {
16
+ key: string;
17
+ optIn: any;
18
+ injection: any;
19
+ featuresRegistry: any;
20
+ shared?: FaceUpSharedContext;
21
+ }
22
+
23
+ /**
24
+ * Validation flags matching the ValidityStateFlags interface.
25
+ * Used with setValidity() to indicate the type of validation error.
26
+ */
27
+ export interface ValidationFlags {
28
+ valueMissing?: boolean;
29
+ typeMismatch?: boolean;
30
+ patternMismatch?: boolean;
31
+ tooLong?: boolean;
32
+ tooShort?: boolean;
33
+ rangeUnderflow?: boolean;
34
+ rangeOverflow?: boolean;
35
+ stepMismatch?: boolean;
36
+ badInput?: boolean;
37
+ customError?: boolean;
38
+ }
39
+
40
+ /**
41
+ * Properties that the FaceUp feature exposes.
42
+ * These allow the host custom element to participate in HTML forms
43
+ * via the ElementInternals API.
44
+ *
45
+ * Because the feature is installed as a getter-only property,
46
+ * assignGingerly merges directly into the instance. The consumer
47
+ * simply sets properties (e.g., el.faceUp.value = x) and the
48
+ * setters handle syncing to ElementInternals automatically.
49
+ */
50
+ export interface FaceUpProps {
51
+ /**
52
+ * The current form value of the control.
53
+ * Setting this calls setFormValue() on the internals.
54
+ */
55
+ value: string | File | FormData | null;
56
+
57
+ /**
58
+ * Internal state for form restoration (optional).
59
+ * If provided, passed as the second argument to setFormValue().
60
+ */
61
+ state: string | File | FormData | null;
62
+
63
+ /**
64
+ * Whether the control is currently disabled.
65
+ */
66
+ disabled: boolean;
67
+
68
+ /**
69
+ * Whether the control requires a value for form submission.
70
+ */
71
+ required: boolean;
72
+
73
+ /**
74
+ * Custom validation message. Setting a non-empty string
75
+ * marks the control as invalid with customError.
76
+ */
77
+ validationMessage: string;
78
+ }
79
+
80
+ /**
81
+ * Internal state
82
+ */
83
+ export interface AllProps extends FaceUpProps {
84
+ /**
85
+ * WeakRef to the host custom element
86
+ */
87
+ hostRef: WeakRef<HTMLElement> | null;
88
+
89
+ /**
90
+ * The ElementInternals instance from the host (for form control APIs)
91
+ */
92
+ internals: ElementInternals | null;
93
+ }
94
+
95
+ export type AP = AllProps;
96
+ export type PAP = Partial<AP>;
97
+
98
+
99
+
100
+ export interface CustomData {
101
+ integrateWithRoundabout: boolean;
102
+ }