@planetaexo/design-system 0.93.1 → 0.94.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/dist/index.cjs CHANGED
@@ -20231,10 +20231,18 @@ function ReviewsSpotlight({
20231
20231
  intervalMs = 7e3,
20232
20232
  className
20233
20233
  }) {
20234
+ const trackRef = React20__namespace.useRef(null);
20234
20235
  const [index, setIndex] = React20__namespace.useState(0);
20235
- const [visible, setVisible] = React20__namespace.useState(true);
20236
20236
  const reduced = React20__namespace.useRef(false);
20237
- const swapRef = React20__namespace.useRef(null);
20237
+ const hoverRef = React20__namespace.useRef(false);
20238
+ const focusRef = React20__namespace.useRef(false);
20239
+ const interactRef = React20__namespace.useRef(false);
20240
+ const programmaticRef = React20__namespace.useRef(false);
20241
+ const rafRef = React20__namespace.useRef(null);
20242
+ const interactTimer = React20__namespace.useRef(null);
20243
+ const programmaticTimer = React20__namespace.useRef(
20244
+ null
20245
+ );
20238
20246
  React20__namespace.useEffect(() => {
20239
20247
  var _a;
20240
20248
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
@@ -20248,56 +20256,107 @@ function ReviewsSpotlight({
20248
20256
  return (_a2 = mq.removeEventListener) == null ? void 0 : _a2.call(mq, "change", onChange);
20249
20257
  };
20250
20258
  }, []);
20251
- const goTo = React20__namespace.useCallback((resolve) => {
20252
- if (reduced.current) {
20253
- setIndex((i) => resolve(i));
20254
- return;
20259
+ const scrollToIndex = React20__namespace.useCallback((i) => {
20260
+ const el = trackRef.current;
20261
+ if (!el) return;
20262
+ const smooth = !reduced.current;
20263
+ programmaticRef.current = true;
20264
+ el.scrollTo({ left: i * el.clientWidth, behavior: smooth ? "smooth" : "auto" });
20265
+ if (programmaticTimer.current) clearTimeout(programmaticTimer.current);
20266
+ programmaticTimer.current = setTimeout(
20267
+ () => {
20268
+ programmaticRef.current = false;
20269
+ },
20270
+ smooth ? 600 : 50
20271
+ );
20272
+ }, []);
20273
+ const onScroll = React20__namespace.useCallback(() => {
20274
+ const el = trackRef.current;
20275
+ if (!el) return;
20276
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
20277
+ rafRef.current = requestAnimationFrame(() => {
20278
+ const i = Math.round(el.scrollLeft / Math.max(1, el.clientWidth));
20279
+ setIndex((prev) => prev === i ? prev : i);
20280
+ });
20281
+ if (!programmaticRef.current) {
20282
+ interactRef.current = true;
20283
+ if (interactTimer.current) clearTimeout(interactTimer.current);
20284
+ interactTimer.current = setTimeout(() => {
20285
+ interactRef.current = false;
20286
+ }, 1500);
20255
20287
  }
20256
- setVisible(false);
20257
- swapRef.current = setTimeout(() => {
20258
- setIndex((i) => resolve(i));
20259
- setVisible(true);
20260
- }, 260);
20261
20288
  }, []);
20262
20289
  React20__namespace.useEffect(() => {
20263
20290
  if (items.length <= 1 || intervalMs <= 0) return;
20264
20291
  const id = setInterval(() => {
20265
- if (!reduced.current) goTo((i) => (i + 1) % items.length);
20292
+ if (reduced.current) return;
20293
+ if (hoverRef.current || focusRef.current || interactRef.current) return;
20294
+ const el = trackRef.current;
20295
+ if (!el) return;
20296
+ const cur = Math.round(el.scrollLeft / Math.max(1, el.clientWidth));
20297
+ scrollToIndex((cur + 1) % items.length);
20266
20298
  }, intervalMs);
20267
- return () => {
20268
- clearInterval(id);
20269
- if (swapRef.current) clearTimeout(swapRef.current);
20270
- };
20271
- }, [items.length, intervalMs, goTo]);
20299
+ return () => clearInterval(id);
20300
+ }, [items.length, intervalMs, scrollToIndex]);
20301
+ React20__namespace.useEffect(
20302
+ () => () => {
20303
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
20304
+ if (interactTimer.current) clearTimeout(interactTimer.current);
20305
+ if (programmaticTimer.current) clearTimeout(programmaticTimer.current);
20306
+ },
20307
+ []
20308
+ );
20272
20309
  if (!items.length) return null;
20273
- const safe = Math.min(index, items.length - 1);
20274
- const review = items[safe];
20310
+ const active = Math.min(index, items.length - 1);
20275
20311
  return /* @__PURE__ */ jsxRuntime.jsx(
20276
20312
  "section",
20277
20313
  {
20278
20314
  className: cn(SURFACE_PRIMARY_9002, "py-24 text-white sm:py-32", className),
20315
+ onMouseEnter: () => {
20316
+ hoverRef.current = true;
20317
+ },
20318
+ onMouseLeave: () => {
20319
+ hoverRef.current = false;
20320
+ },
20321
+ onFocus: () => {
20322
+ focusRef.current = true;
20323
+ },
20324
+ onBlur: () => {
20325
+ focusRef.current = false;
20326
+ },
20279
20327
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl px-6 text-center sm:px-8", children: [
20280
20328
  eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-ui text-xs font-bold uppercase tracking-[0.22em] text-primary", children: eyebrow }),
20281
20329
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: title }),
20282
- /* @__PURE__ */ jsxRuntime.jsxs(
20330
+ /* @__PURE__ */ jsxRuntime.jsx(
20283
20331
  "div",
20284
20332
  {
20285
- className: cn(
20286
- "transition-opacity duration-300",
20287
- visible ? "opacity-100" : "opacity-0"
20288
- ),
20289
- children: [
20290
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TrustpilotStars, { stars: review.stars, className: "h-7 w-7" }) }),
20291
- /* @__PURE__ */ jsxRuntime.jsxs("blockquote", { className: "mx-auto mt-8 max-w-[20ch] font-sans text-3xl font-normal italic leading-[1.3] text-white sm:text-4xl", children: [
20292
- "\u201C",
20293
- review.quote,
20294
- "\u201D"
20295
- ] }),
20296
- (review.author || review.location) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6", children: [
20297
- review.author && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-ui text-base font-bold tracking-wide text-white", children: review.author }),
20298
- review.location && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-sans text-sm text-white/55", children: review.location })
20299
- ] })
20300
- ]
20333
+ ref: trackRef,
20334
+ onScroll,
20335
+ "aria-roledescription": "carousel",
20336
+ "aria-label": title,
20337
+ className: "flex snap-x snap-mandatory overflow-x-auto scrollbar-none",
20338
+ children: items.map((review, i) => /* @__PURE__ */ jsxRuntime.jsxs(
20339
+ "div",
20340
+ {
20341
+ role: "group",
20342
+ "aria-roledescription": "slide",
20343
+ "aria-label": `Review ${i + 1} of ${items.length}`,
20344
+ className: "w-full shrink-0 snap-start",
20345
+ children: [
20346
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TrustpilotStars, { stars: review.stars, className: "h-7 w-7" }) }),
20347
+ /* @__PURE__ */ jsxRuntime.jsxs("blockquote", { className: "mx-auto mt-8 flex min-h-[15rem] max-w-[20ch] items-center justify-center font-sans text-3xl font-normal italic leading-[1.3] text-white sm:min-h-[18rem] sm:text-4xl", children: [
20348
+ "\u201C",
20349
+ review.quote,
20350
+ "\u201D"
20351
+ ] }),
20352
+ (review.author || review.location) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6", children: [
20353
+ review.author && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-ui text-base font-bold tracking-wide text-white", children: review.author }),
20354
+ review.location && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-sans text-sm text-white/55", children: review.location })
20355
+ ] })
20356
+ ]
20357
+ },
20358
+ i
20359
+ ))
20301
20360
  }
20302
20361
  ),
20303
20362
  items.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-10 flex justify-center gap-2.5", children: items.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -20305,11 +20364,11 @@ function ReviewsSpotlight({
20305
20364
  {
20306
20365
  type: "button",
20307
20366
  "aria-label": `Show review ${i + 1} of ${items.length}`,
20308
- "aria-current": i === safe,
20309
- onClick: () => goTo(() => i),
20367
+ "aria-current": i === active,
20368
+ onClick: () => scrollToIndex(i),
20310
20369
  className: cn(
20311
20370
  "h-2 w-2 rounded-full transition-colors",
20312
- i === safe ? "bg-primary" : "bg-white/25 hover:bg-white/45"
20371
+ i === active ? "bg-primary" : "bg-white/25 hover:bg-white/45"
20313
20372
  )
20314
20373
  },
20315
20374
  i