@vectoriox/iox-ui 5.43.0 → 5.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,6 +36,9 @@ declare const SPEC_SITE_PLAN_SHAPE: {
36
36
  readonly mode: "'light' | 'dark' | 'auto'";
37
37
  readonly "tint?": "string — axes.tints (DS9 neutral tint)";
38
38
  readonly "fonts?": "object — uploaded font families per role (DS5)";
39
+ readonly "scroll?": "string — axes.scrolls: how the site scrolls (rev 3.1; absent = axes.motionChoices.pre)";
40
+ readonly "animate?": "string — axes.animations: how much the sections move in as they scroll into view";
41
+ readonly "between?": "string — axes.betweens: what plays between pages";
39
42
  };
40
43
  readonly contentType: {
41
44
  readonly name: "string — unique";
package/index.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { AfterViewInit, EventEmitter, ElementRef, SimpleChanges, OnDestroy, OnInit, Renderer2, ModuleWithProviders, InjectionToken, ChangeDetectorRef, ViewContainerRef, OnChanges, Type, ComponentRef } from '@angular/core';
2
+ import { AfterViewInit, EventEmitter, ElementRef, SimpleChanges, OnDestroy, OnInit, Renderer2, ModuleWithProviders, OnChanges, InjectionToken, ChangeDetectorRef, ViewContainerRef, Type, ComponentRef } from '@angular/core';
3
3
  import { AnimationPlayer, AnimationBuilder } from '@angular/animations';
4
4
  import { Observable } from 'rxjs';
5
5
  import * as i3 from '@angular/platform-browser/animations';
6
- import Lenis from 'lenis';
6
+ import { ScrollId } from '@vectoriox/iox-ui/design-core';
7
+ export * from '@vectoriox/iox-ui/design-core';
8
+ import Lenis, { LenisOptions } from 'lenis';
7
9
  import * as i2 from '@angular/common';
8
10
  import { SafeHtml, DomSanitizer } from '@angular/platform-browser';
9
11
  import { VideoPlan } from '@vectoriox/iox-ui/media-core';
10
12
  import { SliderWords } from '@vectoriox/iox-ui/composite-core';
11
13
  import { OpenerNodeLike } from '@vectoriox/iox-ui/actions-core';
12
- export * from '@vectoriox/iox-ui/design-core';
13
14
  export * from '@vectoriox/iox-ui/data-core';
14
15
 
15
16
  declare class ViewPositionDirective implements AfterViewInit {
@@ -100,11 +101,27 @@ declare class IoxAosModule {
100
101
  static ɵinj: i0.ɵɵInjectorDeclaration<IoxAosModule>;
101
102
  }
102
103
 
104
+ /** Where the page scrolls, and how. `lenis: null` = the browser's own scrolling (planScroll's Normal). */
105
+ interface PageScrollStart {
106
+ /** The scrolling element; absent = the document (the live site scrolls the window, CC1). */
107
+ wrapper?: HTMLElement;
108
+ content?: HTMLElement;
109
+ lenis: LenisOptions | null;
110
+ }
111
+ /**
112
+ * The ONE page scroller (AI plan rev 3.1 §3p). `IoxPageComponent` starts it with the plan of the look's
113
+ * Scrolling choice (`planScroll`); `scrollTo` actions and the dialog lock go through it whether or not
114
+ * Lenis runs. Never on the server: there is no window to scroll.
115
+ */
103
116
  declare class PageScrollProvider {
104
117
  private zone;
105
- private lenis?;
118
+ private lenis;
106
119
  private rafId?;
107
- init(options?: ConstructorParameters<typeof Lenis>[0]): void;
120
+ private wrapper;
121
+ private lockedOverflow;
122
+ init(start: PageScrollStart): void;
123
+ /** Stop Lenis and its loop; the page falls back to the browser's own scrolling. */
124
+ destroy(): void;
108
125
  /** Hard-stop page scroll (dialog open, A1b): wheel/touch frozen until start(). */
109
126
  stop(): void;
110
127
  /** Resume after stop() — the dialog's close path, incl. native Esc/backdrop closes. */
@@ -117,17 +134,23 @@ declare class PageScrollProvider {
117
134
  duration?: number;
118
135
  immediate?: boolean;
119
136
  }): void;
120
- get instance(): Lenis;
137
+ /** The running Lenis, or null when the page scrolls natively (Normal, reduced motion, the server). */
138
+ get instance(): Lenis | null;
139
+ private scrollElement;
140
+ private unlock;
121
141
  private startLoop;
122
142
  static ɵfac: i0.ɵɵFactoryDeclaration<PageScrollProvider, never>;
123
143
  static ɵprov: i0.ɵɵInjectableDeclaration<PageScrollProvider>;
124
144
  }
125
145
 
126
- declare class IoxPageComponent implements AfterViewInit, OnDestroy {
146
+ declare class IoxPageComponent implements AfterViewInit, OnChanges, OnDestroy {
127
147
  private pageScrollProvider;
128
148
  class: string;
149
+ /** The look's Scrolling choice (`designMotion.scroll`); absent = Smooth, what every page did before rev 3.1. */
150
+ scroll?: ScrollId | string | null;
129
151
  page: ElementRef<HTMLDivElement>;
130
152
  private scrollSubscription;
153
+ private started;
131
154
  scrollEvent: EventEmitter<{
132
155
  scrollTop: number;
133
156
  scrollLeft: number;
@@ -139,10 +162,12 @@ declare class IoxPageComponent implements AfterViewInit, OnDestroy {
139
162
  }>;
140
163
  constructor(pageScrollProvider: PageScrollProvider);
141
164
  ngAfterViewInit(): void;
165
+ ngOnChanges(changes: SimpleChanges): void;
142
166
  onScroll(event: Event): void;
143
167
  ngOnDestroy(): void;
168
+ private startScroller;
144
169
  static ɵfac: i0.ɵɵFactoryDeclaration<IoxPageComponent, never>;
145
- static ɵcmp: i0.ɵɵComponentDeclaration<IoxPageComponent, "iox-page-component", never, { "class": { "alias": "class"; "required": false; }; }, { "scrollEvent": "scrollEvent"; }, never, ["*"], false, never>;
170
+ static ɵcmp: i0.ɵɵComponentDeclaration<IoxPageComponent, "iox-page-component", never, { "class": { "alias": "class"; "required": false; }; "scroll": { "alias": "scroll"; "required": false; }; }, { "scrollEvent": "scrollEvent"; }, never, ["*"], false, never>;
146
171
  }
147
172
 
148
173
  declare class IoxPageModule {
@@ -2141,6 +2166,23 @@ declare function prefersReducedMotion(host?: MatchMediaHost | null | undefined):
2141
2166
  /** True when script may animate. The ONE question every runtime asks before moving anything. */
2142
2167
  declare function motionAllowed(host?: MatchMediaHost | null | undefined): boolean;
2143
2168
 
2169
+ /**
2170
+ * Scrolling (AI plan rev 3.1 §3p, MO-D1/MO-D7): the look's `scroll` choice → the options the ONE page
2171
+ * scroller starts Lenis with, or `null` for the browser's own scrolling. The table is the contract's
2172
+ * `axes.scrolls`; this only turns its named easing into the function Lenis wants. Pure: the caller asks the
2173
+ * visitor's reduced-motion preference (`prefersReducedMotion`) and passes the answer in.
2174
+ */
2175
+ declare enum ScrollEasing {
2176
+ ExpoOut = "expo-out"
2177
+ }
2178
+ /** The curves `axes.scrolls[].lenis.easing` may name. Expo-out is Lenis's own default curve. */
2179
+ declare const SCROLL_EASINGS: Readonly<Record<ScrollEasing, (t: number) => number>>;
2180
+ interface ScrollPlanContext {
2181
+ /** The visitor asked for reduced motion: the page always scrolls natively. */
2182
+ reducedMotion: boolean;
2183
+ }
2184
+ declare function planScroll(scroll: string | null | undefined, ctx: ScrollPlanContext): LenisOptions | null;
2185
+
2144
2186
  /**
2145
2187
  * THE interaction keyframes (composite plan T2 · CC1 N5). Both runtimes — the builder's
2146
2188
  * InteractionEngineService and the live site's ClientInteractionService — animate an element action
@@ -2799,19 +2841,6 @@ declare function isActionEnabled(action: ToggleableAction | null | undefined): b
2799
2841
  /** The actions that should actually run, preserving order. */
2800
2842
  declare function activeActions<T extends ToggleableAction>(actions: readonly T[] | undefined): T[];
2801
2843
 
2802
- /**
2803
- * Cinematic page-transition presets — the SINGLE SOURCE OF TRUTH shared by the page builder
2804
- * (preview) and the SSR client engine (production). Pure, transport-agnostic: this module only
2805
- * describes WAAPI keyframe pairs + timing/ordering. It never touches the DOM.
2806
- *
2807
- * A page transition plays TWO synchronized WAAPI animations at once — an `out` animation on a
2808
- * snapshot of the outgoing page and an `in` animation on the incoming page — inside a shared
2809
- * `perspective` container, gated on BOTH finishing (the "cinematic" feel, à la Codrops
2810
- * PageTransitions). The consumer supplies the DOM (snapshot layer + live page) and calls
2811
- * `element.animate(frames, options)`; it does NOT redefine the frames.
2812
- *
2813
- * @see architecture/builder/page-transitions.md
2814
- */
2815
2844
  /** Perspective (px) applied to the transition container unless a preset overrides it. */
2816
2845
  declare const DEFAULT_PAGE_TRANSITION_PERSPECTIVE = 1200;
2817
2846
  /**
@@ -2945,6 +2974,65 @@ declare function effectiveTransitionId(anim: {
2945
2974
  transition?: string | null;
2946
2975
  enter?: string | null;
2947
2976
  } | null | undefined): string;
2977
+ /** The settings a page's own `routeAnimation` carries (Page ▸ Design ▸ Route animation). */
2978
+ interface RouteAnimationLike {
2979
+ transition?: string | null;
2980
+ enter?: string | null;
2981
+ duration?: number | string;
2982
+ easing?: string;
2983
+ perspective?: number | string;
2984
+ curtainColor?: string;
2985
+ }
2986
+ interface PageTransitionContext {
2987
+ /** `pageSettings.designSystem` — only a design-system page follows the look. */
2988
+ designSystem?: boolean;
2989
+ /** The look's Between pages choice (`designMotion.between`); absent = `PRE_MOTION.between`. */
2990
+ between?: string | null;
2991
+ /** The viewport is a phone (`PHONE_TRANSITION_QUERY`): Slide and Cinematic play their `phone` choice. */
2992
+ phone?: boolean;
2993
+ /** The page reads right to left: a Slide pushes from the other side. */
2994
+ rtl?: boolean;
2995
+ /** The page's initial (hydrated) load — no outgoing page. */
2996
+ initial?: boolean;
2997
+ }
2998
+ /** What `resolvePageTransition` is asked for. `transition: 'none'` is a valid, silent choice. */
2999
+ interface PlannedPageTransition {
3000
+ transition: string;
3001
+ duration?: number | string;
3002
+ easing?: string;
3003
+ perspective?: number | string;
3004
+ curtainColor?: string;
3005
+ /** The page follows the look (rather than its own Route animation). */
3006
+ fromLook: boolean;
3007
+ }
3008
+ declare const NO_PAGE_TRANSITION = "none";
3009
+ /** The widest viewport a page change treats as a phone (MO-D5): the Mobile landscape breakpoint. */
3010
+ declare const PHONE_TRANSITION_QUERY: string;
3011
+ /** What a window can tell `pageTransitionOf`: the media query and the computed direction. */
3012
+ interface TransitionViewport {
3013
+ matchMedia?: (query: string) => {
3014
+ matches: boolean;
3015
+ };
3016
+ getComputedStyle?: (el: Element) => {
3017
+ direction?: string;
3018
+ };
3019
+ }
3020
+ /**
3021
+ * The part of the context only the browser knows — is this a phone, does the page read right to left —
3022
+ * read the same way by every runtime that plays a page change (the engine, Preview). No window → neither.
3023
+ */
3024
+ declare function viewportTransitionContext(view: TransitionViewport | null | undefined, el: Element | null | undefined): Pick<PageTransitionContext, 'phone' | 'rtl'>;
3025
+ /** The page set its own transition (a preset id, or a legacy `enter` that migrates to one). */
3026
+ declare function hasOwnPageTransition(anim: RouteAnimationLike | null | undefined): boolean;
3027
+ /**
3028
+ * The ONE decision of what plays when a page arrives, for the engine and every builder preview:
3029
+ * • a page with its own Route animation plays it, exactly as before (an unflagged page too: with none set
3030
+ * it keeps the platform default, `effectiveTransitionId`);
3031
+ * • a design-system page with none follows the look's Between pages — never on its initial load (MO-D6:
3032
+ * no IN half, no server pre-state), as Fade on a phone for Slide and Cinematic (MO-D5), mirrored in RTL.
3033
+ * Reduced motion is the runtime's own question (`motionAllowed`), asked before anything plays.
3034
+ */
3035
+ declare function pageTransitionOf(anim: RouteAnimationLike | null | undefined, ctx?: PageTransitionContext): PlannedPageTransition;
2948
3036
 
2949
3037
  /**
2950
3038
  * Gallery effect registry — the SHARED, effect-agnostic description of how each gallery slide looks
@@ -3068,5 +3156,5 @@ declare class GalleryLoop {
3068
3156
  private emit;
3069
3157
  }
3070
3158
 
3071
- 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, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, 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, ON_STATE_ROLES, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, SectionComponent, 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, applyBindingPipes, applyFeel, 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, feelFor, finalKeyframe, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, parentBreakpoint, parseUrlList, partClassName, planResponsiveRules, planSwiperOptions, 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, sliderControlElements, sliderInputsFromProps, sliderInputsOf, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, targetedStateSelector, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
3072
- export type { A11yNodeSource, BoundValueSpec, BreakpointDef, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, CurrentLinkOptions, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, InteractionKeyframe, IoxContextSlot, IoxPipe, MatchMediaHost, NodeA11yContext, OverrideBreakpoint, PageStyleFieldState, PageStyleOptions, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SliderBootOptions, SliderControlElements, SliderInputs, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, SwiperModuleFlags, SwiperPlan, SwiperPlanContext, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, ValueSource, VirtualTraitGroup };
3159
+ 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, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, 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, 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, SCROLL_EASINGS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, 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, applyBindingPipes, applyFeel, 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, feelFor, finalKeyframe, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, 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, planResponsiveRules, planScroll, planSwiperOptions, 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, sliderControlElements, sliderInputsFromProps, sliderInputsOf, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, targetedStateSelector, viewportTransitionContext, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
3160
+ export type { A11yNodeSource, BoundValueSpec, BreakpointDef, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, CurrentLinkOptions, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, InteractionKeyframe, IoxContextSlot, IoxPipe, MatchMediaHost, NodeA11yContext, OverrideBreakpoint, PageScrollStart, PageStyleFieldState, PageStyleOptions, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionContext, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, PlannedPageTransition, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, RouteAnimationLike, ScrollPlanContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SliderBootOptions, SliderControlElements, SliderInputs, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, SwiperModuleFlags, SwiperPlan, SwiperPlanContext, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, TransitionViewport, ValueSource, VirtualTraitGroup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.43.0",
3
+ "version": "5.44.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -1,6 +1,9 @@
1
1
  import { SPEC_AXES, SPEC_FONT_ROLES } from './flavor-spec.generated';
2
2
  import { parseHexColor } from './brand';
3
- import { DesignChoices, DesignMode, FlavorPackV2, FontRoleToken, PRE_TINT_DEFAULT, SPEC_FLAVORS, TintId } from './design-token.model';
3
+ import {
4
+ AnimateId, BetweenId, DesignChoices, DesignMode, DesignMotion, FlavorPackV2, FontRoleToken, PRE_MOTION, PRE_TINT_DEFAULT, ScrollId,
5
+ SPEC_FLAVORS, TintId,
6
+ } from './design-token.model';
4
7
  import type { FlavorRevertSnapshot } from './flavor-seed';
5
8
 
6
9
  /**
@@ -32,6 +35,9 @@ export enum DesignConfigKey {
32
35
  Mode = 'mode',
33
36
  Tint = 'tint',
34
37
  Fonts = 'fonts',
38
+ Scroll = 'scroll',
39
+ Animate = 'animate',
40
+ Between = 'between',
35
41
  Revert = 'revert',
36
42
  }
37
43
 
@@ -124,6 +130,16 @@ export function resolveDesignChoices(partial: Partial<DesignChoices> | null | un
124
130
  mode: (MODES.includes(asString(partial?.mode) ?? '') ? asString(partial?.mode) : pack.defaults.mode) as DesignMode,
125
131
  tint: keyOf(SPEC_AXES.tints, asString(partial?.tint)) ?? pack.defaults.tint,
126
132
  ...(fonts ? { fonts } : {}),
133
+ ...resolveDesignMotion(partial),
134
+ };
135
+ }
136
+
137
+ /** The look's motion, completed: an absent or unknown choice reads `PRE_MOTION` — what the site already does. */
138
+ export function resolveDesignMotion(partial: Readonly<Partial<Record<keyof DesignMotion, unknown>>> | null | undefined): DesignMotion {
139
+ return {
140
+ scroll: keyOf<ScrollId>(SPEC_AXES.scrolls, asString(partial?.scroll)) ?? PRE_MOTION.scroll,
141
+ animate: keyOf<AnimateId>(SPEC_AXES.animations, asString(partial?.animate)) ?? PRE_MOTION.animate,
142
+ between: keyOf<BetweenId>(SPEC_AXES.betweens, asString(partial?.between)) ?? PRE_MOTION.between,
127
143
  };
128
144
  }
129
145
 
@@ -135,12 +151,13 @@ export function packDesignChoices(flavorId: string): DesignChoices {
135
151
  /**
136
152
  * What picking a pack card means, given the design the studio wears: the applied pack → the applied
137
153
  * choices; another pack → that pack's defaults, CARRYING the author's chosen fonts (§5b — a font the
138
- * author chose survives a pack switch unless the author changes it). Both picker homes call this.
154
+ * author chose survives a pack switch unless the author changes it) and the site's motion (rev 3.1 MO-D3 —
155
+ * how it scrolls and changes page is not a pack's, so a Look pick never resets it). Both picker homes call this.
139
156
  */
140
157
  export function choicesForPack(flavorId: string, applied: DesignChoices | null | undefined): DesignChoices {
141
158
  const pack = flavorPackById(flavorId) ?? defaultPack();
142
159
  if (applied && resolveDesignChoices(applied).flavorId === pack.id) return resolveDesignChoices(applied);
143
- return resolveDesignChoices({ flavorId: pack.id, fonts: applied?.fonts });
160
+ return resolveDesignChoices({ flavorId: pack.id, fonts: applied?.fonts, ...resolveDesignMotion(applied) });
144
161
  }
145
162
 
146
163
  /**
@@ -164,6 +181,9 @@ export function readDesignConfig(config: ReadonlyArray<DesignConfigEntry> | null
164
181
  mode: map.get(DesignConfigKey.Mode) as DesignMode,
165
182
  tint: (storedTint ?? undefined) as TintId | undefined,
166
183
  fonts: map.get(DesignConfigKey.Fonts) as DesignChoices['fonts'],
184
+ scroll: map.get(DesignConfigKey.Scroll) as ScrollId,
185
+ animate: map.get(DesignConfigKey.Animate) as AnimateId,
186
+ between: map.get(DesignConfigKey.Between) as BetweenId,
167
187
  });
168
188
  return {
169
189
  choices,
@@ -180,6 +200,11 @@ export function designModeOf(config: ReadonlyArray<DesignConfigEntry> | null | u
180
200
  return readDesignConfig(config).choices.mode;
181
201
  }
182
202
 
203
+ /** The look's motion of a stored config — what the public page read hands the engine (`designMotion`). */
204
+ export function designMotionOf(config: ReadonlyArray<DesignConfigEntry> | null | undefined): DesignMotion {
205
+ return resolveDesignMotion(readDesignConfig(config).choices);
206
+ }
207
+
183
208
  /** Resolved choices (+ the revert snapshot) → the config array a settings row stores. */
184
209
  export function designConfigEntries(
185
210
  choices: DesignChoices,
@@ -198,6 +223,9 @@ export function designConfigEntries(
198
223
  { key: DesignConfigKey.PillButtons, value: String(c.pillButtons) },
199
224
  { key: DesignConfigKey.Mode, value: c.mode },
200
225
  { key: DesignConfigKey.Tint, value: c.tint },
226
+ { key: DesignConfigKey.Scroll, value: c.scroll },
227
+ { key: DesignConfigKey.Animate, value: c.animate },
228
+ { key: DesignConfigKey.Between, value: c.between },
201
229
  ...(c.fonts ? [{ key: DesignConfigKey.Fonts, value: JSON.stringify(c.fonts) }] : []),
202
230
  ...(revert ? [{ key: DesignConfigKey.Revert, value: JSON.stringify(revert) }] : []),
203
231
  ];
@@ -210,5 +238,6 @@ export function sameDesignChoices(a: DesignChoices, b: DesignChoices): boolean {
210
238
  return x.flavorId === y.flavorId && x.seed.toLowerCase() === y.seed.toLowerCase() && x.pairId === y.pairId
211
239
  && x.shape === y.shape && x.density === y.density && x.motion === y.motion
212
240
  && x.pillButtons === y.pillButtons && x.mode === y.mode && x.tint === y.tint
241
+ && x.scroll === y.scroll && x.animate === y.animate && x.between === y.between
213
242
  && JSON.stringify(x.fonts ?? {}) === JSON.stringify(y.fonts ?? {});
214
243
  }
@@ -46,6 +46,25 @@ export type TintId = keyof typeof SPEC_AXES.tints;
46
46
  export const TINT_IDS = Object.keys(SPEC_AXES.tints) as ReadonlyArray<TintId>;
47
47
  /** What a studio that stored its look BEFORE the tint existed wears: exactly the look's own palette. */
48
48
  export const PRE_TINT_DEFAULT: TintId = 'neutral';
49
+ /** How the site scrolls (AI plan rev 3.1 §3p): Normal (the browser's own) · Smooth · Extra smooth. */
50
+ export type ScrollId = keyof typeof SPEC_AXES.scrolls;
51
+ /** How much the sections move in as they scroll into view: Still · Subtle · Standard · Lively. */
52
+ export type AnimateId = keyof typeof SPEC_AXES.animations;
53
+ /** What plays between pages: None · Fade · Slide · Cinematic. */
54
+ export type BetweenId = keyof typeof SPEC_AXES.betweens;
55
+ export const SCROLL_IDS = Object.keys(SPEC_AXES.scrolls) as ReadonlyArray<ScrollId>;
56
+ export const ANIMATE_IDS = Object.keys(SPEC_AXES.animations) as ReadonlyArray<AnimateId>;
57
+ export const BETWEEN_IDS = Object.keys(SPEC_AXES.betweens) as ReadonlyArray<BetweenId>;
58
+ /** The look's motion — the three choices the runtime and the composer read from `settings.design`. */
59
+ export interface DesignMotion {
60
+ scroll: ScrollId;
61
+ animate: AnimateId;
62
+ between: BetweenId;
63
+ }
64
+ /** What a look stored BEFORE rev 3.1 wears: exactly what every live site did (Smooth · Still · Cinematic). */
65
+ export const PRE_MOTION: Readonly<DesignMotion> = SPEC_AXES.motionChoices.pre;
66
+ /** What a visitor who asks for reduced motion always gets, whatever the look chose. */
67
+ export const REDUCED_DESIGN_MOTION: Readonly<DesignMotion> = SPEC_AXES.motionChoices.reduced;
49
68
  export type SchemeId = keyof typeof SPEC_SCHEMES;
50
69
  export type FontRoleToken = (typeof SPEC_FONT_ROLES)[number]['token'];
51
70
 
@@ -63,6 +82,10 @@ export interface DesignChoices {
63
82
  * `neutral` — the palette it was applied with. */
64
83
  tint?: TintId;
65
84
  fonts?: Partial<Record<FontRoleToken, string>>;
85
+ /** Absent = `PRE_MOTION` (a row stored before rev 3.1 reads what its site already did). */
86
+ scroll?: ScrollId;
87
+ animate?: AnimateId;
88
+ between?: BetweenId;
66
89
  }
67
90
 
68
91
  export interface FlavorSpecToken {
@@ -1429,6 +1429,208 @@ export const SPEC_AXES = {
1429
1429
  "inverse-line": 0.03
1430
1430
  }
1431
1431
  }
1432
+ },
1433
+ "scrolls": {
1434
+ "normal": {
1435
+ "label": "Normal",
1436
+ "he": "רגילה",
1437
+ "hint": "The browser's own scrolling.",
1438
+ "lenis": null
1439
+ },
1440
+ "smooth": {
1441
+ "label": "Smooth",
1442
+ "he": "חלקה",
1443
+ "hint": "A short glide after each wheel turn — what every IOX site does today.",
1444
+ "lenis": {
1445
+ "duration": 1.2,
1446
+ "easing": "expo-out",
1447
+ "wheelMultiplier": 1,
1448
+ "syncTouch": false
1449
+ }
1450
+ },
1451
+ "extra": {
1452
+ "label": "Extra smooth",
1453
+ "he": "חלקה מאוד",
1454
+ "hint": "A longer, softer glide; each wheel turn moves a little less.",
1455
+ "lenis": {
1456
+ "duration": 1.8,
1457
+ "easing": "expo-out",
1458
+ "wheelMultiplier": 0.9,
1459
+ "syncTouch": false
1460
+ }
1461
+ }
1462
+ },
1463
+ "animations": {
1464
+ "still": {
1465
+ "label": "Still",
1466
+ "he": "ללא",
1467
+ "hint": "Nothing moves in as you scroll. Buttons and cards still respond to the pointer (Speed).",
1468
+ "parts": {}
1469
+ },
1470
+ "subtle": {
1471
+ "label": "Subtle",
1472
+ "he": "עדינה",
1473
+ "hint": "Each section's content fades in once, together.",
1474
+ "parts": {
1475
+ "content": {
1476
+ "type": "fadeIn",
1477
+ "duration": 500
1478
+ }
1479
+ }
1480
+ },
1481
+ "standard": {
1482
+ "label": "Standard",
1483
+ "he": "רגילה",
1484
+ "hint": "Headings rise in; cards, prices and steps follow one after another.",
1485
+ "parts": {
1486
+ "heading": {
1487
+ "type": "moveUp",
1488
+ "distance": 16,
1489
+ "duration": 500
1490
+ },
1491
+ "items": {
1492
+ "type": "moveUp",
1493
+ "distance": 16,
1494
+ "duration": 450,
1495
+ "stagger": 80,
1496
+ "cap": 6
1497
+ },
1498
+ "media": {
1499
+ "type": "fadeIn",
1500
+ "duration": 600
1501
+ }
1502
+ }
1503
+ },
1504
+ "lively": {
1505
+ "label": "Lively",
1506
+ "he": "תוססת",
1507
+ "hint": "Bigger, longer entrances; side-by-side rows slide in from their side; the hero's words fade in.",
1508
+ "parts": {
1509
+ "heroText": {
1510
+ "type": "fadeIn",
1511
+ "duration": 400
1512
+ },
1513
+ "heading": {
1514
+ "type": "moveUp",
1515
+ "distance": 24,
1516
+ "duration": 600
1517
+ },
1518
+ "items": {
1519
+ "type": "moveUp",
1520
+ "distance": 24,
1521
+ "duration": 600,
1522
+ "stagger": 100,
1523
+ "cap": 8
1524
+ },
1525
+ "media": {
1526
+ "type": "fadeIn",
1527
+ "duration": 700
1528
+ },
1529
+ "rowMedia": {
1530
+ "type": "moveLeft",
1531
+ "mirror": "moveRight",
1532
+ "distance": 24,
1533
+ "duration": 600
1534
+ }
1535
+ }
1536
+ }
1537
+ },
1538
+ "animationFamilies": {
1539
+ "nav": [],
1540
+ "footer": [],
1541
+ "hero": [
1542
+ "heroText"
1543
+ ],
1544
+ "header": [
1545
+ "heading"
1546
+ ],
1547
+ "detail": [
1548
+ "heading",
1549
+ "media"
1550
+ ],
1551
+ "proof": [
1552
+ "content",
1553
+ "heading",
1554
+ "items"
1555
+ ],
1556
+ "feature": [
1557
+ "content",
1558
+ "heading",
1559
+ "items",
1560
+ "media",
1561
+ "rowMedia"
1562
+ ],
1563
+ "collection": [
1564
+ "content",
1565
+ "heading",
1566
+ "items"
1567
+ ],
1568
+ "story": [
1569
+ "content",
1570
+ "heading",
1571
+ "media"
1572
+ ],
1573
+ "cta": [
1574
+ "content",
1575
+ "heading"
1576
+ ],
1577
+ "contact": [
1578
+ "content",
1579
+ "heading"
1580
+ ],
1581
+ "faq": [
1582
+ "content",
1583
+ "heading"
1584
+ ],
1585
+ "pricing": [
1586
+ "content",
1587
+ "heading",
1588
+ "items"
1589
+ ]
1590
+ },
1591
+ "betweens": {
1592
+ "none": {
1593
+ "label": "None",
1594
+ "he": "ללא",
1595
+ "hint": "The next page simply appears.",
1596
+ "transition": "none"
1597
+ },
1598
+ "fade": {
1599
+ "label": "Fade",
1600
+ "he": "דהייה",
1601
+ "hint": "The page fades into the next.",
1602
+ "transition": "fade",
1603
+ "feel": "snappy"
1604
+ },
1605
+ "slide": {
1606
+ "label": "Slide",
1607
+ "he": "החלקה",
1608
+ "hint": "The next page pushes this one aside (from the left on a Hebrew site). On phones: Fade.",
1609
+ "transition": "move-left",
1610
+ "rtl": "move-right",
1611
+ "feel": "smooth",
1612
+ "phone": "fade"
1613
+ },
1614
+ "cinematic": {
1615
+ "label": "Cinematic",
1616
+ "he": "קולנועית",
1617
+ "hint": "This page sinks back while the next rises over it. On phones: Fade.",
1618
+ "transition": "parallax-up",
1619
+ "feel": "cinematic",
1620
+ "phone": "fade"
1621
+ }
1622
+ },
1623
+ "motionChoices": {
1624
+ "pre": {
1625
+ "scroll": "smooth",
1626
+ "animate": "still",
1627
+ "between": "cinematic"
1628
+ },
1629
+ "reduced": {
1630
+ "scroll": "normal",
1631
+ "animate": "still",
1632
+ "between": "none"
1633
+ }
1432
1634
  }
1433
1635
  } as const;
1434
1636
 
@@ -3,7 +3,7 @@ import { flavorColours, FlavorColours, preTintAccentGrounds } from './brand-neut
3
3
  import { fontStackWithout, formatFontFamily } from './fonts';
4
4
  import { SPEC_AXES } from './flavor-spec.generated';
5
5
  import {
6
- DesignChoices, DesignTokenRow, FlavorPackV2, FlavorSpecToken, flavorSourceStamp, USER_TOKEN_SOURCE,
6
+ DesignChoices, DesignTokenRow, FlavorPackV2, FlavorSpecToken, flavorSourceStamp, PRE_MOTION, USER_TOKEN_SOURCE,
7
7
  } from './design-token.model';
8
8
 
9
9
  /**
@@ -37,6 +37,7 @@ export function defaultDesignChoices(pack: FlavorPackV2): DesignChoices {
37
37
  seed: pack.seed,
38
38
  pairId: pack.pairs[0]?.id ?? '',
39
39
  ...pack.defaults,
40
+ ...PRE_MOTION,
40
41
  };
41
42
  }
42
43
 
@@ -37,7 +37,10 @@ export const SPEC_SITE_PLAN_SHAPE = {
37
37
  "pillButtons": "boolean",
38
38
  "mode": "'light' | 'dark' | 'auto'",
39
39
  "tint?": "string — axes.tints (DS9 neutral tint)",
40
- "fonts?": "object — uploaded font families per role (DS5)"
40
+ "fonts?": "object — uploaded font families per role (DS5)",
41
+ "scroll?": "string — axes.scrolls: how the site scrolls (rev 3.1; absent = axes.motionChoices.pre)",
42
+ "animate?": "string — axes.animations: how much the sections move in as they scroll into view",
43
+ "between?": "string — axes.betweens: what plays between pages"
41
44
  },
42
45
  "contentType": {
43
46
  "name": "string — unique",
@@ -200,6 +200,9 @@ function validateDesign(c: Collector, design: unknown): void {
200
200
  axis('density', SPEC_AXES.densities);
201
201
  axis('motion', SPEC_AXES.motions);
202
202
  axis('tint', SPEC_AXES.tints);
203
+ axis('scroll', SPEC_AXES.scrolls);
204
+ axis('animate', SPEC_AXES.animations);
205
+ axis('between', SPEC_AXES.betweens);
203
206
  if (design['mode'] !== undefined && !(SPEC_AXES.modes as ReadonlyArray<unknown>).includes(design['mode'])) bad('mode', `must be one of ${SPEC_AXES.modes.join(', ')}`);
204
207
  if (design['pillButtons'] !== undefined && typeof design['pillButtons'] !== 'boolean') bad('pillButtons', 'must be a boolean');
205
208
  if (design['fonts'] !== undefined && !isObject(design['fonts'])) bad('fonts', 'must be an object');