@jsenv/navi 0.12.8 → 0.12.9
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_navi.js +1389 -1810
- package/dist/jsenv_navi.js.map +103 -85
- package/dist/jsenv_navi_side_effects.js +2 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { installImportMetaCss } from "./jsenv_navi_side_effects.js";
|
|
2
|
-
import { createIterableWeakSet, createPubSub, createValueEffect, createStyleController, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, allowWheelThrough, resolveCSSColor, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, activeElementSignal, canInterceptKeys, createGroupTransitionController,
|
|
2
|
+
import { createIterableWeakSet, createPubSub, createValueEffect, createStyleController, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, allowWheelThrough, resolveCSSColor, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, activeElementSignal, canInterceptKeys, createGroupTransitionController, getElementSignature, getBorderRadius, getBackground, preventIntermediateScrollbar, createOpacityTransition, stringifyStyle, mergeOneStyle, mergeTwoStyles, appendStyles, normalizeStyles, resolveCSSSize, findBefore, findAfter, initFocusGroup, elementIsFocusable, pickLightOrDark, resolveColorLuminance, dragAfterThreshold, getScrollContainer, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
3
3
|
import { prefixFirstAndIndentRemainingLines } from "@jsenv/humanize";
|
|
4
4
|
import { effect, signal, computed, batch, useSignal } from "@preact/signals";
|
|
5
5
|
import { useEffect, useRef, useCallback, useContext, useState, useLayoutEffect, useMemo, useErrorBoundary, useImperativeHandle, useId } from "preact/hooks";
|
|
6
6
|
import { createContext, toChildArray, createRef, cloneElement } from "preact";
|
|
7
|
-
import {
|
|
7
|
+
import { jsxs, jsx, Fragment } from "preact/jsx-runtime";
|
|
8
8
|
import { createPortal, forwardRef } from "preact/compat";
|
|
9
9
|
|
|
10
10
|
const actionPrivatePropertiesWeakMap = new WeakMap();
|
|
@@ -614,9 +614,9 @@ const weakEffect = (values, callback) => {
|
|
|
614
614
|
return dispose;
|
|
615
615
|
};
|
|
616
616
|
|
|
617
|
-
let DEBUG$
|
|
617
|
+
let DEBUG$2 = false;
|
|
618
618
|
const enableDebugActions = () => {
|
|
619
|
-
DEBUG$
|
|
619
|
+
DEBUG$2 = true;
|
|
620
620
|
};
|
|
621
621
|
|
|
622
622
|
let dispatchActions = (params) => {
|
|
@@ -680,7 +680,7 @@ const prerunProtectionRegistry = (() => {
|
|
|
680
680
|
if (protection) {
|
|
681
681
|
clearTimeout(protection.timeoutId);
|
|
682
682
|
protectedActionMap.delete(action);
|
|
683
|
-
if (DEBUG$
|
|
683
|
+
if (DEBUG$2) {
|
|
684
684
|
const elapsed = Date.now() - protection.timestamp;
|
|
685
685
|
console.debug(`"${action}": GC protection removed after ${elapsed}ms`);
|
|
686
686
|
}
|
|
@@ -698,7 +698,7 @@ const prerunProtectionRegistry = (() => {
|
|
|
698
698
|
const timestamp = Date.now();
|
|
699
699
|
const timeoutId = setTimeout(() => {
|
|
700
700
|
unprotect(action);
|
|
701
|
-
if (DEBUG$
|
|
701
|
+
if (DEBUG$2) {
|
|
702
702
|
console.debug(
|
|
703
703
|
`"${action}": prerun protection expired after ${PROTECTION_DURATION}ms`,
|
|
704
704
|
);
|
|
@@ -707,7 +707,7 @@ const prerunProtectionRegistry = (() => {
|
|
|
707
707
|
|
|
708
708
|
protectedActionMap.set(action, { timeoutId, timestamp });
|
|
709
709
|
|
|
710
|
-
if (DEBUG$
|
|
710
|
+
if (DEBUG$2) {
|
|
711
711
|
console.debug(
|
|
712
712
|
`"${action}": protected from GC for ${PROTECTION_DURATION}ms`,
|
|
713
713
|
);
|
|
@@ -818,7 +818,7 @@ const updateActions = ({
|
|
|
818
818
|
|
|
819
819
|
const { runningSet, settledSet } = getActivationInfo();
|
|
820
820
|
|
|
821
|
-
if (DEBUG$
|
|
821
|
+
if (DEBUG$2) {
|
|
822
822
|
let argSource = `reason: \`${reason}\``;
|
|
823
823
|
if (isReplace) {
|
|
824
824
|
argSource += `, isReplace: true`;
|
|
@@ -942,7 +942,7 @@ ${lines.join("\n")}`,
|
|
|
942
942
|
}
|
|
943
943
|
}
|
|
944
944
|
}
|
|
945
|
-
if (DEBUG$
|
|
945
|
+
if (DEBUG$2) {
|
|
946
946
|
const lines = [
|
|
947
947
|
...(willResetSet.size
|
|
948
948
|
? [formatActionSet(willResetSet, "- will reset:")]
|
|
@@ -1038,7 +1038,7 @@ ${lines.join("\n")}`);
|
|
|
1038
1038
|
actionToPromotePrivateProperties.isPrerunSignal.value = false;
|
|
1039
1039
|
}
|
|
1040
1040
|
}
|
|
1041
|
-
if (DEBUG$
|
|
1041
|
+
if (DEBUG$2) {
|
|
1042
1042
|
console.groupEnd();
|
|
1043
1043
|
}
|
|
1044
1044
|
|
|
@@ -1149,7 +1149,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1149
1149
|
if (!actionAbort) {
|
|
1150
1150
|
return false;
|
|
1151
1151
|
}
|
|
1152
|
-
if (DEBUG$
|
|
1152
|
+
if (DEBUG$2) {
|
|
1153
1153
|
console.log(`"${action}": aborting (reason: ${reason})`);
|
|
1154
1154
|
}
|
|
1155
1155
|
actionAbort(reason);
|
|
@@ -1421,7 +1421,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1421
1421
|
if (isPrerun && (globalAbortSignal.aborted || abortSignal.aborted)) {
|
|
1422
1422
|
prerunProtectionRegistry.unprotect(action);
|
|
1423
1423
|
}
|
|
1424
|
-
if (DEBUG$
|
|
1424
|
+
if (DEBUG$2) {
|
|
1425
1425
|
console.log(`"${action}": aborted (reason: ${abortReason})`);
|
|
1426
1426
|
}
|
|
1427
1427
|
};
|
|
@@ -1494,7 +1494,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1494
1494
|
onComplete?.(computedDataSignal.peek(), action);
|
|
1495
1495
|
completeSideEffect?.(action);
|
|
1496
1496
|
});
|
|
1497
|
-
if (DEBUG$
|
|
1497
|
+
if (DEBUG$2) {
|
|
1498
1498
|
console.log(`"${action}": completed`);
|
|
1499
1499
|
}
|
|
1500
1500
|
return computedDataSignal.peek();
|
|
@@ -1521,7 +1521,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1521
1521
|
"never supposed to happen, abort error should be handled by the abort signal",
|
|
1522
1522
|
);
|
|
1523
1523
|
}
|
|
1524
|
-
if (DEBUG$
|
|
1524
|
+
if (DEBUG$2) {
|
|
1525
1525
|
console.log(
|
|
1526
1526
|
`"${action}": failed (error: ${e}, handled by ui: ${ui.hasRenderers})`,
|
|
1527
1527
|
);
|
|
@@ -1591,7 +1591,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1591
1591
|
|
|
1592
1592
|
const performStop = ({ reason }) => {
|
|
1593
1593
|
abort(reason);
|
|
1594
|
-
if (DEBUG$
|
|
1594
|
+
if (DEBUG$2) {
|
|
1595
1595
|
console.log(`"${action}": stopping (reason: ${reason})`);
|
|
1596
1596
|
}
|
|
1597
1597
|
|
|
@@ -5212,11 +5212,11 @@ const useExternalValueSync = (
|
|
|
5212
5212
|
}
|
|
5213
5213
|
};
|
|
5214
5214
|
|
|
5215
|
-
const UNSET = {};
|
|
5215
|
+
const UNSET$1 = {};
|
|
5216
5216
|
const useInitialValue = (compute) => {
|
|
5217
|
-
const initialValueRef = useRef(UNSET);
|
|
5217
|
+
const initialValueRef = useRef(UNSET$1);
|
|
5218
5218
|
let initialValue = initialValueRef.current;
|
|
5219
|
-
if (initialValue !== UNSET) {
|
|
5219
|
+
if (initialValue !== UNSET$1) {
|
|
5220
5220
|
return initialValue;
|
|
5221
5221
|
}
|
|
5222
5222
|
|
|
@@ -8013,9 +8013,9 @@ const executeWithCleanup = (fn, cleanup) => {
|
|
|
8013
8013
|
}
|
|
8014
8014
|
};
|
|
8015
8015
|
|
|
8016
|
-
let DEBUG$
|
|
8016
|
+
let DEBUG$1 = false;
|
|
8017
8017
|
const enableDebugOnDocumentLoading = () => {
|
|
8018
|
-
DEBUG$
|
|
8018
|
+
DEBUG$1 = true;
|
|
8019
8019
|
};
|
|
8020
8020
|
|
|
8021
8021
|
const windowIsLoadingSignal = signal(true);
|
|
@@ -8035,13 +8035,13 @@ const [
|
|
|
8035
8035
|
removeFromDocumentLoadingRouteArraySignal,
|
|
8036
8036
|
] = arraySignal([]);
|
|
8037
8037
|
const routingWhile = (fn, routeNames = []) => {
|
|
8038
|
-
if (DEBUG$
|
|
8038
|
+
if (DEBUG$1 && routeNames.length > 0) {
|
|
8039
8039
|
console.debug(`routingWhile: Adding routes to loading state:`, routeNames);
|
|
8040
8040
|
}
|
|
8041
8041
|
addToDocumentLoadingRouteArraySignal(...routeNames);
|
|
8042
8042
|
return executeWithCleanup(fn, () => {
|
|
8043
8043
|
removeFromDocumentLoadingRouteArraySignal(...routeNames);
|
|
8044
|
-
if (DEBUG$
|
|
8044
|
+
if (DEBUG$1 && routeNames.length > 0) {
|
|
8045
8045
|
console.debug(
|
|
8046
8046
|
`routingWhile: Removed routes from loading state:`,
|
|
8047
8047
|
routeNames,
|
|
@@ -8058,7 +8058,7 @@ const [
|
|
|
8058
8058
|
removeFromDocumentLoadingActionArraySignal,
|
|
8059
8059
|
] = arraySignal([]);
|
|
8060
8060
|
const workingWhile = (fn, actionNames = []) => {
|
|
8061
|
-
if (DEBUG$
|
|
8061
|
+
if (DEBUG$1 && actionNames.length > 0) {
|
|
8062
8062
|
console.debug(
|
|
8063
8063
|
`workingWhile: Adding actions to loading state:`,
|
|
8064
8064
|
actionNames,
|
|
@@ -8067,7 +8067,7 @@ const workingWhile = (fn, actionNames = []) => {
|
|
|
8067
8067
|
addToDocumentLoadingActionArraySignal(...actionNames);
|
|
8068
8068
|
return executeWithCleanup(fn, () => {
|
|
8069
8069
|
removeFromDocumentLoadingActionArraySignal(...actionNames);
|
|
8070
|
-
if (DEBUG$
|
|
8070
|
+
if (DEBUG$1 && actionNames.length > 0) {
|
|
8071
8071
|
console.debug(
|
|
8072
8072
|
`routingWhile: Removed action from loading state:`,
|
|
8073
8073
|
actionNames,
|
|
@@ -8585,1104 +8585,807 @@ const useUrlSearchParam = (paramName) => {
|
|
|
8585
8585
|
return [value, setSearchParamValue];
|
|
8586
8586
|
};
|
|
8587
8587
|
|
|
8588
|
+
/**
|
|
8589
|
+
* Fix alignment behavior for flex/grid containers that use `height: 100%`.
|
|
8590
|
+
*
|
|
8591
|
+
* Context:
|
|
8592
|
+
* - When a flex/grid container has `height: 100%`, it is "height-locked".
|
|
8593
|
+
* - If its content becomes taller than the container, alignment rules like
|
|
8594
|
+
* `align-items: center` will cause content to be partially clipped.
|
|
8595
|
+
*
|
|
8596
|
+
* Goal:
|
|
8597
|
+
* - Center content only when it fits.
|
|
8598
|
+
* - Align content at start when it overflows.
|
|
8599
|
+
*
|
|
8600
|
+
* How:
|
|
8601
|
+
* - Temporarily remove height-constraint (`height:auto`) to measure natural height.
|
|
8602
|
+
* - Compare natural height to container height.
|
|
8603
|
+
* - Add/remove an attribute so CSS can adapt alignment.
|
|
8604
|
+
*
|
|
8605
|
+
* Usage:
|
|
8606
|
+
* monitorItemsOverflow(containerElement);
|
|
8607
|
+
*
|
|
8608
|
+
* CSS example:
|
|
8609
|
+
* .container { align-items: center; }
|
|
8610
|
+
* .container[data-items-height-overflow] { align-items: flex-start; }
|
|
8611
|
+
*/
|
|
8612
|
+
|
|
8613
|
+
|
|
8614
|
+
const WIDTH_ATTRIBUTE_NAME = "data-items-width-overflow";
|
|
8615
|
+
const HEIGHT_ATTRIBUTE_NAME = "data-items-height-overflow";
|
|
8616
|
+
const monitorItemsOverflow = (container) => {
|
|
8617
|
+
let widthIsOverflowing;
|
|
8618
|
+
let heightIsOverflowing;
|
|
8619
|
+
const onItemsWidthOverflowChange = () => {
|
|
8620
|
+
if (widthIsOverflowing) {
|
|
8621
|
+
container.setAttribute(WIDTH_ATTRIBUTE_NAME, "");
|
|
8622
|
+
} else {
|
|
8623
|
+
container.removeAttribute(WIDTH_ATTRIBUTE_NAME);
|
|
8624
|
+
}
|
|
8625
|
+
};
|
|
8626
|
+
const onItemsHeightOverflowChange = () => {
|
|
8627
|
+
if (heightIsOverflowing) {
|
|
8628
|
+
container.setAttribute(HEIGHT_ATTRIBUTE_NAME, "");
|
|
8629
|
+
} else {
|
|
8630
|
+
container.removeAttribute(HEIGHT_ATTRIBUTE_NAME);
|
|
8631
|
+
}
|
|
8632
|
+
};
|
|
8633
|
+
|
|
8634
|
+
const update = () => {
|
|
8635
|
+
// Save current manual height constraint
|
|
8636
|
+
const prevWidth = container.style.width;
|
|
8637
|
+
const prevHeight = container.style.height;
|
|
8638
|
+
// Remove constraint → get true content dimension
|
|
8639
|
+
container.style.width = "auto";
|
|
8640
|
+
container.style.height = "auto";
|
|
8641
|
+
const naturalWidth = container.scrollWidth;
|
|
8642
|
+
const naturalHeight = container.scrollHeight;
|
|
8643
|
+
if (prevWidth) {
|
|
8644
|
+
container.style.width = prevWidth;
|
|
8645
|
+
} else {
|
|
8646
|
+
container.style.removeProperty("width");
|
|
8647
|
+
}
|
|
8648
|
+
if (prevHeight) {
|
|
8649
|
+
container.style.height = prevHeight;
|
|
8650
|
+
} else {
|
|
8651
|
+
container.style.removeProperty("height");
|
|
8652
|
+
}
|
|
8653
|
+
|
|
8654
|
+
const lockedWidth = container.clientWidth;
|
|
8655
|
+
const lockedHeight = container.clientHeight;
|
|
8656
|
+
const currentWidthIsOverflowing = naturalWidth > lockedWidth;
|
|
8657
|
+
const currentHeightIsOverflowing = naturalHeight > lockedHeight;
|
|
8658
|
+
if (currentWidthIsOverflowing !== widthIsOverflowing) {
|
|
8659
|
+
widthIsOverflowing = currentWidthIsOverflowing;
|
|
8660
|
+
onItemsWidthOverflowChange();
|
|
8661
|
+
}
|
|
8662
|
+
if (currentHeightIsOverflowing !== heightIsOverflowing) {
|
|
8663
|
+
heightIsOverflowing = currentHeightIsOverflowing;
|
|
8664
|
+
onItemsHeightOverflowChange();
|
|
8665
|
+
}
|
|
8666
|
+
};
|
|
8667
|
+
|
|
8668
|
+
const [teardown, addTeardown] = createPubSub();
|
|
8669
|
+
|
|
8670
|
+
update();
|
|
8671
|
+
|
|
8672
|
+
// mutation observer
|
|
8673
|
+
const mutationObserver = new MutationObserver(() => {
|
|
8674
|
+
update();
|
|
8675
|
+
});
|
|
8676
|
+
mutationObserver.observe(container, {
|
|
8677
|
+
childList: true,
|
|
8678
|
+
characterData: true,
|
|
8679
|
+
});
|
|
8680
|
+
addTeardown(() => {
|
|
8681
|
+
mutationObserver.disconnect();
|
|
8682
|
+
});
|
|
8683
|
+
|
|
8684
|
+
// resize observer
|
|
8685
|
+
const resizeObserver = new ResizeObserver(update);
|
|
8686
|
+
resizeObserver.observe(container);
|
|
8687
|
+
addTeardown(() => {
|
|
8688
|
+
resizeObserver.disconnect();
|
|
8689
|
+
});
|
|
8690
|
+
|
|
8691
|
+
const destroy = () => {
|
|
8692
|
+
teardown();
|
|
8693
|
+
container.removeAttribute(WIDTH_ATTRIBUTE_NAME);
|
|
8694
|
+
container.removeAttribute(HEIGHT_ATTRIBUTE_NAME);
|
|
8695
|
+
};
|
|
8696
|
+
return destroy;
|
|
8697
|
+
};
|
|
8698
|
+
|
|
8588
8699
|
installImportMetaCss(import.meta);
|
|
8589
8700
|
import.meta.css = /* css */ `
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
/* Either because the transition slides */
|
|
8593
|
-
/* Or when size transition are disabled because we need to immediatly crop old content when it's bigger than new content */
|
|
8594
|
-
overflow: hidden;
|
|
8701
|
+
* {
|
|
8702
|
+
box-sizing: border-box;
|
|
8595
8703
|
}
|
|
8596
8704
|
|
|
8597
|
-
.
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8705
|
+
.ui_transition {
|
|
8706
|
+
--transition-duration: 300ms;
|
|
8707
|
+
--justify-content: center;
|
|
8708
|
+
--align-items: center;
|
|
8709
|
+
|
|
8710
|
+
--x-transition-duration: var(--transition-duration);
|
|
8711
|
+
--x-justify-content: var(--justify-content);
|
|
8712
|
+
--x-align-items: var(--align-items);
|
|
8713
|
+
|
|
8714
|
+
position: relative;
|
|
8715
|
+
}
|
|
8716
|
+
/* Alignment controls */
|
|
8717
|
+
.ui_transition[data-align-x="start"] {
|
|
8718
|
+
--x-justify-content: flex-start;
|
|
8719
|
+
}
|
|
8720
|
+
.ui_transition[data-align-x="center"] {
|
|
8721
|
+
--x-justify-content: center;
|
|
8722
|
+
}
|
|
8723
|
+
.ui_transition[data-align-x="end"] {
|
|
8724
|
+
--x-justify-content: flex-end;
|
|
8725
|
+
}
|
|
8726
|
+
.ui_transition[data-align-y="start"] {
|
|
8727
|
+
--x-align-items: flex-start;
|
|
8728
|
+
}
|
|
8729
|
+
.ui_transition[data-align-y="center"] {
|
|
8730
|
+
--x-align-items: center;
|
|
8731
|
+
}
|
|
8732
|
+
.ui_transition[data-align-y="end"] {
|
|
8733
|
+
--x-align-items: flex-end;
|
|
8612
8734
|
}
|
|
8613
8735
|
|
|
8614
|
-
.
|
|
8736
|
+
.ui_transition,
|
|
8737
|
+
.active_group,
|
|
8738
|
+
.previous_group,
|
|
8739
|
+
.target_slot,
|
|
8740
|
+
.previous_target_slot,
|
|
8741
|
+
.outgoing_slot,
|
|
8742
|
+
.previous_outgoing_slot {
|
|
8615
8743
|
width: 100%;
|
|
8616
|
-
min-width: fit-content;
|
|
8617
8744
|
height: 100%;
|
|
8618
|
-
min-height: fit-content;
|
|
8619
|
-
flex-direction: column;
|
|
8620
8745
|
}
|
|
8621
8746
|
|
|
8622
|
-
.
|
|
8623
|
-
.
|
|
8624
|
-
|
|
8747
|
+
.target_slot,
|
|
8748
|
+
.outgoing_slot,
|
|
8749
|
+
.previous_target_slot,
|
|
8750
|
+
.previous_outgoing_slot {
|
|
8751
|
+
display: flex;
|
|
8752
|
+
align-items: var(--x-align-items);
|
|
8753
|
+
justify-content: var(--x-justify-content);
|
|
8754
|
+
}
|
|
8755
|
+
.target_slot[data-items-width-overflow],
|
|
8756
|
+
.previous_target_slot[data-items-width-overflow],
|
|
8757
|
+
.previous_target_slot[data-items-width-overflow],
|
|
8758
|
+
.previous_outgoing_slot[data-items-width-overflow] {
|
|
8759
|
+
--x-justify-content: flex-start;
|
|
8760
|
+
}
|
|
8761
|
+
.target_slot[data-items-height-overflow],
|
|
8762
|
+
.previous_slot[data-items-height-overflow],
|
|
8763
|
+
.previous_target_slot[data-items-height-overflow],
|
|
8764
|
+
.previous_outgoing_slot[data-items-height-overflow] {
|
|
8765
|
+
--x-align-items: flex-start;
|
|
8625
8766
|
}
|
|
8626
8767
|
|
|
8627
|
-
.
|
|
8628
|
-
|
|
8768
|
+
.active_group {
|
|
8769
|
+
position: relative;
|
|
8770
|
+
}
|
|
8771
|
+
.target_slot {
|
|
8772
|
+
position: relative;
|
|
8773
|
+
}
|
|
8774
|
+
.outgoing_slot,
|
|
8775
|
+
.previous_outgoing_slot {
|
|
8776
|
+
position: absolute;
|
|
8777
|
+
top: 0;
|
|
8778
|
+
left: 0;
|
|
8779
|
+
}
|
|
8780
|
+
.previous_group {
|
|
8629
8781
|
position: absolute;
|
|
8630
8782
|
inset: 0;
|
|
8783
|
+
}
|
|
8784
|
+
.ui_transition[data-only-previous-group] .previous_group {
|
|
8785
|
+
position: relative;
|
|
8786
|
+
}
|
|
8787
|
+
|
|
8788
|
+
.target_slot_background {
|
|
8789
|
+
position: absolute;
|
|
8790
|
+
top: 0;
|
|
8791
|
+
left: 0;
|
|
8792
|
+
z-index: -1;
|
|
8793
|
+
display: none;
|
|
8794
|
+
width: var(--target-slot-width, 100%);
|
|
8795
|
+
height: var(--target-slot-height, 100%);
|
|
8796
|
+
background: var(--target-slot-background, transparent);
|
|
8631
8797
|
pointer-events: none;
|
|
8632
8798
|
}
|
|
8799
|
+
.ui_transition[data-transitioning] .target_slot_background {
|
|
8800
|
+
display: block;
|
|
8801
|
+
}
|
|
8633
8802
|
`;
|
|
8634
8803
|
|
|
8635
|
-
const
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8804
|
+
const CONTENT_ID_ATTRIBUTE = "data-content-id";
|
|
8805
|
+
const CONTENT_PHASE_ATTRIBUTE = "data-content-phase";
|
|
8806
|
+
const UNSET = {
|
|
8807
|
+
domNodes: [],
|
|
8808
|
+
domNodesClone: [],
|
|
8809
|
+
isEmpty: true,
|
|
8641
8810
|
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8811
|
+
type: "unset",
|
|
8812
|
+
contentId: "unset",
|
|
8813
|
+
contentPhase: undefined,
|
|
8814
|
+
isContentPhase: false,
|
|
8815
|
+
isContent: false,
|
|
8816
|
+
toString: () => "unset",
|
|
8817
|
+
};
|
|
8648
8818
|
|
|
8649
|
-
const
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
return { cleanup: () => {} };
|
|
8653
|
-
}
|
|
8819
|
+
const isSameConfiguration = (configA, configB) => {
|
|
8820
|
+
return configA.toString() === configB.toString();
|
|
8821
|
+
};
|
|
8654
8822
|
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8823
|
+
const createUITransitionController = (
|
|
8824
|
+
root,
|
|
8825
|
+
{
|
|
8826
|
+
duration = 300,
|
|
8827
|
+
alignX = "center",
|
|
8828
|
+
alignY = "center",
|
|
8829
|
+
onStateChange = () => {},
|
|
8830
|
+
pauseBreakpoints = [],
|
|
8831
|
+
} = {},
|
|
8832
|
+
) => {
|
|
8833
|
+
const debugConfig = {
|
|
8834
|
+
detection: root.hasAttribute("data-debug-detection"),
|
|
8835
|
+
size: root.hasAttribute("data-debug-size"),
|
|
8660
8836
|
};
|
|
8661
|
-
const
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
)
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
console.debug(`[${type}]`, ...args);
|
|
8670
|
-
}
|
|
8837
|
+
const hasDebugLogs = debugConfig.size;
|
|
8838
|
+
const debugDetection = (message) => {
|
|
8839
|
+
if (!debugConfig.detection) return;
|
|
8840
|
+
console.debug(`[detection]`, message);
|
|
8841
|
+
};
|
|
8842
|
+
const debugSize = (message) => {
|
|
8843
|
+
if (!debugConfig.size) return;
|
|
8844
|
+
console.debug(`[size]`, message);
|
|
8671
8845
|
};
|
|
8672
8846
|
|
|
8673
|
-
const
|
|
8674
|
-
const
|
|
8675
|
-
const
|
|
8676
|
-
|
|
8847
|
+
const activeGroup = root.querySelector(".active_group");
|
|
8848
|
+
const targetSlot = root.querySelector(".target_slot");
|
|
8849
|
+
const outgoingSlot = root.querySelector(".outgoing_slot");
|
|
8850
|
+
const previousGroup = root.querySelector(".previous_group");
|
|
8851
|
+
const previousTargetSlot = previousGroup?.querySelector(
|
|
8852
|
+
".previous_target_slot",
|
|
8677
8853
|
);
|
|
8678
|
-
const
|
|
8679
|
-
".
|
|
8854
|
+
const previousOutgoingSlot = previousGroup?.querySelector(
|
|
8855
|
+
".previous_outgoing_slot",
|
|
8680
8856
|
);
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8857
|
+
|
|
8858
|
+
if (
|
|
8859
|
+
!root ||
|
|
8860
|
+
!activeGroup ||
|
|
8861
|
+
!targetSlot ||
|
|
8862
|
+
!outgoingSlot ||
|
|
8863
|
+
!previousGroup ||
|
|
8864
|
+
!previousTargetSlot ||
|
|
8865
|
+
!previousOutgoingSlot
|
|
8866
|
+
) {
|
|
8867
|
+
throw new Error(
|
|
8868
|
+
"createUITransitionController requires element with .active_group, .target_slot, .outgoing_slot, .previous_group, .previous_target_slot, and .previous_outgoing_slot elements",
|
|
8869
|
+
);
|
|
8684
8870
|
}
|
|
8685
8871
|
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
);
|
|
8692
|
-
const transitionController = createGroupTransitionController();
|
|
8693
|
-
const setupTransition = ({
|
|
8694
|
-
isPhaseTransition = false,
|
|
8695
|
-
overlay,
|
|
8696
|
-
needsOldChildNodesClone,
|
|
8697
|
-
previousChildNodes,
|
|
8698
|
-
childNodes,
|
|
8699
|
-
slotInfo,
|
|
8700
|
-
attributeToRemove = [],
|
|
8701
|
-
}) => {
|
|
8702
|
-
let cleanup = () => {};
|
|
8703
|
-
let elementToImpact;
|
|
8872
|
+
// we maintain a background copy behind target slot to avoid showing
|
|
8873
|
+
// the body flashing during the fade-in
|
|
8874
|
+
const targetSlotBackground = document.createElement("div");
|
|
8875
|
+
targetSlotBackground.className = "target_slot_background";
|
|
8876
|
+
activeGroup.insertBefore(targetSlotBackground, targetSlot);
|
|
8704
8877
|
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
if (!debugClones) {
|
|
8709
|
-
overlay.innerHTML = "";
|
|
8710
|
-
}
|
|
8711
|
-
};
|
|
8712
|
-
debug(
|
|
8713
|
-
"content",
|
|
8714
|
-
`Continuing from current ${isPhaseTransition ? "phase" : "content"} transition element`,
|
|
8715
|
-
);
|
|
8716
|
-
} else if (needsOldChildNodesClone) {
|
|
8717
|
-
overlay.innerHTML = "";
|
|
8718
|
-
for (const previousChildNode of previousChildNodes) {
|
|
8719
|
-
const previousChildClone = previousChildNode.cloneNode(true);
|
|
8720
|
-
if (previousChildClone.nodeType !== Node.TEXT_NODE) {
|
|
8721
|
-
for (const attrToRemove of attributeToRemove) {
|
|
8722
|
-
previousChildClone.removeAttribute(attrToRemove);
|
|
8723
|
-
}
|
|
8724
|
-
previousChildClone.setAttribute("data-ui-transition-clone", "");
|
|
8725
|
-
}
|
|
8726
|
-
overlay.appendChild(previousChildClone);
|
|
8727
|
-
}
|
|
8728
|
-
elementToImpact = overlay;
|
|
8729
|
-
cleanup = () => {
|
|
8730
|
-
if (!debugClones) {
|
|
8731
|
-
overlay.innerHTML = "";
|
|
8732
|
-
}
|
|
8733
|
-
};
|
|
8734
|
-
debug(
|
|
8735
|
-
"content",
|
|
8736
|
-
`Cloned previous child for ${isPhaseTransition ? "phase" : "content"} transition:`,
|
|
8737
|
-
getElementSignature(previousChildNodes),
|
|
8738
|
-
);
|
|
8739
|
-
if (debugBreakAfterClone === slotInfo.contentKey) {
|
|
8740
|
-
debugger;
|
|
8741
|
-
}
|
|
8742
|
-
} else {
|
|
8743
|
-
overlay.innerHTML = "";
|
|
8744
|
-
debug(
|
|
8745
|
-
"content",
|
|
8746
|
-
`No old child to clone for ${isPhaseTransition ? "phase" : "content"} transition`,
|
|
8747
|
-
);
|
|
8748
|
-
}
|
|
8878
|
+
root.style.setProperty("--x-transition-duration", `${duration}ms`);
|
|
8879
|
+
outgoingSlot.setAttribute("inert", "");
|
|
8880
|
+
previousGroup.setAttribute("inert", "");
|
|
8749
8881
|
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
let newElement;
|
|
8755
|
-
if (isPhaseTransition) {
|
|
8756
|
-
// Phase transitions work on individual elements
|
|
8757
|
-
oldElement = elementToImpact;
|
|
8758
|
-
newElement = slot;
|
|
8759
|
-
} else {
|
|
8760
|
-
// Content transitions work at container level and can outlive content phase changes
|
|
8761
|
-
oldElement = previousChildNodes.length ? elementToImpact : null;
|
|
8762
|
-
newElement = childNodes.length ? slot : null;
|
|
8882
|
+
const detectConfiguration = (slot, { contentId, contentPhase } = {}) => {
|
|
8883
|
+
const domNodes = Array.from(slot.childNodes);
|
|
8884
|
+
if (!domNodes) {
|
|
8885
|
+
return UNSET;
|
|
8763
8886
|
}
|
|
8764
8887
|
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
const [publishResume, addResumeCallback] = createPubSub();
|
|
8774
|
-
|
|
8775
|
-
const [publishChange, subscribeChange] = createPubSub();
|
|
8776
|
-
let triggerChildSlotMutation;
|
|
8777
|
-
let previousSlotInfo;
|
|
8778
|
-
let slotInfo;
|
|
8779
|
-
let changeInfo;
|
|
8780
|
-
{
|
|
8781
|
-
const createSlotInfo = (childNodes, { contentKey, contentPhase }) => {
|
|
8782
|
-
const hasChild = childNodes.length > 0;
|
|
8783
|
-
let contentKeyFormatted;
|
|
8784
|
-
let contentName;
|
|
8785
|
-
if (hasChild) {
|
|
8786
|
-
if (contentKey) {
|
|
8787
|
-
contentKeyFormatted = `[data-content-key="${contentKey}"]`;
|
|
8788
|
-
} else {
|
|
8789
|
-
let onlyTextNodes = true;
|
|
8790
|
-
for (const child of childNodes) {
|
|
8791
|
-
if (child.nodeType !== Node.TEXT_NODE) {
|
|
8792
|
-
onlyTextNodes = false;
|
|
8793
|
-
break;
|
|
8794
|
-
}
|
|
8795
|
-
}
|
|
8796
|
-
contentKeyFormatted = onlyTextNodes ? "[text]" : "[unkeyed]";
|
|
8797
|
-
}
|
|
8798
|
-
contentName = contentPhase ? "content-phase" : "content";
|
|
8799
|
-
} else {
|
|
8800
|
-
contentKeyFormatted = "[empty]";
|
|
8801
|
-
contentName = "null";
|
|
8802
|
-
}
|
|
8803
|
-
|
|
8804
|
-
return {
|
|
8805
|
-
childNodes,
|
|
8806
|
-
contentKey,
|
|
8807
|
-
contentPhase,
|
|
8808
|
-
|
|
8809
|
-
hasChild: childNodes.length > 0,
|
|
8810
|
-
contentKeyFormatted,
|
|
8811
|
-
isContentPhase: Boolean(contentPhase),
|
|
8812
|
-
contentName,
|
|
8813
|
-
};
|
|
8814
|
-
};
|
|
8815
|
-
previousSlotInfo = createSlotInfo([], {
|
|
8816
|
-
contentKey: undefined,
|
|
8817
|
-
contentPhase: undefined,
|
|
8818
|
-
});
|
|
8819
|
-
slotInfo = previousSlotInfo;
|
|
8820
|
-
let isUpdating = false;
|
|
8821
|
-
triggerChildSlotMutation = (reason) => {
|
|
8822
|
-
if (isUpdating) {
|
|
8823
|
-
debug("detection", "Preventing recursive update");
|
|
8824
|
-
return;
|
|
8888
|
+
const isEmpty = domNodes.length === 0;
|
|
8889
|
+
let textNodeCount = 0;
|
|
8890
|
+
let elementNodeCount = 0;
|
|
8891
|
+
let firstElementNode;
|
|
8892
|
+
const domNodesClone = [];
|
|
8893
|
+
if (isEmpty) {
|
|
8894
|
+
if (contentPhase === undefined) {
|
|
8895
|
+
contentPhase = "empty";
|
|
8825
8896
|
}
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
: childNodes.length === 1
|
|
8833
|
-
? getElementSignature(childNodes[0])
|
|
8834
|
-
: getElementSignature(slot);
|
|
8835
|
-
console.group(`UI Update: ${updateLabel} (reason: ${reason})`);
|
|
8836
|
-
}
|
|
8837
|
-
updateSlotChangeInfo(childNodes, reason);
|
|
8838
|
-
if (changeInfo.isStateChangeOnly) {
|
|
8897
|
+
} else {
|
|
8898
|
+
const contentIdSlotAttr = slot.getAttribute(CONTENT_ID_ATTRIBUTE);
|
|
8899
|
+
let contentIdChildAttr;
|
|
8900
|
+
for (const domNode of domNodes) {
|
|
8901
|
+
if (domNode.nodeType === Node.TEXT_NODE) {
|
|
8902
|
+
textNodeCount++;
|
|
8839
8903
|
} else {
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
if (
|
|
8843
|
-
changeInfo.isInitialPopulationWithoutTransition ||
|
|
8844
|
-
changeInfo.becomesPopulated
|
|
8845
|
-
) {
|
|
8846
|
-
hasPopulatedOnce = true;
|
|
8904
|
+
if (!firstElementNode) {
|
|
8905
|
+
firstElementNode = domNode;
|
|
8847
8906
|
}
|
|
8848
|
-
|
|
8849
|
-
} finally {
|
|
8850
|
-
isUpdating = false;
|
|
8851
|
-
if (hasSomeDebugLogs) {
|
|
8852
|
-
console.groupEnd();
|
|
8853
|
-
}
|
|
8854
|
-
}
|
|
8855
|
-
};
|
|
8907
|
+
elementNodeCount++;
|
|
8856
8908
|
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
let contentPhase;
|
|
8861
|
-
if (currentChildNodes.length === 0) {
|
|
8862
|
-
contentPhase = true; // empty treated as phase
|
|
8863
|
-
} else {
|
|
8864
|
-
for (const childNode of currentChildNodes) {
|
|
8865
|
-
if (childNode.nodeType === Node.TEXT_NODE) {
|
|
8866
|
-
continue;
|
|
8867
|
-
}
|
|
8868
|
-
if (childNode.hasAttribute("data-content-phase")) {
|
|
8869
|
-
const contentPhaseAttr =
|
|
8870
|
-
childNode.getAttribute("data-content-phase");
|
|
8871
|
-
contentPhase = contentPhaseAttr || true;
|
|
8909
|
+
if (domNode.hasAttribute("data-content-phase")) {
|
|
8910
|
+
const contentPhaseAttr = domNode.getAttribute("data-content-phase");
|
|
8911
|
+
contentPhase = contentPhaseAttr || "attr";
|
|
8872
8912
|
}
|
|
8873
|
-
if (
|
|
8874
|
-
|
|
8913
|
+
if (domNode.hasAttribute("data-content-key")) {
|
|
8914
|
+
contentIdChildAttr = domNode.getAttribute("data-content-key");
|
|
8875
8915
|
}
|
|
8876
8916
|
}
|
|
8917
|
+
const domNodeClone = domNode.cloneNode(true);
|
|
8918
|
+
domNodesClone.push(domNodeClone);
|
|
8877
8919
|
}
|
|
8878
|
-
|
|
8879
|
-
if (
|
|
8920
|
+
|
|
8921
|
+
if (contentIdSlotAttr && contentIdChildAttr) {
|
|
8880
8922
|
console.warn(
|
|
8881
|
-
`Slot and slot child both have a [
|
|
8923
|
+
`Slot and slot child both have a [${CONTENT_ID_ATTRIBUTE}]. Slot is ${contentIdSlotAttr} and child is ${contentIdChildAttr}, using the child.`,
|
|
8882
8924
|
);
|
|
8883
8925
|
}
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8926
|
+
if (contentId === undefined) {
|
|
8927
|
+
contentId = contentIdChildAttr || contentIdSlotAttr || undefined;
|
|
8928
|
+
}
|
|
8929
|
+
}
|
|
8930
|
+
const isOnlyTextNodes = elementNodeCount === 0 && textNodeCount > 1;
|
|
8931
|
+
const singleElementNode = elementNodeCount === 1 ? firstElementNode : null;
|
|
8932
|
+
|
|
8933
|
+
contentId = contentId || getElementSignature(domNodes[0]);
|
|
8934
|
+
if (!contentPhase && isEmpty) {
|
|
8935
|
+
// Imagine code rendering null while switching to a new content
|
|
8936
|
+
// or even while staying on the same content.
|
|
8937
|
+
// In the UI we want to consider this as an "empty" phase.
|
|
8938
|
+
// meaning the ui will keep the same size until something else happens
|
|
8939
|
+
// This prevent layout shifts of code not properly handling
|
|
8940
|
+
// intermediate states.
|
|
8941
|
+
contentPhase = "empty";
|
|
8942
|
+
}
|
|
8943
|
+
|
|
8944
|
+
let width;
|
|
8945
|
+
let height;
|
|
8946
|
+
let borderRadius;
|
|
8947
|
+
let border;
|
|
8948
|
+
let background;
|
|
8949
|
+
|
|
8950
|
+
if (isEmpty) {
|
|
8951
|
+
debugSize(`measureSlot(".${slot.className}") -> it is empty`);
|
|
8952
|
+
} else if (singleElementNode) {
|
|
8953
|
+
const rect = singleElementNode.getBoundingClientRect();
|
|
8954
|
+
width = rect.width;
|
|
8955
|
+
height = rect.height;
|
|
8956
|
+
debugSize(`measureSlot(".${slot.className}") -> [${width}x${height}]`);
|
|
8957
|
+
borderRadius = getBorderRadius(singleElementNode);
|
|
8958
|
+
border = getComputedStyle(singleElementNode).border;
|
|
8959
|
+
background = getBackground(singleElementNode);
|
|
8960
|
+
} else {
|
|
8961
|
+
// text, multiple elements
|
|
8962
|
+
const rect = slot.getBoundingClientRect();
|
|
8963
|
+
width = rect.width;
|
|
8964
|
+
height = rect.height;
|
|
8965
|
+
debugSize(`measureSlot(".${slot.className}") -> [${width}x${height}]`);
|
|
8966
|
+
}
|
|
8889
8967
|
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
!shouldDoPhaseTransition &&
|
|
8915
|
-
!becomesPopulated &&
|
|
8916
|
-
!becomesEmpty;
|
|
8917
|
-
const shouldDoContentTransitionIncludingPopulation =
|
|
8918
|
-
shouldDoContentTransition ||
|
|
8919
|
-
(becomesPopulated && !shouldDoPhaseTransition);
|
|
8920
|
-
// nothing to transition if no previous and no current child
|
|
8921
|
-
// (Either it's the initial call or just content-key changes but there is no child yet)
|
|
8922
|
-
const isStateChangeOnly = !hadChild && !hasChild;
|
|
8923
|
-
if (isStateChangeOnly) {
|
|
8924
|
-
const prevKey = previousSlotInfo.contentKey;
|
|
8925
|
-
const keyIsTheSame = prevKey === contentKey;
|
|
8926
|
-
if (keyIsTheSame) {
|
|
8927
|
-
debug(
|
|
8928
|
-
"detection",
|
|
8929
|
-
`Childless change: no changes found -> do nothing and skip transitions`,
|
|
8930
|
-
);
|
|
8931
|
-
} else if (!prevKey && contentKey) {
|
|
8932
|
-
debug(
|
|
8933
|
-
"detection",
|
|
8934
|
-
`Childless change: ${contentKey} added -> registering it and skip transitions`,
|
|
8935
|
-
);
|
|
8936
|
-
} else if (prevKey && !contentKey) {
|
|
8937
|
-
debug(
|
|
8938
|
-
"detection",
|
|
8939
|
-
`Childless change: ${contentKey} removed -> registering it and skip transitions`,
|
|
8940
|
-
);
|
|
8941
|
-
} else {
|
|
8942
|
-
debug(
|
|
8943
|
-
"detection",
|
|
8944
|
-
`Childless change: content key updated from ${prevKey} to ${contentKey} -> registering it and skip transitions`,
|
|
8945
|
-
);
|
|
8946
|
-
}
|
|
8947
|
-
} else if (isInitialPopulationWithoutTransition) {
|
|
8948
|
-
debug(
|
|
8949
|
-
"detection",
|
|
8950
|
-
"Initial population detected -> skipping transitions (opt-in with [data-initial-transition])",
|
|
8951
|
-
);
|
|
8952
|
-
} else if (previousSlotInfo.contentKey !== slotInfo.contentKey) {
|
|
8953
|
-
let contentKeysSentence = `Content key: ${previousSlotInfo.contentKeyFormatted} → ${slotInfo.contentKeyFormatted}`;
|
|
8954
|
-
debug("detection", contentKeysSentence);
|
|
8955
|
-
} else if (previousSlotInfo.contentPhase !== slotInfo.contentPhase) {
|
|
8956
|
-
let contentPhasesSentence =
|
|
8957
|
-
slotInfo.contentPhase && previousSlotInfo.contentPhase
|
|
8958
|
-
? `Content phase: ${previousSlotInfo.contentPhase} → ${slotInfo.contentPhase}`
|
|
8959
|
-
: previousSlotInfo.contentPhase
|
|
8960
|
-
? `becomes content (content phase becomes undefined)`
|
|
8961
|
-
: `content phase becomes ${slotInfo.contentPhase}`;
|
|
8962
|
-
debug("detection", contentPhasesSentence);
|
|
8963
|
-
}
|
|
8964
|
-
|
|
8965
|
-
changeInfo = {
|
|
8966
|
-
reason,
|
|
8967
|
-
previousSlotInfo,
|
|
8968
|
-
becomesEmpty,
|
|
8969
|
-
becomesPopulated,
|
|
8970
|
-
isInitialPopulationWithoutTransition,
|
|
8971
|
-
shouldDoContentTransition,
|
|
8972
|
-
shouldDoPhaseTransition,
|
|
8973
|
-
contentChange,
|
|
8974
|
-
phaseChange,
|
|
8975
|
-
isTransitionLess,
|
|
8976
|
-
shouldDoContentTransitionIncludingPopulation,
|
|
8977
|
-
isStateChangeOnly,
|
|
8968
|
+
const commonProperties = {
|
|
8969
|
+
domNodes,
|
|
8970
|
+
domNodesClone,
|
|
8971
|
+
isEmpty,
|
|
8972
|
+
isOnlyTextNodes,
|
|
8973
|
+
singleElementNode,
|
|
8974
|
+
|
|
8975
|
+
width,
|
|
8976
|
+
height,
|
|
8977
|
+
borderRadius,
|
|
8978
|
+
border,
|
|
8979
|
+
background,
|
|
8980
|
+
|
|
8981
|
+
contentId,
|
|
8982
|
+
};
|
|
8983
|
+
|
|
8984
|
+
if (contentPhase) {
|
|
8985
|
+
return {
|
|
8986
|
+
...commonProperties,
|
|
8987
|
+
type: "content_phase",
|
|
8988
|
+
contentPhase,
|
|
8989
|
+
isContentPhase: true,
|
|
8990
|
+
isContent: false,
|
|
8991
|
+
toString: () => `content(${contentId}).phase(${contentPhase})`,
|
|
8978
8992
|
};
|
|
8993
|
+
}
|
|
8994
|
+
return {
|
|
8995
|
+
...commonProperties,
|
|
8996
|
+
type: "content",
|
|
8997
|
+
contentPhase: undefined,
|
|
8998
|
+
isContentPhase: false,
|
|
8999
|
+
isContent: true,
|
|
9000
|
+
toString: () => `content(${contentId})`,
|
|
8979
9001
|
};
|
|
8980
|
-
}
|
|
9002
|
+
};
|
|
8981
9003
|
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
let sizeTransition = null;
|
|
9004
|
+
const targetSlotInitialConfiguration = detectConfiguration(targetSlot);
|
|
9005
|
+
const outgoingSlotInitialConfiguration = detectConfiguration(outgoingSlot, {
|
|
9006
|
+
contentPhase: "true",
|
|
9007
|
+
});
|
|
9008
|
+
let targetSlotConfiguration = targetSlotInitialConfiguration;
|
|
9009
|
+
let outgoingSlotConfiguration = outgoingSlotInitialConfiguration;
|
|
9010
|
+
let previousTargetSlotConfiguration = UNSET;
|
|
8990
9011
|
|
|
8991
|
-
|
|
8992
|
-
{
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9012
|
+
const updateSlotAttributes = () => {
|
|
9013
|
+
if (targetSlotConfiguration.isEmpty && outgoingSlotConfiguration.isEmpty) {
|
|
9014
|
+
root.setAttribute("data-only-previous-group", "");
|
|
9015
|
+
} else {
|
|
9016
|
+
root.removeAttribute("data-only-previous-group");
|
|
9017
|
+
}
|
|
9018
|
+
};
|
|
9019
|
+
const updateAlignment = () => {
|
|
9020
|
+
// Set data attributes for CSS-based alignment
|
|
9021
|
+
root.setAttribute("data-align-x", alignX);
|
|
9022
|
+
root.setAttribute("data-align-y", alignY);
|
|
9023
|
+
};
|
|
9024
|
+
|
|
9025
|
+
const moveConfigurationIntoSlot = (configuration, slot) => {
|
|
9026
|
+
slot.innerHTML = "";
|
|
9027
|
+
for (const domNode of configuration.domNodesClone) {
|
|
9028
|
+
slot.appendChild(domNode);
|
|
9029
|
+
}
|
|
9030
|
+
// in case border or stuff like that have changed we re-detect the config
|
|
9031
|
+
const updatedConfig = detectConfiguration(slot);
|
|
9032
|
+
if (slot === targetSlot) {
|
|
9033
|
+
targetSlotConfiguration = updatedConfig;
|
|
9034
|
+
} else if (slot === outgoingSlot) {
|
|
9035
|
+
outgoingSlotConfiguration = updatedConfig;
|
|
9036
|
+
} else if (slot === previousTargetSlot) {
|
|
9037
|
+
previousTargetSlotConfiguration = updatedConfig;
|
|
9038
|
+
} else if (slot === previousOutgoingSlot) ; else {
|
|
9039
|
+
throw new Error("Unknown slot for applyConfiguration");
|
|
9040
|
+
}
|
|
9041
|
+
};
|
|
9042
|
+
|
|
9043
|
+
updateAlignment();
|
|
9044
|
+
|
|
9045
|
+
let transitionType = "none";
|
|
9046
|
+
const groupTransitionOptions = {
|
|
9047
|
+
// debugBreakpoints: [0.25],
|
|
9048
|
+
pauseBreakpoints,
|
|
9049
|
+
lifecycle: {
|
|
9050
|
+
setup: () => {
|
|
9051
|
+
updateSlotAttributes();
|
|
9052
|
+
root.setAttribute("data-transitioning", "");
|
|
9053
|
+
onStateChange({ isTransitioning: true });
|
|
9054
|
+
return {
|
|
9055
|
+
teardown: () => {
|
|
9056
|
+
root.removeAttribute("data-transitioning");
|
|
9057
|
+
updateSlotAttributes(); // Update positioning after transition
|
|
9058
|
+
onStateChange({ isTransitioning: false });
|
|
9059
|
+
},
|
|
9037
9060
|
};
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9061
|
+
},
|
|
9062
|
+
},
|
|
9063
|
+
};
|
|
9064
|
+
const transitionController = createGroupTransitionController(
|
|
9065
|
+
groupTransitionOptions,
|
|
9066
|
+
);
|
|
9067
|
+
|
|
9068
|
+
const elementToClip = root;
|
|
9069
|
+
const morphContainerIntoTarget = () => {
|
|
9070
|
+
const morphTransitions = [];
|
|
9071
|
+
{
|
|
9072
|
+
// TODO: ideally when scrollContainer is document AND we transition
|
|
9073
|
+
// from a layout with scrollbar to a layout without
|
|
9074
|
+
// we have clip path detecting we go from a given width/height to a new width/height
|
|
9075
|
+
// that might just be the result of scrollbar appearing/disappearing
|
|
9076
|
+
// we should detect when this happens to avoid clipping what correspond to the scrollbar presence toggling
|
|
9077
|
+
const fromWidth = previousTargetSlotConfiguration.width || 0;
|
|
9078
|
+
const fromHeight = previousTargetSlotConfiguration.height || 0;
|
|
9079
|
+
const toWidth = targetSlotConfiguration.width || 0;
|
|
9080
|
+
const toHeight = targetSlotConfiguration.height || 0;
|
|
9081
|
+
debugSize(
|
|
9082
|
+
`transition from [${fromWidth}x${fromHeight}] to [${toWidth}x${toHeight}]`,
|
|
9083
|
+
);
|
|
9084
|
+
const restoreOverflow = preventIntermediateScrollbar(root, {
|
|
9085
|
+
fromWidth,
|
|
9086
|
+
fromHeight,
|
|
9087
|
+
toWidth,
|
|
9088
|
+
toHeight,
|
|
9089
|
+
onPrevent: ({ x, y, scrollContainer }) => {
|
|
9090
|
+
if (x) {
|
|
9091
|
+
debugSize(
|
|
9092
|
+
`Temporarily hiding horizontal overflow during transition on ${getElementSignature(scrollContainer)}`,
|
|
9056
9093
|
);
|
|
9057
|
-
return;
|
|
9058
9094
|
}
|
|
9059
|
-
if (
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
Array.from(pauseReasonSet).join(", ") ||
|
|
9063
|
-
"wait next frame to resume";
|
|
9064
|
-
debug(
|
|
9065
|
-
"size",
|
|
9066
|
-
`[resize observer] size change ignore (${pauseReason})`,
|
|
9095
|
+
if (y) {
|
|
9096
|
+
debugSize(
|
|
9097
|
+
`Temporarily hiding vertical overflow during transition on ${getElementSignature(scrollContainer)}`,
|
|
9067
9098
|
);
|
|
9068
|
-
return;
|
|
9069
|
-
}
|
|
9070
|
-
if (localDebug.size) {
|
|
9071
|
-
console.group("[resize observer] size change detected");
|
|
9072
9099
|
}
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
}
|
|
9078
|
-
requestAnimationFrame(() => {
|
|
9079
|
-
isWithinResizeObserverTick = false;
|
|
9080
|
-
});
|
|
9081
|
-
});
|
|
9082
|
-
resizeObserver.observe(slot);
|
|
9083
|
-
};
|
|
9084
|
-
startResizeObserver();
|
|
9085
|
-
addTeardown(() => {
|
|
9086
|
-
stopResizeObserver();
|
|
9100
|
+
},
|
|
9101
|
+
onRestore: () => {
|
|
9102
|
+
debugSize(`Restored overflow after transition`);
|
|
9103
|
+
},
|
|
9087
9104
|
});
|
|
9088
|
-
}
|
|
9089
9105
|
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
getHeightWithoutTransition(slot),
|
|
9094
|
-
];
|
|
9095
|
-
};
|
|
9096
|
-
const syncContentDimensions = () => {
|
|
9097
|
-
// check content dimensions to see if they changed and sync them
|
|
9098
|
-
const [currentWidth, currentHeight] = measureSlotSize();
|
|
9099
|
-
if (!slotInfo.isContentPhase) {
|
|
9100
|
-
updateNaturalContentSize(currentWidth, currentHeight);
|
|
9101
|
-
}
|
|
9102
|
-
if (sizeTransition) {
|
|
9103
|
-
updateToSize(currentWidth, currentHeight);
|
|
9104
|
-
} else {
|
|
9105
|
-
constrainedWidth = currentWidth;
|
|
9106
|
-
constrainedHeight = currentHeight;
|
|
9107
|
-
}
|
|
9108
|
-
};
|
|
9109
|
-
const applySizeConstraintsUntil = (width, height, reason) => {
|
|
9110
|
-
// we want to pause either because we have a diff and don't want to trigger the resize observer
|
|
9111
|
-
// or if we have no diff because we're about to do something that would trigger it (transition)
|
|
9112
|
-
const resumeResizeObserver = pauseResizeObserver(reason);
|
|
9113
|
-
debug("size", `Applying size constraints (${reason})`, {
|
|
9114
|
-
width: `${constrainedWidth} → ${width}`,
|
|
9115
|
-
height: `${constrainedHeight} → ${height}`,
|
|
9116
|
-
});
|
|
9117
|
-
outerWrapper.style.width = `${width}px`;
|
|
9118
|
-
outerWrapper.style.height = `${height}px`;
|
|
9119
|
-
constrainedWidth = width;
|
|
9120
|
-
constrainedHeight = height;
|
|
9121
|
-
// force content overlay to take the right size
|
|
9122
|
-
// (this way the content clone is not distorted by the new content size)
|
|
9123
|
-
contentOverlay.style.width = `${width}px`;
|
|
9124
|
-
contentOverlay.style.height = `${height}px`;
|
|
9125
|
-
const release = (reason) => {
|
|
9126
|
-
releaseSizeConstraints(reason);
|
|
9127
|
-
resumeResizeObserver(reason);
|
|
9106
|
+
const onSizeTransitionFinished = () => {
|
|
9107
|
+
// Restore overflow when transition is complete
|
|
9108
|
+
restoreOverflow();
|
|
9128
9109
|
};
|
|
9129
|
-
|
|
9130
|
-
|
|
9110
|
+
|
|
9111
|
+
// https://emilkowal.ski/ui/the-magic-of-clip-path
|
|
9112
|
+
const elementToClipRect = elementToClip.getBoundingClientRect();
|
|
9113
|
+
const elementToClipWidth = elementToClipRect.width;
|
|
9114
|
+
const elementToClipHeight = elementToClipRect.height;
|
|
9115
|
+
// Calculate where content is positioned within the large container
|
|
9116
|
+
const getAlignedPosition = (containerSize, contentSize, align) => {
|
|
9117
|
+
switch (align) {
|
|
9118
|
+
case "start":
|
|
9119
|
+
return 0;
|
|
9120
|
+
case "end":
|
|
9121
|
+
return containerSize - contentSize;
|
|
9122
|
+
case "center":
|
|
9123
|
+
default:
|
|
9124
|
+
return (containerSize - contentSize) / 2;
|
|
9125
|
+
}
|
|
9131
9126
|
};
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
const releaseSizeConstraints = (reason) => {
|
|
9138
|
-
if (slotInfo.isContentPhase) {
|
|
9139
|
-
return;
|
|
9140
|
-
}
|
|
9141
|
-
debug("size", `Releasing constraints (${reason})`);
|
|
9142
|
-
const [beforeWidth, beforeHeight] = measureSlotSize();
|
|
9143
|
-
outerWrapper.style.width = "";
|
|
9144
|
-
outerWrapper.style.height = "";
|
|
9145
|
-
const [afterWidth, afterHeight] = measureSlotSize();
|
|
9146
|
-
debug("size", "Size after release:", {
|
|
9147
|
-
width: `${beforeWidth} → ${afterWidth}`,
|
|
9148
|
-
height: `${beforeHeight} → ${afterHeight}`,
|
|
9149
|
-
});
|
|
9150
|
-
updateNaturalContentSize(afterWidth, afterHeight);
|
|
9151
|
-
constrainedWidth = afterWidth;
|
|
9152
|
-
constrainedHeight = afterHeight;
|
|
9153
|
-
contentOverlay.style.width = ``;
|
|
9154
|
-
contentOverlay.style.height = ``;
|
|
9155
|
-
};
|
|
9156
|
-
const updateToSize = (targetWidth, targetHeight) => {
|
|
9157
|
-
if (
|
|
9158
|
-
constrainedWidth === targetWidth &&
|
|
9159
|
-
constrainedHeight === targetHeight
|
|
9160
|
-
) {
|
|
9161
|
-
return;
|
|
9162
|
-
}
|
|
9163
|
-
if (!hasSizeTransitions) {
|
|
9164
|
-
applySizeConstraints(
|
|
9165
|
-
targetWidth,
|
|
9166
|
-
targetHeight,
|
|
9167
|
-
"size update without transition",
|
|
9168
|
-
);
|
|
9169
|
-
return;
|
|
9170
|
-
}
|
|
9171
|
-
const widthDiff = Math.abs(targetWidth - constrainedWidth);
|
|
9172
|
-
const heightDiff = Math.abs(targetHeight - constrainedHeight);
|
|
9173
|
-
if (widthDiff <= SIZE_DIFF_EPSILON && heightDiff <= SIZE_DIFF_EPSILON) {
|
|
9174
|
-
applySizeConstraints(
|
|
9175
|
-
targetWidth,
|
|
9176
|
-
targetHeight,
|
|
9177
|
-
"skip transition (negligible diff)",
|
|
9178
|
-
);
|
|
9179
|
-
return;
|
|
9180
|
-
}
|
|
9181
|
-
const duration = parseInt(
|
|
9182
|
-
container.getAttribute("data-size-transition-duration") ||
|
|
9183
|
-
SIZE_TRANSITION_DURATION,
|
|
9127
|
+
// Position of "from" content within large container
|
|
9128
|
+
const fromLeft = getAlignedPosition(
|
|
9129
|
+
elementToClipWidth,
|
|
9130
|
+
fromWidth,
|
|
9131
|
+
alignX,
|
|
9184
9132
|
);
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
});
|
|
9190
|
-
|
|
9191
|
-
const transitions = [];
|
|
9192
|
-
if (widthDiff === 0) ; else if (widthDiff <= SIZE_DIFF_EPSILON) {
|
|
9193
|
-
debug(
|
|
9194
|
-
"size",
|
|
9195
|
-
`Skip width transition (negligible diff ${widthDiff.toFixed(4)}px)`,
|
|
9196
|
-
);
|
|
9197
|
-
} else {
|
|
9198
|
-
transitions.push(
|
|
9199
|
-
createWidthTransition(outerWrapper, targetWidth, {
|
|
9200
|
-
setup: () =>
|
|
9201
|
-
notifyTransition(outerWrapper, {
|
|
9202
|
-
modelId: "ui_transition_width",
|
|
9203
|
-
canOverflow: true,
|
|
9204
|
-
id:
|
|
9205
|
-
targetWidth > constrainedWidth
|
|
9206
|
-
? "grow_to_new_width"
|
|
9207
|
-
: "shrink_to_new_width",
|
|
9208
|
-
}),
|
|
9209
|
-
duration,
|
|
9210
|
-
onUpdate: ({ value }) => {
|
|
9211
|
-
constrainedWidth = value;
|
|
9212
|
-
},
|
|
9213
|
-
}),
|
|
9214
|
-
);
|
|
9215
|
-
}
|
|
9216
|
-
if (heightDiff === 0) ; else if (heightDiff <= SIZE_DIFF_EPSILON) {
|
|
9217
|
-
debug(
|
|
9218
|
-
"size",
|
|
9219
|
-
`Skip height transition (negligible diff ${heightDiff.toFixed(4)}px)`,
|
|
9220
|
-
);
|
|
9221
|
-
} else {
|
|
9222
|
-
transitions.push(
|
|
9223
|
-
createHeightTransition(outerWrapper, targetHeight, {
|
|
9224
|
-
setup: () =>
|
|
9225
|
-
notifyTransition(outerWrapper, {
|
|
9226
|
-
modelId: "ui_transition_height",
|
|
9227
|
-
canOverflow: true,
|
|
9228
|
-
id:
|
|
9229
|
-
targetHeight > constrainedHeight
|
|
9230
|
-
? "grow_to_new_height"
|
|
9231
|
-
: "shrink_to_new_height",
|
|
9232
|
-
}),
|
|
9233
|
-
duration,
|
|
9234
|
-
onUpdate: ({ value }) => {
|
|
9235
|
-
constrainedHeight = value;
|
|
9236
|
-
},
|
|
9237
|
-
}),
|
|
9238
|
-
);
|
|
9239
|
-
}
|
|
9240
|
-
const release = applySizeConstraintsUntil(
|
|
9241
|
-
constrainedWidth,
|
|
9242
|
-
constrainedHeight,
|
|
9243
|
-
"size transitioning",
|
|
9133
|
+
const fromTop = getAlignedPosition(
|
|
9134
|
+
elementToClipHeight,
|
|
9135
|
+
fromHeight,
|
|
9136
|
+
alignY,
|
|
9244
9137
|
);
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9138
|
+
// Position of target content within large container
|
|
9139
|
+
const targetLeft = getAlignedPosition(
|
|
9140
|
+
elementToClipWidth,
|
|
9141
|
+
toWidth,
|
|
9142
|
+
alignX,
|
|
9143
|
+
);
|
|
9144
|
+
const targetTop = getAlignedPosition(
|
|
9145
|
+
elementToClipHeight,
|
|
9146
|
+
toHeight,
|
|
9147
|
+
alignY,
|
|
9148
|
+
);
|
|
9149
|
+
debugSize(
|
|
9150
|
+
`Positions in container: from [${fromLeft},${fromTop}] ${fromWidth}x${fromHeight} to [${targetLeft},${targetTop}] ${toWidth}x${toHeight}`,
|
|
9151
|
+
);
|
|
9152
|
+
// Get border-radius values
|
|
9153
|
+
const fromBorderRadius =
|
|
9154
|
+
previousTargetSlotConfiguration.borderRadius || 0;
|
|
9155
|
+
const toBorderRadius = targetSlotConfiguration.borderRadius || 0;
|
|
9156
|
+
const startInsetTop = fromTop;
|
|
9157
|
+
const startInsetRight = elementToClipWidth - (fromLeft + fromWidth);
|
|
9158
|
+
const startInsetBottom = elementToClipHeight - (fromTop + fromHeight);
|
|
9159
|
+
const startInsetLeft = fromLeft;
|
|
9160
|
+
|
|
9161
|
+
const endInsetTop = targetTop;
|
|
9162
|
+
const endInsetRight = elementToClipWidth - (targetLeft + toWidth);
|
|
9163
|
+
const endInsetBottom = elementToClipHeight - (targetTop + toHeight);
|
|
9164
|
+
const endInsetLeft = targetLeft;
|
|
9165
|
+
|
|
9166
|
+
const startClipPath = `inset(${startInsetTop}px ${startInsetRight}px ${startInsetBottom}px ${startInsetLeft}px round ${fromBorderRadius}px)`;
|
|
9167
|
+
const endClipPath = `inset(${endInsetTop}px ${endInsetRight}px ${endInsetBottom}px ${endInsetLeft}px round ${toBorderRadius}px)`;
|
|
9168
|
+
// Create clip-path animation using Web Animations API
|
|
9169
|
+
const clipAnimation = elementToClip.animate(
|
|
9170
|
+
[{ clipPath: startClipPath }, { clipPath: endClipPath }],
|
|
9171
|
+
{
|
|
9172
|
+
duration,
|
|
9173
|
+
easing: "ease",
|
|
9174
|
+
fill: "forwards",
|
|
9251
9175
|
},
|
|
9252
|
-
});
|
|
9253
|
-
sizeTransition.play();
|
|
9254
|
-
};
|
|
9255
|
-
const updateNaturalContentSize = (width, height) => {
|
|
9256
|
-
if (width === naturalContentWidth && height === naturalContentHeight) {
|
|
9257
|
-
return;
|
|
9258
|
-
}
|
|
9259
|
-
debug("size", "Updating natural content size:", {
|
|
9260
|
-
width: `${naturalContentWidth} → ${width}`,
|
|
9261
|
-
height: `${naturalContentHeight} → ${height}`,
|
|
9262
|
-
});
|
|
9263
|
-
naturalContentWidth = width;
|
|
9264
|
-
naturalContentHeight = height;
|
|
9265
|
-
};
|
|
9266
|
-
|
|
9267
|
-
// Initialize with current size
|
|
9268
|
-
[constrainedWidth, constrainedHeight] = measureSlotSize();
|
|
9269
|
-
|
|
9270
|
-
const updateSizeTransition = () => {
|
|
9271
|
-
hasSizeTransitions = container.hasAttribute("data-size-transition");
|
|
9272
|
-
const { isContentPhase } = slotInfo;
|
|
9273
|
-
const { isInitialPopulationWithoutTransition } = changeInfo;
|
|
9274
|
-
debug(
|
|
9275
|
-
"size",
|
|
9276
|
-
`updateSizeTransition(), current constrained size: ${constrainedWidth.toFixed(2)}x${constrainedHeight.toFixed(2)}`,
|
|
9277
9176
|
);
|
|
9278
|
-
sizeTransition?.cancel();
|
|
9279
|
-
|
|
9280
|
-
// Initial population skip (first null → something): no content or size animations
|
|
9281
|
-
if (isInitialPopulationWithoutTransition) {
|
|
9282
|
-
const [newWidth, newHeight] = measureSlotSize();
|
|
9283
|
-
debug("size", `content size measured to: ${newWidth}x${newHeight}`);
|
|
9284
|
-
if (isContentPhase) {
|
|
9285
|
-
applySizeConstraints(
|
|
9286
|
-
newWidth,
|
|
9287
|
-
newHeight,
|
|
9288
|
-
"content phase initial population",
|
|
9289
|
-
);
|
|
9290
|
-
} else {
|
|
9291
|
-
updateNaturalContentSize(newWidth, newHeight);
|
|
9292
|
-
releaseSizeConstraints("initial population - skip transitions");
|
|
9293
|
-
}
|
|
9294
|
-
return;
|
|
9295
|
-
}
|
|
9296
9177
|
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
// we don't care about the content phase dimension.
|
|
9311
|
-
// the content dimensions prevails
|
|
9312
|
-
targetWidth = naturalContentWidth;
|
|
9313
|
-
targetHeight = naturalContentHeight;
|
|
9314
|
-
debug(
|
|
9315
|
-
"size",
|
|
9316
|
-
`content phase using natural content size: ${naturalContentWidth}x${naturalContentHeight}`,
|
|
9317
|
-
);
|
|
9318
|
-
}
|
|
9319
|
-
} else {
|
|
9320
|
-
outerWrapper.style.width = "";
|
|
9321
|
-
outerWrapper.style.height = "";
|
|
9322
|
-
const [slotNaturalWidth, slotNaturalHeight] = measureSlotSize();
|
|
9323
|
-
outerWrapper.style.width = `${constrainedWidth}px`;
|
|
9324
|
-
outerWrapper.style.height = `${constrainedHeight}px`;
|
|
9325
|
-
updateNaturalContentSize(slotNaturalWidth, slotNaturalHeight);
|
|
9326
|
-
targetWidth = slotNaturalWidth;
|
|
9327
|
-
targetHeight = slotNaturalHeight;
|
|
9328
|
-
debug(
|
|
9329
|
-
"size",
|
|
9330
|
-
`content size measured to: ${slotNaturalWidth}x${slotNaturalHeight}`,
|
|
9331
|
-
);
|
|
9332
|
-
}
|
|
9333
|
-
|
|
9334
|
-
// If size transitions are disabled hold the previous size to avoid cropping during the content transition.
|
|
9335
|
-
if (!hasSizeTransitions) {
|
|
9336
|
-
debug(
|
|
9337
|
-
"size",
|
|
9338
|
-
`Holding previous size during content transition: ${constrainedWidth}x${constrainedHeight}`,
|
|
9339
|
-
);
|
|
9340
|
-
applySizeConstraints(
|
|
9341
|
-
constrainedWidth,
|
|
9342
|
-
constrainedHeight,
|
|
9343
|
-
"hold size for content transition",
|
|
9344
|
-
);
|
|
9345
|
-
sizeTransition?.cancel();
|
|
9346
|
-
onContentTransitionComplete = () => {
|
|
9347
|
-
onContentTransitionComplete = null;
|
|
9348
|
-
releaseSizeConstraints(
|
|
9349
|
-
"content transition completed - release size hold",
|
|
9350
|
-
);
|
|
9351
|
-
};
|
|
9352
|
-
return;
|
|
9353
|
-
}
|
|
9354
|
-
|
|
9355
|
-
if (
|
|
9356
|
-
targetWidth === constrainedWidth &&
|
|
9357
|
-
targetHeight === constrainedHeight
|
|
9358
|
-
) {
|
|
9359
|
-
sizeTransition?.cancel();
|
|
9360
|
-
debug("size", "No size change required");
|
|
9361
|
-
releaseSizeConstraints("no size change needed");
|
|
9362
|
-
return;
|
|
9363
|
-
}
|
|
9364
|
-
debug("size", "Size change needed:", {
|
|
9365
|
-
width: `${constrainedWidth} → ${targetWidth}`,
|
|
9366
|
-
height: `${constrainedHeight} → ${targetHeight}`,
|
|
9367
|
-
});
|
|
9368
|
-
updateToSize(targetWidth, targetHeight);
|
|
9369
|
-
};
|
|
9370
|
-
subscribeChange(updateSizeTransition);
|
|
9178
|
+
// Handle finish
|
|
9179
|
+
clipAnimation.finished
|
|
9180
|
+
.then(() => {
|
|
9181
|
+
// Clear clip-path to restore normal behavior
|
|
9182
|
+
elementToClip.style.clipPath = "";
|
|
9183
|
+
clipAnimation.cancel();
|
|
9184
|
+
onSizeTransitionFinished();
|
|
9185
|
+
})
|
|
9186
|
+
.catch(() => {
|
|
9187
|
+
// Animation was cancelled
|
|
9188
|
+
});
|
|
9189
|
+
clipAnimation.play();
|
|
9190
|
+
}
|
|
9371
9191
|
|
|
9372
|
-
|
|
9373
|
-
|
|
9192
|
+
return morphTransitions;
|
|
9193
|
+
};
|
|
9194
|
+
const fadeInTargetSlot = () => {
|
|
9195
|
+
targetSlotBackground.style.setProperty(
|
|
9196
|
+
"--target-slot-background",
|
|
9197
|
+
stringifyStyle(targetSlotConfiguration.background, "background"),
|
|
9198
|
+
);
|
|
9199
|
+
targetSlotBackground.style.setProperty(
|
|
9200
|
+
"--target-slot-width",
|
|
9201
|
+
`${targetSlotConfiguration.width || 0}px`,
|
|
9202
|
+
);
|
|
9203
|
+
targetSlotBackground.style.setProperty(
|
|
9204
|
+
"--target-slot-height",
|
|
9205
|
+
`${targetSlotConfiguration.height || 0}px`,
|
|
9206
|
+
);
|
|
9207
|
+
return createOpacityTransition(targetSlot, 1, {
|
|
9208
|
+
from: 0,
|
|
9209
|
+
duration,
|
|
9210
|
+
styleSynchronizer: "inline_style",
|
|
9211
|
+
onFinish: (targetSlotOpacityTransition) => {
|
|
9212
|
+
targetSlotOpacityTransition.cancel();
|
|
9213
|
+
},
|
|
9374
9214
|
});
|
|
9375
|
-
|
|
9376
|
-
|
|
9215
|
+
};
|
|
9216
|
+
const fadeOutPreviousGroup = () => {
|
|
9217
|
+
return createOpacityTransition(previousGroup, 0, {
|
|
9218
|
+
from: 1,
|
|
9219
|
+
duration,
|
|
9220
|
+
styleSynchronizer: "inline_style",
|
|
9221
|
+
onFinish: (previousGroupOpacityTransition) => {
|
|
9222
|
+
previousGroupOpacityTransition.cancel();
|
|
9223
|
+
previousGroup.style.opacity = "0"; // keep previous group visually hidden
|
|
9224
|
+
},
|
|
9377
9225
|
});
|
|
9378
|
-
|
|
9379
|
-
|
|
9226
|
+
};
|
|
9227
|
+
const fadeOutOutgoingSlot = () => {
|
|
9228
|
+
return createOpacityTransition(outgoingSlot, 0, {
|
|
9229
|
+
duration,
|
|
9230
|
+
from: 1,
|
|
9231
|
+
styleSynchronizer: "inline_style",
|
|
9232
|
+
onFinish: (outgoingSlotOpacityTransition) => {
|
|
9233
|
+
outgoingSlotOpacityTransition.cancel();
|
|
9234
|
+
outgoingSlot.style.opacity = "0"; // keep outgoing slot visually hidden
|
|
9235
|
+
},
|
|
9380
9236
|
});
|
|
9381
|
-
}
|
|
9382
|
-
|
|
9383
|
-
{
|
|
9384
|
-
let activeContentTransition = null;
|
|
9385
|
-
let activeContentTransitionType = null;
|
|
9386
|
-
let activePhaseTransition = null;
|
|
9387
|
-
let activePhaseTransitionType = null;
|
|
9388
|
-
|
|
9389
|
-
const updateContentTransitions = () => {
|
|
9390
|
-
const { childNodes, contentName: fromContentName } = slotInfo;
|
|
9391
|
-
const {
|
|
9392
|
-
previousSlotInfo,
|
|
9393
|
-
becomesEmpty,
|
|
9394
|
-
becomesPopulated,
|
|
9395
|
-
shouldDoContentTransition,
|
|
9396
|
-
shouldDoPhaseTransition,
|
|
9397
|
-
contentChange,
|
|
9398
|
-
phaseChange,
|
|
9399
|
-
isTransitionLess,
|
|
9400
|
-
shouldDoContentTransitionIncludingPopulation,
|
|
9401
|
-
} = changeInfo;
|
|
9402
|
-
const { hasChild: hadChild, contentName: toContentName } =
|
|
9403
|
-
previousSlotInfo;
|
|
9404
|
-
|
|
9405
|
-
const preserveOnlyContentTransition =
|
|
9406
|
-
isTransitionLess && activeContentTransition !== null;
|
|
9407
|
-
const previousChildNodes = previousSlotInfo.childNodes;
|
|
9408
|
-
|
|
9409
|
-
// Determine transition scenarios (hadChild/hasChild already computed above for logging)
|
|
9410
|
-
|
|
9411
|
-
/**
|
|
9412
|
-
* Content Phase Logic: Why empty slots are treated as content phases
|
|
9413
|
-
*
|
|
9414
|
-
* When there is no child element (React component returns null), it is considered
|
|
9415
|
-
* that the component does not render anything temporarily. This might be because:
|
|
9416
|
-
* - The component is loading but does not have a loading state
|
|
9417
|
-
* - The component has an error but does not have an error state
|
|
9418
|
-
* - The component is conceptually unloaded (underlying content was deleted/is not accessible)
|
|
9419
|
-
*
|
|
9420
|
-
* This represents a phase of the given content: having nothing to display.
|
|
9421
|
-
*
|
|
9422
|
-
* We support transitions between different contents via the ability to set
|
|
9423
|
-
* [data-content-key] on the ".ui_transition_slot". This is also useful when you want
|
|
9424
|
-
* all children of a React component to inherit the same data-content-key without
|
|
9425
|
-
* explicitly setting the attribute on each child element.
|
|
9426
|
-
*/
|
|
9427
|
-
|
|
9428
|
-
// Content key change when either slot or child has data-content-key and it changed
|
|
9429
|
-
// Content key change detection already computed in getSlotChangeInfo.
|
|
9430
|
-
// We rely on the shouldDoContentTransition value coming from changeInfo.
|
|
9431
|
-
|
|
9432
|
-
const decisions = [];
|
|
9433
|
-
if (shouldDoContentTransition) {
|
|
9434
|
-
decisions.push("CONTENT TRANSITION");
|
|
9435
|
-
}
|
|
9436
|
-
if (shouldDoPhaseTransition) {
|
|
9437
|
-
decisions.push("PHASE TRANSITION");
|
|
9438
|
-
}
|
|
9439
|
-
if (preserveOnlyContentTransition) {
|
|
9440
|
-
decisions.push("PRESERVE CONTENT TRANSITION");
|
|
9441
|
-
}
|
|
9442
|
-
if (decisions.length === 0) {
|
|
9443
|
-
decisions.push("NO TRANSITION");
|
|
9444
|
-
}
|
|
9445
|
-
|
|
9446
|
-
debug("content", `Decision: ${decisions.join(" + ")}`);
|
|
9447
|
-
if (preserveOnlyContentTransition) {
|
|
9448
|
-
const progress = (activeContentTransition.progress * 100).toFixed(1);
|
|
9449
|
-
debug(
|
|
9450
|
-
"content",
|
|
9451
|
-
`Preserving existing content transition (progress ${progress}%)`,
|
|
9452
|
-
);
|
|
9453
|
-
}
|
|
9454
|
-
|
|
9455
|
-
if (changeInfo.isInitialPopulationWithoutTransition) {
|
|
9456
|
-
return;
|
|
9457
|
-
}
|
|
9458
|
-
|
|
9459
|
-
// Handle content transitions (slide-left, cross-fade for content key changes)
|
|
9460
|
-
if (
|
|
9461
|
-
decisions.length === 1 &&
|
|
9462
|
-
decisions[0] === "NO TRANSITION" &&
|
|
9463
|
-
activeContentTransition === null &&
|
|
9464
|
-
activePhaseTransition === null
|
|
9465
|
-
) {
|
|
9466
|
-
// Skip creating any new transitions entirely
|
|
9467
|
-
onContentTransitionComplete?.();
|
|
9468
|
-
} else if (
|
|
9469
|
-
shouldDoContentTransitionIncludingPopulation &&
|
|
9470
|
-
!preserveOnlyContentTransition
|
|
9471
|
-
) {
|
|
9472
|
-
const animationProgress = activeContentTransition?.progress || 0;
|
|
9473
|
-
if (animationProgress > 0) {
|
|
9474
|
-
debug(
|
|
9475
|
-
"content",
|
|
9476
|
-
`Preserving content transition progress: ${(animationProgress * 100).toFixed(1)}%`,
|
|
9477
|
-
);
|
|
9478
|
-
}
|
|
9479
|
-
|
|
9480
|
-
const newTransitionType =
|
|
9481
|
-
container.getAttribute("data-content-transition") ||
|
|
9482
|
-
CONTENT_TRANSITION;
|
|
9483
|
-
const canContinueSmoothly =
|
|
9484
|
-
activeContentTransitionType === newTransitionType &&
|
|
9485
|
-
activeContentTransition;
|
|
9486
|
-
if (canContinueSmoothly) {
|
|
9487
|
-
debug(
|
|
9488
|
-
"content",
|
|
9489
|
-
"Continuing with same content transition type (restarting due to actual change)",
|
|
9490
|
-
);
|
|
9491
|
-
activeContentTransition.cancel();
|
|
9492
|
-
} else if (
|
|
9493
|
-
activeContentTransition &&
|
|
9494
|
-
activeContentTransitionType !== newTransitionType
|
|
9495
|
-
) {
|
|
9496
|
-
debug(
|
|
9497
|
-
"content",
|
|
9498
|
-
"Different content transition type, keeping both",
|
|
9499
|
-
`${activeContentTransitionType} → ${newTransitionType}`,
|
|
9500
|
-
);
|
|
9501
|
-
} else if (activeContentTransition) {
|
|
9502
|
-
debug("content", "Cancelling current content transition");
|
|
9503
|
-
activeContentTransition.cancel();
|
|
9504
|
-
}
|
|
9505
|
-
|
|
9506
|
-
const needsOldChildNodesClone =
|
|
9507
|
-
(contentChange || becomesEmpty) && hadChild;
|
|
9508
|
-
const duration = parseInt(
|
|
9509
|
-
container.getAttribute("data-content-transition-duration") ||
|
|
9510
|
-
CONTENT_TRANSITION_DURATION,
|
|
9511
|
-
);
|
|
9512
|
-
const type =
|
|
9513
|
-
container.getAttribute("data-content-transition") ||
|
|
9514
|
-
CONTENT_TRANSITION;
|
|
9515
|
-
|
|
9516
|
-
const setupContentTransition = () =>
|
|
9517
|
-
setupTransition({
|
|
9518
|
-
isPhaseTransition: false,
|
|
9519
|
-
overlay: contentOverlay,
|
|
9520
|
-
needsOldChildNodesClone,
|
|
9521
|
-
previousChildNodes,
|
|
9522
|
-
childNodes,
|
|
9523
|
-
slotInfo,
|
|
9524
|
-
attributeToRemove: ["data-content-key"],
|
|
9525
|
-
});
|
|
9526
|
-
|
|
9527
|
-
activeContentTransition = applyTransition(
|
|
9528
|
-
transitionController,
|
|
9529
|
-
setupContentTransition,
|
|
9530
|
-
{
|
|
9531
|
-
duration,
|
|
9532
|
-
type,
|
|
9533
|
-
animationProgress,
|
|
9534
|
-
isPhaseTransition: false,
|
|
9535
|
-
previousSlotInfo,
|
|
9536
|
-
slotInfo,
|
|
9537
|
-
onComplete: () => {
|
|
9538
|
-
activeContentTransition = null;
|
|
9539
|
-
activeContentTransitionType = null;
|
|
9540
|
-
onContentTransitionComplete?.();
|
|
9541
|
-
},
|
|
9542
|
-
debug,
|
|
9543
|
-
},
|
|
9544
|
-
);
|
|
9545
|
-
|
|
9546
|
-
if (activeContentTransition) {
|
|
9547
|
-
activeContentTransition.play();
|
|
9548
|
-
}
|
|
9549
|
-
activeContentTransitionType = type;
|
|
9550
|
-
} else if (!shouldDoContentTransition && !preserveOnlyContentTransition) {
|
|
9551
|
-
// Clean up content overlay if no content transition needed and nothing to preserve
|
|
9552
|
-
contentOverlay.innerHTML = "";
|
|
9553
|
-
activeContentTransition = null;
|
|
9554
|
-
activeContentTransitionType = null;
|
|
9555
|
-
}
|
|
9556
|
-
|
|
9557
|
-
// Handle phase transitions (cross-fade for content phase changes)
|
|
9558
|
-
if (shouldDoPhaseTransition) {
|
|
9559
|
-
const phaseTransitionType =
|
|
9560
|
-
container.getAttribute("data-phase-transition") || PHASE_TRANSITION;
|
|
9561
|
-
const phaseAnimationProgress = activePhaseTransition?.progress || 0;
|
|
9562
|
-
if (phaseAnimationProgress > 0) {
|
|
9563
|
-
debug(
|
|
9564
|
-
"content",
|
|
9565
|
-
`Preserving phase transition progress: ${(phaseAnimationProgress * 100).toFixed(1)}%`,
|
|
9566
|
-
);
|
|
9567
|
-
}
|
|
9568
|
-
|
|
9569
|
-
const canContinueSmoothly =
|
|
9570
|
-
activePhaseTransitionType === phaseTransitionType &&
|
|
9571
|
-
activePhaseTransition;
|
|
9237
|
+
};
|
|
9572
9238
|
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9239
|
+
// content_to_content transition (uses previous_group)
|
|
9240
|
+
const applyContentToContentTransition = (toConfiguration) => {
|
|
9241
|
+
// 1. move target slot to previous
|
|
9242
|
+
moveConfigurationIntoSlot(targetSlotConfiguration, previousTargetSlot);
|
|
9243
|
+
targetSlotConfiguration = toConfiguration;
|
|
9244
|
+
// 2. move outgoing slot to previous
|
|
9245
|
+
moveConfigurationIntoSlot(outgoingSlotConfiguration, previousOutgoingSlot);
|
|
9246
|
+
moveConfigurationIntoSlot(UNSET, outgoingSlot);
|
|
9247
|
+
|
|
9248
|
+
const transitions = [
|
|
9249
|
+
...morphContainerIntoTarget(),
|
|
9250
|
+
fadeInTargetSlot(),
|
|
9251
|
+
fadeOutPreviousGroup(),
|
|
9252
|
+
];
|
|
9253
|
+
const transition = transitionController.update(transitions, {
|
|
9254
|
+
onFinish: () => {
|
|
9255
|
+
moveConfigurationIntoSlot(UNSET, previousTargetSlot);
|
|
9256
|
+
moveConfigurationIntoSlot(UNSET, previousOutgoingSlot);
|
|
9257
|
+
if (hasDebugLogs) {
|
|
9258
|
+
console.groupEnd();
|
|
9588
9259
|
}
|
|
9260
|
+
},
|
|
9261
|
+
});
|
|
9262
|
+
transition.play();
|
|
9263
|
+
};
|
|
9264
|
+
// content_phase_to_content_phase transition (uses outgoing_slot)
|
|
9265
|
+
const applyContentPhaseToContentPhaseTransition = (toConfiguration) => {
|
|
9266
|
+
// 1. Move target slot to outgoing
|
|
9267
|
+
moveConfigurationIntoSlot(targetSlotConfiguration, outgoingSlot);
|
|
9268
|
+
targetSlotConfiguration = toConfiguration;
|
|
9269
|
+
|
|
9270
|
+
const transitions = [
|
|
9271
|
+
...morphContainerIntoTarget(),
|
|
9272
|
+
fadeInTargetSlot(),
|
|
9273
|
+
fadeOutOutgoingSlot(),
|
|
9274
|
+
];
|
|
9275
|
+
const transition = transitionController.update(transitions, {
|
|
9276
|
+
onFinish: () => {
|
|
9277
|
+
moveConfigurationIntoSlot(UNSET, outgoingSlot);
|
|
9589
9278
|
|
|
9590
|
-
|
|
9591
|
-
(
|
|
9592
|
-
const phaseDuration = parseInt(
|
|
9593
|
-
container.getAttribute("data-phase-transition-duration") ||
|
|
9594
|
-
PHASE_TRANSITION_DURATION,
|
|
9595
|
-
);
|
|
9596
|
-
|
|
9597
|
-
const setupPhaseTransition = () =>
|
|
9598
|
-
setupTransition({
|
|
9599
|
-
isPhaseTransition: true,
|
|
9600
|
-
overlay: phaseOverlay,
|
|
9601
|
-
needsOldChildNodesClone: needsOldPhaseClone,
|
|
9602
|
-
previousChildNodes,
|
|
9603
|
-
childNodes,
|
|
9604
|
-
slotInfo,
|
|
9605
|
-
attributeToRemove: ["data-content-key", "data-content-phase"],
|
|
9606
|
-
});
|
|
9607
|
-
|
|
9608
|
-
debug(
|
|
9609
|
-
"content",
|
|
9610
|
-
`Starting transition: ${fromContentName} → ${toContentName}`,
|
|
9611
|
-
);
|
|
9612
|
-
|
|
9613
|
-
activePhaseTransition = applyTransition(
|
|
9614
|
-
transitionController,
|
|
9615
|
-
setupPhaseTransition,
|
|
9616
|
-
{
|
|
9617
|
-
duration: phaseDuration,
|
|
9618
|
-
type: phaseTransitionType,
|
|
9619
|
-
animationProgress: phaseAnimationProgress,
|
|
9620
|
-
isPhaseTransition: true,
|
|
9621
|
-
previousSlotInfo,
|
|
9622
|
-
slotInfo,
|
|
9623
|
-
onComplete: () => {
|
|
9624
|
-
activePhaseTransition = null;
|
|
9625
|
-
activePhaseTransitionType = null;
|
|
9626
|
-
onContentTransitionComplete?.();
|
|
9627
|
-
debug("content", "Phase transition complete");
|
|
9628
|
-
},
|
|
9629
|
-
debug,
|
|
9630
|
-
},
|
|
9631
|
-
);
|
|
9632
|
-
|
|
9633
|
-
if (activePhaseTransition) {
|
|
9634
|
-
activePhaseTransition.play();
|
|
9279
|
+
if (hasDebugLogs) {
|
|
9280
|
+
console.groupEnd();
|
|
9635
9281
|
}
|
|
9636
|
-
|
|
9637
|
-
}
|
|
9638
|
-
};
|
|
9639
|
-
subscribeChange(updateContentTransitions);
|
|
9640
|
-
|
|
9641
|
-
addPauseCallback(() => {
|
|
9642
|
-
activeContentTransition?.pause();
|
|
9643
|
-
activePhaseTransition?.pause();
|
|
9644
|
-
});
|
|
9645
|
-
addResumeCallback(() => {
|
|
9646
|
-
activeContentTransition?.play();
|
|
9647
|
-
activePhaseTransition?.play();
|
|
9282
|
+
},
|
|
9648
9283
|
});
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9284
|
+
transition.play();
|
|
9285
|
+
};
|
|
9286
|
+
// any_to_empty transition
|
|
9287
|
+
const applyToEmptyTransition = () => {
|
|
9288
|
+
// 1. move target slot to previous
|
|
9289
|
+
moveConfigurationIntoSlot(targetSlotConfiguration, previousTargetSlot);
|
|
9290
|
+
targetSlotConfiguration = UNSET;
|
|
9291
|
+
// 2. move outgoing slot to previous
|
|
9292
|
+
moveConfigurationIntoSlot(outgoingSlotConfiguration, previousOutgoingSlot);
|
|
9293
|
+
outgoingSlotConfiguration = UNSET;
|
|
9294
|
+
|
|
9295
|
+
const transitions = [...morphContainerIntoTarget(), fadeOutPreviousGroup()];
|
|
9296
|
+
const transition = transitionController.update(transitions, {
|
|
9297
|
+
onFinish: () => {
|
|
9298
|
+
moveConfigurationIntoSlot(UNSET, previousTargetSlot);
|
|
9299
|
+
moveConfigurationIntoSlot(UNSET, previousOutgoingSlot);
|
|
9300
|
+
if (hasDebugLogs) {
|
|
9301
|
+
console.groupEnd();
|
|
9302
|
+
}
|
|
9303
|
+
},
|
|
9652
9304
|
});
|
|
9653
|
-
|
|
9305
|
+
transition.play();
|
|
9306
|
+
};
|
|
9307
|
+
// Main transition method
|
|
9308
|
+
const transitionTo = (
|
|
9309
|
+
newContentElement,
|
|
9310
|
+
{ contentPhase, contentId } = {},
|
|
9311
|
+
) => {
|
|
9312
|
+
if (contentId) {
|
|
9313
|
+
targetSlot.setAttribute(CONTENT_ID_ATTRIBUTE, contentId);
|
|
9314
|
+
} else {
|
|
9315
|
+
targetSlot.removeAttribute(CONTENT_ID_ATTRIBUTE);
|
|
9316
|
+
}
|
|
9317
|
+
if (contentPhase) {
|
|
9318
|
+
targetSlot.setAttribute(CONTENT_PHASE_ATTRIBUTE, contentPhase);
|
|
9319
|
+
} else {
|
|
9320
|
+
targetSlot.removeAttribute(CONTENT_PHASE_ATTRIBUTE);
|
|
9321
|
+
}
|
|
9322
|
+
if (newContentElement) {
|
|
9323
|
+
targetSlot.innerHTML = "";
|
|
9324
|
+
targetSlot.appendChild(newContentElement);
|
|
9325
|
+
} else {
|
|
9326
|
+
targetSlot.innerHTML = "";
|
|
9327
|
+
}
|
|
9328
|
+
};
|
|
9329
|
+
// Reset to initial content
|
|
9330
|
+
const resetContent = () => {
|
|
9331
|
+
transitionController.cancel();
|
|
9332
|
+
moveConfigurationIntoSlot(targetSlotInitialConfiguration, targetSlot);
|
|
9333
|
+
moveConfigurationIntoSlot(outgoingSlotInitialConfiguration, outgoingSlot);
|
|
9334
|
+
moveConfigurationIntoSlot(UNSET, previousTargetSlot);
|
|
9335
|
+
moveConfigurationIntoSlot(UNSET, previousOutgoingSlot);
|
|
9336
|
+
};
|
|
9654
9337
|
|
|
9655
|
-
{
|
|
9656
|
-
const
|
|
9657
|
-
const
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9338
|
+
const targetSlotEffect = (reasons) => {
|
|
9339
|
+
const fromConfiguration = targetSlotConfiguration;
|
|
9340
|
+
const toConfiguration = detectConfiguration(targetSlot);
|
|
9341
|
+
if (hasDebugLogs) {
|
|
9342
|
+
console.group(`targetSlotEffect()`);
|
|
9343
|
+
console.debug(`reasons:`);
|
|
9344
|
+
console.debug(`- ${reasons.join("\n- ")}`);
|
|
9345
|
+
}
|
|
9346
|
+
if (isSameConfiguration(fromConfiguration, toConfiguration)) {
|
|
9347
|
+
debugDetection(
|
|
9348
|
+
`already in desired state (${toConfiguration}) -> early return`,
|
|
9349
|
+
);
|
|
9350
|
+
if (hasDebugLogs) {
|
|
9351
|
+
console.groupEnd();
|
|
9662
9352
|
}
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
}
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9353
|
+
return;
|
|
9354
|
+
}
|
|
9355
|
+
const fromConfigType = fromConfiguration.type;
|
|
9356
|
+
const toConfigType = toConfiguration.type;
|
|
9357
|
+
transitionType = `${fromConfigType}_to_${toConfigType}`;
|
|
9358
|
+
debugDetection(
|
|
9359
|
+
`Prepare "${transitionType}" transition (${fromConfiguration} -> ${toConfiguration})`,
|
|
9360
|
+
);
|
|
9361
|
+
// content_to_empty / content_phase_to_empty
|
|
9362
|
+
if (toConfiguration.isEmpty) {
|
|
9363
|
+
applyToEmptyTransition();
|
|
9364
|
+
return;
|
|
9365
|
+
}
|
|
9366
|
+
// content_phase_to_content_phase
|
|
9367
|
+
if (fromConfiguration.isContentPhase && toConfiguration.isContentPhase) {
|
|
9368
|
+
applyContentPhaseToContentPhaseTransition(toConfiguration);
|
|
9369
|
+
return;
|
|
9370
|
+
}
|
|
9371
|
+
// content_phase_to_content
|
|
9372
|
+
if (fromConfiguration.isContentPhase && toConfiguration.isContent) {
|
|
9373
|
+
applyContentPhaseToContentPhaseTransition(toConfiguration);
|
|
9374
|
+
return;
|
|
9375
|
+
}
|
|
9376
|
+
// content_to_content_phase
|
|
9377
|
+
if (fromConfiguration.isContent && toConfiguration.isContentPhase) {
|
|
9378
|
+
applyContentPhaseToContentPhaseTransition(toConfiguration);
|
|
9379
|
+
return;
|
|
9380
|
+
}
|
|
9381
|
+
// content_to_content (default case)
|
|
9382
|
+
applyContentToContentTransition(toConfiguration);
|
|
9383
|
+
};
|
|
9679
9384
|
|
|
9680
|
-
|
|
9681
|
-
triggerChildSlotMutation("init");
|
|
9385
|
+
const [teardown, addTeardown] = createPubSub();
|
|
9682
9386
|
{
|
|
9683
9387
|
const mutationObserver = new MutationObserver((mutations) => {
|
|
9684
9388
|
const reasonParts = [];
|
|
9685
|
-
|
|
9686
9389
|
for (const mutation of mutations) {
|
|
9687
9390
|
if (mutation.type === "childList") {
|
|
9688
9391
|
const added = mutation.addedNodes.length;
|
|
@@ -9699,10 +9402,27 @@ const initUITransition = (container) => {
|
|
|
9699
9402
|
if (mutation.type === "attributes") {
|
|
9700
9403
|
const { attributeName } = mutation;
|
|
9701
9404
|
if (
|
|
9702
|
-
attributeName ===
|
|
9703
|
-
attributeName ===
|
|
9405
|
+
attributeName === CONTENT_ID_ATTRIBUTE ||
|
|
9406
|
+
attributeName === CONTENT_PHASE_ATTRIBUTE
|
|
9704
9407
|
) {
|
|
9705
|
-
|
|
9408
|
+
const { oldValue } = mutation;
|
|
9409
|
+
if (oldValue === null) {
|
|
9410
|
+
const value = targetSlot.getAttribute(attributeName);
|
|
9411
|
+
reasonParts.push(
|
|
9412
|
+
value
|
|
9413
|
+
? `added [${attributeName}=${value}]`
|
|
9414
|
+
: `added [${attributeName}]`,
|
|
9415
|
+
);
|
|
9416
|
+
} else if (targetSlot.hasAttribute(attributeName)) {
|
|
9417
|
+
const value = targetSlot.getAttribute(attributeName);
|
|
9418
|
+
reasonParts.push(`[${attributeName}] ${oldValue} -> ${value}`);
|
|
9419
|
+
} else {
|
|
9420
|
+
reasonParts.push(
|
|
9421
|
+
oldValue
|
|
9422
|
+
? `removed [${attributeName}=${oldValue}]`
|
|
9423
|
+
: `removed [${attributeName}]`,
|
|
9424
|
+
);
|
|
9425
|
+
}
|
|
9706
9426
|
}
|
|
9707
9427
|
}
|
|
9708
9428
|
}
|
|
@@ -9710,410 +9430,63 @@ const initUITransition = (container) => {
|
|
|
9710
9430
|
if (reasonParts.length === 0) {
|
|
9711
9431
|
return;
|
|
9712
9432
|
}
|
|
9713
|
-
|
|
9714
|
-
triggerChildSlotMutation(reason);
|
|
9433
|
+
targetSlotEffect(reasonParts);
|
|
9715
9434
|
});
|
|
9716
|
-
mutationObserver.observe(
|
|
9435
|
+
mutationObserver.observe(targetSlot, {
|
|
9717
9436
|
childList: true,
|
|
9718
9437
|
attributes: true,
|
|
9719
|
-
attributeFilter: [
|
|
9438
|
+
attributeFilter: [CONTENT_ID_ATTRIBUTE, CONTENT_PHASE_ATTRIBUTE],
|
|
9720
9439
|
characterData: false,
|
|
9721
9440
|
});
|
|
9722
9441
|
addTeardown(() => {
|
|
9723
9442
|
mutationObserver.disconnect();
|
|
9724
9443
|
});
|
|
9725
9444
|
}
|
|
9726
|
-
|
|
9727
|
-
return {
|
|
9728
|
-
slot,
|
|
9729
|
-
|
|
9730
|
-
cleanup: () => {
|
|
9731
|
-
teardown();
|
|
9732
|
-
},
|
|
9733
|
-
pause: () => {
|
|
9734
|
-
if (state.isPaused) {
|
|
9735
|
-
return;
|
|
9736
|
-
}
|
|
9737
|
-
publishPause();
|
|
9738
|
-
state.isPaused = true;
|
|
9739
|
-
},
|
|
9740
|
-
resume: () => {
|
|
9741
|
-
if (!state.isPaused) {
|
|
9742
|
-
return;
|
|
9743
|
-
}
|
|
9744
|
-
state.isPaused = false;
|
|
9745
|
-
publishResume();
|
|
9746
|
-
},
|
|
9747
|
-
getState: () => state,
|
|
9748
|
-
};
|
|
9749
|
-
};
|
|
9750
|
-
|
|
9751
|
-
const applyTransition = (
|
|
9752
|
-
transitionController,
|
|
9753
|
-
setupTransition,
|
|
9754
9445
|
{
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
) => {
|
|
9765
|
-
let transitionType;
|
|
9766
|
-
if (type === "cross-fade") {
|
|
9767
|
-
transitionType = crossFade;
|
|
9768
|
-
} else if (type === "slide-left") {
|
|
9769
|
-
transitionType = slideLeft;
|
|
9770
|
-
} else {
|
|
9771
|
-
return null;
|
|
9446
|
+
const slots = [
|
|
9447
|
+
targetSlot,
|
|
9448
|
+
outgoingSlot,
|
|
9449
|
+
previousTargetSlot,
|
|
9450
|
+
previousOutgoingSlot,
|
|
9451
|
+
];
|
|
9452
|
+
for (const slot of slots) {
|
|
9453
|
+
addTeardown(monitorItemsOverflow(slot));
|
|
9454
|
+
}
|
|
9772
9455
|
}
|
|
9773
9456
|
|
|
9774
|
-
const
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
});
|
|
9785
|
-
|
|
9786
|
-
const remainingDuration = Math.max(100, duration * (1 - animationProgress));
|
|
9787
|
-
debug("content", `Animation duration: ${remainingDuration}ms`);
|
|
9788
|
-
|
|
9789
|
-
const transitions = transitionType.apply(oldElement, newElement, {
|
|
9790
|
-
duration: remainingDuration,
|
|
9791
|
-
startProgress: animationProgress,
|
|
9792
|
-
isPhaseTransition,
|
|
9793
|
-
debug,
|
|
9794
|
-
});
|
|
9795
|
-
|
|
9796
|
-
debug("content", `Created ${transitions.length} transition(s) for animation`);
|
|
9797
|
-
|
|
9798
|
-
if (transitions.length === 0) {
|
|
9799
|
-
debug("content", "No transitions to animate, cleaning up immediately");
|
|
9800
|
-
cleanup();
|
|
9801
|
-
onTeardown?.();
|
|
9802
|
-
onComplete?.();
|
|
9803
|
-
return null;
|
|
9804
|
-
}
|
|
9457
|
+
const setDuration = (newDuration) => {
|
|
9458
|
+
duration = newDuration;
|
|
9459
|
+
// Update CSS variable immediately
|
|
9460
|
+
root.style.setProperty("--x-transition-duration", `${duration}ms`);
|
|
9461
|
+
};
|
|
9462
|
+
const setAlignment = (newAlignX, newAlignY) => {
|
|
9463
|
+
alignX = newAlignX;
|
|
9464
|
+
alignY = newAlignY;
|
|
9465
|
+
updateAlignment();
|
|
9466
|
+
};
|
|
9805
9467
|
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9468
|
+
return {
|
|
9469
|
+
updateContentId: (value) => {
|
|
9470
|
+
if (value) {
|
|
9471
|
+
targetSlot.setAttribute(CONTENT_ID_ATTRIBUTE, value);
|
|
9472
|
+
} else {
|
|
9473
|
+
targetSlot.removeAttribute(CONTENT_ID_ATTRIBUTE);
|
|
9474
|
+
}
|
|
9812
9475
|
},
|
|
9813
|
-
});
|
|
9814
|
-
|
|
9815
|
-
return groupTransition;
|
|
9816
|
-
};
|
|
9817
|
-
|
|
9818
|
-
const slideLeft = {
|
|
9819
|
-
id: "ui_transition_slide_left",
|
|
9820
|
-
name: "slide-left",
|
|
9821
|
-
apply: (
|
|
9822
|
-
oldElement,
|
|
9823
|
-
newElement,
|
|
9824
|
-
{ duration, startProgress = 0, isPhaseTransition = false, debug },
|
|
9825
|
-
) => {
|
|
9826
|
-
if (!oldElement && !newElement) {
|
|
9827
|
-
return [];
|
|
9828
|
-
}
|
|
9829
|
-
|
|
9830
|
-
if (!newElement) {
|
|
9831
|
-
// Content -> Empty (slide out left only)
|
|
9832
|
-
const currentPosition = getTranslateX(oldElement);
|
|
9833
|
-
const containerWidth = getInnerWidth(oldElement.parentElement);
|
|
9834
|
-
const from = currentPosition;
|
|
9835
|
-
const to = -containerWidth;
|
|
9836
|
-
debug("content", "Slide out to empty:", { from, to });
|
|
9837
|
-
|
|
9838
|
-
return [
|
|
9839
|
-
createTranslateXTransition(oldElement, to, {
|
|
9840
|
-
setup: () =>
|
|
9841
|
-
notifyTransition(newElement, {
|
|
9842
|
-
modelId: slideLeft.id,
|
|
9843
|
-
canOverflow: true,
|
|
9844
|
-
id: "slide_out_old_content",
|
|
9845
|
-
}),
|
|
9846
|
-
from,
|
|
9847
|
-
duration,
|
|
9848
|
-
startProgress,
|
|
9849
|
-
onUpdate: ({ value, timing }) => {
|
|
9850
|
-
debug("transition_updates", "Slide out progress:", value);
|
|
9851
|
-
if (timing === "end") {
|
|
9852
|
-
debug("content", "Slide out complete");
|
|
9853
|
-
}
|
|
9854
|
-
},
|
|
9855
|
-
}),
|
|
9856
|
-
];
|
|
9857
|
-
}
|
|
9858
|
-
|
|
9859
|
-
if (!oldElement) {
|
|
9860
|
-
// Empty -> Content (slide in from right)
|
|
9861
|
-
const containerWidth = getInnerWidth(newElement.parentElement);
|
|
9862
|
-
const from = containerWidth; // Start from right edge for slide-in effect
|
|
9863
|
-
const to = getTranslateXWithoutTransition(newElement);
|
|
9864
|
-
debug("content", "Slide in from empty:", { from, to });
|
|
9865
|
-
return [
|
|
9866
|
-
createTranslateXTransition(newElement, to, {
|
|
9867
|
-
setup: () =>
|
|
9868
|
-
notifyTransition(newElement, {
|
|
9869
|
-
modelId: slideLeft.id,
|
|
9870
|
-
canOverflow: true,
|
|
9871
|
-
id: "slide_in_new_content",
|
|
9872
|
-
}),
|
|
9873
|
-
from,
|
|
9874
|
-
duration,
|
|
9875
|
-
startProgress,
|
|
9876
|
-
onUpdate: ({ value, timing }) => {
|
|
9877
|
-
debug("transition_updates", "Slide in progress:", value);
|
|
9878
|
-
if (timing === "end") {
|
|
9879
|
-
debug("content", "Slide in complete");
|
|
9880
|
-
}
|
|
9881
|
-
},
|
|
9882
|
-
}),
|
|
9883
|
-
];
|
|
9884
|
-
}
|
|
9885
|
-
|
|
9886
|
-
// Content -> Content (slide left)
|
|
9887
|
-
// The old content (oldElement) slides OUT to the left
|
|
9888
|
-
// The new content (newElement) slides IN from the right
|
|
9889
|
-
|
|
9890
|
-
// Get positions for the slide animation
|
|
9891
|
-
const containerWidth = getInnerWidth(newElement.parentElement);
|
|
9892
|
-
const oldContentPosition = getTranslateX(oldElement);
|
|
9893
|
-
const currentNewPosition = getTranslateX(newElement);
|
|
9894
|
-
const naturalNewPosition = getTranslateXWithoutTransition(newElement);
|
|
9895
|
-
|
|
9896
|
-
// For smooth continuation: if newElement is mid-transition,
|
|
9897
|
-
// calculate new position to maintain seamless sliding
|
|
9898
|
-
let startNewPosition;
|
|
9899
|
-
if (currentNewPosition !== 0 && naturalNewPosition === 0) {
|
|
9900
|
-
startNewPosition = currentNewPosition + containerWidth;
|
|
9901
|
-
debug(
|
|
9902
|
-
"content",
|
|
9903
|
-
"Calculated seamless position:",
|
|
9904
|
-
`${currentNewPosition} + ${containerWidth} = ${startNewPosition}`,
|
|
9905
|
-
);
|
|
9906
|
-
} else {
|
|
9907
|
-
startNewPosition = naturalNewPosition || containerWidth;
|
|
9908
|
-
}
|
|
9909
|
-
|
|
9910
|
-
// For phase transitions, force new content to start from right edge for proper slide-in
|
|
9911
|
-
const effectiveFromPosition = isPhaseTransition
|
|
9912
|
-
? containerWidth
|
|
9913
|
-
: startNewPosition;
|
|
9914
9476
|
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
notifyTransition(newElement, {
|
|
9927
|
-
modelId: slideLeft.id,
|
|
9928
|
-
canOverflow: true,
|
|
9929
|
-
id: "slide_out_old_content",
|
|
9930
|
-
}),
|
|
9931
|
-
from: oldContentPosition,
|
|
9932
|
-
duration,
|
|
9933
|
-
startProgress,
|
|
9934
|
-
onUpdate: ({ value }) => {
|
|
9935
|
-
debug("transition_updates", "Old content slide out:", value);
|
|
9936
|
-
},
|
|
9937
|
-
}),
|
|
9938
|
-
);
|
|
9939
|
-
|
|
9940
|
-
// Slide new content in
|
|
9941
|
-
transitions.push(
|
|
9942
|
-
createTranslateXTransition(newElement, naturalNewPosition, {
|
|
9943
|
-
setup: () =>
|
|
9944
|
-
notifyTransition(newElement, {
|
|
9945
|
-
modelId: slideLeft.id,
|
|
9946
|
-
canOverflow: true,
|
|
9947
|
-
id: "slide_in_new_content",
|
|
9948
|
-
}),
|
|
9949
|
-
from: effectiveFromPosition,
|
|
9950
|
-
duration,
|
|
9951
|
-
startProgress,
|
|
9952
|
-
onUpdate: ({ value, timing }) => {
|
|
9953
|
-
debug("transition_updates", "New content slide in:", value);
|
|
9954
|
-
if (timing === "end") {
|
|
9955
|
-
debug("content", "Slide complete");
|
|
9956
|
-
}
|
|
9957
|
-
},
|
|
9958
|
-
}),
|
|
9959
|
-
);
|
|
9960
|
-
|
|
9961
|
-
return transitions;
|
|
9962
|
-
},
|
|
9963
|
-
};
|
|
9964
|
-
|
|
9965
|
-
const crossFade = {
|
|
9966
|
-
id: "ui_transition_cross_fade",
|
|
9967
|
-
name: "cross-fade",
|
|
9968
|
-
apply: (
|
|
9969
|
-
oldElement,
|
|
9970
|
-
newElement,
|
|
9971
|
-
{ duration, startProgress = 0, isPhaseTransition = false, debug },
|
|
9972
|
-
) => {
|
|
9973
|
-
if (!oldElement && !newElement) {
|
|
9974
|
-
return [];
|
|
9975
|
-
}
|
|
9976
|
-
|
|
9977
|
-
if (!newElement) {
|
|
9978
|
-
// Content -> Empty (fade out only)
|
|
9979
|
-
const from = getOpacity(oldElement);
|
|
9980
|
-
const to = 0;
|
|
9981
|
-
debug("content", "Fade out to empty:", { from, to });
|
|
9982
|
-
return [
|
|
9983
|
-
createOpacityTransition(oldElement, to, {
|
|
9984
|
-
setup: () =>
|
|
9985
|
-
notifyTransition(newElement, {
|
|
9986
|
-
modelId: crossFade.id,
|
|
9987
|
-
canOverflow: true,
|
|
9988
|
-
id: "fade_out_old_content",
|
|
9989
|
-
}),
|
|
9990
|
-
from,
|
|
9991
|
-
duration,
|
|
9992
|
-
startProgress,
|
|
9993
|
-
onUpdate: ({ value, timing }) => {
|
|
9994
|
-
debug("transition_updates", "Content fade out:", value.toFixed(3));
|
|
9995
|
-
if (timing === "end") {
|
|
9996
|
-
debug("content", "Fade out complete");
|
|
9997
|
-
}
|
|
9998
|
-
},
|
|
9999
|
-
}),
|
|
10000
|
-
];
|
|
10001
|
-
}
|
|
10002
|
-
|
|
10003
|
-
if (!oldElement) {
|
|
10004
|
-
// Empty -> Content (fade in only)
|
|
10005
|
-
const from = 0;
|
|
10006
|
-
const to = getOpacityWithoutTransition(newElement);
|
|
10007
|
-
debug("content", "Fade in from empty:", { from, to });
|
|
10008
|
-
return [
|
|
10009
|
-
createOpacityTransition(newElement, to, {
|
|
10010
|
-
setup: () =>
|
|
10011
|
-
notifyTransition(newElement, {
|
|
10012
|
-
modelId: crossFade.id,
|
|
10013
|
-
canOverflow: true,
|
|
10014
|
-
id: "fade_in_new_content",
|
|
10015
|
-
}),
|
|
10016
|
-
from,
|
|
10017
|
-
duration,
|
|
10018
|
-
startProgress,
|
|
10019
|
-
onUpdate: ({ value, timing }) => {
|
|
10020
|
-
debug("transition_updates", "Fade in progress:", value.toFixed(3));
|
|
10021
|
-
if (timing === "end") {
|
|
10022
|
-
debug("content", "Fade in complete");
|
|
10023
|
-
}
|
|
10024
|
-
},
|
|
10025
|
-
}),
|
|
10026
|
-
];
|
|
10027
|
-
}
|
|
10028
|
-
|
|
10029
|
-
// Content -> Content (cross-fade)
|
|
10030
|
-
// Get current opacity for both elements
|
|
10031
|
-
const oldOpacity = getOpacity(oldElement);
|
|
10032
|
-
const newOpacity = getOpacity(newElement);
|
|
10033
|
-
const newNaturalOpacity = getOpacityWithoutTransition(newElement);
|
|
10034
|
-
|
|
10035
|
-
// For phase transitions, always start new content from 0 for clean visual transition
|
|
10036
|
-
// For content transitions, check for ongoing transitions to continue smoothly
|
|
10037
|
-
let effectiveFromOpacity;
|
|
10038
|
-
if (isPhaseTransition) {
|
|
10039
|
-
effectiveFromOpacity = 0; // Always start fresh for phase transitions (loading → content, etc.)
|
|
10040
|
-
} else {
|
|
10041
|
-
// For content transitions: if new element has ongoing opacity transition
|
|
10042
|
-
// (indicated by non-zero opacity when natural opacity is different),
|
|
10043
|
-
// start from current opacity to continue smoothly, otherwise start from 0
|
|
10044
|
-
const hasOngoingTransition =
|
|
10045
|
-
newOpacity !== newNaturalOpacity && newOpacity > 0;
|
|
10046
|
-
effectiveFromOpacity = hasOngoingTransition ? newOpacity : 0;
|
|
10047
|
-
}
|
|
10048
|
-
|
|
10049
|
-
debug("content", "Cross-fade transition:", {
|
|
10050
|
-
oldOpacity: `${oldOpacity} → 0`,
|
|
10051
|
-
newOpacity: `${effectiveFromOpacity} → ${newNaturalOpacity}`,
|
|
10052
|
-
isPhaseTransition,
|
|
10053
|
-
});
|
|
10054
|
-
|
|
10055
|
-
return [
|
|
10056
|
-
createOpacityTransition(oldElement, 0, {
|
|
10057
|
-
setup: () =>
|
|
10058
|
-
notifyTransition(newElement, {
|
|
10059
|
-
modelId: crossFade.id,
|
|
10060
|
-
canOverflow: true,
|
|
10061
|
-
id: "fade_out_old_content",
|
|
10062
|
-
}),
|
|
10063
|
-
from: oldOpacity,
|
|
10064
|
-
duration,
|
|
10065
|
-
startProgress,
|
|
10066
|
-
onUpdate: ({ value }) => {
|
|
10067
|
-
if (value > 0) {
|
|
10068
|
-
debug(
|
|
10069
|
-
"transition_updates",
|
|
10070
|
-
"Old content fade out:",
|
|
10071
|
-
value.toFixed(3),
|
|
10072
|
-
);
|
|
10073
|
-
}
|
|
10074
|
-
},
|
|
10075
|
-
}),
|
|
10076
|
-
createOpacityTransition(newElement, newNaturalOpacity, {
|
|
10077
|
-
setup: () =>
|
|
10078
|
-
notifyTransition(newElement, {
|
|
10079
|
-
modelId: crossFade.id,
|
|
10080
|
-
canOverflow: true,
|
|
10081
|
-
id: "fade_in_new_content",
|
|
10082
|
-
}),
|
|
10083
|
-
from: effectiveFromOpacity,
|
|
10084
|
-
duration,
|
|
10085
|
-
startProgress: isPhaseTransition ? 0 : startProgress, // Phase transitions: new content always starts fresh
|
|
10086
|
-
onUpdate: ({ value, timing }) => {
|
|
10087
|
-
debug("transition_updates", "New content fade in:", value.toFixed(3));
|
|
10088
|
-
if (timing === "end") {
|
|
10089
|
-
debug("content", "Cross-fade complete");
|
|
10090
|
-
}
|
|
10091
|
-
},
|
|
10092
|
-
}),
|
|
10093
|
-
];
|
|
10094
|
-
},
|
|
10095
|
-
};
|
|
10096
|
-
|
|
10097
|
-
const notifyTransition = (element, detail) => {
|
|
10098
|
-
dispatchUITransitionStartCustomEvent(element, detail);
|
|
10099
|
-
return () => {
|
|
10100
|
-
dispatchUITransitionEndCustomEvent(element, detail);
|
|
9477
|
+
transitionTo,
|
|
9478
|
+
resetContent,
|
|
9479
|
+
setDuration,
|
|
9480
|
+
setAlignment,
|
|
9481
|
+
updateAlignment,
|
|
9482
|
+
setPauseBreakpoints: (value) => {
|
|
9483
|
+
groupTransitionOptions.pauseBreakpoints = value;
|
|
9484
|
+
},
|
|
9485
|
+
cleanup: () => {
|
|
9486
|
+
teardown();
|
|
9487
|
+
},
|
|
10101
9488
|
};
|
|
10102
9489
|
};
|
|
10103
|
-
const dispatchUITransitionStartCustomEvent = (element, detail) => {
|
|
10104
|
-
const customEvent = new CustomEvent("ui_transition_start", {
|
|
10105
|
-
bubbles: true,
|
|
10106
|
-
detail,
|
|
10107
|
-
});
|
|
10108
|
-
element.dispatchEvent(customEvent);
|
|
10109
|
-
};
|
|
10110
|
-
const dispatchUITransitionEndCustomEvent = (element, detail) => {
|
|
10111
|
-
const customEvent = new CustomEvent("ui_transition_end", {
|
|
10112
|
-
bubbles: true,
|
|
10113
|
-
detail,
|
|
10114
|
-
});
|
|
10115
|
-
element.dispatchEvent(customEvent);
|
|
10116
|
-
};
|
|
10117
9490
|
|
|
10118
9491
|
/**
|
|
10119
9492
|
* UITransition
|
|
@@ -10130,76 +9503,79 @@ const dispatchUITransitionEndCustomEvent = (element, detail) => {
|
|
|
10130
9503
|
*
|
|
10131
9504
|
* Usage:
|
|
10132
9505
|
* - Wrap dynamic content in <UITransition> to animate between states
|
|
10133
|
-
* - Set a unique `data-content-
|
|
9506
|
+
* - Set a unique `data-content-id` on your rendered content to identify each content variant
|
|
10134
9507
|
* - Use `data-content-phase` to mark loading/error states for phase transitions
|
|
10135
9508
|
* - Configure transition types and durations for both content and phase changes
|
|
10136
9509
|
*
|
|
10137
9510
|
* Example:
|
|
10138
9511
|
*
|
|
10139
|
-
* <UITransition
|
|
10140
|
-
* transitionType="slide-left"
|
|
10141
|
-
* transitionDuration={400}
|
|
10142
|
-
* phaseTransitionType="cross-fade"
|
|
10143
|
-
* phaseTransitionDuration={300}
|
|
10144
|
-
* >
|
|
9512
|
+
* <UITransition>
|
|
10145
9513
|
* {isLoading
|
|
10146
9514
|
* ? <Spinner data-content-key={userId} data-content-phase />
|
|
10147
9515
|
* : <UserProfile user={user} data-content-key={userId} />}
|
|
10148
9516
|
* </UITransition>
|
|
10149
9517
|
*
|
|
10150
|
-
* When `data-content-
|
|
9518
|
+
* When `data-content-id` changes, UITransition animates content transitions.
|
|
10151
9519
|
* When `data-content-phase` changes for the same key, it animates phase transitions.
|
|
10152
9520
|
*/
|
|
10153
9521
|
|
|
10154
|
-
const
|
|
9522
|
+
const UITransitionContentIdContext = createContext();
|
|
10155
9523
|
const UITransition = ({
|
|
10156
9524
|
children,
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
transitionType,
|
|
10161
|
-
transitionDuration,
|
|
10162
|
-
phaseTransitionType,
|
|
10163
|
-
phaseTransitionDuration,
|
|
9525
|
+
contentId,
|
|
9526
|
+
type,
|
|
9527
|
+
duration,
|
|
10164
9528
|
debugDetection,
|
|
9529
|
+
debugContent,
|
|
10165
9530
|
debugSize,
|
|
10166
|
-
|
|
9531
|
+
disabled,
|
|
9532
|
+
uiTransitionRef,
|
|
10167
9533
|
...props
|
|
10168
9534
|
}) => {
|
|
10169
|
-
const
|
|
10170
|
-
const
|
|
10171
|
-
const
|
|
10172
|
-
|
|
10173
|
-
|
|
9535
|
+
const contentIdRef = useRef(contentId);
|
|
9536
|
+
const updateContentId = () => {
|
|
9537
|
+
const uiTransition = uiTransitionRef.current;
|
|
9538
|
+
if (!uiTransition) {
|
|
9539
|
+
return;
|
|
9540
|
+
}
|
|
9541
|
+
const value = contentIdRef.current;
|
|
9542
|
+
uiTransition.updateContentId(value);
|
|
9543
|
+
};
|
|
9544
|
+
const uiTransitionContentIdContextValue = useMemo(() => {
|
|
9545
|
+
const set = new Set();
|
|
9546
|
+
const onSetChange = () => {
|
|
9547
|
+
const value = Array.from(set).join("|");
|
|
9548
|
+
contentIdRef.current = value;
|
|
9549
|
+
updateContentId();
|
|
10174
9550
|
};
|
|
10175
|
-
const update = (
|
|
10176
|
-
if (!
|
|
10177
|
-
console.warn(`UITransition: trying to update
|
|
9551
|
+
const update = (part, newPart) => {
|
|
9552
|
+
if (!set.has(part)) {
|
|
9553
|
+
console.warn(`UITransition: trying to update an id that does not exist: ${part}`);
|
|
10178
9554
|
return;
|
|
10179
9555
|
}
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
9556
|
+
set.delete(part);
|
|
9557
|
+
set.add(newPart);
|
|
9558
|
+
onSetChange();
|
|
10183
9559
|
};
|
|
10184
|
-
const add =
|
|
10185
|
-
if (!
|
|
9560
|
+
const add = part => {
|
|
9561
|
+
if (!part) {
|
|
10186
9562
|
return;
|
|
10187
9563
|
}
|
|
10188
|
-
if (
|
|
9564
|
+
if (set.has(part)) {
|
|
10189
9565
|
return;
|
|
10190
9566
|
}
|
|
10191
|
-
|
|
10192
|
-
|
|
9567
|
+
set.add(part);
|
|
9568
|
+
onSetChange();
|
|
10193
9569
|
};
|
|
10194
|
-
const remove =
|
|
10195
|
-
if (!
|
|
9570
|
+
const remove = part => {
|
|
9571
|
+
if (!part) {
|
|
10196
9572
|
return;
|
|
10197
9573
|
}
|
|
10198
|
-
if (!
|
|
9574
|
+
if (!set.has(part)) {
|
|
10199
9575
|
return;
|
|
10200
9576
|
}
|
|
10201
|
-
|
|
10202
|
-
|
|
9577
|
+
set.delete(part);
|
|
9578
|
+
onSetChange();
|
|
10203
9579
|
};
|
|
10204
9580
|
return {
|
|
10205
9581
|
add,
|
|
@@ -10207,42 +9583,51 @@ const UITransition = ({
|
|
|
10207
9583
|
remove
|
|
10208
9584
|
};
|
|
10209
9585
|
}, []);
|
|
10210
|
-
const effectiveContentKey = contentKey || contentKeyFromContext;
|
|
10211
9586
|
const ref = useRef();
|
|
9587
|
+
const uiTransitionRefDefault = useRef();
|
|
9588
|
+
uiTransitionRef = uiTransitionRef || uiTransitionRefDefault;
|
|
10212
9589
|
useLayoutEffect(() => {
|
|
10213
|
-
|
|
9590
|
+
if (disabled) {
|
|
9591
|
+
return null;
|
|
9592
|
+
}
|
|
9593
|
+
const uiTransition = createUITransitionController(ref.current);
|
|
9594
|
+
uiTransitionRef.current = uiTransition;
|
|
10214
9595
|
return () => {
|
|
10215
9596
|
uiTransition.cleanup();
|
|
10216
9597
|
};
|
|
10217
|
-
}, []);
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
"
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
children:
|
|
10236
|
-
|
|
10237
|
-
"data-content-key": effectiveContentKey ? effectiveContentKey : undefined,
|
|
9598
|
+
}, [disabled]);
|
|
9599
|
+
if (disabled) {
|
|
9600
|
+
return children;
|
|
9601
|
+
}
|
|
9602
|
+
return jsxs("div", {
|
|
9603
|
+
ref: ref,
|
|
9604
|
+
...props,
|
|
9605
|
+
className: "ui_transition",
|
|
9606
|
+
"data-transition-type": type,
|
|
9607
|
+
"data-transition-duration": duration,
|
|
9608
|
+
"data-debug-detection": debugDetection ? "" : undefined,
|
|
9609
|
+
"data-debug-size": debugSize ? "" : undefined,
|
|
9610
|
+
"data-debug-content": debugContent ? "" : undefined,
|
|
9611
|
+
children: [jsxs("div", {
|
|
9612
|
+
className: "active_group",
|
|
9613
|
+
children: [jsx("div", {
|
|
9614
|
+
className: "target_slot",
|
|
9615
|
+
"data-content-id": contentIdRef.current ? contentIdRef.current : undefined,
|
|
9616
|
+
children: jsx(UITransitionContentIdContext.Provider, {
|
|
9617
|
+
value: uiTransitionContentIdContextValue,
|
|
10238
9618
|
children: children
|
|
10239
|
-
})
|
|
10240
|
-
className: "ui_transition_phase_overlay"
|
|
10241
|
-
})]
|
|
9619
|
+
})
|
|
10242
9620
|
}), jsx("div", {
|
|
10243
|
-
className: "
|
|
9621
|
+
className: "outgoing_slot"
|
|
10244
9622
|
})]
|
|
10245
|
-
})
|
|
9623
|
+
}), jsxs("div", {
|
|
9624
|
+
className: "previous_group",
|
|
9625
|
+
children: [jsx("div", {
|
|
9626
|
+
className: "previous_target_slot"
|
|
9627
|
+
}), jsx("div", {
|
|
9628
|
+
className: "previous_outgoing_slot"
|
|
9629
|
+
})]
|
|
9630
|
+
})]
|
|
10246
9631
|
});
|
|
10247
9632
|
};
|
|
10248
9633
|
|
|
@@ -10254,28 +9639,28 @@ const UITransition = ({
|
|
|
10254
9639
|
* as changed even if the component is still the same
|
|
10255
9640
|
*
|
|
10256
9641
|
* This is used by <Route> to set the content key to the route path
|
|
10257
|
-
* When the route becomes inactive it will call
|
|
10258
|
-
* And if a sibling route becones active it will call
|
|
9642
|
+
* When the route becomes inactive it will call useUITransitionContentId(undefined)
|
|
9643
|
+
* And if a sibling route becones active it will call useUITransitionContentId with its own path
|
|
10259
9644
|
*
|
|
10260
9645
|
*/
|
|
10261
|
-
const
|
|
10262
|
-
const
|
|
10263
|
-
const
|
|
10264
|
-
if (
|
|
10265
|
-
const
|
|
10266
|
-
|
|
10267
|
-
if (
|
|
10268
|
-
|
|
9646
|
+
const useUITransitionContentId = value => {
|
|
9647
|
+
const contentId = useContext(UITransitionContentIdContext);
|
|
9648
|
+
const valueRef = useRef();
|
|
9649
|
+
if (contentId !== undefined && valueRef.current !== value) {
|
|
9650
|
+
const previousValue = valueRef.current;
|
|
9651
|
+
valueRef.current = value;
|
|
9652
|
+
if (previousValue === undefined) {
|
|
9653
|
+
contentId.add(value);
|
|
10269
9654
|
} else {
|
|
10270
|
-
|
|
9655
|
+
contentId.update(previousValue, value);
|
|
10271
9656
|
}
|
|
10272
9657
|
}
|
|
10273
9658
|
useLayoutEffect(() => {
|
|
10274
|
-
if (
|
|
9659
|
+
if (contentId === undefined) {
|
|
10275
9660
|
return null;
|
|
10276
9661
|
}
|
|
10277
9662
|
return () => {
|
|
10278
|
-
|
|
9663
|
+
contentId.remove(valueRef.current);
|
|
10279
9664
|
};
|
|
10280
9665
|
}, []);
|
|
10281
9666
|
};
|
|
@@ -10331,10 +9716,13 @@ const Routes = ({
|
|
|
10331
9716
|
});
|
|
10332
9717
|
};
|
|
10333
9718
|
const SlotContext = createContext(null);
|
|
9719
|
+
const RouteInfoContext = createContext(null);
|
|
9720
|
+
const useActiveRouteInfo = () => useContext(RouteInfoContext);
|
|
10334
9721
|
const Route = ({
|
|
10335
9722
|
element,
|
|
10336
9723
|
route,
|
|
10337
9724
|
fallback,
|
|
9725
|
+
meta,
|
|
10338
9726
|
children
|
|
10339
9727
|
}) => {
|
|
10340
9728
|
const forceRender = useForceRender();
|
|
@@ -10345,6 +9733,7 @@ const Route = ({
|
|
|
10345
9733
|
element: element,
|
|
10346
9734
|
route: route,
|
|
10347
9735
|
fallback: fallback,
|
|
9736
|
+
meta: meta,
|
|
10348
9737
|
onActiveInfoChange: activeInfo => {
|
|
10349
9738
|
hasDiscoveredRef.current = true;
|
|
10350
9739
|
activeInfoRef.current = activeInfo;
|
|
@@ -10371,18 +9760,23 @@ const ActiveRouteManager = ({
|
|
|
10371
9760
|
element,
|
|
10372
9761
|
route,
|
|
10373
9762
|
fallback,
|
|
9763
|
+
meta,
|
|
10374
9764
|
onActiveInfoChange,
|
|
10375
9765
|
children
|
|
10376
9766
|
}) => {
|
|
9767
|
+
if (route && fallback) {
|
|
9768
|
+
throw new Error("Route cannot have both route and fallback props");
|
|
9769
|
+
}
|
|
10377
9770
|
const registerChildRouteFromContext = useContext(RegisterChildRouteContext);
|
|
10378
9771
|
getElementSignature(element);
|
|
10379
9772
|
const candidateSet = new Set();
|
|
10380
|
-
const registerChildRoute = (ChildActiveElement, childRoute, childFallback) => {
|
|
9773
|
+
const registerChildRoute = (ChildActiveElement, childRoute, childFallback, childMeta) => {
|
|
10381
9774
|
getElementSignature(ChildActiveElement);
|
|
10382
9775
|
candidateSet.add({
|
|
10383
9776
|
ActiveElement: ChildActiveElement,
|
|
10384
9777
|
route: childRoute,
|
|
10385
|
-
fallback: childFallback
|
|
9778
|
+
fallback: childFallback,
|
|
9779
|
+
meta: childMeta
|
|
10386
9780
|
});
|
|
10387
9781
|
};
|
|
10388
9782
|
useLayoutEffect(() => {
|
|
@@ -10390,6 +9784,7 @@ const ActiveRouteManager = ({
|
|
|
10390
9784
|
element,
|
|
10391
9785
|
route,
|
|
10392
9786
|
fallback,
|
|
9787
|
+
meta,
|
|
10393
9788
|
candidateSet,
|
|
10394
9789
|
onActiveInfoChange,
|
|
10395
9790
|
registerChildRouteFromContext
|
|
@@ -10404,6 +9799,7 @@ const initRouteObserver = ({
|
|
|
10404
9799
|
element,
|
|
10405
9800
|
route,
|
|
10406
9801
|
fallback,
|
|
9802
|
+
meta,
|
|
10407
9803
|
candidateSet,
|
|
10408
9804
|
onActiveInfoChange,
|
|
10409
9805
|
registerChildRouteFromContext
|
|
@@ -10424,19 +9820,13 @@ const initRouteObserver = ({
|
|
|
10424
9820
|
let fallbackInfo = null;
|
|
10425
9821
|
for (const candidate of candidateSet) {
|
|
10426
9822
|
if (candidate.route?.active) {
|
|
10427
|
-
return
|
|
10428
|
-
ChildActiveElement: candidate.ActiveElement,
|
|
10429
|
-
route: candidate.route
|
|
10430
|
-
};
|
|
9823
|
+
return candidate;
|
|
10431
9824
|
}
|
|
10432
9825
|
// fallback without route can match when no other route matches.
|
|
10433
9826
|
// This is useful solely for "catch all" fallback used on the <Routes>
|
|
10434
9827
|
// otherwise a fallback would always match and make the parent route always active
|
|
10435
9828
|
if (candidate.fallback && !candidate.route.routeFromProps) {
|
|
10436
|
-
fallbackInfo =
|
|
10437
|
-
ChildActiveElement: candidate.ActiveElement,
|
|
10438
|
-
route: candidate.route
|
|
10439
|
-
};
|
|
9829
|
+
fallbackInfo = candidate;
|
|
10440
9830
|
}
|
|
10441
9831
|
}
|
|
10442
9832
|
return fallbackInfo;
|
|
@@ -10453,8 +9843,9 @@ const initRouteObserver = ({
|
|
|
10453
9843
|
return activeChildInfo;
|
|
10454
9844
|
}
|
|
10455
9845
|
return {
|
|
10456
|
-
|
|
10457
|
-
route
|
|
9846
|
+
ActiveElement: null,
|
|
9847
|
+
route,
|
|
9848
|
+
meta
|
|
10458
9849
|
};
|
|
10459
9850
|
} : () => {
|
|
10460
9851
|
// we don't have a route, do we have an active child?
|
|
@@ -10464,22 +9855,22 @@ const initRouteObserver = ({
|
|
|
10464
9855
|
}
|
|
10465
9856
|
return null;
|
|
10466
9857
|
};
|
|
10467
|
-
const
|
|
9858
|
+
const activeRouteInfoSignal = signal();
|
|
10468
9859
|
const SlotActiveElementSignal = signal();
|
|
10469
9860
|
const ActiveElement = () => {
|
|
10470
|
-
const
|
|
10471
|
-
|
|
9861
|
+
const activeRouteInfo = activeRouteInfoSignal.value;
|
|
9862
|
+
useUITransitionContentId(activeRouteInfo ? activeRouteInfo.route.urlPattern : fallback ? "fallback" : undefined);
|
|
10472
9863
|
const SlotActiveElement = SlotActiveElementSignal.value;
|
|
10473
9864
|
if (typeof element === "function") {
|
|
10474
9865
|
const Element = element;
|
|
10475
|
-
|
|
10476
|
-
value: SlotActiveElement,
|
|
10477
|
-
children: jsx(Element, {})
|
|
10478
|
-
});
|
|
9866
|
+
element = jsx(Element, {});
|
|
10479
9867
|
}
|
|
10480
|
-
return jsx(
|
|
10481
|
-
value:
|
|
10482
|
-
children:
|
|
9868
|
+
return jsx(RouteInfoContext.Provider, {
|
|
9869
|
+
value: activeRouteInfo,
|
|
9870
|
+
children: jsx(SlotContext.Provider, {
|
|
9871
|
+
value: SlotActiveElement,
|
|
9872
|
+
children: element
|
|
9873
|
+
})
|
|
10483
9874
|
});
|
|
10484
9875
|
};
|
|
10485
9876
|
ActiveElement.underlyingElementId = candidateSet.size === 0 ? `${getElementSignature(element)} without slot` : `[${getElementSignature(element)} with slot one of ${candidateElementIds}]`;
|
|
@@ -10487,20 +9878,17 @@ const initRouteObserver = ({
|
|
|
10487
9878
|
const newActiveInfo = getActiveInfo();
|
|
10488
9879
|
if (newActiveInfo) {
|
|
10489
9880
|
compositeRoute.active = true;
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
ChildActiveElement
|
|
10493
|
-
} = newActiveInfo;
|
|
10494
|
-
activeRouteSignal.value = route;
|
|
10495
|
-
SlotActiveElementSignal.value = ChildActiveElement;
|
|
9881
|
+
activeRouteInfoSignal.value = newActiveInfo;
|
|
9882
|
+
SlotActiveElementSignal.value = newActiveInfo.ActiveElement;
|
|
10496
9883
|
onActiveInfoChange({
|
|
10497
|
-
route: newActiveInfo.route,
|
|
10498
9884
|
ActiveElement,
|
|
10499
|
-
SlotActiveElement:
|
|
9885
|
+
SlotActiveElement: newActiveInfo.ActiveElement,
|
|
9886
|
+
route: newActiveInfo.route,
|
|
9887
|
+
meta: newActiveInfo.meta
|
|
10500
9888
|
});
|
|
10501
9889
|
} else {
|
|
10502
9890
|
compositeRoute.active = false;
|
|
10503
|
-
|
|
9891
|
+
activeRouteInfoSignal.value = null;
|
|
10504
9892
|
SlotActiveElementSignal.value = null;
|
|
10505
9893
|
onActiveInfoChange(null);
|
|
10506
9894
|
}
|
|
@@ -10516,7 +9904,7 @@ const initRouteObserver = ({
|
|
|
10516
9904
|
candidate.route.subscribeStatus(onChange);
|
|
10517
9905
|
}
|
|
10518
9906
|
if (registerChildRouteFromContext) {
|
|
10519
|
-
registerChildRouteFromContext(ActiveElement, compositeRoute, fallback);
|
|
9907
|
+
registerChildRouteFromContext(ActiveElement, compositeRoute, fallback, meta);
|
|
10520
9908
|
}
|
|
10521
9909
|
updateActiveInfo();
|
|
10522
9910
|
};
|
|
@@ -10718,14 +10106,11 @@ const renderActionableComponent = (props, {
|
|
|
10718
10106
|
|
|
10719
10107
|
const normalizeSpacingStyle = (value, property = "padding") => {
|
|
10720
10108
|
const cssSize = sizeSpacingScale[value];
|
|
10721
|
-
return cssSize ||
|
|
10109
|
+
return cssSize || stringifyStyle(value, property);
|
|
10722
10110
|
};
|
|
10723
10111
|
const normalizeTypoStyle = (value, property = "fontSize") => {
|
|
10724
10112
|
const cssSize = sizeTypoScale[value];
|
|
10725
|
-
return cssSize ||
|
|
10726
|
-
};
|
|
10727
|
-
const normalizeCssStyle = (value, property) => {
|
|
10728
|
-
return normalizeStyle(value, property, "css");
|
|
10113
|
+
return cssSize || stringifyStyle(value, property);
|
|
10729
10114
|
};
|
|
10730
10115
|
|
|
10731
10116
|
const PASS_THROUGH = { name: "pass_through" };
|
|
@@ -10776,6 +10161,13 @@ const applyOnTwoProps = (propA, propB) => {
|
|
|
10776
10161
|
};
|
|
10777
10162
|
};
|
|
10778
10163
|
|
|
10164
|
+
const LAYOUT_PROPS = {
|
|
10165
|
+
// all are handled by data-attributes
|
|
10166
|
+
inline: () => {},
|
|
10167
|
+
box: () => {},
|
|
10168
|
+
row: () => {},
|
|
10169
|
+
column: () => {},
|
|
10170
|
+
};
|
|
10779
10171
|
const OUTER_SPACING_PROPS = {
|
|
10780
10172
|
margin: PASS_THROUGH,
|
|
10781
10173
|
marginLeft: PASS_THROUGH,
|
|
@@ -10812,21 +10204,21 @@ const DIMENSION_PROPS = {
|
|
|
10812
10204
|
return { flexGrow: 1 }; // Grow horizontally in row
|
|
10813
10205
|
}
|
|
10814
10206
|
if (parentLayout === "row") {
|
|
10815
|
-
return { minWidth: "100%" }; // Take full width in column
|
|
10207
|
+
return { minWidth: "100%", width: "auto" }; // Take full width in column
|
|
10816
10208
|
}
|
|
10817
|
-
return { minWidth: "100%" }; // Take full width outside flex
|
|
10209
|
+
return { minWidth: "100%", width: "auto" }; // Take full width outside flex
|
|
10818
10210
|
},
|
|
10819
10211
|
expandY: (value, { parentLayout }) => {
|
|
10820
10212
|
if (!value) {
|
|
10821
10213
|
return null;
|
|
10822
10214
|
}
|
|
10823
10215
|
if (parentLayout === "column") {
|
|
10824
|
-
return { minHeight: "100%" }; // Make column full height
|
|
10216
|
+
return { minHeight: "100%", height: "auto" }; // Make column full height
|
|
10825
10217
|
}
|
|
10826
10218
|
if (parentLayout === "row" || parentLayout === "inline-row") {
|
|
10827
10219
|
return { flexGrow: 1 }; // Make row full height
|
|
10828
10220
|
}
|
|
10829
|
-
return { minHeight: "100%" }; // Take full height outside flex
|
|
10221
|
+
return { minHeight: "100%", height: "auto" }; // Take full height outside flex
|
|
10830
10222
|
},
|
|
10831
10223
|
shrinkX: (value, { parentLayout }) => {
|
|
10832
10224
|
if (!value) {
|
|
@@ -10846,6 +10238,23 @@ const DIMENSION_PROPS = {
|
|
|
10846
10238
|
}
|
|
10847
10239
|
return { maxHeight: "100%" };
|
|
10848
10240
|
},
|
|
10241
|
+
|
|
10242
|
+
scaleX: (value) => {
|
|
10243
|
+
return { transform: `scaleX(${stringifyStyle(value, "scaleX")})` };
|
|
10244
|
+
},
|
|
10245
|
+
scaleY: (value) => {
|
|
10246
|
+
return { transform: `scaleY(${value})` };
|
|
10247
|
+
},
|
|
10248
|
+
scale: (value) => {
|
|
10249
|
+
if (Array.isArray(value)) {
|
|
10250
|
+
const [x, y] = value;
|
|
10251
|
+
return { transform: `scale(${x}, ${y})` };
|
|
10252
|
+
}
|
|
10253
|
+
return { transform: `scale(${value})` };
|
|
10254
|
+
},
|
|
10255
|
+
scaleZ: (value) => {
|
|
10256
|
+
return { transform: `scaleZ(${value})` };
|
|
10257
|
+
},
|
|
10849
10258
|
};
|
|
10850
10259
|
const POSITION_PROPS = {
|
|
10851
10260
|
// For row, alignX uses auto margins for positioning
|
|
@@ -10886,7 +10295,7 @@ const POSITION_PROPS = {
|
|
|
10886
10295
|
|
|
10887
10296
|
if (value === "start") {
|
|
10888
10297
|
if (inlineColumnLayout) {
|
|
10889
|
-
return
|
|
10298
|
+
return { alignSelf: "start" };
|
|
10890
10299
|
}
|
|
10891
10300
|
return { marginBottom: "auto" };
|
|
10892
10301
|
}
|
|
@@ -10906,8 +10315,49 @@ const POSITION_PROPS = {
|
|
|
10906
10315
|
},
|
|
10907
10316
|
left: PASS_THROUGH,
|
|
10908
10317
|
top: PASS_THROUGH,
|
|
10318
|
+
|
|
10319
|
+
translateX: (value) => {
|
|
10320
|
+
return { transform: `translateX(${value})` };
|
|
10321
|
+
},
|
|
10322
|
+
translateY: (value) => {
|
|
10323
|
+
return { transform: `translateY(${value})` };
|
|
10324
|
+
},
|
|
10325
|
+
translate: (value) => {
|
|
10326
|
+
if (Array.isArray(value)) {
|
|
10327
|
+
const [x, y] = value;
|
|
10328
|
+
return { transform: `translate(${x}, ${y})` };
|
|
10329
|
+
}
|
|
10330
|
+
return { transform: `translate(${stringifyStyle(value, "translateX")})` };
|
|
10331
|
+
},
|
|
10332
|
+
rotateX: (value) => {
|
|
10333
|
+
return { transform: `rotateX(${value})` };
|
|
10334
|
+
},
|
|
10335
|
+
rotateY: (value) => {
|
|
10336
|
+
return { transform: `rotateY(${value})` };
|
|
10337
|
+
},
|
|
10338
|
+
rotateZ: (value) => {
|
|
10339
|
+
return { transform: `rotateZ(${value})` };
|
|
10340
|
+
},
|
|
10341
|
+
rotate: (value) => {
|
|
10342
|
+
return { transform: `rotate(${value})` };
|
|
10343
|
+
},
|
|
10344
|
+
skewX: (value) => {
|
|
10345
|
+
return { transform: `skewX(${value})` };
|
|
10346
|
+
},
|
|
10347
|
+
skewY: (value) => {
|
|
10348
|
+
return { transform: `skewY(${value})` };
|
|
10349
|
+
},
|
|
10350
|
+
skew: (value) => {
|
|
10351
|
+
if (Array.isArray(value)) {
|
|
10352
|
+
const [x, y] = value;
|
|
10353
|
+
return { transform: `skew(${x}, ${y})` };
|
|
10354
|
+
}
|
|
10355
|
+
return { transform: `skew(${value})` };
|
|
10356
|
+
},
|
|
10909
10357
|
};
|
|
10910
10358
|
const TYPO_PROPS = {
|
|
10359
|
+
font: PASS_THROUGH,
|
|
10360
|
+
fontFamily: PASS_THROUGH,
|
|
10911
10361
|
size: applyOnCSSProp("fontSize"),
|
|
10912
10362
|
fontSize: PASS_THROUGH,
|
|
10913
10363
|
bold: applyToCssPropWhenTruthy("fontWeight", "bold", "normal"),
|
|
@@ -10924,6 +10374,11 @@ const TYPO_PROPS = {
|
|
|
10924
10374
|
preWrap: applyToCssPropWhenTruthy("whiteSpace", "pre-wrap", "normal"),
|
|
10925
10375
|
};
|
|
10926
10376
|
const VISUAL_PROPS = {
|
|
10377
|
+
outline: PASS_THROUGH,
|
|
10378
|
+
outlineStyle: PASS_THROUGH,
|
|
10379
|
+
outlineColor: PASS_THROUGH,
|
|
10380
|
+
outlineWidth: PASS_THROUGH,
|
|
10381
|
+
boxDecorationBreak: PASS_THROUGH,
|
|
10927
10382
|
boxShadow: PASS_THROUGH,
|
|
10928
10383
|
background: PASS_THROUGH,
|
|
10929
10384
|
backgroundColor: PASS_THROUGH,
|
|
@@ -11003,6 +10458,7 @@ const CONTENT_PROPS = {
|
|
|
11003
10458
|
},
|
|
11004
10459
|
};
|
|
11005
10460
|
const All_PROPS = {
|
|
10461
|
+
...LAYOUT_PROPS,
|
|
11006
10462
|
...OUTER_SPACING_PROPS,
|
|
11007
10463
|
...INNER_SPACING_PROPS,
|
|
11008
10464
|
...DIMENSION_PROPS,
|
|
@@ -11011,6 +10467,7 @@ const All_PROPS = {
|
|
|
11011
10467
|
...VISUAL_PROPS,
|
|
11012
10468
|
...CONTENT_PROPS,
|
|
11013
10469
|
};
|
|
10470
|
+
const LAYOUT_PROP_NAME_SET = new Set(Object.keys(LAYOUT_PROPS));
|
|
11014
10471
|
const OUTER_SPACING_PROP_NAME_SET = new Set(Object.keys(OUTER_SPACING_PROPS));
|
|
11015
10472
|
const INNER_SPACING_PROP_NAME_SET = new Set(Object.keys(INNER_SPACING_PROPS));
|
|
11016
10473
|
const DIMENSION_PROP_NAME_SET = new Set(Object.keys(DIMENSION_PROPS));
|
|
@@ -11026,6 +10483,7 @@ const HANDLED_BY_VISUAL_CHILD_PROP_SET = new Set([
|
|
|
11026
10483
|
...CONTENT_PROP_NAME_SET,
|
|
11027
10484
|
]);
|
|
11028
10485
|
const COPIED_ON_VISUAL_CHILD_PROP_SET = new Set([
|
|
10486
|
+
...LAYOUT_PROP_NAME_SET,
|
|
11029
10487
|
"expand",
|
|
11030
10488
|
"shrink",
|
|
11031
10489
|
"expandX",
|
|
@@ -11037,6 +10495,9 @@ const COPIED_ON_VISUAL_CHILD_PROP_SET = new Set([
|
|
|
11037
10495
|
const isStyleProp = (name) => STYLE_PROP_NAME_SET.has(name);
|
|
11038
10496
|
|
|
11039
10497
|
const getStylePropGroup = (name) => {
|
|
10498
|
+
if (LAYOUT_PROP_NAME_SET.has(name)) {
|
|
10499
|
+
return "layout";
|
|
10500
|
+
}
|
|
11040
10501
|
if (OUTER_SPACING_PROP_NAME_SET.has(name)) {
|
|
11041
10502
|
return "margin";
|
|
11042
10503
|
}
|
|
@@ -11068,7 +10529,7 @@ const getNormalizer = (key) => {
|
|
|
11068
10529
|
if (group === "typo") {
|
|
11069
10530
|
return normalizeTypoStyle;
|
|
11070
10531
|
}
|
|
11071
|
-
return
|
|
10532
|
+
return stringifyStyle;
|
|
11072
10533
|
};
|
|
11073
10534
|
|
|
11074
10535
|
const assignStyle = (
|
|
@@ -11076,7 +10537,7 @@ const assignStyle = (
|
|
|
11076
10537
|
propValue,
|
|
11077
10538
|
propName,
|
|
11078
10539
|
styleContext,
|
|
11079
|
-
|
|
10540
|
+
context = "js",
|
|
11080
10541
|
) => {
|
|
11081
10542
|
if (propValue === undefined) {
|
|
11082
10543
|
return;
|
|
@@ -11085,6 +10546,7 @@ const assignStyle = (
|
|
|
11085
10546
|
if (!managedByCSSVars) {
|
|
11086
10547
|
throw new Error("managedByCSSVars is required in styleContext");
|
|
11087
10548
|
}
|
|
10549
|
+
const normalizer = getNormalizer(propName);
|
|
11088
10550
|
const getStyle = All_PROPS[propName];
|
|
11089
10551
|
if (
|
|
11090
10552
|
getStyle === PASS_THROUGH ||
|
|
@@ -11093,10 +10555,16 @@ const assignStyle = (
|
|
|
11093
10555
|
) {
|
|
11094
10556
|
const cssValue = normalizer(propValue, propName);
|
|
11095
10557
|
const cssVar = managedByCSSVars[propName];
|
|
10558
|
+
const mergedValue = mergeOneStyle(
|
|
10559
|
+
styleObject[propName],
|
|
10560
|
+
cssValue,
|
|
10561
|
+
propName,
|
|
10562
|
+
context,
|
|
10563
|
+
);
|
|
11096
10564
|
if (cssVar) {
|
|
11097
|
-
styleObject[cssVar] =
|
|
10565
|
+
styleObject[cssVar] = mergedValue;
|
|
11098
10566
|
} else {
|
|
11099
|
-
styleObject[propName] =
|
|
10567
|
+
styleObject[propName] = mergedValue;
|
|
11100
10568
|
}
|
|
11101
10569
|
return;
|
|
11102
10570
|
}
|
|
@@ -11108,10 +10576,11 @@ const assignStyle = (
|
|
|
11108
10576
|
const value = values[key];
|
|
11109
10577
|
const cssValue = normalizer(value, key);
|
|
11110
10578
|
const cssVar = managedByCSSVars[key];
|
|
10579
|
+
const mergedValue = mergeOneStyle(styleObject[key], cssValue, key, context);
|
|
11111
10580
|
if (cssVar) {
|
|
11112
|
-
styleObject[cssVar] =
|
|
10581
|
+
styleObject[cssVar] = mergedValue;
|
|
11113
10582
|
} else {
|
|
11114
|
-
styleObject[key] =
|
|
10583
|
+
styleObject[key] = mergedValue;
|
|
11115
10584
|
}
|
|
11116
10585
|
}
|
|
11117
10586
|
};
|
|
@@ -11128,12 +10597,8 @@ const sizeSpacingScale = {
|
|
|
11128
10597
|
xl: "2em", // 2 = 32px at 16px base
|
|
11129
10598
|
xxl: "3em", // 3 = 48px at 16px base
|
|
11130
10599
|
};
|
|
11131
|
-
const resolveSpacingSize = (
|
|
11132
|
-
size,
|
|
11133
|
-
property = "padding",
|
|
11134
|
-
context = "css",
|
|
11135
|
-
) => {
|
|
11136
|
-
return normalizeStyle(sizeSpacingScale[size] || size, property, context);
|
|
10600
|
+
const resolveSpacingSize = (size, property = "padding") => {
|
|
10601
|
+
return stringifyStyle(sizeSpacingScale[size] || size, property);
|
|
11137
10602
|
};
|
|
11138
10603
|
|
|
11139
10604
|
const sizeTypoScale = {
|
|
@@ -11511,7 +10976,7 @@ const initPseudoStyles = (
|
|
|
11511
10976
|
}
|
|
11512
10977
|
currentState[pseudoClass] = currentValue;
|
|
11513
10978
|
const oldValue = state ? state[pseudoClass] : undefined;
|
|
11514
|
-
if (oldValue !== currentValue) {
|
|
10979
|
+
if (oldValue !== currentValue || !state) {
|
|
11515
10980
|
someChange = true;
|
|
11516
10981
|
const { attribute, add, remove } = pseudoClassDefinition;
|
|
11517
10982
|
if (currentValue) {
|
|
@@ -11566,8 +11031,15 @@ const applyStyle = (element, style, pseudoState, pseudoNamedStyles) => {
|
|
|
11566
11031
|
updateStyle(element, getStyleToApply(style, pseudoState, pseudoNamedStyles));
|
|
11567
11032
|
};
|
|
11568
11033
|
|
|
11034
|
+
const PSEUDO_STATE_DEFAULT = {};
|
|
11035
|
+
const PSEUDO_NAMED_STYLES_DEFAULT = {};
|
|
11569
11036
|
const getStyleToApply = (styles, pseudoState, pseudoNamedStyles) => {
|
|
11570
|
-
if (
|
|
11037
|
+
if (
|
|
11038
|
+
!pseudoState ||
|
|
11039
|
+
pseudoState === PSEUDO_STATE_DEFAULT ||
|
|
11040
|
+
!pseudoNamedStyles ||
|
|
11041
|
+
pseudoNamedStyles === PSEUDO_NAMED_STYLES_DEFAULT
|
|
11042
|
+
) {
|
|
11571
11043
|
return styles;
|
|
11572
11044
|
}
|
|
11573
11045
|
|
|
@@ -11634,7 +11106,13 @@ const updateStyle = (element, style) => {
|
|
|
11634
11106
|
}
|
|
11635
11107
|
}
|
|
11636
11108
|
for (const toDeleteKey of toDeleteKeySet) {
|
|
11637
|
-
|
|
11109
|
+
if (toDeleteKey.startsWith("--")) {
|
|
11110
|
+
element.style.removeProperty(toDeleteKey);
|
|
11111
|
+
} else {
|
|
11112
|
+
// we can't use removeProperty because "toDeleteKey" is in camelCase
|
|
11113
|
+
// e.g., backgroundColor (and it's safer to just let the browser do the conversion)
|
|
11114
|
+
element.style[toDeleteKey] = "";
|
|
11115
|
+
}
|
|
11638
11116
|
}
|
|
11639
11117
|
styleKeySetWeakMap.set(element, styleKeySet);
|
|
11640
11118
|
return;
|
|
@@ -11693,8 +11171,10 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
11693
11171
|
flex-direction: row;
|
|
11694
11172
|
}
|
|
11695
11173
|
|
|
11696
|
-
[data-layout-row] >
|
|
11697
|
-
[data-layout-
|
|
11174
|
+
[data-layout-row] > [data-layout-row],
|
|
11175
|
+
[data-layout-row] > [data-layout-column],
|
|
11176
|
+
[data-layout-column] > [data-layout-column],
|
|
11177
|
+
[data-layout-column] > [data-layout-row] {
|
|
11698
11178
|
flex-shrink: 0;
|
|
11699
11179
|
}
|
|
11700
11180
|
|
|
@@ -11709,9 +11189,6 @@ const MANAGED_BY_CSS_VARS_DEFAULT = {};
|
|
|
11709
11189
|
const Box = props => {
|
|
11710
11190
|
const {
|
|
11711
11191
|
as = "div",
|
|
11712
|
-
layoutRow,
|
|
11713
|
-
layoutColumn,
|
|
11714
|
-
layoutInline,
|
|
11715
11192
|
baseClassName,
|
|
11716
11193
|
className,
|
|
11717
11194
|
baseStyle,
|
|
@@ -11741,30 +11218,78 @@ const Box = props => {
|
|
|
11741
11218
|
const defaultRef = useRef();
|
|
11742
11219
|
const ref = props.ref || defaultRef;
|
|
11743
11220
|
const TagName = as;
|
|
11221
|
+
const {
|
|
11222
|
+
box,
|
|
11223
|
+
inline = box,
|
|
11224
|
+
row,
|
|
11225
|
+
column = box
|
|
11226
|
+
} = rest;
|
|
11744
11227
|
let layout;
|
|
11745
|
-
if (
|
|
11746
|
-
if (
|
|
11228
|
+
if (inline) {
|
|
11229
|
+
if (row) {
|
|
11747
11230
|
layout = "inline-row";
|
|
11748
|
-
} else if (
|
|
11231
|
+
} else if (column) {
|
|
11749
11232
|
layout = "inline-column";
|
|
11750
11233
|
} else {
|
|
11751
11234
|
layout = "inline";
|
|
11752
11235
|
}
|
|
11753
|
-
} else if (
|
|
11236
|
+
} else if (row) {
|
|
11754
11237
|
layout = "row";
|
|
11755
|
-
} else if (
|
|
11238
|
+
} else if (column) {
|
|
11756
11239
|
layout = "column";
|
|
11757
11240
|
} else {
|
|
11758
11241
|
layout = getDefaultDisplay(TagName);
|
|
11759
11242
|
}
|
|
11760
11243
|
const innerClassName = withPropsClassName(baseClassName, className);
|
|
11761
11244
|
const remainingProps = {};
|
|
11245
|
+
const propsToForward = {};
|
|
11246
|
+
const shouldForwardAllToChild = visualSelector && pseudoStateSelector;
|
|
11762
11247
|
{
|
|
11763
11248
|
const parentLayout = useContext(BoxLayoutContext);
|
|
11764
|
-
const
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11249
|
+
const styleDeps = [
|
|
11250
|
+
// Layout and alignment props
|
|
11251
|
+
parentLayout, layout,
|
|
11252
|
+
// Style context dependencies
|
|
11253
|
+
managedByCSSVars, pseudoClasses, pseudoElements,
|
|
11254
|
+
// Selectors
|
|
11255
|
+
visualSelector, pseudoStateSelector];
|
|
11256
|
+
let innerPseudoState;
|
|
11257
|
+
if (basePseudoState && pseudoState) {
|
|
11258
|
+
innerPseudoState = {};
|
|
11259
|
+
const baseStateKeys = Object.keys(basePseudoState);
|
|
11260
|
+
const pseudoStateKeySet = new Set(Object.keys(pseudoState));
|
|
11261
|
+
for (const key of baseStateKeys) {
|
|
11262
|
+
if (pseudoStateKeySet.has(key)) {
|
|
11263
|
+
pseudoStateKeySet.delete(key);
|
|
11264
|
+
const value = pseudoState[key];
|
|
11265
|
+
styleDeps.push(value);
|
|
11266
|
+
innerPseudoState[key] = value;
|
|
11267
|
+
} else {
|
|
11268
|
+
const value = basePseudoState[key];
|
|
11269
|
+
styleDeps.push(value);
|
|
11270
|
+
innerPseudoState[key] = value;
|
|
11271
|
+
}
|
|
11272
|
+
}
|
|
11273
|
+
for (const key of pseudoStateKeySet) {
|
|
11274
|
+
const value = pseudoState[key];
|
|
11275
|
+
styleDeps.push(value);
|
|
11276
|
+
innerPseudoState[key] = value;
|
|
11277
|
+
}
|
|
11278
|
+
} else if (basePseudoState) {
|
|
11279
|
+
innerPseudoState = basePseudoState;
|
|
11280
|
+
for (const key of Object.keys(basePseudoState)) {
|
|
11281
|
+
const value = basePseudoState[key];
|
|
11282
|
+
styleDeps.push(value);
|
|
11283
|
+
}
|
|
11284
|
+
} else if (pseudoState) {
|
|
11285
|
+
innerPseudoState = pseudoState;
|
|
11286
|
+
for (const key of Object.keys(pseudoState)) {
|
|
11287
|
+
const value = pseudoState[key];
|
|
11288
|
+
styleDeps.push(value);
|
|
11289
|
+
}
|
|
11290
|
+
} else {
|
|
11291
|
+
innerPseudoState = PSEUDO_STATE_DEFAULT;
|
|
11292
|
+
}
|
|
11768
11293
|
const styleContext = {
|
|
11769
11294
|
parentLayout,
|
|
11770
11295
|
layout,
|
|
@@ -11773,13 +11298,6 @@ const Box = props => {
|
|
|
11773
11298
|
pseudoClasses,
|
|
11774
11299
|
pseudoElements
|
|
11775
11300
|
};
|
|
11776
|
-
const styleDeps = [
|
|
11777
|
-
// Layout and alignment props
|
|
11778
|
-
parentLayout, layout,
|
|
11779
|
-
// Style context dependencies
|
|
11780
|
-
managedByCSSVars, pseudoClasses, pseudoElements,
|
|
11781
|
-
// Selectors
|
|
11782
|
-
visualSelector, pseudoStateSelector];
|
|
11783
11301
|
const boxStyles = {};
|
|
11784
11302
|
if (baseStyle) {
|
|
11785
11303
|
for (const key of baseStyle) {
|
|
@@ -11789,32 +11307,43 @@ const Box = props => {
|
|
|
11789
11307
|
}
|
|
11790
11308
|
}
|
|
11791
11309
|
const stylingKeyCandidateArray = Object.keys(rest);
|
|
11792
|
-
const assignStyleFromProp = (propValue, propName, stylesTarget, context) => {
|
|
11793
|
-
const propEffect = getPropEffect(propName);
|
|
11794
|
-
if (propEffect === "ignore") {
|
|
11795
|
-
return;
|
|
11796
|
-
}
|
|
11797
|
-
if (propEffect === "forward" || propEffect === "style_and_forward") {
|
|
11798
|
-
if (stylesTarget === boxStyles) {
|
|
11799
|
-
remainingProps[propName] = propValue;
|
|
11800
|
-
}
|
|
11801
|
-
if (propEffect === "forward") {
|
|
11802
|
-
return;
|
|
11803
|
-
}
|
|
11804
|
-
}
|
|
11805
|
-
styleDeps.push(propValue);
|
|
11806
|
-
assignStyle(stylesTarget, propValue, propName, context);
|
|
11807
|
-
};
|
|
11808
11310
|
const getPropEffect = propName => {
|
|
11809
11311
|
if (visualSelector) {
|
|
11810
11312
|
if (HANDLED_BY_VISUAL_CHILD_PROP_SET.has(propName)) {
|
|
11811
11313
|
return "forward";
|
|
11812
11314
|
}
|
|
11813
|
-
if (COPIED_ON_VISUAL_CHILD_PROP_SET.has(propName)) {
|
|
11814
|
-
return "style_and_forward";
|
|
11315
|
+
if (COPIED_ON_VISUAL_CHILD_PROP_SET.has(propName)) {
|
|
11316
|
+
return "style_and_forward";
|
|
11317
|
+
}
|
|
11318
|
+
}
|
|
11319
|
+
if (isStyleProp(propName)) {
|
|
11320
|
+
return "style";
|
|
11321
|
+
}
|
|
11322
|
+
if (propName.startsWith("data-")) {
|
|
11323
|
+
return "use";
|
|
11324
|
+
}
|
|
11325
|
+
return "forward";
|
|
11326
|
+
};
|
|
11327
|
+
const assignStyleFromProp = (propValue, propName, stylesTarget, styleContext) => {
|
|
11328
|
+
const propEffect = getPropEffect(propName);
|
|
11329
|
+
if (propEffect === "ignore") {
|
|
11330
|
+
return;
|
|
11331
|
+
}
|
|
11332
|
+
const useToStyle = propEffect === "style" || propEffect === "style_and_forward";
|
|
11333
|
+
const shouldForward = propEffect === "forward" || propEffect === "style_and_forward";
|
|
11334
|
+
if (useToStyle) {
|
|
11335
|
+
styleDeps.push(propValue);
|
|
11336
|
+
assignStyle(stylesTarget, propValue, propName, styleContext, "css");
|
|
11337
|
+
}
|
|
11338
|
+
if (stylesTarget === boxStyles) {
|
|
11339
|
+
if (!shouldForwardAllToChild && !useToStyle) {
|
|
11340
|
+
// we'll put these props on ourselves
|
|
11341
|
+
remainingProps[propName] = propValue;
|
|
11342
|
+
}
|
|
11343
|
+
if (shouldForward) {
|
|
11344
|
+
propsToForward[propName] = propValue;
|
|
11815
11345
|
}
|
|
11816
11346
|
}
|
|
11817
|
-
return isStyleProp(propName) ? "style" : "forward";
|
|
11818
11347
|
};
|
|
11819
11348
|
for (const key of stylingKeyCandidateArray) {
|
|
11820
11349
|
if (key === "ref") {
|
|
@@ -11825,46 +11354,49 @@ const Box = props => {
|
|
|
11825
11354
|
const value = rest[key];
|
|
11826
11355
|
assignStyleFromProp(value, key, boxStyles, styleContext);
|
|
11827
11356
|
}
|
|
11828
|
-
|
|
11357
|
+
let pseudoNamedStyles = PSEUDO_NAMED_STYLES_DEFAULT;
|
|
11829
11358
|
if (pseudoStyle) {
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
...
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11359
|
+
const pseudoStyleKeys = Object.keys(pseudoStyle);
|
|
11360
|
+
if (pseudoStyleKeys.length) {
|
|
11361
|
+
pseudoNamedStyles = {};
|
|
11362
|
+
for (const key of pseudoStyleKeys) {
|
|
11363
|
+
const pseudoStyleContext = {
|
|
11364
|
+
...styleContext,
|
|
11365
|
+
managedByCSSVars: {
|
|
11366
|
+
...managedByCSSVars,
|
|
11367
|
+
...managedByCSSVars[key]
|
|
11368
|
+
},
|
|
11369
|
+
pseudoName: key
|
|
11370
|
+
};
|
|
11839
11371
|
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11372
|
+
// pseudo class
|
|
11373
|
+
if (key.startsWith(":")) {
|
|
11374
|
+
styleDeps.push(key);
|
|
11375
|
+
const pseudoClassStyles = {};
|
|
11376
|
+
const pseudoClassStyle = pseudoStyle[key];
|
|
11377
|
+
for (const pseudoClassStyleKey of Object.keys(pseudoClassStyle)) {
|
|
11378
|
+
const pseudoClassStyleValue = pseudoClassStyle[pseudoClassStyleKey];
|
|
11379
|
+
assignStyleFromProp(pseudoClassStyleValue, pseudoClassStyleKey, pseudoClassStyles, pseudoStyleContext);
|
|
11380
|
+
}
|
|
11381
|
+
pseudoNamedStyles[key] = pseudoClassStyles;
|
|
11382
|
+
continue;
|
|
11848
11383
|
}
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11384
|
+
// pseudo element
|
|
11385
|
+
if (key.startsWith("::")) {
|
|
11386
|
+
styleDeps.push(key);
|
|
11387
|
+
const pseudoElementStyles = {};
|
|
11388
|
+
const pseudoElementStyle = pseudoStyle[key];
|
|
11389
|
+
for (const pseudoElementStyleKey of Object.keys(pseudoElementStyle)) {
|
|
11390
|
+
const pseudoElementStyleValue = pseudoElementStyle[pseudoElementStyleKey];
|
|
11391
|
+
assignStyleFromProp(pseudoElementStyleValue, pseudoElementStyleKey, pseudoElementStyles, pseudoStyleContext);
|
|
11392
|
+
}
|
|
11393
|
+
pseudoNamedStyles[key] = pseudoElementStyles;
|
|
11394
|
+
continue;
|
|
11860
11395
|
}
|
|
11861
|
-
|
|
11862
|
-
continue;
|
|
11396
|
+
console.warn(`unsupported pseudo style key "${key}"`);
|
|
11863
11397
|
}
|
|
11864
|
-
console.warn(`unsupported pseudo style key "${key}"`);
|
|
11865
11398
|
}
|
|
11866
11399
|
remainingProps.pseudoStyle = pseudoStyle;
|
|
11867
|
-
// TODO: we should also pass pseudoState right?
|
|
11868
11400
|
}
|
|
11869
11401
|
if (typeof style === "string") {
|
|
11870
11402
|
appendStyles(boxStyles, normalizeStyles(style, "css"), "css");
|
|
@@ -11872,7 +11404,7 @@ const Box = props => {
|
|
|
11872
11404
|
} else if (style && typeof style === "object") {
|
|
11873
11405
|
for (const key of Object.keys(style)) {
|
|
11874
11406
|
const stylePropValue = style[key];
|
|
11875
|
-
assignStyle(boxStyles, stylePropValue, key, styleContext);
|
|
11407
|
+
assignStyle(boxStyles, stylePropValue, key, styleContext, "css");
|
|
11876
11408
|
styleDeps.push(stylePropValue); // impact box style -> add to deps
|
|
11877
11409
|
}
|
|
11878
11410
|
}
|
|
@@ -11924,23 +11456,22 @@ const Box = props => {
|
|
|
11924
11456
|
let innerChildren;
|
|
11925
11457
|
if (hasChildFunction) {
|
|
11926
11458
|
if (Array.isArray(children)) {
|
|
11927
|
-
innerChildren = children.map(child => typeof child === "function" ? child(
|
|
11459
|
+
innerChildren = children.map(child => typeof child === "function" ? child(propsToForward) : child);
|
|
11928
11460
|
} else if (typeof children === "function") {
|
|
11929
|
-
innerChildren = children(
|
|
11461
|
+
innerChildren = children(propsToForward);
|
|
11930
11462
|
} else {
|
|
11931
11463
|
innerChildren = children;
|
|
11932
11464
|
}
|
|
11933
11465
|
} else {
|
|
11934
11466
|
innerChildren = children;
|
|
11935
11467
|
}
|
|
11936
|
-
const shouldForwardAllToChild = visualSelector && pseudoStateSelector;
|
|
11937
11468
|
return jsx(TagName, {
|
|
11938
11469
|
ref: ref,
|
|
11939
11470
|
className: innerClassName,
|
|
11940
|
-
"data-layout-inline":
|
|
11941
|
-
"data-layout-row":
|
|
11942
|
-
"data-layout-column":
|
|
11943
|
-
...
|
|
11471
|
+
"data-layout-inline": inline ? "" : undefined,
|
|
11472
|
+
"data-layout-row": row ? "" : undefined,
|
|
11473
|
+
"data-layout-column": column ? "" : undefined,
|
|
11474
|
+
...remainingProps,
|
|
11944
11475
|
children: jsx(BoxLayoutContext.Provider, {
|
|
11945
11476
|
value: layout,
|
|
11946
11477
|
children: innerChildren
|
|
@@ -11948,25 +11479,8 @@ const Box = props => {
|
|
|
11948
11479
|
});
|
|
11949
11480
|
};
|
|
11950
11481
|
const Layout = props => {
|
|
11951
|
-
const {
|
|
11952
|
-
row,
|
|
11953
|
-
column,
|
|
11954
|
-
...rest
|
|
11955
|
-
} = props;
|
|
11956
|
-
if (row) {
|
|
11957
|
-
return jsx(Box, {
|
|
11958
|
-
layoutRow: true,
|
|
11959
|
-
...rest
|
|
11960
|
-
});
|
|
11961
|
-
}
|
|
11962
|
-
if (column) {
|
|
11963
|
-
return jsx(Box, {
|
|
11964
|
-
layoutColumn: true,
|
|
11965
|
-
...rest
|
|
11966
|
-
});
|
|
11967
|
-
}
|
|
11968
11482
|
return jsx(Box, {
|
|
11969
|
-
...
|
|
11483
|
+
...props
|
|
11970
11484
|
});
|
|
11971
11485
|
};
|
|
11972
11486
|
|
|
@@ -13907,31 +13421,6 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
13907
13421
|
color: inherit;
|
|
13908
13422
|
}
|
|
13909
13423
|
|
|
13910
|
-
.navi_char_slot_invisible {
|
|
13911
|
-
opacity: 0;
|
|
13912
|
-
}
|
|
13913
|
-
|
|
13914
|
-
.navi_icon {
|
|
13915
|
-
display: flex;
|
|
13916
|
-
aspect-ratio: 1 / 1;
|
|
13917
|
-
height: 100%;
|
|
13918
|
-
max-height: 1em;
|
|
13919
|
-
align-items: center;
|
|
13920
|
-
justify-content: center;
|
|
13921
|
-
}
|
|
13922
|
-
|
|
13923
|
-
.navi_text[data-has-foreground] {
|
|
13924
|
-
display: inline-block;
|
|
13925
|
-
}
|
|
13926
|
-
|
|
13927
|
-
.navi_text_foreground {
|
|
13928
|
-
position: absolute;
|
|
13929
|
-
inset: 0;
|
|
13930
|
-
display: inline-flex;
|
|
13931
|
-
align-items: center;
|
|
13932
|
-
justify-content: center;
|
|
13933
|
-
}
|
|
13934
|
-
|
|
13935
13424
|
.navi_text_overflow_wrapper {
|
|
13936
13425
|
display: flex;
|
|
13937
13426
|
width: 0;
|
|
@@ -14016,91 +13505,18 @@ const TextOverflowPinned = ({
|
|
|
14016
13505
|
};
|
|
14017
13506
|
const TextBasic = ({
|
|
14018
13507
|
as = "span",
|
|
14019
|
-
foregroundColor,
|
|
14020
|
-
foregroundElement,
|
|
14021
13508
|
contentSpacing = " ",
|
|
14022
|
-
box,
|
|
14023
13509
|
children,
|
|
14024
13510
|
...rest
|
|
14025
13511
|
}) => {
|
|
14026
|
-
const
|
|
14027
|
-
const text = jsxs(Box, {
|
|
13512
|
+
const text = jsx(Box, {
|
|
14028
13513
|
...rest,
|
|
14029
13514
|
baseClassName: "navi_text",
|
|
14030
13515
|
as: as,
|
|
14031
|
-
|
|
14032
|
-
layoutColumn: box ? true : undefined,
|
|
14033
|
-
"data-has-foreground": hasForeground ? "" : undefined,
|
|
14034
|
-
children: [applyContentSpacingOnTextChildren(children, contentSpacing), hasForeground && jsx("span", {
|
|
14035
|
-
className: "navi_text_foreground",
|
|
14036
|
-
style: {
|
|
14037
|
-
backgroundColor: foregroundColor
|
|
14038
|
-
},
|
|
14039
|
-
children: foregroundElement
|
|
14040
|
-
})]
|
|
13516
|
+
children: applyContentSpacingOnTextChildren(children, contentSpacing)
|
|
14041
13517
|
});
|
|
14042
13518
|
return text;
|
|
14043
13519
|
};
|
|
14044
|
-
const CharSlot = ({
|
|
14045
|
-
charWidth = 1,
|
|
14046
|
-
// 0 (zéro) is the real char width
|
|
14047
|
-
// but 2 zéros gives too big icons
|
|
14048
|
-
// while 1 "W" gives a nice result
|
|
14049
|
-
baseChar = "W",
|
|
14050
|
-
"aria-label": ariaLabel,
|
|
14051
|
-
role,
|
|
14052
|
-
decorative = false,
|
|
14053
|
-
children,
|
|
14054
|
-
...rest
|
|
14055
|
-
}) => {
|
|
14056
|
-
const invisibleText = baseChar.repeat(charWidth);
|
|
14057
|
-
const ariaProps = decorative ? {
|
|
14058
|
-
"aria-hidden": "true"
|
|
14059
|
-
} : {
|
|
14060
|
-
role,
|
|
14061
|
-
"aria-label": ariaLabel
|
|
14062
|
-
};
|
|
14063
|
-
return jsx(Text, {
|
|
14064
|
-
...rest,
|
|
14065
|
-
...ariaProps,
|
|
14066
|
-
foregroundElement: children,
|
|
14067
|
-
children: jsx("span", {
|
|
14068
|
-
className: "navi_char_slot_invisible",
|
|
14069
|
-
"aria-hidden": "true",
|
|
14070
|
-
children: invisibleText
|
|
14071
|
-
})
|
|
14072
|
-
});
|
|
14073
|
-
};
|
|
14074
|
-
const Icon = ({
|
|
14075
|
-
box,
|
|
14076
|
-
href,
|
|
14077
|
-
children,
|
|
14078
|
-
...props
|
|
14079
|
-
}) => {
|
|
14080
|
-
const innerChildren = href ? jsx("svg", {
|
|
14081
|
-
width: "100%",
|
|
14082
|
-
height: "100%",
|
|
14083
|
-
children: jsx("use", {
|
|
14084
|
-
href: href
|
|
14085
|
-
})
|
|
14086
|
-
}) : children;
|
|
14087
|
-
if (box) {
|
|
14088
|
-
return jsx(Box, {
|
|
14089
|
-
layoutInline: true,
|
|
14090
|
-
layoutColumn: true,
|
|
14091
|
-
...props,
|
|
14092
|
-
children: innerChildren
|
|
14093
|
-
});
|
|
14094
|
-
}
|
|
14095
|
-
return jsx(CharSlot, {
|
|
14096
|
-
decorative: true,
|
|
14097
|
-
...props,
|
|
14098
|
-
children: jsx("span", {
|
|
14099
|
-
className: "navi_icon",
|
|
14100
|
-
children: innerChildren
|
|
14101
|
-
})
|
|
14102
|
-
});
|
|
14103
|
-
};
|
|
14104
13520
|
const Paragraph = ({
|
|
14105
13521
|
contentSpacing = " ",
|
|
14106
13522
|
marginTop = "md",
|
|
@@ -14163,6 +13579,114 @@ const applyContentSpacingOnTextChildren = (children, contentSpacing) => {
|
|
|
14163
13579
|
return childrenWithGap;
|
|
14164
13580
|
};
|
|
14165
13581
|
|
|
13582
|
+
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
13583
|
+
.navi_icon {
|
|
13584
|
+
display: inline-block;
|
|
13585
|
+
box-sizing: border-box;
|
|
13586
|
+
}
|
|
13587
|
+
|
|
13588
|
+
.navi_icon_char_slot {
|
|
13589
|
+
opacity: 0;
|
|
13590
|
+
}
|
|
13591
|
+
.navi_icon_foreground {
|
|
13592
|
+
position: absolute;
|
|
13593
|
+
inset: 0;
|
|
13594
|
+
display: inline-flex;
|
|
13595
|
+
box-sizing: border-box;
|
|
13596
|
+
align-items: center;
|
|
13597
|
+
justify-content: start;
|
|
13598
|
+
}
|
|
13599
|
+
.navi_icon_foreground > .navi_text {
|
|
13600
|
+
display: flex;
|
|
13601
|
+
aspect-ratio: 1 / 1;
|
|
13602
|
+
height: 100%;
|
|
13603
|
+
max-height: 1em;
|
|
13604
|
+
align-items: center;
|
|
13605
|
+
justify-content: center;
|
|
13606
|
+
}
|
|
13607
|
+
|
|
13608
|
+
.navi_icon > svg,
|
|
13609
|
+
.navi_icon > img {
|
|
13610
|
+
width: 100%;
|
|
13611
|
+
height: 100%;
|
|
13612
|
+
}
|
|
13613
|
+
.navi_icon[data-width] > svg,
|
|
13614
|
+
.navi_icon[data-width] > img {
|
|
13615
|
+
width: 100%;
|
|
13616
|
+
height: auto;
|
|
13617
|
+
}
|
|
13618
|
+
.navi_icon[data-height] > svg,
|
|
13619
|
+
.navi_icon[data-height] > img {
|
|
13620
|
+
width: auto;
|
|
13621
|
+
height: 100%;
|
|
13622
|
+
}
|
|
13623
|
+
`;
|
|
13624
|
+
const Icon = ({
|
|
13625
|
+
href,
|
|
13626
|
+
children,
|
|
13627
|
+
className,
|
|
13628
|
+
charWidth = 1,
|
|
13629
|
+
// 0 (zéro) is the real char width
|
|
13630
|
+
// but 2 zéros gives too big icons
|
|
13631
|
+
// while 1 "W" gives a nice result
|
|
13632
|
+
baseChar = "W",
|
|
13633
|
+
"aria-label": ariaLabel,
|
|
13634
|
+
role,
|
|
13635
|
+
decorative = false,
|
|
13636
|
+
...props
|
|
13637
|
+
}) => {
|
|
13638
|
+
const innerChildren = href ? jsx("svg", {
|
|
13639
|
+
width: "100%",
|
|
13640
|
+
height: "100%",
|
|
13641
|
+
children: jsx("use", {
|
|
13642
|
+
href: href
|
|
13643
|
+
})
|
|
13644
|
+
}) : children;
|
|
13645
|
+
let {
|
|
13646
|
+
box,
|
|
13647
|
+
width,
|
|
13648
|
+
height
|
|
13649
|
+
} = props;
|
|
13650
|
+
if (width !== undefined || height !== undefined) {
|
|
13651
|
+
box = true;
|
|
13652
|
+
}
|
|
13653
|
+
if (box) {
|
|
13654
|
+
return jsx(Box, {
|
|
13655
|
+
...props,
|
|
13656
|
+
baseClassName: "navi_icon",
|
|
13657
|
+
"data-width": width,
|
|
13658
|
+
"data-height": height,
|
|
13659
|
+
children: innerChildren
|
|
13660
|
+
});
|
|
13661
|
+
}
|
|
13662
|
+
const invisibleText = baseChar.repeat(charWidth);
|
|
13663
|
+
const ariaProps = decorative ? {
|
|
13664
|
+
"aria-hidden": "true"
|
|
13665
|
+
} : {
|
|
13666
|
+
role,
|
|
13667
|
+
"aria-label": ariaLabel
|
|
13668
|
+
};
|
|
13669
|
+
return jsxs(Text, {
|
|
13670
|
+
...props,
|
|
13671
|
+
...ariaProps,
|
|
13672
|
+
box: box,
|
|
13673
|
+
className: withPropsClassName("navi_icon", className),
|
|
13674
|
+
"data-icon-char": "",
|
|
13675
|
+
"data-width": width,
|
|
13676
|
+
"data-height": height,
|
|
13677
|
+
children: [jsx("span", {
|
|
13678
|
+
className: "navi_icon_char_slot",
|
|
13679
|
+
"aria-hidden": "true",
|
|
13680
|
+
children: invisibleText
|
|
13681
|
+
}), jsx("span", {
|
|
13682
|
+
className: "navi_icon_foreground",
|
|
13683
|
+
children: jsx(Text, {
|
|
13684
|
+
children: innerChildren
|
|
13685
|
+
})
|
|
13686
|
+
})]
|
|
13687
|
+
});
|
|
13688
|
+
};
|
|
13689
|
+
|
|
14166
13690
|
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
14167
13691
|
@layer navi {
|
|
14168
13692
|
.navi_link {
|
|
@@ -14172,7 +13696,7 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
14172
13696
|
--color-visited: light-dark(#6a1b9a, #ab47bc);
|
|
14173
13697
|
--color-active: red;
|
|
14174
13698
|
--text-decoration: underline;
|
|
14175
|
-
--text-decoration-hover:
|
|
13699
|
+
--text-decoration-hover: var(--text-decoration);
|
|
14176
13700
|
--cursor: pointer;
|
|
14177
13701
|
}
|
|
14178
13702
|
}
|
|
@@ -14183,7 +13707,7 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
14183
13707
|
--x-color-visited: var(--color-visited);
|
|
14184
13708
|
--x-color-active: var(--color-active);
|
|
14185
13709
|
--x-text-decoration: var(--text-decoration);
|
|
14186
|
-
--x-text-decoration-hover: var(--text-decoration-hover
|
|
13710
|
+
--x-text-decoration-hover: var(--text-decoration-hover);
|
|
14187
13711
|
--x-cursor: var(--cursor);
|
|
14188
13712
|
|
|
14189
13713
|
position: relative;
|
|
@@ -14301,7 +13825,6 @@ const LinkPlain = props => {
|
|
|
14301
13825
|
rel,
|
|
14302
13826
|
preventDefault,
|
|
14303
13827
|
// visual
|
|
14304
|
-
box,
|
|
14305
13828
|
blankTargetIcon,
|
|
14306
13829
|
anchorIcon,
|
|
14307
13830
|
icon,
|
|
@@ -14361,8 +13884,6 @@ const LinkPlain = props => {
|
|
|
14361
13884
|
// Visual
|
|
14362
13885
|
,
|
|
14363
13886
|
baseClassName: "navi_link",
|
|
14364
|
-
layoutInline: true,
|
|
14365
|
-
layoutColumn: box ? true : undefined,
|
|
14366
13887
|
managedByCSSVars: LinkManagedByCSSVars,
|
|
14367
13888
|
pseudoClasses: LinkPseudoClasses,
|
|
14368
13889
|
pseudoElements: LinkPseudoElements,
|
|
@@ -14408,8 +13929,6 @@ const LinkPlain = props => {
|
|
|
14408
13929
|
const BlankTargetLinkSvg = () => {
|
|
14409
13930
|
return jsx("svg", {
|
|
14410
13931
|
viewBox: "0 0 24 24",
|
|
14411
|
-
width: "100%",
|
|
14412
|
-
height: "100%",
|
|
14413
13932
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14414
13933
|
children: jsx("path", {
|
|
14415
13934
|
d: "M10.0002 5H8.2002C7.08009 5 6.51962 5 6.0918 5.21799C5.71547 5.40973 5.40973 5.71547 5.21799 6.0918C5 6.51962 5 7.08009 5 8.2002V15.8002C5 16.9203 5 17.4801 5.21799 17.9079C5.40973 18.2842 5.71547 18.5905 6.0918 18.7822C6.5192 19 7.07899 19 8.19691 19H15.8031C16.921 19 17.48 19 17.9074 18.7822C18.2837 18.5905 18.5905 18.2839 18.7822 17.9076C19 17.4802 19 16.921 19 15.8031V14M20 9V4M20 4H15M20 4L13 11",
|
|
@@ -14424,8 +13943,6 @@ const BlankTargetLinkSvg = () => {
|
|
|
14424
13943
|
const AnchorLinkSvg = () => {
|
|
14425
13944
|
return jsxs("svg", {
|
|
14426
13945
|
viewBox: "0 0 24 24",
|
|
14427
|
-
width: "100%",
|
|
14428
|
-
height: "100%",
|
|
14429
13946
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14430
13947
|
children: [jsx("path", {
|
|
14431
13948
|
d: "M13.2218 3.32234C15.3697 1.17445 18.8521 1.17445 21 3.32234C23.1479 5.47022 23.1479 8.95263 21 11.1005L17.4645 14.636C15.3166 16.7839 11.8342 16.7839 9.6863 14.636C9.48752 14.4373 9.30713 14.2271 9.14514 14.0075C8.90318 13.6796 8.97098 13.2301 9.25914 12.9419C9.73221 12.4688 10.5662 12.6561 11.0245 13.1435C11.0494 13.1699 11.0747 13.196 11.1005 13.2218C12.4673 14.5887 14.6834 14.5887 16.0503 13.2218L19.5858 9.6863C20.9526 8.31947 20.9526 6.10339 19.5858 4.73655C18.219 3.36972 16.0029 3.36972 14.636 4.73655L13.5754 5.79721C13.1849 6.18774 12.5517 6.18774 12.1612 5.79721C11.7706 5.40669 11.7706 4.77352 12.1612 4.383L13.2218 3.32234Z",
|
|
@@ -14439,8 +13956,6 @@ const AnchorLinkSvg = () => {
|
|
|
14439
13956
|
const PhoneSvg = () => {
|
|
14440
13957
|
return jsx("svg", {
|
|
14441
13958
|
viewBox: "0 0 24 24",
|
|
14442
|
-
width: "100%",
|
|
14443
|
-
height: "100%",
|
|
14444
13959
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14445
13960
|
children: jsx("path", {
|
|
14446
13961
|
d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z",
|
|
@@ -14451,8 +13966,6 @@ const PhoneSvg = () => {
|
|
|
14451
13966
|
const SmsSvg = () => {
|
|
14452
13967
|
return jsx("svg", {
|
|
14453
13968
|
viewBox: "0 0 24 24",
|
|
14454
|
-
width: "100%",
|
|
14455
|
-
height: "100%",
|
|
14456
13969
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14457
13970
|
children: jsx("path", {
|
|
14458
13971
|
d: "M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM18 14H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z",
|
|
@@ -14463,8 +13976,6 @@ const SmsSvg = () => {
|
|
|
14463
13976
|
const EmailSvg = () => {
|
|
14464
13977
|
return jsxs("svg", {
|
|
14465
13978
|
viewBox: "0 0 24 24",
|
|
14466
|
-
width: "100%",
|
|
14467
|
-
height: "100%",
|
|
14468
13979
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14469
13980
|
children: [jsx("path", {
|
|
14470
13981
|
d: "M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z",
|
|
@@ -14484,8 +13995,6 @@ const EmailSvg = () => {
|
|
|
14484
13995
|
const GithubSvg = () => {
|
|
14485
13996
|
return jsx("svg", {
|
|
14486
13997
|
viewBox: "0 0 24 24",
|
|
14487
|
-
width: "100%",
|
|
14488
|
-
height: "100%",
|
|
14489
13998
|
xmlns: "http://www.w3.org/2000/svg",
|
|
14490
13999
|
children: jsx("path", {
|
|
14491
14000
|
d: "M12 2C6.48 2 2 6.48 2 12c0 4.42 2.87 8.17 6.84 9.5.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.87 1.52 2.34 1.07 2.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92 0-1.11.38-2 1.03-2.71-.1-.25-.45-1.29.1-2.64 0 0 .84-.27 2.75 1.02.79-.22 1.65-.33 2.5-.33.85 0 1.71.11 2.5.33 1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.39.1 2.64.65.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0012 2z",
|
|
@@ -17056,13 +16565,10 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
17056
16565
|
--x-color: var(--color);
|
|
17057
16566
|
|
|
17058
16567
|
position: relative;
|
|
16568
|
+
display: inline-flex;
|
|
17059
16569
|
box-sizing: border-box;
|
|
17060
|
-
width: fit-content;
|
|
17061
|
-
height: fit-content;
|
|
17062
16570
|
padding: 0;
|
|
17063
16571
|
flex-direction: inherit;
|
|
17064
|
-
align-items: inherit;
|
|
17065
|
-
justify-content: inherit;
|
|
17066
16572
|
background: none;
|
|
17067
16573
|
border: none;
|
|
17068
16574
|
border-radius: inherit;
|
|
@@ -17071,6 +16577,7 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
17071
16577
|
}
|
|
17072
16578
|
.navi_button_content {
|
|
17073
16579
|
position: relative;
|
|
16580
|
+
box-sizing: border-box;
|
|
17074
16581
|
padding-top: var(--padding-top, var(--padding-y, var(--padding, 1px)));
|
|
17075
16582
|
padding-right: var(--padding-right, var(--padding-x, var(--padding, 6px)));
|
|
17076
16583
|
padding-bottom: var(
|
|
@@ -17241,7 +16748,6 @@ const ButtonBasic = props => {
|
|
|
17241
16748
|
...buttonProps,
|
|
17242
16749
|
as: "span",
|
|
17243
16750
|
baseClassName: "navi_button_content",
|
|
17244
|
-
layoutInline: true,
|
|
17245
16751
|
children: [innerChildren, jsx("span", {
|
|
17246
16752
|
className: "navi_button_shadow"
|
|
17247
16753
|
})]
|
|
@@ -17259,8 +16765,6 @@ const ButtonBasic = props => {
|
|
|
17259
16765
|
// style management
|
|
17260
16766
|
,
|
|
17261
16767
|
baseClassName: "navi_button",
|
|
17262
|
-
layoutInline: true,
|
|
17263
|
-
layoutColumn: true,
|
|
17264
16768
|
managedByCSSVars: ButtonManagedByCSSVars,
|
|
17265
16769
|
pseudoClasses: ButtonPseudoClasses,
|
|
17266
16770
|
pseudoElements: ButtonPseudoElements,
|
|
@@ -21811,29 +21315,97 @@ const SVGMaskOverlay = ({
|
|
|
21811
21315
|
});
|
|
21812
21316
|
};
|
|
21813
21317
|
|
|
21318
|
+
const CSS_VAR_NAME = "--color-contrasting";
|
|
21319
|
+
|
|
21320
|
+
const useContrastingColor = (ref) => {
|
|
21321
|
+
useLayoutEffect(() => {
|
|
21322
|
+
const el = ref.current;
|
|
21323
|
+
if (!el) {
|
|
21324
|
+
return;
|
|
21325
|
+
}
|
|
21326
|
+
const lightColor = "var(--navi-color-light)";
|
|
21327
|
+
const darkColor = "var(--navi-color-dark)";
|
|
21328
|
+
const backgroundColor = getComputedStyle(el).backgroundColor;
|
|
21329
|
+
if (!backgroundColor) {
|
|
21330
|
+
el.style.removeProperty(CSS_VAR_NAME);
|
|
21331
|
+
return;
|
|
21332
|
+
}
|
|
21333
|
+
const colorPicked = pickLightOrDark(
|
|
21334
|
+
backgroundColor,
|
|
21335
|
+
lightColor,
|
|
21336
|
+
darkColor,
|
|
21337
|
+
el,
|
|
21338
|
+
);
|
|
21339
|
+
el.style.setProperty(CSS_VAR_NAME, colorPicked);
|
|
21340
|
+
}, []);
|
|
21341
|
+
};
|
|
21342
|
+
|
|
21814
21343
|
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
21815
|
-
|
|
21816
|
-
|
|
21817
|
-
|
|
21818
|
-
|
|
21344
|
+
@layer navi {
|
|
21345
|
+
.navi_badge {
|
|
21346
|
+
--border-radius: 1em;
|
|
21347
|
+
}
|
|
21348
|
+
}
|
|
21349
|
+
.navi_badge {
|
|
21350
|
+
display: inline-block;
|
|
21351
|
+
box-sizing: border-box;
|
|
21352
|
+
min-width: 1.5em;
|
|
21353
|
+
height: 1.5em;
|
|
21354
|
+
max-height: 1.5em;
|
|
21355
|
+
padding-right: var(
|
|
21356
|
+
--padding-right,
|
|
21357
|
+
var(--padding-x, var(--padding, 0.4em))
|
|
21358
|
+
);
|
|
21359
|
+
padding-left: var(--padding-left, var(--padding-x, var(--padding, 0.4em)));
|
|
21360
|
+
color: var(--color, var(--color-contrasting));
|
|
21361
|
+
text-align: center;
|
|
21362
|
+
line-height: 1.5em;
|
|
21363
|
+
vertical-align: middle;
|
|
21364
|
+
border-radius: var(--border-radius, 1em);
|
|
21819
21365
|
}
|
|
21820
21366
|
`;
|
|
21821
|
-
const
|
|
21367
|
+
const BadgeManagedByCSSVars = {
|
|
21368
|
+
borderWidth: "--border-width",
|
|
21369
|
+
borderRadius: "--border-radius",
|
|
21370
|
+
paddingRight: "--padding-right",
|
|
21371
|
+
paddingLeft: "--padding-left",
|
|
21372
|
+
backgroundColor: "--background-color",
|
|
21373
|
+
borderColor: "--border-color",
|
|
21374
|
+
color: "--color"
|
|
21375
|
+
};
|
|
21376
|
+
const BadgeCount = ({
|
|
21822
21377
|
children,
|
|
21823
|
-
|
|
21378
|
+
bold = true,
|
|
21379
|
+
max,
|
|
21380
|
+
...props
|
|
21824
21381
|
}) => {
|
|
21825
|
-
|
|
21826
|
-
|
|
21827
|
-
baseClassName: ".navi_count",
|
|
21828
|
-
...rest,
|
|
21829
|
-
children: ["(", children, ")"]
|
|
21830
|
-
});
|
|
21831
|
-
};
|
|
21382
|
+
const defaultRef = useRef();
|
|
21383
|
+
const ref = props.ref || defaultRef;
|
|
21832
21384
|
|
|
21833
|
-
|
|
21834
|
-
|
|
21385
|
+
// Calculer la valeur à afficher en fonction du paramètre max
|
|
21386
|
+
const getDisplayValue = () => {
|
|
21387
|
+
if (max === undefined) {
|
|
21388
|
+
return children;
|
|
21389
|
+
}
|
|
21390
|
+
const numericValue = typeof children === "string" ? parseInt(children, 10) : children;
|
|
21391
|
+
const numericMax = typeof max === "string" ? parseInt(max, 10) : max;
|
|
21392
|
+
if (isNaN(numericValue) || isNaN(numericMax)) {
|
|
21393
|
+
return children;
|
|
21394
|
+
}
|
|
21395
|
+
if (numericValue > numericMax) {
|
|
21396
|
+
return `${numericMax}+`;
|
|
21397
|
+
}
|
|
21398
|
+
return children;
|
|
21399
|
+
};
|
|
21400
|
+
const displayValue = getDisplayValue();
|
|
21401
|
+
useContrastingColor(ref);
|
|
21402
|
+
return jsx(Text, {
|
|
21835
21403
|
...props,
|
|
21836
|
-
|
|
21404
|
+
ref: ref,
|
|
21405
|
+
className: "navi_badge",
|
|
21406
|
+
bold: bold,
|
|
21407
|
+
managedByCSSVars: BadgeManagedByCSSVars,
|
|
21408
|
+
children: displayValue
|
|
21837
21409
|
});
|
|
21838
21410
|
};
|
|
21839
21411
|
|
|
@@ -21849,6 +21421,13 @@ const Code = ({
|
|
|
21849
21421
|
});
|
|
21850
21422
|
};
|
|
21851
21423
|
|
|
21424
|
+
const Image = props => {
|
|
21425
|
+
return jsx(Box, {
|
|
21426
|
+
...props,
|
|
21427
|
+
as: "img"
|
|
21428
|
+
});
|
|
21429
|
+
};
|
|
21430
|
+
|
|
21852
21431
|
const LinkWithIcon = () => {};
|
|
21853
21432
|
|
|
21854
21433
|
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
@@ -22086,5 +21665,5 @@ const useDependenciesDiff = (inputs) => {
|
|
|
22086
21665
|
return diffRef.current;
|
|
22087
21666
|
};
|
|
22088
21667
|
|
|
22089
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Box, Button,
|
|
21668
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, BadgeCount, Box, Button, Checkbox, CheckboxList, Code, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, Icon, IconAndText, Image, Input, Label, Layout, Link, LinkWithIcon, MessageBox, Paragraph, Radio, RadioList, Route, RouteLink, Routes, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, SummaryMarker, Svg, Tab, TabList, Table, TableCell, Tbody, Text, Thead, Title, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, enableDebugActions, enableDebugOnDocumentLoading, forwardActionRequested, goBack, goForward, goTo, installCustomConstraintValidation, isCellSelected, isColumnSelected, isRowSelected, openCallout, rawUrlPart, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, setupRoutes, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useActiveRouteInfo, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, useUrlSearchParam, valueInLocalStorage };
|
|
22090
21669
|
//# sourceMappingURL=jsenv_navi.js.map
|