@deriv-web-design/ui 0.1.17 → 0.1.19

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.
@@ -891,7 +891,7 @@
891
891
  "name": "DotLottieInView",
892
892
  "source": "components/DotLottieInView/DotLottieInView.tsx",
893
893
  "props": {},
894
- "description": "Plays a .lottie once when it scrolls into view. Stops and resets when it leaves view. When it enters view again, it plays once more. Set `enabled={false}` to force stop (e.g. hidden slides) without relying on geometry alone."
894
+ "description": "Plays a .lottie once when it scrolls into view. Stops and resets when it leaves view. When it enters view again, it plays once more. Set `enabled={false}` to force stop (e.g. hidden slides) without relying on geometry alone. The player itself is fetched lazily, the first time this instance is in view — so a page with no animation never downloads it, and a below-the-fold band does not pay for it during initial load. `enabled` gates playback only: a consumer that crossfades between stacked instances needs them all loaded, not just the one currently showing."
895
895
  },
896
896
  "DownloadBadge": {
897
897
  "name": "DownloadBadge",
@@ -1836,6 +1836,11 @@
1836
1836
  "required": true,
1837
1837
  "description": "URL to the .lottie file or any image asset (png, jpg, svg, webp…)"
1838
1838
  },
1839
+ "posterUrl": {
1840
+ "type": "string",
1841
+ "required": false,
1842
+ "description": "Still frame painted while an animation's player and engine are loading. Only used when the item resolves to `\"lottie\"`. The WebAssembly engine is ~630 KB compressed and one engine serves every animation on the page, so on a cold visit over a slow connection the first animated band can sit empty for seconds. A poster turns that into a progressive load instead of a hole in the layout. Ignored once the animation is playing."
1843
+ },
1839
1844
  "title": {
1840
1845
  "type": "string",
1841
1846
  "required": true,
@@ -1843,6 +1848,16 @@
1843
1848
  }
1844
1849
  }
1845
1850
  },
