@jsenv/dom 0.6.1 → 0.7.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.
Files changed (109) hide show
  1. package/dist/jsenv_dom.js +259 -314
  2. package/package.json +2 -4
  3. package/index.js +0 -124
  4. package/src/attr/add_attribute_effect.js +0 -93
  5. package/src/attr/attributes.js +0 -32
  6. package/src/color/color_constrast.js +0 -69
  7. package/src/color/color_parsing.js +0 -319
  8. package/src/color/color_scheme.js +0 -28
  9. package/src/color/pick_light_or_dark.js +0 -34
  10. package/src/color/resolve_css_color.js +0 -60
  11. package/src/demos/3_columns_resize_demo.html +0 -84
  12. package/src/demos/3_rows_resize_demo.html +0 -89
  13. package/src/demos/aside_and_main_demo.html +0 -93
  14. package/src/demos/coordinates_demo.html +0 -450
  15. package/src/demos/document_autoscroll_demo.html +0 -517
  16. package/src/demos/drag_gesture_constraints_demo.html +0 -701
  17. package/src/demos/drag_gesture_demo.html +0 -1047
  18. package/src/demos/drag_gesture_element_to_impact_demo.html +0 -445
  19. package/src/demos/drag_reference_element_demo.html +0 -480
  20. package/src/demos/flex_details_set_demo.html +0 -302
  21. package/src/demos/flex_details_set_demo_2.html +0 -315
  22. package/src/demos/visible_rect_demo.html +0 -525
  23. package/src/element_signature.js +0 -100
  24. package/src/interaction/drag/constraint_feedback_line.js +0 -92
  25. package/src/interaction/drag/drag_constraint.js +0 -659
  26. package/src/interaction/drag/drag_debug_markers.js +0 -635
  27. package/src/interaction/drag/drag_element_positioner.js +0 -382
  28. package/src/interaction/drag/drag_gesture.js +0 -566
  29. package/src/interaction/drag/drag_resize_demo.html +0 -571
  30. package/src/interaction/drag/drag_to_move.js +0 -301
  31. package/src/interaction/drag/drag_to_resize_gesture.js +0 -68
  32. package/src/interaction/drag/drop_target_detection.js +0 -148
  33. package/src/interaction/drag/sticky_frontiers.js +0 -160
  34. package/src/interaction/event_marker.js +0 -14
  35. package/src/interaction/focus/active_element.js +0 -33
  36. package/src/interaction/focus/arrow_navigation.js +0 -599
  37. package/src/interaction/focus/element_is_focusable.js +0 -57
  38. package/src/interaction/focus/element_visibility.js +0 -111
  39. package/src/interaction/focus/find_focusable.js +0 -21
  40. package/src/interaction/focus/focus_group.js +0 -91
  41. package/src/interaction/focus/focus_group_registry.js +0 -12
  42. package/src/interaction/focus/focus_nav.js +0 -12
  43. package/src/interaction/focus/focus_nav_event_marker.js +0 -14
  44. package/src/interaction/focus/focus_trap.js +0 -105
  45. package/src/interaction/focus/tab_navigation.js +0 -128
  46. package/src/interaction/focus/tests/focus_group_skip_tab_test.html +0 -206
  47. package/src/interaction/focus/tests/tree_focus_test.html +0 -304
  48. package/src/interaction/focus/tests/tree_focus_test.jsx +0 -261
  49. package/src/interaction/focus/tests/tree_focus_test_preact.html +0 -13
  50. package/src/interaction/isolate_interactions.js +0 -161
  51. package/src/interaction/keyboard.js +0 -26
  52. package/src/interaction/scroll/capture_scroll.js +0 -47
  53. package/src/interaction/scroll/is_scrollable.js +0 -159
  54. package/src/interaction/scroll/scroll_container.js +0 -110
  55. package/src/interaction/scroll/scroll_trap.js +0 -44
  56. package/src/interaction/scroll/scrollbar_size.js +0 -20
  57. package/src/interaction/scroll/wheel_through.js +0 -138
  58. package/src/iterable_weak_set.js +0 -66
  59. package/src/position/dom_coords.js +0 -340
  60. package/src/position/offset_parent.js +0 -15
  61. package/src/position/position_fixed.js +0 -15
  62. package/src/position/position_sticky.js +0 -213
  63. package/src/position/sticky_rect.js +0 -79
  64. package/src/position/visible_rect.js +0 -486
  65. package/src/pub_sub.js +0 -31
  66. package/src/size/can_take_size.js +0 -11
  67. package/src/size/details_content_full_height.js +0 -63
  68. package/src/size/flex_details_set.js +0 -974
  69. package/src/size/get_available_height.js +0 -22
  70. package/src/size/get_available_width.js +0 -22
  71. package/src/size/get_border_sizes.js +0 -14
  72. package/src/size/get_height.js +0 -4
  73. package/src/size/get_inner_height.js +0 -15
  74. package/src/size/get_inner_width.js +0 -15
  75. package/src/size/get_margin_sizes.js +0 -10
  76. package/src/size/get_max_height.js +0 -57
  77. package/src/size/get_max_width.js +0 -47
  78. package/src/size/get_min_height.js +0 -14
  79. package/src/size/get_min_width.js +0 -14
  80. package/src/size/get_padding_sizes.js +0 -10
  81. package/src/size/get_width.js +0 -4
  82. package/src/size/hooks/use_available_height.js +0 -27
  83. package/src/size/hooks/use_available_width.js +0 -27
  84. package/src/size/hooks/use_max_height.js +0 -10
  85. package/src/size/hooks/use_max_width.js +0 -10
  86. package/src/size/hooks/use_resize_status.js +0 -62
  87. package/src/size/resize.js +0 -695
  88. package/src/size/resolve_css_size.js +0 -32
  89. package/src/style/dom_styles.js +0 -97
  90. package/src/style/style_composition.js +0 -121
  91. package/src/style/style_controller.js +0 -345
  92. package/src/style/style_default.js +0 -153
  93. package/src/style/style_default_demo.html +0 -128
  94. package/src/style/style_parsing.js +0 -375
  95. package/src/transition/demos/animation_resumption_test.xhtml +0 -500
  96. package/src/transition/demos/height_toggle_test.xhtml +0 -515
  97. package/src/transition/dom_transition.js +0 -254
  98. package/src/transition/easing.js +0 -48
  99. package/src/transition/group_transition.js +0 -261
  100. package/src/transition/transform_style_parser.js +0 -32
  101. package/src/transition/transition_playback.js +0 -366
  102. package/src/transition/transition_timeline.js +0 -79
  103. package/src/traversal.js +0 -247
  104. package/src/ui_transition/demos/content_states_transition_demo.html +0 -628
  105. package/src/ui_transition/demos/smooth_height_transition_demo.html +0 -149
  106. package/src/ui_transition/demos/transition_testing.html +0 -354
  107. package/src/ui_transition/ui_transition.js +0 -1470
  108. package/src/utils.js +0 -69
  109. package/src/value_effect.js +0 -35
