@vectoriox/iox-ui 5.84.0 → 5.86.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.
- package/actions-core/index.d.ts +47 -3
- package/composite-core/index.d.ts +93 -15
- package/design-core/index.d.ts +2013 -187
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs +153 -70
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-composite-core.mjs +307 -39
- package/fesm2022/vectoriox-iox-ui-composite-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +2222 -85
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-forms-core.mjs +27 -4
- package/fesm2022/vectoriox-iox-ui-forms-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +250 -12
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs +101 -5
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +1545 -78
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/forms-core/index.d.ts +23 -4
- package/generation-core/index.d.ts +13 -0
- package/index.d.ts +563 -62
- package/package.json +1 -1
- package/scene-core/index.d.ts +32 -4
- package/src/actions-core/action.model.ts +1 -1
- package/src/actions-core/dialog-motion.ts +25 -0
- package/src/actions-core/dialog-opener.ts +9 -3
- package/src/actions-core/element-actions.ts +25 -1
- package/src/actions-core/popover-opener.ts +50 -0
- package/src/actions-core/public-api.ts +1 -0
- package/src/composite-core/header.ts +25 -0
- package/src/composite-core/public-api.ts +1 -0
- package/src/composite-core/slider-runtime.ts +82 -0
- package/src/composite-core/slider.ts +194 -27
- package/src/composite-core/structure.ts +14 -9
- package/src/design-core/flavor-spec.generated.ts +2145 -85
- package/src/design-core/header-behaviour.ts +76 -0
- package/src/design-core/public-api.ts +1 -0
- package/src/design-core/theme-color.ts +21 -5
- package/src/forms-core/submit-lifecycle.ts +30 -3
- package/src/generation-core/baseline-roles.ts +1 -0
- package/src/generation-core/compose-site.ts +8 -0
- package/src/generation-core/node-baselines.generated.ts +231 -11
- package/src/generation-core/patterns.ts +2 -0
- package/src/generation-core/site-spec.generated.ts +8 -0
- package/src/scene-core/scene.ts +18 -0
- package/src/scene-core/scroll-driver.ts +83 -6
- package/styles/iox-design.css +431 -1
- package/styles/iox-forms.css +29 -4
|
@@ -48,7 +48,25 @@ var SceneAttribute;
|
|
|
48
48
|
SceneAttribute["Root"] = "data-iox-scene";
|
|
49
49
|
SceneAttribute["Tracks"] = "data-iox-tracks";
|
|
50
50
|
SceneAttribute["PhoneTracks"] = "data-iox-tracks-phone";
|
|
51
|
+
/** A root's stage that is not a Scene's `scene.stage` part: the slider a scroll plays (premium M4). */
|
|
52
|
+
SceneAttribute["Stage"] = "data-iox-scene-stage";
|
|
51
53
|
})(SceneAttribute || (SceneAttribute = {}));
|
|
54
|
+
/**
|
|
55
|
+
* A SLIDER THE SCROLL PLAYS (premium components M4 — M1.3's held gallery). Its progress rests on each picture and changes
|
|
56
|
+
* only over the middle share of each step, so every picture arrives and stays a moment while the page holds it: `p` is
|
|
57
|
+
* the scroll's progress through the runway (0–1), `n` the slides; the answer is Swiper's progress (0–1).
|
|
58
|
+
*/
|
|
59
|
+
const SLIDER_HOLD_SHARE = 0.2;
|
|
60
|
+
/** Page px of scroll per picture, as a share of the screen's height (the runway the renderers reserve). */
|
|
61
|
+
const SLIDER_SCROLL_STEP_SCREENS = 0.55;
|
|
62
|
+
function heldProgress(p, n) {
|
|
63
|
+
if (!(n >= 2) || !Number.isFinite(p))
|
|
64
|
+
return 0;
|
|
65
|
+
const s = clamp01(p) * (n - 1);
|
|
66
|
+
const i = Math.min(n - 2, Math.floor(s));
|
|
67
|
+
const f = clamp01((s - i - SLIDER_HOLD_SHARE) / (1 - 2 * SLIDER_HOLD_SHARE));
|
|
68
|
+
return round((i + smoothstep(f)) / (n - 1));
|
|
69
|
+
}
|
|
52
70
|
/** At or under this stage width a layer plays its phone tracks. The `mobile` breakpoint's width. */
|
|
53
71
|
const SCENE_PHONE_MAX_WIDTH = FRAMES_PHONE_MAX_WIDTH;
|
|
54
72
|
const DECIMALS = 10000;
|
|
@@ -779,8 +797,26 @@ const SETTLED_PX = 0.25;
|
|
|
779
797
|
const MAX_FRAME_SECONDS = 0.1;
|
|
780
798
|
/** The first frame after a sleep has no previous one to measure from: it counts as one frame, or a scroll would lose it. */
|
|
781
799
|
const WAKING_FRAME_SECONDS = 1 / 60;
|
|
782
|
-
const STAGE_SELECTOR = `.iox-role-${SceneRole.Stage.replace(/\./g, '-')}`;
|
|
800
|
+
const STAGE_SELECTOR = `.iox-role-${SceneRole.Stage.replace(/\./g, '-')}, [${SceneAttribute.Stage}]`;
|
|
783
801
|
const TRANSFORM_WILL_CHANGE = 'transform, opacity';
|
|
802
|
+
/**
|
|
803
|
+
* Dispatched (bubbling) by something the driver plays that arrived after the driver last looked — a Swiper booted on a
|
|
804
|
+
* slider's stage. Never a DOM `scroll`: nothing that follows the driver may dispatch one (SC2).
|
|
805
|
+
*/
|
|
806
|
+
const SCENE_WAKE_EVENT = 'iox-scene-wake';
|
|
807
|
+
/**
|
|
808
|
+
* ONE driver per root (premium M4 — §0 "To verify first"). The live site attaches a driver per runtime instance (the
|
|
809
|
+
* page's and the globals'); an instance that scanned a container holding the other's Scenes would drive them a second
|
|
810
|
+
* time, two damped values fighting over one layer. A root is held by the first driver that adds it until that driver
|
|
811
|
+
* lets it go.
|
|
812
|
+
*/
|
|
813
|
+
const HELD = new WeakMap();
|
|
814
|
+
function sliderOf(stage) {
|
|
815
|
+
// The stage is the runway's child: the `.swiper` itself (both engine paths), or the canvas's shell around it.
|
|
816
|
+
const holder = (stage?.classList.contains('swiper') ? stage : stage?.querySelector('.swiper'));
|
|
817
|
+
const swiper = holder?.swiper;
|
|
818
|
+
return swiper && typeof swiper.setProgress === 'function' && !swiper.destroyed ? swiper : null;
|
|
819
|
+
}
|
|
784
820
|
/** One frame of the damping: frame-rate independent, so 60 Hz and 120 Hz settle in the same time. */
|
|
785
821
|
function dampToward(current, target, seconds, rate = SCENE_DAMPING) {
|
|
786
822
|
return current + (target - current) * (1 - Math.exp(-seconds * rate));
|
|
@@ -809,6 +845,13 @@ class ScrollDriver {
|
|
|
809
845
|
this.frame = 0;
|
|
810
846
|
this.lastTime = 0;
|
|
811
847
|
this.wakeUp = () => this.wake();
|
|
848
|
+
this.onWakeEvent = (e) => {
|
|
849
|
+
const scene = [...this.scenes.values()].find(s => s.root.contains(e.target));
|
|
850
|
+
if (scene) {
|
|
851
|
+
scene.fresh = true;
|
|
852
|
+
this.wake();
|
|
853
|
+
}
|
|
854
|
+
};
|
|
812
855
|
this.tick = (now) => {
|
|
813
856
|
this.frame = 0;
|
|
814
857
|
const seconds = this.lastTime ? Math.min(MAX_FRAME_SECONDS, (now - this.lastTime) / 1000) : WAKING_FRAME_SECONDS;
|
|
@@ -837,13 +880,18 @@ class ScrollDriver {
|
|
|
837
880
|
// In capture: a scroll does not bubble, and the canvas's Lenis scrolls an element, not the window.
|
|
838
881
|
this.host.addEventListener('scroll', this.wakeUp, { capture: true, passive: true });
|
|
839
882
|
this.host.addEventListener('resize', this.wakeUp, { passive: true });
|
|
883
|
+
this.host.addEventListener(SCENE_WAKE_EVENT, this.onWakeEvent, { capture: true });
|
|
840
884
|
const Observer = this.host.ResizeObserver;
|
|
841
885
|
this.resize = Observer ? new Observer(() => this.remeasureAll()) : null;
|
|
842
886
|
}
|
|
843
|
-
/** Drive a Scene's root. Idempotent: adding a root again re-reads it. */
|
|
887
|
+
/** Drive a Scene's root. Idempotent: adding a root again re-reads it. False when another driver holds it. */
|
|
844
888
|
add(root) {
|
|
889
|
+
const holder = HELD.get(root);
|
|
890
|
+
if (holder && holder !== this)
|
|
891
|
+
return false;
|
|
845
892
|
this.remove(root);
|
|
846
|
-
|
|
893
|
+
HELD.set(root, this);
|
|
894
|
+
const scene = { root, stage: null, layers: [], progress: 0, target: 0, height: 0, travel: 0, stick: 0, phone: false, inlineSign: 1, fresh: true, mutations: null };
|
|
847
895
|
this.scenes.set(root, scene);
|
|
848
896
|
this.resize?.observe(root);
|
|
849
897
|
if (this.options.observe) {
|
|
@@ -854,11 +902,18 @@ class ScrollDriver {
|
|
|
854
902
|
}
|
|
855
903
|
}
|
|
856
904
|
this.readScene(scene);
|
|
905
|
+
return true;
|
|
906
|
+
}
|
|
907
|
+
/** True when this driver holds `root`. */
|
|
908
|
+
holds(root) {
|
|
909
|
+
return this.scenes.has(root);
|
|
857
910
|
}
|
|
858
911
|
remove(root) {
|
|
859
912
|
const scene = this.scenes.get(root);
|
|
860
913
|
if (!scene)
|
|
861
914
|
return;
|
|
915
|
+
if (HELD.get(root) === this)
|
|
916
|
+
HELD.delete(root);
|
|
862
917
|
scene.mutations?.disconnect();
|
|
863
918
|
this.resize?.unobserve(root);
|
|
864
919
|
if (scene.stage)
|
|
@@ -922,6 +977,7 @@ class ScrollDriver {
|
|
|
922
977
|
this.frame = 0;
|
|
923
978
|
this.host.removeEventListener('scroll', this.wakeUp, { capture: true });
|
|
924
979
|
this.host.removeEventListener('resize', this.wakeUp);
|
|
980
|
+
this.host.removeEventListener(SCENE_WAKE_EVENT, this.onWakeEvent, { capture: true });
|
|
925
981
|
for (const root of [...this.scenes.keys()])
|
|
926
982
|
this.remove(root);
|
|
927
983
|
this.resize?.disconnect();
|
|
@@ -971,6 +1027,9 @@ class ScrollDriver {
|
|
|
971
1027
|
measure(scene) {
|
|
972
1028
|
scene.height = scene.root.offsetHeight;
|
|
973
1029
|
scene.travel = Math.max(0, scene.height - (scene.stage?.offsetHeight ?? scene.height));
|
|
1030
|
+
// Where the stage holds: its sticky top (a played slider holds mid-screen; a Scene's stage at 0). Read when
|
|
1031
|
+
// something resizes, never per frame.
|
|
1032
|
+
scene.stick = scene.stage ? (parseFloat(this.host.getComputedStyle(scene.stage).top) || 0) : 0;
|
|
974
1033
|
scene.phone = (scene.stage?.offsetWidth ?? Infinity) <= SCENE_PHONE_MAX_WIDTH;
|
|
975
1034
|
scene.inlineSign = scene.stage && this.host.getComputedStyle(scene.stage).direction === 'rtl' ? -1 : 1;
|
|
976
1035
|
for (const layer of scene.layers)
|
|
@@ -980,10 +1039,14 @@ class ScrollDriver {
|
|
|
980
1039
|
const rect = scene.root.getBoundingClientRect();
|
|
981
1040
|
const origin = this.options.originOf?.(scene.root)?.getBoundingClientRect().top ?? 0;
|
|
982
1041
|
const zoom = scene.height ? rect.height / scene.height : 1;
|
|
983
|
-
const { local, progress } = sceneScrollState(rect.top, origin, zoom, scene.travel);
|
|
1042
|
+
const { local, progress } = sceneScrollState(rect.top, origin + scene.stick * zoom, zoom, scene.travel);
|
|
984
1043
|
return { scene, local, target: progress };
|
|
985
1044
|
}
|
|
986
1045
|
write(scene) {
|
|
1046
|
+
// A slider the scroll plays (M4): the damped moment, held on each picture.
|
|
1047
|
+
const slider = sliderOf(scene.stage);
|
|
1048
|
+
if (slider)
|
|
1049
|
+
slider.setProgress(heldProgress(scene.progress, slider.slides?.length ?? 0), 0);
|
|
987
1050
|
for (const layer of scene.layers) {
|
|
988
1051
|
const tracks = scene.phone && layer.phoneTracks.length ? layer.phoneTracks : layer.tracks;
|
|
989
1052
|
this.writeWords(layer, sceneWordsLit(tracks, scene.progress));
|
|
@@ -1062,6 +1125,39 @@ function attachScenes(container, options = {}) {
|
|
|
1062
1125
|
roots.forEach(root => driver.add(root));
|
|
1063
1126
|
return driver;
|
|
1064
1127
|
}
|
|
1128
|
+
/**
|
|
1129
|
+
* The Scene roots that belong to `elements` (each, and what it holds) — what one runtime instance owns: the page's nodes,
|
|
1130
|
+
* or the globals'. Never a whole container that also holds another instance's Scenes (premium M4: the globals' instance
|
|
1131
|
+
* scanned the app root and drove the page's Scenes a second time).
|
|
1132
|
+
*/
|
|
1133
|
+
function sceneRootsIn(elements) {
|
|
1134
|
+
const out = new Set();
|
|
1135
|
+
for (const el of elements) {
|
|
1136
|
+
if (el.hasAttribute?.(SceneAttribute.Root))
|
|
1137
|
+
out.add(el);
|
|
1138
|
+
// A wrapper the node renders around itself (a played slider's runway around its `.swiper`) is the node's own.
|
|
1139
|
+
const parent = el.parentElement;
|
|
1140
|
+
if (parent?.hasAttribute(SceneAttribute.Root) && parent.children.length === 1)
|
|
1141
|
+
out.add(parent);
|
|
1142
|
+
el.querySelectorAll?.(`[${SceneAttribute.Root}]`).forEach(root => out.add(root));
|
|
1143
|
+
}
|
|
1144
|
+
return [...out];
|
|
1145
|
+
}
|
|
1146
|
+
/** The live site's door for a runtime that owns some nodes: their Scenes on one driver (null when none, or under reduced
|
|
1147
|
+
* motion — the page's own CSS then lets each Scene flow). A root another driver holds stays with it. */
|
|
1148
|
+
function attachSceneRoots(roots, options = {}) {
|
|
1149
|
+
if (!roots.length)
|
|
1150
|
+
return null;
|
|
1151
|
+
const host = options.host ?? window;
|
|
1152
|
+
if (host.matchMedia?.('(prefers-reduced-motion: reduce)').matches)
|
|
1153
|
+
return null;
|
|
1154
|
+
const driver = new ScrollDriver(options);
|
|
1155
|
+
const held = roots.filter(root => driver.add(root));
|
|
1156
|
+
if (held.length)
|
|
1157
|
+
return driver;
|
|
1158
|
+
driver.destroy();
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1065
1161
|
|
|
1066
1162
|
/**
|
|
1067
1163
|
* @vectoriox/iox-ui/scene-core — a Scene: a stage that holds still while the page scrolls past it, its layers moving on
|
|
@@ -1074,5 +1170,5 @@ function attachScenes(container, options = {}) {
|
|
|
1074
1170
|
* Generated bundle index. Do not edit.
|
|
1075
1171
|
*/
|
|
1076
1172
|
|
|
1077
|
-
export { LIGHT_UP_PIECE_CAP, REVEAL_PIECE_CAP, SCENE_BUDGET, SCENE_DAMPING, SCENE_FILM_SMOOTH, SCENE_FLOW_RULES, SCENE_LENGTH, SCENE_MAX_SCREENS, SCENE_MIN_SCREENS, SCENE_PHONE_LENGTH, SCENE_PHONE_MAX_WIDTH, SCENE_REDUCED_MOTION_CSS, SCENE_REVEAL_DEFAULT, SCENE_STAGE_HEIGHT, SCENE_TRACK_DEFS, SCENE_WORDS, SCENE_WORD_DIM, SceneAttribute, SceneProp, SceneRevealFrom, ScrollDriver, TextRevealBy, applySceneFirstFrame, attachScenes, buildSceneRecipe, colourAt, dampToward, lineIndexes, movedSceneKey, normalizeTracks, playTextReveal, revealClipPath, revealStagger, sceneBudget, sceneColourKeyAt, sceneDisplayValue, sceneFilmDensity, sceneFirstFrame, sceneFrame, sceneFrameDeclarations, sceneHasKeyAt, sceneHeightForScreens, sceneKeyPosition, sceneLayersOf, sceneNodeAttributes, scenePlayed, sceneRevealFromOf, sceneRootOnPath, sceneScreensOf, sceneScrollState, sceneTrackDef, sceneTrackSpan, sceneWordOpacity, sceneWordsLit, scenesIn, splitText, syncSceneAttributes, textPieces, valueAt, withSceneKey, withScenePlayback, withSceneRevealFrom, withoutSceneKey, withoutScenePlayback };
|
|
1173
|
+
export { LIGHT_UP_PIECE_CAP, REVEAL_PIECE_CAP, SCENE_BUDGET, SCENE_DAMPING, SCENE_FILM_SMOOTH, SCENE_FLOW_RULES, SCENE_LENGTH, SCENE_MAX_SCREENS, SCENE_MIN_SCREENS, SCENE_PHONE_LENGTH, SCENE_PHONE_MAX_WIDTH, SCENE_REDUCED_MOTION_CSS, SCENE_REVEAL_DEFAULT, SCENE_STAGE_HEIGHT, SCENE_TRACK_DEFS, SCENE_WAKE_EVENT, SCENE_WORDS, SCENE_WORD_DIM, SLIDER_HOLD_SHARE, SLIDER_SCROLL_STEP_SCREENS, SceneAttribute, SceneProp, SceneRevealFrom, ScrollDriver, TextRevealBy, applySceneFirstFrame, attachSceneRoots, attachScenes, buildSceneRecipe, colourAt, dampToward, heldProgress, lineIndexes, movedSceneKey, normalizeTracks, playTextReveal, revealClipPath, revealStagger, sceneBudget, sceneColourKeyAt, sceneDisplayValue, sceneFilmDensity, sceneFirstFrame, sceneFrame, sceneFrameDeclarations, sceneHasKeyAt, sceneHeightForScreens, sceneKeyPosition, sceneLayersOf, sceneNodeAttributes, scenePlayed, sceneRevealFromOf, sceneRootOnPath, sceneRootsIn, sceneScreensOf, sceneScrollState, sceneTrackDef, sceneTrackSpan, sceneWordOpacity, sceneWordsLit, scenesIn, splitText, syncSceneAttributes, textPieces, valueAt, withSceneKey, withScenePlayback, withSceneRevealFrom, withoutSceneKey, withoutScenePlayback };
|
|
1078
1174
|
//# sourceMappingURL=vectoriox-iox-ui-scene-core.mjs.map
|