@vectoriox/iox-ui 4.4.4 → 4.5.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.
@@ -2326,6 +2326,239 @@ function dataSourcePlanToRequest(plan, endpoints) {
2326
2326
  }
2327
2327
  }
2328
2328
 
2329
+ /**
2330
+ * Cinematic page-transition presets — the SINGLE SOURCE OF TRUTH shared by the page builder
2331
+ * (preview) and the SSR client engine (production). Pure, transport-agnostic: this module only
2332
+ * describes WAAPI keyframe pairs + timing/ordering. It never touches the DOM.
2333
+ *
2334
+ * A page transition plays TWO synchronized WAAPI animations at once — an `out` animation on a
2335
+ * snapshot of the outgoing page and an `in` animation on the incoming page — inside a shared
2336
+ * `perspective` container, gated on BOTH finishing (the "cinematic" feel, à la Codrops
2337
+ * PageTransitions). The consumer supplies the DOM (snapshot layer + live page) and calls
2338
+ * `element.animate(frames, options)`; it does NOT redefine the frames.
2339
+ *
2340
+ * @see architecture/builder/page-transitions.md
2341
+ */
2342
+ /** Perspective (px) applied to the transition container unless a preset overrides it. */
2343
+ const DEFAULT_PAGE_TRANSITION_PERSPECTIVE = 1200;
2344
+ /** Default transition duration (ms) when the page does not specify one. */
2345
+ const DEFAULT_PAGE_TRANSITION_DURATION = 600;
2346
+ // ── Keyframe helpers ────────────────────────────────────────────────────────
2347
+ const FADE_OUT = [{ opacity: 1 }, { opacity: 0 }];
2348
+ const FADE_IN = [{ opacity: 0 }, { opacity: 1 }];
2349
+ const move = (from, to) => [{ transform: from }, { transform: to }];
2350
+ // ── The preset catalog ──────────────────────────────────────────────────────
2351
+ // Keyed by a stable preset id persisted in pageSettings.routeAnimation.transition.
2352
+ const PAGE_TRANSITION_PRESETS = {
2353
+ // ── Move (both pages slide; no fade — the classic Codrops "move") ──────────
2354
+ 'move-left': {
2355
+ category: 'move', label: 'Move left / from right',
2356
+ outKeyframes: move('translateX(0)', 'translateX(-100%)'),
2357
+ inKeyframes: move('translateX(100%)', 'translateX(0)'),
2358
+ },
2359
+ 'move-right': {
2360
+ category: 'move', label: 'Move right / from left',
2361
+ outKeyframes: move('translateX(0)', 'translateX(100%)'),
2362
+ inKeyframes: move('translateX(-100%)', 'translateX(0)'),
2363
+ },
2364
+ 'move-up': {
2365
+ category: 'move', label: 'Move up / from bottom',
2366
+ outKeyframes: move('translateY(0)', 'translateY(-100%)'),
2367
+ inKeyframes: move('translateY(100%)', 'translateY(0)'),
2368
+ },
2369
+ 'move-down': {
2370
+ category: 'move', label: 'Move down / from top',
2371
+ outKeyframes: move('translateY(0)', 'translateY(100%)'),
2372
+ inKeyframes: move('translateY(-100%)', 'translateY(0)'),
2373
+ },
2374
+ // ── Fade (incoming sits on top; outgoing dissolves or holds) ───────────────
2375
+ 'fade': {
2376
+ category: 'fade', label: 'Fade',
2377
+ outKeyframes: FADE_OUT, inKeyframes: FADE_IN, ontop: 'in',
2378
+ },
2379
+ 'fade-from-right': {
2380
+ category: 'fade', label: 'Fade / from right',
2381
+ outKeyframes: FADE_OUT,
2382
+ inKeyframes: [{ transform: 'translateX(100%)', opacity: 0 }, { transform: 'translateX(0)', opacity: 1 }],
2383
+ ontop: 'in',
2384
+ },
2385
+ 'fade-from-left': {
2386
+ category: 'fade', label: 'Fade / from left',
2387
+ outKeyframes: FADE_OUT,
2388
+ inKeyframes: [{ transform: 'translateX(-100%)', opacity: 0 }, { transform: 'translateX(0)', opacity: 1 }],
2389
+ ontop: 'in',
2390
+ },
2391
+ 'fade-from-bottom': {
2392
+ category: 'fade', label: 'Fade / from bottom',
2393
+ outKeyframes: FADE_OUT,
2394
+ inKeyframes: [{ transform: 'translateY(100%)', opacity: 0 }, { transform: 'translateY(0)', opacity: 1 }],
2395
+ ontop: 'in',
2396
+ },
2397
+ // ── Scale ──────────────────────────────────────────────────────────────────
2398
+ 'scale-down-up': {
2399
+ category: 'scale', label: 'Scale down / scale up',
2400
+ outKeyframes: [{ transform: 'scale(1)', opacity: 1 }, { transform: 'scale(0.8)', opacity: 0 }],
2401
+ inKeyframes: [{ transform: 'scale(1.2)', opacity: 0 }, { transform: 'scale(1)', opacity: 1 }],
2402
+ ontop: 'in',
2403
+ },
2404
+ 'scale-down': {
2405
+ category: 'scale', label: 'Scale down / from right',
2406
+ outKeyframes: [{ transform: 'scale(1)', opacity: 1 }, { transform: 'scale(0.8)', opacity: 0 }],
2407
+ inKeyframes: move('translateX(100%)', 'translateX(0)'),
2408
+ ontop: 'in',
2409
+ },
2410
+ 'scale-up': {
2411
+ category: 'scale', label: 'Move to left / scale up',
2412
+ outKeyframes: move('translateX(0)', 'translateX(-100%)'),
2413
+ inKeyframes: [{ transform: 'scale(0.8)', opacity: 0 }, { transform: 'scale(1)', opacity: 1 }],
2414
+ ontop: 'out',
2415
+ },
2416
+ // ── Rotate / 3D (the cinematic "wow" family) ───────────────────────────────
2417
+ 'flip': {
2418
+ category: 'rotate', label: 'Flip',
2419
+ outKeyframes: [{ transform: 'rotateY(0deg)', opacity: 1 }, { transform: 'rotateY(-180deg)', opacity: 0 }],
2420
+ inKeyframes: [{ transform: 'rotateY(180deg)', opacity: 0 }, { transform: 'rotateY(0deg)', opacity: 1 }],
2421
+ outTransformOrigin: '50% 50%', inTransformOrigin: '50% 50%',
2422
+ },
2423
+ 'cube-left': {
2424
+ category: 'rotate', label: 'Cube / to left',
2425
+ outKeyframes: [{ transform: 'translateZ(0) rotateY(0deg)', opacity: 1 }, { transform: 'translateZ(-800px) rotateY(-90deg)', opacity: 0.3 }],
2426
+ inKeyframes: [{ transform: 'translateZ(-800px) rotateY(90deg)', opacity: 0.3 }, { transform: 'translateZ(0) rotateY(0deg)', opacity: 1 }],
2427
+ outTransformOrigin: '50% 50%', inTransformOrigin: '50% 50%',
2428
+ },
2429
+ 'cube-right': {
2430
+ category: 'rotate', label: 'Cube / to right',
2431
+ outKeyframes: [{ transform: 'translateZ(0) rotateY(0deg)', opacity: 1 }, { transform: 'translateZ(-800px) rotateY(90deg)', opacity: 0.3 }],
2432
+ inKeyframes: [{ transform: 'translateZ(-800px) rotateY(-90deg)', opacity: 0.3 }, { transform: 'translateZ(0) rotateY(0deg)', opacity: 1 }],
2433
+ outTransformOrigin: '50% 50%', inTransformOrigin: '50% 50%',
2434
+ },
2435
+ 'carousel-left': {
2436
+ category: 'rotate', label: 'Carousel / to left',
2437
+ outKeyframes: [{ transform: 'translateX(0) scale(1)', opacity: 1 }, { transform: 'translateX(-150%) scale(0.4)', opacity: 0.3 }],
2438
+ inKeyframes: [{ transform: 'translateX(200%) scale(0.4)', opacity: 0.3 }, { transform: 'translateX(0) scale(1)', opacity: 1 }],
2439
+ ontop: 'in',
2440
+ },
2441
+ 'carousel-right': {
2442
+ category: 'rotate', label: 'Carousel / to right',
2443
+ outKeyframes: [{ transform: 'translateX(0) scale(1)', opacity: 1 }, { transform: 'translateX(150%) scale(0.4)', opacity: 0.3 }],
2444
+ inKeyframes: [{ transform: 'translateX(-200%) scale(0.4)', opacity: 0.3 }, { transform: 'translateX(0) scale(1)', opacity: 1 }],
2445
+ ontop: 'in',
2446
+ },
2447
+ 'fall': {
2448
+ category: 'rotate', label: 'Fall',
2449
+ outKeyframes: [{ transform: 'translateZ(0) rotateX(0deg)', opacity: 1 }, { transform: 'translateZ(-500px) rotateX(90deg)', opacity: 0 }],
2450
+ inKeyframes: FADE_IN,
2451
+ outTransformOrigin: '50% 50%', ontop: 'out', inDelayRatio: 0.3,
2452
+ },
2453
+ 'newspaper': {
2454
+ category: 'rotate', label: 'Newspaper',
2455
+ outKeyframes: [{ transform: 'scale(1) rotate(0deg)', opacity: 1 }, { transform: 'scale(0) rotate(720deg)', opacity: 0 }],
2456
+ inKeyframes: [{ transform: 'scale(0) rotate(-720deg)', opacity: 0 }, { transform: 'scale(1) rotate(0deg)', opacity: 1 }],
2457
+ ontop: 'in', inDelayRatio: 0.5,
2458
+ },
2459
+ 'fold-left': {
2460
+ category: 'rotate', label: 'Fold / unfold',
2461
+ outKeyframes: [{ transform: 'translateX(0) rotateY(0deg)', opacity: 1 }, { transform: 'translateX(-100%) rotateY(-90deg)', opacity: 0.6 }],
2462
+ inKeyframes: [{ transform: 'translateX(100%) rotateY(90deg)', opacity: 0.6 }, { transform: 'translateX(0) rotateY(0deg)', opacity: 1 }],
2463
+ outTransformOrigin: '0% 50%', inTransformOrigin: '100% 50%', ontop: 'in',
2464
+ },
2465
+ 'room-left': {
2466
+ category: 'rotate', label: 'Room / to left',
2467
+ outKeyframes: [{ transform: 'translateX(0) rotateY(0deg)', opacity: 1 }, { transform: 'translateX(-100%) rotateY(90deg)', opacity: 0.4 }],
2468
+ inKeyframes: [{ transform: 'translateX(100%) rotateY(-90deg)', opacity: 0.4 }, { transform: 'translateX(0) rotateY(0deg)', opacity: 1 }],
2469
+ outTransformOrigin: '100% 50%', inTransformOrigin: '0% 50%',
2470
+ },
2471
+ };
2472
+ /** Built from PAGE_TRANSITION_PRESETS so the two lists can never drift apart. */
2473
+ const PAGE_TRANSITION_CATEGORIES = (() => {
2474
+ const order = [
2475
+ { value: 'move', label: 'Move' },
2476
+ { value: 'fade', label: 'Fade' },
2477
+ { value: 'scale', label: 'Scale' },
2478
+ { value: 'rotate', label: 'Rotate / 3D' },
2479
+ ];
2480
+ return order.map(cat => ({
2481
+ ...cat,
2482
+ presets: Object.entries(PAGE_TRANSITION_PRESETS)
2483
+ .filter(([, p]) => p.category === cat.value)
2484
+ .map(([value, p]) => ({ value, label: p.label })),
2485
+ }));
2486
+ })();
2487
+ /**
2488
+ * Resolve a persisted preset id + timing into the two WAAPI animations the consumer runs.
2489
+ * Returns `null` for unknown / 'none' presets (caller should skip the transition).
2490
+ */
2491
+ function resolvePageTransition(name, opts = {}) {
2492
+ if (!name || name === 'none')
2493
+ return null;
2494
+ const preset = PAGE_TRANSITION_PRESETS[name];
2495
+ if (!preset)
2496
+ return null;
2497
+ const duration = opts.duration ?? DEFAULT_PAGE_TRANSITION_DURATION;
2498
+ const easing = opts.easing ?? 'ease';
2499
+ const perspective = preset.perspective ?? opts.perspective ?? DEFAULT_PAGE_TRANSITION_PERSPECTIVE;
2500
+ const clampRatio = (r) => Math.max(0, Math.min(1, r ?? 0));
2501
+ const outDelay = clampRatio(preset.outDelayRatio) * duration;
2502
+ const inDelay = clampRatio(preset.inDelayRatio) * duration;
2503
+ return {
2504
+ outFrames: preset.outKeyframes,
2505
+ inFrames: preset.inKeyframes,
2506
+ // fill:'both' holds keyframe[0] during the delay and keyframe[last] after — no flash.
2507
+ outOptions: { duration, easing, delay: outDelay, fill: 'both' },
2508
+ inOptions: { duration, easing, delay: inDelay, fill: 'both' },
2509
+ ontop: preset.ontop ?? 'in',
2510
+ perspective,
2511
+ outTransformOrigin: preset.outTransformOrigin,
2512
+ inTransformOrigin: preset.inTransformOrigin,
2513
+ };
2514
+ }
2515
+ /**
2516
+ * The ordered phases a navigation runs, given what is present. The order is INVARIANT:
2517
+ * every element `leave` animation finishes before the page `transition` starts, and the page
2518
+ * `transition` finishes before any element `enter` animation begins. Phases with nothing to
2519
+ * play are omitted. The engine iterates this list and `await`s each phase's completion.
2520
+ */
2521
+ function buildTransitionTimeline(input) {
2522
+ const phases = [];
2523
+ if (input.hasLeave)
2524
+ phases.push('leave');
2525
+ if (input.hasTransition)
2526
+ phases.push('transition');
2527
+ if (input.hasEnter)
2528
+ phases.push('enter');
2529
+ return phases;
2530
+ }
2531
+ // ── Back-compat migration from the legacy single-page route-animation presets ─
2532
+ /**
2533
+ * Map a legacy `enter` preset (none/fade/slideUp/slideDown/zoomIn/zoomOut/blurIn/flip) to the
2534
+ * nearest cinematic paired `transition` id, so pages saved before this system keep animating.
2535
+ * Returns 'fade' for anything unmapped, and 'none' for 'none'.
2536
+ */
2537
+ function legacyEnterToTransition(enter) {
2538
+ switch (enter) {
2539
+ case 'none': return 'none';
2540
+ case 'slideUp': return 'fade-from-bottom';
2541
+ case 'slideDown': return 'fade';
2542
+ case 'zoomIn': return 'scale-down-up';
2543
+ case 'zoomOut': return 'scale-down-up';
2544
+ case 'blurIn': return 'fade';
2545
+ case 'flip': return 'flip';
2546
+ case 'fade': return 'fade';
2547
+ default: return 'fade';
2548
+ }
2549
+ }
2550
+ /**
2551
+ * Resolve the effective transition id for a page's route-animation settings: the explicit
2552
+ * `transition` when present, otherwise migrated from the legacy `enter` preset.
2553
+ */
2554
+ function effectiveTransitionId(anim) {
2555
+ if (!anim)
2556
+ return 'fade';
2557
+ if (anim.transition)
2558
+ return anim.transition;
2559
+ return legacyEnterToTransition(anim.enter);
2560
+ }
2561
+
2329
2562
  /*
2330
2563
  * Public API Surface of iox-ui
2331
2564
  */