1851
+ "mediaRootMargin": {
1852
+ "type": "string",
1853
+ "required": false,
1854
+ "description": "`rootMargin` for the observer that decides when an animation's player may load and play. Forwarded to `DotLottieInView`. The default starts the work only once a band is actually on screen, which is the safe choice: the engine is large, and fetching it earlier competes with whatever the visitor is currently looking at — including the LCP image. Widen it (e.g. `\"600px 0px\"`) on pages where the animated band sits well below the fold and you would rather it were ready on arrival."
1855
+ },
1856
+ "mediaThreshold": {
1857
+ "type": "number | number[]",
1858
+ "required": false,
1859
+ "description": "`threshold` for that same observer. Forwarded to `DotLottieInView`."
1860
+ },
1846
1861
  "variant": {
1847
1862
  "type": "\"media-left\" | \"media-right\"",
1848
1863
  "required": false,
package/dist/index.d.mts CHANGED
@@ -750,6 +750,16 @@ interface FeatureScrollItem {
750
750
  mediaType?: "lottie" | "image";
751
751
  /** URL to the .lottie file or any image asset (png, jpg, svg, webp…) */
752
752
  mediaUrl: string;
753
+ /**
754
+ * Still frame painted while an animation's player and engine are loading.
755
+ *
756
+ * Only used when the item resolves to `"lottie"`. The WebAssembly engine is
757
+ * ~630 KB compressed and one engine serves every animation on the page, so
758
+ * on a cold visit over a slow connection the first animated band can sit
759
+ * empty for seconds. A poster turns that into a progressive load instead of
760
+ * a hole in the layout. Ignored once the animation is playing.
761
+ */
762
+ posterUrl?: string;
753
763
  }
754
764
  interface FeatureScrollProps extends HTMLAttributes<HTMLElement> {
755
765
  /** Section heading rendered as an H2 */
@@ -764,10 +774,27 @@ interface FeatureScrollProps extends HTMLAttributes<HTMLElement> {
764
774
  * @default "media-right"
765
775
  */
766
776
  variant?: "media-left" | "media-right";
777
+ /**
778
+ * `rootMargin` for the observer that decides when an animation's player may
779
+ * load and play. Forwarded to `DotLottieInView`.
780
+ *
781
+ * The default starts the work only once a band is actually on screen, which
782
+ * is the safe choice: the engine is large, and fetching it earlier competes
783
+ * with whatever the visitor is currently looking at — including the LCP
784
+ * image. Widen it (e.g. `"600px 0px"`) on pages where the animated band sits
785
+ * well below the fold and you would rather it were ready on arrival.
786
+ * @default "0px"
787
+ */
788
+ mediaRootMargin?: string;
789
+ /**
790
+ * `threshold` for that same observer. Forwarded to `DotLottieInView`.
791
+ * @default 0.2
792
+ */
793
+ mediaThreshold?: number | number[];
767
794
  }
768
795
 
769
796
  declare const FEATURE_SCROLL_DATA: FeatureScrollItem[];
770
- declare const FeatureScroll: ({ header, items, variant, className, ...props }: FeatureScrollProps) => react_jsx_runtime.JSX.Element;
797
+ declare const FeatureScroll: ({ header, items, variant, className, mediaRootMargin, mediaThreshold, ...props }: FeatureScrollProps) => react_jsx_runtime.JSX.Element;
771
798
 
772
799
  type FeatureSplitImagePosition = "left" | "right";
773
800
  interface FeatureSplitProps {
@@ -799,13 +826,31 @@ type DotLottieInViewProps = Omit<ComponentProps<typeof DotLottieReact>, "autopla
799
826
  enabled?: boolean;
800
827
  rootMargin?: string;
801
828
  threshold?: number | number[];
829
+ /**
830
+ * Painted in place of the player until the animation is ready, and as the
831
+ * Suspense fallback while the player chunk loads.
832
+ *
833
+ * Worth supplying on anything a visitor meets early on a slow connection:
834
+ * the WebAssembly engine is ~630 KB compressed and one engine serves every
835
+ * animation on the page, so the first animated band on a cold visit can sit
836
+ * empty for seconds. A still frame makes that a progressive load rather than
837
+ * a hole in the layout. A `ReactNode` rather than a URL so the consumer can
838
+ * pass a properly sized, responsive `<img>` rather than a bare `src`.
839
+ */
840
+ poster?: ReactNode;
802
841
  };
803
842
  /**
804
843
  * Plays a .lottie once when it scrolls into view. Stops and resets when it leaves view.
805
844
  * When it enters view again, it plays once more. Set `enabled={false}` to force stop
806
845
  * (e.g. hidden slides) without relying on geometry alone.
846
+ *
847
+ * The player itself is fetched lazily, the first time this instance is in view —
848
+ * so a page with no animation never downloads it, and a below-the-fold band does
849
+ * not pay for it during initial load. `enabled` gates playback only: a consumer
850
+ * that crossfades between stacked instances needs them all loaded, not just the
851
+ * one currently showing.
807
852
  */
808
- declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
853
+ declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, poster, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
809
854
 
810
855
  /** Visual colour theme for each card — matches the Figma palette. */
811
856
  type StickyCardTheme = "light" | "coral" | "dark";
package/dist/index.d.ts CHANGED
@@ -750,6 +750,16 @@ interface FeatureScrollItem {
750
750
  mediaType?: "lottie" | "image";
751
751
  /** URL to the .lottie file or any image asset (png, jpg, svg, webp…) */
752
752
  mediaUrl: string;
753
+ /**
754
+ * Still frame painted while an animation's player and engine are loading.
755
+ *
756
+ * Only used when the item resolves to `"lottie"`. The WebAssembly engine is
757
+ * ~630 KB compressed and one engine serves every animation on the page, so
758
+ * on a cold visit over a slow connection the first animated band can sit
759
+ * empty for seconds. A poster turns that into a progressive load instead of
760
+ * a hole in the layout. Ignored once the animation is playing.
761
+ */
762
+ posterUrl?: string;
753
763
  }
754
764
  interface FeatureScrollProps extends HTMLAttributes<HTMLElement> {
755
765
  /** Section heading rendered as an H2 */
@@ -764,10 +774,27 @@ interface FeatureScrollProps extends HTMLAttributes<HTMLElement> {
764
774
  * @default "media-right"
765
775
  */
766
776
  variant?: "media-left" | "media-right";
777
+ /**
778
+ * `rootMargin` for the observer that decides when an animation's player may
779
+ * load and play. Forwarded to `DotLottieInView`.
780
+ *
781
+ * The default starts the work only once a band is actually on screen, which
782
+ * is the safe choice: the engine is large, and fetching it earlier competes
783
+ * with whatever the visitor is currently looking at — including the LCP
784
+ * image. Widen it (e.g. `"600px 0px"`) on pages where the animated band sits
785
+ * well below the fold and you would rather it were ready on arrival.
786
+ * @default "0px"
787
+ */
788
+ mediaRootMargin?: string;
789
+ /**
790
+ * `threshold` for that same observer. Forwarded to `DotLottieInView`.
791
+ * @default 0.2
792
+ */
793
+ mediaThreshold?: number | number[];
767
794
  }
768
795
 
769
796
  declare const FEATURE_SCROLL_DATA: FeatureScrollItem[];
770
- declare const FeatureScroll: ({ header, items, variant, className, ...props }: FeatureScrollProps) => react_jsx_runtime.JSX.Element;
797
+ declare const FeatureScroll: ({ header, items, variant, className, mediaRootMargin, mediaThreshold, ...props }: FeatureScrollProps) => react_jsx_runtime.JSX.Element;
771
798
 
772
799
  type FeatureSplitImagePosition = "left" | "right";
773
800
  interface FeatureSplitProps {
@@ -799,13 +826,31 @@ type DotLottieInViewProps = Omit<ComponentProps<typeof DotLottieReact>, "autopla
799
826
  enabled?: boolean;
800
827
  rootMargin?: string;
801
828
  threshold?: number | number[];
829
+ /**
830
+ * Painted in place of the player until the animation is ready, and as the
831
+ * Suspense fallback while the player chunk loads.
832
+ *
833
+ * Worth supplying on anything a visitor meets early on a slow connection:
834
+ * the WebAssembly engine is ~630 KB compressed and one engine serves every
835
+ * animation on the page, so the first animated band on a cold visit can sit
836
+ * empty for seconds. A still frame makes that a progressive load rather than
837
+ * a hole in the layout. A `ReactNode` rather than a URL so the consumer can
838
+ * pass a properly sized, responsive `<img>` rather than a bare `src`.
839
+ */
840
+ poster?: ReactNode;
802
841
  };
803
842
  /**
804
843
  * Plays a .lottie once when it scrolls into view. Stops and resets when it leaves view.
805
844
  * When it enters view again, it plays once more. Set `enabled={false}` to force stop
806
845
  * (e.g. hidden slides) without relying on geometry alone.
846
+ *
847
+ * The player itself is fetched lazily, the first time this instance is in view —
848
+ * so a page with no animation never downloads it, and a below-the-fold band does
849
+ * not pay for it during initial load. `enabled` gates playback only: a consumer
850
+ * that crossfades between stacked instances needs them all loaded, not just the
851
+ * one currently showing.
807
852
  */
808
- declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
853
+ declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, poster, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
809
854
 
810
855
  /** Visual colour theme for each card — matches the Figma palette. */
811
856
  type StickyCardTheme = "light" | "coral" | "dark";
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // index.ts
@@ -2635,18 +2645,22 @@ var import_react13 = require("react");
2635
2645
 
2636
2646
  // components/DotLottieInView/DotLottieInView.tsx
2637
2647
  var import_react12 = require("react");
2638
- var import_dotlottie_react = require("@lottiefiles/dotlottie-react");
2639
2648
  var import_jsx_runtime27 = require("react/jsx-runtime");
2649
+ var DotLottiePlayer = (0, import_react12.lazy)(
2650
+ () => import("@lottiefiles/dotlottie-react").then((m) => ({ default: m.DotLottieReact }))
2651
+ );
2640
2652
  function DotLottieInView({
2641
2653
  enabled = true,
2642
2654
  rootMargin = "0px",
2643
2655
  threshold = 0.2,
2644
2656
  className,
2645
2657
  style,
2658
+ poster,
2646
2659
  ...dotProps
2647
2660
  }) {
2648
2661
  const wrapRef = (0, import_react12.useRef)(null);
2649
2662
  const [inView, setInView] = (0, import_react12.useState)(false);
2663
+ const [mounted, setMounted] = (0, import_react12.useState)(false);
2650
2664
  const [dotLottie, setDotLottie] = (0, import_react12.useState)(null);
2651
2665
  (0, import_react12.useEffect)(() => {
2652
2666
  const el = wrapRef.current;
@@ -2660,14 +2674,40 @@ function DotLottieInView({
2660
2674
  observer.observe(el);
2661
2675
  return () => observer.disconnect();
2662
2676
  }, [rootMargin, threshold]);
2677
+ (0, import_react12.useEffect)(() => {
2678
+ if (mounted) return;
2679
+ if (!inView) return;
2680
+ if (typeof window === "undefined") return;
2681
+ let cancelled = false;
2682
+ const commit = () => {
2683
+ if (!cancelled) setMounted(true);
2684
+ };
2685
+ const idleId = typeof window.requestIdleCallback === "function" ? window.requestIdleCallback(commit, { timeout: 400 }) : null;
2686
+ const timerId = idleId === null ? window.setTimeout(commit, 0) : null;
2687
+ return () => {
2688
+ cancelled = true;
2689
+ if (idleId !== null && typeof window.cancelIdleCallback === "function") {
2690
+ window.cancelIdleCallback(idleId);
2691
+ }
2692
+ if (timerId !== null) window.clearTimeout(timerId);
2693
+ };
2694
+ }, [inView, mounted]);
2663
2695
  (0, import_react12.useEffect)(() => {
2664
2696
  if (!dotLottie) return;
2665
2697
  if (!enabled || !inView) {
2666
2698
  dotLottie.stop();
2667
2699
  return;
2668
2700
  }
2669
- dotLottie.stop();
2670
- dotLottie.play();
2701
+ const start = () => {
2702
+ dotLottie.stop();
2703
+ dotLottie.play();
2704
+ };
2705
+ if (dotLottie.isLoaded) {
2706
+ start();
2707
+ return;
2708
+ }
2709
+ dotLottie.addEventListener("load", start);
2710
+ return () => dotLottie.removeEventListener("load", start);
2671
2711
  }, [dotLottie, enabled, inView]);
2672
2712
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2673
2713
  "div",
@@ -2684,15 +2724,15 @@ function DotLottieInView({
2684
2724
  backgroundColor: "transparent",
2685
2725
  ...style
2686
2726
  },
2687
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2688
- import_dotlottie_react.DotLottieReact,
2727
+ children: mounted ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react12.Suspense, { fallback: poster ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2728
+ DotLottiePlayer,
2689
2729
  {
2690
2730
  ...dotProps,
2691
2731
  loop: false,
2692
2732
  autoplay: false,
2693
2733
  dotLottieRefCallback: setDotLottie
2694
2734
  }
2695
- )
2735
+ ) }) : poster ?? null
2696
2736
  }
2697
2737
  );
2698
2738
  }
@@ -2764,26 +2804,45 @@ var ChevronRight2 = () => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2764
2804
  )
