@jsenv/dom 0.6.1 → 0.7.1
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/dist/jsenv_dom.js +339 -327
- package/package.json +2 -4
- package/index.js +0 -124
- package/src/attr/add_attribute_effect.js +0 -93
- package/src/attr/attributes.js +0 -32
- package/src/color/color_constrast.js +0 -69
- package/src/color/color_parsing.js +0 -319
- package/src/color/color_scheme.js +0 -28
- package/src/color/pick_light_or_dark.js +0 -34
- package/src/color/resolve_css_color.js +0 -60
- package/src/demos/3_columns_resize_demo.html +0 -84
- package/src/demos/3_rows_resize_demo.html +0 -89
- package/src/demos/aside_and_main_demo.html +0 -93
- package/src/demos/coordinates_demo.html +0 -450
- package/src/demos/document_autoscroll_demo.html +0 -517
- package/src/demos/drag_gesture_constraints_demo.html +0 -701
- package/src/demos/drag_gesture_demo.html +0 -1047
- package/src/demos/drag_gesture_element_to_impact_demo.html +0 -445
- package/src/demos/drag_reference_element_demo.html +0 -480
- package/src/demos/flex_details_set_demo.html +0 -302
- package/src/demos/flex_details_set_demo_2.html +0 -315
- package/src/demos/visible_rect_demo.html +0 -525
- package/src/element_signature.js +0 -100
- package/src/interaction/drag/constraint_feedback_line.js +0 -92
- package/src/interaction/drag/drag_constraint.js +0 -659
- package/src/interaction/drag/drag_debug_markers.js +0 -635
- package/src/interaction/drag/drag_element_positioner.js +0 -382
- package/src/interaction/drag/drag_gesture.js +0 -566
- package/src/interaction/drag/drag_resize_demo.html +0 -571
- package/src/interaction/drag/drag_to_move.js +0 -301
- package/src/interaction/drag/drag_to_resize_gesture.js +0 -68
- package/src/interaction/drag/drop_target_detection.js +0 -148
- package/src/interaction/drag/sticky_frontiers.js +0 -160
- package/src/interaction/event_marker.js +0 -14
- package/src/interaction/focus/active_element.js +0 -33
- package/src/interaction/focus/arrow_navigation.js +0 -599
- package/src/interaction/focus/element_is_focusable.js +0 -57
- package/src/interaction/focus/element_visibility.js +0 -111
- package/src/interaction/focus/find_focusable.js +0 -21
- package/src/interaction/focus/focus_group.js +0 -91
- package/src/interaction/focus/focus_group_registry.js +0 -12
- package/src/interaction/focus/focus_nav.js +0 -12
- package/src/interaction/focus/focus_nav_event_marker.js +0 -14
- package/src/interaction/focus/focus_trap.js +0 -105
- package/src/interaction/focus/tab_navigation.js +0 -128
- package/src/interaction/focus/tests/focus_group_skip_tab_test.html +0 -206
- package/src/interaction/focus/tests/tree_focus_test.html +0 -304
- package/src/interaction/focus/tests/tree_focus_test.jsx +0 -261
- package/src/interaction/focus/tests/tree_focus_test_preact.html +0 -13
- package/src/interaction/isolate_interactions.js +0 -161
- package/src/interaction/keyboard.js +0 -26
- package/src/interaction/scroll/capture_scroll.js +0 -47
- package/src/interaction/scroll/is_scrollable.js +0 -159
- package/src/interaction/scroll/scroll_container.js +0 -110
- package/src/interaction/scroll/scroll_trap.js +0 -44
- package/src/interaction/scroll/scrollbar_size.js +0 -20
- package/src/interaction/scroll/wheel_through.js +0 -138
- package/src/iterable_weak_set.js +0 -66
- package/src/position/dom_coords.js +0 -340
- package/src/position/offset_parent.js +0 -15
- package/src/position/position_fixed.js +0 -15
- package/src/position/position_sticky.js +0 -213
- package/src/position/sticky_rect.js +0 -79
- package/src/position/visible_rect.js +0 -486
- package/src/pub_sub.js +0 -31
- package/src/size/can_take_size.js +0 -11
- package/src/size/details_content_full_height.js +0 -63
- package/src/size/flex_details_set.js +0 -974
- package/src/size/get_available_height.js +0 -22
- package/src/size/get_available_width.js +0 -22
- package/src/size/get_border_sizes.js +0 -14
- package/src/size/get_height.js +0 -4
- package/src/size/get_inner_height.js +0 -15
- package/src/size/get_inner_width.js +0 -15
- package/src/size/get_margin_sizes.js +0 -10
- package/src/size/get_max_height.js +0 -57
- package/src/size/get_max_width.js +0 -47
- package/src/size/get_min_height.js +0 -14
- package/src/size/get_min_width.js +0 -14
- package/src/size/get_padding_sizes.js +0 -10
- package/src/size/get_width.js +0 -4
- package/src/size/hooks/use_available_height.js +0 -27
- package/src/size/hooks/use_available_width.js +0 -27
- package/src/size/hooks/use_max_height.js +0 -10
- package/src/size/hooks/use_max_width.js +0 -10
- package/src/size/hooks/use_resize_status.js +0 -62
- package/src/size/resize.js +0 -695
- package/src/size/resolve_css_size.js +0 -32
- package/src/style/dom_styles.js +0 -97
- package/src/style/style_composition.js +0 -121
- package/src/style/style_controller.js +0 -345
- package/src/style/style_default.js +0 -153
- package/src/style/style_default_demo.html +0 -128
- package/src/style/style_parsing.js +0 -375
- package/src/transition/demos/animation_resumption_test.xhtml +0 -500
- package/src/transition/demos/height_toggle_test.xhtml +0 -515
- package/src/transition/dom_transition.js +0 -254
- package/src/transition/easing.js +0 -48
- package/src/transition/group_transition.js +0 -261
- package/src/transition/transform_style_parser.js +0 -32
- package/src/transition/transition_playback.js +0 -366
- package/src/transition/transition_timeline.js +0 -79
- package/src/traversal.js +0 -247
- package/src/ui_transition/demos/content_states_transition_demo.html +0 -628
- package/src/ui_transition/demos/smooth_height_transition_demo.html +0 -149
- package/src/ui_transition/demos/transition_testing.html +0 -354
- package/src/ui_transition/ui_transition.js +0 -1470
- package/src/utils.js +0 -69
- package/src/value_effect.js +0 -35
package/dist/jsenv_dom.js
CHANGED
|
@@ -61,6 +61,16 @@ import { useState, useLayoutEffect } from "preact/hooks";
|
|
|
61
61
|
* getElementSignature(null) // Returns: "null"
|
|
62
62
|
*/
|
|
63
63
|
const getElementSignature = (element) => {
|
|
64
|
+
if (Array.isArray(element)) {
|
|
65
|
+
if (element.length === 0) {
|
|
66
|
+
return "empty";
|
|
67
|
+
}
|
|
68
|
+
if (element.length === 1) {
|
|
69
|
+
return getElementSignature(element[0]);
|
|
70
|
+
}
|
|
71
|
+
const parent = element[0].parentNode;
|
|
72
|
+
return `${getElementSignature(parent)} children`;
|
|
73
|
+
}
|
|
64
74
|
if (!element) {
|
|
65
75
|
return String(element);
|
|
66
76
|
}
|
|
@@ -306,11 +316,13 @@ const getAssociatedElements = (element) => {
|
|
|
306
316
|
return null;
|
|
307
317
|
};
|
|
308
318
|
|
|
309
|
-
const getComputedStyle$1 = (element) =>
|
|
310
|
-
elementToOwnerWindow(element).getComputedStyle(element);
|
|
319
|
+
const getComputedStyle$1 = (element) => {
|
|
320
|
+
return elementToOwnerWindow(element).getComputedStyle(element);
|
|
321
|
+
};
|
|
311
322
|
|
|
312
|
-
const getStyle = (element, name) =>
|
|
313
|
-
getComputedStyle$1(element).getPropertyValue(name);
|
|
323
|
+
const getStyle = (element, name) => {
|
|
324
|
+
return getComputedStyle$1(element).getPropertyValue(name);
|
|
325
|
+
};
|
|
314
326
|
const setStyle = (element, name, value) => {
|
|
315
327
|
|
|
316
328
|
const prevValue = element.style[name];
|
|
@@ -487,7 +499,11 @@ const normalizeStyle = (value, propertyName, context = "js") => {
|
|
|
487
499
|
// If value is a CSS transform string, parse it first to extract the specific property
|
|
488
500
|
if (typeof value === "string") {
|
|
489
501
|
if (value === "none") {
|
|
490
|
-
|
|
502
|
+
if (transformProperty.startsWith("scale")) {
|
|
503
|
+
return 1;
|
|
504
|
+
}
|
|
505
|
+
// translate, rotate, skew
|
|
506
|
+
return 0;
|
|
491
507
|
}
|
|
492
508
|
const parsedTransform = parseCSSTransform(value);
|
|
493
509
|
return parsedTransform?.[transformProperty];
|
|
@@ -501,38 +517,55 @@ const normalizeStyle = (value, propertyName, context = "js") => {
|
|
|
501
517
|
}
|
|
502
518
|
|
|
503
519
|
if (pxProperties.includes(propertyName)) {
|
|
504
|
-
return normalizeNumber(value,
|
|
520
|
+
return normalizeNumber(value, {
|
|
521
|
+
propertyName,
|
|
522
|
+
unit: "px",
|
|
523
|
+
preferedType: context === "js" ? "number" : "string",
|
|
524
|
+
});
|
|
505
525
|
}
|
|
506
526
|
if (degProperties.includes(propertyName)) {
|
|
507
|
-
return normalizeNumber(value,
|
|
527
|
+
return normalizeNumber(value, {
|
|
528
|
+
propertyName,
|
|
529
|
+
unit: "deg",
|
|
530
|
+
preferedType: "string",
|
|
531
|
+
});
|
|
508
532
|
}
|
|
509
533
|
if (unitlessProperties.includes(propertyName)) {
|
|
510
|
-
return normalizeNumber(value,
|
|
534
|
+
return normalizeNumber(value, {
|
|
535
|
+
propertyName,
|
|
536
|
+
unit: "",
|
|
537
|
+
preferedType: context === "js" ? "number" : "string",
|
|
538
|
+
});
|
|
511
539
|
}
|
|
512
540
|
|
|
513
541
|
return value;
|
|
514
542
|
};
|
|
515
|
-
const normalizeNumber = (value,
|
|
516
|
-
if (context === "css") {
|
|
517
|
-
if (typeof value === "number") {
|
|
518
|
-
if (isNaN(value)) {
|
|
519
|
-
console.warn(`NaN found for "${propertyName}"`);
|
|
520
|
-
}
|
|
521
|
-
return `${value}${unit}`;
|
|
522
|
-
}
|
|
523
|
-
return value;
|
|
524
|
-
}
|
|
543
|
+
const normalizeNumber = (value, { unit, propertyName, preferedType }) => {
|
|
525
544
|
if (typeof value === "string") {
|
|
526
|
-
//
|
|
527
|
-
if (
|
|
545
|
+
// Keep strings as-is (including %, em, rem, auto, none, etc.)
|
|
546
|
+
if (preferedType === "string") {
|
|
547
|
+
return value;
|
|
548
|
+
}
|
|
549
|
+
// convert to number if possible (font-size: "12px" -> fontSize:12, opacity: "0.5" -> opacity: 0.5)
|
|
550
|
+
if (!unit || value.endsWith(unit)) {
|
|
528
551
|
const numericValue = parseFloat(value);
|
|
529
552
|
if (!isNaN(numericValue)) {
|
|
530
553
|
return numericValue;
|
|
531
554
|
}
|
|
532
555
|
}
|
|
533
|
-
// Keep all other strings as-is (including %, em, rem, auto, none, etc.)
|
|
534
556
|
return value;
|
|
535
557
|
}
|
|
558
|
+
if (typeof value === "number") {
|
|
559
|
+
if (isNaN(value)) {
|
|
560
|
+
console.warn(`NaN found for "${propertyName}"`);
|
|
561
|
+
}
|
|
562
|
+
if (preferedType === "number") {
|
|
563
|
+
return value;
|
|
564
|
+
}
|
|
565
|
+
// convert to string with unit
|
|
566
|
+
return `${value}${unit}`;
|
|
567
|
+
}
|
|
568
|
+
|
|
536
569
|
return value;
|
|
537
570
|
};
|
|
538
571
|
|
|
@@ -958,6 +991,21 @@ const onElementControllerRemoved = (element, controller) => {
|
|
|
958
991
|
}
|
|
959
992
|
};
|
|
960
993
|
|
|
994
|
+
/**
|
|
995
|
+
* Creates a style controller that can safely manage CSS styles on DOM elements.
|
|
996
|
+
*
|
|
997
|
+
* Uses Web Animations API to override styles without touching inline styles,
|
|
998
|
+
* allowing multiple controllers to work together and providing intelligent transform composition.
|
|
999
|
+
*
|
|
1000
|
+
* @param {string} [name="anonymous"] - Debug name for the controller
|
|
1001
|
+
* @returns {Object} Controller with methods: set, get, delete, getUnderlyingValue, commit, clear, clearAll
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* const controller = createStyleController("myFeature");
|
|
1005
|
+
* controller.set(element, { opacity: 0.5, transform: { translateX: 100 } });
|
|
1006
|
+
* controller.getUnderlyingValue(element, "opacity"); // Read value without controller influence
|
|
1007
|
+
* controller.clearAll(); // Cleanup
|
|
1008
|
+
*/
|
|
961
1009
|
const createStyleController = (name = "anonymous") => {
|
|
962
1010
|
// Store element data for this controller: element -> { styles, animation }
|
|
963
1011
|
const elementWeakMap = new WeakMap();
|
|
@@ -1014,11 +1062,27 @@ const createStyleController = (name = "anonymous") => {
|
|
|
1014
1062
|
return;
|
|
1015
1063
|
}
|
|
1016
1064
|
const { styles, animation } = elementData;
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1065
|
+
if (propertyName.startsWith("transform.")) {
|
|
1066
|
+
const transformProp = propertyName.slice("transform.".length);
|
|
1067
|
+
const transformObject = styles.transform;
|
|
1068
|
+
if (!transformObject) {
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const hasTransformProp = Object.hasOwn(transformObject, transformProp);
|
|
1072
|
+
if (!hasTransformProp) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
delete transformObject[transformProp];
|
|
1076
|
+
if (Object.keys(transformObject).length === 0) {
|
|
1077
|
+
delete styles.transform;
|
|
1078
|
+
}
|
|
1079
|
+
} else {
|
|
1080
|
+
const hasStyle = Object.hasOwn(styles, propertyName);
|
|
1081
|
+
if (!hasStyle) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
delete styles[propertyName];
|
|
1020
1085
|
}
|
|
1021
|
-
delete styles[propertyName];
|
|
1022
1086
|
const isEmpty = Object.keys(styles).length === 0;
|
|
1023
1087
|
// Clean up empty controller
|
|
1024
1088
|
if (isEmpty) {
|
|
@@ -1220,6 +1284,44 @@ const updateAnimationStyles = (animation, styles) => {
|
|
|
1220
1284
|
animation.pause();
|
|
1221
1285
|
};
|
|
1222
1286
|
|
|
1287
|
+
const dormantStyleController = createStyleController("dormant");
|
|
1288
|
+
const getOpacity = (
|
|
1289
|
+
element,
|
|
1290
|
+
styleControllerToIgnore = dormantStyleController,
|
|
1291
|
+
) => {
|
|
1292
|
+
return styleControllerToIgnore.getUnderlyingValue(element, "opacity");
|
|
1293
|
+
};
|
|
1294
|
+
const getTranslateX = (
|
|
1295
|
+
element,
|
|
1296
|
+
styleControllerToIgnore = dormantStyleController,
|
|
1297
|
+
) => {
|
|
1298
|
+
return styleControllerToIgnore.getUnderlyingValue(
|
|
1299
|
+
element,
|
|
1300
|
+
"transform.translateX",
|
|
1301
|
+
);
|
|
1302
|
+
};
|
|
1303
|
+
const getTranslateY = (
|
|
1304
|
+
element,
|
|
1305
|
+
styleControllerToIgnore = dormantStyleController,
|
|
1306
|
+
) => {
|
|
1307
|
+
return styleControllerToIgnore.getUnderlyingValue(
|
|
1308
|
+
element,
|
|
1309
|
+
"transform.translateY",
|
|
1310
|
+
);
|
|
1311
|
+
};
|
|
1312
|
+
const getWidth$1 = (
|
|
1313
|
+
element,
|
|
1314
|
+
styleControllerToIgnore = dormantStyleController,
|
|
1315
|
+
) => {
|
|
1316
|
+
return styleControllerToIgnore.getUnderlyingValue(element, "rect.width");
|
|
1317
|
+
};
|
|
1318
|
+
const getHeight$1 = (
|
|
1319
|
+
element,
|
|
1320
|
+
styleControllerToIgnore = dormantStyleController,
|
|
1321
|
+
) => {
|
|
1322
|
+
return styleControllerToIgnore.getUnderlyingValue(element, "rect.height");
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1223
1325
|
// Register the style isolator custom element once
|
|
1224
1326
|
let persistentStyleIsolator = null;
|
|
1225
1327
|
const getNaviStyleIsolator = () => {
|
|
@@ -7990,32 +8092,6 @@ const pickPositionRelativeTo = (
|
|
|
7990
8092
|
};
|
|
7991
8093
|
};
|
|
7992
8094
|
|
|
7993
|
-
const parseTransform = (transform) => {
|
|
7994
|
-
if (!transform || transform === "none") return new Map();
|
|
7995
|
-
const transformMap = new Map();
|
|
7996
|
-
|
|
7997
|
-
if (transform.startsWith("matrix(")) {
|
|
7998
|
-
// matrix(a, b, c, d, e, f) where e is translateX and f is translateY
|
|
7999
|
-
const values = transform
|
|
8000
|
-
.match(/matrix\((.*?)\)/)?.[1]
|
|
8001
|
-
.split(",")
|
|
8002
|
-
.map(Number);
|
|
8003
|
-
if (values) {
|
|
8004
|
-
const translateX = values[4]; // e value from matrix
|
|
8005
|
-
transformMap.set("translateX", { value: translateX, unit: "px" });
|
|
8006
|
-
return transformMap;
|
|
8007
|
-
}
|
|
8008
|
-
}
|
|
8009
|
-
|
|
8010
|
-
// For direct transform functions (when set via style.transform)
|
|
8011
|
-
const matches = transform.matchAll(/(\w+)\(([-\d.]+)(%|px|deg)?\)/g);
|
|
8012
|
-
for (const match of matches) {
|
|
8013
|
-
const [, func, value, unit = ""] = match;
|
|
8014
|
-
transformMap.set(func, { value: parseFloat(value), unit });
|
|
8015
|
-
}
|
|
8016
|
-
return transformMap;
|
|
8017
|
-
};
|
|
8018
|
-
|
|
8019
8095
|
const EASING = {
|
|
8020
8096
|
LINEAR: (x) => x,
|
|
8021
8097
|
EASE: (x) => {
|
|
@@ -8241,7 +8317,7 @@ const createTransition = ({
|
|
|
8241
8317
|
transition.value = value;
|
|
8242
8318
|
transition.timing = isLast ? "end" : isFirstUpdate ? "start" : "progress";
|
|
8243
8319
|
isFirstUpdate = false;
|
|
8244
|
-
executionLifecycle.update(transition);
|
|
8320
|
+
executionLifecycle.update?.(transition);
|
|
8245
8321
|
executeUpdateCallbacks(transition);
|
|
8246
8322
|
},
|
|
8247
8323
|
|
|
@@ -8263,8 +8339,8 @@ const createTransition = ({
|
|
|
8263
8339
|
cancel: () => {
|
|
8264
8340
|
if (executionLifecycle) {
|
|
8265
8341
|
lifecycle.cancel(transition);
|
|
8266
|
-
executionLifecycle.teardown();
|
|
8267
|
-
executionLifecycle.restore();
|
|
8342
|
+
executionLifecycle.teardown?.();
|
|
8343
|
+
executionLifecycle.restore?.();
|
|
8268
8344
|
}
|
|
8269
8345
|
resume = null;
|
|
8270
8346
|
playState = "idle";
|
|
@@ -8281,7 +8357,7 @@ const createTransition = ({
|
|
|
8281
8357
|
}
|
|
8282
8358
|
// "running" or "paused"
|
|
8283
8359
|
lifecycle.finish(transition);
|
|
8284
|
-
executionLifecycle.teardown();
|
|
8360
|
+
executionLifecycle.teardown?.();
|
|
8285
8361
|
resume = null;
|
|
8286
8362
|
playState = "finished";
|
|
8287
8363
|
executeFinishCallbacks();
|
|
@@ -8493,25 +8569,7 @@ const createCallbackController = () => {
|
|
|
8493
8569
|
return [callbacks, execute];
|
|
8494
8570
|
};
|
|
8495
8571
|
|
|
8496
|
-
|
|
8497
|
-
import.meta.css = /* css */ `
|
|
8498
|
-
/* Transition data attributes override inline styles using CSS custom properties */
|
|
8499
|
-
*[data-transition-opacity] {
|
|
8500
|
-
opacity: var(--ui-transition-opacity) !important;
|
|
8501
|
-
}
|
|
8502
|
-
|
|
8503
|
-
*[data-transition-translate-x] {
|
|
8504
|
-
transform: translateX(var(--ui-transition-translate-x)) !important;
|
|
8505
|
-
}
|
|
8506
|
-
|
|
8507
|
-
*[data-transition-width] {
|
|
8508
|
-
width: var(--ui-transition-width) !important;
|
|
8509
|
-
}
|
|
8510
|
-
|
|
8511
|
-
*[data-transition-height] {
|
|
8512
|
-
height: var(--ui-transition-height) !important;
|
|
8513
|
-
}
|
|
8514
|
-
`;
|
|
8572
|
+
const transitionStyleController = createStyleController("transition");
|
|
8515
8573
|
|
|
8516
8574
|
const createHeightTransition = (element, to, options) => {
|
|
8517
8575
|
const heightTransition = createTimelineTransition({
|
|
@@ -8523,22 +8581,13 @@ const createHeightTransition = (element, to, options) => {
|
|
|
8523
8581
|
minDiff: 10,
|
|
8524
8582
|
lifecycle: {
|
|
8525
8583
|
setup: () => {
|
|
8526
|
-
const restoreWillChange = addWillChange(element, "height");
|
|
8527
8584
|
return {
|
|
8528
|
-
from: getHeight(element),
|
|
8585
|
+
from: getHeight$1(element),
|
|
8529
8586
|
update: ({ value }) => {
|
|
8530
|
-
|
|
8531
|
-
element.setAttribute("data-transition-height", valueWithUnit);
|
|
8532
|
-
element.style.setProperty("--ui-transition-height", valueWithUnit);
|
|
8587
|
+
transitionStyleController.set(element, { height: value });
|
|
8533
8588
|
},
|
|
8534
8589
|
teardown: () => {
|
|
8535
|
-
|
|
8536
|
-
element.style.removeProperty("--ui-transition-height");
|
|
8537
|
-
restoreWillChange();
|
|
8538
|
-
},
|
|
8539
|
-
restore: () => {
|
|
8540
|
-
element.removeAttribute("data-transition-height");
|
|
8541
|
-
element.style.removeProperty("--ui-transition-height");
|
|
8590
|
+
transitionStyleController.delete(element, "height");
|
|
8542
8591
|
},
|
|
8543
8592
|
};
|
|
8544
8593
|
},
|
|
@@ -8556,22 +8605,13 @@ const createWidthTransition = (element, to, options) => {
|
|
|
8556
8605
|
isVisual: true,
|
|
8557
8606
|
lifecycle: {
|
|
8558
8607
|
setup: () => {
|
|
8559
|
-
const restoreWillChange = addWillChange(element, "width");
|
|
8560
8608
|
return {
|
|
8561
|
-
from: getWidth(element),
|
|
8609
|
+
from: getWidth$1(element),
|
|
8562
8610
|
update: ({ value }) => {
|
|
8563
|
-
|
|
8564
|
-
element.setAttribute("data-transition-width", valueWithUnit);
|
|
8565
|
-
element.style.setProperty("--ui-transition-width", valueWithUnit);
|
|
8611
|
+
transitionStyleController.set(element, { width: value });
|
|
8566
8612
|
},
|
|
8567
8613
|
teardown: () => {
|
|
8568
|
-
|
|
8569
|
-
element.style.removeProperty("--ui-transition-width");
|
|
8570
|
-
restoreWillChange();
|
|
8571
|
-
},
|
|
8572
|
-
restore: () => {
|
|
8573
|
-
element.removeAttribute("data-transition-width");
|
|
8574
|
-
element.style.removeProperty("--ui-transition-width");
|
|
8614
|
+
transitionStyleController.delete(element, "width");
|
|
8575
8615
|
},
|
|
8576
8616
|
};
|
|
8577
8617
|
},
|
|
@@ -8589,21 +8629,13 @@ const createOpacityTransition = (element, to, options = {}) => {
|
|
|
8589
8629
|
isVisual: true,
|
|
8590
8630
|
lifecycle: {
|
|
8591
8631
|
setup: () => {
|
|
8592
|
-
const restoreWillChange = addWillChange(element, "opacity");
|
|
8593
8632
|
return {
|
|
8594
8633
|
from: getOpacity(element),
|
|
8595
8634
|
update: ({ value }) => {
|
|
8596
|
-
|
|
8597
|
-
element.style.setProperty("--ui-transition-opacity", value);
|
|
8635
|
+
transitionStyleController.set(element, { opacity: value });
|
|
8598
8636
|
},
|
|
8599
8637
|
teardown: () => {
|
|
8600
|
-
|
|
8601
|
-
element.style.removeProperty("--ui-transition-opacity");
|
|
8602
|
-
restoreWillChange();
|
|
8603
|
-
},
|
|
8604
|
-
restore: () => {
|
|
8605
|
-
element.removeAttribute("data-transition-opacity");
|
|
8606
|
-
element.style.removeProperty("--ui-transition-opacity");
|
|
8638
|
+
transitionStyleController.delete(element, "opacity");
|
|
8607
8639
|
},
|
|
8608
8640
|
};
|
|
8609
8641
|
},
|
|
@@ -8611,21 +8643,11 @@ const createOpacityTransition = (element, to, options = {}) => {
|
|
|
8611
8643
|
});
|
|
8612
8644
|
return opacityTransition;
|
|
8613
8645
|
};
|
|
8614
|
-
const getOpacity = (element) => {
|
|
8615
|
-
return parseFloat(getComputedStyle(element).opacity) || 0;
|
|
8616
|
-
};
|
|
8617
|
-
|
|
8618
|
-
const createTranslateXTransition = (element, to, options) => {
|
|
8619
|
-
let unit = "px";
|
|
8620
|
-
if (typeof to === "string") {
|
|
8621
|
-
if (to.endsWith("%")) {
|
|
8622
|
-
unit = "%";
|
|
8623
|
-
}
|
|
8624
|
-
to = parseFloat(to);
|
|
8625
|
-
}
|
|
8626
8646
|
|
|
8647
|
+
const createTranslateXTransition = (element, to, options = {}) => {
|
|
8648
|
+
const { setup, ...rest } = options;
|
|
8627
8649
|
const translateXTransition = createTimelineTransition({
|
|
8628
|
-
...
|
|
8650
|
+
...rest,
|
|
8629
8651
|
constructor: createTranslateXTransition,
|
|
8630
8652
|
key: element,
|
|
8631
8653
|
to,
|
|
@@ -8633,25 +8655,19 @@ const createTranslateXTransition = (element, to, options) => {
|
|
|
8633
8655
|
isVisual: true,
|
|
8634
8656
|
lifecycle: {
|
|
8635
8657
|
setup: () => {
|
|
8636
|
-
const
|
|
8658
|
+
const teardown = setup?.();
|
|
8637
8659
|
return {
|
|
8638
8660
|
from: getTranslateX(element),
|
|
8639
8661
|
update: ({ value }) => {
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
);
|
|
8662
|
+
transitionStyleController.set(element, {
|
|
8663
|
+
transform: {
|
|
8664
|
+
translateX: value,
|
|
8665
|
+
},
|
|
8666
|
+
});
|
|
8646
8667
|
},
|
|
8647
8668
|
teardown: () => {
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
element.style.removeProperty("--ui-transition-translate-x");
|
|
8651
|
-
},
|
|
8652
|
-
restore: () => {
|
|
8653
|
-
element.removeAttribute("data-transition-translate-x");
|
|
8654
|
-
element.style.removeProperty("--ui-transition-translate-x");
|
|
8669
|
+
teardown?.();
|
|
8670
|
+
transitionStyleController.delete(element, "transform.translateX");
|
|
8655
8671
|
},
|
|
8656
8672
|
};
|
|
8657
8673
|
},
|
|
@@ -8659,48 +8675,12 @@ const createTranslateXTransition = (element, to, options) => {
|
|
|
8659
8675
|
});
|
|
8660
8676
|
return translateXTransition;
|
|
8661
8677
|
};
|
|
8662
|
-
const getTranslateX = (element) => {
|
|
8663
|
-
const transform = getComputedStyle(element).transform;
|
|
8664
|
-
const transformMap = parseTransform(transform);
|
|
8665
|
-
return transformMap.get("translateX")?.value || 0;
|
|
8666
|
-
};
|
|
8667
|
-
|
|
8668
|
-
// Helper functions for getting natural (non-transition) values
|
|
8669
|
-
const getOpacityWithoutTransition = (element) => {
|
|
8670
|
-
const transitionOpacity = element.getAttribute("data-transition-opacity");
|
|
8671
|
-
|
|
8672
|
-
// Temporarily remove transition attribute
|
|
8673
|
-
element.removeAttribute("data-transition-opacity");
|
|
8674
|
-
|
|
8675
|
-
const naturalValue = parseFloat(getComputedStyle(element).opacity) || 0;
|
|
8676
|
-
|
|
8677
|
-
// Restore transition attribute if it existed
|
|
8678
|
-
if (transitionOpacity !== null) {
|
|
8679
|
-
element.setAttribute("data-transition-opacity", transitionOpacity);
|
|
8680
|
-
}
|
|
8681
|
-
|
|
8682
|
-
return naturalValue;
|
|
8683
|
-
};
|
|
8684
|
-
|
|
8685
|
-
const getTranslateXWithoutTransition = (element) => {
|
|
8686
|
-
const transitionTranslateX = element.getAttribute(
|
|
8687
|
-
"data-transition-translate-x",
|
|
8688
|
-
);
|
|
8689
8678
|
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
const naturalValue = transformMap.get("translateX")?.value || 0;
|
|
8696
|
-
|
|
8697
|
-
// Restore transition attribute if it existed
|
|
8698
|
-
if (transitionTranslateX !== null) {
|
|
8699
|
-
element.setAttribute("data-transition-translate-x", transitionTranslateX);
|
|
8700
|
-
}
|
|
8701
|
-
|
|
8702
|
-
return naturalValue;
|
|
8703
|
-
};
|
|
8679
|
+
// Helper functions for getting natural values
|
|
8680
|
+
const getOpacityWithoutTransition = (element) =>
|
|
8681
|
+
getOpacity(element, transitionStyleController);
|
|
8682
|
+
const getTranslateXWithoutTransition = (element) =>
|
|
8683
|
+
getTranslateX(element, transitionStyleController);
|
|
8704
8684
|
|
|
8705
8685
|
// transition that manages multiple transitions
|
|
8706
8686
|
const createGroupTransition = (transitionArray) => {
|
|
@@ -10287,13 +10267,28 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
|
|
|
10287
10267
|
|
|
10288
10268
|
installImportMetaCss(import.meta);
|
|
10289
10269
|
import.meta.css = /* css */ `
|
|
10270
|
+
.ui_transition_container[data-transition-overflow] {
|
|
10271
|
+
overflow: hidden;
|
|
10272
|
+
}
|
|
10273
|
+
|
|
10290
10274
|
.ui_transition_container,
|
|
10291
10275
|
.ui_transition_outer_wrapper,
|
|
10292
10276
|
.ui_transition_measure_wrapper,
|
|
10293
|
-
.ui_transition_slot
|
|
10294
|
-
|
|
10277
|
+
.ui_transition_slot,
|
|
10278
|
+
.ui_transition_phase_overlay,
|
|
10279
|
+
.ui_transition_content_overlay {
|
|
10280
|
+
display: flex;
|
|
10295
10281
|
width: fit-content;
|
|
10282
|
+
min-width: 100%;
|
|
10296
10283
|
height: fit-content;
|
|
10284
|
+
min-height: 100%;
|
|
10285
|
+
flex-direction: inherit;
|
|
10286
|
+
align-items: inherit;
|
|
10287
|
+
justify-content: inherit;
|
|
10288
|
+
}
|
|
10289
|
+
|
|
10290
|
+
.ui_transition_measure_wrapper[data-transition-translate-x] {
|
|
10291
|
+
overflow: hidden;
|
|
10297
10292
|
}
|
|
10298
10293
|
|
|
10299
10294
|
.ui_transition_container,
|
|
@@ -10316,11 +10311,15 @@ const DEBUG = {
|
|
|
10316
10311
|
};
|
|
10317
10312
|
|
|
10318
10313
|
// Utility function to format content key states consistently for debug logs
|
|
10319
|
-
const formatContentKeyState = (
|
|
10314
|
+
const formatContentKeyState = (
|
|
10315
|
+
contentKey,
|
|
10316
|
+
hasChildren,
|
|
10317
|
+
hasTextNode = false,
|
|
10318
|
+
) => {
|
|
10320
10319
|
if (hasTextNode) {
|
|
10321
10320
|
return "[text]";
|
|
10322
10321
|
}
|
|
10323
|
-
if (!
|
|
10322
|
+
if (!hasChildren) {
|
|
10324
10323
|
return "[empty]";
|
|
10325
10324
|
}
|
|
10326
10325
|
if (contentKey === null || contentKey === undefined) {
|
|
@@ -10341,6 +10340,7 @@ const initUITransition = (container) => {
|
|
|
10341
10340
|
...DEBUG,
|
|
10342
10341
|
transition: container.hasAttribute("data-debug-transition"),
|
|
10343
10342
|
};
|
|
10343
|
+
const debugClones = container.hasAttribute("data-debug-clones");
|
|
10344
10344
|
|
|
10345
10345
|
const debug = (type, ...args) => {
|
|
10346
10346
|
if (localDebug[type]) {
|
|
@@ -10365,17 +10365,6 @@ const initUITransition = (container) => {
|
|
|
10365
10365
|
".ui_transition_content_overlay",
|
|
10366
10366
|
);
|
|
10367
10367
|
|
|
10368
|
-
if (!phaseOverlay) {
|
|
10369
|
-
phaseOverlay = document.createElement("div");
|
|
10370
|
-
phaseOverlay.className = "ui_transition_phase_overlay";
|
|
10371
|
-
measureWrapper.appendChild(phaseOverlay);
|
|
10372
|
-
}
|
|
10373
|
-
if (!contentOverlay) {
|
|
10374
|
-
contentOverlay = document.createElement("div");
|
|
10375
|
-
contentOverlay.className = "ui_transition_content_overlay";
|
|
10376
|
-
container.appendChild(contentOverlay);
|
|
10377
|
-
}
|
|
10378
|
-
|
|
10379
10368
|
if (
|
|
10380
10369
|
!outerWrapper ||
|
|
10381
10370
|
!measureWrapper ||
|
|
@@ -10387,6 +10376,39 @@ const initUITransition = (container) => {
|
|
|
10387
10376
|
return { cleanup: () => {} };
|
|
10388
10377
|
}
|
|
10389
10378
|
|
|
10379
|
+
const [teardown, addTeardown] = createPubSub();
|
|
10380
|
+
|
|
10381
|
+
{
|
|
10382
|
+
const transitionOverflowSet = new Set();
|
|
10383
|
+
const updateTransitionOverflowAttribute = () => {
|
|
10384
|
+
if (transitionOverflowSet.size > 0) {
|
|
10385
|
+
container.setAttribute("data-transition-overflow", "");
|
|
10386
|
+
} else {
|
|
10387
|
+
container.removeAttribute("data-transition-overflow");
|
|
10388
|
+
}
|
|
10389
|
+
};
|
|
10390
|
+
const onOverflowStart = (event) => {
|
|
10391
|
+
transitionOverflowSet.add(event.detail.transitionId);
|
|
10392
|
+
updateTransitionOverflowAttribute();
|
|
10393
|
+
};
|
|
10394
|
+
const onOverflowEnd = (event) => {
|
|
10395
|
+
transitionOverflowSet.delete(event.detail.transitionId);
|
|
10396
|
+
updateTransitionOverflowAttribute();
|
|
10397
|
+
};
|
|
10398
|
+
container.addEventListener("ui_transition_overflow_start", onOverflowStart);
|
|
10399
|
+
container.addEventListener("ui_transition_overflow_end", onOverflowEnd);
|
|
10400
|
+
addTeardown(() => {
|
|
10401
|
+
container.removeEventListener(
|
|
10402
|
+
"ui_transition_overflow_start",
|
|
10403
|
+
onOverflowStart,
|
|
10404
|
+
);
|
|
10405
|
+
container.removeEventListener(
|
|
10406
|
+
"ui_transition_overflow_end",
|
|
10407
|
+
onOverflowEnd,
|
|
10408
|
+
);
|
|
10409
|
+
});
|
|
10410
|
+
}
|
|
10411
|
+
|
|
10390
10412
|
const transitionController = createGroupTransitionController();
|
|
10391
10413
|
|
|
10392
10414
|
// Transition state
|
|
@@ -10418,7 +10440,7 @@ const initUITransition = (container) => {
|
|
|
10418
10440
|
|
|
10419
10441
|
// Child state
|
|
10420
10442
|
let lastContentKey = null;
|
|
10421
|
-
let
|
|
10443
|
+
let previousChildNodes = [];
|
|
10422
10444
|
let isContentPhase = false; // Current state: true when showing content phase (loading/error)
|
|
10423
10445
|
let wasContentPhase = false; // Previous state for comparison
|
|
10424
10446
|
|
|
@@ -10647,40 +10669,48 @@ const initUITransition = (container) => {
|
|
|
10647
10669
|
const setupTransition = ({
|
|
10648
10670
|
isPhaseTransition = false,
|
|
10649
10671
|
overlay,
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
firstChild,
|
|
10672
|
+
needsOldChildNodesClone,
|
|
10673
|
+
previousChildNodes,
|
|
10674
|
+
childNodes,
|
|
10654
10675
|
attributeToRemove = [],
|
|
10655
10676
|
}) => {
|
|
10656
|
-
let oldChild = null;
|
|
10657
10677
|
let cleanup = () => {};
|
|
10658
|
-
|
|
10678
|
+
let elementToImpact;
|
|
10659
10679
|
|
|
10660
|
-
if (
|
|
10661
|
-
|
|
10680
|
+
if (overlay.childNodes.length > 0) {
|
|
10681
|
+
elementToImpact = overlay;
|
|
10682
|
+
cleanup = () => {
|
|
10683
|
+
if (!debugClones) {
|
|
10684
|
+
overlay.innerHTML = "";
|
|
10685
|
+
}
|
|
10686
|
+
};
|
|
10662
10687
|
debug(
|
|
10663
10688
|
"transition",
|
|
10664
10689
|
`Continuing from current ${isPhaseTransition ? "phase" : "content"} transition element`,
|
|
10665
10690
|
);
|
|
10666
|
-
|
|
10667
|
-
} else if (needsOldChildClone) {
|
|
10691
|
+
} else if (needsOldChildNodesClone) {
|
|
10668
10692
|
overlay.innerHTML = "";
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10693
|
+
for (const previousChildNode of previousChildNodes) {
|
|
10694
|
+
const previousChildClone = previousChildNode.cloneNode(true);
|
|
10695
|
+
if (previousChildClone.nodeType !== Node.TEXT_NODE) {
|
|
10696
|
+
for (const attrToRemove of attributeToRemove) {
|
|
10697
|
+
previousChildClone.removeAttribute(attrToRemove);
|
|
10698
|
+
}
|
|
10699
|
+
previousChildClone.setAttribute("data-ui-transition-clone", "");
|
|
10700
|
+
}
|
|
10701
|
+
overlay.appendChild(previousChildClone);
|
|
10702
|
+
}
|
|
10703
|
+
elementToImpact = overlay;
|
|
10704
|
+
cleanup = () => {
|
|
10705
|
+
if (!debugClones) {
|
|
10706
|
+
overlay.innerHTML = "";
|
|
10707
|
+
}
|
|
10708
|
+
};
|
|
10678
10709
|
debug(
|
|
10679
10710
|
"transition",
|
|
10680
10711
|
`Cloned previous child for ${isPhaseTransition ? "phase" : "content"} transition:`,
|
|
10681
|
-
getElementSignature(
|
|
10712
|
+
getElementSignature(previousChildNodes),
|
|
10682
10713
|
);
|
|
10683
|
-
cleanup = () => oldChild.remove();
|
|
10684
10714
|
} else {
|
|
10685
10715
|
overlay.innerHTML = "";
|
|
10686
10716
|
debug(
|
|
@@ -10696,16 +10726,15 @@ const initUITransition = (container) => {
|
|
|
10696
10726
|
let newElement;
|
|
10697
10727
|
if (isPhaseTransition) {
|
|
10698
10728
|
// Phase transitions work on individual elements
|
|
10699
|
-
oldElement =
|
|
10700
|
-
newElement =
|
|
10729
|
+
oldElement = elementToImpact;
|
|
10730
|
+
newElement = slot;
|
|
10701
10731
|
} else {
|
|
10702
10732
|
// Content transitions work at container level and can outlive content phase changes
|
|
10703
|
-
oldElement =
|
|
10704
|
-
newElement =
|
|
10733
|
+
oldElement = previousChildNodes.length ? elementToImpact : null;
|
|
10734
|
+
newElement = childNodes.length ? measureWrapper : null;
|
|
10705
10735
|
}
|
|
10706
10736
|
|
|
10707
10737
|
return {
|
|
10708
|
-
oldChild,
|
|
10709
10738
|
cleanup,
|
|
10710
10739
|
oldElement,
|
|
10711
10740
|
newElement,
|
|
@@ -10725,102 +10754,75 @@ const initUITransition = (container) => {
|
|
|
10725
10754
|
|
|
10726
10755
|
try {
|
|
10727
10756
|
isUpdating = true;
|
|
10728
|
-
const
|
|
10729
|
-
const childUIName = firstChild?.getAttribute("data-ui-name");
|
|
10757
|
+
const childNodes = Array.from(slot.childNodes);
|
|
10730
10758
|
if (localDebug.transition) {
|
|
10731
10759
|
const updateLabel =
|
|
10732
|
-
|
|
10733
|
-
|
|
10760
|
+
childNodes.length === 0
|
|
10761
|
+
? "cleared/empty"
|
|
10762
|
+
: childNodes.length === 1
|
|
10763
|
+
? getElementSignature(childNodes[0])
|
|
10764
|
+
: getElementSignature(slot);
|
|
10734
10765
|
console.group(`UI Update: ${updateLabel} (reason: ${reason})`);
|
|
10735
10766
|
}
|
|
10736
10767
|
|
|
10737
|
-
//
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
console.warn(
|
|
10743
|
-
"UI Transition: Text nodes in transition slots are not supported. Please wrap text content in an element.",
|
|
10744
|
-
{ slot, textContent: slot.textContent.trim() },
|
|
10745
|
-
);
|
|
10746
|
-
}
|
|
10747
|
-
|
|
10748
|
-
// Check for multiple elements in the slot (not supported yet)
|
|
10749
|
-
const hasMultipleElements = slot.children.length > 1;
|
|
10750
|
-
if (hasMultipleElements) {
|
|
10751
|
-
console.warn(
|
|
10752
|
-
"UI Transition: Multiple elements in transition slots are not supported yet. Please use a single container element.",
|
|
10753
|
-
{ slot, elementCount: slot.children.length },
|
|
10754
|
-
);
|
|
10755
|
-
}
|
|
10768
|
+
// Determine transition scenarios early for early registration check
|
|
10769
|
+
// Prepare phase info early so logging can be unified (even for early return)
|
|
10770
|
+
wasContentPhase = isContentPhase;
|
|
10771
|
+
const hadChild = previousChildNodes.length > 0;
|
|
10772
|
+
const hasChild = childNodes.length > 0;
|
|
10756
10773
|
|
|
10757
10774
|
// Prefer data-content-key on child, fallback to slot
|
|
10758
10775
|
let currentContentKey = null;
|
|
10759
10776
|
let slotContentKey = slot.getAttribute("data-content-key");
|
|
10760
|
-
let childContentKey
|
|
10777
|
+
let childContentKey;
|
|
10778
|
+
|
|
10779
|
+
if (childNodes.length === 0) {
|
|
10780
|
+
childContentKey = null;
|
|
10781
|
+
isContentPhase = true; // empty (no child) is treated as content phase
|
|
10782
|
+
} else {
|
|
10783
|
+
for (const childNode of childNodes) {
|
|
10784
|
+
if (childNode.nodeType === Node.TEXT_NODE) {
|
|
10785
|
+
} else if (childNode.hasAttribute("data-content-key")) {
|
|
10786
|
+
childContentKey = childNode.getAttribute("data-content-key");
|
|
10787
|
+
} else if (childNode.hasAttribute("data-content-phase")) {
|
|
10788
|
+
isContentPhase = true;
|
|
10789
|
+
}
|
|
10790
|
+
}
|
|
10791
|
+
}
|
|
10761
10792
|
if (childContentKey && slotContentKey) {
|
|
10762
10793
|
console.warn(
|
|
10763
|
-
|
|
10764
|
-
{ childContentKey, slotContentKey },
|
|
10794
|
+
`Slot and slot child both have a [data-content-key]. Slot is ${slotContentKey} and child is ${childContentKey}, using the child.`,
|
|
10765
10795
|
);
|
|
10766
10796
|
}
|
|
10767
10797
|
currentContentKey = childContentKey || slotContentKey || null;
|
|
10768
|
-
|
|
10769
|
-
// Determine transition scenarios early for early registration check
|
|
10770
|
-
const hadChild = previousChild !== null;
|
|
10771
|
-
const hasChild = firstChild !== null;
|
|
10772
|
-
|
|
10773
|
-
// Check for text nodes in previous state (reconstruct from previousChild)
|
|
10774
|
-
const hadTextNode =
|
|
10775
|
-
previousChild && previousChild.nodeType === Node.TEXT_NODE;
|
|
10776
|
-
|
|
10777
10798
|
// Compute formatted content key states ONCE per mutation (requirement: max 2 calls)
|
|
10778
10799
|
const previousContentKeyState = formatContentKeyState(
|
|
10779
10800
|
lastContentKey,
|
|
10780
10801
|
hadChild,
|
|
10781
|
-
hadTextNode,
|
|
10782
10802
|
);
|
|
10783
10803
|
const currentContentKeyState = formatContentKeyState(
|
|
10784
10804
|
currentContentKey,
|
|
10785
10805
|
hasChild,
|
|
10786
|
-
hasTextNode,
|
|
10787
10806
|
);
|
|
10788
|
-
|
|
10789
10807
|
// Track previous key before any potential early registration update
|
|
10790
10808
|
const prevKeyBeforeRegistration = lastContentKey;
|
|
10791
|
-
|
|
10792
|
-
// Prepare phase info early so logging can be unified (even for early return)
|
|
10793
|
-
wasContentPhase = isContentPhase;
|
|
10794
|
-
isContentPhase = firstChild
|
|
10795
|
-
? firstChild.hasAttribute("data-content-phase")
|
|
10796
|
-
: true; // empty (no child) is treated as content phase
|
|
10797
|
-
|
|
10798
10809
|
const previousIsContentPhase = !hadChild || wasContentPhase;
|
|
10799
10810
|
const currentIsContentPhase = !hasChild || isContentPhase;
|
|
10800
10811
|
|
|
10801
|
-
// Early conceptual registration path: empty slot
|
|
10802
|
-
const shouldGiveUpEarlyAndJustRegister =
|
|
10803
|
-
(!hadChild && !hasChild && !hasTextNode) ||
|
|
10804
|
-
hasTextNode ||
|
|
10805
|
-
hasMultipleElements;
|
|
10812
|
+
// Early conceptual registration path: empty slot
|
|
10813
|
+
const shouldGiveUpEarlyAndJustRegister = !hadChild && !hasChild;
|
|
10806
10814
|
let earlyAction = null;
|
|
10807
10815
|
if (shouldGiveUpEarlyAndJustRegister) {
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
earlyAction = "
|
|
10816
|
+
const prevKey = prevKeyBeforeRegistration;
|
|
10817
|
+
const keyChanged = prevKey !== currentContentKey;
|
|
10818
|
+
if (!keyChanged) {
|
|
10819
|
+
earlyAction = "unchanged";
|
|
10820
|
+
} else if (prevKey === null && currentContentKey !== null) {
|
|
10821
|
+
earlyAction = "registered";
|
|
10822
|
+
} else if (prevKey !== null && currentContentKey === null) {
|
|
10823
|
+
earlyAction = "cleared";
|
|
10812
10824
|
} else {
|
|
10813
|
-
|
|
10814
|
-
const keyChanged = prevKey !== currentContentKey;
|
|
10815
|
-
if (!keyChanged) {
|
|
10816
|
-
earlyAction = "unchanged";
|
|
10817
|
-
} else if (prevKey === null && currentContentKey !== null) {
|
|
10818
|
-
earlyAction = "registered";
|
|
10819
|
-
} else if (prevKey !== null && currentContentKey === null) {
|
|
10820
|
-
earlyAction = "cleared";
|
|
10821
|
-
} else {
|
|
10822
|
-
earlyAction = "changed";
|
|
10823
|
-
}
|
|
10825
|
+
earlyAction = "changed";
|
|
10824
10826
|
}
|
|
10825
10827
|
// Will update lastContentKey after unified logging
|
|
10826
10828
|
}
|
|
@@ -10871,7 +10873,7 @@ const initUITransition = (container) => {
|
|
|
10871
10873
|
|
|
10872
10874
|
// Handle resize observation
|
|
10873
10875
|
stopResizeObserver();
|
|
10874
|
-
if (
|
|
10876
|
+
if (hasChild && !isContentPhase) {
|
|
10875
10877
|
startResizeObserver();
|
|
10876
10878
|
debug("size", "Observing child resize");
|
|
10877
10879
|
}
|
|
@@ -10897,11 +10899,7 @@ const initUITransition = (container) => {
|
|
|
10897
10899
|
|
|
10898
10900
|
// Content key change when either slot or child has data-content-key and it changed
|
|
10899
10901
|
let shouldDoContentTransition = false;
|
|
10900
|
-
if (
|
|
10901
|
-
(slot.getAttribute("data-content-key") ||
|
|
10902
|
-
firstChild?.getAttribute("data-content-key")) &&
|
|
10903
|
-
lastContentKey !== null
|
|
10904
|
-
) {
|
|
10902
|
+
if (currentContentKey && lastContentKey !== null) {
|
|
10905
10903
|
shouldDoContentTransition = currentContentKey !== lastContentKey;
|
|
10906
10904
|
}
|
|
10907
10905
|
|
|
@@ -10985,7 +10983,7 @@ const initUITransition = (container) => {
|
|
|
10985
10983
|
}
|
|
10986
10984
|
|
|
10987
10985
|
// Register state and mark initial population done
|
|
10988
|
-
|
|
10986
|
+
previousChildNodes = childNodes;
|
|
10989
10987
|
lastContentKey = currentContentKey;
|
|
10990
10988
|
hasPopulatedOnce = true;
|
|
10991
10989
|
if (localDebug.transition) {
|
|
@@ -11058,11 +11056,7 @@ const initUITransition = (container) => {
|
|
|
11058
11056
|
shouldDoContentTransitionIncludingPopulation &&
|
|
11059
11057
|
!preserveOnlyContentTransition
|
|
11060
11058
|
) {
|
|
11061
|
-
const existingOldContents = contentOverlay.querySelectorAll(
|
|
11062
|
-
"[data-ui-transition-old]",
|
|
11063
|
-
);
|
|
11064
11059
|
const animationProgress = activeContentTransition?.progress || 0;
|
|
11065
|
-
|
|
11066
11060
|
if (animationProgress > 0) {
|
|
11067
11061
|
debug(
|
|
11068
11062
|
"transition",
|
|
@@ -11076,7 +11070,6 @@ const initUITransition = (container) => {
|
|
|
11076
11070
|
const canContinueSmoothly =
|
|
11077
11071
|
activeContentTransitionType === newTransitionType &&
|
|
11078
11072
|
activeContentTransition;
|
|
11079
|
-
|
|
11080
11073
|
if (canContinueSmoothly) {
|
|
11081
11074
|
debug(
|
|
11082
11075
|
"transition",
|
|
@@ -11097,11 +11090,8 @@ const initUITransition = (container) => {
|
|
|
11097
11090
|
activeContentTransition.cancel();
|
|
11098
11091
|
}
|
|
11099
11092
|
|
|
11100
|
-
const
|
|
11101
|
-
(contentChange || becomesEmpty) &&
|
|
11102
|
-
previousChild &&
|
|
11103
|
-
!existingOldContents[0];
|
|
11104
|
-
|
|
11093
|
+
const needsOldChildNodesClone =
|
|
11094
|
+
(contentChange || becomesEmpty) && hadChild;
|
|
11105
11095
|
const duration = parseInt(
|
|
11106
11096
|
container.getAttribute("data-content-transition-duration") ||
|
|
11107
11097
|
CONTENT_TRANSITION_DURATION,
|
|
@@ -11114,10 +11104,9 @@ const initUITransition = (container) => {
|
|
|
11114
11104
|
setupTransition({
|
|
11115
11105
|
isPhaseTransition: false,
|
|
11116
11106
|
overlay: contentOverlay,
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
firstChild,
|
|
11107
|
+
needsOldChildNodesClone,
|
|
11108
|
+
previousChildNodes,
|
|
11109
|
+
childNodes,
|
|
11121
11110
|
attributeToRemove: ["data-content-key"],
|
|
11122
11111
|
});
|
|
11123
11112
|
|
|
@@ -11136,9 +11125,8 @@ const initUITransition = (container) => {
|
|
|
11136
11125
|
}
|
|
11137
11126
|
}
|
|
11138
11127
|
|
|
11139
|
-
activeContentTransition =
|
|
11128
|
+
activeContentTransition = applyTransition(
|
|
11140
11129
|
transitionController,
|
|
11141
|
-
firstChild,
|
|
11142
11130
|
setupContentTransition,
|
|
11143
11131
|
{
|
|
11144
11132
|
duration,
|
|
@@ -11180,12 +11168,7 @@ const initUITransition = (container) => {
|
|
|
11180
11168
|
if (shouldDoPhaseTransition) {
|
|
11181
11169
|
const phaseTransitionType =
|
|
11182
11170
|
container.getAttribute("data-phase-transition") || PHASE_TRANSITION;
|
|
11183
|
-
|
|
11184
|
-
const existingOldPhaseContents = phaseOverlay.querySelectorAll(
|
|
11185
|
-
"[data-ui-transition-old]",
|
|
11186
|
-
);
|
|
11187
11171
|
const phaseAnimationProgress = activePhaseTransition?.progress || 0;
|
|
11188
|
-
|
|
11189
11172
|
if (phaseAnimationProgress > 0) {
|
|
11190
11173
|
debug(
|
|
11191
11174
|
"transition",
|
|
@@ -11215,10 +11198,7 @@ const initUITransition = (container) => {
|
|
|
11215
11198
|
}
|
|
11216
11199
|
|
|
11217
11200
|
const needsOldPhaseClone =
|
|
11218
|
-
(becomesEmpty || becomesPopulated || phaseChange) &&
|
|
11219
|
-
previousChild &&
|
|
11220
|
-
!existingOldPhaseContents[0];
|
|
11221
|
-
|
|
11201
|
+
(becomesEmpty || becomesPopulated || phaseChange) && hadChild;
|
|
11222
11202
|
const phaseDuration = parseInt(
|
|
11223
11203
|
container.getAttribute("data-phase-transition-duration") ||
|
|
11224
11204
|
PHASE_TRANSITION_DURATION,
|
|
@@ -11228,10 +11208,9 @@ const initUITransition = (container) => {
|
|
|
11228
11208
|
setupTransition({
|
|
11229
11209
|
isPhaseTransition: true,
|
|
11230
11210
|
overlay: phaseOverlay,
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
firstChild,
|
|
11211
|
+
needsOldChildNodesClone: needsOldPhaseClone,
|
|
11212
|
+
previousChildNodes,
|
|
11213
|
+
childNodes,
|
|
11235
11214
|
attributeToRemove: ["data-content-key", "data-content-phase"],
|
|
11236
11215
|
});
|
|
11237
11216
|
|
|
@@ -11251,9 +11230,8 @@ const initUITransition = (container) => {
|
|
|
11251
11230
|
`Starting phase transition: ${fromPhase} → ${toPhase}`,
|
|
11252
11231
|
);
|
|
11253
11232
|
|
|
11254
|
-
activePhaseTransition =
|
|
11233
|
+
activePhaseTransition = applyTransition(
|
|
11255
11234
|
transitionController,
|
|
11256
|
-
firstChild,
|
|
11257
11235
|
setupPhaseTransition,
|
|
11258
11236
|
{
|
|
11259
11237
|
duration: phaseDuration,
|
|
@@ -11279,7 +11257,7 @@ const initUITransition = (container) => {
|
|
|
11279
11257
|
}
|
|
11280
11258
|
|
|
11281
11259
|
// Store current child for next transition
|
|
11282
|
-
|
|
11260
|
+
previousChildNodes = childNodes;
|
|
11283
11261
|
lastContentKey = currentContentKey;
|
|
11284
11262
|
if (becomesPopulated) {
|
|
11285
11263
|
hasPopulatedOnce = true;
|
|
@@ -11363,11 +11341,11 @@ const initUITransition = (container) => {
|
|
|
11363
11341
|
characterData: false,
|
|
11364
11342
|
});
|
|
11365
11343
|
|
|
11366
|
-
// Return API
|
|
11367
11344
|
return {
|
|
11368
11345
|
slot,
|
|
11369
11346
|
|
|
11370
11347
|
cleanup: () => {
|
|
11348
|
+
teardown();
|
|
11371
11349
|
mutationObserver.disconnect();
|
|
11372
11350
|
stopResizeObserver();
|
|
11373
11351
|
if (sizeTransition) {
|
|
@@ -11408,9 +11386,8 @@ const initUITransition = (container) => {
|
|
|
11408
11386
|
};
|
|
11409
11387
|
};
|
|
11410
11388
|
|
|
11411
|
-
const
|
|
11389
|
+
const applyTransition = (
|
|
11412
11390
|
transitionController,
|
|
11413
|
-
newChild,
|
|
11414
11391
|
setupTransition,
|
|
11415
11392
|
{
|
|
11416
11393
|
type,
|
|
@@ -11432,7 +11409,7 @@ const animateTransition = (
|
|
|
11432
11409
|
return null;
|
|
11433
11410
|
}
|
|
11434
11411
|
|
|
11435
|
-
const { cleanup, oldElement, newElement } = setupTransition();
|
|
11412
|
+
const { cleanup, oldElement, newElement, onTeardown } = setupTransition();
|
|
11436
11413
|
// Use precomputed content key states (expected to be provided by caller)
|
|
11437
11414
|
const fromContentKey = fromContentKeyState;
|
|
11438
11415
|
const toContentKey = toContentKeyState;
|
|
@@ -11462,6 +11439,7 @@ const animateTransition = (
|
|
|
11462
11439
|
if (transitions.length === 0) {
|
|
11463
11440
|
debug("transition", "No transitions to animate, cleaning up immediately");
|
|
11464
11441
|
cleanup();
|
|
11442
|
+
onTeardown?.();
|
|
11465
11443
|
onComplete?.();
|
|
11466
11444
|
return null;
|
|
11467
11445
|
}
|
|
@@ -11470,6 +11448,7 @@ const animateTransition = (
|
|
|
11470
11448
|
onFinish: () => {
|
|
11471
11449
|
groupTransition.cancel();
|
|
11472
11450
|
cleanup();
|
|
11451
|
+
onTeardown?.();
|
|
11473
11452
|
onComplete?.();
|
|
11474
11453
|
},
|
|
11475
11454
|
});
|
|
@@ -11498,6 +11477,8 @@ const slideLeft = {
|
|
|
11498
11477
|
|
|
11499
11478
|
return [
|
|
11500
11479
|
createTranslateXTransition(oldElement, to, {
|
|
11480
|
+
setup: () =>
|
|
11481
|
+
notifyTransitionOverflow(newElement, "slide_out_old_content"),
|
|
11501
11482
|
from,
|
|
11502
11483
|
duration,
|
|
11503
11484
|
startProgress,
|
|
@@ -11519,6 +11500,8 @@ const slideLeft = {
|
|
|
11519
11500
|
debug("transition", "Slide in from empty:", { from, to });
|
|
11520
11501
|
return [
|
|
11521
11502
|
createTranslateXTransition(newElement, to, {
|
|
11503
|
+
setup: () =>
|
|
11504
|
+
notifyTransitionOverflow(newElement, "slice_in_new_content"),
|
|
11522
11505
|
from,
|
|
11523
11506
|
duration,
|
|
11524
11507
|
startProgress,
|
|
@@ -11571,6 +11554,8 @@ const slideLeft = {
|
|
|
11571
11554
|
// Slide old content out
|
|
11572
11555
|
transitions.push(
|
|
11573
11556
|
createTranslateXTransition(oldElement, -containerWidth, {
|
|
11557
|
+
setup: () =>
|
|
11558
|
+
notifyTransitionOverflow(newElement, "slide_out_old_content"),
|
|
11574
11559
|
from: oldContentPosition,
|
|
11575
11560
|
duration,
|
|
11576
11561
|
startProgress,
|
|
@@ -11583,6 +11568,8 @@ const slideLeft = {
|
|
|
11583
11568
|
// Slide new content in
|
|
11584
11569
|
transitions.push(
|
|
11585
11570
|
createTranslateXTransition(newElement, naturalNewPosition, {
|
|
11571
|
+
setup: () =>
|
|
11572
|
+
notifyTransitionOverflow(newElement, "slide_in_new_content"),
|
|
11586
11573
|
from: effectiveFromPosition,
|
|
11587
11574
|
duration,
|
|
11588
11575
|
startProgress,
|
|
@@ -11706,4 +11693,29 @@ const crossFade = {
|
|
|
11706
11693
|
},
|
|
11707
11694
|
};
|
|
11708
11695
|
|
|
11709
|
-
|
|
11696
|
+
const dispatchTransitionOverflowStartCustomEvent = (element, transitionId) => {
|
|
11697
|
+
const customEvent = new CustomEvent("ui_transition_overflow_start", {
|
|
11698
|
+
bubbles: true,
|
|
11699
|
+
detail: {
|
|
11700
|
+
transitionId,
|
|
11701
|
+
},
|
|
11702
|
+
});
|
|
11703
|
+
element.dispatchEvent(customEvent);
|
|
11704
|
+
};
|
|
11705
|
+
const dispatchTransitionOverflowEndCustomEvent = (element, transitionId) => {
|
|
11706
|
+
const customEvent = new CustomEvent("ui_transition_overflow_end", {
|
|
11707
|
+
bubbles: true,
|
|
11708
|
+
detail: {
|
|
11709
|
+
transitionId,
|
|
11710
|
+
},
|
|
11711
|
+
});
|
|
11712
|
+
element.dispatchEvent(customEvent);
|
|
11713
|
+
};
|
|
11714
|
+
const notifyTransitionOverflow = (element, transitionId) => {
|
|
11715
|
+
dispatchTransitionOverflowStartCustomEvent(element, transitionId);
|
|
11716
|
+
return () => {
|
|
11717
|
+
dispatchTransitionOverflowEndCustomEvent(element, transitionId);
|
|
11718
|
+
};
|
|
11719
|
+
};
|
|
11720
|
+
|
|
11721
|
+
export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, appendStyles, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateY, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, mergeStyles, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
|