@reblu/site-blocks 0.20.0 → 0.22.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 +35 -0
- package/README.md +209 -14
- package/dist/client.cjs +218 -83
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +40 -13
- package/dist/client.d.ts +40 -13
- package/dist/client.js +221 -86
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +620 -321
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -2
- package/dist/index.d.ts +144 -2
- package/dist/index.js +614 -321
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/client.d.cts
CHANGED
|
@@ -307,21 +307,29 @@ declare function ProductCarousel<T extends {
|
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
309
|
* `CourseSyllabus` — the drop-in programme of a public course page, over
|
|
310
|
-
* `AcademyCourseDetail.modules` (`client.academy.bySlug()`), REB-1134
|
|
310
|
+
* `AcademyCourseDetail.modules` (`client.academy.bySlug()`), REB-1134; redesigned
|
|
311
|
+
* in REB-1135.
|
|
311
312
|
*
|
|
312
|
-
* - The header reads the heading, then «N módulos · M lecciones
|
|
313
|
+
* - The header reads the heading, then «N módulos · M lecciones · total», and
|
|
314
|
+
* carries an «Expandir todo / Contraer todo» control.
|
|
313
315
|
* - Each module with lessons is a `<button>` with `aria-expanded` and
|
|
314
316
|
* `aria-controls`, showing its lesson count and, when every lesson has one,
|
|
315
|
-
* the module's total duration
|
|
316
|
-
*
|
|
317
|
+
* the module's total duration, with a chevron that rotates when open.
|
|
318
|
+
* - Each lesson row shows a type icon (video / reading, named for screen
|
|
319
|
+
* readers), its title, its duration as «m:ss» / «h:mm:ss», and — on a
|
|
320
|
+
* free-preview lesson — «Vista previa gratuita» as a call to action:
|
|
321
|
+
* a link (`getPreviewHref` / `previewHrefTemplate`), a button (`onPreview`),
|
|
322
|
+
* or a plain label when the SITE wires neither.
|
|
317
323
|
* - A module without lessons is a plain row (title only), never a button.
|
|
318
324
|
*
|
|
319
|
-
* It renders only what the API serves
|
|
320
|
-
*
|
|
325
|
+
* It renders only what the API serves: published lessons, no video, playback
|
|
326
|
+
* or attachment data. The preview itself is fetched by the page it links to
|
|
327
|
+
* (`getLessonPreview` in `@reblu/site-next/academy-server`).
|
|
321
328
|
*
|
|
322
|
-
* Client Component: it holds the open/closed state
|
|
323
|
-
*
|
|
324
|
-
*
|
|
329
|
+
* Client Component: it holds the open/closed state. Copy and
|
|
330
|
+
* `previewHrefTemplate` are serialisable, so a Server Component page can render
|
|
331
|
+
* it directly; `getPreviewHref` / `onPreview` are functions, so they need a
|
|
332
|
+
* Client Component parent.
|
|
325
333
|
*/
|
|
326
334
|
interface CourseSyllabusLabels {
|
|
327
335
|
heading: string;
|
|
@@ -329,12 +337,18 @@ interface CourseSyllabusLabels {
|
|
|
329
337
|
modulePlural: string;
|
|
330
338
|
lessonSingular: string;
|
|
331
339
|
lessonPlural: string;
|
|
332
|
-
/**
|
|
340
|
+
/** Call to action (or label) on a lesson with `isFreePreview`. */
|
|
333
341
|
freePreview: string;
|
|
334
|
-
/** Hours unit in
|
|
342
|
+
/** Hours unit in module/course totals («1 h 15 min»). */
|
|
335
343
|
hours: string;
|
|
336
|
-
/** Minutes unit in
|
|
344
|
+
/** Minutes unit in module/course totals. */
|
|
337
345
|
minutes: string;
|
|
346
|
+
expandAll: string;
|
|
347
|
+
collapseAll: string;
|
|
348
|
+
/** Screen-reader name of the video lesson icon. */
|
|
349
|
+
videoLesson: string;
|
|
350
|
+
/** Screen-reader name of the text (reading) lesson icon. */
|
|
351
|
+
textLesson: string;
|
|
338
352
|
}
|
|
339
353
|
declare const DEFAULT_COURSE_SYLLABUS_LABELS: CourseSyllabusLabels;
|
|
340
354
|
/** A module as served. `lessons` is optional so an older payload still renders. */
|
|
@@ -351,8 +365,21 @@ interface CourseSyllabusProps {
|
|
|
351
365
|
/** Replaces the heading's default typography (see the className rule in the README). */
|
|
352
366
|
headingClassName?: string;
|
|
353
367
|
className?: string;
|
|
368
|
+
/**
|
|
369
|
+
* Link target of a free-preview lesson, e.g. `/cursos/${slug}/preview/${l.slug}`.
|
|
370
|
+
* Called only for lessons with `isFreePreview`. Return `undefined` to keep the label.
|
|
371
|
+
*/
|
|
372
|
+
getPreviewHref?: (lesson: AcademyLessonPublic) => string | undefined;
|
|
373
|
+
/**
|
|
374
|
+
* Serialisable alternative to `getPreviewHref` for Server Component pages:
|
|
375
|
+
* `{lessonSlug}` and `{lessonId}` are replaced (URL-encoded). Ignored when
|
|
376
|
+
* `getPreviewHref` is given.
|
|
377
|
+
*/
|
|
378
|
+
previewHrefTemplate?: string;
|
|
379
|
+
/** Renders the call to action as a `<button>` (e.g. to open a modal). Used when no href resolves. */
|
|
380
|
+
onPreview?: (lesson: AcademyLessonPublic) => void;
|
|
354
381
|
}
|
|
355
|
-
declare function CourseSyllabus({ modules, labels: labelOverrides, headingLevel, headingClassName, className, }: CourseSyllabusProps): react_jsx_runtime.JSX.Element | null;
|
|
382
|
+
declare function CourseSyllabus({ modules, labels: labelOverrides, headingLevel, headingClassName, className, getPreviewHref, previewHrefTemplate, onPreview, }: CourseSyllabusProps): react_jsx_runtime.JSX.Element | null;
|
|
356
383
|
|
|
357
384
|
/**
|
|
358
385
|
* `DataRequestForm` — drop-in public GDPR rights form (REB-980) over
|
package/dist/client.d.ts
CHANGED
|
@@ -307,21 +307,29 @@ declare function ProductCarousel<T extends {
|
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
309
|
* `CourseSyllabus` — the drop-in programme of a public course page, over
|
|
310
|
-
* `AcademyCourseDetail.modules` (`client.academy.bySlug()`), REB-1134
|
|
310
|
+
* `AcademyCourseDetail.modules` (`client.academy.bySlug()`), REB-1134; redesigned
|
|
311
|
+
* in REB-1135.
|
|
311
312
|
*
|
|
312
|
-
* - The header reads the heading, then «N módulos · M lecciones
|
|
313
|
+
* - The header reads the heading, then «N módulos · M lecciones · total», and
|
|
314
|
+
* carries an «Expandir todo / Contraer todo» control.
|
|
313
315
|
* - Each module with lessons is a `<button>` with `aria-expanded` and
|
|
314
316
|
* `aria-controls`, showing its lesson count and, when every lesson has one,
|
|
315
|
-
* the module's total duration
|
|
316
|
-
*
|
|
317
|
+
* the module's total duration, with a chevron that rotates when open.
|
|
318
|
+
* - Each lesson row shows a type icon (video / reading, named for screen
|
|
319
|
+
* readers), its title, its duration as «m:ss» / «h:mm:ss», and — on a
|
|
320
|
+
* free-preview lesson — «Vista previa gratuita» as a call to action:
|
|
321
|
+
* a link (`getPreviewHref` / `previewHrefTemplate`), a button (`onPreview`),
|
|
322
|
+
* or a plain label when the SITE wires neither.
|
|
317
323
|
* - A module without lessons is a plain row (title only), never a button.
|
|
318
324
|
*
|
|
319
|
-
* It renders only what the API serves
|
|
320
|
-
*
|
|
325
|
+
* It renders only what the API serves: published lessons, no video, playback
|
|
326
|
+
* or attachment data. The preview itself is fetched by the page it links to
|
|
327
|
+
* (`getLessonPreview` in `@reblu/site-next/academy-server`).
|
|
321
328
|
*
|
|
322
|
-
* Client Component: it holds the open/closed state
|
|
323
|
-
*
|
|
324
|
-
*
|
|
329
|
+
* Client Component: it holds the open/closed state. Copy and
|
|
330
|
+
* `previewHrefTemplate` are serialisable, so a Server Component page can render
|
|
331
|
+
* it directly; `getPreviewHref` / `onPreview` are functions, so they need a
|
|
332
|
+
* Client Component parent.
|
|
325
333
|
*/
|
|
326
334
|
interface CourseSyllabusLabels {
|
|
327
335
|
heading: string;
|
|
@@ -329,12 +337,18 @@ interface CourseSyllabusLabels {
|
|
|
329
337
|
modulePlural: string;
|
|
330
338
|
lessonSingular: string;
|
|
331
339
|
lessonPlural: string;
|
|
332
|
-
/**
|
|
340
|
+
/** Call to action (or label) on a lesson with `isFreePreview`. */
|
|
333
341
|
freePreview: string;
|
|
334
|
-
/** Hours unit in
|
|
342
|
+
/** Hours unit in module/course totals («1 h 15 min»). */
|
|
335
343
|
hours: string;
|
|
336
|
-
/** Minutes unit in
|
|
344
|
+
/** Minutes unit in module/course totals. */
|
|
337
345
|
minutes: string;
|
|
346
|
+
expandAll: string;
|
|
347
|
+
collapseAll: string;
|
|
348
|
+
/** Screen-reader name of the video lesson icon. */
|
|
349
|
+
videoLesson: string;
|
|
350
|
+
/** Screen-reader name of the text (reading) lesson icon. */
|
|
351
|
+
textLesson: string;
|
|
338
352
|
}
|
|
339
353
|
declare const DEFAULT_COURSE_SYLLABUS_LABELS: CourseSyllabusLabels;
|
|
340
354
|
/** A module as served. `lessons` is optional so an older payload still renders. */
|
|
@@ -351,8 +365,21 @@ interface CourseSyllabusProps {
|
|
|
351
365
|
/** Replaces the heading's default typography (see the className rule in the README). */
|
|
352
366
|
headingClassName?: string;
|
|
353
367
|
className?: string;
|
|
368
|
+
/**
|
|
369
|
+
* Link target of a free-preview lesson, e.g. `/cursos/${slug}/preview/${l.slug}`.
|
|
370
|
+
* Called only for lessons with `isFreePreview`. Return `undefined` to keep the label.
|
|
371
|
+
*/
|
|
372
|
+
getPreviewHref?: (lesson: AcademyLessonPublic) => string | undefined;
|
|
373
|
+
/**
|
|
374
|
+
* Serialisable alternative to `getPreviewHref` for Server Component pages:
|
|
375
|
+
* `{lessonSlug}` and `{lessonId}` are replaced (URL-encoded). Ignored when
|
|
376
|
+
* `getPreviewHref` is given.
|
|
377
|
+
*/
|
|
378
|
+
previewHrefTemplate?: string;
|
|
379
|
+
/** Renders the call to action as a `<button>` (e.g. to open a modal). Used when no href resolves. */
|
|
380
|
+
onPreview?: (lesson: AcademyLessonPublic) => void;
|
|
354
381
|
}
|
|
355
|
-
declare function CourseSyllabus({ modules, labels: labelOverrides, headingLevel, headingClassName, className, }: CourseSyllabusProps): react_jsx_runtime.JSX.Element | null;
|
|
382
|
+
declare function CourseSyllabus({ modules, labels: labelOverrides, headingLevel, headingClassName, className, getPreviewHref, previewHrefTemplate, onPreview, }: CourseSyllabusProps): react_jsx_runtime.JSX.Element | null;
|
|
356
383
|
|
|
357
384
|
/**
|
|
358
385
|
* `DataRequestForm` — drop-in public GDPR rights form (REB-980) over
|
package/dist/client.js
CHANGED
|
@@ -707,7 +707,7 @@ function slotClassName(structural, defaults, consumer) {
|
|
|
707
707
|
}
|
|
708
708
|
|
|
709
709
|
// src/components/course-syllabus.tsx
|
|
710
|
-
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
710
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
711
711
|
var DEFAULT_COURSE_SYLLABUS_LABELS = {
|
|
712
712
|
heading: "Programa",
|
|
713
713
|
moduleSingular: "m\xF3dulo",
|
|
@@ -716,42 +716,104 @@ var DEFAULT_COURSE_SYLLABUS_LABELS = {
|
|
|
716
716
|
lessonPlural: "lecciones",
|
|
717
717
|
freePreview: "Vista previa gratuita",
|
|
718
718
|
hours: "h",
|
|
719
|
-
minutes: "min"
|
|
719
|
+
minutes: "min",
|
|
720
|
+
expandAll: "Expandir todo",
|
|
721
|
+
collapseAll: "Contraer todo",
|
|
722
|
+
videoLesson: "V\xEDdeo",
|
|
723
|
+
textLesson: "Lectura"
|
|
720
724
|
};
|
|
721
725
|
var SEPARATOR = " \xB7 ";
|
|
722
726
|
function count(n, singular, plural) {
|
|
723
727
|
return `${n} ${n === 1 ? singular : plural}`;
|
|
724
728
|
}
|
|
725
|
-
function
|
|
729
|
+
function formatTotal(seconds, labels) {
|
|
726
730
|
const totalMinutes = Math.max(1, Math.round(seconds / 60));
|
|
727
731
|
const hours = Math.floor(totalMinutes / 60);
|
|
728
732
|
const minutes = totalMinutes % 60;
|
|
729
733
|
if (hours === 0) return `${minutes} ${labels.minutes}`;
|
|
730
734
|
return minutes === 0 ? `${hours} ${labels.hours}` : `${hours} ${labels.hours} ${minutes} ${labels.minutes}`;
|
|
731
735
|
}
|
|
732
|
-
function
|
|
736
|
+
function splitSeconds(seconds) {
|
|
737
|
+
const whole = Math.round(seconds);
|
|
738
|
+
return [Math.floor(whole / 3600), Math.floor(whole % 3600 / 60), whole % 60];
|
|
739
|
+
}
|
|
740
|
+
function formatClock(seconds) {
|
|
741
|
+
const [h, m, s] = splitSeconds(seconds);
|
|
742
|
+
const ss = String(s).padStart(2, "0");
|
|
743
|
+
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
744
|
+
}
|
|
745
|
+
function isoDuration(seconds) {
|
|
746
|
+
const [h, m, s] = splitSeconds(seconds);
|
|
747
|
+
return `PT${h > 0 ? `${h}H` : ""}${m}M${s}S`;
|
|
748
|
+
}
|
|
749
|
+
function hasDuration(lesson) {
|
|
750
|
+
return typeof lesson.durationSeconds === "number" && lesson.durationSeconds > 0;
|
|
751
|
+
}
|
|
752
|
+
function totalSeconds(lessons) {
|
|
753
|
+
if (lessons.length === 0) return null;
|
|
733
754
|
let total = 0;
|
|
734
755
|
for (const l of lessons) {
|
|
735
|
-
if (
|
|
756
|
+
if (!hasDuration(l)) return null;
|
|
736
757
|
total += l.durationSeconds;
|
|
737
758
|
}
|
|
738
759
|
return total;
|
|
739
760
|
}
|
|
761
|
+
function fillTemplate(template, lesson) {
|
|
762
|
+
return template.replaceAll("{lessonSlug}", encodeURIComponent(lesson.slug)).replaceAll("{lessonId}", encodeURIComponent(lesson.id));
|
|
763
|
+
}
|
|
764
|
+
var ICON_PROPS = {
|
|
765
|
+
"aria-hidden": true,
|
|
766
|
+
viewBox: "0 0 24 24",
|
|
767
|
+
fill: "none",
|
|
768
|
+
stroke: "currentColor",
|
|
769
|
+
strokeWidth: 2,
|
|
770
|
+
strokeLinecap: "round",
|
|
771
|
+
strokeLinejoin: "round"
|
|
772
|
+
};
|
|
773
|
+
function LessonIcon({ type }) {
|
|
774
|
+
return /* @__PURE__ */ jsx8(
|
|
775
|
+
"svg",
|
|
776
|
+
{
|
|
777
|
+
...ICON_PROPS,
|
|
778
|
+
"data-slot": "course-syllabus-lesson-icon",
|
|
779
|
+
"data-type": type,
|
|
780
|
+
className: "size-4 shrink-0 text-muted-foreground",
|
|
781
|
+
children: type === "video" ? /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
782
|
+
/* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "9" }),
|
|
783
|
+
/* @__PURE__ */ jsx8("path", { d: "m10 8.5 5 3.5-5 3.5z" })
|
|
784
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
785
|
+
/* @__PURE__ */ jsx8("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }),
|
|
786
|
+
/* @__PURE__ */ jsx8("path", { d: "M14 3v5h5M9 13h6M9 17h4" })
|
|
787
|
+
] })
|
|
788
|
+
}
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
function PlayIcon() {
|
|
792
|
+
return /* @__PURE__ */ jsx8("svg", { ...ICON_PROPS, className: "size-3.5 shrink-0", children: /* @__PURE__ */ jsx8("path", { d: "m7 4 13 8-13 8z", fill: "currentColor" }) });
|
|
793
|
+
}
|
|
794
|
+
var PREVIEW_CTA = "inline-flex items-center gap-1.5 rounded-full border border-primary/40 bg-primary/10 px-3 py-1 text-xs font-semibold text-primary transition-colors hover:bg-primary hover:text-primary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none";
|
|
740
795
|
function CourseSyllabus({
|
|
741
796
|
modules,
|
|
742
797
|
labels: labelOverrides,
|
|
743
798
|
headingLevel = 2,
|
|
744
799
|
headingClassName,
|
|
745
|
-
className
|
|
800
|
+
className,
|
|
801
|
+
getPreviewHref,
|
|
802
|
+
previewHrefTemplate,
|
|
803
|
+
onPreview
|
|
746
804
|
}) {
|
|
747
805
|
const labels = { ...DEFAULT_COURSE_SYLLABUS_LABELS, ...labelOverrides };
|
|
748
806
|
const baseId = useId2();
|
|
749
807
|
const [open, setOpen] = useState6(() => /* @__PURE__ */ new Set());
|
|
750
808
|
if (modules.length === 0) return null;
|
|
751
|
-
const
|
|
809
|
+
const expandable = modules.filter((m) => (m.lessons?.length ?? 0) > 0).map((m) => m.id);
|
|
810
|
+
const allLessons = modules.flatMap((m) => m.lessons ?? []);
|
|
811
|
+
const courseSeconds = totalSeconds(allLessons);
|
|
752
812
|
const summary = [count(modules.length, labels.moduleSingular, labels.modulePlural)];
|
|
753
|
-
if (
|
|
754
|
-
summary.push(count(
|
|
813
|
+
if (allLessons.length > 0)
|
|
814
|
+
summary.push(count(allLessons.length, labels.lessonSingular, labels.lessonPlural));
|
|
815
|
+
if (courseSeconds !== null) summary.push(formatTotal(courseSeconds, labels));
|
|
816
|
+
const allOpen = expandable.length > 0 && expandable.every((id) => open.has(id));
|
|
755
817
|
const headingId = `${baseId}-heading`;
|
|
756
818
|
const Tag = `h${headingLevel}`;
|
|
757
819
|
function toggle(id) {
|
|
@@ -762,6 +824,44 @@ function CourseSyllabus({
|
|
|
762
824
|
return next;
|
|
763
825
|
});
|
|
764
826
|
}
|
|
827
|
+
function previewHref(lesson) {
|
|
828
|
+
if (getPreviewHref) return getPreviewHref(lesson);
|
|
829
|
+
return previewHrefTemplate ? fillTemplate(previewHrefTemplate, lesson) : void 0;
|
|
830
|
+
}
|
|
831
|
+
function renderPreview(lesson) {
|
|
832
|
+
const name = `${labels.freePreview}: ${lesson.title}`;
|
|
833
|
+
const href = previewHref(lesson);
|
|
834
|
+
if (href) {
|
|
835
|
+
return /* @__PURE__ */ jsxs6("a", { href, "aria-label": name, "data-slot": "course-syllabus-preview", className: PREVIEW_CTA, children: [
|
|
836
|
+
/* @__PURE__ */ jsx8(PlayIcon, {}),
|
|
837
|
+
labels.freePreview
|
|
838
|
+
] });
|
|
839
|
+
}
|
|
840
|
+
if (onPreview) {
|
|
841
|
+
return /* @__PURE__ */ jsxs6(
|
|
842
|
+
"button",
|
|
843
|
+
{
|
|
844
|
+
type: "button",
|
|
845
|
+
"aria-label": name,
|
|
846
|
+
"data-slot": "course-syllabus-preview",
|
|
847
|
+
onClick: () => onPreview(lesson),
|
|
848
|
+
className: cn8(PREVIEW_CTA, "cursor-pointer"),
|
|
849
|
+
children: [
|
|
850
|
+
/* @__PURE__ */ jsx8(PlayIcon, {}),
|
|
851
|
+
labels.freePreview
|
|
852
|
+
]
|
|
853
|
+
}
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
return /* @__PURE__ */ jsx8(
|
|
857
|
+
"span",
|
|
858
|
+
{
|
|
859
|
+
"data-slot": "course-syllabus-preview",
|
|
860
|
+
className: "rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground",
|
|
861
|
+
children: labels.freePreview
|
|
862
|
+
}
|
|
863
|
+
);
|
|
864
|
+
}
|
|
765
865
|
return /* @__PURE__ */ jsxs6(
|
|
766
866
|
"section",
|
|
767
867
|
{
|
|
@@ -769,24 +869,43 @@ function CourseSyllabus({
|
|
|
769
869
|
"aria-labelledby": headingId,
|
|
770
870
|
className: cn8("space-y-4", className),
|
|
771
871
|
children: [
|
|
772
|
-
/* @__PURE__ */ jsxs6(
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
children:
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
872
|
+
/* @__PURE__ */ jsxs6(
|
|
873
|
+
"div",
|
|
874
|
+
{
|
|
875
|
+
"data-slot": "course-syllabus-header",
|
|
876
|
+
className: "flex flex-wrap items-end justify-between gap-x-4 gap-y-2",
|
|
877
|
+
children: [
|
|
878
|
+
/* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
|
|
879
|
+
/* @__PURE__ */ jsx8(
|
|
880
|
+
Tag,
|
|
881
|
+
{
|
|
882
|
+
id: headingId,
|
|
883
|
+
className: slotClassName("", "text-xl font-semibold", headingClassName),
|
|
884
|
+
children: labels.heading
|
|
885
|
+
}
|
|
886
|
+
),
|
|
887
|
+
/* @__PURE__ */ jsx8("p", { "data-slot": "course-syllabus-summary", className: "text-sm text-muted-foreground", children: summary.join(SEPARATOR) })
|
|
888
|
+
] }),
|
|
889
|
+
expandable.length > 0 && /* @__PURE__ */ jsx8(
|
|
890
|
+
"button",
|
|
891
|
+
{
|
|
892
|
+
type: "button",
|
|
893
|
+
"data-slot": "course-syllabus-toggle-all",
|
|
894
|
+
onClick: () => setOpen(allOpen ? /* @__PURE__ */ new Set() : new Set(expandable)),
|
|
895
|
+
className: "cursor-pointer rounded-md px-2 py-1 text-sm font-semibold text-primary underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
|
|
896
|
+
children: allOpen ? labels.collapseAll : labels.expandAll
|
|
897
|
+
}
|
|
898
|
+
)
|
|
899
|
+
]
|
|
900
|
+
}
|
|
901
|
+
),
|
|
902
|
+
/* @__PURE__ */ jsx8("ol", { className: "list-none divide-y divide-border overflow-hidden rounded-xl border border-border bg-card p-0", children: modules.map((module, index) => {
|
|
784
903
|
const lessons = module.lessons ?? [];
|
|
785
904
|
const number = /* @__PURE__ */ jsx8(
|
|
786
905
|
"span",
|
|
787
906
|
{
|
|
788
907
|
"aria-hidden": "true",
|
|
789
|
-
className: "flex size-
|
|
908
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-full bg-primary/10 text-sm font-semibold text-primary tabular-nums",
|
|
790
909
|
children: index + 1
|
|
791
910
|
}
|
|
792
911
|
);
|
|
@@ -795,7 +914,7 @@ function CourseSyllabus({
|
|
|
795
914
|
"li",
|
|
796
915
|
{
|
|
797
916
|
"data-slot": "course-syllabus-module",
|
|
798
|
-
className: "flex items-center gap-
|
|
917
|
+
className: "flex items-center gap-4 px-4 py-4 sm:px-5",
|
|
799
918
|
children: [
|
|
800
919
|
number,
|
|
801
920
|
/* @__PURE__ */ jsx8("span", { className: "font-semibold leading-snug", children: module.title })
|
|
@@ -806,69 +925,85 @@ function CourseSyllabus({
|
|
|
806
925
|
}
|
|
807
926
|
const isOpen = open.has(module.id);
|
|
808
927
|
const panelId = `${baseId}-module-${index}`;
|
|
809
|
-
const seconds =
|
|
928
|
+
const seconds = totalSeconds(lessons);
|
|
810
929
|
const meta = [count(lessons.length, labels.lessonSingular, labels.lessonPlural)];
|
|
811
|
-
if (seconds !== null) meta.push(
|
|
812
|
-
return /* @__PURE__ */ jsxs6(
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
}
|
|
930
|
+
if (seconds !== null) meta.push(formatTotal(seconds, labels));
|
|
931
|
+
return /* @__PURE__ */ jsxs6("li", { "data-slot": "course-syllabus-module", children: [
|
|
932
|
+
/* @__PURE__ */ jsxs6(
|
|
933
|
+
"button",
|
|
934
|
+
{
|
|
935
|
+
type: "button",
|
|
936
|
+
"aria-expanded": isOpen,
|
|
937
|
+
"aria-controls": panelId,
|
|
938
|
+
onClick: () => toggle(module.id),
|
|
939
|
+
className: cn8(
|
|
940
|
+
"flex w-full cursor-pointer items-center gap-4 px-4 py-4 text-left transition-colors hover:bg-muted/60 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none sm:px-5",
|
|
941
|
+
isOpen && "bg-muted/40"
|
|
845
942
|
),
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
{
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
943
|
+
children: [
|
|
944
|
+
number,
|
|
945
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
946
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold leading-snug", children: module.title }),
|
|
947
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm text-muted-foreground", children: meta.join(SEPARATOR) })
|
|
948
|
+
] }),
|
|
949
|
+
/* @__PURE__ */ jsx8(
|
|
950
|
+
"svg",
|
|
951
|
+
{
|
|
952
|
+
...ICON_PROPS,
|
|
953
|
+
"data-slot": "course-syllabus-chevron",
|
|
954
|
+
className: cn8(
|
|
955
|
+
"size-5 shrink-0 text-muted-foreground transition-transform duration-200 motion-reduce:transition-none",
|
|
956
|
+
isOpen && "rotate-180"
|
|
957
|
+
),
|
|
958
|
+
children: /* @__PURE__ */ jsx8("path", { d: "m6 9 6 6 6-6" })
|
|
959
|
+
}
|
|
960
|
+
)
|
|
961
|
+
]
|
|
962
|
+
}
|
|
963
|
+
),
|
|
964
|
+
/* @__PURE__ */ jsx8(
|
|
965
|
+
"ol",
|
|
966
|
+
{
|
|
967
|
+
id: panelId,
|
|
968
|
+
hidden: !isOpen,
|
|
969
|
+
"data-slot": "course-syllabus-lessons",
|
|
970
|
+
className: "list-none border-t border-border p-0 py-1",
|
|
971
|
+
children: lessons.map((lesson) => {
|
|
972
|
+
const type = lesson.hasVideo ? "video" : "text";
|
|
973
|
+
return /* @__PURE__ */ jsxs6(
|
|
974
|
+
"li",
|
|
975
|
+
{
|
|
976
|
+
"data-slot": "course-syllabus-lesson",
|
|
977
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1.5 px-4 py-2.5 text-sm sm:pl-[4.25rem] sm:pr-5",
|
|
978
|
+
children: [
|
|
979
|
+
/* @__PURE__ */ jsx8(LessonIcon, { type }),
|
|
980
|
+
/* @__PURE__ */ jsx8("span", { className: "sr-only", children: type === "video" ? labels.videoLesson : labels.textLesson }),
|
|
981
|
+
/* @__PURE__ */ jsx8(
|
|
982
|
+
"span",
|
|
983
|
+
{
|
|
984
|
+
"data-slot": "course-syllabus-lesson-title",
|
|
985
|
+
className: "min-w-0 flex-1 leading-snug",
|
|
986
|
+
children: lesson.title
|
|
987
|
+
}
|
|
988
|
+
),
|
|
989
|
+
lesson.isFreePreview && renderPreview(lesson),
|
|
990
|
+
hasDuration(lesson) && /* @__PURE__ */ jsx8(
|
|
991
|
+
"time",
|
|
992
|
+
{
|
|
993
|
+
"data-slot": "course-syllabus-lesson-duration",
|
|
994
|
+
dateTime: isoDuration(lesson.durationSeconds),
|
|
995
|
+
className: "ml-auto shrink-0 text-muted-foreground tabular-nums",
|
|
996
|
+
children: formatClock(lesson.durationSeconds)
|
|
997
|
+
}
|
|
998
|
+
)
|
|
999
|
+
]
|
|
1000
|
+
},
|
|
1001
|
+
lesson.id
|
|
1002
|
+
);
|
|
1003
|
+
})
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
1006
|
+
] }, module.id);
|
|
872
1007
|
}) })
|
|
873
1008
|
]
|
|
874
1009
|
}
|
|
@@ -881,7 +1016,7 @@ import { cn as cn9 } from "@reblu/site-ui/utils";
|
|
|
881
1016
|
import {
|
|
882
1017
|
DATA_REQUEST_TYPES
|
|
883
1018
|
} from "@reblu/site-contracts";
|
|
884
|
-
import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1019
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
885
1020
|
var DATA_REQUEST_FIELD_LIMITS = Object.freeze({ name: 200, email: 320, details: 5e3 });
|
|
886
1021
|
var DEFAULT_DATA_REQUEST_FORM_LABELS = {
|
|
887
1022
|
requestTypeLabel: "Derecho que quieres ejercer",
|
|
@@ -907,7 +1042,7 @@ var DEFAULT_DATA_REQUEST_FORM_LABELS = {
|
|
|
907
1042
|
nameRequired: "Indica tu nombre.",
|
|
908
1043
|
emailInvalid: "Introduce un email v\xE1lido.",
|
|
909
1044
|
consentRequired: "Debes aceptar el tratamiento de tus datos para gestionar la solicitud.",
|
|
910
|
-
success: (deadline) => /* @__PURE__ */ jsxs7(
|
|
1045
|
+
success: (deadline) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
911
1046
|
"Hemos recibido tu solicitud. Te responderemos como m\xE1ximo el ",
|
|
912
1047
|
deadline,
|
|
913
1048
|
"."
|