@reblu/site-blocks 0.18.0 → 0.19.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/client.js CHANGED
@@ -183,7 +183,7 @@ function Collection({
183
183
  ] })
184
184
  ] });
185
185
  }
186
- var PAGE_BTN_CLASS = "inline-flex h-9 items-center rounded-md border border-border px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
186
+ var PAGE_BTN_CLASS = "inline-flex h-9 cursor-pointer items-center rounded-md border border-border px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
187
187
  function FacetChip({ label, pressed, color, onClick }) {
188
188
  return /* @__PURE__ */ jsx(
189
189
  "button",
@@ -193,7 +193,7 @@ function FacetChip({ label, pressed, color, onClick }) {
193
193
  onClick,
194
194
  style: color ? { "--facet-color": color } : void 0,
195
195
  className: cn(
196
- "inline-flex items-center rounded-full border px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
196
+ "inline-flex cursor-pointer items-center rounded-full border px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
197
197
  pressed ? color ? "border-transparent bg-[var(--facet-color)] text-white" : "border-transparent bg-primary text-primary-foreground" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
198
198
  ),
199
199
  children: label
@@ -278,7 +278,7 @@ function ShareButtons({ url, title, className, labels }) {
278
278
  {
279
279
  type: "button",
280
280
  onClick: handleCopy,
281
- className: cn2(LINK_CLASS, "w-auto gap-2 px-3", copied && "border-primary text-primary"),
281
+ className: cn2(LINK_CLASS, "w-auto cursor-pointer gap-2 px-3", copied && "border-primary text-primary"),
282
282
  "aria-label": t.copy,
283
283
  children: [
284
284
  copied ? /* @__PURE__ */ jsx2(CheckIcon, {}) : /* @__PURE__ */ jsx2(CopyIcon, {}),
@@ -399,7 +399,7 @@ function AddToCartButton({
399
399
  "data-slot": "add-to-cart-button",
400
400
  "data-status": status,
401
401
  className: cn3(
402
- "inline-flex items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
402
+ "inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
403
403
  isDisabled ? "cursor-not-allowed bg-muted text-muted-foreground" : "bg-primary text-primary-foreground hover:bg-primary/90",
404
404
  status === "error" && "bg-destructive text-destructive-foreground",
405
405
  className
@@ -484,13 +484,216 @@ function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }) {
484
484
  );
485
485
  }
486
486
 
487
- // src/components/data-request-form.tsx
488
- import { useEffect as useEffect3, useId, useRef as useRef3, useState as useState5 } from "react";
487
+ // src/components/product-carousel.tsx
488
+ import { useCallback as useCallback2, useEffect as useEffect3, useId, useRef as useRef3, useState as useState5 } from "react";
489
+ import { buttonVariants } from "@reblu/site-ui/button-variants";
490
+ import { cn as cn6 } from "@reblu/site-ui/utils";
491
+
492
+ // src/components/_heading.tsx
493
+ import { jsx as jsx5 } from "react/jsx-runtime";
494
+ function Heading({
495
+ level,
496
+ className,
497
+ children
498
+ }) {
499
+ const Tag = `h${level}`;
500
+ return /* @__PURE__ */ jsx5(Tag, { className, children });
501
+ }
502
+
503
+ // src/components/product-card.tsx
489
504
  import { cn as cn5 } from "@reblu/site-ui/utils";
505
+
506
+ // src/lib/format-price.ts
507
+ function formatPrice(cents, currency = "EUR", locale = "es-ES") {
508
+ return new Intl.NumberFormat(locale, {
509
+ style: "currency",
510
+ currency
511
+ }).format(cents / 100);
512
+ }
513
+
514
+ // src/components/product-card.tsx
515
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
516
+ function ProductCard({ product, href, currencyCode = "EUR", locale, className }) {
517
+ const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
518
+ const hasDiscount = product.comparePrice != null && product.comparePrice > product.price;
519
+ return /* @__PURE__ */ jsxs4(
520
+ "article",
521
+ {
522
+ className: cn5(
523
+ "group flex flex-col overflow-hidden rounded-lg border bg-card transition-shadow hover:shadow-lg",
524
+ className
525
+ ),
526
+ "data-slot": "product-card",
527
+ children: [
528
+ /* @__PURE__ */ jsx6("div", { className: "relative aspect-square overflow-hidden bg-muted", children: image ? /* @__PURE__ */ jsx6(
529
+ "img",
530
+ {
531
+ src: image.url,
532
+ alt: image.alt ?? product.name,
533
+ loading: "lazy",
534
+ className: "h-full w-full object-cover transition-transform group-hover:scale-105"
535
+ }
536
+ ) : /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center text-4xl text-muted-foreground/30", "aria-hidden": "true", children: "\u{1F6CD}\uFE0F" }) }),
537
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-1 flex-col gap-2 p-4", children: [
538
+ product.category && /* @__PURE__ */ jsx6("span", { className: "inline-flex w-fit items-center rounded-full bg-secondary px-2.5 py-0.5 text-xs font-medium text-secondary-foreground", children: product.category.name }),
539
+ /* @__PURE__ */ jsx6("h3", { className: "text-base font-semibold leading-tight", children: href ? /* @__PURE__ */ jsx6("a", { href, className: "transition-colors hover:text-primary", children: product.name }) : product.name }),
540
+ product.shortDescription && /* @__PURE__ */ jsx6("p", { className: "line-clamp-2 text-sm text-muted-foreground", children: product.shortDescription }),
541
+ /* @__PURE__ */ jsxs4("div", { className: "mt-auto flex items-baseline gap-2 pt-2", children: [
542
+ /* @__PURE__ */ jsx6("span", { className: "text-lg font-bold text-foreground", children: formatPrice(product.price, currencyCode, locale) }),
543
+ hasDiscount && /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground line-through", children: formatPrice(product.comparePrice, currencyCode, locale) })
544
+ ] })
545
+ ] })
546
+ ]
547
+ }
548
+ );
549
+ }
550
+
551
+ // src/components/product-carousel.tsx
552
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
553
+ var DEFAULT_PRODUCT_CAROUSEL_LABELS = {
554
+ region: "Productos relacionados",
555
+ roleDescription: "carrusel",
556
+ previous: "Anterior",
557
+ next: "Siguiente"
558
+ };
559
+ var EDGE_EPSILON = 1;
560
+ function prefersReducedMotion() {
561
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false;
562
+ }
563
+ function ChevronIcon({ direction }) {
564
+ return /* @__PURE__ */ jsx7(
565
+ "svg",
566
+ {
567
+ "aria-hidden": "true",
568
+ viewBox: "0 0 24 24",
569
+ fill: "none",
570
+ stroke: "currentColor",
571
+ strokeWidth: 2,
572
+ strokeLinecap: "round",
573
+ strokeLinejoin: "round",
574
+ className: "rtl:-scale-x-100",
575
+ children: /* @__PURE__ */ jsx7("path", { d: direction === "left" ? "m15 18-6-6 6-6" : "m9 18 6-6-6-6" })
576
+ }
577
+ );
578
+ }
579
+ function ProductCarousel(props) {
580
+ const { products, heading, headingLevel = 2, headingClassName, labels, className, itemClassName } = props;
581
+ const t = { ...DEFAULT_PRODUCT_CAROUSEL_LABELS, ...labels };
582
+ const trackId = useId();
583
+ const headingId = useId();
584
+ const trackRef = useRef3(null);
585
+ const [canPrev, setCanPrev] = useState5(false);
586
+ const [canNext, setCanNext] = useState5(false);
587
+ const update = useCallback2(() => {
588
+ const el = trackRef.current;
589
+ if (!el) return;
590
+ const max = el.scrollWidth - el.clientWidth;
591
+ const pos = Math.abs(el.scrollLeft);
592
+ setCanPrev(pos > EDGE_EPSILON);
593
+ setCanNext(pos < max - EDGE_EPSILON);
594
+ }, []);
595
+ useEffect3(() => {
596
+ const el = trackRef.current;
597
+ if (!el) return;
598
+ update();
599
+ el.addEventListener("scroll", update, { passive: true });
600
+ let observer;
601
+ if (typeof ResizeObserver !== "undefined") {
602
+ observer = new ResizeObserver(update);
603
+ observer.observe(el);
604
+ } else {
605
+ window.addEventListener("resize", update);
606
+ }
607
+ return () => {
608
+ el.removeEventListener("scroll", update);
609
+ if (observer) observer.disconnect();
610
+ else window.removeEventListener("resize", update);
611
+ };
612
+ }, [update, products.length]);
613
+ const scrollByPage = (direction) => {
614
+ const el = trackRef.current;
615
+ if (!el) return;
616
+ const rtl = typeof getComputedStyle === "function" && getComputedStyle(el).direction === "rtl";
617
+ el.scrollBy({
618
+ left: direction * el.clientWidth * (rtl ? -1 : 1),
619
+ behavior: prefersReducedMotion() ? "auto" : "smooth"
620
+ });
621
+ };
622
+ if (products.length === 0) return null;
623
+ const scrollable = canPrev || canNext;
624
+ return /* @__PURE__ */ jsxs5("section", { "data-slot": "product-carousel", className: cn6("flex flex-col gap-4", className), children: [
625
+ (heading || scrollable) && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between gap-4", children: [
626
+ heading ? /* @__PURE__ */ jsx7("div", { id: headingId, children: /* @__PURE__ */ jsx7(Heading, { level: headingLevel, className: headingClassName ?? "text-xl font-semibold", children: heading }) }) : /* @__PURE__ */ jsx7("span", {}),
627
+ scrollable && /* @__PURE__ */ jsxs5("div", { className: "hidden gap-2 sm:flex", "data-slot": "product-carousel-controls", children: [
628
+ /* @__PURE__ */ jsx7(
629
+ "button",
630
+ {
631
+ type: "button",
632
+ className: cn6(buttonVariants({ variant: "outline", size: "icon" }), "rounded-full"),
633
+ "aria-label": t.previous,
634
+ "aria-controls": trackId,
635
+ disabled: !canPrev,
636
+ onClick: () => scrollByPage(-1),
637
+ children: /* @__PURE__ */ jsx7(ChevronIcon, { direction: "left" })
638
+ }
639
+ ),
640
+ /* @__PURE__ */ jsx7(
641
+ "button",
642
+ {
643
+ type: "button",
644
+ className: cn6(buttonVariants({ variant: "outline", size: "icon" }), "rounded-full"),
645
+ "aria-label": t.next,
646
+ "aria-controls": trackId,
647
+ disabled: !canNext,
648
+ onClick: () => scrollByPage(1),
649
+ children: /* @__PURE__ */ jsx7(ChevronIcon, { direction: "right" })
650
+ }
651
+ )
652
+ ] })
653
+ ] }),
654
+ /* @__PURE__ */ jsx7(
655
+ "div",
656
+ {
657
+ ref: trackRef,
658
+ id: trackId,
659
+ role: "region",
660
+ "aria-roledescription": t.roleDescription,
661
+ ...heading ? { "aria-labelledby": headingId } : { "aria-label": t.region },
662
+ tabIndex: 0,
663
+ "data-slot": "product-carousel-track",
664
+ className: "snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth rounded-lg pb-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:scroll-auto",
665
+ children: /* @__PURE__ */ jsx7("ul", { role: "list", className: "m-0 flex list-none gap-4 p-0", children: products.map((product, index) => /* @__PURE__ */ jsx7(
666
+ "li",
667
+ {
668
+ className: cn6(
669
+ "shrink-0 snap-start basis-[calc((100%-1rem)/1.5)] sm:basis-[calc((100%-2rem)/3)] lg:basis-[calc((100%-3rem)/4)]",
670
+ itemClassName
671
+ ),
672
+ children: props.renderProduct ? props.renderProduct(product, index) : /* @__PURE__ */ jsx7(
673
+ ProductCard,
674
+ {
675
+ product,
676
+ href: props.productHref?.(product),
677
+ currencyCode: props.currencyCode,
678
+ locale: props.locale,
679
+ className: "h-full"
680
+ }
681
+ )
682
+ },
683
+ product.id
684
+ )) })
685
+ }
686
+ )
687
+ ] });
688
+ }
689
+
690
+ // src/components/data-request-form.tsx
691
+ import { useEffect as useEffect4, useId as useId2, useRef as useRef4, useState as useState6 } from "react";
692
+ import { cn as cn7 } from "@reblu/site-ui/utils";
490
693
  import {
491
694
  DATA_REQUEST_TYPES
492
695
  } from "@reblu/site-contracts";
493
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
696
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
494
697
  var DATA_REQUEST_FIELD_LIMITS = Object.freeze({ name: 200, email: 320, details: 5e3 });
495
698
  var DEFAULT_DATA_REQUEST_FORM_LABELS = {
496
699
  requestTypeLabel: "Derecho que quieres ejercer",
@@ -516,7 +719,7 @@ var DEFAULT_DATA_REQUEST_FORM_LABELS = {
516
719
  nameRequired: "Indica tu nombre.",
517
720
  emailInvalid: "Introduce un email v\xE1lido.",
518
721
  consentRequired: "Debes aceptar el tratamiento de tus datos para gestionar la solicitud.",
519
- success: (deadline) => /* @__PURE__ */ jsxs4(Fragment, { children: [
722
+ success: (deadline) => /* @__PURE__ */ jsxs6(Fragment, { children: [
520
723
  "Hemos recibido tu solicitud. Te responderemos como m\xE1ximo el ",
521
724
  deadline,
522
725
  "."
@@ -565,18 +768,18 @@ function DataRequestForm({
565
768
  ...labels,
566
769
  requestTypes: { ...DEFAULT_DATA_REQUEST_FORM_LABELS.requestTypes, ...labels?.requestTypes }
567
770
  };
568
- const id = useId();
569
- const [requestType, setRequestType] = useState5("");
570
- const [name, setName] = useState5("");
571
- const [email, setEmail] = useState5("");
572
- const [details, setDetails] = useState5("");
573
- const [consent, setConsent] = useState5(false);
574
- const [errors, setErrors] = useState5({});
575
- const [formError, setFormError] = useState5(null);
576
- const [submitting, setSubmitting] = useState5(false);
577
- const [ack, setAck] = useState5(null);
578
- const statusRef = useRef3(null);
579
- useEffect3(() => {
771
+ const id = useId2();
772
+ const [requestType, setRequestType] = useState6("");
773
+ const [name, setName] = useState6("");
774
+ const [email, setEmail] = useState6("");
775
+ const [details, setDetails] = useState6("");
776
+ const [consent, setConsent] = useState6(false);
777
+ const [errors, setErrors] = useState6({});
778
+ const [formError, setFormError] = useState6(null);
779
+ const [submitting, setSubmitting] = useState6(false);
780
+ const [ack, setAck] = useState6(null);
781
+ const statusRef = useRef4(null);
782
+ useEffect4(() => {
580
783
  if (ack) statusRef.current?.focus();
581
784
  }, [ack]);
582
785
  async function handleSubmit(event) {
@@ -627,21 +830,21 @@ function DataRequestForm({
627
830
  dateStyle: "long",
628
831
  timeStyle: "short"
629
832
  }).format(new Date(ack.slaDueAt));
630
- return /* @__PURE__ */ jsx5(
833
+ return /* @__PURE__ */ jsx8(
631
834
  "div",
632
835
  {
633
836
  ref: statusRef,
634
837
  tabIndex: -1,
635
838
  role: "status",
636
839
  "data-slot": "data-request-form-success",
637
- className: cn5("rounded-lg border border-border p-6 text-sm", className),
840
+ className: cn7("rounded-lg border border-border p-6 text-sm", className),
638
841
  children: t.success(deadline)
639
842
  }
640
843
  );
641
844
  }
642
845
  const errorId = (field) => errors[field] ? `${id}-${field}-error` : void 0;
643
- const fieldError = (field) => errors[field] ? /* @__PURE__ */ jsx5("p", { id: `${id}-${field}-error`, className: "text-xs text-destructive", children: errors[field] }) : null;
644
- return /* @__PURE__ */ jsxs4(
846
+ const fieldError = (field) => errors[field] ? /* @__PURE__ */ jsx8("p", { id: `${id}-${field}-error`, className: "text-xs text-destructive", children: errors[field] }) : null;
847
+ return /* @__PURE__ */ jsxs6(
645
848
  "form",
646
849
  {
647
850
  noValidate: true,
@@ -649,11 +852,11 @@ function DataRequestForm({
649
852
  onFocusCapture: onInteract,
650
853
  onPointerDown: onInteract,
651
854
  "data-slot": "data-request-form",
652
- className: cn5("space-y-4", className),
855
+ className: cn7("space-y-4", className),
653
856
  children: [
654
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
655
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-type`, className: "text-sm font-medium", children: t.requestTypeLabel }),
656
- /* @__PURE__ */ jsxs4(
857
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
858
+ /* @__PURE__ */ jsx8("label", { htmlFor: `${id}-type`, className: "text-sm font-medium", children: t.requestTypeLabel }),
859
+ /* @__PURE__ */ jsxs6(
657
860
  "select",
658
861
  {
659
862
  id: `${id}-type`,
@@ -661,18 +864,18 @@ function DataRequestForm({
661
864
  onChange: (e) => setRequestType(e.target.value),
662
865
  "aria-invalid": errors.requestType ? true : void 0,
663
866
  "aria-describedby": errorId("requestType"),
664
- className: cn5(FIELD_CLASS, errors.requestType ? "border-destructive" : "border-border"),
867
+ className: cn7(FIELD_CLASS, errors.requestType ? "border-destructive" : "border-border"),
665
868
  children: [
666
- /* @__PURE__ */ jsx5("option", { value: "", children: t.requestTypePlaceholder }),
667
- requestTypes.map((type) => /* @__PURE__ */ jsx5("option", { value: type, children: t.requestTypes[type] }, type))
869
+ /* @__PURE__ */ jsx8("option", { value: "", children: t.requestTypePlaceholder }),
870
+ requestTypes.map((type) => /* @__PURE__ */ jsx8("option", { value: type, children: t.requestTypes[type] }, type))
668
871
  ]
669
872
  }
670
873
  ),
671
874
  fieldError("requestType")
672
875
  ] }),
673
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
674
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-name`, className: "text-sm font-medium", children: t.nameLabel }),
675
- /* @__PURE__ */ jsx5(
876
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
877
+ /* @__PURE__ */ jsx8("label", { htmlFor: `${id}-name`, className: "text-sm font-medium", children: t.nameLabel }),
878
+ /* @__PURE__ */ jsx8(
676
879
  "input",
677
880
  {
678
881
  id: `${id}-name`,
@@ -683,14 +886,14 @@ function DataRequestForm({
683
886
  onChange: (e) => setName(e.target.value),
684
887
  "aria-invalid": errors.name ? true : void 0,
685
888
  "aria-describedby": errorId("name"),
686
- className: cn5(FIELD_CLASS, errors.name ? "border-destructive" : "border-border")
889
+ className: cn7(FIELD_CLASS, errors.name ? "border-destructive" : "border-border")
687
890
  }
688
891
  ),
689
892
  fieldError("name")
690
893
  ] }),
691
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
692
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-email`, className: "text-sm font-medium", children: t.emailLabel }),
693
- /* @__PURE__ */ jsx5(
894
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
895
+ /* @__PURE__ */ jsx8("label", { htmlFor: `${id}-email`, className: "text-sm font-medium", children: t.emailLabel }),
896
+ /* @__PURE__ */ jsx8(
694
897
  "input",
695
898
  {
696
899
  id: `${id}-email`,
@@ -701,14 +904,14 @@ function DataRequestForm({
701
904
  onChange: (e) => setEmail(e.target.value),
702
905
  "aria-invalid": errors.email ? true : void 0,
703
906
  "aria-describedby": errorId("email"),
704
- className: cn5(FIELD_CLASS, errors.email ? "border-destructive" : "border-border")
907
+ className: cn7(FIELD_CLASS, errors.email ? "border-destructive" : "border-border")
705
908
  }
706
909
  ),
707
910
  fieldError("email")
708
911
  ] }),
709
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
710
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-details`, className: "text-sm font-medium", children: t.detailsLabel }),
711
- /* @__PURE__ */ jsx5(
912
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
913
+ /* @__PURE__ */ jsx8("label", { htmlFor: `${id}-details`, className: "text-sm font-medium", children: t.detailsLabel }),
914
+ /* @__PURE__ */ jsx8(
712
915
  "textarea",
713
916
  {
714
917
  id: `${id}-details`,
@@ -717,13 +920,13 @@ function DataRequestForm({
717
920
  value: details,
718
921
  onChange: (e) => setDetails(e.target.value),
719
922
  placeholder: t.detailsPlaceholder,
720
- className: cn5(FIELD_CLASS, "border-border")
923
+ className: cn7(FIELD_CLASS, "border-border")
721
924
  }
722
925
  )
723
926
  ] }),
724
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
725
- /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-2", children: [
726
- /* @__PURE__ */ jsx5(
927
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
928
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-start gap-2", children: [
929
+ /* @__PURE__ */ jsx8(
727
930
  "input",
728
931
  {
729
932
  id: `${id}-consent`,
@@ -735,17 +938,17 @@ function DataRequestForm({
735
938
  className: "mt-1"
736
939
  }
737
940
  ),
738
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-consent`, className: "text-sm text-foreground/80", children: t.consent })
941
+ /* @__PURE__ */ jsx8("label", { htmlFor: `${id}-consent`, className: "text-sm text-foreground/80", children: t.consent })
739
942
  ] }),
740
943
  fieldError("consent")
741
944
  ] }),
742
- formError && /* @__PURE__ */ jsx5("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
743
- /* @__PURE__ */ jsx5(
945
+ formError && /* @__PURE__ */ jsx8("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
946
+ /* @__PURE__ */ jsx8(
744
947
  "button",
745
948
  {
746
949
  type: "submit",
747
950
  disabled: submitting,
748
- className: "rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:opacity-60",
951
+ className: "cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60",
749
952
  children: submitting ? t.submitting : t.submit
750
953
  }
751
954
  )
@@ -760,7 +963,9 @@ export {
760
963
  DATA_REQUEST_FIELD_LIMITS,
761
964
  DATA_REQUEST_TYPES,
762
965
  DEFAULT_DATA_REQUEST_FORM_LABELS,
966
+ DEFAULT_PRODUCT_CAROUSEL_LABELS,
763
967
  DataRequestForm,
968
+ ProductCarousel,
764
969
  ShareButtons,
765
970
  useCollection
766
971
  };