@avocadostudio-ai/blocks 0.1.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.
Files changed (88) hide show
  1. package/LICENSE +201 -0
  2. package/dist/block-error-boundary.d.ts +18 -0
  3. package/dist/block-error-boundary.js +36 -0
  4. package/dist/blocks/_base.css +108 -0
  5. package/dist/blocks/_layout.css +47 -0
  6. package/dist/blocks/_shared.d.ts +24 -0
  7. package/dist/blocks/_shared.js +153 -0
  8. package/dist/blocks/_tokens.css +127 -0
  9. package/dist/blocks/banner/renderer.d.ts +2 -0
  10. package/dist/blocks/banner/renderer.js +21 -0
  11. package/dist/blocks/banner/styles.css +72 -0
  12. package/dist/blocks/block-image.d.ts +14 -0
  13. package/dist/blocks/block-image.js +10 -0
  14. package/dist/blocks/block-types.d.ts +2 -0
  15. package/dist/blocks/block-types.js +22 -0
  16. package/dist/blocks/card/renderer.d.ts +1 -0
  17. package/dist/blocks/card/renderer.js +11 -0
  18. package/dist/blocks/card/styles.css +94 -0
  19. package/dist/blocks/card-grid/renderer.d.ts +1 -0
  20. package/dist/blocks/card-grid/renderer.js +17 -0
  21. package/dist/blocks/card-grid/styles.css +38 -0
  22. package/dist/blocks/carousel/init.d.ts +8 -0
  23. package/dist/blocks/carousel/init.js +35 -0
  24. package/dist/blocks/carousel/renderer.d.ts +2 -0
  25. package/dist/blocks/carousel/renderer.js +19 -0
  26. package/dist/blocks/carousel/styles.css +136 -0
  27. package/dist/blocks/cta/renderer.d.ts +1 -0
  28. package/dist/blocks/cta/renderer.js +7 -0
  29. package/dist/blocks/cta/styles.css +111 -0
  30. package/dist/blocks/embed/renderer.d.ts +2 -0
  31. package/dist/blocks/embed/renderer.js +16 -0
  32. package/dist/blocks/embed/styles.css +58 -0
  33. package/dist/blocks/faq-accordion/renderer.d.ts +1 -0
  34. package/dist/blocks/faq-accordion/renderer.js +11 -0
  35. package/dist/blocks/faq-accordion/styles.css +68 -0
  36. package/dist/blocks/feature-grid/renderer.d.ts +1 -0
  37. package/dist/blocks/feature-grid/renderer.js +12 -0
  38. package/dist/blocks/feature-grid/styles.css +62 -0
  39. package/dist/blocks/footer/renderer.d.ts +1 -0
  40. package/dist/blocks/footer/renderer.js +19 -0
  41. package/dist/blocks/footer/styles.css +64 -0
  42. package/dist/blocks/gallery/renderer.d.ts +2 -0
  43. package/dist/blocks/gallery/renderer.js +21 -0
  44. package/dist/blocks/gallery/styles.css +59 -0
  45. package/dist/blocks/hero/renderer.d.ts +1 -0
  46. package/dist/blocks/hero/renderer.js +20 -0
  47. package/dist/blocks/hero/styles.css +137 -0
  48. package/dist/blocks/index.d.ts +5 -0
  49. package/dist/blocks/index.js +47 -0
  50. package/dist/blocks/quote/renderer.d.ts +2 -0
  51. package/dist/blocks/quote/renderer.js +9 -0
  52. package/dist/blocks/quote/styles.css +97 -0
  53. package/dist/blocks/rich-text/renderer.d.ts +1 -0
  54. package/dist/blocks/rich-text/renderer.js +15 -0
  55. package/dist/blocks/rich-text/styles.css +64 -0
  56. package/dist/blocks/site-header/renderer.d.ts +1 -0
  57. package/dist/blocks/site-header/renderer.js +64 -0
  58. package/dist/blocks/site-header/styles.css +345 -0
  59. package/dist/blocks/stats/renderer.d.ts +1 -0
  60. package/dist/blocks/stats/renderer.js +13 -0
  61. package/dist/blocks/stats/styles.css +64 -0
  62. package/dist/blocks/styles.css +23 -0
  63. package/dist/blocks/table/renderer.d.ts +2 -0
  64. package/dist/blocks/table/renderer.js +14 -0
  65. package/dist/blocks/table/styles.css +87 -0
  66. package/dist/blocks/tabs/init.d.ts +5 -0
  67. package/dist/blocks/tabs/init.js +38 -0
  68. package/dist/blocks/tabs/renderer.d.ts +2 -0
  69. package/dist/blocks/tabs/renderer.js +30 -0
  70. package/dist/blocks/tabs/styles.css +147 -0
  71. package/dist/blocks/testimonials/renderer.d.ts +1 -0
  72. package/dist/blocks/testimonials/renderer.js +15 -0
  73. package/dist/blocks/testimonials/styles.css +92 -0
  74. package/dist/blocks/two-column/renderer.d.ts +1 -0
  75. package/dist/blocks/two-column/renderer.js +41 -0
  76. package/dist/blocks/two-column/styles.css +83 -0
  77. package/dist/blocks/video/renderer.d.ts +2 -0
  78. package/dist/blocks/video/renderer.js +52 -0
  79. package/dist/blocks/video/styles.css +84 -0
  80. package/dist/blocks-hydrator.d.ts +5 -0
  81. package/dist/blocks-hydrator.js +15 -0
  82. package/dist/catalogue.d.ts +1 -0
  83. package/dist/catalogue.js +741 -0
  84. package/dist/index.d.ts +7 -0
  85. package/dist/index.js +5 -0
  86. package/dist/renderer.d.ts +12 -0
  87. package/dist/renderer.js +35 -0
  88. package/package.json +54 -0
