@vectoriox/iox-ui 5.81.0 → 5.82.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1556,7 +1556,15 @@ const MARKER_FUNS = [
1556
1556
  ['markerColor', 'color'],
1557
1557
  ['markerFontSize', 'fontSize'],
1558
1558
  ];
1559
- const TRANSITION_KEYS = ['transitionDuration', 'transitionTimingFunction', 'transitionDelay'];
1559
+ const TRANSITION_KEYS = ['transitionDuration', 'transitionTimingFunction', 'transitionDelay', 'transitionProperty'];
1560
+ /** The Transition ▸ Properties trait (G2): what a transition names, comma-separated. Unset, the renderers fill in the
1561
+ * properties the node's states change (`withStateTransitions`); a transition never names `all` unless an author
1562
+ * typed it. */
1563
+ const TRANSITION_PROPERTY_KEY = 'transitionProperty';
1564
+ /** The property list a `transitionProperty` value names (`color, transform` → two entries). */
1565
+ function transitionPropertyList(value) {
1566
+ return String(value ?? '').split(',').map(p => p.trim()).filter(Boolean);
1567
+ }
1560
1568
  // ─── Max lines (line clamp) ─────────────────────────────────────────────────────
1561
1569
  /** "Max lines" (Card v2, board 8 — opt-in on a Title / Text): a whole number clamps the text to that many
1562
1570
  * lines with an ellipsis. It composes into the four declarations a line clamp takes; anything else
@@ -1740,13 +1748,15 @@ function composeVirtualTraits(raw, base = raw) {
1740
1748
  const transformStr = buildFns(TRANSFORM_FUNS, merged(TRANSFORM_FUNS));
1741
1749
  if (transformStr)
1742
1750
  result['transform'] = transformStr;
1743
- // transition — only emit when duration is non-zero
1751
+ // transition — only when it is timed (a non-zero duration) and names what it moves: one entry per property
1752
+ // (G2, Emil 183 — never `transition: all`); nothing to name, nothing to transition.
1744
1753
  const dur = raw['transitionDuration'] ?? base['transitionDuration'];
1745
1754
  const ease = raw['transitionTimingFunction'] ?? base['transitionTimingFunction'];
1746
1755
  const del = raw['transitionDelay'] ?? base['transitionDelay'];
1747
- if (dur && ease && dur !== '0ms' && dur !== '0s') {
1756
+ const props = transitionPropertyList(raw[TRANSITION_PROPERTY_KEY] ?? base[TRANSITION_PROPERTY_KEY]);
1757
+ if (dur && ease && dur !== '0ms' && dur !== '0s' && props.length) {
1748
1758
  const delPart = del && del !== '0ms' && del !== '0s' ? ` ${del}` : '';
1749
- result['transition'] = `all ${dur} ${ease}${delPart}`;
1759
+ result['transition'] = props.map(p => `${p} ${dur} ${ease}${delPart}`).join(', ');
1750
1760
  }
1751
1761
  // Max lines: the clamp takes over the node's display and overflow.
1752
1762
  const clamp = lineClamp(raw[MAX_LINES_KEY] ?? base[MAX_LINES_KEY]);
@@ -1858,6 +1868,8 @@ const NOOP_INITIAL_VALUES = {
1858
1868
  'aspect-ratio': ['auto'],
1859
1869
  'mix-blend-mode': ['normal'],
1860
1870
  'transform': ['none'], 'box-shadow': ['none'],
1871
+ // G3: the Transform origin trait's default is the CSS initial (the centre) — never emitted on every node.
1872
+ 'transform-origin': ['center', 'center center', '50% 50%', '50% 50% 0', '50% 50% 0px'],
1861
1873
  // overflow initial is `visible`. A self-describing node serialises the Overflow trait's default,
1862
1874
  // so WITHOUT this the emitted `overflow: visible` would override component baselines that clip
1863
1875
  // (e.g. `.iox-card__img-wrap`, `.iox-slider`, `.iox-gallery` all set `overflow: hidden`), breaking
@@ -1978,12 +1990,41 @@ var ComponentState;
1978
1990
  * `targetedStateSelector`. On a node OUTSIDE every item (composite T20, CC4a) the author PICKS the item — "when tab
1979
1991
  * Night is on" — the same state and the same `:has()` scoped to the nearest common ancestor. */
