@reblu/site-blocks 0.18.0 → 0.20.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/CHANGELOG.md +33 -0
- package/README.md +179 -2
- package/dist/_heading-BsNbc1wc.d.cts +4 -0
- package/dist/_heading-BsNbc1wc.d.ts +4 -0
- package/dist/client.cjs +449 -50
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +109 -2
- package/dist/client.d.ts +109 -2
- package/dist/client.js +446 -51
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +374 -309
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -8
- package/dist/index.d.ts +51 -8
- package/dist/index.js +373 -309
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
@@ -415,7 +415,7 @@ import { cn as cn4 } from "@reblu/site-ui/utils";
|
|
|
415
415
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
416
416
|
var DEFAULT_LABELS4 = {
|
|
417
417
|
cart: "Carrito",
|
|
418
|
-
itemsSuffix: (
|
|
418
|
+
itemsSuffix: (count2) => `${count2} art\xEDculo${count2 === 1 ? "" : "s"}`
|
|
419
419
|
};
|
|
420
420
|
var BUMP_DURATION_MS = 400;
|
|
421
421
|
function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }) {
|
|
@@ -484,13 +484,404 @@ function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }) {
|
|
|
484
484
|
);
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
// src/components/
|
|
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/course-syllabus.tsx
|
|
691
|
+
import { useId as useId2, useState as useState6 } from "react";
|
|
692
|
+
import { cn as cn8 } from "@reblu/site-ui/utils";
|
|
693
|
+
|
|
694
|
+
// src/lib/slot-class-name.ts
|
|
695
|
+
import { cn as cn7 } from "@reblu/site-ui/utils";
|
|
696
|
+
var MERGE_SAFE_TEXT = /(?:^|:)!?text-(?:left|center|right|justify|start|end|wrap|nowrap|balance|pretty|ellipsis|clip)$/;
|
|
697
|
+
var TEXT_UTILITY = /(?:^|:)!?-?text-/;
|
|
698
|
+
function bypassesMerge(token) {
|
|
699
|
+
return TEXT_UTILITY.test(token) && !MERGE_SAFE_TEXT.test(token);
|
|
700
|
+
}
|
|
701
|
+
function slotClassName(structural, defaults, consumer) {
|
|
702
|
+
if (consumer === void 0 || consumer.trim() === "") return cn7(structural, defaults);
|
|
703
|
+
const tokens = consumer.split(/\s+/).filter(Boolean);
|
|
704
|
+
const verbatim = tokens.filter(bypassesMerge);
|
|
705
|
+
const merged = cn7(structural, tokens.filter((token) => !bypassesMerge(token)));
|
|
706
|
+
return [merged, ...verbatim].filter(Boolean).join(" ");
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/components/course-syllabus.tsx
|
|
710
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
711
|
+
var DEFAULT_COURSE_SYLLABUS_LABELS = {
|
|
712
|
+
heading: "Programa",
|
|
713
|
+
moduleSingular: "m\xF3dulo",
|
|
714
|
+
modulePlural: "m\xF3dulos",
|
|
715
|
+
lessonSingular: "lecci\xF3n",
|
|
716
|
+
lessonPlural: "lecciones",
|
|
717
|
+
freePreview: "Vista previa gratuita",
|
|
718
|
+
hours: "h",
|
|
719
|
+
minutes: "min"
|
|
720
|
+
};
|
|
721
|
+
var SEPARATOR = " \xB7 ";
|
|
722
|
+
function count(n, singular, plural) {
|
|
723
|
+
return `${n} ${n === 1 ? singular : plural}`;
|
|
724
|
+
}
|
|
725
|
+
function formatDuration(seconds, labels) {
|
|
726
|
+
const totalMinutes = Math.max(1, Math.round(seconds / 60));
|
|
727
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
728
|
+
const minutes = totalMinutes % 60;
|
|
729
|
+
if (hours === 0) return `${minutes} ${labels.minutes}`;
|
|
730
|
+
return minutes === 0 ? `${hours} ${labels.hours}` : `${hours} ${labels.hours} ${minutes} ${labels.minutes}`;
|
|
731
|
+
}
|
|
732
|
+
function moduleSeconds(lessons) {
|
|
733
|
+
let total = 0;
|
|
734
|
+
for (const l of lessons) {
|
|
735
|
+
if (typeof l.durationSeconds !== "number" || l.durationSeconds <= 0) return null;
|
|
736
|
+
total += l.durationSeconds;
|
|
737
|
+
}
|
|
738
|
+
return total;
|
|
739
|
+
}
|
|
740
|
+
function CourseSyllabus({
|
|
741
|
+
modules,
|
|
742
|
+
labels: labelOverrides,
|
|
743
|
+
headingLevel = 2,
|
|
744
|
+
headingClassName,
|
|
745
|
+
className
|
|
746
|
+
}) {
|
|
747
|
+
const labels = { ...DEFAULT_COURSE_SYLLABUS_LABELS, ...labelOverrides };
|
|
748
|
+
const baseId = useId2();
|
|
749
|
+
const [open, setOpen] = useState6(() => /* @__PURE__ */ new Set());
|
|
750
|
+
if (modules.length === 0) return null;
|
|
751
|
+
const totalLessons = modules.reduce((sum, m) => sum + (m.lessons?.length ?? 0), 0);
|
|
752
|
+
const summary = [count(modules.length, labels.moduleSingular, labels.modulePlural)];
|
|
753
|
+
if (totalLessons > 0)
|
|
754
|
+
summary.push(count(totalLessons, labels.lessonSingular, labels.lessonPlural));
|
|
755
|
+
const headingId = `${baseId}-heading`;
|
|
756
|
+
const Tag = `h${headingLevel}`;
|
|
757
|
+
function toggle(id) {
|
|
758
|
+
setOpen((prev) => {
|
|
759
|
+
const next = new Set(prev);
|
|
760
|
+
if (next.has(id)) next.delete(id);
|
|
761
|
+
else next.add(id);
|
|
762
|
+
return next;
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
return /* @__PURE__ */ jsxs6(
|
|
766
|
+
"section",
|
|
767
|
+
{
|
|
768
|
+
"data-slot": "course-syllabus",
|
|
769
|
+
"aria-labelledby": headingId,
|
|
770
|
+
className: cn8("space-y-4", className),
|
|
771
|
+
children: [
|
|
772
|
+
/* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
|
|
773
|
+
/* @__PURE__ */ jsx8(
|
|
774
|
+
Tag,
|
|
775
|
+
{
|
|
776
|
+
id: headingId,
|
|
777
|
+
className: slotClassName("", "text-xl font-semibold", headingClassName),
|
|
778
|
+
children: labels.heading
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
/* @__PURE__ */ jsx8("p", { "data-slot": "course-syllabus-summary", className: "text-sm text-muted-foreground", children: summary.join(SEPARATOR) })
|
|
782
|
+
] }),
|
|
783
|
+
/* @__PURE__ */ jsx8("ol", { className: "flex list-none flex-col gap-2.5 p-0", children: modules.map((module, index) => {
|
|
784
|
+
const lessons = module.lessons ?? [];
|
|
785
|
+
const number = /* @__PURE__ */ jsx8(
|
|
786
|
+
"span",
|
|
787
|
+
{
|
|
788
|
+
"aria-hidden": "true",
|
|
789
|
+
className: "flex size-7 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-primary",
|
|
790
|
+
children: index + 1
|
|
791
|
+
}
|
|
792
|
+
);
|
|
793
|
+
if (lessons.length === 0) {
|
|
794
|
+
return /* @__PURE__ */ jsxs6(
|
|
795
|
+
"li",
|
|
796
|
+
{
|
|
797
|
+
"data-slot": "course-syllabus-module",
|
|
798
|
+
className: "flex items-center gap-3.5 rounded-lg border border-border bg-card px-4 py-3.5",
|
|
799
|
+
children: [
|
|
800
|
+
number,
|
|
801
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold leading-snug", children: module.title })
|
|
802
|
+
]
|
|
803
|
+
},
|
|
804
|
+
module.id
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
const isOpen = open.has(module.id);
|
|
808
|
+
const panelId = `${baseId}-module-${index}`;
|
|
809
|
+
const seconds = moduleSeconds(lessons);
|
|
810
|
+
const meta = [count(lessons.length, labels.lessonSingular, labels.lessonPlural)];
|
|
811
|
+
if (seconds !== null) meta.push(formatDuration(seconds, labels));
|
|
812
|
+
return /* @__PURE__ */ jsxs6(
|
|
813
|
+
"li",
|
|
814
|
+
{
|
|
815
|
+
"data-slot": "course-syllabus-module",
|
|
816
|
+
className: "rounded-lg border border-border bg-card",
|
|
817
|
+
children: [
|
|
818
|
+
/* @__PURE__ */ jsxs6(
|
|
819
|
+
"button",
|
|
820
|
+
{
|
|
821
|
+
type: "button",
|
|
822
|
+
"aria-expanded": isOpen,
|
|
823
|
+
"aria-controls": panelId,
|
|
824
|
+
onClick: () => toggle(module.id),
|
|
825
|
+
className: "flex w-full cursor-pointer items-center gap-3.5 rounded-lg px-4 py-3.5 text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
|
|
826
|
+
children: [
|
|
827
|
+
number,
|
|
828
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
829
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold leading-snug", children: module.title }),
|
|
830
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm text-muted-foreground", children: meta.join(SEPARATOR) })
|
|
831
|
+
] }),
|
|
832
|
+
/* @__PURE__ */ jsx8(
|
|
833
|
+
"span",
|
|
834
|
+
{
|
|
835
|
+
"aria-hidden": "true",
|
|
836
|
+
className: cn8(
|
|
837
|
+
"shrink-0 text-muted-foreground transition-transform motion-reduce:transition-none",
|
|
838
|
+
isOpen && "rotate-180"
|
|
839
|
+
),
|
|
840
|
+
children: "\u25BE"
|
|
841
|
+
}
|
|
842
|
+
)
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
),
|
|
846
|
+
/* @__PURE__ */ jsx8(
|
|
847
|
+
"ol",
|
|
848
|
+
{
|
|
849
|
+
id: panelId,
|
|
850
|
+
hidden: !isOpen,
|
|
851
|
+
"data-slot": "course-syllabus-lessons",
|
|
852
|
+
className: "list-none space-y-2 border-t border-border px-4 py-3 pl-[3.75rem]",
|
|
853
|
+
children: lessons.map((lesson) => /* @__PURE__ */ jsxs6(
|
|
854
|
+
"li",
|
|
855
|
+
{
|
|
856
|
+
"data-slot": "course-syllabus-lesson",
|
|
857
|
+
className: "flex flex-wrap items-baseline gap-x-3 gap-y-1 text-sm",
|
|
858
|
+
children: [
|
|
859
|
+
/* @__PURE__ */ jsx8("span", { "data-slot": "course-syllabus-lesson-title", className: "flex-1", children: lesson.title }),
|
|
860
|
+
lesson.isFreePreview && /* @__PURE__ */ jsx8("span", { className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium", children: labels.freePreview }),
|
|
861
|
+
typeof lesson.durationSeconds === "number" && lesson.durationSeconds > 0 && /* @__PURE__ */ jsx8("span", { className: "text-muted-foreground tabular-nums", children: formatDuration(lesson.durationSeconds, labels) })
|
|
862
|
+
]
|
|
863
|
+
},
|
|
864
|
+
lesson.id
|
|
865
|
+
))
|
|
866
|
+
}
|
|
867
|
+
)
|
|
868
|
+
]
|
|
869
|
+
},
|
|
870
|
+
module.id
|
|
871
|
+
);
|
|
872
|
+
}) })
|
|
873
|
+
]
|
|
874
|
+
}
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// src/components/data-request-form.tsx
|
|
879
|
+
import { useEffect as useEffect4, useId as useId3, useRef as useRef4, useState as useState7 } from "react";
|
|
880
|
+
import { cn as cn9 } from "@reblu/site-ui/utils";
|
|
490
881
|
import {
|
|
491
882
|
DATA_REQUEST_TYPES
|
|
492
883
|
} from "@reblu/site-contracts";
|
|
493
|
-
import { Fragment, jsx as
|
|
884
|
+
import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
494
885
|
var DATA_REQUEST_FIELD_LIMITS = Object.freeze({ name: 200, email: 320, details: 5e3 });
|
|
495
886
|
var DEFAULT_DATA_REQUEST_FORM_LABELS = {
|
|
496
887
|
requestTypeLabel: "Derecho que quieres ejercer",
|
|
@@ -516,7 +907,7 @@ var DEFAULT_DATA_REQUEST_FORM_LABELS = {
|
|
|
516
907
|
nameRequired: "Indica tu nombre.",
|
|
517
908
|
emailInvalid: "Introduce un email v\xE1lido.",
|
|
518
909
|
consentRequired: "Debes aceptar el tratamiento de tus datos para gestionar la solicitud.",
|
|
519
|
-
success: (deadline) => /* @__PURE__ */
|
|
910
|
+
success: (deadline) => /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
520
911
|
"Hemos recibido tu solicitud. Te responderemos como m\xE1ximo el ",
|
|
521
912
|
deadline,
|
|
522
913
|
"."
|
|
@@ -565,18 +956,18 @@ function DataRequestForm({
|
|
|
565
956
|
...labels,
|
|
566
957
|
requestTypes: { ...DEFAULT_DATA_REQUEST_FORM_LABELS.requestTypes, ...labels?.requestTypes }
|
|
567
958
|
};
|
|
568
|
-
const id =
|
|
569
|
-
const [requestType, setRequestType] =
|
|
570
|
-
const [name, setName] =
|
|
571
|
-
const [email, setEmail] =
|
|
572
|
-
const [details, setDetails] =
|
|
573
|
-
const [consent, setConsent] =
|
|
574
|
-
const [errors, setErrors] =
|
|
575
|
-
const [formError, setFormError] =
|
|
576
|
-
const [submitting, setSubmitting] =
|
|
577
|
-
const [ack, setAck] =
|
|
578
|
-
const statusRef =
|
|
579
|
-
|
|
959
|
+
const id = useId3();
|
|
960
|
+
const [requestType, setRequestType] = useState7("");
|
|
961
|
+
const [name, setName] = useState7("");
|
|
962
|
+
const [email, setEmail] = useState7("");
|
|
963
|
+
const [details, setDetails] = useState7("");
|
|
964
|
+
const [consent, setConsent] = useState7(false);
|
|
965
|
+
const [errors, setErrors] = useState7({});
|
|
966
|
+
const [formError, setFormError] = useState7(null);
|
|
967
|
+
const [submitting, setSubmitting] = useState7(false);
|
|
968
|
+
const [ack, setAck] = useState7(null);
|
|
969
|
+
const statusRef = useRef4(null);
|
|
970
|
+
useEffect4(() => {
|
|
580
971
|
if (ack) statusRef.current?.focus();
|
|
581
972
|
}, [ack]);
|
|
582
973
|
async function handleSubmit(event) {
|
|
@@ -627,21 +1018,21 @@ function DataRequestForm({
|
|
|
627
1018
|
dateStyle: "long",
|
|
628
1019
|
timeStyle: "short"
|
|
629
1020
|
}).format(new Date(ack.slaDueAt));
|
|
630
|
-
return /* @__PURE__ */
|
|
1021
|
+
return /* @__PURE__ */ jsx9(
|
|
631
1022
|
"div",
|
|
632
1023
|
{
|
|
633
1024
|
ref: statusRef,
|
|
634
1025
|
tabIndex: -1,
|
|
635
1026
|
role: "status",
|
|
636
1027
|
"data-slot": "data-request-form-success",
|
|
637
|
-
className:
|
|
1028
|
+
className: cn9("rounded-lg border border-border p-6 text-sm", className),
|
|
638
1029
|
children: t.success(deadline)
|
|
639
1030
|
}
|
|
640
1031
|
);
|
|
641
1032
|
}
|
|
642
1033
|
const errorId = (field) => errors[field] ? `${id}-${field}-error` : void 0;
|
|
643
|
-
const fieldError = (field) => errors[field] ? /* @__PURE__ */
|
|
644
|
-
return /* @__PURE__ */
|
|
1034
|
+
const fieldError = (field) => errors[field] ? /* @__PURE__ */ jsx9("p", { id: `${id}-${field}-error`, className: "text-xs text-destructive", children: errors[field] }) : null;
|
|
1035
|
+
return /* @__PURE__ */ jsxs7(
|
|
645
1036
|
"form",
|
|
646
1037
|
{
|
|
647
1038
|
noValidate: true,
|
|
@@ -649,11 +1040,11 @@ function DataRequestForm({
|
|
|
649
1040
|
onFocusCapture: onInteract,
|
|
650
1041
|
onPointerDown: onInteract,
|
|
651
1042
|
"data-slot": "data-request-form",
|
|
652
|
-
className:
|
|
1043
|
+
className: cn9("space-y-4", className),
|
|
653
1044
|
children: [
|
|
654
|
-
/* @__PURE__ */
|
|
655
|
-
/* @__PURE__ */
|
|
656
|
-
/* @__PURE__ */
|
|
1045
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
1046
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: `${id}-type`, className: "text-sm font-medium", children: t.requestTypeLabel }),
|
|
1047
|
+
/* @__PURE__ */ jsxs7(
|
|
657
1048
|
"select",
|
|
658
1049
|
{
|
|
659
1050
|
id: `${id}-type`,
|
|
@@ -661,18 +1052,18 @@ function DataRequestForm({
|
|
|
661
1052
|
onChange: (e) => setRequestType(e.target.value),
|
|
662
1053
|
"aria-invalid": errors.requestType ? true : void 0,
|
|
663
1054
|
"aria-describedby": errorId("requestType"),
|
|
664
|
-
className:
|
|
1055
|
+
className: cn9(FIELD_CLASS, errors.requestType ? "border-destructive" : "border-border"),
|
|
665
1056
|
children: [
|
|
666
|
-
/* @__PURE__ */
|
|
667
|
-
requestTypes.map((type) => /* @__PURE__ */
|
|
1057
|
+
/* @__PURE__ */ jsx9("option", { value: "", children: t.requestTypePlaceholder }),
|
|
1058
|
+
requestTypes.map((type) => /* @__PURE__ */ jsx9("option", { value: type, children: t.requestTypes[type] }, type))
|
|
668
1059
|
]
|
|
669
1060
|
}
|
|
670
1061
|
),
|
|
671
1062
|
fieldError("requestType")
|
|
672
1063
|
] }),
|
|
673
|
-
/* @__PURE__ */
|
|
674
|
-
/* @__PURE__ */
|
|
675
|
-
/* @__PURE__ */
|
|
1064
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
1065
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: `${id}-name`, className: "text-sm font-medium", children: t.nameLabel }),
|
|
1066
|
+
/* @__PURE__ */ jsx9(
|
|
676
1067
|
"input",
|
|
677
1068
|
{
|
|
678
1069
|
id: `${id}-name`,
|
|
@@ -683,14 +1074,14 @@ function DataRequestForm({
|
|
|
683
1074
|
onChange: (e) => setName(e.target.value),
|
|
684
1075
|
"aria-invalid": errors.name ? true : void 0,
|
|
685
1076
|
"aria-describedby": errorId("name"),
|
|
686
|
-
className:
|
|
1077
|
+
className: cn9(FIELD_CLASS, errors.name ? "border-destructive" : "border-border")
|
|
687
1078
|
}
|
|
688
1079
|
),
|
|
689
1080
|
fieldError("name")
|
|
690
1081
|
] }),
|
|
691
|
-
/* @__PURE__ */
|
|
692
|
-
/* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
1082
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
1083
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: `${id}-email`, className: "text-sm font-medium", children: t.emailLabel }),
|
|
1084
|
+
/* @__PURE__ */ jsx9(
|
|
694
1085
|
"input",
|
|
695
1086
|
{
|
|
696
1087
|
id: `${id}-email`,
|
|
@@ -701,14 +1092,14 @@ function DataRequestForm({
|
|
|
701
1092
|
onChange: (e) => setEmail(e.target.value),
|
|
702
1093
|
"aria-invalid": errors.email ? true : void 0,
|
|
703
1094
|
"aria-describedby": errorId("email"),
|
|
704
|
-
className:
|
|
1095
|
+
className: cn9(FIELD_CLASS, errors.email ? "border-destructive" : "border-border")
|
|
705
1096
|
}
|
|
706
1097
|
),
|
|
707
1098
|
fieldError("email")
|
|
708
1099
|
] }),
|
|
709
|
-
/* @__PURE__ */
|
|
710
|
-
/* @__PURE__ */
|
|
711
|
-
/* @__PURE__ */
|
|
1100
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
1101
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: `${id}-details`, className: "text-sm font-medium", children: t.detailsLabel }),
|
|
1102
|
+
/* @__PURE__ */ jsx9(
|
|
712
1103
|
"textarea",
|
|
713
1104
|
{
|
|
714
1105
|
id: `${id}-details`,
|
|
@@ -717,13 +1108,13 @@ function DataRequestForm({
|
|
|
717
1108
|
value: details,
|
|
718
1109
|
onChange: (e) => setDetails(e.target.value),
|
|
719
1110
|
placeholder: t.detailsPlaceholder,
|
|
720
|
-
className:
|
|
1111
|
+
className: cn9(FIELD_CLASS, "border-border")
|
|
721
1112
|
}
|
|
722
1113
|
)
|
|
723
1114
|
] }),
|
|
724
|
-
/* @__PURE__ */
|
|
725
|
-
/* @__PURE__ */
|
|
726
|
-
/* @__PURE__ */
|
|
1115
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
1116
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-2", children: [
|
|
1117
|
+
/* @__PURE__ */ jsx9(
|
|
727
1118
|
"input",
|
|
728
1119
|
{
|
|
729
1120
|
id: `${id}-consent`,
|
|
@@ -735,17 +1126,17 @@ function DataRequestForm({
|
|
|
735
1126
|
className: "mt-1"
|
|
736
1127
|
}
|
|
737
1128
|
),
|
|
738
|
-
/* @__PURE__ */
|
|
1129
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: `${id}-consent`, className: "text-sm text-foreground/80", children: t.consent })
|
|
739
1130
|
] }),
|
|
740
1131
|
fieldError("consent")
|
|
741
1132
|
] }),
|
|
742
|
-
formError && /* @__PURE__ */
|
|
743
|
-
/* @__PURE__ */
|
|
1133
|
+
formError && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
|
|
1134
|
+
/* @__PURE__ */ jsx9(
|
|
744
1135
|
"button",
|
|
745
1136
|
{
|
|
746
1137
|
type: "submit",
|
|
747
1138
|
disabled: submitting,
|
|
748
|
-
className: "rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:opacity-60",
|
|
1139
|
+
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
1140
|
children: submitting ? t.submitting : t.submit
|
|
750
1141
|
}
|
|
751
1142
|
)
|
|
@@ -757,10 +1148,14 @@ export {
|
|
|
757
1148
|
AddToCartButton,
|
|
758
1149
|
CartIcon,
|
|
759
1150
|
Collection,
|
|
1151
|
+
CourseSyllabus,
|
|
760
1152
|
DATA_REQUEST_FIELD_LIMITS,
|
|
761
1153
|
DATA_REQUEST_TYPES,
|
|
1154
|
+
DEFAULT_COURSE_SYLLABUS_LABELS,
|
|
762
1155
|
DEFAULT_DATA_REQUEST_FORM_LABELS,
|
|
1156
|
+
DEFAULT_PRODUCT_CAROUSEL_LABELS,
|
|
763
1157
|
DataRequestForm,
|
|
1158
|
+
ProductCarousel,
|
|
764
1159
|
ShareButtons,
|
|
765
1160
|
useCollection
|
|
766
1161
|
};
|