@@ -0,0 +1,137 @@
1
+ .hero {
2
+ border-radius: 0;
3
+ padding: 56px 20px;
4
+ background: var(--hero-bg);
5
+ position: relative;
6
+ overflow: hidden;
7
+ }
8
+
9
+ .hero__inner {
10
+ position: relative;
11
+ gap: 24px;
12
+ }
13
+
14
+ .hero__content h1 {
15
+ margin: 0 0 14px;
16
+ font-size: clamp(2rem, 4.2vw, 3.35rem);
17
+ line-height: 1.08;
18
+ letter-spacing: -0.02em;
19
+ font-weight: 800;
20
+ color: var(--heading);
21
+ }
22
+
23
+ .hero__content p {
24
+ margin: 0;
25
+ font-size: clamp(1rem, 1.65vw, 1.05rem);
26
+ line-height: 1.55;
27
+ color: var(--text-200);
28
+ max-width: 36ch;
29
+ }
30
+
31
+ .hero__media img {
32
+ background: var(--placeholder-img);
33
+ }
34
+
35
+ .hero__eyebrow {
36
+ display: inline-block;
37
+ font-size: 0.8125rem;
38
+ font-weight: 700;
39
+ letter-spacing: 0.08em;
40
+ text-transform: uppercase;
41
+ color: var(--brand);
42
+ margin-bottom: 8px;
43
+ }
44
+
45
+ .hero__content--center {
46
+ text-align: center;
47
+ align-items: center;
48
+ }
49
+
50
+ .hero__content--center p {
51
+ max-width: 52ch;
52
+ }
53
+
54
+ .hero__content--center .hero__actions {
55
+ justify-content: center;
56
+ }
57
+
58
+ .hero__actions {
59
+ display: flex;
60
+ flex-wrap: wrap;
61
+ gap: 12px;
62
+ margin-top: 24px;
63
+ }
64
+
65
+ /* ── Full-width image variant ─────────────────────────────── */
66
+ .hero--full-width {
67
+ position: relative;
68
+ min-height: 420px;
69
+ display: flex;
70
+ align-items: center;
71
+ }
72
+
73
+ .hero--full-width .hero__bg-image {
74
+ position: absolute;
75
+ inset: 0;
76
+ width: 100%;
77
+ height: 100%;
78
+ object-fit: cover;
79
+ z-index: 0;
80
+ }
81
+
82
+ .hero--full-width::after {
83
+ content: "";
84
+ position: absolute;
85
+ inset: 0;
86
+ background: rgba(0, 0, 0, 0.45);
87
+ z-index: 0;
88
+ }
89
+
90
+ .hero--full-width .hero__inner {
91
+ position: relative;
92
+ z-index: 1;
93
+ display: flex;
94
+ flex-direction: column;
95
+ align-items: center;
96
+ text-align: center;
97
+ width: 100%;
98
+ }
99
+
100
+ .hero--full-width .hero__content {
101
+ max-width: 720px;
102
+ }
103
+
104
+ .hero--full-width .hero__eyebrow {
105
+ color: rgba(255, 255, 255, 0.85);
106
+ }
107
+
108
+ .hero--full-width .hero__content h1,
109
+ .hero--full-width .hero__content h2,
110
+ .hero--full-width .hero__content h3 {
111
+ color: #fff;
112
+ }
113
+
114
+ .hero--full-width .hero__content p {
115
+ color: rgba(255, 255, 255, 0.9);
116
+ max-width: 52ch;
117
+ }
118
+
119
+ .hero--full-width .hero__actions {
120
+ justify-content: center;
121
+ }
122
+
123
+ /* Dark */
124
+ .dark .hero__content p {
125
+ color: var(--body);
126
+ }
127
+
128
+ @media (max-width: 900px) {
129
+ .hero__content h1 {
130
+ font-size: clamp(1.75rem, 8vw, 2.5rem);
131
+ line-height: 1.12;
132
+ }
133
+
134
+ .hero {
135
+ padding: 40px 16px;
136
+ }
137
+ }
@@ -0,0 +1,5 @@
1
+ import type { JSX } from "react";
2
+ import { type RendererBlockType } from "./block-types.ts";
3
+ export declare const renderers: Record<RendererBlockType, (props: Record<string, unknown>) => JSX.Element | null>;
4
+ export declare const rendererTypes: ("Hero" | "FeatureGrid" | "Testimonials" | "FAQAccordion" | "CTA" | "Card" | "CardGrid" | "RichText" | "Stats" | "TwoColumn" | "Footer" | "Gallery" | "Carousel" | "Tabs" | "Quote" | "SiteHeader" | "Embed" | "Banner" | "Table" | "Video")[];
5
+ export declare function isRendererBlockType(value: string): value is RendererBlockType;
@@ -0,0 +1,47 @@
1
+ import { rendererBlockTypes } from "./block-types.js";
2
+ import { Hero } from "./hero/renderer.js";
3
+ import { FeatureGrid } from "./feature-grid/renderer.js";
4
+ import { Testimonials } from "./testimonials/renderer.js";
5
+ import { FAQAccordion } from "./faq-accordion/renderer.js";
6
+ import { CTA } from "./cta/renderer.js";
7
+ import { Card } from "./card/renderer.js";
8
+ import { CardGrid } from "./card-grid/renderer.js";
9
+ import { RichText } from "./rich-text/renderer.js";
10
+ import { Stats } from "./stats/renderer.js";
11
+ import { TwoColumn } from "./two-column/renderer.js";
12
+ import { Footer } from "./footer/renderer.js";
13
+ import { SiteHeader } from "./site-header/renderer.js";
14
+ import { Embed } from "./embed/renderer.js";
15
+ import { Banner } from "./banner/renderer.js";
16
+ import { Carousel } from "./carousel/renderer.js";
17
+ import { Gallery } from "./gallery/renderer.js";
18
+ import { Tabs } from "./tabs/renderer.js";
19
+ import { Table } from "./table/renderer.js";
20
+ import { Quote } from "./quote/renderer.js";
21
+ import { Video } from "./video/renderer.js";
22
+ export const renderers = {
23
+ Hero,
24
+ FeatureGrid,
25
+ Testimonials,
26
+ FAQAccordion,
27
+ CTA,
28
+ Card,
29
+ CardGrid,
30
+ RichText,
31
+ Stats,
32
+ TwoColumn,
33
+ Footer,
34
+ SiteHeader,
35
+ Embed,
36
+ Banner,
37
+ Carousel,
38
+ Gallery,
39
+ Tabs,
40
+ Table,
41
+ Quote,
42
+ Video
43
+ };
44
+ export const rendererTypes = [...rendererBlockTypes];
45
+ export function isRendererBlockType(value) {
46
+ return rendererBlockTypes.includes(value);
47
+ }
@@ -0,0 +1,2 @@
1
+ import type { JSX } from "react";
2
+ export declare function Quote(props: Record<string, unknown>): JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { renderRichTextContent, BlockImage } from "../_shared.js";
3
+ export function Quote(props) {
4
+ const quote = String(props.quote ?? "");
5
+ const author = String(props.author ?? "");
6
+ const role = String(props.role ?? "");
7
+ const imageUrl = String(props.imageUrl ?? "");
8
+ return (_jsx("section", { className: "quote-block", children: _jsxs("div", { className: "section__inner", children: [_jsxs("blockquote", { className: "quote-block__quote", children: [_jsx("span", { className: "quote-block__mark", "aria-hidden": "true", children: "\u201C" }), _jsx("div", { className: "quote-block__text", "data-editable-target": "quote", "data-editable-target-label": "quote", "data-editable-label": "quote", children: renderRichTextContent(quote) })] }), (author.length > 0 || role.length > 0) && (_jsxs("div", { className: "quote-block__attribution", children: [imageUrl.length > 0 && (_jsx(BlockImage, { className: "quote-block__avatar", src: imageUrl, alt: author || "Author", width: 48, height: 48 })), _jsxs("div", { className: "quote-block__author-info", children: [author.length > 0 && (_jsx("span", { className: "quote-block__author", "data-editable-target": "author", "data-editable-target-label": "author", "data-editable-label": "author", children: author })), role.length > 0 && (_jsx("span", { className: "quote-block__role", "data-editable-target": "role", "data-editable-target-label": "role", "data-editable-label": "role", children: role }))] })] }))] }) }));
9
+ }
@@ -0,0 +1,97 @@
1
+ .quote-block {
2
+ padding: 40px 20px;
3
+ background: var(--section-bg);
4
+ }
5
+
6
+ .quote-block .section__inner {
7
+ max-width: 720px;
8
+ text-align: center;
9
+ }
10
+
11
+ .quote-block__quote {
12
+ margin: 0;
13
+ padding: 0;
14
+ position: relative;
15
+ }
16
+
17
+ .quote-block__mark {
18
+ display: block;
19
+ font-size: 4rem;
20
+ line-height: 0.8;
21
+ color: var(--brand);
22
+ font-family: Georgia, "Times New Roman", serif;
23
+ user-select: none;
24
+ }
25
+
26
+ .quote-block__text {
27
+ font-size: 1.25rem;
28
+ line-height: 1.6;
29
+ color: var(--heading);
30
+ font-style: italic;
31
+ margin-top: 4px;
32
+ }
33
+
34
+ .quote-block__text p {
35
+ margin: 0 0 8px;
36
+ }
37
+
38
+ .quote-block__text p:last-child {
39
+ margin-bottom: 0;
40
+ }
41
+
42
+ /* Attribution row */
43
+ .quote-block__attribution {
44
+ display: inline-flex;
45
+ flex-direction: row;
46
+ align-items: center;
47
+ gap: 12px;
48
+ margin-top: 20px;
49
+ padding-top: 16px;
50
+ border-top: 1px solid var(--border);
51
+ text-align: left;
52
+ }
53
+
54
+ .quote-block__avatar {
55
+ width: 48px;
56
+ height: 48px;
57
+ min-width: 48px;
58
+ min-height: 48px;
59
+ max-width: 48px;
60
+ max-height: 48px;
61
+ border-radius: 50%;
62
+ object-fit: cover;
63
+ display: block;
64
+ }
65
+
66
+ .quote-block__author-info {
67
+ display: flex;
68
+ flex-direction: column;
69
+ gap: 2px;
70
+ }
71
+
72
+ .quote-block__author {
73
+ display: block;
74
+ font-weight: 600;
75
+ font-size: 0.95rem;
76
+ color: var(--heading);
77
+ }
78
+
79
+ .quote-block__role {
80
+ display: block;
81
+ font-size: 0.85rem;
82
+ color: var(--text-200);
83
+ }
84
+
85
+ @media (max-width: 600px) {
86
+ .quote-block {
87
+ padding: 24px 12px;
88
+ }
89
+
90
+ .quote-block__mark {
91
+ font-size: 3rem;
92
+ }
93
+
94
+ .quote-block__text {
95
+ font-size: 1.1rem;
96
+ }
97
+ }
@@ -0,0 +1 @@
1
+ export declare function RichText(props: Record<string, unknown>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { isValidElement } from "react";
3
+ import { resolveHeadingTag } from "@avocadostudio-ai/shared";
4
+ import { renderRichTextContent } from "../_shared.js";
5
+ export function RichText(props) {
6
+ const title = String(props.title ?? "");
7
+ // Puck's richtext transform passes React elements; plain strings come from
8
+ // the orchestrator/preview. Handle both.
9
+ const body = props.body;
10
+ const renderedBody = isValidElement(body)
11
+ ? body
12
+ : renderRichTextContent(String(body ?? ""));
13
+ const HeadingTag = resolveHeadingTag("RichText", props);
14
+ return (_jsx("section", { className: "rich-text", children: _jsxs("div", { className: "section__inner", children: [title.length > 0 && (_jsx(HeadingTag, { "data-editable-target": "title", "data-editable-target-label": "title", "data-editable-label": "title", children: title })), _jsx("div", { className: "rich-text__body", "data-editable-target": "body", "data-editable-target-label": "body", "data-editable-label": "body", children: renderedBody })] }) }));
15
+ }
@@ -0,0 +1,64 @@
1
+ .rich-text {}
2
+
3
+ .rich-text__body p {
4
+ font-size: 1rem;
5
+ line-height: 1.75;
6
+ color: var(--body);
7
+ margin-bottom: 1em;
8
+ }
9
+
10
+ .rich-text__body p:last-child {
11
+ margin-bottom: 0;
12
+ }
13
+
14
+ .rich-text__body ul,
15
+ .rich-text__body ol {
16
+ margin: 0 0 1em;
17
+ padding-left: 1.4em;
18
+ color: var(--body);
19
+ line-height: 1.75;
20
+ }
21
+
22
+ .rich-text__body ul {
23
+ list-style: disc;
24
+ }
25
+
26
+ .rich-text__body ol {
27
+ list-style: decimal;
28
+ }
29
+
30
+ .rich-text__body li {
31
+ display: list-item;
32
+ }
33
+
34
+ .rich-text__body li + li {
35
+ margin-top: 0.3em;
36
+ }
37
+
38
+ .rich-text__body h3 {
39
+ margin-top: 1.25em;
40
+ margin-bottom: 0.45em;
41
+ font-size: 1.25rem;
42
+ line-height: 1.3;
43
+ letter-spacing: -0.01em;
44
+ font-weight: 700;
45
+ color: var(--heading);
46
+ }
47
+
48
+ /* Dark */
49
+ .dark .rich-text__body,
50
+ .dark .rich-text__body li,
51
+ .dark .rich-text__body strong,
52
+ .dark .rich-text__body em {
53
+ color: #dbe7f7;
54
+ }
55
+
56
+ .dark main.editor-mode .rich-text__body p,
57
+ .dark main.editor-mode .rich-text__body li {
58
+ color: #dbe7f7;
59
+ }
60
+
61
+ .dark .editor-live-draft-active .rich-text__body[data-editable-target]::after {
62
+ background: rgba(56, 189, 248, 0.025);
63
+ animation: none;
64
+ }
@@ -0,0 +1 @@
1
+ export declare function SiteHeader(props: Record<string, unknown>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useRef, useEffect, useCallback } from "react";
4
+ import { BlockImage } from "../_shared.js";
5
+ function ChevronDown({ className }) {
6
+ return (_jsx("svg", { className: className, width: "14", height: "14", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
7
+ }
8
+ export function SiteHeader(props) {
9
+ const siteName = String(props.siteName ?? "Site");
10
+ const logoUrl = String(props.logoUrl ?? "/logo.svg");
11
+ const activePath = typeof props.activePath === "string" ? props.activePath : undefined;
12
+ const links = Array.isArray(props.links)
13
+ ? props.links.filter((l) => l && typeof l.label === "string" && (typeof l.href === "string" || Array.isArray(l.children)))
14
+ : [];
15
+ const [isOpen, setIsOpen] = useState(false);
16
+ const [expandedGroup, setExpandedGroup] = useState(null);
17
+ const menuRef = useRef(null);
18
+ const toggleRef = useRef(null);
19
+ const menuId = "site-mobile-menu";
20
+ const close = useCallback(() => { setIsOpen(false); setExpandedGroup(null); }, []);
21
+ const toggle = useCallback(() => setIsOpen((v) => !v), []);
22
+ // Close on popstate (SPA navigation)
23
+ useEffect(() => {
24
+ const handler = () => setIsOpen(false);
25
+ window.addEventListener("popstate", handler);
26
+ return () => window.removeEventListener("popstate", handler);
27
+ }, []);
28
+ // Outside click & Escape key
29
+ useEffect(() => {
30
+ if (!isOpen)
31
+ return;
32
+ function handlePointerDown(event) {
33
+ if (!menuRef.current?.contains(event.target))
34
+ setIsOpen(false);
35
+ }
36
+ function handleEscape(event) {
37
+ if (event.key === "Escape") {
38
+ setIsOpen(false);
39
+ toggleRef.current?.focus();
40
+ }
41
+ }
42
+ window.addEventListener("pointerdown", handlePointerDown);
43
+ window.addEventListener("keydown", handleEscape);
44
+ return () => {
45
+ window.removeEventListener("pointerdown", handlePointerDown);
46
+ window.removeEventListener("keydown", handleEscape);
47
+ };
48
+ }, [isOpen]);
49
+ // Focus first link when menu opens
50
+ useEffect(() => {
51
+ if (isOpen) {
52
+ const first = menuRef.current?.querySelector("a, button:not([aria-expanded])");
53
+ first?.focus();
54
+ }
55
+ }, [isOpen]);
56
+ const isActive = (href) => {
57
+ if (!activePath || !href)
58
+ return false;
59
+ const clean = href.split("?")[0];
60
+ return clean === activePath;
61
+ };
62
+ const hasActiveChild = (link) => link.children?.some((c) => isActive(c.href)) ?? false;
63
+ return (_jsxs(_Fragment, { children: [_jsx("header", { className: "site-top-nav", "data-block-chrome": "true", children: _jsxs("div", { className: "site-top-nav-inner", children: [_jsxs("a", { className: "site-brand", href: links[0]?.href ?? "/", "data-editable-target": "siteName", children: [logoUrl ? _jsx(BlockImage, { className: "site-logo", src: logoUrl, alt: `${siteName} logo`, width: 38, height: 38, "data-editable-target": "logoUrl" }) : null, _jsx("span", { className: "site-brand-text", children: siteName })] }), _jsx("nav", { className: "site-nav-links site-nav-links-desktop", "aria-label": "Primary", children: links.map((link, i) => link.children?.length ? (_jsxs("div", { className: "site-nav-dropdown", children: [_jsxs("button", { type: "button", className: `site-nav-dropdown-trigger${hasActiveChild(link) ? " is-active" : ""}`, "data-editable-target": `links[${i}].label`, "data-editable-target-label": `links[${i}].label`, children: [link.label, _jsx(ChevronDown, { className: "site-nav-chevron" })] }), _jsx("div", { className: "site-nav-dropdown-menu", children: link.children.map((child, j) => (_jsx("a", { href: child.href ?? "#", className: isActive(child.href) ? "is-active" : undefined, "data-editable-target": `links[${i}].children[${j}].label`, "data-editable-target-label": `links[${i}].children[${j}].label`, onClick: (e) => e.currentTarget.blur(), children: child.label }, child.href))) })] }, `group-${i}`)) : (_jsx("a", { href: link.href ?? "#", className: isActive(link.href) ? "is-active" : undefined, "data-editable-target": `links[${i}].label`, "data-editable-target-label": `links[${i}].label`, children: link.label }, link.href))) }), _jsxs("div", { ref: menuRef, className: `site-mobile-menu${isOpen ? " is-open" : ""}`, children: [_jsx("button", { ref: toggleRef, type: "button", className: "site-mobile-menu-button", "aria-expanded": isOpen, "aria-controls": menuId, "aria-label": "Toggle navigation menu", onClick: toggle, children: _jsxs("span", { className: "burger-icon", "aria-hidden": "true", children: [_jsx("i", {}), _jsx("i", {}), _jsx("i", {})] }) }), isOpen ? (_jsx("nav", { id: menuId, className: "site-nav-links site-nav-links-mobile", "aria-label": "Mobile primary", children: links.map((link, i) => link.children?.length ? (_jsxs("div", { className: "site-nav-mobile-group", children: [_jsxs("button", { type: "button", className: `site-nav-mobile-group-trigger${hasActiveChild(link) ? " is-active" : ""}`, "aria-expanded": expandedGroup === i, onClick: () => setExpandedGroup(expandedGroup === i ? null : i), "data-editable-target": `links[${i}].label`, children: [link.label, _jsx(ChevronDown, { className: `site-nav-chevron${expandedGroup === i ? " is-expanded" : ""}` })] }), expandedGroup === i ? (_jsx("div", { className: "site-nav-mobile-group-children", children: link.children.map((child, j) => (_jsx("a", { href: child.href ?? "#", className: isActive(child.href) ? "is-active" : undefined, "data-editable-target": `links[${i}].children[${j}].label`, onClick: close, children: child.label }, `mobile-${child.href}`))) })) : null] }, `mobile-group-${i}`)) : (_jsx("a", { href: link.href ?? "#", className: isActive(link.href) ? "is-active" : undefined, "data-editable-target": `links[${i}].label`, onClick: close, children: link.label }, `mobile-${link.href}`))) })) : null] })] }) }), isOpen ? (_jsx("div", { className: "site-mobile-backdrop", onClick: close })) : null] }));
64
+ }