1980
1992
  ComponentState["ItemOpen"] = "trigger-on";
1993
+ /** G4: a control that cannot be used — a disabled button, or one marked aria-disabled. */
1994
+ ComponentState["Disabled"] = "disabled";
1995
+ /** G4: busy — a submit button while its form sends (`syncSubmitButtons`), a form while it sends. */
1996
+ ComponentState["Busy"] = "busy";
1997
+ /** G4: chosen — a toggle button pressed, an option checked or selected. */
1998
+ ComponentState["Selected"] = "selected";
1999
+ /** G4: the page has scrolled (a header that firms up once the page moves) — `SCROLLED_ATTRIBUTE` on the root. */
2000
+ ComponentState["Scrolled"] = "scrolled";
1981
2001
  })(ComponentState || (ComponentState = {}));
1982
- /** The attribute selector each (non-targeted) component state compiles to. */
2002
+ /** The page has scrolled: written on the document element by the live site (and on the canvas page by the builder). */
2003
+ const SCROLLED_ATTRIBUTE = 'data-iox-scrolled';
2004
+ /** The selector each (non-targeted) component state compiles to — an attribute, or an `:is()` of the ways a node can
2005
+ * be in it (a native `:disabled` or `aria-disabled`; pressed / checked / selected), so one rule covers every markup. */
1983
2006
  const COMPONENT_STATE_SELECTOR = {
1984
2007
  [ComponentState.Current]: '[aria-current="page"]',
1985
2008
  [ComponentState.On]: '[data-iox-on]',
2009
+ [ComponentState.Disabled]: ':is(:disabled, [aria-disabled="true"])',
2010
+ [ComponentState.Busy]: '[aria-busy="true"]',
2011
+ [ComponentState.Selected]: ':is([aria-pressed="true"], [aria-checked="true"], [aria-selected="true"])',
2012
+ [ComponentState.Scrolled]: `:is([${SCROLLED_ATTRIBUTE}], [${SCROLLED_ATTRIBUTE}] *)`,
1986
2013
  };
2014
+ /** Which types offer each control state (G4) in the Style panel's Component state group. */
2015
+ const G4_STATE_TYPES = [
2016
+ [ComponentState.Disabled, ['ButtonBlock', 'Button', 'SubmitButton']],
2017
+ [ComponentState.Busy, ['SubmitButton', 'ButtonBlock', 'Form']],
2018
+ [ComponentState.Selected, ['ButtonBlock', 'LinkedContainer']],
2019
+ ];
2020
+ /** Scrolled is offered on the site header and its parts — generation-core's NavRole family (`nav`, `nav.brand`…), the
2021
+ * drawer's parts aside (the drawer is a dialog over the page). Spelled here — the main entry does not import
2022
+ * generation-core; style-compiler.spec pins it to the enum. */
2023
+ const SCROLLED_STATE_ROLE = 'nav';
2024
+ const DRAWER_ROLE_PREFIX = 'nav.drawer';
2025
+ function offersScrolled(role) {
2026
+ return !!role && (role === SCROLLED_STATE_ROLE || role.startsWith(`${SCROLLED_STATE_ROLE}.`)) && !role.startsWith(DRAWER_ROLE_PREFIX);
2027
+ }
1987
2028
  /** The composite roles that are themselves "on" when their item is open (they carry `data-iox-on`): an accordion's
1988
2029
  * item, header and panel; a disclosure (its own item), its toggle and its panel (CC2.1); a tab and its panel (CC4a —
1989
2030
  * "Active"). The main entry does not import composite-core, so the roles are spelled here — `style-compiler.spec` pins
@@ -2008,6 +2049,11 @@ function componentStatesFor(type, role, insideItem = false, itemsToPick = false)
2008
2049
  out.push(ComponentState.Current);
2009
2050
  if (insideItem || itemsToPick)
2010
2051
  out.push(ComponentState.ItemOpen);
2052
+ for (const [state, types] of G4_STATE_TYPES)
2053
+ if (type && types.includes(type))
2054
+ out.push(state);
2055
+ if (offersScrolled(role))
2056
+ out.push(ComponentState.Scrolled);
2011
2057
  return out;
2012
2058
  }
2013
2059
  /**
@@ -2042,29 +2088,73 @@ function hasHoverState(stateStyles) {
2042
2088
  * The base-rule transition a hovering node gets when it declares none of its own, so a hover
2043
2089
  * animates instead of snapping. ONE copy for the builder canvas, the style panel and the engine.
2044
2090
  *
2045
- * Two values, chosen by the element-style MARKER (§3b-bis): a node wearing an element style takes
2091
+ * Two timings, chosen by the element-style MARKER (§3b-bis): a node wearing an element style takes
2046
2092
  * the motion tokens; a node without one (saved before DS2, not adopted) keeps the pre-DS2 literal
2047
- * byte-for-byte — seeding an org's tokens must never change how a legacy page hovers.
2093
+ * timing — seeding an org's tokens must never change how a legacy page hovers. WHAT transitions is the
2094
+ * list of properties the node's states change (G2 — never `transition: all`).
2048
2095
  */
