@deriv-web-design/ui 0.1.17 → 0.1.18
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/components.manifest.json +16 -1
- package/dist/index.d.mts +45 -2
- package/dist/index.d.ts +45 -2
- package/dist/index.js +139 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +169 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/components.manifest.json
CHANGED
|
@@ -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 both enabled and 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."
|
|
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,29 @@ 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 both
|
|
848
|
+
* enabled and in view — so a page with no animation never downloads it, and a
|
|
849
|
+
* below-the-fold band does not pay for it during initial load.
|
|
807
850
|
*/
|
|
808
|
-
declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
|
|
851
|
+
declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, poster, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
|
|
809
852
|
|
|
810
853
|
/** Visual colour theme for each card — matches the Figma palette. */
|
|
811
854
|
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,29 @@ 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 both
|
|
848
|
+
* enabled and in view — so a page with no animation never downloads it, and a
|
|
849
|
+
* below-the-fold band does not pay for it during initial load.
|
|
807
850
|
*/
|
|
808
|
-
declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
|
|
851
|
+
declare function DotLottieInView({ enabled, rootMargin, threshold, className, style, poster, ...dotProps }: DotLottieInViewProps): react_jsx_runtime.JSX.Element;
|
|
809
852
|
|
|
810
853
|
/** Visual colour theme for each card — matches the Figma palette. */
|
|
811
854
|
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,6 +2674,24 @@ 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 && enabled)) 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, enabled, mounted]);
|
|
2663
2695
|
(0, import_react12.useEffect)(() => {
|
|
2664
2696
|
if (!dotLottie) return;
|
|
2665
2697
|
if (!enabled || !inView) {
|
|
@@ -2684,15 +2716,15 @@ function DotLottieInView({
|
|
|
2684
2716
|
backgroundColor: "transparent",
|
|
2685
2717
|
...style
|
|
2686
2718
|
},
|
|
2687
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2688
|
-
|
|
2719
|
+
children: mounted ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react12.Suspense, { fallback: poster ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2720
|
+
DotLottiePlayer,
|
|
2689
2721
|
{
|
|
2690
2722
|
...dotProps,
|
|
2691
2723
|
loop: false,
|
|
2692
2724
|
autoplay: false,
|
|
2693
2725
|
dotLottieRefCallback: setDotLottie
|
|
2694
2726
|
}
|
|
2695
|
-
)
|
|
2727
|
+
) }) : poster ?? null
|
|
2696
2728
|
}
|
|
2697
2729
|
);
|
|
2698
2730
|
}
|
|
@@ -2764,26 +2796,45 @@ var ChevronRight2 = () => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
|
2764
2796
|
)
|
|
2765
2797
|
}
|
|
2766
2798
|
);
|
|
2799
|
+
function mediaSrc(item) {
|
|
2800
|
+
return typeof item.mediaUrl === "string" && item.mediaUrl !== "" ? item.mediaUrl : null;
|
|
2801
|
+
}
|
|
2767
2802
|
function resolveMediaType(item) {
|
|
2768
2803
|
if (item.mediaType) return item.mediaType;
|
|
2769
|
-
|
|
2804
|
+
const src = mediaSrc(item);
|
|
2805
|
+
return src?.split("?")[0].endsWith(".lottie") ? "lottie" : "image";
|
|
2770
2806
|
}
|
|
2771
|
-
var MediaRenderer = ({ item, active }) => {
|
|
2807
|
+
var MediaRenderer = ({ item, active, rootMargin, threshold }) => {
|
|
2808
|
+
const src = mediaSrc(item);
|
|
2809
|
+
if (!src) return null;
|
|
2772
2810
|
if (resolveMediaType(item) === "lottie") {
|
|
2773
2811
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
2774
2812
|
DotLottieInView,
|
|
2775
2813
|
{
|
|
2776
|
-
src
|
|
2777
|
-
enabled: active
|
|
2814
|
+
src,
|
|
2815
|
+
enabled: active,
|
|
2816
|
+
rootMargin,
|
|
2817
|
+
threshold,
|
|
2818
|
+
poster: item.posterUrl ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
2819
|
+
"img",
|
|
2820
|
+
{
|
|
2821
|
+
src: item.posterUrl,
|
|
2822
|
+
alt: "",
|
|
2823
|
+
"aria-hidden": "true",
|
|
2824
|
+
loading: "lazy",
|
|
2825
|
+
decoding: "async"
|
|
2826
|
+
}
|
|
2827
|
+
) : void 0
|
|
2778
2828
|
}
|
|
2779
2829
|
);
|
|
2780
2830
|
}
|
|
2781
2831
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
2782
2832
|
"img",
|
|
2783
2833
|
{
|
|
2784
|
-
src
|
|
2834
|
+
src,
|
|
2785
2835
|
alt: item.title,
|
|
2786
|
-
loading: "lazy"
|
|
2836
|
+
loading: "lazy",
|
|
2837
|
+
decoding: "async"
|
|
2787
2838
|
}
|
|
2788
2839
|
);
|
|
2789
2840
|
};
|
|
@@ -2792,10 +2843,16 @@ var FeatureScroll = ({
|
|
|
2792
2843
|
items,
|
|
2793
2844
|
variant = "media-right",
|
|
2794
2845
|
className,
|
|
2846
|
+
// Destructured, not spread: `...props` lands on the <section>, and an
|
|
2847
|
+
// unknown attribute there is a React DOM warning at best.
|
|
2848
|
+
mediaRootMargin,
|
|
2849
|
+
mediaThreshold,
|
|
2795
2850
|
...props
|
|
2796
2851
|
}) => {
|
|
2797
2852
|
const [activeIndex, setActiveIndex] = (0, import_react13.useState)(0);
|
|
2853
|
+
const [mobileActiveIndex, setMobileActiveIndex] = (0, import_react13.useState)(0);
|
|
2798
2854
|
const titleRefs = (0, import_react13.useRef)([]);
|
|
2855
|
+
const mobileItemRefs = (0, import_react13.useRef)([]);
|
|
2799
2856
|
const mediaColRef = (0, import_react13.useRef)(null);
|
|
2800
2857
|
(0, import_react13.useLayoutEffect)(() => {
|
|
2801
2858
|
if (items.length === 0) return;
|
|
@@ -2843,6 +2900,40 @@ var FeatureScroll = ({
|
|
|
2843
2900
|
if (rafId !== 0) window.cancelAnimationFrame(rafId);
|
|
2844
2901
|
};
|
|
2845
2902
|
}, [items.length]);
|
|
2903
|
+
(0, import_react13.useEffect)(() => {
|
|
2904
|
+
if (items.length === 0) return;
|
|
2905
|
+
if (typeof window === "undefined") return;
|
|
2906
|
+
const ratios = /* @__PURE__ */ new Map();
|
|
2907
|
+
const observer = new IntersectionObserver(
|
|
2908
|
+
(entries) => {
|
|
2909
|
+
if (window.matchMedia("(min-width: 992px)").matches) return;
|
|
2910
|
+
for (const entry of entries) {
|
|
2911
|
+
const idx = Number(entry.target.getAttribute("data-fs-mobile-idx"));
|
|
2912
|
+
if (!Number.isFinite(idx)) continue;
|
|
2913
|
+
ratios.set(idx, entry.isIntersecting ? entry.intersectionRatio : 0);
|
|
2914
|
+
}
|
|
2915
|
+
let best = 0;
|
|
2916
|
+
let bestRatio = -1;
|
|
2917
|
+
for (const [idx, ratio] of ratios) {
|
|
2918
|
+
if (ratio > bestRatio) {
|
|
2919
|
+
bestRatio = ratio;
|
|
2920
|
+
best = idx;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
if (bestRatio <= 0) return;
|
|
2924
|
+
setMobileActiveIndex((prev) => prev === best ? prev : best);
|
|
2925
|
+
},
|
|
2926
|
+
{
|
|
2927
|
+
root: null,
|
|
2928
|
+
rootMargin: "-28% 0px -28% 0px",
|
|
2929
|
+
threshold: [0, 0.15, 0.3, 0.5, 0.75, 1]
|
|
2930
|
+
}
|
|
2931
|
+
);
|
|
2932
|
+
for (const el of mobileItemRefs.current) {
|
|
2933
|
+
if (el) observer.observe(el);
|
|
2934
|
+
}
|
|
2935
|
+
return () => observer.disconnect();
|
|
2936
|
+
}, [items.length]);
|
|
2846
2937
|
const rootClass = [
|
|
2847
2938
|
"fs",
|
|
2848
2939
|
`fs--${variant}`,
|
|
@@ -2870,7 +2961,9 @@ var FeatureScroll = ({
|
|
|
2870
2961
|
MediaRenderer,
|
|
2871
2962
|
{
|
|
2872
2963
|
item,
|
|
2873
|
-
active: i === activeIndex
|
|
2964
|
+
active: i === activeIndex,
|
|
2965
|
+
rootMargin: mediaRootMargin,
|
|
2966
|
+
threshold: mediaThreshold
|
|
2874
2967
|
}
|
|
2875
2968
|
)
|
|
2876
2969
|
},
|
|
@@ -2910,22 +3003,41 @@ var FeatureScroll = ({
|
|
|
2910
3003
|
i
|
|
2911
3004
|
)) })
|
|
2912
3005
|
] }),
|
|
2913
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-list", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
children:
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-list", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
3007
|
+
"div",
|
|
3008
|
+
{
|
|
3009
|
+
className: "fs__mobile-item",
|
|
3010
|
+
"data-fs-mobile-idx": i,
|
|
3011
|
+
ref: (el) => {
|
|
3012
|
+
mobileItemRefs.current[i] = el;
|
|
3013
|
+
},
|
|
3014
|
+
children: [
|
|
3015
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h3", { className: "fs__item-title", children: item.title }),
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fs__mobile-media", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3017
|
+
MediaRenderer,
|
|
3018
|
+
{
|
|
3019
|
+
item,
|
|
3020
|
+
active: i === mobileActiveIndex,
|
|
3021
|
+
rootMargin: mediaRootMargin,
|
|
3022
|
+
threshold: mediaThreshold
|
|
3023
|
+
}
|
|
3024
|
+
) }),
|
|
3025
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "fs__item-desc", children: item.description }),
|
|
3026
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
3027
|
+
"a",
|
|
3028
|
+
{
|
|
3029
|
+
href: item.link,
|
|
3030
|
+
className: "fs__item-link",
|
|
3031
|
+
children: [
|
|
3032
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: item.linkLabel }),
|
|
3033
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRight2, {})
|
|
3034
|
+
]
|
|
3035
|
+
}
|
|
3036
|
+
)
|
|
3037
|
+
]
|
|
3038
|
+
},
|
|
3039
|
+
i
|
|
3040
|
+
)) })
|
|
2929
3041
|
] }) });
|
|
2930
3042
|
};
|
|
2931
3043
|
|