@dreamtree-org/landing-kit 0.4.0 → 0.6.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/MIGRATION.md +12 -0
- package/README.md +61 -0
- package/demo-server.d.ts +21 -4
- package/dist/demo-server.cjs +53 -7
- package/dist/demo-server.js +48 -7
- package/dist/index.cjs +1065 -52
- package/dist/index.js +1065 -52
- package/dist/schema.cjs +728 -0
- package/dist/schema.js +676 -0
- package/index.d.ts +29 -0
- package/package.json +11 -3
- package/schema.d.ts +96 -0
- package/styles.css +122 -0
package/dist/index.js
CHANGED
|
@@ -129,7 +129,7 @@ function brandStyle(brand) {
|
|
|
129
129
|
}
|
|
130
130
|
function AuthShell({
|
|
131
131
|
brand,
|
|
132
|
-
title,
|
|
132
|
+
title: title2,
|
|
133
133
|
subtitle,
|
|
134
134
|
children,
|
|
135
135
|
footer,
|
|
@@ -174,7 +174,7 @@ function AuthShell({
|
|
|
174
174
|
] }),
|
|
175
175
|
/* @__PURE__ */ jsxs2("section", { className: "dt-auth-card", "aria-labelledby": headingId, children: [
|
|
176
176
|
/* @__PURE__ */ jsxs2("header", { className: "dt-auth-head", children: [
|
|
177
|
-
/* @__PURE__ */ jsx2("h1", { id: headingId, ...demoAccounts ? tapProps : {}, children:
|
|
177
|
+
/* @__PURE__ */ jsx2("h1", { id: headingId, ...demoAccounts ? tapProps : {}, children: title2 }),
|
|
178
178
|
subtitle ? /* @__PURE__ */ jsx2("p", { children: subtitle }) : null
|
|
179
179
|
] }),
|
|
180
180
|
children,
|
|
@@ -316,16 +316,16 @@ function Eyebrow({ children, className = "" }) {
|
|
|
316
316
|
if (!children) return null;
|
|
317
317
|
return /* @__PURE__ */ jsx7("p", { className: `dt-lp-eyebrow ${className}`.trim(), children });
|
|
318
318
|
}
|
|
319
|
-
function SectionIntro({ id, eyebrow, title, body, align = "start", children }) {
|
|
319
|
+
function SectionIntro({ id, eyebrow, title: title2, body, align = "start", children }) {
|
|
320
320
|
return /* @__PURE__ */ jsxs5("div", { className: `dt-lp-intro dt-lp-intro--${align}`, children: [
|
|
321
321
|
/* @__PURE__ */ jsx7(Eyebrow, { children: eyebrow }),
|
|
322
|
-
/* @__PURE__ */ jsx7("h2", { id, className: "dt-lp-h2", children:
|
|
322
|
+
/* @__PURE__ */ jsx7("h2", { id, className: "dt-lp-h2", children: title2 }),
|
|
323
323
|
body ? /* @__PURE__ */ jsx7("p", { className: "dt-lp-lead", children: body }) : null,
|
|
324
324
|
children
|
|
325
325
|
] });
|
|
326
326
|
}
|
|
327
|
-
function Section({ id, labelledBy, tone = "plain", className = "", children, narrow }) {
|
|
328
|
-
return /* @__PURE__ */ jsx7("section", { id, "aria-labelledby": labelledBy, className: `dt-lp-section dt-lp-tone--${
|
|
327
|
+
function Section({ id, labelledBy, tone: tone2 = "plain", className = "", children, narrow }) {
|
|
328
|
+
return /* @__PURE__ */ jsx7("section", { id, "aria-labelledby": labelledBy, className: `dt-lp-section dt-lp-tone--${tone2} ${className}`.trim(), children: /* @__PURE__ */ jsx7(Wrap, { narrow, children }) });
|
|
329
329
|
}
|
|
330
330
|
function TextLink({ href, children, className = "", ...rest }) {
|
|
331
331
|
return /* @__PURE__ */ jsxs5(SmartLink, { href, className: `dt-lp-textlink ${className}`.trim(), ...rest, children: [
|
|
@@ -467,7 +467,7 @@ function MarketingLayout({
|
|
|
467
467
|
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
468
468
|
function Hero({
|
|
469
469
|
eyebrow,
|
|
470
|
-
title,
|
|
470
|
+
title: title2,
|
|
471
471
|
subtitle,
|
|
472
472
|
lead,
|
|
473
473
|
chips,
|
|
@@ -478,7 +478,7 @@ function Hero({
|
|
|
478
478
|
id = "hero",
|
|
479
479
|
backdrop
|
|
480
480
|
}) {
|
|
481
|
-
const [first, hi] = Array.isArray(
|
|
481
|
+
const [first, hi] = Array.isArray(title2) ? title2 : [title2, null];
|
|
482
482
|
return /* @__PURE__ */ jsxs9("section", { id, "aria-labelledby": "hero-title", className: `dt-lp-hero${overlap ? " dt-lp-hero--overlap" : ""}`, children: [
|
|
483
483
|
/* @__PURE__ */ jsx11("div", { className: "dt-lp-hero-glow", "aria-hidden": "true" }),
|
|
484
484
|
backdrop ? /* @__PURE__ */ jsx11("div", { className: "dt-lp-hero-backdrop", "aria-hidden": "true", children: backdrop }) : null,
|
|
@@ -515,8 +515,8 @@ function Items({ items, kind }) {
|
|
|
515
515
|
] })
|
|
516
516
|
] }, f.title)) });
|
|
517
517
|
}
|
|
518
|
-
function Checklist({ items, icon: Icon = Check, tone = "good", labelledBy }) {
|
|
519
|
-
return /* @__PURE__ */ jsx12("ul", { className: `dt-lp-checklist dt-lp-checklist--${
|
|
518
|
+
function Checklist({ items, icon: Icon = Check, tone: tone2 = "good", labelledBy }) {
|
|
519
|
+
return /* @__PURE__ */ jsx12("ul", { className: `dt-lp-checklist dt-lp-checklist--${tone2}`, "aria-labelledby": labelledBy, children: items.map((x) => /* @__PURE__ */ jsxs10("li", { children: [
|
|
520
520
|
/* @__PURE__ */ jsx12(Icon, { size: 18 }),
|
|
521
521
|
/* @__PURE__ */ jsx12("span", { children: x })
|
|
522
522
|
] }, x)) });
|
|
@@ -545,10 +545,10 @@ function Chips({ items, label, className = "" }) {
|
|
|
545
545
|
return /* @__PURE__ */ jsx12("ul", { className: `dt-lp-chips dt-lp-chips--start ${className}`.trim(), "aria-label": label, children: items.map((c) => /* @__PURE__ */ jsx12("li", { children: c }, c)) });
|
|
546
546
|
}
|
|
547
547
|
function FeatureSection(props) {
|
|
548
|
-
const { id, variant = "cards", tone, eyebrow, title, body, items = [], cta, footnote, media, reverse, bullets } = props;
|
|
549
|
-
const
|
|
548
|
+
const { id, variant = "cards", tone: tone2, eyebrow, title: title2, body, items = [], cta, footnote, media, reverse, bullets } = props;
|
|
549
|
+
const hid2 = `${id || variant}-title`;
|
|
550
550
|
const defaultTone = { highlight: "dark", split: "tinted", compare: "muted" }[variant] || "plain";
|
|
551
|
-
const shell = (children, extra = {}) => /* @__PURE__ */ jsx12(Section, { id, labelledBy:
|
|
551
|
+
const shell = (children, extra = {}) => /* @__PURE__ */ jsx12(Section, { id, labelledBy: hid2, tone: tone2 || defaultTone, className: `dt-lp-feature dt-lp-feature--${variant}`, ...extra, children });
|
|
552
552
|
switch (variant) {
|
|
553
553
|
case "split":
|
|
554
554
|
return shell(
|
|
@@ -557,7 +557,7 @@ function FeatureSection(props) {
|
|
|
557
557
|
{
|
|
558
558
|
className: "dt-lp-split",
|
|
559
559
|
reverse,
|
|
560
|
-
text: /* @__PURE__ */ jsxs10(SectionIntro, { id:
|
|
560
|
+
text: /* @__PURE__ */ jsxs10(SectionIntro, { id: hid2, eyebrow, title: title2, body, children: [
|
|
561
561
|
media ? /* @__PURE__ */ jsx12(InlineItems, { items }) : null,
|
|
562
562
|
(bullets == null ? void 0 : bullets.length) ? /* @__PURE__ */ jsx12(Checklist, { items: bullets }) : null,
|
|
563
563
|
/* @__PURE__ */ jsx12(Cta, { cta, className: "dt-lp-mt" })
|
|
@@ -569,7 +569,7 @@ function FeatureSection(props) {
|
|
|
569
569
|
case "list":
|
|
570
570
|
return shell(
|
|
571
571
|
/* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
572
|
-
/* @__PURE__ */ jsx12(SectionIntro, { id:
|
|
572
|
+
/* @__PURE__ */ jsx12(SectionIntro, { id: hid2, eyebrow, title: title2, body, align: "center" }),
|
|
573
573
|
/* @__PURE__ */ jsx12(Items, { items, kind: "list" }),
|
|
574
574
|
/* @__PURE__ */ jsx12(Cta, { cta, className: "dt-lp-mt" })
|
|
575
575
|
] })
|
|
@@ -579,14 +579,14 @@ function FeatureSection(props) {
|
|
|
579
579
|
return shell(
|
|
580
580
|
/* @__PURE__ */ jsxs10("div", { className: "dt-lp-intro-grid", children: [
|
|
581
581
|
/* @__PURE__ */ jsxs10("div", { children: [
|
|
582
|
-
/* @__PURE__ */ jsx12(SectionIntro, { id:
|
|
582
|
+
/* @__PURE__ */ jsx12(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
|
|
583
583
|
subtitle ? /* @__PURE__ */ jsx12("h3", { className: "dt-lp-h3 dt-lp-h3--lg", children: subtitle }) : null,
|
|
584
584
|
detail ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-body", children: detail }) : null,
|
|
585
585
|
note ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-note", children: note }) : null,
|
|
586
586
|
/* @__PURE__ */ jsx12(Cta, { cta, className: "dt-lp-mt" })
|
|
587
587
|
] }),
|
|
588
|
-
glance.length ? /* @__PURE__ */ jsxs10("aside", { className: "dt-lp-glance", "aria-labelledby": `${
|
|
589
|
-
/* @__PURE__ */ jsx12("p", { id: `${
|
|
588
|
+
glance.length ? /* @__PURE__ */ jsxs10("aside", { className: "dt-lp-glance", "aria-labelledby": `${hid2}-glance`, children: [
|
|
589
|
+
/* @__PURE__ */ jsx12("p", { id: `${hid2}-glance`, className: "dt-lp-eyebrow", children: glanceTitle }),
|
|
590
590
|
/* @__PURE__ */ jsx12(Checklist, { items: glance }),
|
|
591
591
|
props.glanceNote ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-note", children: props.glanceNote }) : null,
|
|
592
592
|
link ? /* @__PURE__ */ jsx12(TextLink, { href: link.href, className: "dt-lp-mt", children: link.label }) : null
|
|
@@ -598,7 +598,7 @@ function FeatureSection(props) {
|
|
|
598
598
|
const { note, noteLabel = "Important:" } = props;
|
|
599
599
|
const text = /* @__PURE__ */ jsxs10("div", { className: "dt-lp-intro", children: [
|
|
600
600
|
/* @__PURE__ */ jsx12(Eyebrow, { children: eyebrow }),
|
|
601
|
-
/* @__PURE__ */ jsx12("h2", { id:
|
|
601
|
+
/* @__PURE__ */ jsx12("h2", { id: hid2, className: "dt-lp-h2", children: title2 }),
|
|
602
602
|
body ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-lead", children: body }) : null,
|
|
603
603
|
note ? /* @__PURE__ */ jsxs10("p", { className: "dt-lp-callout", children: [
|
|
604
604
|
/* @__PURE__ */ jsx12(Alert, { size: 20 }),
|
|
@@ -620,7 +620,7 @@ function FeatureSection(props) {
|
|
|
620
620
|
case "band": {
|
|
621
621
|
const { examples, examplesLabel = "Examples" } = props;
|
|
622
622
|
const text = /* @__PURE__ */ jsxs10("div", { children: [
|
|
623
|
-
/* @__PURE__ */ jsx12(SectionIntro, { id:
|
|
623
|
+
/* @__PURE__ */ jsx12(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
|
|
624
624
|
/* @__PURE__ */ jsx12(Chips, { items: examples, label: examplesLabel, className: "dt-lp-mt" }),
|
|
625
625
|
media ? /* @__PURE__ */ jsx12(InlineItems, { items }) : null,
|
|
626
626
|
/* @__PURE__ */ jsx12(Cta, { cta, className: "dt-lp-mt" })
|
|
@@ -635,13 +635,13 @@ function FeatureSection(props) {
|
|
|
635
635
|
const { columns = [] } = props;
|
|
636
636
|
return shell(
|
|
637
637
|
/* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
638
|
-
/* @__PURE__ */ jsx12(SectionIntro, { id:
|
|
638
|
+
/* @__PURE__ */ jsx12(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
|
|
639
639
|
/* @__PURE__ */ jsx12("div", { className: "dt-lp-compare", children: columns.map((col, i) => {
|
|
640
640
|
const cid = `${id || "compare"}-${i + 1}`;
|
|
641
|
-
const
|
|
641
|
+
const tone3 = col.tone || (i === 0 ? "good" : "check");
|
|
642
642
|
return /* @__PURE__ */ jsxs10("div", { className: "dt-lp-card dt-lp-card--compare", children: [
|
|
643
643
|
/* @__PURE__ */ jsx12("h3", { id: cid, className: "dt-lp-h3 dt-lp-h3--lg", children: col.title }),
|
|
644
|
-
/* @__PURE__ */ jsx12(Checklist, { items: col.items, tone:
|
|
644
|
+
/* @__PURE__ */ jsx12(Checklist, { items: col.items, tone: tone3, icon: tone3 === "good" ? Check : Help, labelledBy: cid })
|
|
645
645
|
] }, cid);
|
|
646
646
|
}) })
|
|
647
647
|
] })
|
|
@@ -650,7 +650,7 @@ function FeatureSection(props) {
|
|
|
650
650
|
case "links":
|
|
651
651
|
return shell(
|
|
652
652
|
/* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
653
|
-
/* @__PURE__ */ jsx12(SectionIntro, { id:
|
|
653
|
+
/* @__PURE__ */ jsx12(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
|
|
654
654
|
/* @__PURE__ */ jsx12("ul", { className: "dt-lp-grid dt-lp-grid--links", children: items.map((s) => /* @__PURE__ */ jsx12("li", { children: /* @__PURE__ */ jsxs10(SmartLink, { href: s.href, className: "dt-lp-linkcard", children: [
|
|
655
655
|
/* @__PURE__ */ jsxs10("span", { className: "dt-lp-linkcard-title", children: [
|
|
656
656
|
s.title,
|
|
@@ -664,7 +664,7 @@ function FeatureSection(props) {
|
|
|
664
664
|
case "detail": {
|
|
665
665
|
return shell(
|
|
666
666
|
/* @__PURE__ */ jsxs10("div", { className: "dt-lp-detail", children: [
|
|
667
|
-
/* @__PURE__ */ jsx12("h2", { id:
|
|
667
|
+
/* @__PURE__ */ jsx12("h2", { id: hid2, className: "dt-lp-h2", children: title2 }),
|
|
668
668
|
/* @__PURE__ */ jsxs10("div", { children: [
|
|
669
669
|
body ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-body", children: body }) : null,
|
|
670
670
|
(bullets == null ? void 0 : bullets.length) ? /* @__PURE__ */ jsx12(Checklist, { items: bullets }) : null
|
|
@@ -675,7 +675,7 @@ function FeatureSection(props) {
|
|
|
675
675
|
default:
|
|
676
676
|
return shell(
|
|
677
677
|
/* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
678
|
-
/* @__PURE__ */ jsx12(SectionIntro, { id:
|
|
678
|
+
/* @__PURE__ */ jsx12(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
|
|
679
679
|
/* @__PURE__ */ jsx12(Items, { items, kind: "cards" }),
|
|
680
680
|
footnote ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-footnote", children: footnote }) : null,
|
|
681
681
|
/* @__PURE__ */ jsx12(Cta, { cta, className: "dt-lp-mt" })
|
|
@@ -686,10 +686,10 @@ function FeatureSection(props) {
|
|
|
686
686
|
|
|
687
687
|
// src/Audiences.jsx
|
|
688
688
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
689
|
-
function Audiences({ id = "audiences", eyebrow, title, body, items = [], linkLabel = "See how it works", tone = "muted" }) {
|
|
690
|
-
const
|
|
691
|
-
return /* @__PURE__ */ jsxs11(Section, { id, labelledBy:
|
|
692
|
-
/* @__PURE__ */ jsx13(SectionIntro, { id:
|
|
689
|
+
function Audiences({ id = "audiences", eyebrow, title: title2, body, items = [], linkLabel = "See how it works", tone: tone2 = "muted" }) {
|
|
690
|
+
const hid2 = `${id}-title`;
|
|
691
|
+
return /* @__PURE__ */ jsxs11(Section, { id, labelledBy: hid2, tone: tone2, className: "dt-lp-audiences", children: [
|
|
692
|
+
/* @__PURE__ */ jsx13(SectionIntro, { id: hid2, eyebrow, title: title2, body, align: "center" }),
|
|
693
693
|
/* @__PURE__ */ jsx13("div", { className: `dt-lp-grid dt-lp-grid--audiences dt-lp-grid--n${Math.min(items.length, 4)}`, children: items.map((a, i) => {
|
|
694
694
|
var _a;
|
|
695
695
|
const aid = a.id || `${id}-${i + 1}`;
|
|
@@ -718,11 +718,11 @@ function Audiences({ id = "audiences", eyebrow, title, body, items = [], linkLab
|
|
|
718
718
|
|
|
719
719
|
// src/HowItWorks.jsx
|
|
720
720
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
721
|
-
function HowItWorks({ id = "how-it-works", eyebrow, title, body, steps, items, tone = "plain", cta }) {
|
|
721
|
+
function HowItWorks({ id = "how-it-works", eyebrow, title: title2, body, steps, items, tone: tone2 = "plain", cta }) {
|
|
722
722
|
const list = steps || items || [];
|
|
723
|
-
const
|
|
724
|
-
return /* @__PURE__ */ jsxs12(Section, { id, labelledBy:
|
|
725
|
-
/* @__PURE__ */ jsx14(SectionIntro, { id:
|
|
723
|
+
const hid2 = `${id}-title`;
|
|
724
|
+
return /* @__PURE__ */ jsxs12(Section, { id, labelledBy: hid2, tone: tone2, className: "dt-lp-steps", children: [
|
|
725
|
+
/* @__PURE__ */ jsx14(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
|
|
726
726
|
/* @__PURE__ */ jsx14("ol", { className: "dt-lp-grid dt-lp-grid--steps", children: list.map((st, i) => /* @__PURE__ */ jsxs12("li", { className: "dt-lp-card", children: [
|
|
727
727
|
/* @__PURE__ */ jsx14("span", { className: "dt-lp-step-num", "aria-hidden": "true", children: i + 1 }),
|
|
728
728
|
/* @__PURE__ */ jsx14("h3", { className: "dt-lp-h3", children: st.name || st.title }),
|
|
@@ -734,10 +734,10 @@ function HowItWorks({ id = "how-it-works", eyebrow, title, body, steps, items, t
|
|
|
734
734
|
|
|
735
735
|
// src/Faq.jsx
|
|
736
736
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
737
|
-
function Faq({ id = "faq", eyebrow, title = "Frequently asked questions", intro, items = [], openFirst = true, tone = "muted" }) {
|
|
738
|
-
const
|
|
739
|
-
return /* @__PURE__ */ jsxs13(Section, { id, labelledBy:
|
|
740
|
-
/* @__PURE__ */ jsx15(SectionIntro, { id:
|
|
737
|
+
function Faq({ id = "faq", eyebrow, title: title2 = "Frequently asked questions", intro: intro2, items = [], openFirst = true, tone: tone2 = "muted" }) {
|
|
738
|
+
const hid2 = `${id}-title`;
|
|
739
|
+
return /* @__PURE__ */ jsxs13(Section, { id, labelledBy: hid2, tone: tone2, narrow: true, className: "dt-lp-faq", children: [
|
|
740
|
+
/* @__PURE__ */ jsx15(SectionIntro, { id: hid2, eyebrow, title: title2, body: intro2, align: "center" }),
|
|
741
741
|
/* @__PURE__ */ jsx15("div", { className: "dt-lp-faq-list", children: items.map((f, i) => /* @__PURE__ */ jsxs13("details", { className: "dt-lp-faq-item", open: openFirst && i === 0, children: [
|
|
742
742
|
/* @__PURE__ */ jsxs13("summary", { children: [
|
|
743
743
|
/* @__PURE__ */ jsx15("h3", { className: "dt-lp-h3", children: f.q }),
|
|
@@ -750,10 +750,10 @@ function Faq({ id = "faq", eyebrow, title = "Frequently asked questions", intro,
|
|
|
750
750
|
|
|
751
751
|
// src/DemoCta.jsx
|
|
752
752
|
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
753
|
-
function DemoCta({ id = "demo", eyebrow, title, body, label = "Book a demo", chips, chipsLabel = "Business types", note, link }) {
|
|
753
|
+
function DemoCta({ id = "demo", eyebrow, title: title2, body, label = "Book a demo", chips, chipsLabel = "Business types", note, link }) {
|
|
754
754
|
return /* @__PURE__ */ jsx16("section", { id, "aria-labelledby": `${id}-title`, className: "dt-lp-section dt-lp-cta", children: /* @__PURE__ */ jsx16(Wrap, { children: /* @__PURE__ */ jsxs14("div", { className: "dt-lp-cta-panel", children: [
|
|
755
755
|
eyebrow ? /* @__PURE__ */ jsx16("p", { className: "dt-lp-eyebrow dt-lp-eyebrow--inverse", children: eyebrow }) : null,
|
|
756
|
-
/* @__PURE__ */ jsx16("h2", { id: `${id}-title`, className: "dt-lp-h2 dt-lp-h2--cta", children:
|
|
756
|
+
/* @__PURE__ */ jsx16("h2", { id: `${id}-title`, className: "dt-lp-h2 dt-lp-h2--cta", children: title2 }),
|
|
757
757
|
body ? /* @__PURE__ */ jsx16("p", { className: "dt-lp-cta-body", children: body }) : null,
|
|
758
758
|
/* @__PURE__ */ jsx16(BookDemoButton, { size: "lg", variant: "inverse", children: label }),
|
|
759
759
|
(chips == null ? void 0 : chips.length) ? /* @__PURE__ */ jsx16("ul", { className: "dt-lp-chips dt-lp-chips--inverse", "aria-label": chipsLabel, children: chips.map((k) => /* @__PURE__ */ jsx16("li", { children: k }, k)) }) : null,
|
|
@@ -858,7 +858,7 @@ var DEFAULT_DEMO_FIELDS = [
|
|
|
858
858
|
{ name: "email", label: "Email", type: "email", maxLength: 190, autoComplete: "email" },
|
|
859
859
|
{ name: "company", label: "Business name", maxLength: 160, autoComplete: "organization" }
|
|
860
860
|
];
|
|
861
|
-
var initial = (
|
|
861
|
+
var initial = (fields2) => Object.fromEntries(fields2.map((f) => [f.name, ""]));
|
|
862
862
|
function Field({ f, value, onChange, error }) {
|
|
863
863
|
const id = `dt-demo-${f.name}`;
|
|
864
864
|
const errId = `${id}-error`;
|
|
@@ -908,9 +908,9 @@ function DemoDialog({
|
|
|
908
908
|
onClose = () => {
|
|
909
909
|
},
|
|
910
910
|
onSubmit,
|
|
911
|
-
fields = DEFAULT_DEMO_FIELDS,
|
|
912
|
-
title,
|
|
913
|
-
intro = "Tell us a little about your business and we will walk you through the workflows that matter to your team.",
|
|
911
|
+
fields: fields2 = DEFAULT_DEMO_FIELDS,
|
|
912
|
+
title: title2,
|
|
913
|
+
intro: intro2 = "Tell us a little about your business and we will walk you through the workflows that matter to your team.",
|
|
914
914
|
fiduciary = "Dreamtree-Org",
|
|
915
915
|
consentText,
|
|
916
916
|
privacyHref = "/privacy",
|
|
@@ -920,7 +920,7 @@ function DemoDialog({
|
|
|
920
920
|
validate
|
|
921
921
|
}) {
|
|
922
922
|
const ref = useRef3(null);
|
|
923
|
-
const [form, setForm] = useState4(() => initial(
|
|
923
|
+
const [form, setForm] = useState4(() => initial(fields2));
|
|
924
924
|
const [website, setWebsite] = useState4("");
|
|
925
925
|
const [consent, setConsent] = useState4(false);
|
|
926
926
|
const [busy, setBusy] = useState4(false);
|
|
@@ -940,7 +940,7 @@ function DemoDialog({
|
|
|
940
940
|
setForm((f) => ({ ...f, [k]: e.target.value }));
|
|
941
941
|
if (fieldErrors[k]) setFieldErrors(({ [k]: _gone, ...rest }) => rest);
|
|
942
942
|
};
|
|
943
|
-
const ready = consent && (typeof validate === "function" ||
|
|
943
|
+
const ready = consent && (typeof validate === "function" || fields2.every((f) => !f.required || String(form[f.name] || "").trim()));
|
|
944
944
|
const submit = async (e) => {
|
|
945
945
|
e.preventDefault();
|
|
946
946
|
if (!ready || busy) return;
|
|
@@ -977,16 +977,16 @@ function DemoDialog({
|
|
|
977
977
|
},
|
|
978
978
|
children: [
|
|
979
979
|
/* @__PURE__ */ jsxs17("div", { className: "dt-lp-dialog-head", children: [
|
|
980
|
-
/* @__PURE__ */ jsx19("h2", { id: "dt-demo-title", children: sent ? "Thank you" :
|
|
980
|
+
/* @__PURE__ */ jsx19("h2", { id: "dt-demo-title", children: sent ? "Thank you" : title2 || `Book a ${name} demo` }),
|
|
981
981
|
/* @__PURE__ */ jsx19("button", { type: "button", className: "dt-lp-dialog-x", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx19(Close, { size: 18 }) })
|
|
982
982
|
] }),
|
|
983
983
|
/* @__PURE__ */ jsx19("div", { className: "dt-lp-dialog-body", children: sent ? /* @__PURE__ */ jsxs17("div", { className: "dt-lp-stack", children: [
|
|
984
984
|
/* @__PURE__ */ jsx19("p", { role: "status", children: thanks }),
|
|
985
985
|
/* @__PURE__ */ jsx19("button", { type: "button", className: "dt-lp-btn dt-lp-btn--outline dt-lp-btn--md", onClick: onClose, children: "Close" })
|
|
986
986
|
] }) : /* @__PURE__ */ jsxs17("form", { className: "dt-lp-stack", onSubmit: submit, noValidate: true, children: [
|
|
987
|
-
|
|
987
|
+
intro2 ? /* @__PURE__ */ jsx19("p", { className: "dt-lp-muted", children: intro2 }) : null,
|
|
988
988
|
err ? /* @__PURE__ */ jsx19("p", { role: "alert", className: "dt-lp-error", children: err }) : null,
|
|
989
|
-
/* @__PURE__ */ jsx19("div", { className: "dt-lp-fields", children:
|
|
989
|
+
/* @__PURE__ */ jsx19("div", { className: "dt-lp-fields", children: fields2.map((f) => /* @__PURE__ */ jsx19(Field, { f, value: form[f.name], onChange: set(f.name), error: fieldErrors[f.name] }, f.name)) }),
|
|
990
990
|
/* @__PURE__ */ jsx19("div", { "aria-hidden": "true", className: "dt-lp-honeypot", children: /* @__PURE__ */ jsxs17("label", { children: [
|
|
991
991
|
"Website",
|
|
992
992
|
/* @__PURE__ */ jsx19("input", { type: "text", name: "website", tabIndex: -1, autoComplete: "off", value: website, onChange: (e) => setWebsite(e.target.value) })
|
|
@@ -1023,11 +1023,11 @@ function Breadcrumbs({ items = [] }) {
|
|
|
1023
1023
|
] }, `${c.label}-${i}`);
|
|
1024
1024
|
}) }) });
|
|
1025
1025
|
}
|
|
1026
|
-
function PageHero({ breadcrumbs, eyebrow, title, lead, cta = "Book a demo", secondary }) {
|
|
1026
|
+
function PageHero({ breadcrumbs, eyebrow, title: title2, lead, cta = "Book a demo", secondary }) {
|
|
1027
1027
|
return /* @__PURE__ */ jsx20("section", { "aria-labelledby": "hero-title", className: "dt-lp-hero dt-lp-hero--page", children: /* @__PURE__ */ jsxs18(Wrap, { className: "dt-lp-hero-inner dt-lp-hero-inner--start", children: [
|
|
1028
1028
|
/* @__PURE__ */ jsx20(Breadcrumbs, { items: breadcrumbs }),
|
|
1029
1029
|
/* @__PURE__ */ jsx20(Eyebrow, { className: "dt-lp-mt-lg", children: eyebrow }),
|
|
1030
|
-
/* @__PURE__ */ jsx20("h1", { id: "hero-title", className: "dt-lp-h1 dt-lp-h1--page", children:
|
|
1030
|
+
/* @__PURE__ */ jsx20("h1", { id: "hero-title", className: "dt-lp-h1 dt-lp-h1--page", children: title2 }),
|
|
1031
1031
|
lead ? /* @__PURE__ */ jsx20("p", { id: "answer", className: "dt-lp-hero-lead", children: lead }) : null,
|
|
1032
1032
|
/* @__PURE__ */ jsxs18("div", { className: "dt-lp-actions dt-lp-actions--start", children: [
|
|
1033
1033
|
cta ? /* @__PURE__ */ jsx20(BookDemoButton, { size: "lg", children: cta }) : null,
|
|
@@ -1214,6 +1214,1012 @@ function toLandingBrand(file, copy = {}) {
|
|
|
1214
1214
|
};
|
|
1215
1215
|
return Object.fromEntries(Object.entries(brand).filter(([, v]) => v !== void 0));
|
|
1216
1216
|
}
|
|
1217
|
+
|
|
1218
|
+
// src/tenant/TenantPage.jsx
|
|
1219
|
+
import { Component, useMemo } from "react";
|
|
1220
|
+
|
|
1221
|
+
// src/schema/sections.js
|
|
1222
|
+
var ICON_NAMES = [
|
|
1223
|
+
"shield-check",
|
|
1224
|
+
"badge-check",
|
|
1225
|
+
"truck",
|
|
1226
|
+
"wrench",
|
|
1227
|
+
"refresh",
|
|
1228
|
+
"battery",
|
|
1229
|
+
"cpu",
|
|
1230
|
+
"laptop",
|
|
1231
|
+
"star",
|
|
1232
|
+
"headset",
|
|
1233
|
+
"rupee",
|
|
1234
|
+
"award",
|
|
1235
|
+
"clock",
|
|
1236
|
+
"map-pin",
|
|
1237
|
+
"phone",
|
|
1238
|
+
"check",
|
|
1239
|
+
"sparkles",
|
|
1240
|
+
"tag",
|
|
1241
|
+
"gift",
|
|
1242
|
+
"heart"
|
|
1243
|
+
];
|
|
1244
|
+
var TONES = [
|
|
1245
|
+
{ value: "plain", label: "Plain" },
|
|
1246
|
+
{ value: "tinted", label: "Brand tint" },
|
|
1247
|
+
{ value: "muted", label: "Muted" },
|
|
1248
|
+
{ value: "dark", label: "Dark" }
|
|
1249
|
+
];
|
|
1250
|
+
var tone = (d = "plain") => ({ key: "tone", kind: "select", label: "Background", options: TONES, default: d });
|
|
1251
|
+
var title = (d = "", max = 90) => ({ key: "title", kind: "text", label: "Heading", max, default: d });
|
|
1252
|
+
var intro = { key: "intro", kind: "textarea", label: "Intro", max: 240, default: "" };
|
|
1253
|
+
var ctaLabel = { key: "cta_label", kind: "text", label: "Button text", max: 30, default: "" };
|
|
1254
|
+
var ctaHref = { key: "cta_href", kind: "url", label: "Button link", default: "" };
|
|
1255
|
+
var SECTION_TYPES = {
|
|
1256
|
+
hero_slider: {
|
|
1257
|
+
label: "Hero slider",
|
|
1258
|
+
description: "Full-width slides with an image, heading, text and a button.",
|
|
1259
|
+
fields: [
|
|
1260
|
+
{
|
|
1261
|
+
key: "slides",
|
|
1262
|
+
kind: "list",
|
|
1263
|
+
label: "Slides",
|
|
1264
|
+
min: 1,
|
|
1265
|
+
max: 6,
|
|
1266
|
+
itemLabel: "heading",
|
|
1267
|
+
of: [
|
|
1268
|
+
{ key: "image", kind: "image", label: "Image (wide, 16:7)", default: null },
|
|
1269
|
+
{ key: "mobile_image", kind: "image", label: "Phone image (4:5, optional)", default: null },
|
|
1270
|
+
{ key: "eyebrow", kind: "text", label: "Small label above", max: 60, default: "" },
|
|
1271
|
+
{ key: "heading", kind: "text", label: "Heading", max: 90, required: true, default: "Your headline" },
|
|
1272
|
+
{ key: "text", kind: "textarea", label: "Text", max: 220, default: "" },
|
|
1273
|
+
ctaLabel,
|
|
1274
|
+
ctaHref,
|
|
1275
|
+
{ key: "align", kind: "select", label: "Text position", options: [{ value: "start", label: "Left" }, { value: "center", label: "Centre" }], default: "start" },
|
|
1276
|
+
{ key: "overlay", kind: "select", label: "Darken image", options: [{ value: "none", label: "None" }, { value: "light", label: "Light" }, { value: "strong", label: "Strong" }], default: "light" }
|
|
1277
|
+
],
|
|
1278
|
+
default: [{ heading: "Your headline" }]
|
|
1279
|
+
},
|
|
1280
|
+
{ key: "height", kind: "select", label: "Height", options: [{ value: "compact", label: "Compact" }, { value: "standard", label: "Standard" }, { value: "tall", label: "Tall" }], default: "standard" },
|
|
1281
|
+
{ key: "autoplay", kind: "bool", label: "Move to the next slide automatically", default: true },
|
|
1282
|
+
{ key: "interval", kind: "select", label: "Seconds per slide", options: [{ value: "4", label: "4" }, { value: "6", label: "6" }, { value: "8", label: "8" }], default: "6" }
|
|
1283
|
+
]
|
|
1284
|
+
},
|
|
1285
|
+
featured: {
|
|
1286
|
+
label: "Featured products",
|
|
1287
|
+
description: "The store's newest products (filled in by the app).",
|
|
1288
|
+
slot: true,
|
|
1289
|
+
fields: [title("Featured"), intro, { key: "limit", kind: "select", label: "How many", options: [{ value: "4", label: "4" }, { value: "8", label: "8" }, { value: "12", label: "12" }], default: "8" }, tone()]
|
|
1290
|
+
},
|
|
1291
|
+
categories: {
|
|
1292
|
+
label: "Shop by category",
|
|
1293
|
+
description: "Category tiles (filled in by the app).",
|
|
1294
|
+
slot: true,
|
|
1295
|
+
fields: [title("Shop by category"), tone()]
|
|
1296
|
+
},
|
|
1297
|
+
promo_banner: {
|
|
1298
|
+
label: "Promo banner",
|
|
1299
|
+
description: "A highlighted offer with an optional image and button.",
|
|
1300
|
+
fields: [
|
|
1301
|
+
{ key: "eyebrow", kind: "text", label: "Small label above", max: 60, default: "" },
|
|
1302
|
+
{ key: "heading", kind: "text", label: "Heading", max: 90, required: true, default: "Festive offer" },
|
|
1303
|
+
{ key: "text", kind: "textarea", label: "Text", max: 240, default: "" },
|
|
1304
|
+
ctaLabel,
|
|
1305
|
+
ctaHref,
|
|
1306
|
+
{ key: "image", kind: "image", label: "Image (optional)", default: null },
|
|
1307
|
+
{ key: "style", kind: "select", label: "Style", options: [{ value: "brand", label: "Brand colour" }, { value: "accent", label: "Accent colour" }, { value: "dark", label: "Dark" }], default: "brand" }
|
|
1308
|
+
]
|
|
1309
|
+
},
|
|
1310
|
+
trust_badges: {
|
|
1311
|
+
label: "Trust badges",
|
|
1312
|
+
description: "Why buy from you: icon, title and a line each.",
|
|
1313
|
+
fields: [
|
|
1314
|
+
title("Why buy from us"),
|
|
1315
|
+
intro,
|
|
1316
|
+
{
|
|
1317
|
+
key: "items",
|
|
1318
|
+
kind: "list",
|
|
1319
|
+
label: "Badges",
|
|
1320
|
+
min: 1,
|
|
1321
|
+
max: 8,
|
|
1322
|
+
itemLabel: "title",
|
|
1323
|
+
of: [
|
|
1324
|
+
{ key: "icon", kind: "icon", label: "Icon", default: "shield-check" },
|
|
1325
|
+
{ key: "title", kind: "text", label: "Title", max: 50, required: true, default: "Inspected and graded" },
|
|
1326
|
+
{ key: "text", kind: "textarea", label: "Text", max: 140, default: "" }
|
|
1327
|
+
],
|
|
1328
|
+
default: [{ title: "Inspected and graded" }]
|
|
1329
|
+
},
|
|
1330
|
+
tone()
|
|
1331
|
+
]
|
|
1332
|
+
},
|
|
1333
|
+
cta_cards: {
|
|
1334
|
+
label: "Action cards",
|
|
1335
|
+
description: "Two to four cards that each lead somewhere (repairs, sell your laptop\u2026).",
|
|
1336
|
+
fields: [
|
|
1337
|
+
title("", 90),
|
|
1338
|
+
{
|
|
1339
|
+
key: "items",
|
|
1340
|
+
kind: "list",
|
|
1341
|
+
label: "Cards",
|
|
1342
|
+
min: 1,
|
|
1343
|
+
max: 4,
|
|
1344
|
+
itemLabel: "title",
|
|
1345
|
+
of: [
|
|
1346
|
+
{ key: "icon", kind: "icon", label: "Icon", default: "wrench" },
|
|
1347
|
+
{ key: "title", kind: "text", label: "Title", max: 50, required: true, default: "Book a repair" },
|
|
1348
|
+
{ key: "text", kind: "textarea", label: "Text", max: 140, default: "" },
|
|
1349
|
+
{ key: "cta_label", kind: "text", label: "Link text", max: 30, default: "Learn more" },
|
|
1350
|
+
{ key: "cta_href", kind: "url", label: "Link", required: true, default: "/" }
|
|
1351
|
+
],
|
|
1352
|
+
default: [{ title: "Book a repair", cta_href: "/" }]
|
|
1353
|
+
},
|
|
1354
|
+
tone()
|
|
1355
|
+
]
|
|
1356
|
+
},
|
|
1357
|
+
testimonials: {
|
|
1358
|
+
label: "Testimonials",
|
|
1359
|
+
description: "What customers say.",
|
|
1360
|
+
fields: [
|
|
1361
|
+
title("What customers say"),
|
|
1362
|
+
{
|
|
1363
|
+
key: "items",
|
|
1364
|
+
kind: "list",
|
|
1365
|
+
label: "Quotes",
|
|
1366
|
+
min: 1,
|
|
1367
|
+
max: 12,
|
|
1368
|
+
itemLabel: "name",
|
|
1369
|
+
of: [
|
|
1370
|
+
{ key: "quote", kind: "textarea", label: "Quote", max: 400, required: true, default: "Great service." },
|
|
1371
|
+
{ key: "name", kind: "text", label: "Name", max: 60, required: true, default: "A happy customer" },
|
|
1372
|
+
{ key: "detail", kind: "text", label: "Detail (city, purchase)", max: 80, default: "" },
|
|
1373
|
+
{ key: "rating", kind: "select", label: "Stars", options: ["5", "4", "3", "2", "1"].map((v) => ({ value: v, label: v })), default: "5" }
|
|
1374
|
+
],
|
|
1375
|
+
default: [{ quote: "Great service.", name: "A happy customer" }]
|
|
1376
|
+
},
|
|
1377
|
+
tone("muted")
|
|
1378
|
+
]
|
|
1379
|
+
},
|
|
1380
|
+
faq: {
|
|
1381
|
+
label: "FAQ",
|
|
1382
|
+
description: "Questions and answers (also used for search snippets).",
|
|
1383
|
+
fields: [
|
|
1384
|
+
title("Frequently asked questions"),
|
|
1385
|
+
intro,
|
|
1386
|
+
{
|
|
1387
|
+
key: "items",
|
|
1388
|
+
kind: "list",
|
|
1389
|
+
label: "Questions",
|
|
1390
|
+
min: 1,
|
|
1391
|
+
max: 20,
|
|
1392
|
+
itemLabel: "q",
|
|
1393
|
+
of: [
|
|
1394
|
+
{ key: "q", kind: "text", label: "Question", max: 160, required: true, default: "A question?" },
|
|
1395
|
+
{ key: "a", kind: "textarea", label: "Answer", max: 800, required: true, default: "The answer." }
|
|
1396
|
+
],
|
|
1397
|
+
default: [{ q: "A question?", a: "The answer." }]
|
|
1398
|
+
},
|
|
1399
|
+
tone("muted")
|
|
1400
|
+
]
|
|
1401
|
+
},
|
|
1402
|
+
rich_text: {
|
|
1403
|
+
label: "Text",
|
|
1404
|
+
description: "Free text with headings, lists and links.",
|
|
1405
|
+
fields: [title("", 90), { key: "html", kind: "richtext", label: "Text", max: 8e3, default: "<p>Write something about your store.</p>" }, { key: "width", kind: "select", label: "Width", options: [{ value: "narrow", label: "Narrow" }, { value: "wide", label: "Wide" }], default: "narrow" }, tone()]
|
|
1406
|
+
},
|
|
1407
|
+
usp_strip: {
|
|
1408
|
+
label: "Highlights strip",
|
|
1409
|
+
description: "A short row of selling points under the hero.",
|
|
1410
|
+
fields: [
|
|
1411
|
+
{
|
|
1412
|
+
key: "items",
|
|
1413
|
+
kind: "list",
|
|
1414
|
+
label: "Highlights",
|
|
1415
|
+
min: 2,
|
|
1416
|
+
max: 6,
|
|
1417
|
+
itemLabel: "title",
|
|
1418
|
+
of: [
|
|
1419
|
+
{ key: "icon", kind: "icon", label: "Icon", default: "check" },
|
|
1420
|
+
{ key: "title", kind: "text", label: "Title", max: 40, required: true, default: "Free delivery" },
|
|
1421
|
+
{ key: "text", kind: "text", label: "Line", max: 80, default: "" }
|
|
1422
|
+
],
|
|
1423
|
+
default: [{ title: "Free delivery" }, { title: "Warranty included" }]
|
|
1424
|
+
},
|
|
1425
|
+
tone("tinted")
|
|
1426
|
+
]
|
|
1427
|
+
},
|
|
1428
|
+
brand_strip: {
|
|
1429
|
+
label: "Brands we sell",
|
|
1430
|
+
description: "Brand names or logos.",
|
|
1431
|
+
fields: [
|
|
1432
|
+
title("Brands we sell"),
|
|
1433
|
+
{
|
|
1434
|
+
key: "items",
|
|
1435
|
+
kind: "list",
|
|
1436
|
+
label: "Brands",
|
|
1437
|
+
min: 1,
|
|
1438
|
+
max: 16,
|
|
1439
|
+
itemLabel: "name",
|
|
1440
|
+
of: [
|
|
1441
|
+
{ key: "name", kind: "text", label: "Brand", max: 40, required: true, default: "Brand" },
|
|
1442
|
+
{ key: "logo", kind: "image", label: "Logo (optional)", default: null }
|
|
1443
|
+
],
|
|
1444
|
+
default: [{ name: "Brand" }]
|
|
1445
|
+
},
|
|
1446
|
+
tone()
|
|
1447
|
+
]
|
|
1448
|
+
},
|
|
1449
|
+
newsletter: {
|
|
1450
|
+
label: "Newsletter sign-up",
|
|
1451
|
+
description: "Email sign-up with consent; the app decides where it goes.",
|
|
1452
|
+
fields: [
|
|
1453
|
+
{ key: "title", kind: "text", label: "Heading", max: 90, required: true, default: "Get new arrivals first" },
|
|
1454
|
+
{ key: "text", kind: "textarea", label: "Text", max: 200, default: "" },
|
|
1455
|
+
{ key: "button_label", kind: "text", label: "Button text", max: 30, required: true, default: "Subscribe" },
|
|
1456
|
+
{ key: "consent_text", kind: "text", label: "Consent line", max: 160, required: true, default: "I agree to receive emails from this store. I can unsubscribe any time." },
|
|
1457
|
+
{ key: "success_text", kind: "text", label: "Thank-you message", max: 120, default: "Thanks! You are on the list." },
|
|
1458
|
+
tone("tinted")
|
|
1459
|
+
]
|
|
1460
|
+
},
|
|
1461
|
+
gallery: {
|
|
1462
|
+
label: "Gallery",
|
|
1463
|
+
description: "Photos of your store, workshop or products.",
|
|
1464
|
+
fields: [
|
|
1465
|
+
title("Inside our store"),
|
|
1466
|
+
{
|
|
1467
|
+
key: "items",
|
|
1468
|
+
kind: "list",
|
|
1469
|
+
label: "Photos",
|
|
1470
|
+
min: 1,
|
|
1471
|
+
max: 12,
|
|
1472
|
+
itemLabel: "caption",
|
|
1473
|
+
of: [
|
|
1474
|
+
{ key: "image", kind: "image", label: "Photo", required: true, default: null },
|
|
1475
|
+
{ key: "caption", kind: "text", label: "Caption", max: 100, default: "" }
|
|
1476
|
+
],
|
|
1477
|
+
default: []
|
|
1478
|
+
},
|
|
1479
|
+
{ key: "columns", kind: "select", label: "Columns", options: [{ value: "2", label: "2" }, { value: "3", label: "3" }, { value: "4", label: "4" }], default: "3" },
|
|
1480
|
+
tone()
|
|
1481
|
+
]
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
var SECTION_TYPE_KEYS = Object.keys(SECTION_TYPES);
|
|
1485
|
+
var PAGE_VERSION = 1;
|
|
1486
|
+
var MAX_SECTIONS = 20;
|
|
1487
|
+
|
|
1488
|
+
// src/schema/richtext.js
|
|
1489
|
+
var ALLOWED = /* @__PURE__ */ new Set(["p", "br", "h2", "h3", "h4", "strong", "b", "em", "i", "u", "ul", "ol", "li", "a", "blockquote"]);
|
|
1490
|
+
var VOID = /* @__PURE__ */ new Set(["br"]);
|
|
1491
|
+
var DROP_WITH_CONTENT = /<(script|style|iframe|object|embed|template|noscript|svg|math|textarea|select|title|head)\b[\s\S]*?<\/\1\s*>/gi;
|
|
1492
|
+
var TAG = /<!--[\s\S]*?-->|<\/?([a-zA-Z][a-zA-Z0-9]*)\b([^>]*)>/g;
|
|
1493
|
+
var escapeText = (s) => s.replace(/&(?!(?:[a-z][a-z0-9]{1,31}|#\d{1,7}|#x[0-9a-f]{1,6});)/gi, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1494
|
+
var escapeAttr = (s) => s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1495
|
+
var decode = (s) => s.replace(/&(amp|quot|#39|lt|gt|#x[0-9a-f]+|#\d+);/gi, (m, e) => {
|
|
1496
|
+
const k = e.toLowerCase();
|
|
1497
|
+
if (k === "amp") return "&";
|
|
1498
|
+
if (k === "quot") return '"';
|
|
1499
|
+
if (k === "#39") return "'";
|
|
1500
|
+
if (k === "lt") return "<";
|
|
1501
|
+
if (k === "gt") return ">";
|
|
1502
|
+
const code = k.startsWith("#x") ? parseInt(k.slice(2), 16) : parseInt(k.slice(1), 10);
|
|
1503
|
+
return Number.isFinite(code) && code < 1114112 ? String.fromCodePoint(code) : "";
|
|
1504
|
+
});
|
|
1505
|
+
function safeHref(raw) {
|
|
1506
|
+
const v = decode(String(raw || "")).replace(/[\u0000- \u007f-\u009f]/g, "");
|
|
1507
|
+
if (!v) return null;
|
|
1508
|
+
if (/^(https?:\/\/|mailto:|tel:)/i.test(v)) return v;
|
|
1509
|
+
if (/^\/(?!\/)/.test(v) || v.startsWith("#")) return v;
|
|
1510
|
+
return null;
|
|
1511
|
+
}
|
|
1512
|
+
function hrefOf(attrs) {
|
|
1513
|
+
var _a, _b;
|
|
1514
|
+
const m = /\bhref\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/i.exec(attrs);
|
|
1515
|
+
return m ? safeHref((_b = (_a = m[1]) != null ? _a : m[2]) != null ? _b : m[3]) : null;
|
|
1516
|
+
}
|
|
1517
|
+
function sanitizeRichText(html, { maxLength = 2e4 } = {}) {
|
|
1518
|
+
const src = String(html == null ? "" : html).slice(0, maxLength * 4).replace(/<[a-zA-Z/!][^>]*$/, "").replace(DROP_WITH_CONTENT, "");
|
|
1519
|
+
let out = "";
|
|
1520
|
+
let last = 0;
|
|
1521
|
+
for (const m of src.matchAll(TAG)) {
|
|
1522
|
+
out += escapeText(src.slice(last, m.index));
|
|
1523
|
+
last = m.index + m[0].length;
|
|
1524
|
+
const name = (m[1] || "").toLowerCase();
|
|
1525
|
+
if (!name || !ALLOWED.has(name)) continue;
|
|
1526
|
+
const closing = m[0][1] === "/";
|
|
1527
|
+
if (closing) {
|
|
1528
|
+
if (!VOID.has(name)) out += `</${name}>`;
|
|
1529
|
+
} else if (name === "a") {
|
|
1530
|
+
const href = hrefOf(m[2] || "");
|
|
1531
|
+
out += href ? `<a href="${escapeAttr(href)}" rel="nofollow noopener noreferrer">` : "<a>";
|
|
1532
|
+
} else {
|
|
1533
|
+
out += `<${name}>`;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
out += escapeText(src.slice(last));
|
|
1537
|
+
return out.length > maxLength ? out.slice(0, maxLength).replace(/<[^>]*$/, "") : out;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// src/schema/validate.js
|
|
1541
|
+
var ID_RE = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
1542
|
+
var MEDIA_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
1543
|
+
var CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
|
|
1544
|
+
function field(f, value, path, errors) {
|
|
1545
|
+
var _a, _b, _c, _d;
|
|
1546
|
+
const absent = value === void 0 || value === null;
|
|
1547
|
+
const missing = absent || value === "";
|
|
1548
|
+
switch (f.kind) {
|
|
1549
|
+
case "text":
|
|
1550
|
+
case "textarea": {
|
|
1551
|
+
let v = absent ? (_a = f.default) != null ? _a : "" : value;
|
|
1552
|
+
if (typeof v !== "string") {
|
|
1553
|
+
errors.push({ path, message: "must be text" });
|
|
1554
|
+
return (_b = f.default) != null ? _b : "";
|
|
1555
|
+
}
|
|
1556
|
+
v = v.replace(CONTROL, "");
|
|
1557
|
+
v = f.kind === "text" ? v.replace(/\s+/g, " ").trim() : v.trim();
|
|
1558
|
+
if (f.max && v.length > f.max) errors.push({ path, message: `must be at most ${f.max} characters` });
|
|
1559
|
+
if (f.required && !v) errors.push({ path, message: "is required" });
|
|
1560
|
+
return v.slice(0, f.max || v.length);
|
|
1561
|
+
}
|
|
1562
|
+
case "url": {
|
|
1563
|
+
if (missing) {
|
|
1564
|
+
if (f.required) errors.push({ path, message: "is required" });
|
|
1565
|
+
return "";
|
|
1566
|
+
}
|
|
1567
|
+
const v = typeof value === "string" ? safeHref(value.trim()) : null;
|
|
1568
|
+
if (!v || v.length > 500) {
|
|
1569
|
+
errors.push({ path, message: "must be a page on this site (/\u2026), a full https:// address, mailto: or tel:" });
|
|
1570
|
+
return "";
|
|
1571
|
+
}
|
|
1572
|
+
return v;
|
|
1573
|
+
}
|
|
1574
|
+
case "image": {
|
|
1575
|
+
if (missing) {
|
|
1576
|
+
if (f.required) errors.push({ path, message: "is required" });
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
if (typeof value !== "object" || !MEDIA_RE.test(String((_c = value.media_id) != null ? _c : ""))) {
|
|
1580
|
+
errors.push({ path, message: "must be an uploaded image" });
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1583
|
+
return { media_id: typeof value.media_id === "number" ? value.media_id : String(value.media_id), alt: String((_d = value.alt) != null ? _d : "").replace(CONTROL, "").slice(0, 120) };
|
|
1584
|
+
}
|
|
1585
|
+
case "icon": {
|
|
1586
|
+
const v = absent ? f.default : value;
|
|
1587
|
+
if (!ICON_NAMES.includes(v)) {
|
|
1588
|
+
errors.push({ path, message: `must be one of the icon names (${ICON_NAMES.slice(0, 4).join(", ")}\u2026)` });
|
|
1589
|
+
return f.default;
|
|
1590
|
+
}
|
|
1591
|
+
return v;
|
|
1592
|
+
}
|
|
1593
|
+
case "select": {
|
|
1594
|
+
const v = missing ? f.default : String(value);
|
|
1595
|
+
if (!f.options.some((o) => o.value === v)) {
|
|
1596
|
+
errors.push({ path, message: `must be one of ${f.options.map((o) => o.value).join(", ")}` });
|
|
1597
|
+
return f.default;
|
|
1598
|
+
}
|
|
1599
|
+
return v;
|
|
1600
|
+
}
|
|
1601
|
+
case "bool":
|
|
1602
|
+
if (missing) return f.default;
|
|
1603
|
+
if (typeof value !== "boolean") {
|
|
1604
|
+
errors.push({ path, message: "must be true or false" });
|
|
1605
|
+
return f.default;
|
|
1606
|
+
}
|
|
1607
|
+
return value;
|
|
1608
|
+
case "richtext": {
|
|
1609
|
+
const raw = absent ? f.default : value;
|
|
1610
|
+
if (typeof raw !== "string") {
|
|
1611
|
+
errors.push({ path, message: "must be text" });
|
|
1612
|
+
return f.default;
|
|
1613
|
+
}
|
|
1614
|
+
if (f.max && raw.length > f.max * 2) errors.push({ path, message: `must be at most ${f.max} characters` });
|
|
1615
|
+
return sanitizeRichText(raw, { maxLength: f.max });
|
|
1616
|
+
}
|
|
1617
|
+
case "list": {
|
|
1618
|
+
const arr = absent ? f.default || [] : value;
|
|
1619
|
+
if (!Array.isArray(arr)) {
|
|
1620
|
+
errors.push({ path, message: "must be a list" });
|
|
1621
|
+
return [];
|
|
1622
|
+
}
|
|
1623
|
+
if (f.min && arr.length < f.min) errors.push({ path, message: `needs at least ${f.min}` });
|
|
1624
|
+
if (f.max && arr.length > f.max) errors.push({ path, message: `can have at most ${f.max}` });
|
|
1625
|
+
return arr.slice(0, f.max || arr.length).map((item, i) => fields(f.of, item, `${path}[${i}]`, errors));
|
|
1626
|
+
}
|
|
1627
|
+
default:
|
|
1628
|
+
throw new Error(`Unknown field kind "${f.kind}" (${path})`);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
function fields(defs, obj, path, errors) {
|
|
1632
|
+
const src = obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
1633
|
+
if (obj != null && src !== obj) errors.push({ path, message: "must be an object" });
|
|
1634
|
+
return Object.fromEntries(defs.map((f) => [f.key, field(f, Object.prototype.hasOwnProperty.call(src, f.key) ? src[f.key] : void 0, `${path}.${f.key}`, errors)]));
|
|
1635
|
+
}
|
|
1636
|
+
function validatePage(input) {
|
|
1637
|
+
const errors = [];
|
|
1638
|
+
const doc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
|
|
1639
|
+
if (!doc) return { ok: false, errors: [{ path: "page", message: "must be an object" }], page: { version: PAGE_VERSION, sections: [] } };
|
|
1640
|
+
if (doc.version != null && doc.version !== PAGE_VERSION) errors.push({ path: "version", message: `must be ${PAGE_VERSION}` });
|
|
1641
|
+
const list = Array.isArray(doc.sections) ? doc.sections : [];
|
|
1642
|
+
if (!Array.isArray(doc.sections)) errors.push({ path: "sections", message: "must be a list" });
|
|
1643
|
+
if (list.length > MAX_SECTIONS) errors.push({ path: "sections", message: `can have at most ${MAX_SECTIONS}` });
|
|
1644
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1645
|
+
const sections = [];
|
|
1646
|
+
list.slice(0, MAX_SECTIONS).forEach((s, i) => {
|
|
1647
|
+
var _a, _b;
|
|
1648
|
+
const path = `sections[${i}]`;
|
|
1649
|
+
if (!s || typeof s !== "object") {
|
|
1650
|
+
errors.push({ path, message: "must be an object" });
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
const def = SECTION_TYPES[s.type];
|
|
1654
|
+
if (!def) {
|
|
1655
|
+
errors.push({ path: `${path}.type`, message: `unknown section type "${s.type}"` });
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
const id = String((_a = s.id) != null ? _a : "");
|
|
1659
|
+
if (!ID_RE.test(id)) errors.push({ path: `${path}.id`, message: "must be 1\u201340 lowercase letters, digits or dashes" });
|
|
1660
|
+
else if (seen.has(id)) errors.push({ path: `${path}.id`, message: `duplicate id "${id}"` });
|
|
1661
|
+
seen.add(id);
|
|
1662
|
+
const enabled = s.enabled === void 0 ? true : s.enabled;
|
|
1663
|
+
if (typeof enabled !== "boolean") errors.push({ path: `${path}.enabled`, message: "must be true or false" });
|
|
1664
|
+
sections.push({ id, type: s.type, enabled: enabled !== false, props: fields(def.fields, (_b = s.props) != null ? _b : {}, `${path}.props`, errors) });
|
|
1665
|
+
});
|
|
1666
|
+
return { ok: errors.length === 0, errors, page: { version: PAGE_VERSION, sections } };
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// src/schema/color.js
|
|
1670
|
+
var HEX_RE = /^#([0-9a-f]{6})$/i;
|
|
1671
|
+
var isHex = (v) => typeof v === "string" && HEX_RE.test(v);
|
|
1672
|
+
function hexToRgb(hex) {
|
|
1673
|
+
const m = HEX_RE.exec(String(hex || ""));
|
|
1674
|
+
if (!m) throw new Error(`Not a #rrggbb colour: ${hex}`);
|
|
1675
|
+
const n = parseInt(m[1], 16);
|
|
1676
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
1677
|
+
}
|
|
1678
|
+
function rgbToHex([r, g, b]) {
|
|
1679
|
+
const c = (x) => Math.round(Math.min(255, Math.max(0, x))).toString(16).padStart(2, "0");
|
|
1680
|
+
return `#${c(r)}${c(g)}${c(b)}`;
|
|
1681
|
+
}
|
|
1682
|
+
var toLinear = (c) => {
|
|
1683
|
+
const x = c / 255;
|
|
1684
|
+
return x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
|
|
1685
|
+
};
|
|
1686
|
+
var fromLinear = (x) => 255 * (x <= 31308e-7 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055);
|
|
1687
|
+
function hexToOklch(hex) {
|
|
1688
|
+
const [r, g, b] = hexToRgb(hex).map(toLinear);
|
|
1689
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
1690
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
1691
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
1692
|
+
const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
|
|
1693
|
+
const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
|
|
1694
|
+
const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
|
|
1695
|
+
return { l: L, c: Math.hypot(A, B), h: Math.atan2(B, A) * 180 / Math.PI };
|
|
1696
|
+
}
|
|
1697
|
+
function oklchToLinear({ l, c, h }) {
|
|
1698
|
+
const a = c * Math.cos(h * Math.PI / 180);
|
|
1699
|
+
const b = c * Math.sin(h * Math.PI / 180);
|
|
1700
|
+
const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
1701
|
+
const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
1702
|
+
const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
1703
|
+
return [
|
|
1704
|
+
4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
|
|
1705
|
+
-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
|
|
1706
|
+
-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_
|
|
1707
|
+
];
|
|
1708
|
+
}
|
|
1709
|
+
var inGamut = (rgb) => rgb.every((x) => x >= -1e-4 && x <= 1 + 1e-4);
|
|
1710
|
+
function oklchToHex({ l, c, h }) {
|
|
1711
|
+
let lo = 0;
|
|
1712
|
+
let hi = c;
|
|
1713
|
+
let rgb = oklchToLinear({ l, c, h });
|
|
1714
|
+
if (!inGamut(rgb)) {
|
|
1715
|
+
for (let i = 0; i < 24; i += 1) {
|
|
1716
|
+
const mid = (lo + hi) / 2;
|
|
1717
|
+
if (inGamut(oklchToLinear({ l, c: mid, h }))) lo = mid;
|
|
1718
|
+
else hi = mid;
|
|
1719
|
+
}
|
|
1720
|
+
rgb = oklchToLinear({ l, c: lo, h });
|
|
1721
|
+
}
|
|
1722
|
+
return rgbToHex(rgb.map((x) => fromLinear(Math.min(1, Math.max(0, x)))));
|
|
1723
|
+
}
|
|
1724
|
+
var PALETTE_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
1725
|
+
var LIGHTNESS = { 50: 0.975, 100: 0.945, 200: 0.89, 300: 0.81, 400: 0.71, 500: 0.62, 700: 0.46, 800: 0.39, 900: 0.32, 950: 0.24 };
|
|
1726
|
+
var CHROMA = { 50: 0.18, 100: 0.32, 200: 0.55, 300: 0.78, 400: 0.92, 500: 1, 700: 0.92, 800: 0.82, 900: 0.7, 950: 0.58 };
|
|
1727
|
+
function brandPalette(hex) {
|
|
1728
|
+
const base3 = hexToOklch(hex);
|
|
1729
|
+
const out = { 600: rgbToHex(hexToRgb(hex)) };
|
|
1730
|
+
for (const step of PALETTE_STEPS) {
|
|
1731
|
+
if (step === 600) continue;
|
|
1732
|
+
const l = step < 600 ? Math.max(LIGHTNESS[step], Math.min(0.985, base3.l + (0.985 - base3.l) * ((600 - step) / 600))) : Math.min(LIGHTNESS[step], Math.max(0.12, base3.l - base3.l * 0.6 * ((step - 600) / 350)));
|
|
1733
|
+
out[step] = oklchToHex({ l, c: base3.c * CHROMA[step], h: base3.h });
|
|
1734
|
+
}
|
|
1735
|
+
return Object.fromEntries(PALETTE_STEPS.map((s) => [s, out[s]]));
|
|
1736
|
+
}
|
|
1737
|
+
function luminance(hex) {
|
|
1738
|
+
const [r, g, b] = hexToRgb(hex).map(toLinear);
|
|
1739
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
1740
|
+
}
|
|
1741
|
+
function contrastRatio(a, b) {
|
|
1742
|
+
const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
|
|
1743
|
+
return (x + 0.05) / (y + 0.05);
|
|
1744
|
+
}
|
|
1745
|
+
function readableOn(hex) {
|
|
1746
|
+
return contrastRatio(hex, "#ffffff") >= contrastRatio(hex, "#101828") ? "#ffffff" : "#101828";
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
// src/schema/theme.js
|
|
1750
|
+
var FONT_PAIRS = {
|
|
1751
|
+
system: { label: "System (fastest)", heading: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif", body: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif", href: null },
|
|
1752
|
+
inter: { label: "Inter", heading: "Inter, ui-sans-serif, system-ui, sans-serif", body: "Inter, ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" },
|
|
1753
|
+
poppins: { label: "Poppins + Inter", heading: "Poppins, ui-sans-serif, system-ui, sans-serif", body: "Inter, ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@600;700&display=swap" },
|
|
1754
|
+
dmsans: { label: "DM Sans", heading: "'DM Sans', ui-sans-serif, system-ui, sans-serif", body: "'DM Sans', ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap" },
|
|
1755
|
+
nunito: { label: "Nunito (rounded)", heading: "Nunito, ui-rounded, ui-sans-serif, system-ui, sans-serif", body: "Nunito, ui-rounded, ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;800&display=swap" },
|
|
1756
|
+
serif: { label: "Merriweather + Source Sans", heading: "Merriweather, Georgia, 'Times New Roman', serif", body: "'Source Sans 3', ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Merriweather:wght@700&family=Source+Sans+3:wght@400;600&display=swap" }
|
|
1757
|
+
};
|
|
1758
|
+
var RADII = { none: ["0", "0"], sm: ["0.375rem", "0.5rem"], md: ["0.75rem", "1rem"], lg: ["1rem", "1.5rem"], full: ["1.5rem", "2rem"] };
|
|
1759
|
+
var MODES = ["light", "dark", "auto"];
|
|
1760
|
+
var THEME_PRESETS = {
|
|
1761
|
+
classic: { label: "Classic blue", primary: "#2563eb", accent: "#f59e0b", font: "inter", radius: "md", mode: "light" },
|
|
1762
|
+
forest: { label: "Forest", primary: "#15803d", accent: "#ca8a04", font: "dmsans", radius: "lg", mode: "light" },
|
|
1763
|
+
sunset: { label: "Sunset", primary: "#c2410c", accent: "#0369a1", font: "poppins", radius: "md", mode: "light" },
|
|
1764
|
+
midnight: { label: "Midnight", primary: "#4338ca", accent: "#0891b2", font: "inter", radius: "lg", mode: "dark" },
|
|
1765
|
+
slate: { label: "Minimal slate", primary: "#334155", accent: "#0f766e", font: "system", radius: "sm", mode: "light" },
|
|
1766
|
+
berry: { label: "Berry", primary: "#be185d", accent: "#6d28d9", font: "nunito", radius: "full", mode: "light" }
|
|
1767
|
+
};
|
|
1768
|
+
var { label: _classicLabel, ...classic } = THEME_PRESETS.classic;
|
|
1769
|
+
var DEFAULT_THEME = { preset: "classic", ...classic, logo: null, favicon: null };
|
|
1770
|
+
var mediaRef = (v, path, errors, { alt = false } = {}) => {
|
|
1771
|
+
if (v == null || v === "") return null;
|
|
1772
|
+
if (typeof v !== "object" || v.media_id == null || !/^[A-Za-z0-9_-]{1,64}$/.test(String(v.media_id))) {
|
|
1773
|
+
errors.push({ path, message: "must be an uploaded image" });
|
|
1774
|
+
return null;
|
|
1775
|
+
}
|
|
1776
|
+
const ref = { media_id: typeof v.media_id === "number" ? v.media_id : String(v.media_id) };
|
|
1777
|
+
if (alt) ref.alt = String(v.alt == null ? "" : v.alt).slice(0, 120);
|
|
1778
|
+
return ref;
|
|
1779
|
+
};
|
|
1780
|
+
function validateTheme(input) {
|
|
1781
|
+
const errors = [];
|
|
1782
|
+
const warnings = [];
|
|
1783
|
+
const t = input && typeof input === "object" && !Array.isArray(input) ? input : {};
|
|
1784
|
+
if (input != null && t !== input) errors.push({ path: "theme", message: "must be an object" });
|
|
1785
|
+
const preset = t.preset == null || t.preset === "" ? null : String(t.preset);
|
|
1786
|
+
if (preset && !THEME_PRESETS[preset] && preset !== "custom") errors.push({ path: "preset", message: `must be one of ${Object.keys(THEME_PRESETS).join(", ")} or custom` });
|
|
1787
|
+
const base3 = preset && THEME_PRESETS[preset] || THEME_PRESETS.classic;
|
|
1788
|
+
const color = (key) => {
|
|
1789
|
+
const v = t[key] == null || t[key] === "" ? base3[key] : String(t[key]).toLowerCase();
|
|
1790
|
+
if (!isHex(v)) {
|
|
1791
|
+
errors.push({ path: key, message: "must be a colour like #2563eb" });
|
|
1792
|
+
return base3[key];
|
|
1793
|
+
}
|
|
1794
|
+
return v;
|
|
1795
|
+
};
|
|
1796
|
+
const pick = (key, allowed, fallback) => {
|
|
1797
|
+
const v = t[key] == null || t[key] === "" ? fallback : String(t[key]);
|
|
1798
|
+
if (!allowed.includes(v)) {
|
|
1799
|
+
errors.push({ path: key, message: `must be one of ${allowed.join(", ")}` });
|
|
1800
|
+
return fallback;
|
|
1801
|
+
}
|
|
1802
|
+
return v;
|
|
1803
|
+
};
|
|
1804
|
+
const theme = {
|
|
1805
|
+
preset: preset || "classic",
|
|
1806
|
+
primary: color("primary"),
|
|
1807
|
+
accent: color("accent"),
|
|
1808
|
+
font: pick("font", Object.keys(FONT_PAIRS), base3.font),
|
|
1809
|
+
radius: pick("radius", Object.keys(RADII), base3.radius),
|
|
1810
|
+
mode: pick("mode", MODES, base3.mode),
|
|
1811
|
+
logo: mediaRef(t.logo, "logo", errors, { alt: true }),
|
|
1812
|
+
favicon: mediaRef(t.favicon, "favicon", errors)
|
|
1813
|
+
};
|
|
1814
|
+
const onBrand = contrastRatio(theme.primary, readableOn(theme.primary));
|
|
1815
|
+
if (onBrand < 4.5) warnings.push({ path: "primary", message: `Button text on this colour is hard to read (contrast ${onBrand.toFixed(1)}:1, needs 4.5:1).` });
|
|
1816
|
+
const onPage = contrastRatio(theme.primary, "#ffffff");
|
|
1817
|
+
if (onPage < 3) warnings.push({ path: "primary", message: `Links in this colour are hard to see on white (contrast ${onPage.toFixed(1)}:1, needs 3:1).` });
|
|
1818
|
+
const accentText = contrastRatio(theme.accent, readableOn(theme.accent));
|
|
1819
|
+
if (accentText < 4.5) warnings.push({ path: "accent", message: `Text on the accent colour is hard to read (contrast ${accentText.toFixed(1)}:1, needs 4.5:1).` });
|
|
1820
|
+
return { ok: errors.length === 0, errors, warnings, theme };
|
|
1821
|
+
}
|
|
1822
|
+
function themeVars(input) {
|
|
1823
|
+
const { theme } = validateTheme(input);
|
|
1824
|
+
const font = FONT_PAIRS[theme.font];
|
|
1825
|
+
const [radius, radiusLg] = RADII[theme.radius];
|
|
1826
|
+
return {
|
|
1827
|
+
theme,
|
|
1828
|
+
font,
|
|
1829
|
+
palette: { primary: brandPalette(theme.primary), accent: brandPalette(theme.accent) },
|
|
1830
|
+
style: {
|
|
1831
|
+
"--dt-brand": theme.primary,
|
|
1832
|
+
"--dt-brand-accent": theme.accent,
|
|
1833
|
+
"--dt-brand-contrast": readableOn(theme.primary),
|
|
1834
|
+
"--dt-brand-accent-contrast": readableOn(theme.accent),
|
|
1835
|
+
"--dt-lp-font": font.body,
|
|
1836
|
+
"--dt-lp-font-heading": font.heading,
|
|
1837
|
+
"--dt-lp-radius": radius,
|
|
1838
|
+
"--dt-lp-radius-lg": radiusLg
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
// src/tenant/HeroSlider.jsx
|
|
1844
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
|
|
1845
|
+
|
|
1846
|
+
// src/tenant/icons.jsx
|
|
1847
|
+
import { Fragment as Fragment6, jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1848
|
+
var base2 = {
|
|
1849
|
+
viewBox: "0 0 24 24",
|
|
1850
|
+
fill: "none",
|
|
1851
|
+
stroke: "currentColor",
|
|
1852
|
+
strokeWidth: 1.8,
|
|
1853
|
+
strokeLinecap: "round",
|
|
1854
|
+
strokeLinejoin: "round",
|
|
1855
|
+
"aria-hidden": "true",
|
|
1856
|
+
focusable: "false"
|
|
1857
|
+
};
|
|
1858
|
+
var PATHS = {
|
|
1859
|
+
"shield-check": /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1860
|
+
/* @__PURE__ */ jsx22("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z" }),
|
|
1861
|
+
/* @__PURE__ */ jsx22("path", { d: "m9 12 2 2 4-4" })
|
|
1862
|
+
] }),
|
|
1863
|
+
"badge-check": /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1864
|
+
/* @__PURE__ */ jsx22("path", { d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" }),
|
|
1865
|
+
/* @__PURE__ */ jsx22("path", { d: "m9 12 2 2 4-4" })
|
|
1866
|
+
] }),
|
|
1867
|
+
truck: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1868
|
+
/* @__PURE__ */ jsx22("path", { d: "M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" }),
|
|
1869
|
+
/* @__PURE__ */ jsx22("path", { d: "M15 18H9" }),
|
|
1870
|
+
/* @__PURE__ */ jsx22("path", { d: "M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62L18.3 9.38a1 1 0 0 0-.78-.38H14" }),
|
|
1871
|
+
/* @__PURE__ */ jsx22("circle", { cx: "17", cy: "18", r: "2" }),
|
|
1872
|
+
/* @__PURE__ */ jsx22("circle", { cx: "7", cy: "18", r: "2" })
|
|
1873
|
+
] }),
|
|
1874
|
+
wrench: /* @__PURE__ */ jsx22("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" }),
|
|
1875
|
+
refresh: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1876
|
+
/* @__PURE__ */ jsx22("path", { d: "M3 12a9 9 0 0 1 15-6.7L21 8" }),
|
|
1877
|
+
/* @__PURE__ */ jsx22("path", { d: "M21 3v5h-5" }),
|
|
1878
|
+
/* @__PURE__ */ jsx22("path", { d: "M21 12a9 9 0 0 1-15 6.7L3 16" }),
|
|
1879
|
+
/* @__PURE__ */ jsx22("path", { d: "M8 16H3v5" })
|
|
1880
|
+
] }),
|
|
1881
|
+
battery: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1882
|
+
/* @__PURE__ */ jsx22("rect", { x: "2", y: "7", width: "16", height: "10", rx: "2" }),
|
|
1883
|
+
/* @__PURE__ */ jsx22("path", { d: "M22 11v2M6 11v2M10 11v2" })
|
|
1884
|
+
] }),
|
|
1885
|
+
cpu: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1886
|
+
/* @__PURE__ */ jsx22("rect", { x: "4", y: "4", width: "16", height: "16", rx: "2" }),
|
|
1887
|
+
/* @__PURE__ */ jsx22("rect", { x: "9", y: "9", width: "6", height: "6" }),
|
|
1888
|
+
/* @__PURE__ */ jsx22("path", { d: "M15 2v2M15 20v2M2 15h2M2 9h2M20 15h2M20 9h2M9 2v2M9 20v2" })
|
|
1889
|
+
] }),
|
|
1890
|
+
laptop: /* @__PURE__ */ jsx22("path", { d: "M20 16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9m16 0H4m16 0 1.28 2.55a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45L4 16" }),
|
|
1891
|
+
star: /* @__PURE__ */ jsx22("path", { 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" }),
|
|
1892
|
+
headset: /* @__PURE__ */ jsx22("path", { d: "M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3" }),
|
|
1893
|
+
rupee: /* @__PURE__ */ jsx22("path", { d: "M6 3h12M6 8h12M6 13l8.5 8M6 13h3M9 13c6.67 0 6.67-10 0-10" }),
|
|
1894
|
+
award: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1895
|
+
/* @__PURE__ */ jsx22("circle", { cx: "12", cy: "8", r: "6" }),
|
|
1896
|
+
/* @__PURE__ */ jsx22("path", { d: "M15.48 12.89 17 22l-5-3-5 3 1.52-9.11" })
|
|
1897
|
+
] }),
|
|
1898
|
+
clock: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1899
|
+
/* @__PURE__ */ jsx22("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1900
|
+
/* @__PURE__ */ jsx22("path", { d: "M12 6v6l4 2" })
|
|
1901
|
+
] }),
|
|
1902
|
+
"map-pin": /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1903
|
+
/* @__PURE__ */ jsx22("path", { d: "M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" }),
|
|
1904
|
+
/* @__PURE__ */ jsx22("circle", { cx: "12", cy: "10", r: "3" })
|
|
1905
|
+
] }),
|
|
1906
|
+
phone: /* @__PURE__ */ jsx22("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }),
|
|
1907
|
+
check: /* @__PURE__ */ jsx22("path", { d: "M20 6 9 17l-5-5" }),
|
|
1908
|
+
sparkles: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1909
|
+
/* @__PURE__ */ jsx22("path", { d: "M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 3z" }),
|
|
1910
|
+
/* @__PURE__ */ jsx22("path", { d: "M5 3v4M3 5h4M19 17v4M17 19h4" })
|
|
1911
|
+
] }),
|
|
1912
|
+
tag: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1913
|
+
/* @__PURE__ */ jsx22("path", { d: "M12.59 2.59A2 2 0 0 0 11.17 2H4a2 2 0 0 0-2 2v7.17a2 2 0 0 0 .59 1.42l8.7 8.7a2.43 2.43 0 0 0 3.42 0l6.58-6.58a2.43 2.43 0 0 0 0-3.42z" }),
|
|
1914
|
+
/* @__PURE__ */ jsx22("circle", { cx: "7.5", cy: "7.5", r: "1.5" })
|
|
1915
|
+
] }),
|
|
1916
|
+
gift: /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
1917
|
+
/* @__PURE__ */ jsx22("rect", { x: "3", y: "8", width: "18", height: "4", rx: "1" }),
|
|
1918
|
+
/* @__PURE__ */ jsx22("path", { d: "M12 8v13M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7" }),
|
|
1919
|
+
/* @__PURE__ */ jsx22("path", { d: "M7.5 8a2.5 2.5 0 0 1 0-5C10 3 12 8 12 8s2-5 4.5-5a2.5 2.5 0 0 1 0 5" })
|
|
1920
|
+
] }),
|
|
1921
|
+
heart: /* @__PURE__ */ jsx22("path", { d: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" }),
|
|
1922
|
+
// Controls (not offered to content).
|
|
1923
|
+
"chevron-left": /* @__PURE__ */ jsx22("path", { d: "m15 18-6-6 6-6" }),
|
|
1924
|
+
"chevron-right": /* @__PURE__ */ jsx22("path", { d: "m9 18 6-6-6-6" }),
|
|
1925
|
+
pause: /* @__PURE__ */ jsx22("path", { d: "M8 5v14M16 5v14" }),
|
|
1926
|
+
play: /* @__PURE__ */ jsx22("path", { d: "M7 4.5v15l12-7.5-12-7.5Z" })
|
|
1927
|
+
};
|
|
1928
|
+
function NamedIcon({ name, size = 20, className = "dt-lp-icon" }) {
|
|
1929
|
+
const paths = PATHS[name];
|
|
1930
|
+
if (!paths) return null;
|
|
1931
|
+
return /* @__PURE__ */ jsx22("svg", { ...base2, width: size, height: size, className, children: paths });
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
// src/tenant/HeroSlider.jsx
|
|
1935
|
+
import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1936
|
+
var reducedMotion = () => typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1937
|
+
function Slide({ slide, index, count, active, headingTag: H, media, eager }) {
|
|
1938
|
+
const wide = media(slide.image, "wide");
|
|
1939
|
+
const narrow = media(slide.mobile_image, "narrow");
|
|
1940
|
+
return /* @__PURE__ */ jsxs20(
|
|
1941
|
+
"div",
|
|
1942
|
+
{
|
|
1943
|
+
className: `dt-lp-slide dt-lp-slide--${slide.align} dt-lp-slide--overlay-${wide || narrow ? slide.overlay : "none"}${wide || narrow ? " dt-lp-slide--image" : ""}`,
|
|
1944
|
+
role: "group",
|
|
1945
|
+
"aria-roledescription": "slide",
|
|
1946
|
+
"aria-label": `${index + 1} of ${count}`,
|
|
1947
|
+
"aria-hidden": active ? void 0 : "true",
|
|
1948
|
+
"data-active": active ? "" : void 0,
|
|
1949
|
+
children: [
|
|
1950
|
+
wide || narrow ? /* @__PURE__ */ jsxs20("picture", { className: "dt-lp-slide-media", children: [
|
|
1951
|
+
narrow && wide ? /* @__PURE__ */ jsx23("source", { media: "(max-width: 640px)", srcSet: narrow }) : null,
|
|
1952
|
+
/* @__PURE__ */ jsx23("img", { src: wide || narrow, alt: slide.image && slide.image.alt || "", loading: eager ? "eager" : "lazy", decoding: "async" })
|
|
1953
|
+
] }) : null,
|
|
1954
|
+
/* @__PURE__ */ jsx23("div", { className: "dt-lp-wrap dt-lp-slide-body", children: /* @__PURE__ */ jsxs20("div", { className: "dt-lp-slide-copy", children: [
|
|
1955
|
+
slide.eyebrow ? /* @__PURE__ */ jsx23("p", { className: "dt-lp-eyebrow dt-lp-slide-eyebrow", children: slide.eyebrow }) : null,
|
|
1956
|
+
/* @__PURE__ */ jsx23(H, { className: "dt-lp-slide-heading", children: slide.heading }),
|
|
1957
|
+
slide.text ? /* @__PURE__ */ jsx23("p", { className: "dt-lp-slide-text", children: slide.text }) : null,
|
|
1958
|
+
slide.cta_label && slide.cta_href ? /* @__PURE__ */ jsx23(SmartLink, { href: slide.cta_href, className: "dt-lp-btn dt-lp-btn--lg dt-lp-btn--primary dt-lp-slide-cta", tabIndex: active ? void 0 : -1, children: slide.cta_label }) : null
|
|
1959
|
+
] }) })
|
|
1960
|
+
]
|
|
1961
|
+
}
|
|
1962
|
+
);
|
|
1963
|
+
}
|
|
1964
|
+
function HeroSlider({ id, slides = [], height = "standard", autoplay = true, interval = "6", first = false, media }) {
|
|
1965
|
+
const count = slides.length;
|
|
1966
|
+
const [index, setIndex] = useState5(0);
|
|
1967
|
+
const [playing, setPlaying] = useState5(false);
|
|
1968
|
+
const [hold, setHold] = useState5(false);
|
|
1969
|
+
const touch = useRef4(null);
|
|
1970
|
+
const go = useCallback2((i) => setIndex((i % count + count) % count), [count]);
|
|
1971
|
+
useEffect3(() => {
|
|
1972
|
+
setPlaying(Boolean(autoplay) && count > 1 && !reducedMotion());
|
|
1973
|
+
}, [autoplay, count]);
|
|
1974
|
+
useEffect3(() => {
|
|
1975
|
+
if (!playing || hold || count < 2) return void 0;
|
|
1976
|
+
const t = setTimeout(() => go(index + 1), Math.max(3, Number(interval) || 6) * 1e3);
|
|
1977
|
+
return () => clearTimeout(t);
|
|
1978
|
+
}, [playing, hold, index, interval, count, go]);
|
|
1979
|
+
useEffect3(() => {
|
|
1980
|
+
if (index >= count) setIndex(0);
|
|
1981
|
+
}, [count, index]);
|
|
1982
|
+
if (!count) return null;
|
|
1983
|
+
const onKey = (e) => {
|
|
1984
|
+
if (e.key === "ArrowRight") {
|
|
1985
|
+
e.preventDefault();
|
|
1986
|
+
go(index + 1);
|
|
1987
|
+
}
|
|
1988
|
+
if (e.key === "ArrowLeft") {
|
|
1989
|
+
e.preventDefault();
|
|
1990
|
+
go(index - 1);
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
const onTouchStart = (e) => {
|
|
1994
|
+
touch.current = e.touches[0].clientX;
|
|
1995
|
+
};
|
|
1996
|
+
const onTouchEnd = (e) => {
|
|
1997
|
+
if (touch.current == null) return;
|
|
1998
|
+
const dx = e.changedTouches[0].clientX - touch.current;
|
|
1999
|
+
touch.current = null;
|
|
2000
|
+
if (Math.abs(dx) > 40) go(index + (dx < 0 ? 1 : -1));
|
|
2001
|
+
};
|
|
2002
|
+
return /* @__PURE__ */ jsxs20(
|
|
2003
|
+
"section",
|
|
2004
|
+
{
|
|
2005
|
+
id,
|
|
2006
|
+
className: `dt-lp-hero-slider dt-lp-hero-slider--${height}`,
|
|
2007
|
+
"aria-roledescription": "carousel",
|
|
2008
|
+
"aria-label": "Highlights",
|
|
2009
|
+
onKeyDown: onKey,
|
|
2010
|
+
onMouseEnter: () => setHold(true),
|
|
2011
|
+
onMouseLeave: () => setHold(false),
|
|
2012
|
+
onFocus: () => setHold(true),
|
|
2013
|
+
onBlur: () => setHold(false),
|
|
2014
|
+
onTouchStart,
|
|
2015
|
+
onTouchEnd,
|
|
2016
|
+
children: [
|
|
2017
|
+
/* @__PURE__ */ jsx23("div", { className: "dt-lp-slides", "aria-live": playing && !hold ? "off" : "polite", children: slides.map((s, i) => /* @__PURE__ */ jsx23(Slide, { slide: s, index: i, count, active: i === index, eager: i === 0, headingTag: first && i === 0 ? "h1" : "h2", media }, i)) }),
|
|
2018
|
+
count > 1 ? /* @__PURE__ */ jsxs20("div", { className: "dt-lp-slider-controls", children: [
|
|
2019
|
+
/* @__PURE__ */ jsx23("button", { type: "button", className: "dt-lp-slider-btn", "aria-label": "Previous slide", onClick: () => go(index - 1), children: /* @__PURE__ */ jsx23(NamedIcon, { name: "chevron-left", size: 20 }) }),
|
|
2020
|
+
/* @__PURE__ */ jsx23("div", { className: "dt-lp-slider-dots", children: slides.map((s, i) => /* @__PURE__ */ jsx23("button", { type: "button", className: "dt-lp-slider-dot", "aria-label": `Go to slide ${i + 1}`, "aria-current": i === index ? "true" : void 0, onClick: () => go(i) }, i)) }),
|
|
2021
|
+
/* @__PURE__ */ jsx23("button", { type: "button", className: "dt-lp-slider-btn", "aria-label": "Next slide", onClick: () => go(index + 1), children: /* @__PURE__ */ jsx23(NamedIcon, { name: "chevron-right", size: 20 }) }),
|
|
2022
|
+
autoplay ? /* @__PURE__ */ jsx23("button", { type: "button", className: "dt-lp-slider-btn", "aria-label": playing ? "Pause slides" : "Play slides", onClick: () => setPlaying((p) => !p), children: /* @__PURE__ */ jsx23(NamedIcon, { name: playing ? "pause" : "play", size: 18 }) }) : null
|
|
2023
|
+
] }) : null
|
|
2024
|
+
]
|
|
2025
|
+
}
|
|
2026
|
+
);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
// src/tenant/blocks.jsx
|
|
2030
|
+
import { useState as useState6 } from "react";
|
|
2031
|
+
import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2032
|
+
var hid = (id) => `${id}-title`;
|
|
2033
|
+
var Heading = ({ id, title: title2, intro: intro2, align = "center" }) => title2 ? /* @__PURE__ */ jsx24(SectionIntro, { id: hid(id), title: title2, body: intro2, align }) : null;
|
|
2034
|
+
function SlotSection({ id, title: title2, intro: intro2, tone: tone2, slot, ...props }) {
|
|
2035
|
+
const body = typeof slot === "function" ? slot({ title: title2, intro: intro2, ...props }) : slot;
|
|
2036
|
+
if (!body) return null;
|
|
2037
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, className: "dt-lp-slot", children: [
|
|
2038
|
+
/* @__PURE__ */ jsx24(Heading, { id, title: title2, intro: intro2, align: "start" }),
|
|
2039
|
+
/* @__PURE__ */ jsx24("div", { className: "dt-lp-slot-body", children: body })
|
|
2040
|
+
] });
|
|
2041
|
+
}
|
|
2042
|
+
function PromoBanner({ id, eyebrow, heading, text, cta_label: label, cta_href: href, image, style = "brand", media }) {
|
|
2043
|
+
const src = media(image, "wide");
|
|
2044
|
+
return /* @__PURE__ */ jsx24("section", { id, "aria-labelledby": hid(id), className: "dt-lp-section dt-lp-promo-wrap", children: /* @__PURE__ */ jsx24("div", { className: "dt-lp-wrap", children: /* @__PURE__ */ jsxs21("div", { className: `dt-lp-promo dt-lp-promo--${style}${src ? " dt-lp-promo--image" : ""}`, children: [
|
|
2045
|
+
/* @__PURE__ */ jsxs21("div", { className: "dt-lp-promo-copy", children: [
|
|
2046
|
+
eyebrow ? /* @__PURE__ */ jsx24("p", { className: "dt-lp-eyebrow dt-lp-eyebrow--inverse", children: eyebrow }) : null,
|
|
2047
|
+
/* @__PURE__ */ jsx24("h2", { id: hid(id), className: "dt-lp-h2 dt-lp-promo-heading", children: heading }),
|
|
2048
|
+
text ? /* @__PURE__ */ jsx24("p", { className: "dt-lp-promo-text", children: text }) : null,
|
|
2049
|
+
label && href ? /* @__PURE__ */ jsx24(SmartLink, { href, className: "dt-lp-btn dt-lp-btn--lg dt-lp-btn--inverse", children: label }) : null
|
|
2050
|
+
] }),
|
|
2051
|
+
src ? /* @__PURE__ */ jsx24("img", { className: "dt-lp-promo-img", src, alt: image.alt || "", loading: "lazy", decoding: "async" }) : null
|
|
2052
|
+
] }) }) });
|
|
2053
|
+
}
|
|
2054
|
+
function TrustBadges({ id, title: title2, intro: intro2, items = [], tone: tone2 }) {
|
|
2055
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
|
|
2056
|
+
/* @__PURE__ */ jsx24(Heading, { id, title: title2, intro: intro2 }),
|
|
2057
|
+
/* @__PURE__ */ jsx24("ul", { className: "dt-lp-tgrid dt-lp-tgrid--4", children: items.map((it, i) => /* @__PURE__ */ jsxs21("li", { className: "dt-lp-card dt-lp-tcard", children: [
|
|
2058
|
+
/* @__PURE__ */ jsx24("span", { className: "dt-lp-icon-tile", children: /* @__PURE__ */ jsx24(NamedIcon, { name: it.icon, size: 22 }) }),
|
|
2059
|
+
/* @__PURE__ */ jsxs21("div", { className: "dt-lp-card-text", children: [
|
|
2060
|
+
/* @__PURE__ */ jsx24("h3", { className: "dt-lp-h3", children: it.title }),
|
|
2061
|
+
it.text ? /* @__PURE__ */ jsx24("p", { children: it.text }) : null
|
|
2062
|
+
] })
|
|
2063
|
+
] }, i)) })
|
|
2064
|
+
] });
|
|
2065
|
+
}
|
|
2066
|
+
function CtaCards({ id, title: title2, items = [], tone: tone2 }) {
|
|
2067
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
|
|
2068
|
+
/* @__PURE__ */ jsx24(Heading, { id, title: title2 }),
|
|
2069
|
+
/* @__PURE__ */ jsx24("ul", { className: `dt-lp-tgrid dt-lp-tgrid--${Math.min(items.length, 4) || 1}`, children: items.map((it, i) => /* @__PURE__ */ jsx24("li", { children: /* @__PURE__ */ jsxs21(SmartLink, { href: it.cta_href, className: "dt-lp-card dt-lp-tcard dt-lp-ctacard", children: [
|
|
2070
|
+
/* @__PURE__ */ jsx24("span", { className: "dt-lp-icon-tile", children: /* @__PURE__ */ jsx24(NamedIcon, { name: it.icon, size: 22 }) }),
|
|
2071
|
+
/* @__PURE__ */ jsxs21("div", { className: "dt-lp-card-text", children: [
|
|
2072
|
+
/* @__PURE__ */ jsx24("h3", { className: "dt-lp-h3", children: it.title }),
|
|
2073
|
+
it.text ? /* @__PURE__ */ jsx24("p", { children: it.text }) : null,
|
|
2074
|
+
it.cta_label ? /* @__PURE__ */ jsxs21("span", { className: "dt-lp-textlink", children: [
|
|
2075
|
+
it.cta_label,
|
|
2076
|
+
" ",
|
|
2077
|
+
/* @__PURE__ */ jsx24(NamedIcon, { name: "chevron-right", size: 16 })
|
|
2078
|
+
] }) : null
|
|
2079
|
+
] })
|
|
2080
|
+
] }) }, i)) })
|
|
2081
|
+
] });
|
|
2082
|
+
}
|
|
2083
|
+
function Testimonials({ id, title: title2, items = [], tone: tone2 }) {
|
|
2084
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
|
|
2085
|
+
/* @__PURE__ */ jsx24(Heading, { id, title: title2 }),
|
|
2086
|
+
/* @__PURE__ */ jsx24("ul", { className: "dt-lp-tgrid dt-lp-tgrid--3", children: items.map((it, i) => {
|
|
2087
|
+
const stars = Math.max(1, Math.min(5, Number(it.rating) || 5));
|
|
2088
|
+
return /* @__PURE__ */ jsxs21("li", { className: "dt-lp-card dt-lp-quote", children: [
|
|
2089
|
+
/* @__PURE__ */ jsx24("p", { className: "dt-lp-stars", "aria-label": `${stars} out of 5 stars`, children: Array.from({ length: 5 }, (_, s) => /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", className: s < stars ? "dt-lp-star dt-lp-star--on" : "dt-lp-star", children: "\u2605" }, s)) }),
|
|
2090
|
+
/* @__PURE__ */ jsx24("blockquote", { children: /* @__PURE__ */ jsx24("p", { children: it.quote }) }),
|
|
2091
|
+
/* @__PURE__ */ jsxs21("p", { className: "dt-lp-quote-by", children: [
|
|
2092
|
+
/* @__PURE__ */ jsx24("strong", { children: it.name }),
|
|
2093
|
+
it.detail ? /* @__PURE__ */ jsxs21("span", { children: [
|
|
2094
|
+
" \xB7 ",
|
|
2095
|
+
it.detail
|
|
2096
|
+
] }) : null
|
|
2097
|
+
] })
|
|
2098
|
+
] }, i);
|
|
2099
|
+
}) })
|
|
2100
|
+
] });
|
|
2101
|
+
}
|
|
2102
|
+
function FaqSection({ id, title: title2, intro: intro2, items = [], tone: tone2 }) {
|
|
2103
|
+
return /* @__PURE__ */ jsx24(Faq, { id, title: title2, intro: intro2, items, tone: tone2, openFirst: true });
|
|
2104
|
+
}
|
|
2105
|
+
function RichText({ id, title: title2, html, width = "narrow", tone: tone2 }) {
|
|
2106
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, narrow: width === "narrow", children: [
|
|
2107
|
+
title2 ? /* @__PURE__ */ jsx24("h2", { id: hid(id), className: "dt-lp-h2", children: title2 }) : null,
|
|
2108
|
+
/* @__PURE__ */ jsx24("div", { className: "dt-lp-prose", dangerouslySetInnerHTML: { __html: sanitizeRichText(html) } })
|
|
2109
|
+
] });
|
|
2110
|
+
}
|
|
2111
|
+
function UspStrip({ id, items = [], tone: tone2 = "tinted" }) {
|
|
2112
|
+
return /* @__PURE__ */ jsx24("section", { id, "aria-label": "Highlights", className: `dt-lp-usp dt-lp-tone--${tone2}`, children: /* @__PURE__ */ jsx24("div", { className: "dt-lp-wrap", children: /* @__PURE__ */ jsx24("ul", { className: "dt-lp-usp-list", children: items.map((it, i) => /* @__PURE__ */ jsxs21("li", { className: "dt-lp-usp-item", children: [
|
|
2113
|
+
/* @__PURE__ */ jsx24(NamedIcon, { name: it.icon, size: 20, className: "dt-lp-icon dt-lp-usp-icon" }),
|
|
2114
|
+
/* @__PURE__ */ jsxs21("span", { children: [
|
|
2115
|
+
/* @__PURE__ */ jsx24("strong", { children: it.title }),
|
|
2116
|
+
it.text ? /* @__PURE__ */ jsx24("span", { className: "dt-lp-usp-text", children: it.text }) : null
|
|
2117
|
+
] })
|
|
2118
|
+
] }, i)) }) }) });
|
|
2119
|
+
}
|
|
2120
|
+
function BrandStrip({ id, title: title2, items = [], tone: tone2, media }) {
|
|
2121
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, className: "dt-lp-brands", children: [
|
|
2122
|
+
/* @__PURE__ */ jsx24(Heading, { id, title: title2 }),
|
|
2123
|
+
/* @__PURE__ */ jsx24("ul", { className: "dt-lp-brand-list", children: items.map((it, i) => {
|
|
2124
|
+
const src = media(it.logo, "logo");
|
|
2125
|
+
return /* @__PURE__ */ jsx24("li", { className: "dt-lp-brand-item", children: src ? /* @__PURE__ */ jsx24("img", { src, alt: it.name, loading: "lazy", decoding: "async" }) : /* @__PURE__ */ jsx24("span", { children: it.name }) }, i);
|
|
2126
|
+
}) })
|
|
2127
|
+
] });
|
|
2128
|
+
}
|
|
2129
|
+
function Newsletter({ id, title: title2, text, button_label: button, consent_text: consentText, success_text: success, tone: tone2, onSubscribe }) {
|
|
2130
|
+
const [email, setEmail] = useState6("");
|
|
2131
|
+
const [consent, setConsent] = useState6(false);
|
|
2132
|
+
const [state, setState] = useState6({ status: "idle", message: "" });
|
|
2133
|
+
const submit = async (e) => {
|
|
2134
|
+
e.preventDefault();
|
|
2135
|
+
if (!onSubscribe) return;
|
|
2136
|
+
setState({ status: "busy", message: "" });
|
|
2137
|
+
try {
|
|
2138
|
+
await onSubscribe({ email: email.trim(), consent, section: id });
|
|
2139
|
+
setState({ status: "done", message: success || "Thanks!" });
|
|
2140
|
+
} catch (err) {
|
|
2141
|
+
setState({ status: "error", message: err && err.message || "Could not subscribe. Please try again." });
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: hid(id), tone: tone2, narrow: true, children: [
|
|
2145
|
+
/* @__PURE__ */ jsx24(SectionIntro, { id: hid(id), title: title2, body: text, align: "center" }),
|
|
2146
|
+
state.status === "done" ? /* @__PURE__ */ jsx24("p", { className: "dt-lp-news-done", role: "status", children: state.message }) : /* @__PURE__ */ jsxs21("form", { className: "dt-lp-news", onSubmit: submit, children: [
|
|
2147
|
+
/* @__PURE__ */ jsxs21("div", { className: "dt-lp-news-row", children: [
|
|
2148
|
+
/* @__PURE__ */ jsx24("label", { className: "dt-lp-sr", htmlFor: `${id}-email`, children: "Email" }),
|
|
2149
|
+
/* @__PURE__ */ jsx24("input", { id: `${id}-email`, type: "email", required: true, autoComplete: "email", placeholder: "you@example.com", value: email, onChange: (e) => setEmail(e.target.value), className: "dt-lp-input" }),
|
|
2150
|
+
/* @__PURE__ */ jsx24("button", { type: "submit", className: "dt-lp-btn dt-lp-btn--md dt-lp-btn--primary", disabled: !consent || state.status === "busy", "aria-busy": state.status === "busy" ? "true" : void 0, children: button })
|
|
2151
|
+
] }),
|
|
2152
|
+
/* @__PURE__ */ jsxs21("label", { className: "dt-lp-news-consent", children: [
|
|
2153
|
+
/* @__PURE__ */ jsx24("input", { type: "checkbox", checked: consent, onChange: (e) => setConsent(e.target.checked), required: true }),
|
|
2154
|
+
/* @__PURE__ */ jsx24("span", { children: consentText })
|
|
2155
|
+
] }),
|
|
2156
|
+
state.status === "error" ? /* @__PURE__ */ jsx24("p", { className: "dt-lp-news-error", role: "alert", children: state.message }) : null
|
|
2157
|
+
] })
|
|
2158
|
+
] });
|
|
2159
|
+
}
|
|
2160
|
+
function Gallery({ id, title: title2, items = [], columns = "3", tone: tone2, media }) {
|
|
2161
|
+
const shown = items.map((it) => ({ ...it, src: media(it.image, "gallery") })).filter((it) => it.src);
|
|
2162
|
+
if (!shown.length) return null;
|
|
2163
|
+
return /* @__PURE__ */ jsxs21(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
|
|
2164
|
+
/* @__PURE__ */ jsx24(Heading, { id, title: title2 }),
|
|
2165
|
+
/* @__PURE__ */ jsx24("ul", { className: `dt-lp-gallery dt-lp-gallery--${columns}`, children: shown.map((it, i) => /* @__PURE__ */ jsx24("li", { children: /* @__PURE__ */ jsxs21("figure", { children: [
|
|
2166
|
+
/* @__PURE__ */ jsx24("img", { src: it.src, alt: it.image.alt || it.caption || "", loading: "lazy", decoding: "async" }),
|
|
2167
|
+
it.caption ? /* @__PURE__ */ jsx24("figcaption", { children: it.caption }) : null
|
|
2168
|
+
] }) }, i)) })
|
|
2169
|
+
] });
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
// src/tenant/TenantPage.jsx
|
|
2173
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
2174
|
+
var TENANT_BLOCKS = {
|
|
2175
|
+
hero_slider: HeroSlider,
|
|
2176
|
+
featured: SlotSection,
|
|
2177
|
+
categories: SlotSection,
|
|
2178
|
+
promo_banner: PromoBanner,
|
|
2179
|
+
trust_badges: TrustBadges,
|
|
2180
|
+
cta_cards: CtaCards,
|
|
2181
|
+
testimonials: Testimonials,
|
|
2182
|
+
faq: FaqSection,
|
|
2183
|
+
rich_text: RichText,
|
|
2184
|
+
usp_strip: UspStrip,
|
|
2185
|
+
brand_strip: BrandStrip,
|
|
2186
|
+
newsletter: Newsletter,
|
|
2187
|
+
gallery: Gallery
|
|
2188
|
+
};
|
|
2189
|
+
var SectionBoundary = class extends Component {
|
|
2190
|
+
constructor(props) {
|
|
2191
|
+
super(props);
|
|
2192
|
+
this.state = { failed: false };
|
|
2193
|
+
}
|
|
2194
|
+
static getDerivedStateFromError() {
|
|
2195
|
+
return { failed: true };
|
|
2196
|
+
}
|
|
2197
|
+
render() {
|
|
2198
|
+
return this.state.failed ? null : this.props.children;
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2201
|
+
var noMedia = () => null;
|
|
2202
|
+
function TenantPage({
|
|
2203
|
+
page,
|
|
2204
|
+
theme,
|
|
2205
|
+
slots = {},
|
|
2206
|
+
resolveMedia = noMedia,
|
|
2207
|
+
linkComponent = null,
|
|
2208
|
+
onSubscribe = null,
|
|
2209
|
+
storeName = "",
|
|
2210
|
+
className = "",
|
|
2211
|
+
headingStart = 1
|
|
2212
|
+
}) {
|
|
2213
|
+
const doc = useMemo(() => validatePage(page).page, [page]);
|
|
2214
|
+
const vars = useMemo(() => themeVars(theme), [theme]);
|
|
2215
|
+
const ctx = useMemo(() => ({ brand: { id: "tenant", name: storeName, primary: vars.theme.primary }, linkComponent, onBookDemo: null, home: true }), [storeName, vars.theme.primary, linkComponent]);
|
|
2216
|
+
const media = (ref, variant) => ref && ref.media_id != null ? resolveMedia(ref, variant) : null;
|
|
2217
|
+
const visible = doc.sections.filter((s) => s.enabled && TENANT_BLOCKS[s.type]);
|
|
2218
|
+
return /* @__PURE__ */ jsx25(LandingProvider, { value: ctx, children: /* @__PURE__ */ jsx25("div", { className: `dt-landing dt-lp-tenant ${className}`.trim(), style: vars.style, "data-theme": vars.theme.mode === "auto" ? void 0 : vars.theme.mode, children: visible.map((s, i) => {
|
|
2219
|
+
const Block = TENANT_BLOCKS[s.type];
|
|
2220
|
+
return /* @__PURE__ */ jsx25(SectionBoundary, { children: /* @__PURE__ */ jsx25(Block, { ...s.props, id: `s-${s.id}`, first: i === 0 && headingStart === 1, media, slot: slots[s.type], onSubscribe }) }, s.id);
|
|
2221
|
+
}) }) });
|
|
2222
|
+
}
|
|
1217
2223
|
export {
|
|
1218
2224
|
Audiences,
|
|
1219
2225
|
AuthShell,
|
|
@@ -1234,15 +2240,19 @@ export {
|
|
|
1234
2240
|
FeatureSection,
|
|
1235
2241
|
Header,
|
|
1236
2242
|
Hero,
|
|
2243
|
+
HeroSlider,
|
|
1237
2244
|
HowItWorks,
|
|
1238
2245
|
LandingPage,
|
|
1239
2246
|
LandingProvider,
|
|
1240
2247
|
MarketingLayout,
|
|
2248
|
+
NamedIcon,
|
|
1241
2249
|
PageHero,
|
|
1242
2250
|
PreviewFrame,
|
|
1243
2251
|
Section,
|
|
1244
2252
|
SectionIntro,
|
|
1245
2253
|
SmartLink,
|
|
2254
|
+
TENANT_BLOCKS,
|
|
2255
|
+
TenantPage,
|
|
1246
2256
|
TextLink,
|
|
1247
2257
|
Wordmark,
|
|
1248
2258
|
Wrap,
|
|
@@ -1255,7 +2265,10 @@ export {
|
|
|
1255
2265
|
resolveHref,
|
|
1256
2266
|
stashDemoFill,
|
|
1257
2267
|
takeDemoFill,
|
|
2268
|
+
themeVars,
|
|
1258
2269
|
toLandingBrand,
|
|
1259
2270
|
useLanding,
|
|
1260
|
-
useTapReveal
|
|
2271
|
+
useTapReveal,
|
|
2272
|
+
validatePage,
|
|
2273
|
+
validateTheme
|
|
1261
2274
|
};
|