mount-observer 0.1.11 → 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 -98
- package/ElementMountExtension.js +183 -8
- package/ElementMountExtension.ts +218 -11
- package/EnhanceMountedElementHandler.js +96 -95
- package/Events.js +18 -18
- package/Events.ts +6 -6
- package/EvtRt.js +24 -17
- package/EvtRt.ts +30 -18
- package/MountObserver.js +296 -81
- package/MountObserver.ts +387 -121
- package/README.md +1508 -235
- package/RegistryMountCoordinator.js +125 -0
- package/RegistryMountCoordinator.ts +181 -0
- package/connectionMonitor.js +116 -0
- package/connectionMonitor.ts +164 -0
- package/elementIntersection.js +67 -0
- package/elementIntersection.ts +96 -0
- 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 -0
- package/package.json +67 -61
- package/playwright.config.ts +1 -0
- package/rootSizeObserver.js +124 -0
- package/rootSizeObserver.ts +157 -0
- package/upShadowSearch.js +64 -0
- package/upShadowSearch.ts +62 -0
- package/DefineCustomElementHandler.ts +0 -116
- package/EnhanceMountedElementHandler.ts +0 -110
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Element intersection observation for MountObserver
|
|
2
|
+
import type { MountConfig, WeakDual } from './types/mount-observer/types.js';
|
|
3
|
+
import { DismountEvent } from './Events.js';
|
|
4
|
+
|
|
5
|
+
export function setupElementIntersection(
|
|
6
|
+
init: MountConfig,
|
|
7
|
+
rootNodeRef: WeakRef<Node>,
|
|
8
|
+
mountedElements: WeakDual<Element>,
|
|
9
|
+
modules: any[],
|
|
10
|
+
observer: EventTarget,
|
|
11
|
+
matchesSelector: (element: Element) => boolean,
|
|
12
|
+
handleMatch: (element: Element) => void
|
|
13
|
+
): {
|
|
14
|
+
intersectionObserver: IntersectionObserver;
|
|
15
|
+
observeElement: (element: Element) => void;
|
|
16
|
+
cleanup: () => void;
|
|
17
|
+
} {
|
|
18
|
+
const { whereElementIntersectsWith } = init;
|
|
19
|
+
|
|
20
|
+
if (!whereElementIntersectsWith) {
|
|
21
|
+
throw new Error('whereElementIntersectsWith is required');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Track which elements are currently intersecting
|
|
25
|
+
const intersectingElements = new WeakSet<Element>();
|
|
26
|
+
|
|
27
|
+
// Create IntersectionObserver with the provided options
|
|
28
|
+
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const element = entry.target as Element;
|
|
31
|
+
|
|
32
|
+
if (entry.isIntersecting) {
|
|
33
|
+
// Element is now intersecting
|
|
34
|
+
intersectingElements.add(element);
|
|
35
|
+
|
|
36
|
+
// Check if element matches all other conditions and mount if so
|
|
37
|
+
if (matchesSelector(element)) {
|
|
38
|
+
handleMatch(element);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
// Element is no longer intersecting
|
|
42
|
+
intersectingElements.delete(element);
|
|
43
|
+
|
|
44
|
+
// Dismount if it was mounted
|
|
45
|
+
if (mountedElements.weakSet.has(element)) {
|
|
46
|
+
dismountElement(element);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}, whereElementIntersectsWith);
|
|
51
|
+
|
|
52
|
+
function dismountElement(element: Element): void {
|
|
53
|
+
// Remove from mounted elements
|
|
54
|
+
mountedElements.weakSet.delete(element);
|
|
55
|
+
for (const ref of mountedElements.setWeak) {
|
|
56
|
+
if (ref.deref() === element) {
|
|
57
|
+
mountedElements.setWeak.delete(ref);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Dispatch dismount event
|
|
63
|
+
observer.dispatchEvent(new DismountEvent(element, 'intersection-failed', init));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function observeElement(element: Element): void {
|
|
67
|
+
intersectionObserver.observe(element);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
intersectionObserver,
|
|
72
|
+
observeElement,
|
|
73
|
+
cleanup: () => {
|
|
74
|
+
intersectionObserver.disconnect();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if an element is currently intersecting
|
|
81
|
+
* This is called from #matchesSelector to determine if intersection condition is met
|
|
82
|
+
*/
|
|
83
|
+
export function isElementIntersecting(
|
|
84
|
+
element: Element,
|
|
85
|
+
intersectionObserver: IntersectionObserver | undefined
|
|
86
|
+
): boolean {
|
|
87
|
+
// If no intersection observer is set up, consider all elements as intersecting
|
|
88
|
+
if (!intersectionObserver) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// When intersection observer is active, we can't synchronously determine intersection state
|
|
93
|
+
// The element will be observed and the callback will handle mounting when it intersects
|
|
94
|
+
// Return false here to prevent immediate mounting
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @param node - The starting node to check
|
|
7
7
|
* @returns The highest node with matching customElementRegistry, or null if node is invalid
|
|
8
8
|
*/
|
|
9
|
-
export function
|
|
9
|
+
export function getRegistryRoot(node) {
|
|
10
10
|
if (!node) {
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @param node - The starting node to check
|
|
7
7
|
* @returns The highest node with matching customElementRegistry, or null if node is invalid
|
|
8
8
|
*/
|
|
9
|
-
export function
|
|
9
|
+
export function getRegistryRoot(node: Node): Node | null {
|
|
10
10
|
if (!node) {
|
|
11
11
|
return null;
|
|
12
12
|
}
|
package/index.js
CHANGED
|
@@ -4,15 +4,20 @@ export { withScopePerimeter } from './withScopePerimeter.js';
|
|
|
4
4
|
export { emitMountedElementEvents } from './emitEvents.js';
|
|
5
5
|
export { arr } from './arr.js';
|
|
6
6
|
export { EvtRt } from './EvtRt.js';
|
|
7
|
-
export { DefineCustomElementHandler } from './
|
|
8
|
-
export { EnhanceMountedElementHandler } from './
|
|
7
|
+
export { DefineCustomElementHandler, DefineScopedCustomElementHandler } from './handlers/DefineCustomElement.js';
|
|
8
|
+
export { EnhanceMountedElementHandler } from './handlers/EnhanceMountedElement.js';
|
|
9
|
+
export { ScriptNoModuleHandler } from './handlers/ScriptNoModule.js';
|
|
10
|
+
export { MountObserverScriptHandler } from './handlers/MountObserverScript.js';
|
|
11
|
+
export { HoistTemplateHandler } from './handlers/HoistTemplate.js';
|
|
12
|
+
export { HTMLIncludeHandler } from './handlers/HTMLInclude.js';
|
|
13
|
+
export { upShadowSearch } from './upShadowSearch.js';
|
|
9
14
|
export { mountEventName, dismountEventName, disconnectEventName, loadEventName, mediamatchEventName, mediaunmatchEventName } from './Events.js';
|
|
10
15
|
// Register built-in handlers
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
import './EvtRt.js';
|
|
17
|
+
import './handlers/DefineCustomElement.js';
|
|
18
|
+
import './handlers/EnhanceMountedElement.js';
|
|
19
|
+
import './handlers/GenIds.js'; // Temporarily disabled due to missing dependency
|
|
20
|
+
import './handlers/ScriptNoModule.js';
|
|
21
|
+
import './handlers/MountObserverScript.js';
|
|
22
|
+
import './handlers/HoistTemplate.js';
|
|
23
|
+
import './handlers/HTMLInclude.js';
|
package/index.ts
CHANGED
|
@@ -4,8 +4,13 @@ export { withScopePerimeter } from './withScopePerimeter.js';
|
|
|
4
4
|
export { emitMountedElementEvents } from './emitEvents.js';
|
|
5
5
|
export { arr } from './arr.js';
|
|
6
6
|
export { EvtRt } from './EvtRt.js';
|
|
7
|
-
export { DefineCustomElementHandler } from './
|
|
8
|
-
export { EnhanceMountedElementHandler } from './
|
|
7
|
+
export { DefineCustomElementHandler, DefineScopedCustomElementHandler } from './handlers/DefineCustomElement.js';
|
|
8
|
+
export { EnhanceMountedElementHandler } from './handlers/EnhanceMountedElement.js';
|
|
9
|
+
export { ScriptNoModuleHandler } from './handlers/ScriptNoModule.js';
|
|
10
|
+
export { MountObserverScriptHandler } from './handlers/MountObserverScript.js';
|
|
11
|
+
export { HoistTemplateHandler } from './handlers/HoistTemplate.js';
|
|
12
|
+
export { HTMLIncludeHandler } from './handlers/HTMLInclude.js';
|
|
13
|
+
export { upShadowSearch } from './upShadowSearch.js';
|
|
9
14
|
export type {
|
|
10
15
|
MountConfig,
|
|
11
16
|
MountObserverOptions,
|
|
@@ -26,12 +31,12 @@ export {
|
|
|
26
31
|
} from './Events.js';
|
|
27
32
|
|
|
28
33
|
// Register built-in handlers
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
import
|
|
32
|
-
import
|
|
34
|
+
import './EvtRt.js';
|
|
35
|
+
import './handlers/DefineCustomElement.js';
|
|
36
|
+
import './handlers/EnhanceMountedElement.js';
|
|
37
|
+
import './handlers/GenIds.js'; // Temporarily disabled due to missing dependency
|
|
38
|
+
import './handlers/ScriptNoModule.js';
|
|
39
|
+
import './handlers/MountObserverScript.js';
|
|
40
|
+
import './handlers/HoistTemplate.js';
|
|
41
|
+
import './handlers/HTMLInclude.js';
|
|
33
42
|
|
|
34
|
-
MountObserver.define('builtIns.logToConsole', EvtRt);
|
|
35
|
-
MountObserver.define('builtIns.defineCustomElement', DefineCustomElementHandler);
|
|
36
|
-
MountObserver.define('buildIns.defineScopedCustomElement', DefineScopedCustomElementHandler);
|
|
37
|
-
MountObserver.define('builtIns.enhanceMountedElement', EnhanceMountedElementHandler);
|
package/mediaQuery.js
CHANGED
|
@@ -49,7 +49,7 @@ export function setupMediaQuery(init, rootNodeRef, mountedElements, modules, obs
|
|
|
49
49
|
modules,
|
|
50
50
|
observer: observer,
|
|
51
51
|
rootNode,
|
|
52
|
-
|
|
52
|
+
mountConfig: init
|
|
53
53
|
};
|
|
54
54
|
// Get all mounted elements from the WeakDual setWeak
|
|
55
55
|
const mountedElementsList = [];
|
package/mediaQuery.ts
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DismountEvent } from './Events.js';
|
|
2
|
+
export function setupObservedRootHas(init, rootNodeRef, mountedElements, modules, observer, processNode) {
|
|
3
|
+
const { whereObservedRootHas } = init;
|
|
4
|
+
if (!whereObservedRootHas) {
|
|
5
|
+
throw new Error('whereObservedRootHas is required');
|
|
6
|
+
}
|
|
7
|
+
const rootNode = rootNodeRef.deref();
|
|
8
|
+
if (!rootNode) {
|
|
9
|
+
throw new Error('Root node has been garbage collected');
|
|
10
|
+
}
|
|
11
|
+
// Get the element to query against
|
|
12
|
+
const rootElement = rootNode instanceof Element
|
|
13
|
+
? rootNode
|
|
14
|
+
: rootNode.documentElement || rootNode.host;
|
|
15
|
+
if (!rootElement) {
|
|
16
|
+
throw new Error('Could not determine root element for whereObservedRootHas');
|
|
17
|
+
}
|
|
18
|
+
// Track current state
|
|
19
|
+
let conditionMatches = !!rootElement.querySelector(whereObservedRootHas);
|
|
20
|
+
// Set up mutation observer to watch for changes
|
|
21
|
+
const mutationObserver = new MutationObserver(() => {
|
|
22
|
+
const previousMatches = conditionMatches;
|
|
23
|
+
conditionMatches = !!rootElement.querySelector(whereObservedRootHas);
|
|
24
|
+
if (conditionMatches && !previousMatches) {
|
|
25
|
+
// Condition now matches - process elements
|
|
26
|
+
handleConditionMatch();
|
|
27
|
+
}
|
|
28
|
+
else if (!conditionMatches && previousMatches) {
|
|
29
|
+
// Condition no longer matches - dismount all elements
|
|
30
|
+
handleConditionUnmatch();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
function handleConditionMatch() {
|
|
34
|
+
// Process all elements in the observed node
|
|
35
|
+
const rootNode = rootNodeRef.deref();
|
|
36
|
+
if (rootNode) {
|
|
37
|
+
processNode(rootNode);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function handleConditionUnmatch() {
|
|
41
|
+
// Dismount all currently mounted elements
|
|
42
|
+
const rootNode = rootNodeRef.deref();
|
|
43
|
+
if (!rootNode) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const context = {
|
|
47
|
+
modules,
|
|
48
|
+
observer: observer,
|
|
49
|
+
rootNode,
|
|
50
|
+
MountConfig: init
|
|
51
|
+
};
|
|
52
|
+
// Get all mounted elements from the WeakDual setWeak
|
|
53
|
+
const mountedElementsList = [];
|
|
54
|
+
for (const ref of mountedElements.setWeak) {
|
|
55
|
+
const element = ref.deref();
|
|
56
|
+
if (element) {
|
|
57
|
+
mountedElementsList.push(element);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Dismount each element
|
|
61
|
+
for (const element of mountedElementsList) {
|
|
62
|
+
// Remove from both structures
|
|
63
|
+
mountedElements.weakSet.delete(element);
|
|
64
|
+
for (const ref of mountedElements.setWeak) {
|
|
65
|
+
if (ref.deref() === element) {
|
|
66
|
+
mountedElements.setWeak.delete(ref);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Dispatch dismount event with reason
|
|
71
|
+
observer.dispatchEvent(new DismountEvent(element, 'observed-root-has-failed', init));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Observe the root element for changes
|
|
75
|
+
mutationObserver.observe(rootElement, {
|
|
76
|
+
childList: true,
|
|
77
|
+
subtree: true,
|
|
78
|
+
attributes: true,
|
|
79
|
+
attributeOldValue: false
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
conditionMatches,
|
|
83
|
+
cleanup: () => {
|
|
84
|
+
mutationObserver.disconnect();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,61 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mount-observer",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Observe and act on css matches.",
|
|
5
|
-
"main": "MountObserver.js",
|
|
6
|
-
"module": "MountObserver.js",
|
|
7
|
-
"dependencies": {
|
|
8
|
-
"assign-gingerly": "0.0.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "mount-observer",
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"description": "Observe and act on css matches.",
|
|
5
|
+
"main": "MountObserver.js",
|
|
6
|
+
"module": "MountObserver.js",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"assign-gingerly": "0.0.22",
|
|
9
|
+
"id-generation": "0.0.4"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@playwright/test": "1.59.0-alpha-2026-02-28",
|
|
13
|
+
"spa-ssi": "0.0.27"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"default": "./index.js",
|
|
18
|
+
"types": "./index.ts"
|
|
19
|
+
},
|
|
20
|
+
"./MountObserver.js": {
|
|
21
|
+
"default": "./MountObserver.js",
|
|
22
|
+
"types": "./MountObserver.ts"
|
|
23
|
+
},
|
|
24
|
+
"./withScopePerimeter.js": {
|
|
25
|
+
"default": "./withScopePerimeter.js",
|
|
26
|
+
"types": "./withScopePerimeter.ts"
|
|
27
|
+
},
|
|
28
|
+
"./emitEvents.js": {
|
|
29
|
+
"default": "./emitEvents.js",
|
|
30
|
+
"types": "./emitEvents.ts"
|
|
31
|
+
},
|
|
32
|
+
"./arr.js": {
|
|
33
|
+
"default": "./arr.js",
|
|
34
|
+
"types": "./arr.ts"
|
|
35
|
+
},
|
|
36
|
+
"./EvtRt.js": {
|
|
37
|
+
"default": "./EvtRt.js",
|
|
38
|
+
"types": "./EvtRt.ts"
|
|
39
|
+
},
|
|
40
|
+
"./DefineCustomElementHandler.js": {
|
|
41
|
+
"default": "./DefineCustomElementHandler.js",
|
|
42
|
+
"types": "./DefineCustomElementHandler.ts"
|
|
43
|
+
},
|
|
44
|
+
"./EnhanceMountedElementHandler.js": {
|
|
45
|
+
"default": "./EnhanceMountedElementHandler.js",
|
|
46
|
+
"types": "./EnhanceMountedElementHandler.ts"
|
|
47
|
+
},
|
|
48
|
+
"./upShadowSearch.js": {
|
|
49
|
+
"default": "./upShadowSearch.js",
|
|
50
|
+
"types": "./upShadowSearch.ts"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"*.js",
|
|
55
|
+
"*.ts"
|
|
56
|
+
],
|
|
57
|
+
"types": "./types/mount-observer/types.d.ts",
|
|
58
|
+
"scripts": {
|
|
59
|
+
"serve": "node ./node_modules/spa-ssi/serve.js",
|
|
60
|
+
"test": "playwright test",
|
|
61
|
+
"safari": "npx playwright wk http://localhost:8000",
|
|
62
|
+
"chrome": "npx playwright cr http://localhost:8000",
|
|
63
|
+
"update": "ncu -u && npm install"
|
|
64
|
+
},
|
|
65
|
+
"author": "Bruce B. Anderson <anderson.bruce.b@gmail.com>",
|
|
66
|
+
"license": "MIT"
|
|
67
|
+
}
|
package/playwright.config.ts
CHANGED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DismountEvent } from './Events.js';
|
|
2
|
+
export function setupRootSizeObserver(init, rootNodeRef, mountedElements, modules, observer, processNode) {
|
|
3
|
+
const { whereObservedRootSizeMatches } = init;
|
|
4
|
+
if (!whereObservedRootSizeMatches) {
|
|
5
|
+
throw new Error('whereObservedRootSizeMatches is required');
|
|
6
|
+
}
|
|
7
|
+
const rootNode = rootNodeRef.deref();
|
|
8
|
+
if (!rootNode) {
|
|
9
|
+
throw new Error('Root node has been garbage collected');
|
|
10
|
+
}
|
|
11
|
+
// Get the element to observe
|
|
12
|
+
const rootElement = rootNode instanceof Element
|
|
13
|
+
? rootNode
|
|
14
|
+
: rootNode.documentElement;
|
|
15
|
+
if (!rootElement) {
|
|
16
|
+
throw new Error('Could not determine root element for whereObservedRootSizeMatches');
|
|
17
|
+
}
|
|
18
|
+
// Parse the container query condition
|
|
19
|
+
// Container queries use the same syntax as media queries: (min-width: 700px)
|
|
20
|
+
const containerQuery = whereObservedRootSizeMatches;
|
|
21
|
+
// Check if condition currently matches
|
|
22
|
+
let conditionMatches = evaluateContainerQuery(rootElement, containerQuery);
|
|
23
|
+
// Set up ResizeObserver to watch for size changes
|
|
24
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
const previousMatches = conditionMatches;
|
|
27
|
+
conditionMatches = evaluateContainerQuery(entry.target, containerQuery);
|
|
28
|
+
if (conditionMatches && !previousMatches) {
|
|
29
|
+
// Condition now matches - process elements
|
|
30
|
+
handleConditionMatch();
|
|
31
|
+
}
|
|
32
|
+
else if (!conditionMatches && previousMatches) {
|
|
33
|
+
// Condition no longer matches - dismount all elements
|
|
34
|
+
handleConditionUnmatch();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
function handleConditionMatch() {
|
|
39
|
+
// Process all elements in the observed node
|
|
40
|
+
const rootNode = rootNodeRef.deref();
|
|
41
|
+
if (rootNode) {
|
|
42
|
+
processNode(rootNode);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function handleConditionUnmatch() {
|
|
46
|
+
// Dismount all currently mounted elements
|
|
47
|
+
const rootNode = rootNodeRef.deref();
|
|
48
|
+
if (!rootNode) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const context = {
|
|
52
|
+
modules,
|
|
53
|
+
observer: observer,
|
|
54
|
+
rootNode,
|
|
55
|
+
mountConfig: init
|
|
56
|
+
};
|
|
57
|
+
// Get all mounted elements from the WeakDual setWeak
|
|
58
|
+
const mountedElementsList = [];
|
|
59
|
+
for (const ref of mountedElements.setWeak) {
|
|
60
|
+
const element = ref.deref();
|
|
61
|
+
if (element) {
|
|
62
|
+
mountedElementsList.push(element);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Dismount each element
|
|
66
|
+
for (const element of mountedElementsList) {
|
|
67
|
+
// Remove from both structures
|
|
68
|
+
mountedElements.weakSet.delete(element);
|
|
69
|
+
for (const ref of mountedElements.setWeak) {
|
|
70
|
+
if (ref.deref() === element) {
|
|
71
|
+
mountedElements.setWeak.delete(ref);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Dispatch dismount event with reason
|
|
76
|
+
observer.dispatchEvent(new DismountEvent(element, 'root-size-failed', init));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Start observing the root element
|
|
80
|
+
resizeObserver.observe(rootElement);
|
|
81
|
+
return {
|
|
82
|
+
conditionMatches,
|
|
83
|
+
cleanup: () => {
|
|
84
|
+
resizeObserver.disconnect();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Evaluate a container query condition against an element
|
|
90
|
+
* Supports: min-width, max-width, min-height, max-height
|
|
91
|
+
*/
|
|
92
|
+
function evaluateContainerQuery(element, query) {
|
|
93
|
+
// Parse container query: (min-width: 700px) or (max-height: 500px)
|
|
94
|
+
const match = query.match(/\(([^:]+):\s*([^)]+)\)/);
|
|
95
|
+
if (!match) {
|
|
96
|
+
console.warn(`Invalid container query format: ${query}`);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const [, property, valueStr] = match;
|
|
100
|
+
const prop = property.trim();
|
|
101
|
+
const value = parseFloat(valueStr);
|
|
102
|
+
if (isNaN(value)) {
|
|
103
|
+
console.warn(`Invalid container query value: ${valueStr}`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// Get element dimensions
|
|
107
|
+
const rect = element.getBoundingClientRect();
|
|
108
|
+
const width = rect.width;
|
|
109
|
+
const height = rect.height;
|
|
110
|
+
// Evaluate condition
|
|
111
|
+
switch (prop) {
|
|
112
|
+
case 'min-width':
|
|
113
|
+
return width >= value;
|
|
114
|
+
case 'max-width':
|
|
115
|
+
return width <= value;
|
|
116
|
+
case 'min-height':
|
|
117
|
+
return height >= value;
|
|
118
|
+
case 'max-height':
|
|
119
|
+
return height <= value;
|
|
120
|
+
default:
|
|
121
|
+
console.warn(`Unsupported container query property: ${prop}`);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|