@salesmind-ai/design-system 0.6.0 → 0.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.
- package/README.md +2 -0
- package/dist/Anton-Regular-MLEXVTB2.woff2 +0 -0
- package/dist/admin/index.cjs +5 -61
- package/dist/admin/index.css +0 -3918
- package/dist/admin/index.css.map +1 -1
- package/dist/admin/index.d.cts +1 -422
- package/dist/admin/index.d.ts +1 -422
- package/dist/admin/index.js +1 -5
- package/dist/blog/index.cjs +13 -34
- package/dist/blog/index.css +0 -579
- package/dist/blog/index.css.map +1 -1
- package/dist/blog/index.d.cts +1 -54
- package/dist/blog/index.d.ts +1 -54
- package/dist/blog/index.js +5 -6
- package/dist/charts/index.cjs +0 -46
- package/dist/charts/index.d.cts +1 -452
- package/dist/charts/index.d.ts +1 -452
- package/dist/charts/index.js +1 -3
- package/dist/{chunk-HDVAMYSG.js → chunk-27Y5ESMM.js} +7 -2
- package/dist/chunk-27Y5ESMM.js.map +1 -0
- package/dist/{chunk-YTYDQBVY.cjs → chunk-2VVRZBUR.cjs} +4 -4
- package/dist/{chunk-GQELL2MF.cjs → chunk-3NS6X2R4.cjs} +20 -203
- package/dist/chunk-3NS6X2R4.cjs.map +1 -0
- package/dist/{chunk-XEX2AEZK.cjs → chunk-65DTHLVX.cjs} +66 -186
- package/dist/chunk-65DTHLVX.cjs.map +1 -0
- package/dist/{chunk-QALDZ7WQ.js → chunk-6BUS7RMS.js} +21 -198
- package/dist/chunk-6BUS7RMS.js.map +1 -0
- package/dist/{chunk-BJZ2DKS5.cjs → chunk-6QIQCUYC.cjs} +11 -10
- package/dist/chunk-6QIQCUYC.cjs.map +1 -0
- package/dist/{chunk-H2Y6BSTL.cjs → chunk-7EUR3AKV.cjs} +1 -1
- package/dist/chunk-7EUR3AKV.cjs.map +1 -0
- package/dist/{chunk-VFJZQQZU.js → chunk-AMNY5TS3.js} +11 -10
- package/dist/chunk-AMNY5TS3.js.map +1 -0
- package/dist/{chunk-YJ6C3EKW.js → chunk-CLXLQCNQ.js} +52 -168
- package/dist/chunk-CLXLQCNQ.js.map +1 -0
- package/dist/{chunk-H2KQ3WSH.cjs → chunk-CVLD5RQK.cjs} +12 -14
- package/dist/chunk-CVLD5RQK.cjs.map +1 -0
- package/dist/chunk-EPD4ZEPY.cjs +344 -0
- package/dist/chunk-EPD4ZEPY.cjs.map +1 -0
- package/dist/chunk-FXYOSA4E.cjs +118 -0
- package/dist/chunk-FXYOSA4E.cjs.map +1 -0
- package/dist/{chunk-ECXBTUH6.cjs → chunk-GPHQGLR5.cjs} +27 -204
- package/dist/chunk-GPHQGLR5.cjs.map +1 -0
- package/dist/{chunk-Y26OHHMX.js → chunk-HHQ6J7B6.js} +513 -888
- package/dist/chunk-HHQ6J7B6.js.map +1 -0
- package/dist/chunk-JPUJWI7F.cjs +73 -0
- package/dist/chunk-JPUJWI7F.cjs.map +1 -0
- package/dist/{chunk-6UNG76Y2.js → chunk-K526GN7P.js} +2 -2
- package/dist/{chunk-SICKWUWB.js → chunk-KJHPOB3J.js} +1 -1
- package/dist/chunk-KJHPOB3J.js.map +1 -0
- package/dist/chunk-KXVFFEGD.js +60 -0
- package/dist/chunk-KXVFFEGD.js.map +1 -0
- package/dist/chunk-LQB7QLD3.js +288 -0
- package/dist/chunk-LQB7QLD3.js.map +1 -0
- package/dist/chunk-LUD52ZJF.cjs +726 -0
- package/dist/chunk-LUD52ZJF.cjs.map +1 -0
- package/dist/{chunk-7UZ5DETZ.js → chunk-MBAG654R.js} +4 -216
- package/dist/chunk-MBAG654R.js.map +1 -0
- package/dist/chunk-OMP6FAZ6.cjs +183 -0
- package/dist/chunk-OMP6FAZ6.cjs.map +1 -0
- package/dist/{chunk-WYH4TKS5.js → chunk-PBYRTNQ5.js} +6 -8
- package/dist/chunk-PBYRTNQ5.js.map +1 -0
- package/dist/chunk-PYREXCZK.js +679 -0
- package/dist/chunk-PYREXCZK.js.map +1 -0
- package/dist/{chunk-6D22TFLA.cjs → chunk-R3ZECV5P.cjs} +9 -4
- package/dist/chunk-R3ZECV5P.cjs.map +1 -0
- package/dist/{chunk-P5BOFE5A.js → chunk-RSLA2FJN.js} +28 -183
- package/dist/chunk-RSLA2FJN.js.map +1 -0
- package/dist/chunk-S46SKHMD.js +173 -0
- package/dist/chunk-S46SKHMD.js.map +1 -0
- package/dist/chunk-SFXTB7JL.js +190 -0
- package/dist/chunk-SFXTB7JL.js.map +1 -0
- package/dist/chunk-SGYXYMKZ.cjs +214 -0
- package/dist/chunk-SGYXYMKZ.cjs.map +1 -0
- package/dist/chunk-UGKYP6F3.cjs +296 -0
- package/dist/chunk-UGKYP6F3.cjs.map +1 -0
- package/dist/chunk-WB6XDNU7.js +115 -0
- package/dist/chunk-WB6XDNU7.js.map +1 -0
- package/dist/{chunk-LTPTW2US.cjs → chunk-WE4QIIVN.cjs} +592 -974
- package/dist/chunk-WE4QIIVN.cjs.map +1 -0
- package/dist/core/index.cjs +144 -626
- package/dist/core/index.css +178 -3567
- package/dist/core/index.css.map +1 -1
- package/dist/core/index.d.cts +940 -902
- package/dist/core/index.d.ts +940 -902
- package/dist/core/index.js +6 -12
- package/dist/i18n/index.cjs +54 -49
- package/dist/i18n/index.d.cts +46 -11
- package/dist/i18n/index.d.ts +46 -11
- package/dist/i18n/index.js +2 -1
- package/dist/index-BJ8rBqrO.d.cts +1100 -0
- package/dist/index-BxMqCbqE.d.ts +1100 -0
- package/dist/index.cjs +507 -1001
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +6057 -16713
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +306 -21
- package/dist/index.d.ts +306 -21
- package/dist/index.js +274 -39
- package/dist/index.js.map +1 -1
- package/dist/marketing/index.cjs +33 -76
- package/dist/marketing/index.css +1896 -3234
- package/dist/marketing/index.css.map +1 -1
- package/dist/marketing/index.d.cts +3 -1351
- package/dist/marketing/index.d.ts +3 -1351
- package/dist/marketing/index.js +5 -8
- package/dist/motion/index.cjs +3 -20
- package/dist/motion/index.css +0 -580
- package/dist/motion/index.css.map +1 -1
- package/dist/motion/index.d.cts +1 -37
- package/dist/motion/index.d.ts +1 -37
- package/dist/motion/index.js +1 -2
- package/dist/nav/index.cjs +10 -35
- package/dist/nav/index.css +28 -580
- package/dist/nav/index.css.map +1 -1
- package/dist/nav/index.d.cts +2 -60
- package/dist/nav/index.d.ts +2 -60
- package/dist/nav/index.js +1 -2
- package/dist/report/index.cjs +1166 -175
- package/dist/report/index.cjs.map +1 -1
- package/dist/report/index.d.cts +208 -5
- package/dist/report/index.d.ts +208 -5
- package/dist/report/index.js +1141 -3
- package/dist/report/index.js.map +1 -1
- package/dist/sections/index.cjs +8 -10
- package/dist/sections/index.cjs.map +1 -1
- package/dist/sections/index.css +0 -206
- package/dist/sections/index.css.map +1 -1
- package/dist/sections/index.js +2 -4
- package/dist/sections/index.js.map +1 -1
- package/dist/social-media/index.cjs +4 -0
- package/dist/social-media/index.cjs.map +1 -0
- package/dist/social-media/index.d.cts +2 -0
- package/dist/social-media/index.d.ts +2 -0
- package/dist/social-media/index.js +3 -0
- package/dist/social-media/index.js.map +1 -0
- package/dist/social-proof/index.cjs +4 -36
- package/dist/social-proof/index.css +3 -1106
- package/dist/social-proof/index.css.map +1 -1
- package/dist/social-proof/index.d.cts +26 -171
- package/dist/social-proof/index.d.ts +26 -171
- package/dist/social-proof/index.js +1 -5
- package/dist/styles/styles.css +657 -2990
- package/dist/theme/index.cjs +12 -16
- package/dist/theme/index.css +1 -245
- package/dist/theme/index.css.map +1 -1
- package/dist/theme/index.d.cts +4 -1
- package/dist/theme/index.d.ts +4 -1
- package/dist/theme/index.js +2 -2
- package/dist/web/client/index.cjs +10 -10
- package/dist/web/client/index.css +118 -0
- package/dist/web/client/index.css.map +1 -1
- package/dist/web/client/index.js +2 -2
- package/dist/web/index.cjs +10 -10
- package/dist/web/index.css +118 -0
- package/dist/web/index.css.map +1 -1
- package/dist/web/index.js +2 -2
- package/package.json +10 -4
- package/dist/AppearancePanel-UT57J69V.d.cts +0 -51
- package/dist/AppearancePanel-UT57J69V.d.ts +0 -51
- package/dist/ExportMenu-A2TLFiVv.d.cts +0 -311
- package/dist/ExportMenu-C8qck5AT.d.ts +0 -311
- package/dist/Select-BdZmK0Lt.d.cts +0 -66
- package/dist/Select-BdZmK0Lt.d.ts +0 -66
- package/dist/chart-types-BGVVO-zl.d.cts +0 -208
- package/dist/chart-types-BGVVO-zl.d.ts +0 -208
- package/dist/charts/index.css +0 -1167
- package/dist/charts/index.css.map +0 -1
- package/dist/chunk-3BAQDW3V.cjs +0 -1207
- package/dist/chunk-3BAQDW3V.cjs.map +0 -1
- package/dist/chunk-3NKRFUAR.js +0 -37
- package/dist/chunk-3NKRFUAR.js.map +0 -1
- package/dist/chunk-3TGSIILM.cjs +0 -201
- package/dist/chunk-3TGSIILM.cjs.map +0 -1
- package/dist/chunk-4GM5BGBN.cjs +0 -801
- package/dist/chunk-4GM5BGBN.cjs.map +0 -1
- package/dist/chunk-5LA3T22E.cjs +0 -562
- package/dist/chunk-5LA3T22E.cjs.map +0 -1
- package/dist/chunk-5SN66B2X.js +0 -2542
- package/dist/chunk-5SN66B2X.js.map +0 -1
- package/dist/chunk-6D22TFLA.cjs.map +0 -1
- package/dist/chunk-6H4DSTXR.js +0 -786
- package/dist/chunk-6H4DSTXR.js.map +0 -1
- package/dist/chunk-6HKQ5ILL.cjs +0 -1624
- package/dist/chunk-6HKQ5ILL.cjs.map +0 -1
- package/dist/chunk-7PX2AZ6Y.js +0 -39
- package/dist/chunk-7PX2AZ6Y.js.map +0 -1
- package/dist/chunk-7UZ5DETZ.js.map +0 -1
- package/dist/chunk-B6AVAX4F.js +0 -1415
- package/dist/chunk-B6AVAX4F.js.map +0 -1
- package/dist/chunk-BJZ2DKS5.cjs.map +0 -1
- package/dist/chunk-BUTQSDQH.js +0 -200
- package/dist/chunk-BUTQSDQH.js.map +0 -1
- package/dist/chunk-C2BCDNAV.js +0 -24
- package/dist/chunk-C2BCDNAV.js.map +0 -1
- package/dist/chunk-CJ2MKVAF.cjs +0 -46
- package/dist/chunk-CJ2MKVAF.cjs.map +0 -1
- package/dist/chunk-E7D6EKJ4.cjs +0 -44
- package/dist/chunk-E7D6EKJ4.cjs.map +0 -1
- package/dist/chunk-ECXBTUH6.cjs.map +0 -1
- package/dist/chunk-FAFAP4L5.js +0 -183
- package/dist/chunk-FAFAP4L5.js.map +0 -1
- package/dist/chunk-G2XGBO5V.cjs +0 -2565
- package/dist/chunk-G2XGBO5V.cjs.map +0 -1
- package/dist/chunk-GQELL2MF.cjs.map +0 -1
- package/dist/chunk-H2KQ3WSH.cjs.map +0 -1
- package/dist/chunk-H2Y6BSTL.cjs.map +0 -1
- package/dist/chunk-HCZW5AJN.cjs +0 -234
- package/dist/chunk-HCZW5AJN.cjs.map +0 -1
- package/dist/chunk-HDVAMYSG.js.map +0 -1
- package/dist/chunk-HN4PHABT.js +0 -126
- package/dist/chunk-HN4PHABT.js.map +0 -1
- package/dist/chunk-LTPTW2US.cjs.map +0 -1
- package/dist/chunk-MDB2WCRQ.cjs +0 -137
- package/dist/chunk-MDB2WCRQ.cjs.map +0 -1
- package/dist/chunk-MQRB634A.cjs +0 -34
- package/dist/chunk-MQRB634A.cjs.map +0 -1
- package/dist/chunk-NN3TUHIH.js +0 -28
- package/dist/chunk-NN3TUHIH.js.map +0 -1
- package/dist/chunk-OWS2KAXZ.js +0 -701
- package/dist/chunk-OWS2KAXZ.js.map +0 -1
- package/dist/chunk-P5BOFE5A.js.map +0 -1
- package/dist/chunk-PUPSK3DI.cjs +0 -216
- package/dist/chunk-PUPSK3DI.cjs.map +0 -1
- package/dist/chunk-Q2MFGYTE.cjs +0 -1449
- package/dist/chunk-Q2MFGYTE.cjs.map +0 -1
- package/dist/chunk-Q75DBVDY.cjs +0 -68
- package/dist/chunk-Q75DBVDY.cjs.map +0 -1
- package/dist/chunk-QALDZ7WQ.js.map +0 -1
- package/dist/chunk-QWE2RNCS.js +0 -1195
- package/dist/chunk-QWE2RNCS.js.map +0 -1
- package/dist/chunk-RQUFZAZ7.js +0 -1608
- package/dist/chunk-RQUFZAZ7.js.map +0 -1
- package/dist/chunk-SICKWUWB.js.map +0 -1
- package/dist/chunk-TCFC7XTB.js +0 -212
- package/dist/chunk-TCFC7XTB.js.map +0 -1
- package/dist/chunk-UTVXGAQP.cjs +0 -2437
- package/dist/chunk-UTVXGAQP.cjs.map +0 -1
- package/dist/chunk-UVEMY3FQ.cjs +0 -717
- package/dist/chunk-UVEMY3FQ.cjs.map +0 -1
- package/dist/chunk-VFJZQQZU.js.map +0 -1
- package/dist/chunk-WH7PYHZY.cjs +0 -35
- package/dist/chunk-WH7PYHZY.cjs.map +0 -1
- package/dist/chunk-WYH4TKS5.js.map +0 -1
- package/dist/chunk-XEX2AEZK.cjs.map +0 -1
- package/dist/chunk-XPTVHPCN.js +0 -2320
- package/dist/chunk-XPTVHPCN.js.map +0 -1
- package/dist/chunk-XWPDRMZG.js +0 -62
- package/dist/chunk-XWPDRMZG.js.map +0 -1
- package/dist/chunk-Y26OHHMX.js.map +0 -1
- package/dist/chunk-YJ6C3EKW.js.map +0 -1
- package/dist/motion-C651Ry6d.d.cts +0 -832
- package/dist/motion-C651Ry6d.d.ts +0 -832
- package/dist/report/index.css +0 -1239
- package/dist/report/index.css.map +0 -1
- /package/dist/{chunk-6UNG76Y2.js.map → chunk-2VVRZBUR.cjs.map} +0 -0
- /package/dist/{chunk-YTYDQBVY.cjs.map → chunk-K526GN7P.js.map} +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var clsx = require('clsx');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var dialog = require('@base-ui/react/dialog');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var clsx__default = /*#__PURE__*/_interopDefault(clsx);
|
|
11
|
+
|
|
12
|
+
// src/components/StarRating/StarRating.tsx
|
|
13
|
+
var StarRating = react.forwardRef(
|
|
14
|
+
({
|
|
15
|
+
value,
|
|
16
|
+
max = 5,
|
|
17
|
+
size = "md",
|
|
18
|
+
animate = false,
|
|
19
|
+
showValue = false,
|
|
20
|
+
valueLabel,
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}, ref) => {
|
|
24
|
+
const [visible, setVisible] = react.useState(!animate);
|
|
25
|
+
const containerRef = react.useRef(null);
|
|
26
|
+
react.useEffect(() => {
|
|
27
|
+
if (!animate) return;
|
|
28
|
+
if (typeof window !== "undefined") {
|
|
29
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
30
|
+
if (mq.matches) {
|
|
31
|
+
setVisible(true);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const target = containerRef.current;
|
|
36
|
+
if (!target) return;
|
|
37
|
+
const observer = new IntersectionObserver(
|
|
38
|
+
([entry]) => {
|
|
39
|
+
if (entry.isIntersecting) {
|
|
40
|
+
setVisible(true);
|
|
41
|
+
observer.disconnect();
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{ threshold: 0.5 }
|
|
45
|
+
);
|
|
46
|
+
observer.observe(target);
|
|
47
|
+
return () => observer.disconnect();
|
|
48
|
+
}, [animate]);
|
|
49
|
+
const clampedValue = Math.min(Math.max(value, 0), max);
|
|
50
|
+
const stars = Array.from({ length: max }, (_, i) => {
|
|
51
|
+
const starIndex = i + 1;
|
|
52
|
+
const fillPercent = clampedValue >= starIndex ? 100 : clampedValue > starIndex - 1 ? (clampedValue - (starIndex - 1)) * 100 : 0;
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
54
|
+
"span",
|
|
55
|
+
{
|
|
56
|
+
className: clsx__default.default(
|
|
57
|
+
"ds-star-rating__star",
|
|
58
|
+
visible && "ds-star-rating__star--visible"
|
|
59
|
+
),
|
|
60
|
+
style: {
|
|
61
|
+
"--star-fill": `${fillPercent}%`,
|
|
62
|
+
"--star-delay": animate ? `${i * 100}ms` : "0ms"
|
|
63
|
+
},
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
66
|
+
"svg",
|
|
67
|
+
{
|
|
68
|
+
viewBox: "0 0 24 24",
|
|
69
|
+
fill: "none",
|
|
70
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
71
|
+
className: "ds-star-rating__svg",
|
|
72
|
+
"aria-hidden": "true",
|
|
73
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
74
|
+
"path",
|
|
75
|
+
{
|
|
76
|
+
d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
|
|
77
|
+
className: "ds-star-rating__empty"
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
83
|
+
"svg",
|
|
84
|
+
{
|
|
85
|
+
viewBox: "0 0 24 24",
|
|
86
|
+
fill: "none",
|
|
87
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
88
|
+
className: "ds-star-rating__svg ds-star-rating__svg--filled",
|
|
89
|
+
"aria-hidden": "true",
|
|
90
|
+
style: {
|
|
91
|
+
clipPath: `inset(0 ${100 - fillPercent}% 0 0)`
|
|
92
|
+
},
|
|
93
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
94
|
+
"path",
|
|
95
|
+
{
|
|
96
|
+
d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
|
|
97
|
+
className: "ds-star-rating__filled"
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
i
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
ref: (node) => {
|
|
111
|
+
containerRef.current = node;
|
|
112
|
+
if (typeof ref === "function") ref(node);
|
|
113
|
+
else if (ref) ref.current = node;
|
|
114
|
+
},
|
|
115
|
+
className: clsx__default.default("ds-star-rating", `ds-star-rating--${size}`, className),
|
|
116
|
+
role: "img",
|
|
117
|
+
"aria-label": `${clampedValue} out of ${max} stars`,
|
|
118
|
+
...props,
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-star-rating__stars", children: stars }),
|
|
121
|
+
showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-star-rating__value", children: [
|
|
122
|
+
clampedValue.toLocaleString(void 0, {
|
|
123
|
+
minimumFractionDigits: clampedValue % 1 !== 0 ? 1 : 0,
|
|
124
|
+
maximumFractionDigits: 1
|
|
125
|
+
}),
|
|
126
|
+
valueLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-star-rating__value-label", children: valueLabel })
|
|
127
|
+
] })
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
StarRating.displayName = "StarRating";
|
|
134
|
+
var TrustpilotIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2l2.9 8.9H24l-7.35 5.34 2.81 8.64L12 19.56l-7.46 5.32 2.81-8.64L0 10.9h9.1L12 2z", fill: "#00B67A" }) });
|
|
135
|
+
var G2Icon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
136
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z", fill: "#FF492C" }),
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: "50%", y: "54%", dominantBaseline: "middle", textAnchor: "middle", fill: "white", fontWeight: "bold", fontSize: "11", fontFamily: "Arial, sans-serif", children: "G2" })
|
|
138
|
+
] });
|
|
139
|
+
var GoogleIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z", fill: "#4285F4" }),
|
|
141
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }),
|
|
142
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 001 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z", fill: "#FBBC05" }),
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })
|
|
144
|
+
] });
|
|
145
|
+
var ChromeWebStoreIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", fill: "#4285F4", opacity: "0.1", stroke: "#4285F4", strokeWidth: "1.5" }),
|
|
147
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z", fill: "#4285F4" }),
|
|
148
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 10.5L4 5.5", stroke: "#EA4335", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
149
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 10.5L20 5.5", stroke: "#FBBC05", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 17l-3.5 5", stroke: "#34A853", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
151
|
+
] });
|
|
152
|
+
var PLATFORM_ICONS = {
|
|
153
|
+
trustpilot: TrustpilotIcon,
|
|
154
|
+
g2: G2Icon,
|
|
155
|
+
google: GoogleIcon,
|
|
156
|
+
"chrome-web-store": ChromeWebStoreIcon
|
|
157
|
+
};
|
|
158
|
+
var PLATFORM_LABELS = {
|
|
159
|
+
trustpilot: "Trustpilot",
|
|
160
|
+
g2: "G2",
|
|
161
|
+
google: "Google",
|
|
162
|
+
"chrome-web-store": "Chrome Web Store"
|
|
163
|
+
};
|
|
164
|
+
var PLATFORM_COLORS = {
|
|
165
|
+
trustpilot: "#00B67A",
|
|
166
|
+
g2: "#FF492C",
|
|
167
|
+
google: "#4285F4",
|
|
168
|
+
"chrome-web-store": "#4285F4"
|
|
169
|
+
};
|
|
170
|
+
var PlatformBadge = react.forwardRef(
|
|
171
|
+
({
|
|
172
|
+
platform,
|
|
173
|
+
variant = "icon-label",
|
|
174
|
+
rating,
|
|
175
|
+
count,
|
|
176
|
+
size = "sm",
|
|
177
|
+
className,
|
|
178
|
+
...props
|
|
179
|
+
}, ref) => {
|
|
180
|
+
const Icon = PLATFORM_ICONS[platform];
|
|
181
|
+
const label = PLATFORM_LABELS[platform];
|
|
182
|
+
const accentColor = PLATFORM_COLORS[platform];
|
|
183
|
+
const iconSize = size === "sm" ? 18 : 22;
|
|
184
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
ref,
|
|
188
|
+
role: "img",
|
|
189
|
+
className: clsx__default.default(
|
|
190
|
+
"ds-platform-badge",
|
|
191
|
+
`ds-platform-badge--${variant}`,
|
|
192
|
+
`ds-platform-badge--${size}`,
|
|
193
|
+
className
|
|
194
|
+
),
|
|
195
|
+
style: { "--platform-accent": accentColor },
|
|
196
|
+
"aria-label": variant === "full" && rating ? `${label}: ${rating} stars${count ? ` from ${count} reviews` : ""}` : label,
|
|
197
|
+
...props,
|
|
198
|
+
children: [
|
|
199
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-platform-badge__icon", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: iconSize }) }),
|
|
200
|
+
variant !== "icon-only" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-platform-badge__label", children: label }),
|
|
201
|
+
variant === "full" && rating != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-platform-badge__rating", children: [
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx(StarRating, { value: rating, size: "sm" }),
|
|
203
|
+
count != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-platform-badge__count", children: [
|
|
204
|
+
"(",
|
|
205
|
+
count,
|
|
206
|
+
")"
|
|
207
|
+
] })
|
|
208
|
+
] })
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
PlatformBadge.displayName = "PlatformBadge";
|
|
215
|
+
function getEmbedUrl(src) {
|
|
216
|
+
const ytMatch = src.match(
|
|
217
|
+
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)/
|
|
218
|
+
);
|
|
219
|
+
if (ytMatch) return `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?autoplay=1&rel=0`;
|
|
220
|
+
const vimeoMatch = src.match(/(?:vimeo\.com\/)(\d+)/);
|
|
221
|
+
if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`;
|
|
222
|
+
const loomMatch = src.match(/(?:loom\.com\/share\/)([a-zA-Z0-9]+)/);
|
|
223
|
+
if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}?autoplay=1`;
|
|
224
|
+
return src;
|
|
225
|
+
}
|
|
226
|
+
var PlayIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
227
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "32", cy: "32", r: "32", className: "ds-video-lightbox__play-bg" }),
|
|
228
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M26 20v24l20-12L26 20z", fill: "white" })
|
|
229
|
+
] });
|
|
230
|
+
var CloseIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
231
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
232
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
233
|
+
] });
|
|
234
|
+
var VideoLightbox = react.forwardRef(
|
|
235
|
+
({
|
|
236
|
+
videoUrl,
|
|
237
|
+
thumbnailSrc,
|
|
238
|
+
thumbnailAlt,
|
|
239
|
+
title = "Video",
|
|
240
|
+
trigger,
|
|
241
|
+
className,
|
|
242
|
+
onPlay
|
|
243
|
+
}, ref) => {
|
|
244
|
+
const [open, setOpen] = react.useState(false);
|
|
245
|
+
const handleOpen = react.useCallback(() => {
|
|
246
|
+
setOpen(true);
|
|
247
|
+
onPlay?.();
|
|
248
|
+
}, [onPlay]);
|
|
249
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: clsx__default.default("ds-video-lightbox", className), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
251
|
+
dialog.Dialog.Trigger,
|
|
252
|
+
{
|
|
253
|
+
className: "ds-video-lightbox__trigger",
|
|
254
|
+
onClick: handleOpen,
|
|
255
|
+
children: trigger || /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-video-lightbox__thumbnail-wrap", children: [
|
|
256
|
+
thumbnailSrc ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
257
|
+
"img",
|
|
258
|
+
{
|
|
259
|
+
src: thumbnailSrc,
|
|
260
|
+
alt: thumbnailAlt || `Play: ${title}`,
|
|
261
|
+
className: "ds-video-lightbox__thumbnail",
|
|
262
|
+
loading: "lazy",
|
|
263
|
+
decoding: "async"
|
|
264
|
+
}
|
|
265
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__placeholder" }),
|
|
266
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__overlay", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__play-btn", children: /* @__PURE__ */ jsxRuntime.jsx(PlayIcon, {}) }) })
|
|
267
|
+
] })
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
/* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Portal, { children: [
|
|
271
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Backdrop, { className: "ds-video-lightbox__backdrop" }),
|
|
272
|
+
/* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Popup, { className: "ds-video-lightbox__popup", children: [
|
|
273
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Close, { className: "ds-video-lightbox__close", "aria-label": "Close video", children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) }),
|
|
274
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Title, { className: "ds-sr-only", children: title }),
|
|
275
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__iframe-wrap", children: open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
276
|
+
"iframe",
|
|
277
|
+
{
|
|
278
|
+
src: getEmbedUrl(videoUrl),
|
|
279
|
+
title,
|
|
280
|
+
className: "ds-video-lightbox__iframe",
|
|
281
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
282
|
+
allowFullScreen: true
|
|
283
|
+
}
|
|
284
|
+
) })
|
|
285
|
+
] })
|
|
286
|
+
] })
|
|
287
|
+
] }) });
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
VideoLightbox.displayName = "VideoLightbox";
|
|
291
|
+
|
|
292
|
+
exports.PlatformBadge = PlatformBadge;
|
|
293
|
+
exports.StarRating = StarRating;
|
|
294
|
+
exports.VideoLightbox = VideoLightbox;
|
|
295
|
+
//# sourceMappingURL=out.js.map
|
|
296
|
+
//# sourceMappingURL=chunk-UGKYP6F3.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/StarRating/StarRating.tsx","../src/components/PlatformBadge/PlatformBadge.tsx","../src/components/VideoLightbox/VideoLightbox.tsx"],"names":["forwardRef","clsx","jsx","jsxs","useState"],"mappings":";AAAA,SAAgB,YAAY,WAAW,QAAQ,gBAAgB;AAC/D,OAAO,UAAU;AAiGT,SAmBI,KAnBJ;AA1DD,IAAM,aAAa;AAAA,EACxB,CACE;AAAA,IACE;AAAA,IACA,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,CAAC,SAAS,UAAU,IAAI,SAAS,CAAC,OAAO;AAC/C,UAAM,eAAe,OAAuB,IAAI;AAEhD,cAAU,MAAM;AACd,UAAI,CAAC,QAAS;AAGd,UAAI,OAAO,WAAW,aAAa;AACjC,cAAM,KAAK,OAAO,WAAW,kCAAkC;AAC/D,YAAI,GAAG,SAAS;AACd,qBAAW,IAAI;AACf;AAAA,QACF;AAAA,MACF;AAEA,YAAM,SAAS,aAAa;AAC5B,UAAI,CAAC,OAAQ;AAEb,YAAM,WAAW,IAAI;AAAA,QACnB,CAAC,CAAC,KAAK,MAAM;AACX,cAAI,MAAM,gBAAgB;AACxB,uBAAW,IAAI;AACf,qBAAS,WAAW;AAAA,UACtB;AAAA,QACF;AAAA,QACA,EAAE,WAAW,IAAI;AAAA,MACnB;AAEA,eAAS,QAAQ,MAAM;AACvB,aAAO,MAAM,SAAS,WAAW;AAAA,IACnC,GAAG,CAAC,OAAO,CAAC;AAEZ,UAAM,eAAe,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,GAAG;AAErD,UAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,IAAI,GAAG,CAAC,GAAG,MAAM;AAClD,YAAM,YAAY,IAAI;AACtB,YAAM,cACJ,gBAAgB,YACZ,MACA,eAAe,YAAY,KACxB,gBAAgB,YAAY,MAAM,MACnC;AAER,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW;AAAA,YACT;AAAA,YACA,WAAW;AAAA,UACb;AAAA,UACA,OAAO;AAAA,YACL,eAAe,GAAG,WAAW;AAAA,YAC7B,gBAAgB,UAAU,GAAG,IAAI,GAAG,OAAO;AAAA,UAC7C;AAAA,UAGA;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,OAAM;AAAA,gBACN,WAAU;AAAA,gBACV,eAAY;AAAA,gBAEZ;AAAA,kBAAC;AAAA;AAAA,oBACC,GAAE;AAAA,oBACF,WAAU;AAAA;AAAA,gBACZ;AAAA;AAAA,YACF;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,OAAM;AAAA,gBACN,WAAU;AAAA,gBACV,eAAY;AAAA,gBACZ,OAAO;AAAA,kBACL,UAAU,WAAW,MAAM,WAAW;AAAA,gBACxC;AAAA,gBAEA;AAAA,kBAAC;AAAA;AAAA,oBACC,GAAE;AAAA,oBACF,WAAU;AAAA;AAAA,gBACZ;AAAA;AAAA,YACF;AAAA;AAAA;AAAA,QAtCK;AAAA,MAuCP;AAAA,IAEJ,CAAC;AAED,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,CAAC,SAAS;AACb,UAAC,aAA+D,UAAU;AAC1E,cAAI,OAAO,QAAQ,WAAY,KAAI,IAAI;AAAA,mBAC9B,IAAK,CAAC,IAAsD,UAAU;AAAA,QACjF;AAAA,QACA,WAAW,KAAK,kBAAkB,mBAAmB,IAAI,IAAI,SAAS;AAAA,QACtE,MAAK;AAAA,QACL,cAAY,GAAG,YAAY,WAAW,GAAG;AAAA,QACxC,GAAG;AAAA,QAEJ;AAAA,8BAAC,UAAK,WAAU,yBAAyB,iBAAM;AAAA,UAC9C,aACC,qBAAC,UAAK,WAAU,yBACb;AAAA,yBAAa,eAAe,QAAW;AAAA,cACtC,uBAAuB,eAAe,MAAM,IAAI,IAAI;AAAA,cACpD,uBAAuB;AAAA,YACzB,CAAC;AAAA,YACA,cACC,oBAAC,UAAK,WAAU,+BAA+B,sBAAW;AAAA,aAE9D;AAAA;AAAA;AAAA,IAEJ;AAAA,EAEJ;AACF;AAEA,WAAW,cAAc;;;AC3KzB,SAAgB,cAAAA,mBAAkB;AAClC,OAAOC,WAAU;AAmCb,gBAAAC,MAKF,QAAAC,aALE;AAFJ,IAAM,iBAAiB,CAAC,EAAE,KAAK,MAC7B,gBAAAD,KAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,OAAM,8BAA6B,eAAY,QAC7G,0BAAAA,KAAC,UAAK,GAAE,wFAAuF,MAAK,WAAU,GAChH;AAGF,IAAM,SAAS,CAAC,EAAE,KAAK,MACrB,gBAAAC,MAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,OAAM,8BAA6B,eAAY,QAC7G;AAAA,kBAAAD,KAAC,UAAK,GAAE,sEAAqE,MAAK,WAAU;AAAA,EAC5F,gBAAAA,KAAC,UAAK,GAAE,OAAM,GAAE,OAAM,kBAAiB,UAAS,YAAW,UAAS,MAAK,SAAQ,YAAW,QAAO,UAAS,MAAK,YAAW,qBAAoB,gBAAE;AAAA,GACpJ;AAGF,IAAM,aAAa,CAAC,EAAE,KAAK,MACzB,gBAAAC,MAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,OAAM,8BAA6B,eAAY,QAC7G;AAAA,kBAAAD,KAAC,UAAK,GAAE,oHAAmH,MAAK,WAAS;AAAA,EACzI,gBAAAA,KAAC,UAAK,GAAE,yIAAwI,MAAK,WAAS;AAAA,EAC9J,gBAAAA,KAAC,UAAK,GAAE,8HAA6H,MAAK,WAAS;AAAA,EACnJ,gBAAAA,KAAC,UAAK,GAAE,uIAAsI,MAAK,WAAS;AAAA,GAC9J;AAGF,IAAM,qBAAqB,CAAC,EAAE,KAAK,MACjC,gBAAAC,MAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,OAAM,8BAA6B,eAAY,QAC7G;AAAA,kBAAAD,KAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,MAAK,WAAU,SAAQ,OAAM,QAAO,WAAU,aAAY,OAAK;AAAA,EAC9F,gBAAAA,KAAC,UAAK,GAAE,iJAAgJ,MAAK,WAAS;AAAA,EACtK,gBAAAA,KAAC,UAAK,GAAE,mBAAkB,QAAO,WAAU,aAAY,OAAM,eAAc,SAAO;AAAA,EAClF,gBAAAA,KAAC,UAAK,GAAE,qBAAoB,QAAO,WAAU,aAAY,OAAM,eAAc,SAAO;AAAA,EACpF,gBAAAA,KAAC,UAAK,GAAE,iBAAgB,QAAO,WAAU,aAAY,OAAM,eAAc,SAAO;AAAA,GAClF;AAGF,IAAM,iBAA+D;AAAA,EACnE,YAAY;AAAA,EACZ,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,oBAAoB;AACtB;AAEA,IAAM,kBAA4C;AAAA,EAChD,YAAY;AAAA,EACZ,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,oBAAoB;AACtB;AAEA,IAAM,kBAA4C;AAAA,EAChD,YAAY;AAAA,EACZ,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,oBAAoB;AACtB;AAaO,IAAM,gBAAgBF;AAAA,EAC3B,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,OAAO,eAAe,QAAQ;AACpC,UAAM,QAAQ,gBAAgB,QAAQ;AACtC,UAAM,cAAc,gBAAgB,QAAQ;AAC5C,UAAM,WAAW,SAAS,OAAO,KAAK;AAEtC,WACE,gBAAAG;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAWF;AAAA,UACT;AAAA,UACA,sBAAsB,OAAO;AAAA,UAC7B,sBAAsB,IAAI;AAAA,UAC1B;AAAA,QACF;AAAA,QACA,OAAO,EAAE,qBAAqB,YAAY;AAAA,QAC1C,cACE,YAAY,UAAU,SAClB,GAAG,KAAK,KAAK,MAAM,SAAS,QAAQ,SAAS,KAAK,aAAa,EAAE,KACjE;AAAA,QAEL,GAAG;AAAA,QAEJ;AAAA,0BAAAC,KAAC,UAAK,WAAU,2BACd,0BAAAA,KAAC,QAAK,MAAM,UAAU,GACxB;AAAA,UAEC,YAAY,eACX,gBAAAA,KAAC,UAAK,WAAU,4BAA4B,iBAAM;AAAA,UAGnD,YAAY,UAAU,UAAU,QAC/B,gBAAAC,MAAC,UAAK,WAAU,6BACd;AAAA,4BAAAD,KAAC,cAAW,OAAO,QAAQ,MAAK,MAAK;AAAA,YACpC,SAAS,QACR,gBAAAC,MAAC,UAAK,WAAU,4BAA2B;AAAA;AAAA,cAAE;AAAA,cAAM;AAAA,eAAC;AAAA,aAExD;AAAA;AAAA;AAAA,IAEJ;AAAA,EAEJ;AACF;AAEA,cAAc,cAAc;;;AC3J5B,SAAgB,cAAAH,aAAY,YAAAI,WAAU,mBAAmB;AACzD,SAAS,UAAU,kBAAkB;AACrC,OAAOH,WAAU;AAsDf,SACE,OAAAC,MADF,QAAAC,aAAA;AAnBF,SAAS,YAAY,KAAqB;AAExC,QAAM,UAAU,IAAI;AAAA,IAClB;AAAA,EACF;AACA,MAAI,QAAS,QAAO,0CAA0C,QAAQ,CAAC,CAAC;AAGxE,QAAM,aAAa,IAAI,MAAM,uBAAuB;AACpD,MAAI,WAAY,QAAO,kCAAkC,WAAW,CAAC,CAAC;AAGtE,QAAM,YAAY,IAAI,MAAM,sCAAsC;AAClE,MAAI,UAAW,QAAO,8BAA8B,UAAU,CAAC,CAAC;AAEhE,SAAO;AACT;AAEA,IAAM,WAAW,MACf,gBAAAA,MAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,OAAM,8BAA6B,eAAY,QACzG;AAAA,kBAAAD,KAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,WAAU,8BAA6B;AAAA,EACtE,gBAAAA,KAAC,UAAK,GAAE,0BAAyB,MAAK,SAAQ;AAAA,GAChD;AAGF,IAAM,YAAY,MAChB,gBAAAC,MAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACzJ;AAAA,kBAAAD,KAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK;AAAA,EACpC,gBAAAA,KAAC,UAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK;AAAA,GACtC;AAqBK,IAAM,gBAAgBF;AAAA,EAC3B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF,GACA,QACG;AACH,UAAM,CAAC,MAAM,OAAO,IAAII,UAAS,KAAK;AAEtC,UAAM,aAAa,YAAY,MAAM;AACnC,cAAQ,IAAI;AACZ,eAAS;AAAA,IACX,GAAG,CAAC,MAAM,CAAC;AAEX,WACE,gBAAAF,KAAC,SAAI,KAAU,WAAWD,MAAK,qBAAqB,SAAS,GAC3D,0BAAAE,MAAC,WAAW,MAAX,EAAgB,MAAY,cAAc,SACzC;AAAA,sBAAAD;AAAA,QAAC,WAAW;AAAA,QAAX;AAAA,UACC,WAAU;AAAA,UACV,SAAS;AAAA,UAER,qBACC,gBAAAC,MAAC,SAAI,WAAU,qCACZ;AAAA,2BACC,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,KAAK,gBAAgB,SAAS,KAAK;AAAA,gBACnC,WAAU;AAAA,gBACV,SAAQ;AAAA,gBACR,UAAS;AAAA;AAAA,YACX,IAEA,gBAAAA,KAAC,SAAI,WAAU,kCAAiC;AAAA,YAElD,gBAAAA,KAAC,SAAI,WAAU,8BACb,0BAAAA,KAAC,SAAI,WAAU,+BACb,0BAAAA,KAAC,YAAS,GACZ,GACF;AAAA,aACF;AAAA;AAAA,MAEJ;AAAA,MAEA,gBAAAC,MAAC,WAAW,QAAX,EACC;AAAA,wBAAAD,KAAC,WAAW,UAAX,EAAoB,WAAU,+BAA8B;AAAA,QAC7D,gBAAAC,MAAC,WAAW,OAAX,EAAiB,WAAU,4BAC1B;AAAA,0BAAAD,KAAC,WAAW,OAAX,EAAiB,WAAU,4BAA2B,cAAW,eAChE,0BAAAA,KAAC,aAAU,GACb;AAAA,UACA,gBAAAA,KAAC,WAAW,OAAX,EAAiB,WAAU,cAAc,iBAAM;AAAA,UAChD,gBAAAA,KAAC,SAAI,WAAU,kCACZ,kBACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK,YAAY,QAAQ;AAAA,cACzB;AAAA,cACA,WAAU;AAAA,cACV,OAAM;AAAA,cACN,iBAAe;AAAA;AAAA,UACjB,GAEJ;AAAA,WACF;AAAA,SACF;AAAA,OACF,GACF;AAAA,EAEJ;AACF;AAEA,cAAc,cAAc","sourcesContent":["import React, { forwardRef, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport './StarRating.css';\n\n/* ============================================================================\n STAR RATING\n ============================================================================\n Animated star display with half-star support, configurable sizes, and\n optional viewport-triggered fill animation. Replaces text-character\n star displays across all social proof surfaces.\n\n Strategic objective: Instant visual trust signal\n ============================================================================ */\n\nexport interface StarRatingProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Rating value (0-5, supports decimals for half-stars) */\n value: number;\n /** Maximum number of stars */\n max?: number;\n /** Star size variant */\n size?: 'sm' | 'md' | 'lg';\n /** Animate stars filling on viewport entry */\n animate?: boolean;\n /** Show numeric value next to stars */\n showValue?: boolean;\n /** Label shown after value (e.g., \"/ 5\") */\n valueLabel?: string;\n}\n\n/**\n * Star rating display with gold-fill, half-star support, and optional\n * entrance animation. Uses inline SVGs with clip-path for precise\n * partial fills.\n *\n * @example\n * ```tsx\n * <StarRating value={4.5} size=\"lg\" animate />\n * <StarRating value={5} size=\"sm\" showValue />\n * ```\n */\nexport const StarRating = forwardRef<HTMLDivElement, StarRatingProps>(\n (\n {\n value,\n max = 5,\n size = 'md',\n animate = false,\n showValue = false,\n valueLabel,\n className,\n ...props\n },\n ref,\n ) => {\n const [visible, setVisible] = useState(!animate);\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!animate) return;\n\n // Respect reduced motion\n if (typeof window !== 'undefined') {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n if (mq.matches) {\n setVisible(true);\n return;\n }\n }\n\n const target = containerRef.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.5 },\n );\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [animate]);\n\n const clampedValue = Math.min(Math.max(value, 0), max);\n\n const stars = Array.from({ length: max }, (_, i) => {\n const starIndex = i + 1;\n const fillPercent =\n clampedValue >= starIndex\n ? 100\n : clampedValue > starIndex - 1\n ? (clampedValue - (starIndex - 1)) * 100\n : 0;\n\n return (\n <span\n key={i}\n className={clsx(\n 'ds-star-rating__star',\n visible && 'ds-star-rating__star--visible',\n )}\n style={{\n '--star-fill': `${fillPercent}%`,\n '--star-delay': animate ? `${i * 100}ms` : '0ms',\n } as React.CSSProperties}\n >\n {/* Background (empty) star */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__empty\"\n />\n </svg>\n {/* Foreground (filled) star with clip */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg ds-star-rating__svg--filled\"\n aria-hidden=\"true\"\n style={{\n clipPath: `inset(0 ${100 - fillPercent}% 0 0)`,\n }}\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__filled\"\n />\n </svg>\n </span>\n );\n });\n\n return (\n <div\n ref={(node) => {\n (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }}\n className={clsx('ds-star-rating', `ds-star-rating--${size}`, className)}\n role=\"img\"\n aria-label={`${clampedValue} out of ${max} stars`}\n {...props}\n >\n <span className=\"ds-star-rating__stars\">{stars}</span>\n {showValue && (\n <span className=\"ds-star-rating__value\">\n {clampedValue.toLocaleString(undefined, {\n minimumFractionDigits: clampedValue % 1 !== 0 ? 1 : 0,\n maximumFractionDigits: 1,\n })}\n {valueLabel && (\n <span className=\"ds-star-rating__value-label\">{valueLabel}</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nStarRating.displayName = 'StarRating';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { StarRating } from '../StarRating/StarRating';\nimport './PlatformBadge.css';\n\n/* ============================================================================\n PLATFORM BADGE\n ============================================================================\n Recognizable review platform icons (Trustpilot, G2, Google, Chrome Web\n Store) with optional rating display. Builds instant trust through visual\n brand recognition.\n\n Strategic objective: Third-party authority signal\n ============================================================================ */\n\nexport type Platform = 'trustpilot' | 'g2' | 'google' | 'chrome-web-store';\n\nexport interface PlatformBadgeProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Review platform */\n platform: Platform;\n /** Display mode */\n variant?: 'icon-only' | 'icon-label' | 'full';\n /** Platform rating (used in 'full' variant) */\n rating?: number;\n /** Number of reviews (used in 'full' variant) */\n count?: number;\n /** Size variant */\n size?: 'sm' | 'md';\n}\n\n/* --------------------------------------------------------------------------\n Platform SVG Icons — inline for zero-latency rendering\n -------------------------------------------------------------------------- */\n\nconst TrustpilotIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2l2.9 8.9H24l-7.35 5.34 2.81 8.64L12 19.56l-7.46 5.32 2.81-8.64L0 10.9h9.1L12 2z\" fill=\"#00B67A\" />\n </svg>\n);\n\nconst G2Icon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z\" fill=\"#FF492C\" />\n <text x=\"50%\" y=\"54%\" dominantBaseline=\"middle\" textAnchor=\"middle\" fill=\"white\" fontWeight=\"bold\" fontSize=\"11\" fontFamily=\"Arial, sans-serif\">G2</text>\n </svg>\n);\n\nconst GoogleIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" fill=\"#4285F4\"/>\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 001 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z\" fill=\"#FBBC05\"/>\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\n </svg>\n);\n\nconst ChromeWebStoreIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#4285F4\" opacity=\"0.1\" stroke=\"#4285F4\" strokeWidth=\"1.5\"/>\n <path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z\" fill=\"#4285F4\"/>\n <path d=\"M7.5 10.5L4 5.5\" stroke=\"#EA4335\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M16.5 10.5L20 5.5\" stroke=\"#FBBC05\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M12 17l-3.5 5\" stroke=\"#34A853\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n </svg>\n);\n\nconst PLATFORM_ICONS: Record<Platform, React.FC<{ size: number }>> = {\n trustpilot: TrustpilotIcon,\n g2: G2Icon,\n google: GoogleIcon,\n 'chrome-web-store': ChromeWebStoreIcon,\n};\n\nconst PLATFORM_LABELS: Record<Platform, string> = {\n trustpilot: 'Trustpilot',\n g2: 'G2',\n google: 'Google',\n 'chrome-web-store': 'Chrome Web Store',\n};\n\nconst PLATFORM_COLORS: Record<Platform, string> = {\n trustpilot: '#00B67A',\n g2: '#FF492C',\n google: '#4285F4',\n 'chrome-web-store': '#4285F4',\n};\n\n/**\n * Review platform badge with recognizable brand icon, optional label,\n * and optional rating display.\n *\n * @example\n * ```tsx\n * <PlatformBadge platform=\"trustpilot\" variant=\"icon-only\" />\n * <PlatformBadge platform=\"g2\" variant=\"icon-label\" />\n * <PlatformBadge platform=\"google\" variant=\"full\" rating={4.9} count={42} />\n * ```\n */\nexport const PlatformBadge = forwardRef<HTMLDivElement, PlatformBadgeProps>(\n (\n {\n platform,\n variant = 'icon-label',\n rating,\n count,\n size = 'sm',\n className,\n ...props\n },\n ref,\n ) => {\n const Icon = PLATFORM_ICONS[platform];\n const label = PLATFORM_LABELS[platform];\n const accentColor = PLATFORM_COLORS[platform];\n const iconSize = size === 'sm' ? 18 : 22;\n\n return (\n <div\n ref={ref}\n role=\"img\"\n className={clsx(\n 'ds-platform-badge',\n `ds-platform-badge--${variant}`,\n `ds-platform-badge--${size}`,\n className,\n )}\n style={{ '--platform-accent': accentColor } as React.CSSProperties}\n aria-label={\n variant === 'full' && rating\n ? `${label}: ${rating} stars${count ? ` from ${count} reviews` : ''}`\n : label\n }\n {...props}\n >\n <span className=\"ds-platform-badge__icon\">\n <Icon size={iconSize} />\n </span>\n\n {variant !== 'icon-only' && (\n <span className=\"ds-platform-badge__label\">{label}</span>\n )}\n\n {variant === 'full' && rating != null && (\n <span className=\"ds-platform-badge__rating\">\n <StarRating value={rating} size=\"sm\" />\n {count != null && (\n <span className=\"ds-platform-badge__count\">({count})</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nPlatformBadge.displayName = 'PlatformBadge';\n","import React, { forwardRef, useState, useCallback } from 'react';\nimport { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport clsx from 'clsx';\nimport './VideoLightbox.css';\n\n/* ============================================================================\n VIDEO LIGHTBOX\n ============================================================================\n Modal overlay video player built on @base-ui/react Dialog. Opens a lazy\n iframe embed (YouTube/Vimeo/Loom) with backdrop blur, keyboard navigation,\n and focus trap. Designed for inline playback from thumbnail cards without\n navigating away from the current page.\n\n Strategic objective: Engagement — keep users on the Wall of Love\n ============================================================================ */\n\nexport interface VideoLightboxProps {\n /** Video embed URL (YouTube, Vimeo, Loom) */\n videoUrl: string;\n /** Poster image shown as thumbnail trigger */\n thumbnailSrc?: string;\n /** Alt text for thumbnail */\n thumbnailAlt?: string;\n /** Video title for accessibility */\n title?: string;\n /** Custom trigger element (overrides default thumbnail + play button) */\n trigger?: React.ReactNode;\n /** Additional class name for the trigger wrapper */\n className?: string;\n /** Callback when video starts playing */\n onPlay?: () => void;\n}\n\n/* --------------------------------------------------------------------------\n Helpers\n -------------------------------------------------------------------------- */\n\nfunction getEmbedUrl(src: string): string {\n // YouTube\n const ytMatch = src.match(\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([a-zA-Z0-9_-]+)/,\n );\n if (ytMatch) return `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?autoplay=1&rel=0`;\n\n // Vimeo\n const vimeoMatch = src.match(/(?:vimeo\\.com\\/)(\\d+)/);\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`;\n\n // Loom\n const loomMatch = src.match(/(?:loom\\.com\\/share\\/)([a-zA-Z0-9]+)/);\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}?autoplay=1`;\n\n return src;\n}\n\nconst PlayIcon = () => (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"32\" className=\"ds-video-lightbox__play-bg\" />\n <path d=\"M26 20v24l20-12L26 20z\" fill=\"white\" />\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\n/**\n * Modal video player triggered by a thumbnail or custom element.\n * Opens a full-screen overlay with lazy iframe injection.\n *\n * @example\n * ```tsx\n * <VideoLightbox\n * videoUrl=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n * thumbnailSrc=\"/thumbnails/demo.jpg\"\n * title=\"Customer testimonial\"\n * />\n *\n * <VideoLightbox\n * videoUrl=\"https://vimeo.com/123456789\"\n * trigger={<Button>Watch Video</Button>}\n * />\n * ```\n */\nexport const VideoLightbox = forwardRef<HTMLDivElement, VideoLightboxProps>(\n (\n {\n videoUrl,\n thumbnailSrc,\n thumbnailAlt,\n title = 'Video',\n trigger,\n className,\n onPlay,\n },\n ref,\n ) => {\n const [open, setOpen] = useState(false);\n\n const handleOpen = useCallback(() => {\n setOpen(true);\n onPlay?.();\n }, [onPlay]);\n\n return (\n <div ref={ref} className={clsx('ds-video-lightbox', className)}>\n <BaseDialog.Root open={open} onOpenChange={setOpen}>\n <BaseDialog.Trigger\n className=\"ds-video-lightbox__trigger\"\n onClick={handleOpen}\n >\n {trigger || (\n <div className=\"ds-video-lightbox__thumbnail-wrap\">\n {thumbnailSrc ? (\n <img\n src={thumbnailSrc}\n alt={thumbnailAlt || `Play: ${title}`}\n className=\"ds-video-lightbox__thumbnail\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <div className=\"ds-video-lightbox__placeholder\" />\n )}\n <div className=\"ds-video-lightbox__overlay\">\n <div className=\"ds-video-lightbox__play-btn\">\n <PlayIcon />\n </div>\n </div>\n </div>\n )}\n </BaseDialog.Trigger>\n\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className=\"ds-video-lightbox__backdrop\" />\n <BaseDialog.Popup className=\"ds-video-lightbox__popup\">\n <BaseDialog.Close className=\"ds-video-lightbox__close\" aria-label=\"Close video\">\n <CloseIcon />\n </BaseDialog.Close>\n <BaseDialog.Title className=\"ds-sr-only\">{title}</BaseDialog.Title>\n <div className=\"ds-video-lightbox__iframe-wrap\">\n {open && (\n <iframe\n src={getEmbedUrl(videoUrl)}\n title={title}\n className=\"ds-video-lightbox__iframe\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n )}\n </div>\n </BaseDialog.Popup>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n </div>\n );\n },\n);\n\nVideoLightbox.displayName = 'VideoLightbox';\n"]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/components/CursorSpotlight/CursorSpotlight.tsx
|
|
5
|
+
var CursorSpotlight = ({
|
|
6
|
+
children,
|
|
7
|
+
color = "var(--accent-rgb)",
|
|
8
|
+
size = 400,
|
|
9
|
+
opacity = 0.15,
|
|
10
|
+
borderGlow = true,
|
|
11
|
+
className,
|
|
12
|
+
disabled = false
|
|
13
|
+
}) => {
|
|
14
|
+
const containerRef = useRef(null);
|
|
15
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
16
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
17
|
+
const [reducedMotion, setReducedMotion] = useState(false);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
20
|
+
setReducedMotion(mediaQuery.matches);
|
|
21
|
+
const handler = (e) => setReducedMotion(e.matches);
|
|
22
|
+
mediaQuery.addEventListener("change", handler);
|
|
23
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
24
|
+
}, []);
|
|
25
|
+
const isDisabled = disabled || reducedMotion;
|
|
26
|
+
const handleMouseMove = useCallback(
|
|
27
|
+
(e) => {
|
|
28
|
+
if (!containerRef.current || isDisabled) return;
|
|
29
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
30
|
+
setPosition({
|
|
31
|
+
x: e.clientX - rect.left,
|
|
32
|
+
y: e.clientY - rect.top
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
[isDisabled]
|
|
36
|
+
);
|
|
37
|
+
const handleMouseEnter = useCallback(() => {
|
|
38
|
+
if (!isDisabled) setIsHovering(true);
|
|
39
|
+
}, [isDisabled]);
|
|
40
|
+
const handleMouseLeave = useCallback(() => {
|
|
41
|
+
setIsHovering(false);
|
|
42
|
+
}, []);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const container = containerRef.current;
|
|
45
|
+
if (!container || isDisabled) return;
|
|
46
|
+
container.addEventListener("mousemove", handleMouseMove);
|
|
47
|
+
container.addEventListener("mouseenter", handleMouseEnter);
|
|
48
|
+
container.addEventListener("mouseleave", handleMouseLeave);
|
|
49
|
+
return () => {
|
|
50
|
+
container.removeEventListener("mousemove", handleMouseMove);
|
|
51
|
+
container.removeEventListener("mouseenter", handleMouseEnter);
|
|
52
|
+
container.removeEventListener("mouseleave", handleMouseLeave);
|
|
53
|
+
};
|
|
54
|
+
}, [handleMouseMove, handleMouseEnter, handleMouseLeave, isDisabled]);
|
|
55
|
+
const spotlightColor = color.startsWith("var(") ? `rgba(${color.replace("var(", "").replace(")", "")}, ${opacity})` : color.includes(",") ? `rgba(${color}, ${opacity})` : color;
|
|
56
|
+
const borderColor = color.startsWith("var(") ? `rgba(${color.replace("var(", "").replace(")", "")}, 0.5)` : color.includes(",") ? `rgba(${color}, 0.5)` : color;
|
|
57
|
+
return /* @__PURE__ */ jsxs(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
ref: containerRef,
|
|
61
|
+
className: `ds-cursor-spotlight ${className || ""}`,
|
|
62
|
+
style: {
|
|
63
|
+
"--spotlight-x": `${position.x}px`,
|
|
64
|
+
"--spotlight-y": `${position.y}px`,
|
|
65
|
+
"--spotlight-size": `${size}px`,
|
|
66
|
+
"--spotlight-color": spotlightColor,
|
|
67
|
+
"--spotlight-border-color": borderColor,
|
|
68
|
+
"--spotlight-opacity": isHovering ? 1 : 0
|
|
69
|
+
},
|
|
70
|
+
"data-hovering": isHovering,
|
|
71
|
+
"data-border-glow": borderGlow,
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx("div", { className: "ds-cursor-spotlight__glow", "aria-hidden": "true" }),
|
|
74
|
+
borderGlow && /* @__PURE__ */ jsx("div", { className: "ds-cursor-spotlight__border", "aria-hidden": "true" }),
|
|
75
|
+
/* @__PURE__ */ jsx("div", { className: "ds-cursor-spotlight__content", children })
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
CursorSpotlight.displayName = "CursorSpotlight";
|
|
81
|
+
var SpotlightCard = ({
|
|
82
|
+
title,
|
|
83
|
+
description,
|
|
84
|
+
icon,
|
|
85
|
+
children,
|
|
86
|
+
onClick,
|
|
87
|
+
...spotlightProps
|
|
88
|
+
}) => {
|
|
89
|
+
return /* @__PURE__ */ jsx(CursorSpotlight, { ...spotlightProps, className: "ds-spotlight-card", children: /* @__PURE__ */ jsxs(
|
|
90
|
+
"div",
|
|
91
|
+
{
|
|
92
|
+
className: "ds-spotlight-card__inner",
|
|
93
|
+
onClick,
|
|
94
|
+
role: onClick ? "button" : void 0,
|
|
95
|
+
tabIndex: onClick ? 0 : void 0,
|
|
96
|
+
onKeyDown: onClick ? (e) => {
|
|
97
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
onClick();
|
|
100
|
+
}
|
|
101
|
+
} : void 0,
|
|
102
|
+
children: [
|
|
103
|
+
icon && /* @__PURE__ */ jsx("div", { className: "ds-spotlight-card__icon", children: icon }),
|
|
104
|
+
title && /* @__PURE__ */ jsx("h3", { className: "ds-spotlight-card__title", children: title }),
|
|
105
|
+
description && /* @__PURE__ */ jsx("p", { className: "ds-spotlight-card__description", children: description }),
|
|
106
|
+
children
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
) });
|
|
110
|
+
};
|
|
111
|
+
SpotlightCard.displayName = "SpotlightCard";
|
|
112
|
+
|
|
113
|
+
export { CursorSpotlight, SpotlightCard };
|
|
114
|
+
//# sourceMappingURL=out.js.map
|
|
115
|
+
//# sourceMappingURL=chunk-WB6XDNU7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/CursorSpotlight/CursorSpotlight.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useRef, useState, useCallback } from 'react';\nimport './CursorSpotlight.css';\n\n/* ============================================================================\n CURSOR SPOTLIGHT — \"The Glow\" Effect\n ============================================================================\n \n A mouse-following spotlight effect that creates a premium, interactive feel\n on cards, buttons, and other surfaces. The spotlight reveals a subtle glow\n that follows the cursor position.\n \n Usage:\n <CursorSpotlight>\n <YourCard />\n </CursorSpotlight>\n \n ============================================================================ */\n\nexport interface CursorSpotlightProps {\n /** Children to wrap with spotlight effect */\n children: React.ReactNode;\n /** Spotlight color (CSS color or rgb values) */\n color?: string;\n /** Spotlight size in pixels */\n size?: number;\n /** Spotlight opacity (0-1) */\n opacity?: number;\n /** Enable border glow effect */\n borderGlow?: boolean;\n /** Custom class name */\n className?: string;\n /** Disable the effect */\n disabled?: boolean;\n}\n\nexport const CursorSpotlight: React.FC<CursorSpotlightProps> = ({\n children,\n color = 'var(--accent-rgb)',\n size = 400,\n opacity = 0.15,\n borderGlow = true,\n className,\n disabled = false,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isHovering, setIsHovering] = useState(false);\n const [reducedMotion, setReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReducedMotion(mediaQuery.matches);\n\n const handler = (e: MediaQueryListEvent) => setReducedMotion(e.matches);\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }, []);\n\n const isDisabled = disabled || reducedMotion;\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!containerRef.current || isDisabled) return;\n\n const rect = containerRef.current.getBoundingClientRect();\n setPosition({\n x: e.clientX - rect.left,\n y: e.clientY - rect.top,\n });\n },\n [isDisabled],\n );\n\n const handleMouseEnter = useCallback(() => {\n if (!isDisabled) setIsHovering(true);\n }, [isDisabled]);\n\n const handleMouseLeave = useCallback(() => {\n setIsHovering(false);\n }, []);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container || isDisabled) return;\n\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('mouseenter', handleMouseEnter);\n container.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeEventListener('mouseenter', handleMouseEnter);\n container.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [handleMouseMove, handleMouseEnter, handleMouseLeave, isDisabled]);\n\n // Parse color to handle both CSS variables and rgb values\n const spotlightColor = color.startsWith('var(')\n ? `rgba(${color.replace('var(', '').replace(')', '')}, ${opacity})`\n : color.includes(',')\n ? `rgba(${color}, ${opacity})`\n : color;\n\n const borderColor = color.startsWith('var(')\n ? `rgba(${color.replace('var(', '').replace(')', '')}, 0.5)`\n : color.includes(',')\n ? `rgba(${color}, 0.5)`\n : color;\n\n return (\n <div\n ref={containerRef}\n className={`ds-cursor-spotlight ${className || ''}`}\n style={\n {\n '--spotlight-x': `${position.x}px`,\n '--spotlight-y': `${position.y}px`,\n '--spotlight-size': `${size}px`,\n '--spotlight-color': spotlightColor,\n '--spotlight-border-color': borderColor,\n '--spotlight-opacity': isHovering ? 1 : 0,\n } as React.CSSProperties\n }\n data-hovering={isHovering}\n data-border-glow={borderGlow}\n >\n {/* Spotlight overlay */}\n <div className=\"ds-cursor-spotlight__glow\" aria-hidden=\"true\" />\n\n {/* Border glow */}\n {borderGlow && <div className=\"ds-cursor-spotlight__border\" aria-hidden=\"true\" />}\n\n {/* Content */}\n <div className=\"ds-cursor-spotlight__content\">{children}</div>\n </div>\n );\n};\n\nCursorSpotlight.displayName = 'CursorSpotlight';\n\n/* ============================================================================\n SPOTLIGHT CARD — Pre-styled card with spotlight effect\n ============================================================================ */\n\nexport interface SpotlightCardProps extends Omit<CursorSpotlightProps, 'children'> {\n /** Card title */\n title?: string;\n /** Card description */\n description?: string;\n /** Card icon */\n icon?: React.ReactNode;\n /** Card children */\n children?: React.ReactNode;\n /** Click handler */\n onClick?: () => void;\n}\n\nexport const SpotlightCard: React.FC<SpotlightCardProps> = ({\n title,\n description,\n icon,\n children,\n onClick,\n ...spotlightProps\n}) => {\n return (\n <CursorSpotlight {...spotlightProps} className=\"ds-spotlight-card\">\n <div\n className=\"ds-spotlight-card__inner\"\n onClick={onClick}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n onKeyDown={\n onClick\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick();\n }\n }\n : undefined\n }\n >\n {icon && <div className=\"ds-spotlight-card__icon\">{icon}</div>}\n {title && <h3 className=\"ds-spotlight-card__title\">{title}</h3>}\n {description && <p className=\"ds-spotlight-card__description\">{description}</p>}\n {children}\n </div>\n </CursorSpotlight>\n );\n};\n\nSpotlightCard.displayName = 'SpotlightCard';\n"]}
|