2765
2805
  }
2766
2806
  );
2807
+ function mediaSrc(item) {
2808
+ return typeof item.mediaUrl === "string" && item.mediaUrl !== "" ? item.mediaUrl : null;
2809
+ }
2767
2810
  function resolveMediaType(item) {
2768
2811
  if (item.mediaType) return item.mediaType;
2769
- return item.mediaUrl.split("?")[0].endsWith(".lottie") ? "lottie" : "image";
2812
+ const src = mediaSrc(item);
2813
+ return src?.split("?")[0].endsWith(".lottie") ? "lottie" : "image";
2770
2814
  }
2771
- var MediaRenderer = ({ item, active }) => {
2815
+ var MediaRenderer = ({ item, active, rootMargin, threshold }) => {
2816
+ const src = mediaSrc(item);
2817
+ if (!src) return null;
2772
2818
  if (resolveMediaType(item) === "lottie") {
2773
2819
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2774
2820
  DotLottieInView,
2775
2821
  {
2776
- src: item.mediaUrl,
2777
- enabled: active
2822
+ src,
2823
+ enabled: active,
2824
+ rootMargin,
2825
+ threshold,
2826
+ poster: item.posterUrl ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2827
+ "img",
2828
+ {
2829
+ src: item.posterUrl,
2830
+ alt: "",
2831
+ "aria-hidden": "true",
2832
+ loading: "lazy",
2833
+ decoding: "async"
2834
+ }
2835
+ ) : void 0
2778
2836
  }
2779
2837
  );
2780
2838
  }
