@vectoriox/iox-ui 4.5.2 → 4.7.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.
@@ -2341,33 +2341,182 @@ function dataSourcePlanToRequest(plan, endpoints) {
2341
2341
  */
2342
2342
  /** Perspective (px) applied to the transition container unless a preset overrides it. */
2343
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;
2344
+ /**
2345
+ * Default transition duration (ms). Deliberately long — a slow, settled transition is most of what
2346
+ * makes a page change read as "premium"; the same motion at ~400ms feels like a snap.
2347
+ */
2348
+ const DEFAULT_PAGE_TRANSITION_DURATION = 1500;
2349
+ /**
2350
+ * Default easing: a strong symmetric in-out. Paired with the long duration this is the "smoothness"
2351
+ * — slow to leave, quick through the middle, gentle to settle.
2352
+ */
2353
+ const DEFAULT_PAGE_TRANSITION_EASING = 'cubic-bezier(0.76, 0, 0.24, 1)';
2354
+ /** Default preset used when a page has no transition configured at all. */
2355
+ const DEFAULT_PAGE_TRANSITION = 'parallax-up';
2346
2356
  // ── Keyframe helpers ────────────────────────────────────────────────────────
2347
2357
  const FADE_OUT = [{ opacity: 1 }, { opacity: 0 }];
2348
2358
  const FADE_IN = [{ opacity: 0 }, { opacity: 1 }];
2349
2359
  const move = (from, to) => [{ transform: from }, { transform: to }];
2360
+ /** Gentle recede for the outgoing page under a mask/reveal — it stays put but drops back a little,
2361
+ * so the page underneath never looks like a frozen screenshot. */
2362
+ const RECEDE_SOFT = [
2363
+ { transform: 'scale(1)', opacity: 1 },
2364
+ { transform: 'scale(0.94)', opacity: 0.6 },
2365
+ ];
2366
+ /** Barely-there settle for the incoming page under a curtain — the panel hides most of it, so this
2367
+ * only takes the edge off the reveal. Ends at identity (required: IN frames must not persist). */
2368
+ const SETTLE_IN = [{ transform: 'scale(1.04)' }, { transform: 'scale(1)' }];
2350
2369
  // ── The preset catalog ──────────────────────────────────────────────────────
2351
2370
  // Keyed by a stable preset id persisted in pageSettings.routeAnimation.transition.
2352
2371
  const PAGE_TRANSITION_PRESETS = {
2353
- // ── Move (both pages slide; no fade — the classic Codrops "move") ──────────
2372
+ // ── Parallax (the default family) ──────────────────────────────────────────
2373
+ // The outgoing page travels only HALF the distance while shrinking and dimming, so it recedes
2374
+ // *behind* the incoming page rather than sliding away. That travel difference (50% vs 100%) is
2375
+ // what reads as depth — there is no 3D here at all. Pair with the long default duration.
2376
+ 'parallax-up': {
2377
+ category: 'parallax', label: 'Parallax up (recommended)',
2378
+ outKeyframes: [
2379
+ { transform: 'translateY(0) scale(1)', opacity: 1 },
2380
+ { transform: 'translateY(-50%) scale(0.8)', opacity: 0.4 },
2381
+ ],
2382
+ inKeyframes: move('translateY(100%)', 'translateY(0)'),
2383
+ ontop: 'in',
2384
+ },
2385
+ 'parallax-down': {
2386
+ category: 'parallax', label: 'Parallax down',
2387
+ outKeyframes: [
2388
+ { transform: 'translateY(0) scale(1)', opacity: 1 },
2389
+ { transform: 'translateY(50%) scale(0.8)', opacity: 0.4 },
2390
+ ],
2391
+ inKeyframes: move('translateY(-100%)', 'translateY(0)'),
2392
+ ontop: 'in',
2393
+ },
2394
+ 'parallax-left': {
2395
+ category: 'parallax', label: 'Parallax left',
2396
+ outKeyframes: [
2397
+ { transform: 'translateX(0) scale(1)', opacity: 1 },
2398
+ { transform: 'translateX(-50%) scale(0.8)', opacity: 0.4 },
2399
+ ],
2400
+ inKeyframes: move('translateX(100%)', 'translateX(0)'),
2401
+ ontop: 'in',
2402
+ },
2403
+ 'parallax-right': {
2404
+ category: 'parallax', label: 'Parallax right',
2405
+ outKeyframes: [
2406
+ { transform: 'translateX(0) scale(1)', opacity: 1 },
2407
+ { transform: 'translateX(50%) scale(0.8)', opacity: 0.4 },
2408
+ ],
2409
+ inKeyframes: move('translateX(-100%)', 'translateX(0)'),
2410
+ ontop: 'in',
2411
+ },
2412
+ // ── Reveal (clip-path) ─────────────────────────────────────────────────────
2413
+ // The incoming page is UNMASKED over the outgoing one, which recedes gently behind. No overlay
2414
+ // panel is involved — the mask is animated directly on the incoming layer, so this needs no
2415
+ // extra DOM. (WAAPI interpolates clip-path only between the SAME shape function with the same
2416
+ // number of points — keep each pair consistent.)
2417
+ 'circle-reveal': {
2418
+ category: 'reveal', label: 'Circle reveal (iris)',
2419
+ outKeyframes: RECEDE_SOFT,
2420
+ inKeyframes: [
2421
+ { clipPath: 'circle(0% at 50% 50%)' },
2422
+ { clipPath: 'circle(75% at 50% 50%)' },
2423
+ ],
2424
+ ontop: 'in',
2425
+ },
2426
+ 'wipe-right': {
2427
+ category: 'reveal', label: 'Wipe → right',
2428
+ outKeyframes: RECEDE_SOFT,
2429
+ inKeyframes: [{ clipPath: 'inset(0 100% 0 0)' }, { clipPath: 'inset(0 0 0 0)' }],
2430
+ ontop: 'in',
2431
+ },
2432
+ 'wipe-left': {
2433
+ category: 'reveal', label: 'Wipe ← left',
2434
+ outKeyframes: RECEDE_SOFT,
2435
+ inKeyframes: [{ clipPath: 'inset(0 0 0 100%)' }, { clipPath: 'inset(0 0 0 0)' }],
2436
+ ontop: 'in',
2437
+ },
2438
+ 'wipe-down': {
2439
+ category: 'reveal', label: 'Wipe ↓ down',
2440
+ outKeyframes: RECEDE_SOFT,
2441
+ inKeyframes: [{ clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0 0)' }],
2442
+ ontop: 'in',
2443
+ },
2444
+ 'wipe-up': {
2445
+ category: 'reveal', label: 'Wipe ↑ up',
2446
+ outKeyframes: RECEDE_SOFT,
2447
+ inKeyframes: [{ clipPath: 'inset(100% 0 0 0)' }, { clipPath: 'inset(0 0 0 0)' }],
2448
+ ontop: 'in',
2449
+ },
2450
+ 'diagonal-wipe': {
2451
+ category: 'reveal', label: 'Diagonal wipe',
2452
+ outKeyframes: RECEDE_SOFT,
2453
+ // 4-point polygons on both ends so WAAPI can interpolate them.
2454
+ inKeyframes: [
2455
+ { clipPath: 'polygon(0% 0%, 0% 0%, -40% 100%, -40% 100%)' },
2456
+ { clipPath: 'polygon(0% 0%, 140% 0%, 100% 100%, -40% 100%)' },
2457
+ ],
2458
+ ontop: 'in',
2459
+ },
2460
+ 'inset-reveal': {
2461
+ category: 'reveal', label: 'Box reveal (from centre)',
2462
+ outKeyframes: RECEDE_SOFT,
2463
+ inKeyframes: [{ clipPath: 'inset(50% 50% 50% 50%)' }, { clipPath: 'inset(0 0 0 0)' }],
2464
+ ontop: 'in',
2465
+ },
2466
+ // ── Curtain (overlay panel sweeps across, hiding the swap) ────────────────
2467
+ // A solid panel covers the screen, the outgoing page is dropped behind it, then the panel
2468
+ // sweeps away revealing the incoming page. Colour comes from routeAnimation.curtainColor, or
2469
+ // the incoming page's own background.
2470
+ 'curtain-up': {
2471
+ category: 'curtain', label: 'Curtain up',
2472
+ outKeyframes: RECEDE_SOFT, inKeyframes: SETTLE_IN, ontop: 'in',
2473
+ overlay: {
2474
+ coverKeyframes: move('translateY(100%)', 'translateY(0)'),
2475
+ uncoverKeyframes: move('translateY(0)', 'translateY(-100%)'),
2476
+ },
2477
+ },
2478
+ 'curtain-down': {
2479
+ category: 'curtain', label: 'Curtain down',
2480
+ outKeyframes: RECEDE_SOFT, inKeyframes: SETTLE_IN, ontop: 'in',
2481
+ overlay: {
2482
+ coverKeyframes: move('translateY(-100%)', 'translateY(0)'),
2483
+ uncoverKeyframes: move('translateY(0)', 'translateY(100%)'),
2484
+ },
2485
+ },
2486
+ 'curtain-left': {
2487
+ category: 'curtain', label: 'Curtain left',
2488
+ outKeyframes: RECEDE_SOFT, inKeyframes: SETTLE_IN, ontop: 'in',
2489
+ overlay: {
2490
+ coverKeyframes: move('translateX(100%)', 'translateX(0)'),
2491
+ uncoverKeyframes: move('translateX(0)', 'translateX(-100%)'),
2492
+ },
2493
+ },
2494
+ 'curtain-right': {
2495
+ category: 'curtain', label: 'Curtain right',
2496
+ outKeyframes: RECEDE_SOFT, inKeyframes: SETTLE_IN, ontop: 'in',
2497
+ overlay: {
2498
+ coverKeyframes: move('translateX(-100%)', 'translateX(0)'),
2499
+ uncoverKeyframes: move('translateX(0)', 'translateX(100%)'),
2500
+ },
2501
+ },
2502
+ // ── Move / Push (both pages travel the full distance, side by side — no overlap) ─────
2354
2503
  'move-left': {
2355
- category: 'move', label: 'Move left / from right',
2504
+ category: 'move', label: 'Push left',
2356
2505
  outKeyframes: move('translateX(0)', 'translateX(-100%)'),
2357
2506
  inKeyframes: move('translateX(100%)', 'translateX(0)'),
2358
2507
  },
2359
2508
  'move-right': {
2360
- category: 'move', label: 'Move right / from left',
2509
+ category: 'move', label: 'Push right',
2361
2510
  outKeyframes: move('translateX(0)', 'translateX(100%)'),
2362
2511
  inKeyframes: move('translateX(-100%)', 'translateX(0)'),
2363
2512
  },
2364
2513
  'move-up': {
2365
- category: 'move', label: 'Move up / from bottom',
2514
+ category: 'move', label: 'Push up',
2366
2515
  outKeyframes: move('translateY(0)', 'translateY(-100%)'),
2367
2516
  inKeyframes: move('translateY(100%)', 'translateY(0)'),
2368
2517
  },
2369
2518
  'move-down': {
2370
- category: 'move', label: 'Move down / from top',
2519
+ category: 'move', label: 'Push down',
2371
2520
  outKeyframes: move('translateY(0)', 'translateY(100%)'),
2372
2521
  inKeyframes: move('translateY(-100%)', 'translateY(0)'),
2373
2522
  },
@@ -2469,10 +2618,31 @@ const PAGE_TRANSITION_PRESETS = {
2469
2618
  outTransformOrigin: '100% 50%', inTransformOrigin: '0% 50%',
2470
2619
  },
2471
2620
  };
2621
+ const TRANSITION_FEELS = [
2622
+ { value: 'calm', label: 'Calm', duration: 1800, easing: 'cubic-bezier(0.33, 1, 0.68, 1)', description: 'Slow and unhurried — editorial, luxury' },
2623
+ { value: 'cinematic', label: 'Cinematic', duration: 1500, easing: DEFAULT_PAGE_TRANSITION_EASING, description: 'Long settle with a strong middle — the default' },
2624
+ { value: 'smooth', label: 'Smooth', duration: 1200, easing: 'cubic-bezier(0.16, 1, 0.3, 1)', description: 'Quick out of the gate, glides to a stop' },
2625
+ { value: 'snappy', label: 'Snappy', duration: 600, easing: 'cubic-bezier(0.16, 1, 0.3, 1)', description: 'Fast and responsive — app-like' },
2626
+ { value: 'crazy', label: 'Crazy', duration: 900, easing: 'cubic-bezier(0.68, -0.6, 0.32, 1.6)', description: 'Overshoots and springs back' },
2627
+ ];
2628
+ /** The feel matching this duration+easing exactly, else 'custom'. Drives the panel's selector. */
2629
+ function feelFor(duration, easing) {
2630
+ const d = toFiniteNumber(duration, DEFAULT_PAGE_TRANSITION_DURATION);
2631
+ const e = easing || DEFAULT_PAGE_TRANSITION_EASING;
2632
+ return TRANSITION_FEELS.find(f => f.duration === d && f.easing === e)?.value ?? 'custom';
2633
+ }
2634
+ /** The duration+easing patch for a feel id (undefined for 'custom' / unknown). */
2635
+ function applyFeel(feel) {
2636
+ const f = TRANSITION_FEELS.find(x => x.value === feel);
2637
+ return f ? { duration: f.duration, easing: f.easing } : undefined;
2638
+ }
2472
2639
  /** Built from PAGE_TRANSITION_PRESETS so the two lists can never drift apart. */
2473
2640
  const PAGE_TRANSITION_CATEGORIES = (() => {
2474
2641
  const order = [
2475
- { value: 'move', label: 'Move' },
2642
+ { value: 'parallax', label: 'Smooth (parallax)' },
2643
+ { value: 'reveal', label: 'Reveal (mask)' },
2644
+ { value: 'curtain', label: 'Curtain (overlay)' },
2645
+ { value: 'move', label: 'Push / slide' },
2476
2646
  { value: 'fade', label: 'Fade' },
2477
2647
  { value: 'scale', label: 'Scale' },
2478
2648
  { value: 'rotate', label: 'Rotate / 3D' },
@@ -2504,7 +2674,7 @@ function resolvePageTransition(name, opts = {}) {
2504
2674
  // the single point where these values become animation options, so every consumer is protected
2505
2675
  // and already-saved string data keeps working.
2506
2676
  const duration = toFiniteNumber(opts.duration, DEFAULT_PAGE_TRANSITION_DURATION);
2507
- const easing = opts.easing ?? 'ease';
2677
+ const easing = opts.easing || DEFAULT_PAGE_TRANSITION_EASING;
2508
2678
  const perspective = toFiniteNumber(preset.perspective ?? opts.perspective, DEFAULT_PAGE_TRANSITION_PERSPECTIVE);
2509
2679
  const clampRatio = (r) => Math.max(0, Math.min(1, r ?? 0));
2510
2680
  const outDelay = clampRatio(preset.outDelayRatio) * duration;
@@ -2524,6 +2694,14 @@ function resolvePageTransition(name, opts = {}) {
2524
2694
  perspective,
2525
2695
  outTransformOrigin: preset.outTransformOrigin,
2526
2696
  inTransformOrigin: preset.inTransformOrigin,
2697
+ overlay: preset.overlay && {
2698
+ coverFrames: preset.overlay.coverKeyframes,
2699
+ uncoverFrames: preset.overlay.uncoverKeyframes,
2700
+ // Cover and uncover split the total duration; 'both' holds the panel in place between
2701
+ // the two halves so the swap is never visible through a gap.
2702
+ coverOptions: { duration: duration / 2, easing, fill: 'both' },
2703
+ uncoverOptions: { duration: duration / 2, easing, fill: 'both' },
2704
+ },
2527
2705
  };
2528
2706
  }
2529
2707
  /**
@@ -2558,7 +2736,8 @@ function legacyEnterToTransition(enter) {
2558
2736
  case 'blurIn': return 'fade';
2559
2737
  case 'flip': return 'flip';
2560
2738
  case 'fade': return 'fade';
2561
- default: return 'fade';
2739
+ // Never configured (or an unrecognised value) → the default parallax transition.
2740
+ default: return DEFAULT_PAGE_TRANSITION;
2562
2741
  }
2563
2742
  }
2564
2743
  /**
@@ -2567,7 +2746,7 @@ function legacyEnterToTransition(enter) {
2567
2746
  */
2568
2747
  function effectiveTransitionId(anim) {
2569
2748
  if (!anim)
2570
- return 'fade';
2749
+ return DEFAULT_PAGE_TRANSITION;
2571
2750
  if (anim.transition)
2572
2751
  return anim.transition;
2573
2752
  return legacyEnterToTransition(anim.enter);
@@ -2581,5 +2760,5 @@ function effectiveTransitionId(anim) {
2581
2760
  * Generated bundle index. Do not edit.
2582
2761
  */
2583
2762
 
2584
- 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 };
2763
+ 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, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, 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, TRANSITION_FEELS, TextBlockComponent, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, WebSettingsService, applyFeel, buildDataSourceFilter, buildTransitionTimeline, compileDeclarations, composeVirtualTraits, dataSourcePlanToRequest, effectiveTransitionId, feelFor, legacyEnterToTransition, matchRouteParams, normalizeCollectionResult, planDataSource, renderDefaultDeclarations, resolvePageTransition, resolveSingleItemId, rewriteViewportUnits, styleKeyToKebab };
2585
2764
  //# sourceMappingURL=vectoriox-iox-ui.mjs.map