@@ -1,32 +0,0 @@
1
- export const resolveCSSSize = (
2
- size,
3
- { availableSize, fontSize, autoIsRelativeToFont } = {},
4
- ) => {
5
- if (typeof size === "string") {
6
- if (size === "auto") {
7
- return autoIsRelativeToFont ? fontSize : availableSize;
8
- }
9
- if (size.endsWith("%")) {
10
- return availableSize * (parseFloat(size) / 100);
11
- }
12
- if (size.endsWith("px")) {
13
- return parseFloat(size);
14
- }
15
- if (size.endsWith("em")) {
16
- return parseFloat(size) * fontSize;
17
- }
18
- if (size.endsWith("rem")) {
19
- return (
20
- parseFloat(size) * getComputedStyle(document.documentElement).fontSize
21
- );
22
- }
23
- if (size.endsWith("vw")) {
24
- return (parseFloat(size) / 100) * window.innerWidth;
25
- }
26
- if (size.endsWith("vh")) {
27
- return (parseFloat(size) / 100) * window.innerHeight;
28
- }
29
- return parseFloat(size);
30
- }
31
- return size;
32
- };
@@ -1,97 +0,0 @@
1
- import { elementToOwnerWindow } from "../utils.js";
2
-
3
- export const getComputedStyle = (element) =>
4
- elementToOwnerWindow(element).getComputedStyle(element);
5
-
6
- export const getStyle = (element, name) =>
7
- getComputedStyle(element).getPropertyValue(name);
8
-
9
- const isCamelCase = (str) => {
10
- // Check if string contains lowercase letter followed by uppercase letter (camelCase pattern)
11
- return /[a-z][A-Z]/.test(str);
12
- };
13
- const kebabCase = (str) => {
14
- // Convert camelCase to kebab-case by inserting a hyphen before uppercase letters
15
- // and converting the uppercase letter to lowercase
16
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
17
- };
18
- export const setStyle = (element, name, value) => {
19
- if (import.meta.dev) {
20
- if (isCamelCase(name)) {
21
- console.warn(
22
- `setStyle: style name "${name}" should be in kebab-case, not camelCase. Use "${kebabCase(name)}" instead.`,
23
- );
24
- }
25
- }
26
-
27
- const prevValue = element.style[name];
28
- if (prevValue) {
29
- element.style.setProperty(name, value);
30
- return () => {
31
- element.style.setProperty(name, prevValue);
32
- };
33
- }
34
- element.style.setProperty(name, value);
35
- return () => {
36
- element.style.removeProperty(name);
37
- };
38
- };
39
- export const forceStyle = (element, name, value) => {
40
- const inlineStyleValue = element.style[name];
41
- if (inlineStyleValue === value) {
42
- return () => {};
43
- }
44
- const computedStyleValue = getStyle(element, name);
45
- if (computedStyleValue === value) {
46
- return () => {};
47
- }
48
- const restoreStyle = setStyle(element, name, value);
49
- return restoreStyle;
50
- };
51
-
52
- export const addWillChange = (element, property) => {
53
- const currentWillChange = element.style.willChange;
54
- const willChangeValues = currentWillChange
55
- ? currentWillChange
56
- .split(",")
57
- .map((v) => v.trim())
58
- .filter(Boolean)
59
- : [];
60
-
61
- if (willChangeValues.includes(property)) {
62
- // Property already exists, return no-op
63
- return () => {};
64
- }
65
-
66
- willChangeValues.push(property);
67
- element.style.willChange = willChangeValues.join(", ");
68
- // Return function to remove only this property
69
- return () => {
70
- const newValues = willChangeValues.filter((v) => v !== property);
71
- if (newValues.length === 0) {
72
- element.style.removeProperty("will-change");
73
- } else {
74
- element.style.willChange = newValues.join(", ");
75
- }
76
- };
77
- };
78
-
79
- const createSetMany = (setter) => {
80
- return (element, description) => {
81
- const cleanupCallbackSet = new Set();
82
- for (const name of Object.keys(description)) {
83
- const value = description[name];
84
- const restoreStyle = setter(element, name, value);
85
- cleanupCallbackSet.add(restoreStyle);
86
- }
87
- return () => {
88
- for (const cleanupCallback of cleanupCallbackSet) {
89
- cleanupCallback();
90
- }
91
- cleanupCallbackSet.clear();
92
- };
93
- };
94
- };
95
-
96
- export const setStyles = createSetMany(setStyle);
97
- export const forceStyles = createSetMany(forceStyle);
@@ -1,121 +0,0 @@
1
- import {
2
- normalizeStyle,
3
- normalizeStyles,
4
- parseCSSTransform,
5
- stringifyCSSTransform,
6
- } from "./style_parsing.js";
7
-
8
- // Merge two style objects, handling special cases like transform
9
- export const mergeStyles = (stylesA, stylesB, context = "js") => {
10
- if (!stylesA) {
11
- return normalizeStyles(stylesB, context);
12
- }
13
- if (!stylesB) {
14
- return normalizeStyles(stylesA, context);
15
- }
16
- const result = {};
17
- const aKeys = Object.keys(stylesA);
18
- // in case stylesB is a string we first parse it
19
- stylesB = normalizeStyles(stylesB, context);
20
- const bKeyToVisitSet = new Set(Object.keys(stylesB));
21
- for (const aKey of aKeys) {
22
- const bHasKey = bKeyToVisitSet.has(aKey);
23
- if (bHasKey) {
24
- bKeyToVisitSet.delete(aKey);
25
- result[aKey] = mergeOneStyle(stylesA[aKey], stylesB[aKey], aKey, context);
26
- } else {
27
- result[aKey] = normalizeStyle(stylesA[aKey], aKey, context);
28
- }
29
- }
30
- for (const bKey of bKeyToVisitSet) {
31
- result[bKey] = stylesB[bKey];
32
- }
33
- return result;
34
- };
35
-
36
- export const appendStyles = (
37
- stylesAObject,
38
- stylesBNormalized,
39
- context = "js",
40
- ) => {
41
- const aKeys = Object.keys(stylesAObject);
42
- const bKeys = Object.keys(stylesBNormalized);
43
- for (const bKey of bKeys) {
44
- const aHasKey = aKeys.includes(bKey);
45
- if (aHasKey) {
46
- stylesAObject[bKey] = mergeOneStyle(
47
- stylesAObject[bKey],
48
- stylesBNormalized[bKey],
49
- bKey,
50
- context,
51
- );
52
- } else {
53
- stylesAObject[bKey] = stylesBNormalized[bKey];
54
- }
55
- }
56
- return stylesAObject;
57
- };
58
-
59
- // Merge a single style property value with an existing value
60
- export const mergeOneStyle = (
61
- existingValue,
62
- newValue,
63
- propertyName,
64
- context = "js",
65
- ) => {
66
- if (propertyName === "transform") {
67
- // Matrix parsing is now handled automatically in parseCSSTransform
68
-
69
- // Determine the types
70
- const existingIsString =
71
- typeof existingValue === "string" && existingValue !== "none";
72
- const newIsString = typeof newValue === "string" && newValue !== "none";
73
- const existingIsObject =
74
- typeof existingValue === "object" && existingValue !== null;
75
- const newIsObject = typeof newValue === "object" && newValue !== null;
76
-
77
- // Case 1: Both are objects - merge directly
78
- if (existingIsObject && newIsObject) {
79
- const merged = { ...existingValue, ...newValue };
80
- return context === "css" ? stringifyCSSTransform(merged) : merged;
81
- }
82
-
83
- // Case 2: New is object, existing is string - parse existing and merge
84
- if (newIsObject && existingIsString) {
85
- const parsedExisting = parseCSSTransform(existingValue);
86
- const merged = { ...parsedExisting, ...newValue };
87
- return context === "css" ? stringifyCSSTransform(merged) : merged;
88
- }
89
-
90
- // Case 3: New is string, existing is object - parse new and merge
91
- if (newIsString && existingIsObject) {
92
- const parsedNew = parseCSSTransform(newValue);
93
- const merged = { ...existingValue, ...parsedNew };
94
- return context === "css" ? stringifyCSSTransform(merged) : merged;
95
- }
96
-
97
- // Case 4: Both are strings - parse both and merge
98
- if (existingIsString && newIsString) {
99
- const parsedExisting = parseCSSTransform(existingValue);
100
- const parsedNew = parseCSSTransform(newValue);
101
- const merged = { ...parsedExisting, ...parsedNew };
102
- return context === "css" ? stringifyCSSTransform(merged) : merged;
103
- }
104
-
105
- // Case 5: New is object, no existing or existing is none/null
106
- if (newIsObject) {
107
- return context === "css" ? stringifyCSSTransform(newValue) : newValue;
108
- }
109
-
110
- // Case 6: New is string, no existing or existing is none/null
111
- if (newIsString) {
112
- if (context === "css") {
113
- return newValue; // Already a string
114
- }
115
- return parseCSSTransform(newValue); // Convert to object
116
- }
117
- }
118
-
119
- // For all other properties, simple replacement
120
- return newValue;
121
- };
@@ -1,345 +0,0 @@
1
- /**
2
- * Style Controller System
3
- *
4
- * Solves CSS style manipulation problems in JavaScript:
5
- *
6
- * ## Main problems:
7
- * 1. **Temporary style override**: Code wants to read current style, force another style,
8
- * then restore original. With inline styles this is ugly and loses original info.
9
- * 2. **Multiple code parts**: When different parts of code want to touch styles simultaneously,
10
- * they step on each other (rare but happens).
11
- * 3. **Transform composition**: CSS transforms are especially painful - you want to keep
12
- * existing transforms but force specific parts (e.g., keep `rotate(45deg)` but override
13
- * `translateX`). Native CSS overwrites the entire transform property.
14
- *
15
- * ## Solution:
16
- * Controller pattern + Web Animations API to preserve inline styles. Code that sets
17
- * inline styles expects to find them unchanged - we use animations for clean override:
18
- *
19
- * ```js
20
- * const controller = createStyleController("myFeature");
21
- *
22
- * // Smart value conversion (100 → "100px", 45 → "45deg")
23
- * controller.set(element, {
24
- * transform: { translateX: 100, rotate: 45 }, // Individual transform properties
25
- * opacity: 0.5
26
- * });
27
- *
28
- * // Transform objects merged intelligently
29
- * controller.set(element, {
30
- * transform: { translateX: 50 } // Merges with existing transforms
31
- * });
32
- *
33
- * // Get underlying value without this controller's influence
34
- * const originalOpacity = controller.getUnderlyingValue(element, "opacity");
35
- * const originalTranslateX = controller.getUnderlyingValue(element, "transform.translateX"); // Magic dot notation!
36
- * const actualWidth = controller.getUnderlyingValue(element, "rect.width"); // Layout measurements
37
- *
38
- * controller.delete(element, "opacity"); // Only removes opacity, keeps transform
39
- * controller.clear(element); // Removes all styles from this controller only
40
- * controller.clearAll(); // Cleanup when done
41
- * ```
42
- *
43
- * **Key features:**
44
- * - **Transform composition**: Intelligently merges transform components instead of overwriting
45
- * - **Magic properties**: Access transform components with dot notation (e.g., "transform.translateX")
46
- * - **Layout measurements**: Access actual rendered dimensions with rect.* (e.g., "rect.width")
47
- * - **getUnderlyingValue()**: Read the "natural" value without this controller's influence
48
- * - **Smart units**: Numeric values get appropriate units automatically (px, deg, unitless)
49
- *
50
- * **Transform limitations:**
51
- * - **3D Transforms**: Complex `matrix3d()` transforms are preserved as-is and cannot be decomposed
52
- * into individual properties. Only `matrix3d()` that represent simple 2D transforms are converted
53
- * to object notation. Magic properties like "transform.rotateX" work only with explicit CSS functions,
54
- * not with complex 3D matrices.
55
- *
56
- * Multiple controllers can safely manage the same element without conflicts.
57
- */
58
-
59
- import { mergeOneStyle, mergeStyles } from "./style_composition.js";
60
- import { normalizeStyle, normalizeStyles } from "./style_parsing.js";
61
-
62
- // Global registry to track which controllers are managing each element's styles
63
- const elementControllerSetRegistry = new WeakMap(); // element -> Set<controller>
64
-
65
- // Top-level helpers for controller attachment tracking
66
- const onElementControllerAdded = (element, controller) => {
67
- if (!elementControllerSetRegistry.has(element)) {
68
- elementControllerSetRegistry.set(element, new Set());
69
- }
70
- const elementControllerSet = elementControllerSetRegistry.get(element);
71
- elementControllerSet.add(controller);
72
- };
73
- const onElementControllerRemoved = (element, controller) => {
74
- const elementControllerSet = elementControllerSetRegistry.get(element);
75
- if (elementControllerSet) {
76
- elementControllerSet.delete(controller);
77
-
78
- // Clean up empty element registry
79
- if (elementControllerSet.size === 0) {
80
- elementControllerSetRegistry.delete(element);
81
- }
82
- }
83
- };
84
-
85
- export const createStyleController = (name = "anonymous") => {
86
- // Store element data for this controller: element -> { styles, animation }
87
- const elementWeakMap = new WeakMap();
88
-
89
- const set = (element, stylesToSet) => {
90
- if (!element || typeof element !== "object") {
91
- throw new Error("Element must be a valid DOM element");
92
- }
93
- if (!stylesToSet || typeof stylesToSet !== "object") {
94
- throw new Error("styles must be an object");
95
- }
96
-
97
- const elementData = elementWeakMap.get(element);
98
- if (!elementData) {
99
- const normalizedStylesToSet = normalizeStyles(stylesToSet, "js");
100
- const animation = createAnimationForStyles(
101
- element,
102
- normalizedStylesToSet,
103
- name,
104
- );
105
- elementWeakMap.set(element, {
106
- styles: normalizedStylesToSet,
107
- animation,
108
- });
109
- onElementControllerAdded(element, controller);
110
- return;
111
- }
112
-
113
- const { styles, animation } = elementData;
114
- const mergedStyles = mergeStyles(styles, stylesToSet);
115
- elementData.styles = mergedStyles;
116
- updateAnimationStyles(animation, mergedStyles);
117
- };
118
-
119
- const get = (element, propertyName) => {
120
- const elementData = elementWeakMap.get(element);
121
- if (!elementData) {
122
- return undefined;
123
- }
124
- const { styles } = elementData;
125
- if (propertyName === undefined) {
126
- return { ...styles };
127
- }
128
- if (propertyName.startsWith("transform.")) {
129
- const transformProp = propertyName.slice("transform.".length);
130
- return styles.transform?.[transformProp];
131
- }
132
- return styles[propertyName];
133
- };
134
-
135
- const deleteMethod = (element, propertyName) => {
136
- const elementData = elementWeakMap.get(element);
137
- if (!elementData) {
138
- return;
139
- }
140
- const { styles, animation } = elementData;
141
- const hasStyle = Object.hasOwn(styles, propertyName);
142
- if (!hasStyle) {
143
- return;
144
- }
145
- delete styles[propertyName];
146
- const isEmpty = Object.keys(styles).length === 0;
147
- // Clean up empty controller
148
- if (isEmpty) {
149
- animation.cancel();
150
- elementWeakMap.delete(element);
151
- onElementControllerRemoved(element, controller);
152
- return;
153
- }
154
- updateAnimationStyles(animation, styles);
155
- };
156
-
157
- const commit = (element) => {
158
- const elementData = elementWeakMap.get(element);
159
- if (!elementData) {
160
- return; // Nothing to commit on this element for this controller
161
- }
162
- const { styles, animation } = elementData;
163
- // Cancel our animation permanently since we're committing styles to inline
164
- // (Keep this BEFORE getComputedStyle to prevent computedStyle reading our animation styles)
165
- animation.cancel();
166
- // Now read the true underlying styles (without our animation influence)
167
- const computedStyles = getComputedStyle(element);
168
- // Convert controller styles to CSS and commit to inline styles
169
- const cssStyles = normalizeStyles(styles, "css");
170
- for (const [key, value] of Object.entries(cssStyles)) {
171
- // Merge with existing computed styles for all properties
172
- const existingValue = computedStyles[key];
173
- element.style[key] = mergeOneStyle(existingValue, value, key, "css");
174
- }
175
- // Clear this controller's styles since they're now inline
176
- elementWeakMap.delete(element);
177
- // Clean up controller from element registry
178
- onElementControllerRemoved(element, controller);
179
- };
180
-
181
- const clear = (element) => {
182
- const elementData = elementWeakMap.get(element);
183
- if (!elementData) {
184
- return;
185
- }
186
- const { animation } = elementData;
187
- animation.cancel();
188
- elementWeakMap.delete(element);
189
- onElementControllerRemoved(element, controller);
190
- };
191
-
192
- const getUnderlyingValue = (element, propertyName) => {
193
- const elementControllerSet = elementControllerSetRegistry.get(element);
194
-
195
- const normalizeValueForJs = (value) => {
196
- // Use normalizeStyle to handle all property types including transform dot notation
197
- return normalizeStyle(value, propertyName, "js");
198
- };
199
-
200
- const getFromOtherControllers = () => {
201
- if (!elementControllerSet || elementControllerSet.size <= 1) {
202
- return undefined;
203
- }
204
-
205
- let resultValue;
206
- for (const otherController of elementControllerSet) {
207
- if (otherController === controller) continue;
208
- const otherStyles = otherController.get(element);
209
- if (propertyName in otherStyles) {
210
- resultValue = mergeOneStyle(
211
- resultValue,
212
- otherStyles[propertyName],
213
- propertyName,
214
- );
215
- }
216
- }
217
-
218
- // Note: For CSS width/height properties, we can trust the values from other controllers
219
- // because we assume box-sizing: border-box. If the element used content-box,
220
- // the CSS width/height would differ from getBoundingClientRect() due to padding/borders,
221
- // but since controllers set the final rendered size, the CSS value is what matters.
222
- // For actual layout measurements, use rect.* properties instead.
223
- return normalizeValueForJs(resultValue);
224
- };
225
-
226
- const getFromDOM = () => {
227
- // Handle transform dot notation
228
- if (propertyName.startsWith("transform.")) {
229
- const transformValue = getComputedStyle(element).transform;
230
- return normalizeValueForJs(transformValue);
231
- }
232
- // For all other CSS properties, use computed styles
233
- const computedValue = getComputedStyle(element)[propertyName];
234
- return normalizeValueForJs(computedValue);
235
- };
236
-
237
- const getFromDOMLayout = () => {
238
- // For rect.* properties that reflect actual layout, always read from DOM
239
- // These represent the actual rendered dimensions, bypassing any controller influence
240
- if (propertyName === "rect.width") {
241
- return element.getBoundingClientRect().width;
242
- }
243
- if (propertyName === "rect.height") {
244
- return element.getBoundingClientRect().height;
245
- }
246
- if (propertyName === "rect.left") {
247
- return element.getBoundingClientRect().left;
248
- }
249
- if (propertyName === "rect.top") {
250
- return element.getBoundingClientRect().top;
251
- }
252
- if (propertyName === "rect.right") {
253
- return element.getBoundingClientRect().right;
254
- }
255
- if (propertyName === "rect.bottom") {
256
- return element.getBoundingClientRect().bottom;
257
- }
258
- return undefined;
259
- };
260
-
261
- const getWhileDisablingThisController = (fn) => {
262
- const elementData = elementWeakMap.get(element);
263
- if (!elementData) {
264
- return fn();
265
- }
266
- const { styles, animation } = elementData;
267
- // Temporarily cancel our animation to read underlying value
268
- animation.cancel();
269
- const underlyingValue = fn();
270
- // Restore our animation
271
- elementData.animation = createAnimationForStyles(element, styles, name);
272
- return underlyingValue;
273
- };
274
-
275
- if (typeof propertyName === "function") {
276
- return getWhileDisablingThisController(propertyName);
277
- }
278
-
279
- // Handle computed layout properties (rect.*) - always read from DOM, bypass controllers
280
- if (propertyName.startsWith("rect.")) {
281
- return getWhileDisablingThisController(getFromDOMLayout);
282
- }
283
- if (!elementControllerSet || !elementControllerSet.has(controller)) {
284
- // This controller is not applied, just read current value
285
- return getFromDOM();
286
- }
287
- // Check if other controllers would provide this style
288
- const valueFromOtherControllers = getFromOtherControllers();
289
- if (valueFromOtherControllers !== undefined) {
290
- return valueFromOtherControllers;
291
- }
292
- return getWhileDisablingThisController(getFromDOM);
293
- };
294
-
295
- const clearAll = () => {
296
- // Remove this controller from all elements and clean up animations
297
- for (const [
298
- element,
299
- elementControllerSet,
300
- ] of elementControllerSetRegistry) {
301
- if (!elementControllerSet.has(controller)) {
302
- continue;
303
- }
304
- const elementData = elementWeakMap.get(element);
305
- if (!elementData) {
306
- continue;
307
- }
308
- const { animation } = elementData;
309
- animation.cancel();
310
- elementWeakMap.delete(element);
311
- onElementControllerRemoved(element, controller);
312
- }
313
- };
314
- const controller = {
315
- name,
316
- set,
317
- get,
318
- delete: deleteMethod,
319
- getUnderlyingValue,
320
- commit,
321
- clear,
322
- clearAll,
323
- };
324
-
325
- return controller;
326
- };
327
-
328
- const createAnimationForStyles = (element, styles, id) => {
329
- const cssStylesToSet = normalizeStyles(styles, "css");
330
- const animation = element.animate([cssStylesToSet], {
331
- duration: 0,
332
- fill: "forwards",
333
- });
334
- animation.id = id; // Set a debug name for this animation
335
- animation.play();
336
- animation.pause();
337
- return animation; // Return the created animation
338
- };
339
-
340
- const updateAnimationStyles = (animation, styles) => {
341
- const cssStyles = normalizeStyles(styles, "css");
342
- animation.effect.setKeyframes([cssStyles]);
343
- animation.play();
344
- animation.pause();
345
- };