2781
2839
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2782
2840
  "img",
2783
2841
  {
2784
- src: item.mediaUrl,
2842
+ src,
2785
2843
  alt: item.title,
2786
- loading: "lazy"
2844
+ loading: "lazy",
2845
+ decoding: "async"
2787
2846
  }
2788
2847
  );
2789
2848
  };
@@ -2792,10 +2851,16 @@ var FeatureScroll = ({
2792
2851
  items,
2793
2852
  variant = "media-right",
2794
2853
  className,
2854
+ // Destructured, not spread: `...props` lands on the <section>, and an
2855
+ // unknown attribute there is a React DOM warning at best.
2856
+ mediaRootMargin,
2857
+ mediaThreshold,
2795
2858
  ...props
2796
2859
  }) => {
2797
2860
  const [activeIndex, setActiveIndex] = (0, import_react13.useState)(0);
2861
+ const [mobileActiveIndex, setMobileActiveIndex] = (0, import_react13.useState)(0);
2798
2862
  const titleRefs = (0, import_react13.useRef)([]);
2863
+ const mobileItemRefs = (0, import_react13.useRef)([]);
2799
2864
  const mediaColRef = (0, import_react13.useRef)(null);
2800
2865
  (0, import_react13.useLayoutEffect)(() => {
2801
2866
  if (items.length === 0) return;
@@ -2843,6 +2908,40 @@ var FeatureScroll = ({
2843
2908
  if (rafId !== 0) window.cancelAnimationFrame(rafId);
2844
2909
  };
2845
2910
  }, [items.length]);
2911
+ (0, import_react13.useEffect)(() => {
2912
+ if (items.length === 0) return;
2913
+ if (typeof window === "undefined") return;
2914
+ const ratios = /* @__PURE__ */ new Map();
2915
+ const observer = new IntersectionObserver(
2916
+ (entries) => {
2917
+ if (window.matchMedia("(min-width: 992px)").matches) return;
2918
+ for (const entry of entries) {
2919
+ const idx = Number(entry.target.getAttribute("data-fs-mobile-idx"));
2920
+ if (!Number.isFinite(idx)) continue;
2921
+ ratios.set(idx, entry.isIntersecting ? entry.intersectionRatio : 0);
2922
+ }
2923
+ let best = 0;
2924
+ let bestRatio = -1;
2925
+ for (const [idx, ratio] of ratios) {
2926
+ if (ratio > bestRatio) {
2927
+ bestRatio = ratio;
2928
+ best = idx;
2929
+ }
2930
+ }
2931
+ if (bestRatio <= 0) return;
2932
+ setMobileActiveIndex((prev) => prev === best ? prev : best);
2933
+ },
2934
+ {
2935
+ root: null,
2936
+ rootMargin: "-28% 0px -28% 0px",
2937
+ threshold: [0, 0.15, 0.3, 0.5, 0.75, 1]
2938
+ }
2939
+ );
2940
+ for (const el of mobileItemRefs.current) {
2941
+ if (el) observer.observe(el);
2942
+ }
2943
+ return () => observer.disconnect();
2944
+ }, [items.length]);
2846
2945
  const rootClass = [
2847
2946
  "fs",
2848
2947
  `fs--${variant}`,
@@ -2870,7 +2969,9 @@ var FeatureScroll = ({
2870
2969
  MediaRenderer,
2871
2970
  {
2872
2971
  item,
2873
- active: i === activeIndex
2972
+ active: i === activeIndex,
2973
+ rootMargin: mediaRootMargin,
2974
+ threshold: mediaThreshold
2874
2975
  }
2875
2976
  )
2876
2977
  },
@@ -2910,22 +3011,41 @@ var FeatureScroll = ({
2910
3011
  i
2911
3012
  )) })
2912
3013
  ] }),