2049
- const HOVER_DEFAULT_TRANSITION = 'all var(--iox-dur-fast) var(--iox-ease-standard)';
2050
- const LEGACY_HOVER_TRANSITION = 'all 0.2s ease';
2051
- /** The default hover transition for a node that wears `elementStyle` (or none). */
2052
- function defaultHoverTransition(elementStyle) {
2053
- return elementStyle ? HOVER_DEFAULT_TRANSITION : LEGACY_HOVER_TRANSITION;
2054
- }
2055
- /** True when the RAW style map already yields a transition (an explicit one, or the composed
2056
- * transitionDuration/TimingFunction an element style writes). */
2096
+ const HOVER_DEFAULT_TRANSITION_TRAITS = {
2097
+ transitionDuration: 'var(--iox-dur-fast)', transitionTimingFunction: 'var(--iox-ease-standard)',
2098
+ };
2099
+ const LEGACY_HOVER_TRANSITION_TRAITS = { transitionDuration: '0.2s', transitionTimingFunction: 'ease' };
2100
+ /** The default hover timing for a node that wears `elementStyle` (or none). */
2101
+ function defaultHoverTransitionTraits(elementStyle) {
2102
+ return elementStyle ? HOVER_DEFAULT_TRANSITION_TRAITS : LEGACY_HOVER_TRANSITION_TRAITS;
2103
+ }
2104
+ const isFilled = (v) => v !== undefined && v !== null && String(v).trim() !== '';
2105
+ const isZeroDuration = (v) => /^-?0+(\.0+)?(ms|s)?$/.test(String(v ?? '').trim());
2106
+ /** True when the RAW style map times a transition of its own (an explicit `transition`, or the
2107
+ * transitionDuration / TimingFunction an element style writes). */
2057
2108
  function hasOwnTransition(styleProps) {
2058
2109
  const sp = styleProps ?? {};
2059
- if (sp['transition'] !== undefined && sp['transition'] !== null && sp['transition'] !== '')
2110
+ if (isFilled(sp['transition']))
2060
2111
  return true;
2061
- return composeVirtualTraits(sp)['transition'] !== undefined;
2112
+ return isFilled(sp['transitionDuration']) && !isZeroDuration(sp['transitionDuration']) && isFilled(sp['transitionTimingFunction']);
2113
+ }
2114
+ /** Keys a composed state map holds that are not properties a transition can name. */
2115
+ const NOT_TRANSITIONED = new Set(['__markerStyles', 'WebkitBackdropFilter', 'transition', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay', 'transitionProperty']);
2116
+ /**
2117
+ * The CSS properties a node's states change — what its transition names (G2, Emil 183: never `transition: all`).
2118
+ * Every state the node holds, at every breakpoint, composed as the renderers compose it (translateX / scaleX → one
2119
+ * `transform`, a gradient background → `background-image`…), in a stable order so both renderers emit one string.
2120
+ */
2121
+ function stateTransitionProperties(stateStyles, responsive, type) {
2122
+ const maps = [];
2123
+ for (const map of Object.values(stateStyles ?? {}))
2124
+ if (map)
2125
+ maps.push(map);
2126
+ for (const layer of Object.values(responsive ?? {}))
2127
+ for (const map of Object.values(layer?.stateStyles ?? {}))
2128
+ if (map)
2129
+ maps.push(map);
2130
+ const out = [];
2131
+ // A prop the type's renderer owns (a Dialog's positioning) never reaches a rule — nor the transition's list.
2132
+ for (const map of maps.map(m => (type ? stripSuppressedProps(type, m) : m))) {
2133
+ for (const key of Object.keys(composeVirtualTraits(map, map))) {
2134
+ if (NOT_TRANSITIONED.has(key))
2135
+ continue;
2136
+ const prop = styleKeyToKebab(key);
2137
+ if (!out.includes(prop))
2138
+ out.push(prop);
2139
+ }
2140
+ }
2141
+ return out;
2062
2142
  }
2063
- /** `styleProps` plus the default hover transition when the node hovers and sets none itself. */
2064
- function withDefaultHoverTransition(styleProps, stateStyles, elementStyle) {
2065
- return hasHoverState(stateStyles) && !hasOwnTransition(styleProps)
2066
- ? { ...styleProps, transition: defaultHoverTransition(elementStyle) }
2067
- : styleProps;
2143
+ /**
2144
+ * `styleProps` as a renderer compiles them: a hovering node with no timing of its own takes the default hover timing,
2145
+ * and a node whose transition names no properties gets the ones its states change. An author's own `transitionProperty`
2146
+ * (or a raw `transition`) always wins. Pure: the node's stored styles are never touched.
2147
+ */
2148
+ function withStateTransitions(styleProps, stateStyles, elementStyle, responsive, type) {
2149
+ let out = styleProps;
2150
+ if (hasHoverState(stateStyles) && !hasOwnTransition(styleProps))
2151
+ out = { ...out, ...defaultHoverTransitionTraits(elementStyle) };
2152
+ if (!isFilled(out['transitionProperty']) && !isFilled(out['transition'])) {
2153
+ const props = stateTransitionProperties(stateStyles, responsive, type);
2154
+ if (props.length)
2155
+ out = { ...out, transitionProperty: props.join(', ') };
2156
+ }
2157
+ return out;
2068
2158
  }
2069
2159
  /**
2070
2160
  * Per-type default `width` that the builder applies from each component's config
@@ -2088,12 +2178,13 @@ const DEFAULT_WIDTH_BY_TYPE = {
2088
2178
  * behaviour, which the engine must re-apply explicitly for parity. Returns `"prop: value"`
2089
2179
  * declarations to append AFTER the node's own compiled declarations.
2090
2180
  *
2181
+ * The default hover transition is no longer one of them: it is composed into the node's own rule by
2182
+ * `withStateTransitions` (both renderers), listing the properties its states change (G2).
2183
+ *
2091
2184
  * @param type Node type (registry key).
2092
2185
  * @param styleProps The node's persisted styleProps.
2093
- * @param hasHoverState Whether the node has a non-empty hover / parent-hover state.
2094
- * @param elementStyle The element style the node wears — picks the default hover transition.
2095
2186
  */
2096
- function renderDefaultDeclarations(type, styleProps, hasHoverState, elementStyle) {
2187
+ function renderDefaultDeclarations(type, styleProps) {
2097
2188
  const sp = styleProps ?? {};
2098
2189
  const out = [];
2099
2190
  const isOutOfFlow = sp['position'] === 'fixed' || sp['position'] === 'absolute';
@@ -2121,12 +2212,6 @@ function renderDefaultDeclarations(type, styleProps, hasHoverState, elementStyle
2121
2212
  if (sp['borderWidth'] && sp['borderStyle'] === undefined) {
2122
2213
  out.push('border-style: solid');
2123
2214
  }
2124
- // Default transition so own/parent hover animates smoothly (parity with the builder). Skipped
2125
- // when the node composes its own (an element style's transitionDuration) — appended after the
2126
- // node's declarations, it would otherwise beat them in the engine only.
2127
- if (hasHoverState && !hasOwnTransition(sp)) {
2128
- out.push(`transition: ${defaultHoverTransition(elementStyle)}`);
2129
- }
2130
2215
  // Image defaults reapplied when unset (the config defaults aren't always persisted). height:auto
2131
2216
  // is only a DEFAULT — when the user sets an explicit height (e.g. 100%) it must WIN, so guard it
2132
2217
  // on `sp['height'] === undefined` (matching the builder, which respects a configured height). An
@@ -6220,6 +6305,20 @@ function splitOuterProps(styles) {
6220
6305
  (OUTER_STYLE_PROPS.has(k) ? outer : inner)[k] = v;
6221
6306
  return { inner, outer };
6222
6307
  }
6308
+ /**
6309
+ * The devices a hover rule is for (G1 — Emil 91, mobile-native M1): a phone has no hover, and a tap fires a false
6310
+ * one that STICKS until the next tap elsewhere. Every hover, parent-hover and trigger-hover rule — the node's own and
6311
+ * a part's — is emitted inside this query by both renderers; `:active` (a real press on touch) never is.
6312
+ */
6313
+ const HOVER_MEDIA_QUERY = '(hover: hover) and (pointer: fine)';
6314
+ /** True for a state driven by a pointer hovering something (its own, its parent's, a chosen trigger's). */
6315
+ function isHoverState(state) {
6316
+ return !!state && HOVER_STATES.includes(state);
6317
+ }
6318
+ /** `css` as it is emitted for `state`: inside `HOVER_MEDIA_QUERY` for a hover state, unchanged otherwise. */
6319
+ function gateHoverRule(state, css) {
6320
+ return css && isHoverState(state) ? `@media ${HOVER_MEDIA_QUERY} {\n${css}\n}` : css;
6321
+ }
6223
6322
 
6224
6323
  /**
6225
6324
  * "This link is the page you're on" (CC1, N2). ONE decision for the builder canvas and both engine
@@ -6654,6 +6753,145 @@ function finalKeyframe(frames) {
6654
6753
  return typeof last['clipPath'] === 'string' && FULLY_SHOWN_CLIP.test(last['clipPath']) ? { ...last, clipPath: 'none' } : last;
6655
6754
  }
6656
6755
 
6756
+ /**
6757
+ * What an interaction's easing may be (G6, decision D8): one of the five CSS keywords, a `cubic-bezier()`, or the
6758
+ * NAME of one of the look's easing tokens — so an author's entrance moves on the same curve as the site's buttons and
6759
+ * dialogs (Emil 21: one token system), and Spring plays the look's spring (a `linear()` curve, `springToLinear`).
6760
+ * Both runtimes resolve it the same way, through the CSS custom property on the element itself.
6761
+ */
6762
+ var EasingKeyword;
6763
+ (function (EasingKeyword) {
6764
+ EasingKeyword["Linear"] = "linear";
6765
+ EasingKeyword["Ease"] = "ease";
6766
+ EasingKeyword["EaseIn"] = "ease-in";
6767
+ EasingKeyword["EaseOut"] = "ease-out";
6768
+ EasingKeyword["EaseInOut"] = "ease-in-out";
6769
+ })(EasingKeyword || (EasingKeyword = {}));
6770
+ /** The look's easing tokens an interaction may name (their `--iox-<name>` holds the curve). */
6771
+ var EasingToken;
6772
+ (function (EasingToken) {
6773
+ EasingToken["Standard"] = "ease-standard";
6774
+ EasingToken["Enter"] = "ease-enter";
6775
+ EasingToken["Exit"] = "ease-exit";
6776
+ EasingToken["Spring"] = "ease-spring";
6777
+ })(EasingToken || (EasingToken = {}));
6778
+ const EASING_KEYWORDS = Object.values(EasingKeyword);
6779
+ const EASING_TOKENS = Object.values(EasingToken);
6780
+ /** What an unknown or unresolvable easing plays as: a deceleration, never ease-in (Emil 17). */
6781
+ const DEFAULT_INTERACTION_EASING = EasingKeyword.EaseOut;
6782
+ const NUMBER = '\\s*(-?\\d*\\.?\\d+)\\s*';
6783
+ const CUBIC_BEZIER = new RegExp(`^cubic-bezier\\(${NUMBER},${NUMBER},${NUMBER},${NUMBER}\\)$`);
6784
+ /** A well-formed `cubic-bezier(x1, y1, x2, y2)` whose x values sit in [0, 1] (what CSS and WAAPI accept). */
6785
+ function isCubicBezier(value) {
6786
+ const m = CUBIC_BEZIER.exec(String(value ?? '').trim());
6787
+ if (!m)
6788
+ return false;
6789
+ const [x1, , x2] = [Number(m[1]), Number(m[2]), Number(m[3])];
6790
+ return x1 >= 0 && x1 <= 1 && x2 >= 0 && x2 <= 1;
6791
+ }
6792
+ function isEasingToken(value) {
6793
+ return EASING_TOKENS.includes(String(value ?? ''));
6794
+ }
6795
+ /** True for anything an interaction may store as its easing. */
6796
+ function isInteractionEasing(value) {
6797
+ return EASING_KEYWORDS.includes(String(value ?? '')) || isEasingToken(value) || isCubicBezier(value);
6798
+ }
6799
+ /** A token's value is a timing function WAAPI plays: a keyword, a cubic-bezier, or a `linear()` / `steps()` curve (the
6800
+ * spring's). Read by shape, so it answers alike in every browser, on the server and in a spec. */
6801
+ const TIMING_FUNCTION = /^(linear|steps)\(.+\)$/;
6802
+ function supportedEasing(value) {
6803
+ return EASING_KEYWORDS.includes(value) || isCubicBezier(value) || TIMING_FUNCTION.test(value);
6804
+ }
6805
+ /**
6806
+ * The timing function WAAPI plays for `easing` on `el`: a keyword or a curve as stored; a token name → the token's
6807
+ * value as the element sees it (`--iox-ease-spring` → its `linear()`), so a section scheme or the canvas's token scope
6808
+ * applies; anything else, or a token the element cannot resolve → `fallback`.
6809
+ */
6810
+ function resolveInteractionEasing(easing, el, fallback = DEFAULT_INTERACTION_EASING) {
6811
+ const value = String(easing ?? '').trim();
6812
+ if (EASING_KEYWORDS.includes(value) || isCubicBezier(value))
6813
+ return value;
6814
+ if (!isEasingToken(value))
6815
+ return fallback;
6816
+ const resolved = el?.ownerDocument?.defaultView?.getComputedStyle(el).getPropertyValue(`--iox-${value}`)?.trim();
6817
+ return resolved && supportedEasing(resolved) ? resolved : fallback;
6818
+ }
6819
+
6820
+ /**
6821
+ * When a pointer event should move something (G7 — Emil 85/86/91, mobile-native M1/M5). Both runtimes ask here.
6822
+ *
6823
+ * · A hover trigger answers a mouse or a pen only: a finger has no hover, and a tap's emulated mouseenter left the
6824
+ * hover state on until the next tap elsewhere.
6825
+ * · Press feedback lands on pointer-DOWN, at once — the click (pointer-up) commits. Only an action that is pure
6826
+ * feedback moves early: an animation of the pressed element itself that hides nothing and shows nothing. Opening,
6827
+ * closing, navigating and toggling stay on the click, so dragging away still cancels them. A keyboard press (Enter,
6828
+ * Space) has no pointer-down, so it never animates (Emil 2).
6829
+ */
6830
+ var PointerKind;
6831
+ (function (PointerKind) {
6832
+ PointerKind["Mouse"] = "mouse";
6833
+ PointerKind["Pen"] = "pen";
6834
+ PointerKind["Touch"] = "touch";
6835
+ })(PointerKind || (PointerKind = {}));
6836
+ const HOVERING_POINTERS = new Set([PointerKind.Mouse, PointerKind.Pen]);
6837
+ /** True for a pointer that really hovers — a mouse or a pen, never a finger. An event with no pointer type (a synthetic
6838
+ * `mouseenter`) counts as a mouse. */
6839
+ function isHoverPointer(event) {
6840
+ const kind = event?.pointerType;
6841
+ return !kind || HOVERING_POINTERS.has(kind);
6842
+ }
6843
+ /** True for an action that is press FEEDBACK: it animates the pressed element itself and neither hides nor shows it. */
6844
+ function isPressFeedback(action) {
6845
+ if (!action || action.disabled)
6846
+ return false;
6847
+ if (action.target && action.target !== 'self')
6848
+ return false;
6849
+ if (ENTRANCE_ACTION_TYPES.has(action.type) || EXIT_ACTION_TYPES.has(action.type) || action.type === VisibilityActionType.Toggle)
6850
+ return false;
6851
+ return !!buildInteractionKeyframes(action.type, action.params);
6852
+ }
6853
+ /** A click interaction's actions split by when they run: the feedback on pointer-down, the rest on the click. */
6854
+ function splitPressFeedback(actions) {
6855
+ const onPress = [];
6856
+ const onClick = [];
6857
+ for (const action of actions ?? [])
6858
+ (isPressFeedback(action) ? onPress : onClick).push(action);
6859
+ return { onPress, onClick };
6860
+ }
6861
+
6862
+ /** How far the page has to move before it counts as scrolled — past a trackpad's rest jitter. */
6863
+ const SCROLLED_THRESHOLD_PX = 8;
6864
+ /** True once `offset` has passed the threshold. */
6865
+ function isScrolled(offset, threshold = SCROLLED_THRESHOLD_PX) {
6866
+ return Number.isFinite(offset) && offset > threshold;
6867
+ }
6868
+ /**
6869
+ * Keep `SCROLLED_ATTRIBUTE` on `target` while `source` has scrolled past the threshold — what the Scrolled state (G4)
6870
+ * compiles against, on the live site (the window → the document element) and on the canvas (its scroller → the page).
6871
+ * One passive listener, read once per frame, written only when it changes. Returns the stop.
6872
+ */
6873
+ function watchScrolled(source, target, threshold = SCROLLED_THRESHOLD_PX) {
6874
+ let frame = 0;
6875
+ const raf = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : (fn) => setTimeout(() => fn(0), 16);
6876
+ const caf = typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : (id) => clearTimeout(id);
6877
+ const sync = () => {
6878
+ frame = 0;
6879
+ const scrolled = isScrolled(source.scrollY ?? source.scrollTop ?? 0, threshold);
6880
+ if (scrolled !== target.hasAttribute(SCROLLED_ATTRIBUTE))
6881
+ target.toggleAttribute(SCROLLED_ATTRIBUTE, scrolled);
6882
+ };
6883
+ const onScroll = () => { if (!frame)
6884
+ frame = raf(sync); };
6885
+ source.addEventListener('scroll', onScroll, { passive: true });
6886
+ sync();
6887
+ return () => {
6888
+ source.removeEventListener('scroll', onScroll);
6889
+ if (frame)
6890
+ caf(frame);
6891
+ target.removeAttribute(SCROLLED_ATTRIBUTE);
6892
+ };
6893
+ }
6894
+
6657
6895
  /**
6658
6896
  * page-style — the ONE compiler that turns a page's style settings into CSS.
6659
6897
  *
@@ -8721,5 +8959,5 @@ function attachStringForm(form, options = {}) {
8721
8959
  * Generated bundle index. Do not edit.
8722
8960
  */
8723
8961
 
8724
- export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, ENTRANCE_FALLBACK_CSS, ENTRANCE_NOSCRIPT_CSS, ENTRANCE_PRESTATE_ATTRIBUTE, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_ANIMATION_ID, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, IoxVideoComponent, LAST_EMITTED_STATES, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LEGACY_SWIPER_CONTROLS, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, NAMED_GROUP_ROLES, NO_PAGE_TRANSITION, ON_STATE_ROLES, OUTER_STYLE_PROPS, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PHONE_TRANSITION_QUERY, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, RELATIONAL_STATE_PSEUDO, RevealActionType, SCROLL_EASINGS, SELF_PLAYING_TRIGGERS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, STRUCTURAL_STATES, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, SettleMode, StringFormRuntime, SwiperModuleName, TARGETED_STATE_SUFFIX, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, animateAction, applyBindingPipes, applyFeel, attachStringForm, bootSlider, breakpointDef, breakpointForWidth, breakpointQueryFor, buildInteractionKeyframes, buildPartSelector, buildSwiperBreakpoints, buildTargetedStateSelector, buildTargetedSuffixSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, domHasSliderParts, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, entranceStartsHidden, feelFor, finalKeyframe, framesFromLiveValue, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, isStructuralState, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, pageTransitionOf, parentBreakpoint, parseUrlList, partClassName, planPartRule, planResponsiveRules, planScroll, planStateRule, planSwiperOptions, playDualLayerTransition, playInteraction, prefersReducedMotion, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveSeo, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, settleInteraction, settleModeFor, sliderControlElements, sliderInputsFromProps, sliderInputsOf, splitOuterProps, stateNamesIn, statePseudoClass, stateSelectorSuffix, stringFormRuntimeOf, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, syncSubmitButtons, targetedStateSelector, viewportTransitionContext, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
8962
+ export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_INTERACTION_EASING, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, EASING_KEYWORDS, EASING_TOKENS, ENTRANCE_ACTION_TYPES, ENTRANCE_FALLBACK_CSS, ENTRANCE_NOSCRIPT_CSS, ENTRANCE_PRESTATE_ATTRIBUTE, EXIT_ACTION_TYPES, EasingKeyword, EasingToken, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION_TRAITS, HOVER_MEDIA_QUERY, HOVER_STATES, INTERACTION_ANIMATION_ID, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, IoxVideoComponent, LAST_EMITTED_STATES, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION_TRAITS, LEGACY_SWIPER_CONTROLS, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, NAMED_GROUP_ROLES, NO_PAGE_TRANSITION, ON_STATE_ROLES, OUTER_STYLE_PROPS, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PHONE_TRANSITION_QUERY, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, PointerKind, REDUCED_MOTION_QUERY, RELATIONAL_STATE_PSEUDO, RevealActionType, SCROLLED_ATTRIBUTE, SCROLLED_STATE_ROLE, SCROLLED_THRESHOLD_PX, SCROLL_EASINGS, SELF_PLAYING_TRIGGERS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, STRUCTURAL_STATES, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, SettleMode, StringFormRuntime, SwiperModuleName, TARGETED_STATE_SUFFIX, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TRANSITION_PROPERTY_KEY, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, animateAction, applyBindingPipes, applyFeel, attachStringForm, bootSlider, breakpointDef, breakpointForWidth, breakpointQueryFor, buildInteractionKeyframes, buildPartSelector, buildSwiperBreakpoints, buildTargetedStateSelector, buildTargetedSuffixSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, declaredInputNames, decomposeBackdropFilter, defaultHoverTransitionTraits, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, domHasSliderParts, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, entranceStartsHidden, feelFor, finalKeyframe, framesFromLiveValue, gateHoverRule, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCubicBezier, isCurrentLink, isEasingToken, isExternalOrFragment, isGradientPaint, isHoverPointer, isHoverState, isInputOverriddenAt, isInteractionEasing, isLastEmittedStateKey, isOverriddenAt, isPressFeedback, isScrolled, isStructuralState, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, pageTransitionOf, parentBreakpoint, parseUrlList, partClassName, planPartRule, planResponsiveRules, planScroll, planStateRule, planSwiperOptions, playDualLayerTransition, playInteraction, prefersReducedMotion, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolveInteractionEasing, resolvePageTransition, resolvePath, resolveSeo, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, settleInteraction, settleModeFor, sliderControlElements, sliderInputsFromProps, sliderInputsOf, splitOuterProps, splitPressFeedback, stateNamesIn, statePseudoClass, stateSelectorSuffix, stateTransitionProperties, stringFormRuntimeOf, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, syncSubmitButtons, targetedStateSelector, transitionPropertyList, viewportTransitionContext, watchScrolled, withContextSlots, withStateTransitions, wrapInBreakpointQuery, wrapInMediaQuery };
8725
8963
  //# sourceMappingURL=vectoriox-iox-ui.mjs.map