@@ -2334,5 +2567,5 @@ function dataSourcePlanToRequest(plan, endpoints) {
2334
2567
  * Generated bundle index. Do not edit.
2335
2568
  */
2336
2569
 
2337
- export { AOSService, AnalyticsService, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CMSClientInfraModule, CardComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ConsentService, ContainerComponent, ContentService, DEFAULT_WIDTH_BY_TYPE, ENVIRONMENT, IOX_BUILDER_EVENTS, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxPageComponent, IoxPageModule, IoxUiModule, LinkedContainerComponent, PageScrollProvider, PrivacyModalComponent, PrivacyModalService, PrivacyModelEvent, SectionComponent, TextBlockComponent, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, WebSettingsService, buildDataSourceFilter, compileDeclarations, composeVirtualTraits, dataSourcePlanToRequest, matchRouteParams, normalizeCollectionResult, planDataSource, renderDefaultDeclarations, resolveSingleItemId, rewriteViewportUnits, styleKeyToKebab };
2570
+ export { AOSService, AnalyticsService, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CMSClientInfraModule, CardComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ConsentService, ContainerComponent, ContentService, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_WIDTH_BY_TYPE, ENVIRONMENT, IOX_BUILDER_EVENTS, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxPageComponent, IoxPageModule, IoxUiModule, LinkedContainerComponent, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PageScrollProvider, PrivacyModalComponent, PrivacyModalService, PrivacyModelEvent, SectionComponent, TextBlockComponent, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, WebSettingsService, buildDataSourceFilter, buildTransitionTimeline, compileDeclarations, composeVirtualTraits, dataSourcePlanToRequest, effectiveTransitionId, legacyEnterToTransition, matchRouteParams, normalizeCollectionResult, planDataSource, renderDefaultDeclarations, resolvePageTransition, resolveSingleItemId, rewriteViewportUnits, styleKeyToKebab };
2338
2571
  //# sourceMappingURL=vectoriox-iox-ui.mjs.map