2913
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-list", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "fs__mobile-item", children: [
2914
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h3", { className: "fs__item-title", children: item.title }),
2915
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-media", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MediaRenderer, { item, active: true }) }),
2916
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "fs__item-desc", children: item.description }),
2917
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2918
- "a",
2919
- {
2920
- href: item.link,
2921
- className: "fs__item-link",
2922
- children: [
2923
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: item.linkLabel }),
2924
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRight2, {})
2925
- ]
2926
- }
2927
- )
2928
- ] }, i)) })
3014
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-list", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
3015
+ "div",
3016
+ {
3017
+ className: "fs__mobile-item",
3018
+ "data-fs-mobile-idx": i,
3019
+ ref: (el) => {
3020
+ mobileItemRefs.current[i] = el;
3021
+ },
3022
+ children: [
3023
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h3", { className: "fs__item-title", children: item.title }),
3024
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-media", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3025
+ MediaRenderer,
3026
+ {
3027
+ item,
3028
+ active: i === mobileActiveIndex,
3029
+ rootMargin: mediaRootMargin,
3030
+ threshold: mediaThreshold
3031
+ }
3032
+ ) }),
3033
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "fs__item-desc", children: item.description }),
3034
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
3035
+ "a",
3036
+ {
3037
+ href: item.link,
3038
+ className: "fs__item-link",
3039
+ children: [
3040
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: item.linkLabel }),
3041
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRight2, {})
3042
+ ]
3043
+ }
3044
+ )
3045
+ ]
3046
+ },
3047
+ i
3048
+ )) })
2929
3049
  ] }) });
2930
3050
  };
2931
3051