@frontlit/page-builder 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/alignment-dZDzN96G.d.ts +5 -0
- package/dist/blocks/index.d.ts +255 -0
- package/dist/blocks/index.js +6 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/builder/index.d.ts +240 -0
- package/dist/builder/index.js +8 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/button-C3VrMJkr.d.ts +20 -0
- package/dist/chunk-7KIX4WAO.js +4229 -0
- package/dist/chunk-7KIX4WAO.js.map +1 -0
- package/dist/chunk-7W6Q4WA7.js +515 -0
- package/dist/chunk-7W6Q4WA7.js.map +1 -0
- package/dist/chunk-GTY75HW2.js +22 -0
- package/dist/chunk-GTY75HW2.js.map +1 -0
- package/dist/chunk-MO76KZY2.js +3580 -0
- package/dist/chunk-MO76KZY2.js.map +1 -0
- package/dist/chunk-RBJBODQP.js +2011 -0
- package/dist/chunk-RBJBODQP.js.map +1 -0
- package/dist/chunk-RUYOL7JZ.js +4 -0
- package/dist/chunk-RUYOL7JZ.js.map +1 -0
- package/dist/chunk-SSQWYMQR.js +393 -0
- package/dist/chunk-SSQWYMQR.js.map +1 -0
- package/dist/color-mode-DIEEt9WP.d.ts +3 -0
- package/dist/components/index.d.ts +36 -0
- package/dist/components/index.js +5 -0
- package/dist/components/index.js.map +1 -0
- package/dist/data-slots.d.ts +25 -0
- package/dist/data-slots.js +3 -0
- package/dist/data-slots.js.map +1 -0
- package/dist/fonts/alegreya/alegreya-latin-variable.woff2 +0 -0
- package/dist/fonts/bebas-neue/bebas-neue-latin-400-normal.woff2 +0 -0
- package/dist/fonts/inter/inter-latin-variable.woff2 +0 -0
- package/dist/fonts/lato/lato-latin-100-normal.woff2 +0 -0
- package/dist/fonts/lato/lato-latin-300-normal.woff2 +0 -0
- package/dist/fonts/lato/lato-latin-400-normal.woff2 +0 -0
- package/dist/fonts/lato/lato-latin-700-normal.woff2 +0 -0
- package/dist/fonts/lato/lato-latin-900-normal.woff2 +0 -0
- package/dist/fonts/merriweather/merriweather-latin-variable.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-100-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-200-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-300-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-400-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-500-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-600-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-700-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-800-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-900-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-variable.woff2 +0 -0
- package/dist/fonts/mulish/mulish-latin-variable.woff2 +0 -0
- package/dist/fonts/noto-sans/noto-sans-latin-variable.woff2 +0 -0
- package/dist/fonts/nunito/nunito-latin-variable.woff2 +0 -0
- package/dist/fonts/open-sans/open-sans-latin-variable.woff2 +0 -0
- package/dist/fonts/oswald/oswald-latin-variable.woff2 +0 -0
- package/dist/fonts/playfair-display/playfair-display-latin-variable.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-100-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-200-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-300-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-400-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-500-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-600-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-700-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-800-normal.woff2 +0 -0
- package/dist/fonts/poppins/poppins-latin-900-normal.woff2 +0 -0
- package/dist/fonts/pt-sans/pt-sans-latin-400-normal.woff2 +0 -0
- package/dist/fonts/pt-sans/pt-sans-latin-700-normal.woff2 +0 -0
- package/dist/fonts/quicksand/quicksand-latin-variable.woff2 +0 -0
- package/dist/fonts/raleway/raleway-latin-variable.woff2 +0 -0
- package/dist/fonts/roboto/roboto-latin-variable.woff2 +0 -0
- package/dist/fonts/roboto-slab/roboto-slab-latin-variable.woff2 +0 -0
- package/dist/fonts/rubik/rubik-latin-variable.woff2 +0 -0
- package/dist/fonts/source-sans-3/source-sans-3-latin-400-normal.woff2 +0 -0
- package/dist/fonts/source-sans-3/source-sans-3-latin-variable.woff2 +0 -0
- package/dist/fonts/source-serif-4/source-serif-4-latin-variable.woff2 +0 -0
- package/dist/fonts/work-sans/work-sans-latin-variable.woff2 +0 -0
- package/dist/image-D7GlnOio.d.ts +30 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/media-Njv378j7.d.ts +13 -0
- package/dist/models/index.d.ts +8 -0
- package/dist/models/index.js +4 -0
- package/dist/models/index.js.map +1 -0
- package/dist/nav-link-BfxvQ1s6.d.ts +37 -0
- package/dist/page-DfVl_aHX.d.ts +26 -0
- package/dist/primitives/index.d.ts +140 -0
- package/dist/primitives/index.js +4 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/renderer/index.d.ts +81 -0
- package/dist/renderer/index.js +7 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/section-background-BgOCp5l3.d.ts +50 -0
- package/dist/styles.css +15322 -0
- package/dist/theme-style-39nxXQy0.d.ts +141 -0
- package/dist/theme.css +242 -0
- package/dist/widget--uEOpdq3.d.ts +105 -0
- package/package.json +142 -0
|
@@ -0,0 +1,3580 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { blockRegistry, getAddableBlocks, getWidget, PageRenderer } from './chunk-SSQWYMQR.js';
|
|
3
|
+
import { generateUniqueId, FieldGroup, TextField, ToggleField } from './chunk-7KIX4WAO.js';
|
|
4
|
+
import { ThemeModeSwitcher } from './chunk-7W6Q4WA7.js';
|
|
5
|
+
import { themes, cn, Button, Switch, Input, PageCard, PageCardContent, PageCardHeader, Link, Button2 } from './chunk-RBJBODQP.js';
|
|
6
|
+
import React2, { useState, useMemo, useCallback, useRef, useEffect } from 'react';
|
|
7
|
+
import * as Icons from 'lucide-react';
|
|
8
|
+
import { Smartphone, Tablet, Monitor, Palette, Globe, Plus, HelpCircle, X, ImageIcon, Sparkles, ExternalLink, Check, ChevronLeft, LayoutTemplate, Layers3, Pin, ChevronDown, FilePenLine, Brush, ChevronRight, Type, SquareMousePointer, Columns3, Pencil, Info } from 'lucide-react';
|
|
9
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
10
|
+
import { createPortal } from 'react-dom';
|
|
11
|
+
|
|
12
|
+
// src/utils/debounce.ts
|
|
13
|
+
function debounce(fn, wait) {
|
|
14
|
+
let timeout;
|
|
15
|
+
return function(...args) {
|
|
16
|
+
clearTimeout(timeout);
|
|
17
|
+
timeout = setTimeout(() => {
|
|
18
|
+
fn(...args);
|
|
19
|
+
}, wait);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/utils/color-mode.ts
|
|
24
|
+
function resolveColorMode(mode, systemPrefersDark) {
|
|
25
|
+
if (mode === "system") return systemPrefersDark ? "dark" : "light";
|
|
26
|
+
return mode;
|
|
27
|
+
}
|
|
28
|
+
function toggleColorMode(resolved) {
|
|
29
|
+
return resolved === "dark" ? "light" : "dark";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/starter.ts
|
|
33
|
+
function createWidgetInstance(name, overrides) {
|
|
34
|
+
const definition = blockRegistry[name];
|
|
35
|
+
return {
|
|
36
|
+
widgetId: generateUniqueId(),
|
|
37
|
+
name,
|
|
38
|
+
deletable: !["header", "footer", "data-slot"].includes(name),
|
|
39
|
+
moveable: !["header", "footer"].includes(name),
|
|
40
|
+
shared: ["header", "footer"].includes(name),
|
|
41
|
+
settings: {
|
|
42
|
+
...definition?.getDefaultSettings?.() ?? {},
|
|
43
|
+
...overrides ?? {}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function ensureRequiredSharedWidgets(layout) {
|
|
48
|
+
const header = layout.find((widget) => widget.name === "header") ?? createWidgetInstance("header");
|
|
49
|
+
const footer = layout.find((widget) => widget.name === "footer") ?? createWidgetInstance("footer");
|
|
50
|
+
const body = layout.filter(
|
|
51
|
+
(widget) => !["header", "footer"].includes(widget.name)
|
|
52
|
+
);
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
...header,
|
|
56
|
+
name: "header",
|
|
57
|
+
deletable: false,
|
|
58
|
+
moveable: false,
|
|
59
|
+
shared: true
|
|
60
|
+
},
|
|
61
|
+
...body,
|
|
62
|
+
{
|
|
63
|
+
...footer,
|
|
64
|
+
name: "footer",
|
|
65
|
+
deletable: false,
|
|
66
|
+
moveable: false,
|
|
67
|
+
shared: true
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
function getDefaultTheme() {
|
|
72
|
+
return themes[0];
|
|
73
|
+
}
|
|
74
|
+
function createDefaultLayout() {
|
|
75
|
+
return ensureRequiredSharedWidgets([
|
|
76
|
+
createWidgetInstance("header", { ctaHref: "/builder" }),
|
|
77
|
+
createWidgetInstance("hero", { buttonAction: "/builder" }),
|
|
78
|
+
createWidgetInstance("featured"),
|
|
79
|
+
createWidgetInstance("content"),
|
|
80
|
+
createWidgetInstance("pricing"),
|
|
81
|
+
createWidgetInstance("faq"),
|
|
82
|
+
createWidgetInstance("email-form"),
|
|
83
|
+
createWidgetInstance("footer")
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
var DEVICES = [
|
|
87
|
+
{ key: "mobile", label: "Mobile preview", icon: Smartphone },
|
|
88
|
+
{ key: "tablet", label: "Tablet preview", icon: Tablet },
|
|
89
|
+
{ key: "desktop", label: "Desktop preview", icon: Monitor }
|
|
90
|
+
];
|
|
91
|
+
function BuilderTopBar({
|
|
92
|
+
resolvedMode,
|
|
93
|
+
onModeChange,
|
|
94
|
+
device,
|
|
95
|
+
onDeviceChange,
|
|
96
|
+
onOpenTheme,
|
|
97
|
+
onOpenSeo,
|
|
98
|
+
onAddBlock,
|
|
99
|
+
allowThemeEditing = true
|
|
100
|
+
}) {
|
|
101
|
+
return /* @__PURE__ */ jsxs("header", { className: "flex h-14 w-full shrink-0 items-center justify-between border-b bg-background px-4", children: [
|
|
102
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Page builder" }) }),
|
|
103
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
"div",
|
|
106
|
+
{
|
|
107
|
+
className: "flex items-center gap-0.5 rounded-md border border-border p-0.5",
|
|
108
|
+
role: "group",
|
|
109
|
+
"aria-label": "Preview device",
|
|
110
|
+
children: DEVICES.map(({ key, label, icon: Icon }) => /* @__PURE__ */ jsx(
|
|
111
|
+
"button",
|
|
112
|
+
{
|
|
113
|
+
type: "button",
|
|
114
|
+
title: label,
|
|
115
|
+
"aria-label": label,
|
|
116
|
+
"aria-pressed": device === key,
|
|
117
|
+
className: cn(
|
|
118
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-sm transition-colors",
|
|
119
|
+
device === key ? "bg-muted text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
120
|
+
),
|
|
121
|
+
onClick: () => onDeviceChange(key),
|
|
122
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
|
|
123
|
+
},
|
|
124
|
+
key
|
|
125
|
+
))
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
/* @__PURE__ */ jsx(
|
|
129
|
+
"div",
|
|
130
|
+
{
|
|
131
|
+
className: "h-6 w-px bg-border",
|
|
132
|
+
role: "separator",
|
|
133
|
+
"aria-orientation": "vertical"
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
137
|
+
/* @__PURE__ */ jsx(
|
|
138
|
+
ThemeModeSwitcher,
|
|
139
|
+
{
|
|
140
|
+
resolved: resolvedMode,
|
|
141
|
+
onToggle: () => onModeChange(toggleColorMode(resolvedMode))
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
allowThemeEditing && /* @__PURE__ */ jsx(
|
|
145
|
+
Button,
|
|
146
|
+
{
|
|
147
|
+
variant: "outline",
|
|
148
|
+
size: "icon",
|
|
149
|
+
title: "Theme",
|
|
150
|
+
"aria-label": "Edit theme",
|
|
151
|
+
onClick: onOpenTheme,
|
|
152
|
+
children: /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
|
|
153
|
+
}
|
|
154
|
+
),
|
|
155
|
+
/* @__PURE__ */ jsx(
|
|
156
|
+
Button,
|
|
157
|
+
{
|
|
158
|
+
variant: "outline",
|
|
159
|
+
size: "icon",
|
|
160
|
+
title: "SEO",
|
|
161
|
+
"aria-label": "Edit SEO settings",
|
|
162
|
+
onClick: onOpenSeo,
|
|
163
|
+
children: /* @__PURE__ */ jsx(Globe, { className: "h-4 w-4" })
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
] }),
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
"div",
|
|
169
|
+
{
|
|
170
|
+
className: "h-6 w-px bg-border",
|
|
171
|
+
role: "separator",
|
|
172
|
+
"aria-orientation": "vertical"
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
/* @__PURE__ */ jsxs(
|
|
176
|
+
Button,
|
|
177
|
+
{
|
|
178
|
+
size: "sm",
|
|
179
|
+
className: "gap-2 whitespace-nowrap",
|
|
180
|
+
title: "Add a new block to the page",
|
|
181
|
+
onClick: onAddBlock,
|
|
182
|
+
children: [
|
|
183
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
184
|
+
"Add block"
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
] })
|
|
189
|
+
] });
|
|
190
|
+
}
|
|
191
|
+
var COPIED_ATTR = "data-pb-preview-style";
|
|
192
|
+
var THEME_FONT_VARS = [
|
|
193
|
+
"--font-sans",
|
|
194
|
+
"--font-mono",
|
|
195
|
+
"--font-inter",
|
|
196
|
+
"--font-open-sans",
|
|
197
|
+
"--font-source-sans-3",
|
|
198
|
+
"--font-noto-sans",
|
|
199
|
+
"--font-roboto",
|
|
200
|
+
"--font-mulish",
|
|
201
|
+
"--font-nunito",
|
|
202
|
+
"--font-work-sans",
|
|
203
|
+
"--font-merriweather",
|
|
204
|
+
"--font-alegreya",
|
|
205
|
+
"--font-playfair-display",
|
|
206
|
+
"--font-roboto-slab",
|
|
207
|
+
"--font-source-serif-4",
|
|
208
|
+
"--font-montserrat",
|
|
209
|
+
"--font-poppins",
|
|
210
|
+
"--font-raleway",
|
|
211
|
+
"--font-rubik",
|
|
212
|
+
"--font-oswald",
|
|
213
|
+
"--font-bebas-neue",
|
|
214
|
+
"--font-lato",
|
|
215
|
+
"--font-pt-sans",
|
|
216
|
+
"--font-quicksand"
|
|
217
|
+
];
|
|
218
|
+
function readCssVar(sourceDoc, name) {
|
|
219
|
+
const view = sourceDoc.defaultView;
|
|
220
|
+
if (!view) return "";
|
|
221
|
+
const html = view.getComputedStyle(sourceDoc.documentElement).getPropertyValue(name).trim();
|
|
222
|
+
if (html) return html;
|
|
223
|
+
return view.getComputedStyle(sourceDoc.body).getPropertyValue(name).trim();
|
|
224
|
+
}
|
|
225
|
+
function syncFontVariables(sourceDoc, targetDoc) {
|
|
226
|
+
const root = targetDoc.documentElement;
|
|
227
|
+
for (const name of THEME_FONT_VARS) {
|
|
228
|
+
const value = readCssVar(sourceDoc, name);
|
|
229
|
+
if (value) root.style.setProperty(name, value);
|
|
230
|
+
else root.style.removeProperty(name);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function syncStyles(sourceDoc, targetDoc) {
|
|
234
|
+
targetDoc.head.querySelectorAll(`[${COPIED_ATTR}]`).forEach((node) => node.remove());
|
|
235
|
+
sourceDoc.querySelectorAll('style, link[rel="stylesheet"]').forEach((node) => {
|
|
236
|
+
const clone = node.cloneNode(true);
|
|
237
|
+
clone.setAttribute(COPIED_ATTR, "");
|
|
238
|
+
targetDoc.head.appendChild(clone);
|
|
239
|
+
});
|
|
240
|
+
syncFontVariables(sourceDoc, targetDoc);
|
|
241
|
+
}
|
|
242
|
+
function PreviewFrame({
|
|
243
|
+
className,
|
|
244
|
+
title = "Page preview",
|
|
245
|
+
children
|
|
246
|
+
}) {
|
|
247
|
+
const iframeRef = useRef(null);
|
|
248
|
+
const [mountNode, setMountNode] = useState(null);
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
const iframe = iframeRef.current;
|
|
251
|
+
if (!iframe) return;
|
|
252
|
+
let observer;
|
|
253
|
+
const init = () => {
|
|
254
|
+
const doc = iframe.contentDocument;
|
|
255
|
+
if (!doc) return;
|
|
256
|
+
if (!doc.getElementById("pb-preview-base")) {
|
|
257
|
+
const base = doc.createElement("style");
|
|
258
|
+
base.id = "pb-preview-base";
|
|
259
|
+
base.textContent = "html,body{margin:0;height:100%;}";
|
|
260
|
+
doc.head.appendChild(base);
|
|
261
|
+
}
|
|
262
|
+
syncStyles(document, doc);
|
|
263
|
+
observer?.disconnect();
|
|
264
|
+
observer = new MutationObserver(() => syncStyles(document, doc));
|
|
265
|
+
observer.observe(document.head, {
|
|
266
|
+
childList: true,
|
|
267
|
+
subtree: true,
|
|
268
|
+
characterData: true
|
|
269
|
+
});
|
|
270
|
+
setMountNode(doc.body);
|
|
271
|
+
};
|
|
272
|
+
if (iframe.contentDocument?.readyState === "complete") init();
|
|
273
|
+
iframe.addEventListener("load", init);
|
|
274
|
+
return () => {
|
|
275
|
+
iframe.removeEventListener("load", init);
|
|
276
|
+
observer?.disconnect();
|
|
277
|
+
};
|
|
278
|
+
}, []);
|
|
279
|
+
return /* @__PURE__ */ jsx("iframe", { ref: iframeRef, title, className, children: mountNode ? createPortal(children, mountNode) : null });
|
|
280
|
+
}
|
|
281
|
+
function resolveIcon(name) {
|
|
282
|
+
if (name && name in Icons) {
|
|
283
|
+
return Icons[name];
|
|
284
|
+
}
|
|
285
|
+
return Icons.LayoutTemplate;
|
|
286
|
+
}
|
|
287
|
+
function BlockPicker({
|
|
288
|
+
blocks,
|
|
289
|
+
onPick,
|
|
290
|
+
onClose
|
|
291
|
+
}) {
|
|
292
|
+
return /* @__PURE__ */ jsx(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",
|
|
296
|
+
onClick: onClose,
|
|
297
|
+
children: /* @__PURE__ */ jsxs(
|
|
298
|
+
"div",
|
|
299
|
+
{
|
|
300
|
+
className: "flex max-h-[80vh] w-full max-w-lg flex-col overflow-hidden rounded-lg border border-border bg-background shadow-xl",
|
|
301
|
+
onClick: (e) => e.stopPropagation(),
|
|
302
|
+
children: [
|
|
303
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
|
|
304
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Add a block" }),
|
|
305
|
+
/* @__PURE__ */ jsx(
|
|
306
|
+
"button",
|
|
307
|
+
{
|
|
308
|
+
type: "button",
|
|
309
|
+
"aria-label": "Close",
|
|
310
|
+
className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
311
|
+
onClick: onClose,
|
|
312
|
+
children: /* @__PURE__ */ jsx(Icons.X, { className: "h-4 w-4" })
|
|
313
|
+
}
|
|
314
|
+
)
|
|
315
|
+
] }),
|
|
316
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 overflow-y-auto p-4 sm:grid-cols-3", children: blocks.map((block) => {
|
|
317
|
+
const Icon = resolveIcon(block.metadata.icon);
|
|
318
|
+
return /* @__PURE__ */ jsxs(
|
|
319
|
+
"button",
|
|
320
|
+
{
|
|
321
|
+
type: "button",
|
|
322
|
+
className: "flex flex-col items-center gap-2 rounded-md border border-border p-4 text-center transition-colors hover:border-primary hover:bg-muted",
|
|
323
|
+
onClick: () => onPick(block.metadata.name),
|
|
324
|
+
children: [
|
|
325
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-6 w-6 text-foreground" }),
|
|
326
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: block.metadata.displayName })
|
|
327
|
+
]
|
|
328
|
+
},
|
|
329
|
+
block.metadata.name
|
|
330
|
+
);
|
|
331
|
+
}) })
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
function PanelHeader({
|
|
339
|
+
title,
|
|
340
|
+
onClose,
|
|
341
|
+
eyebrow = "Settings",
|
|
342
|
+
description,
|
|
343
|
+
icon: Icon = Layers3
|
|
344
|
+
}) {
|
|
345
|
+
return /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 border-b border-border/80 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 px-5 py-4", children: [
|
|
346
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-primary/15 bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4", "aria-hidden": "true" }) }),
|
|
347
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
348
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground", children: eyebrow }),
|
|
349
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-1 truncate text-base font-semibold tracking-tight text-foreground", children: title }),
|
|
350
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 text-xs leading-5 text-muted-foreground", children: description })
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ jsx(
|
|
353
|
+
Button,
|
|
354
|
+
{
|
|
355
|
+
variant: "ghost",
|
|
356
|
+
size: "icon",
|
|
357
|
+
className: "-mr-2 -mt-1 h-8 w-8 shrink-0 rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
358
|
+
"aria-label": `Close ${title} settings`,
|
|
359
|
+
onClick: onClose,
|
|
360
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
] }) });
|
|
364
|
+
}
|
|
365
|
+
function SettingsPanel({
|
|
366
|
+
title,
|
|
367
|
+
onClose,
|
|
368
|
+
children,
|
|
369
|
+
eyebrow,
|
|
370
|
+
description,
|
|
371
|
+
icon
|
|
372
|
+
}) {
|
|
373
|
+
return /* @__PURE__ */ jsxs("aside", { className: "flex h-full min-h-0 w-[min(360px,calc(100vw-2rem))] max-w-full shrink-0 flex-col overflow-hidden rounded-2xl border border-border/80 bg-background shadow-lg shadow-foreground/5", children: [
|
|
374
|
+
/* @__PURE__ */ jsx(
|
|
375
|
+
PanelHeader,
|
|
376
|
+
{
|
|
377
|
+
title,
|
|
378
|
+
onClose,
|
|
379
|
+
eyebrow,
|
|
380
|
+
description,
|
|
381
|
+
icon
|
|
382
|
+
}
|
|
383
|
+
),
|
|
384
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 min-w-0 flex-1 overflow-x-hidden overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "min-w-0 px-5 py-5", children }) })
|
|
385
|
+
] });
|
|
386
|
+
}
|
|
387
|
+
function PageBuilderPropertyHeader({
|
|
388
|
+
label,
|
|
389
|
+
tooltip
|
|
390
|
+
}) {
|
|
391
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex grow items-center gap-1.5", children: [
|
|
392
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xs font-semibold text-foreground", children: label }),
|
|
393
|
+
tooltip && /* @__PURE__ */ jsx("span", { title: tooltip, className: "inline-flex cursor-help", children: /* @__PURE__ */ jsx(HelpCircle, { className: "h-3.5 w-3.5 text-muted-foreground" }) })
|
|
394
|
+
] });
|
|
395
|
+
}
|
|
396
|
+
function PageBuilderSlider({
|
|
397
|
+
title,
|
|
398
|
+
min,
|
|
399
|
+
max,
|
|
400
|
+
value,
|
|
401
|
+
onChange,
|
|
402
|
+
tooltip,
|
|
403
|
+
className,
|
|
404
|
+
allowsReset = false,
|
|
405
|
+
unit = "px"
|
|
406
|
+
}) {
|
|
407
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2.5", className), children: [
|
|
408
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
409
|
+
/* @__PURE__ */ jsx(PageBuilderPropertyHeader, { label: title, tooltip }),
|
|
410
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
411
|
+
/* @__PURE__ */ jsx(
|
|
412
|
+
"input",
|
|
413
|
+
{
|
|
414
|
+
type: "number",
|
|
415
|
+
inputMode: "numeric",
|
|
416
|
+
min,
|
|
417
|
+
max,
|
|
418
|
+
value: Number.isFinite(value) ? value : 0,
|
|
419
|
+
onChange: (e) => {
|
|
420
|
+
const parsed = Number(e.currentTarget.value);
|
|
421
|
+
if (Number.isNaN(parsed)) return;
|
|
422
|
+
onChange(Math.min(Math.max(parsed, min), max));
|
|
423
|
+
},
|
|
424
|
+
className: "h-7 w-12 rounded-md border border-input bg-muted/20 px-1.5 py-1 text-center text-xs font-semibold text-foreground outline-none transition-colors focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20"
|
|
425
|
+
}
|
|
426
|
+
),
|
|
427
|
+
unit && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-muted-foreground", children: unit }),
|
|
428
|
+
allowsReset && /* @__PURE__ */ jsx(
|
|
429
|
+
"button",
|
|
430
|
+
{
|
|
431
|
+
type: "button",
|
|
432
|
+
title: "Reset",
|
|
433
|
+
className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
434
|
+
onClick: (e) => {
|
|
435
|
+
e.preventDefault();
|
|
436
|
+
onChange();
|
|
437
|
+
},
|
|
438
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-muted-foreground hover:text-foreground" })
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
] })
|
|
442
|
+
] }),
|
|
443
|
+
/* @__PURE__ */ jsx(
|
|
444
|
+
"input",
|
|
445
|
+
{
|
|
446
|
+
type: "range",
|
|
447
|
+
min,
|
|
448
|
+
max,
|
|
449
|
+
step: 1,
|
|
450
|
+
value: Number.isFinite(value) ? value : min,
|
|
451
|
+
onChange: (e) => onChange(Number(e.currentTarget.value)),
|
|
452
|
+
"aria-label": title,
|
|
453
|
+
className: "h-1.5 w-full cursor-pointer accent-primary"
|
|
454
|
+
}
|
|
455
|
+
)
|
|
456
|
+
] });
|
|
457
|
+
}
|
|
458
|
+
function ColorSelector({
|
|
459
|
+
title,
|
|
460
|
+
value,
|
|
461
|
+
onChange,
|
|
462
|
+
tooltip,
|
|
463
|
+
className,
|
|
464
|
+
allowReset = true,
|
|
465
|
+
description
|
|
466
|
+
}) {
|
|
467
|
+
const [localValue, setLocalValue] = React2.useState(value);
|
|
468
|
+
React2.useEffect(() => {
|
|
469
|
+
setLocalValue(value);
|
|
470
|
+
}, [value]);
|
|
471
|
+
const handleChange = (newValue) => {
|
|
472
|
+
setLocalValue(newValue);
|
|
473
|
+
onChange(newValue);
|
|
474
|
+
};
|
|
475
|
+
const swatchValue = /^#[0-9a-fA-F]{6}$/.test(localValue) ? localValue : "#000000";
|
|
476
|
+
return /* @__PURE__ */ jsxs(
|
|
477
|
+
"div",
|
|
478
|
+
{
|
|
479
|
+
className: cn(
|
|
480
|
+
"flex min-w-0 items-center justify-between gap-2",
|
|
481
|
+
className
|
|
482
|
+
),
|
|
483
|
+
children: [
|
|
484
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
|
|
485
|
+
/* @__PURE__ */ jsx(PageBuilderPropertyHeader, { label: title, tooltip }),
|
|
486
|
+
description && /* @__PURE__ */ jsx("p", { className: "break-words text-xs text-muted-foreground", children: description })
|
|
487
|
+
] }),
|
|
488
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
|
|
489
|
+
/* @__PURE__ */ jsx(
|
|
490
|
+
"input",
|
|
491
|
+
{
|
|
492
|
+
type: "color",
|
|
493
|
+
value: swatchValue,
|
|
494
|
+
onChange: (e) => handleChange(e.currentTarget.value),
|
|
495
|
+
className: "h-7 w-9 shrink-0 cursor-pointer rounded-md border border-input bg-background p-0.5",
|
|
496
|
+
"aria-label": `${title} swatch`
|
|
497
|
+
}
|
|
498
|
+
),
|
|
499
|
+
/* @__PURE__ */ jsx(
|
|
500
|
+
"input",
|
|
501
|
+
{
|
|
502
|
+
type: "text",
|
|
503
|
+
value: localValue ?? "",
|
|
504
|
+
placeholder: "#000000",
|
|
505
|
+
onChange: (e) => handleChange(e.currentTarget.value),
|
|
506
|
+
className: "h-7 w-20 min-w-0 rounded-md border border-input bg-background px-2 py-1 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring/20",
|
|
507
|
+
"aria-label": `${title} hex value`
|
|
508
|
+
}
|
|
509
|
+
),
|
|
510
|
+
allowReset && /* @__PURE__ */ jsx(
|
|
511
|
+
"button",
|
|
512
|
+
{
|
|
513
|
+
type: "button",
|
|
514
|
+
title: "Reset",
|
|
515
|
+
className: "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-md hover:bg-muted/50",
|
|
516
|
+
onClick: (e) => {
|
|
517
|
+
e.preventDefault();
|
|
518
|
+
setLocalValue("");
|
|
519
|
+
onChange();
|
|
520
|
+
},
|
|
521
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-muted-foreground hover:text-foreground" })
|
|
522
|
+
}
|
|
523
|
+
)
|
|
524
|
+
] })
|
|
525
|
+
]
|
|
526
|
+
}
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
var MAX_WIDTH_MAP = {
|
|
530
|
+
1: "max-w-2xl",
|
|
531
|
+
2: "max-w-3xl",
|
|
532
|
+
3: "max-w-4xl",
|
|
533
|
+
4: "max-w-5xl",
|
|
534
|
+
5: "max-w-6xl"
|
|
535
|
+
};
|
|
536
|
+
function getMaxWidthTWClass(value) {
|
|
537
|
+
const roundedValue = Math.max(1, Math.min(5, Math.round(value)));
|
|
538
|
+
return MAX_WIDTH_MAP[roundedValue] || MAX_WIDTH_MAP[5];
|
|
539
|
+
}
|
|
540
|
+
function MaxWidthSelector({
|
|
541
|
+
value,
|
|
542
|
+
onChange,
|
|
543
|
+
className = "",
|
|
544
|
+
themeDefault
|
|
545
|
+
}) {
|
|
546
|
+
const numericValue = Object.entries(MAX_WIDTH_MAP).find(
|
|
547
|
+
([_, v]) => v === (value ?? themeDefault)
|
|
548
|
+
)?.[0] || "5";
|
|
549
|
+
const handleChange = (newValue) => {
|
|
550
|
+
if (newValue === void 0) {
|
|
551
|
+
onChange();
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
onChange(getMaxWidthTWClass(newValue));
|
|
555
|
+
};
|
|
556
|
+
return /* @__PURE__ */ jsx(
|
|
557
|
+
PageBuilderSlider,
|
|
558
|
+
{
|
|
559
|
+
title: "Maximum width",
|
|
560
|
+
max: 5,
|
|
561
|
+
min: 1,
|
|
562
|
+
onChange: handleChange,
|
|
563
|
+
value: parseInt(numericValue),
|
|
564
|
+
tooltip: "Maximum width of the content",
|
|
565
|
+
className,
|
|
566
|
+
allowsReset: true,
|
|
567
|
+
unit: ""
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
var PADDING_MAP = {
|
|
572
|
+
0: "py-0",
|
|
573
|
+
1: "py-4",
|
|
574
|
+
2: "py-6",
|
|
575
|
+
3: "py-8",
|
|
576
|
+
4: "py-10",
|
|
577
|
+
5: "py-12",
|
|
578
|
+
6: "py-16",
|
|
579
|
+
7: "py-20",
|
|
580
|
+
8: "py-24",
|
|
581
|
+
9: "py-32",
|
|
582
|
+
10: "py-40",
|
|
583
|
+
11: "py-48",
|
|
584
|
+
12: "py-56",
|
|
585
|
+
13: "py-64"
|
|
586
|
+
};
|
|
587
|
+
function getVerticalPaddingTWClass(value) {
|
|
588
|
+
const roundedValue = Math.max(0, Math.min(13, Math.round(value)));
|
|
589
|
+
return PADDING_MAP[roundedValue] || PADDING_MAP[5];
|
|
590
|
+
}
|
|
591
|
+
function VerticalPaddingSelector({
|
|
592
|
+
value,
|
|
593
|
+
onChange,
|
|
594
|
+
className = "",
|
|
595
|
+
themeDefault
|
|
596
|
+
}) {
|
|
597
|
+
const numericValue = Object.entries(PADDING_MAP).find(
|
|
598
|
+
([_, v]) => v === (value ?? themeDefault)
|
|
599
|
+
)?.[0] || "6";
|
|
600
|
+
const handleChange = (newValue) => {
|
|
601
|
+
if (newValue === void 0) {
|
|
602
|
+
onChange();
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
onChange(getVerticalPaddingTWClass(newValue));
|
|
606
|
+
};
|
|
607
|
+
return /* @__PURE__ */ jsx(
|
|
608
|
+
PageBuilderSlider,
|
|
609
|
+
{
|
|
610
|
+
title: "Vertical padding",
|
|
611
|
+
max: 13,
|
|
612
|
+
min: 0,
|
|
613
|
+
onChange: handleChange,
|
|
614
|
+
value: parseInt(numericValue),
|
|
615
|
+
tooltip: "Vertical padding of the section",
|
|
616
|
+
className,
|
|
617
|
+
unit: ""
|
|
618
|
+
}
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
function ContentPaddingSelector({
|
|
622
|
+
value,
|
|
623
|
+
onChange,
|
|
624
|
+
className = "",
|
|
625
|
+
variant = "horizontal",
|
|
626
|
+
min,
|
|
627
|
+
max
|
|
628
|
+
}) {
|
|
629
|
+
const tooltip = variant === "horizontal" ? "Width of the actual content. It affects only desktop class displays" : "Space on the top and bottom of the content. It affects only desktop class displays";
|
|
630
|
+
const defaultMax = variant === "horizontal" ? 100 : 200;
|
|
631
|
+
const defaultMin = variant === "horizontal" ? 10 : 16;
|
|
632
|
+
return /* @__PURE__ */ jsx(
|
|
633
|
+
PageBuilderSlider,
|
|
634
|
+
{
|
|
635
|
+
title: variant === "horizontal" ? "Content width" : "Vertical padding",
|
|
636
|
+
max: max || defaultMax,
|
|
637
|
+
min: min || defaultMin,
|
|
638
|
+
onChange,
|
|
639
|
+
value,
|
|
640
|
+
tooltip,
|
|
641
|
+
className
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// src/builder/controls/pattern-presets.ts
|
|
647
|
+
function svgToDataUri(svg) {
|
|
648
|
+
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
649
|
+
}
|
|
650
|
+
var light = {
|
|
651
|
+
soft: "rgba(0,0,0,0.06)",
|
|
652
|
+
mid: "rgba(0,0,0,0.09)",
|
|
653
|
+
firm: "rgba(0,0,0,0.12)"};
|
|
654
|
+
var dark = {
|
|
655
|
+
soft: "rgba(255,255,255,0.06)",
|
|
656
|
+
mid: "rgba(255,255,255,0.09)",
|
|
657
|
+
firm: "rgba(255,255,255,0.12)"};
|
|
658
|
+
function dotsSvg(fill, size = 16, r = 1.35) {
|
|
659
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><circle cx="${size / 4}" cy="${size / 4}" r="${r}" fill="${fill}"/></svg>`;
|
|
660
|
+
}
|
|
661
|
+
function gridSvg(stroke, size = 32) {
|
|
662
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${size} 0H0V${size}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
663
|
+
}
|
|
664
|
+
function diagonalSvg(stroke, size = 24) {
|
|
665
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size} ${size} 0M-${size / 4} ${size / 4} ${size / 4}-${size / 4}M${size * 0.75} ${size * 1.25} ${size * 1.25} ${size * 0.75}" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
666
|
+
}
|
|
667
|
+
function contourSvg(stroke) {
|
|
668
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="300" height="150"><g fill="none" stroke="${stroke}" stroke-width="1">
|
|
669
|
+
<path d="M0,15 Q37.5,-5 75,15 T150,15 T225,15 T300,15"/>
|
|
670
|
+
<path d="M0,45 Q37.5,25 75,45 T150,45 T225,45 T300,45"/>
|
|
671
|
+
<path d="M0,75 Q37.5,55 75,75 T150,75 T225,75 T300,75"/>
|
|
672
|
+
<path d="M0,105 Q37.5,85 75,105 T150,105 T225,105 T300,105"/>
|
|
673
|
+
<path d="M0,135 Q37.5,115 75,135 T150,135 T225,135 T300,135"/>
|
|
674
|
+
</g></svg>`;
|
|
675
|
+
}
|
|
676
|
+
function plusSvg(stroke, size = 24) {
|
|
677
|
+
const mid = size / 2;
|
|
678
|
+
const arm = size * 0.22;
|
|
679
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${mid} ${mid - arm}v${arm * 2}M${mid - arm} ${mid}h${arm * 2}" fill="none" stroke="${stroke}" stroke-width="1.25" stroke-linecap="round"/></svg>`;
|
|
680
|
+
}
|
|
681
|
+
function graphPaperSvg(minor, major, size = 80) {
|
|
682
|
+
const step = size / 4;
|
|
683
|
+
const lines = [];
|
|
684
|
+
for (let i = 1; i < 4; i++) {
|
|
685
|
+
lines.push(`M${i * step} 0V${size}`, `M0 ${i * step}H${size}`);
|
|
686
|
+
}
|
|
687
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
|
|
688
|
+
<path d="${lines.join("")}" fill="none" stroke="${minor}" stroke-width="1"/>
|
|
689
|
+
<path d="M${size} 0H0V${size}" fill="none" stroke="${major}" stroke-width="1"/>
|
|
690
|
+
</svg>`;
|
|
691
|
+
}
|
|
692
|
+
function hexSvg(stroke, size = 56) {
|
|
693
|
+
const w = size;
|
|
694
|
+
const h = Math.round(size * 0.866);
|
|
695
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}"><path d="M${w * 0.25} 0 L${w * 0.75} 0 L${w} ${h / 2} L${w * 0.75} ${h} L${w * 0.25} ${h} L0 ${h / 2} Z" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
696
|
+
}
|
|
697
|
+
function chevronSvg(stroke, size = 28) {
|
|
698
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size * 0.35} L${size / 2} ${size * 0.65} L${size} ${size * 0.35}" fill="none" stroke="${stroke}" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
699
|
+
}
|
|
700
|
+
function verticalLinesSvg(stroke, size = 12) {
|
|
701
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M1 0V${size}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
702
|
+
}
|
|
703
|
+
function horizontalLinesSvg(stroke, size = 14) {
|
|
704
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 1H${size}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
705
|
+
}
|
|
706
|
+
function crosshatchSvg(stroke, size = 20) {
|
|
707
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size} ${size} 0M-${size / 4} ${size / 4} ${size / 4}-${size / 4}M${size * 0.75} ${size * 1.25} ${size * 1.25} ${size * 0.75}M0 0 ${size} ${size}M-${size / 4} ${size * 0.75} ${size / 4} ${size * 1.25}M${size * 0.75} ${-size / 4} ${size * 1.25} ${size / 4}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
708
|
+
}
|
|
709
|
+
function diamondSvg(stroke, size = 28) {
|
|
710
|
+
const m = size / 2;
|
|
711
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${m} 1 L${size - 1} ${m} L${m} ${size - 1} L1 ${m} Z" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
712
|
+
}
|
|
713
|
+
function wavesSvg(stroke) {
|
|
714
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="40"><path d="M0 20 Q15 0 30 20 T60 20 T90 20 T120 20" fill="none" stroke="${stroke}" stroke-width="1.25"/></svg>`;
|
|
715
|
+
}
|
|
716
|
+
function concentricSvg(stroke, size = 64) {
|
|
717
|
+
const c = size / 2;
|
|
718
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
|
|
719
|
+
<circle cx="${c}" cy="${c}" r="${size * 0.18}" fill="none" stroke="${stroke}" stroke-width="1"/>
|
|
720
|
+
<circle cx="${c}" cy="${c}" r="${size * 0.32}" fill="none" stroke="${stroke}" stroke-width="1"/>
|
|
721
|
+
<circle cx="${c}" cy="${c}" r="${size * 0.46}" fill="none" stroke="${stroke}" stroke-width="1"/>
|
|
722
|
+
</svg>`;
|
|
723
|
+
}
|
|
724
|
+
function isometricSvg(stroke, size = 48) {
|
|
725
|
+
const s = size;
|
|
726
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${s}" height="${s * 0.866}">
|
|
727
|
+
<path d="M${s / 2} 0 L${s} ${s * 0.2165} L${s / 2} ${s * 0.433} L0 ${s * 0.2165} Z M${s / 2} ${s * 0.433} V${s * 0.866} M0 ${s * 0.2165} V${s * 0.65} L${s / 2} ${s * 0.866} L${s} ${s * 0.65} V${s * 0.2165}" fill="none" stroke="${stroke}" stroke-width="1"/>
|
|
728
|
+
</svg>`;
|
|
729
|
+
}
|
|
730
|
+
function trianglesSvg(stroke, size = 36) {
|
|
731
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${Math.round(size * 0.866)}"><path d="M${size / 2} 1 L${size - 1} ${size * 0.866 - 1} L1 ${size * 0.866 - 1} Z" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
732
|
+
}
|
|
733
|
+
function scatterDotsSvg(fill, size = 48) {
|
|
734
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
|
|
735
|
+
<circle cx="8" cy="10" r="1.2" fill="${fill}"/>
|
|
736
|
+
<circle cx="22" cy="6" r="0.9" fill="${fill}"/>
|
|
737
|
+
<circle cx="38" cy="14" r="1.5" fill="${fill}"/>
|
|
738
|
+
<circle cx="14" cy="28" r="1" fill="${fill}"/>
|
|
739
|
+
<circle cx="30" cy="24" r="0.8" fill="${fill}"/>
|
|
740
|
+
<circle cx="42" cy="36" r="1.1" fill="${fill}"/>
|
|
741
|
+
<circle cx="6" cy="40" r="0.95" fill="${fill}"/>
|
|
742
|
+
<circle cx="24" cy="42" r="1.3" fill="${fill}"/>
|
|
743
|
+
</svg>`;
|
|
744
|
+
}
|
|
745
|
+
function brickSvg(stroke, w = 40, h = 20) {
|
|
746
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h * 2}">
|
|
747
|
+
<path d="M0 0H${w}V${h}H0Z M0 ${h}H${w / 2}V${h * 2}H0Z M${w / 2} ${h}H${w}V${h * 2}H${w / 2}Z" fill="none" stroke="${stroke}" stroke-width="1"/>
|
|
748
|
+
</svg>`;
|
|
749
|
+
}
|
|
750
|
+
function scalesSvg(stroke, size = 32) {
|
|
751
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size * 0.6}"><path d="M0 ${size * 0.6} A${size / 2} ${size * 0.55} 0 0 1 ${size} ${size * 0.6}" fill="none" stroke="${stroke}" stroke-width="1"/></svg>`;
|
|
752
|
+
}
|
|
753
|
+
function dashedGridSvg(stroke, size = 28) {
|
|
754
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M${size} 0H0V${size}" fill="none" stroke="${stroke}" stroke-width="1" stroke-dasharray="3 4"/></svg>`;
|
|
755
|
+
}
|
|
756
|
+
function circuitSvg(stroke, size = 64) {
|
|
757
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" fill="none" stroke="${stroke}" stroke-width="1" stroke-linecap="round">
|
|
758
|
+
<path d="M8 8H28V28H8Z"/>
|
|
759
|
+
<path d="M36 12H52V20H36Z"/>
|
|
760
|
+
<path d="M12 36H20V52H12Z"/>
|
|
761
|
+
<path d="M28 8V4M8 16H4M28 20H36M20 28V36M44 20V36H36"/>
|
|
762
|
+
<circle cx="36" cy="36" r="3"/>
|
|
763
|
+
<circle cx="52" cy="44" r="2.5"/>
|
|
764
|
+
<path d="M39 36H49M52 41.5V36"/>
|
|
765
|
+
</svg>`;
|
|
766
|
+
}
|
|
767
|
+
function zigZagSvg(stroke, size = 24) {
|
|
768
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}"><path d="M0 ${size / 2} L${size / 4} ${size * 0.2} L${size / 2} ${size / 2} L${size * 0.75} ${size * 0.8} L${size} ${size / 2}" fill="none" stroke="${stroke}" stroke-width="1.25" stroke-linejoin="round"/></svg>`;
|
|
769
|
+
}
|
|
770
|
+
function svgPattern(id, label, lightSvg, darkSvg, size, description, extras) {
|
|
771
|
+
return {
|
|
772
|
+
id,
|
|
773
|
+
label,
|
|
774
|
+
category: "tile",
|
|
775
|
+
description,
|
|
776
|
+
apply: {
|
|
777
|
+
backgroundImage: svgToDataUri(lightSvg),
|
|
778
|
+
backgroundImageDark: svgToDataUri(darkSvg),
|
|
779
|
+
gradientBackgroundSize: size,
|
|
780
|
+
gradientBackgroundRepeat: "repeat",
|
|
781
|
+
gradientBackgroundPosition: "0 0",
|
|
782
|
+
gradientMaskImage: void 0,
|
|
783
|
+
...extras
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function cssPattern(id, label, lightCss, darkCss, description, extras) {
|
|
788
|
+
return {
|
|
789
|
+
id,
|
|
790
|
+
label,
|
|
791
|
+
category: "glow",
|
|
792
|
+
description,
|
|
793
|
+
apply: {
|
|
794
|
+
backgroundImage: lightCss,
|
|
795
|
+
backgroundImageDark: darkCss,
|
|
796
|
+
gradientBackgroundSize: "100% 100%",
|
|
797
|
+
gradientBackgroundRepeat: "no-repeat",
|
|
798
|
+
gradientBackgroundPosition: "center",
|
|
799
|
+
gradientMaskImage: void 0,
|
|
800
|
+
...extras
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
var PATTERN_PRESETS = [
|
|
805
|
+
// ── Classic tiles ────────────────────────────────────────────────────
|
|
806
|
+
svgPattern(
|
|
807
|
+
"dots",
|
|
808
|
+
"Dots",
|
|
809
|
+
dotsSvg(light.firm),
|
|
810
|
+
dotsSvg(dark.firm),
|
|
811
|
+
"16px 16px",
|
|
812
|
+
"Subtle dot grid \u2014 the default SaaS texture"
|
|
813
|
+
),
|
|
814
|
+
svgPattern(
|
|
815
|
+
"dots-fine",
|
|
816
|
+
"Fine dots",
|
|
817
|
+
dotsSvg(light.mid, 10, 0.9),
|
|
818
|
+
dotsSvg(dark.mid, 10, 0.9),
|
|
819
|
+
"10px 10px",
|
|
820
|
+
"Tighter, quieter polka texture"
|
|
821
|
+
),
|
|
822
|
+
svgPattern(
|
|
823
|
+
"scatter",
|
|
824
|
+
"Scatter",
|
|
825
|
+
scatterDotsSvg(light.firm),
|
|
826
|
+
scatterDotsSvg(dark.firm),
|
|
827
|
+
"48px 48px",
|
|
828
|
+
"Organic, uneven speckles"
|
|
829
|
+
),
|
|
830
|
+
svgPattern(
|
|
831
|
+
"grid",
|
|
832
|
+
"Grid",
|
|
833
|
+
gridSvg(light.mid),
|
|
834
|
+
gridSvg(dark.mid),
|
|
835
|
+
"32px 32px",
|
|
836
|
+
"Clean square lattice"
|
|
837
|
+
),
|
|
838
|
+
svgPattern(
|
|
839
|
+
"graph-paper",
|
|
840
|
+
"Graph paper",
|
|
841
|
+
graphPaperSvg(light.soft, light.firm),
|
|
842
|
+
graphPaperSvg(dark.soft, dark.firm),
|
|
843
|
+
"80px 80px",
|
|
844
|
+
"Notebook-style minor + major grid"
|
|
845
|
+
),
|
|
846
|
+
svgPattern(
|
|
847
|
+
"dashed-grid",
|
|
848
|
+
"Dashed grid",
|
|
849
|
+
dashedGridSvg(light.mid),
|
|
850
|
+
dashedGridSvg(dark.mid),
|
|
851
|
+
"28px 28px",
|
|
852
|
+
"Soft technical grid with dashed strokes"
|
|
853
|
+
),
|
|
854
|
+
svgPattern(
|
|
855
|
+
"diagonal",
|
|
856
|
+
"Diagonal lines",
|
|
857
|
+
diagonalSvg(light.mid),
|
|
858
|
+
diagonalSvg(dark.mid),
|
|
859
|
+
"24px 24px",
|
|
860
|
+
"Classic hatch at 45\xB0"
|
|
861
|
+
),
|
|
862
|
+
svgPattern(
|
|
863
|
+
"crosshatch",
|
|
864
|
+
"Crosshatch",
|
|
865
|
+
crosshatchSvg(light.soft),
|
|
866
|
+
crosshatchSvg(dark.soft),
|
|
867
|
+
"20px 20px",
|
|
868
|
+
"Crossed diagonals for richer grain"
|
|
869
|
+
),
|
|
870
|
+
svgPattern(
|
|
871
|
+
"plus",
|
|
872
|
+
"Plus marks",
|
|
873
|
+
plusSvg(light.firm),
|
|
874
|
+
plusSvg(dark.firm),
|
|
875
|
+
"24px 24px",
|
|
876
|
+
"Cross / plus marks \u2014 popular on product pages"
|
|
877
|
+
),
|
|
878
|
+
svgPattern(
|
|
879
|
+
"vertical",
|
|
880
|
+
"Pinstripe",
|
|
881
|
+
verticalLinesSvg(light.mid),
|
|
882
|
+
verticalLinesSvg(dark.mid),
|
|
883
|
+
"12px 12px",
|
|
884
|
+
"Editorial vertical lines"
|
|
885
|
+
),
|
|
886
|
+
svgPattern(
|
|
887
|
+
"horizontal",
|
|
888
|
+
"Ruled lines",
|
|
889
|
+
horizontalLinesSvg(light.mid),
|
|
890
|
+
horizontalLinesSvg(dark.mid),
|
|
891
|
+
"14px 14px",
|
|
892
|
+
"Horizontal notebook rules"
|
|
893
|
+
),
|
|
894
|
+
svgPattern(
|
|
895
|
+
"zigzag",
|
|
896
|
+
"Zigzag",
|
|
897
|
+
zigZagSvg(light.mid),
|
|
898
|
+
zigZagSvg(dark.mid),
|
|
899
|
+
"24px 24px",
|
|
900
|
+
"Continuous zigzag band"
|
|
901
|
+
),
|
|
902
|
+
svgPattern(
|
|
903
|
+
"chevron",
|
|
904
|
+
"Chevron",
|
|
905
|
+
chevronSvg(light.mid),
|
|
906
|
+
chevronSvg(dark.mid),
|
|
907
|
+
"28px 28px",
|
|
908
|
+
"Stacked chevrons for motion"
|
|
909
|
+
),
|
|
910
|
+
svgPattern(
|
|
911
|
+
"diamond",
|
|
912
|
+
"Diamonds",
|
|
913
|
+
diamondSvg(light.mid),
|
|
914
|
+
diamondSvg(dark.mid),
|
|
915
|
+
"28px 28px",
|
|
916
|
+
"Geometric diamond lattice"
|
|
917
|
+
),
|
|
918
|
+
svgPattern(
|
|
919
|
+
"triangles",
|
|
920
|
+
"Triangles",
|
|
921
|
+
trianglesSvg(light.mid),
|
|
922
|
+
trianglesSvg(dark.mid),
|
|
923
|
+
"36px 31px",
|
|
924
|
+
"Tessellated triangle outlines"
|
|
925
|
+
),
|
|
926
|
+
svgPattern(
|
|
927
|
+
"hexagon",
|
|
928
|
+
"Hexagons",
|
|
929
|
+
hexSvg(light.mid),
|
|
930
|
+
hexSvg(dark.mid),
|
|
931
|
+
"56px 48px",
|
|
932
|
+
"Honeycomb / tech hex grid"
|
|
933
|
+
),
|
|
934
|
+
svgPattern(
|
|
935
|
+
"isometric",
|
|
936
|
+
"Isometric",
|
|
937
|
+
isometricSvg(light.mid),
|
|
938
|
+
isometricSvg(dark.mid),
|
|
939
|
+
"48px 42px",
|
|
940
|
+
"3D cube lattice"
|
|
941
|
+
),
|
|
942
|
+
svgPattern(
|
|
943
|
+
"waves",
|
|
944
|
+
"Waves",
|
|
945
|
+
wavesSvg(light.mid),
|
|
946
|
+
wavesSvg(dark.mid),
|
|
947
|
+
"120px 40px",
|
|
948
|
+
"Gentle repeating sine waves"
|
|
949
|
+
),
|
|
950
|
+
svgPattern(
|
|
951
|
+
"contour",
|
|
952
|
+
"Contour",
|
|
953
|
+
contourSvg(light.soft),
|
|
954
|
+
contourSvg(dark.soft),
|
|
955
|
+
"300px 150px",
|
|
956
|
+
"Topographic contour strokes"
|
|
957
|
+
),
|
|
958
|
+
svgPattern(
|
|
959
|
+
"concentric",
|
|
960
|
+
"Ripples",
|
|
961
|
+
concentricSvg(light.soft),
|
|
962
|
+
concentricSvg(dark.soft),
|
|
963
|
+
"64px 64px",
|
|
964
|
+
"Concentric circle tiles"
|
|
965
|
+
),
|
|
966
|
+
svgPattern(
|
|
967
|
+
"scales",
|
|
968
|
+
"Scales",
|
|
969
|
+
scalesSvg(light.mid),
|
|
970
|
+
scalesSvg(dark.mid),
|
|
971
|
+
"32px 19px",
|
|
972
|
+
"Fish-scale / arc tile"
|
|
973
|
+
),
|
|
974
|
+
svgPattern(
|
|
975
|
+
"brick",
|
|
976
|
+
"Brick",
|
|
977
|
+
brickSvg(light.mid),
|
|
978
|
+
brickSvg(dark.mid),
|
|
979
|
+
"40px 40px",
|
|
980
|
+
"Offset brick running bond"
|
|
981
|
+
),
|
|
982
|
+
svgPattern(
|
|
983
|
+
"circuit",
|
|
984
|
+
"Circuit",
|
|
985
|
+
circuitSvg(light.mid),
|
|
986
|
+
circuitSvg(dark.mid),
|
|
987
|
+
"64px 64px",
|
|
988
|
+
"Tech / PCB-inspired traces"
|
|
989
|
+
),
|
|
990
|
+
// ── Full-bleed CSS (modern hero / marketing) ─────────────────────────
|
|
991
|
+
cssPattern(
|
|
992
|
+
"soft-mesh",
|
|
993
|
+
"Soft mesh",
|
|
994
|
+
`radial-gradient(at 20% 20%, rgba(99,102,241,0.18) 0px, transparent 50%), radial-gradient(at 80% 10%, rgba(236,72,153,0.14) 0px, transparent 45%), radial-gradient(at 50% 80%, rgba(34,211,238,0.12) 0px, transparent 50%), radial-gradient(at 90% 70%, rgba(250,204,21,0.1) 0px, transparent 40%)`,
|
|
995
|
+
`radial-gradient(at 20% 20%, rgba(99,102,241,0.28) 0px, transparent 50%), radial-gradient(at 80% 10%, rgba(236,72,153,0.22) 0px, transparent 45%), radial-gradient(at 50% 80%, rgba(34,211,238,0.18) 0px, transparent 50%), radial-gradient(at 90% 70%, rgba(250,204,21,0.12) 0px, transparent 40%)`,
|
|
996
|
+
"Colorful soft blobs \u2014 modern marketing hero"
|
|
997
|
+
),
|
|
998
|
+
cssPattern(
|
|
999
|
+
"aurora",
|
|
1000
|
+
"Aurora",
|
|
1001
|
+
`linear-gradient(135deg, rgba(56,189,248,0.2), transparent 50%), linear-gradient(225deg, rgba(167,139,250,0.22), transparent 50%), linear-gradient(45deg, rgba(52,211,153,0.15), transparent 40%)`,
|
|
1002
|
+
`linear-gradient(135deg, rgba(56,189,248,0.28), transparent 50%), linear-gradient(225deg, rgba(167,139,250,0.3), transparent 50%), linear-gradient(45deg, rgba(52,211,153,0.2), transparent 40%)`,
|
|
1003
|
+
"Layered aurora bands"
|
|
1004
|
+
),
|
|
1005
|
+
cssPattern(
|
|
1006
|
+
"spotlight",
|
|
1007
|
+
"Spotlight",
|
|
1008
|
+
`radial-gradient(ellipse 80% 60% at 50% -10%, rgba(99,102,241,0.22), transparent 55%)`,
|
|
1009
|
+
`radial-gradient(ellipse 80% 60% at 50% -10%, rgba(129,140,248,0.35), transparent 55%)`,
|
|
1010
|
+
"Top-centered spotlight glow"
|
|
1011
|
+
),
|
|
1012
|
+
cssPattern(
|
|
1013
|
+
"bottom-glow",
|
|
1014
|
+
"Bottom glow",
|
|
1015
|
+
`radial-gradient(ellipse 70% 50% at 50% 110%, rgba(14,165,233,0.2), transparent 55%)`,
|
|
1016
|
+
`radial-gradient(ellipse 70% 50% at 50% 110%, rgba(56,189,248,0.28), transparent 55%)`,
|
|
1017
|
+
"Soft light rising from the bottom edge"
|
|
1018
|
+
),
|
|
1019
|
+
cssPattern(
|
|
1020
|
+
"corner-glow",
|
|
1021
|
+
"Corner glow",
|
|
1022
|
+
`radial-gradient(ellipse 60% 50% at 0% 0%, rgba(244,63,94,0.16), transparent 50%), radial-gradient(ellipse 50% 45% at 100% 100%, rgba(59,130,246,0.16), transparent 50%)`,
|
|
1023
|
+
`radial-gradient(ellipse 60% 50% at 0% 0%, rgba(251,113,133,0.25), transparent 50%), radial-gradient(ellipse 50% 45% at 100% 100%, rgba(96,165,250,0.25), transparent 50%)`,
|
|
1024
|
+
"Opposite-corner color accents"
|
|
1025
|
+
),
|
|
1026
|
+
cssPattern(
|
|
1027
|
+
"horizon",
|
|
1028
|
+
"Horizon fade",
|
|
1029
|
+
`linear-gradient(180deg, transparent 0%, rgba(15,23,42,0.04) 55%, rgba(15,23,42,0.08) 100%)`,
|
|
1030
|
+
`linear-gradient(180deg, transparent 0%, rgba(255,255,255,0.04) 55%, rgba(255,255,255,0.1) 100%)`,
|
|
1031
|
+
"Gentle vertical vignette toward the base"
|
|
1032
|
+
),
|
|
1033
|
+
cssPattern(
|
|
1034
|
+
"diagonal-fade",
|
|
1035
|
+
"Diagonal fade",
|
|
1036
|
+
`linear-gradient(135deg, rgba(99,102,241,0.12) 0%, transparent 45%, rgba(236,72,153,0.1) 100%)`,
|
|
1037
|
+
`linear-gradient(135deg, rgba(129,140,248,0.2) 0%, transparent 45%, rgba(244,114,182,0.16) 100%)`,
|
|
1038
|
+
"Two-tone diagonal wash"
|
|
1039
|
+
),
|
|
1040
|
+
svgPattern(
|
|
1041
|
+
"grid-fade",
|
|
1042
|
+
"Fading grid",
|
|
1043
|
+
gridSvg(light.firm, 40),
|
|
1044
|
+
gridSvg(dark.firm, 40),
|
|
1045
|
+
"40px 40px",
|
|
1046
|
+
"Grid that softens toward the edges",
|
|
1047
|
+
{
|
|
1048
|
+
gradientMaskImage: "radial-gradient(ellipse at center, black 25%, transparent 75%)"
|
|
1049
|
+
}
|
|
1050
|
+
)
|
|
1051
|
+
];
|
|
1052
|
+
var BG_TYPES = [
|
|
1053
|
+
{
|
|
1054
|
+
type: "color",
|
|
1055
|
+
label: "Color",
|
|
1056
|
+
description: "Solid fill",
|
|
1057
|
+
icon: Palette
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
type: "image",
|
|
1061
|
+
label: "Image",
|
|
1062
|
+
description: "Photo or asset",
|
|
1063
|
+
icon: ImageIcon
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
type: "gradient",
|
|
1067
|
+
label: "Pattern",
|
|
1068
|
+
description: "Texture or glow",
|
|
1069
|
+
icon: Sparkles
|
|
1070
|
+
}
|
|
1071
|
+
];
|
|
1072
|
+
var BLEND_MODES = [
|
|
1073
|
+
{ label: "Normal", value: "normal" },
|
|
1074
|
+
{ label: "Multiply", value: "multiply" },
|
|
1075
|
+
{ label: "Screen", value: "screen" },
|
|
1076
|
+
{ label: "Overlay", value: "overlay" },
|
|
1077
|
+
{ label: "Darken", value: "darken" },
|
|
1078
|
+
{ label: "Lighten", value: "lighten" },
|
|
1079
|
+
{ label: "Color Dodge", value: "color-dodge" },
|
|
1080
|
+
{ label: "Color Burn", value: "color-burn" },
|
|
1081
|
+
{ label: "Hard Light", value: "hard-light" },
|
|
1082
|
+
{ label: "Soft Light", value: "soft-light" },
|
|
1083
|
+
{ label: "Difference", value: "difference" },
|
|
1084
|
+
{ label: "Exclusion", value: "exclusion" },
|
|
1085
|
+
{ label: "Hue", value: "hue" },
|
|
1086
|
+
{ label: "Saturation", value: "saturation" },
|
|
1087
|
+
{ label: "Color", value: "color" },
|
|
1088
|
+
{ label: "Luminosity", value: "luminosity" }
|
|
1089
|
+
];
|
|
1090
|
+
var BACKGROUND_REPEAT_OPTIONS = [
|
|
1091
|
+
{ label: "No repeat", value: "no-repeat" },
|
|
1092
|
+
{ label: "Repeat", value: "repeat" },
|
|
1093
|
+
{ label: "Repeat X", value: "repeat-x" },
|
|
1094
|
+
{ label: "Repeat Y", value: "repeat-y" },
|
|
1095
|
+
{ label: "Space", value: "space" },
|
|
1096
|
+
{ label: "Round", value: "round" }
|
|
1097
|
+
];
|
|
1098
|
+
var PATTERN_FILTERS = [
|
|
1099
|
+
{ id: "all", label: "All" },
|
|
1100
|
+
{ id: "tile", label: "Tiles" },
|
|
1101
|
+
{ id: "glow", label: "Glows" }
|
|
1102
|
+
];
|
|
1103
|
+
var fieldLabelClass = "text-xs font-semibold tracking-tight text-foreground";
|
|
1104
|
+
var fieldHintClass = "text-[11px] leading-relaxed text-muted-foreground break-words";
|
|
1105
|
+
var controlClass = "box-border h-9 w-full min-w-0 max-w-full rounded-lg border border-input bg-muted/20 px-3 text-sm text-foreground outline-none transition-colors placeholder:text-muted-foreground/70 focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20";
|
|
1106
|
+
var textareaClass = "box-border min-h-[4.5rem] w-full min-w-0 max-w-full resize-y rounded-lg border border-input bg-muted/20 px-3 py-2 font-mono text-[11px] leading-relaxed text-foreground outline-none transition-colors placeholder:text-muted-foreground/70 focus:border-ring focus:bg-background focus:ring-2 focus:ring-ring/20 break-all";
|
|
1107
|
+
function FieldLabel({
|
|
1108
|
+
htmlFor,
|
|
1109
|
+
children,
|
|
1110
|
+
hint,
|
|
1111
|
+
trailing
|
|
1112
|
+
}) {
|
|
1113
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start justify-between gap-2", children: [
|
|
1114
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
|
|
1115
|
+
/* @__PURE__ */ jsx("label", { htmlFor, className: fieldLabelClass, children }),
|
|
1116
|
+
hint ? /* @__PURE__ */ jsx("p", { className: fieldHintClass, children: hint }) : null
|
|
1117
|
+
] }),
|
|
1118
|
+
trailing ? /* @__PURE__ */ jsx("div", { className: "shrink-0 pt-0.5", children: trailing }) : null
|
|
1119
|
+
] });
|
|
1120
|
+
}
|
|
1121
|
+
function PanelInput(props) {
|
|
1122
|
+
return /* @__PURE__ */ jsx("input", { ...props, className: cn(controlClass, props.className) });
|
|
1123
|
+
}
|
|
1124
|
+
function PanelTextarea(props) {
|
|
1125
|
+
return /* @__PURE__ */ jsx(
|
|
1126
|
+
"textarea",
|
|
1127
|
+
{
|
|
1128
|
+
...props,
|
|
1129
|
+
className: cn(textareaClass, props.className),
|
|
1130
|
+
spellCheck: false
|
|
1131
|
+
}
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
function PanelSelect({
|
|
1135
|
+
id,
|
|
1136
|
+
title,
|
|
1137
|
+
hint,
|
|
1138
|
+
value,
|
|
1139
|
+
onChange,
|
|
1140
|
+
options
|
|
1141
|
+
}) {
|
|
1142
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1143
|
+
/* @__PURE__ */ jsx(FieldLabel, { htmlFor: id, hint, children: title }),
|
|
1144
|
+
/* @__PURE__ */ jsx(
|
|
1145
|
+
"select",
|
|
1146
|
+
{
|
|
1147
|
+
id,
|
|
1148
|
+
className: cn(
|
|
1149
|
+
controlClass,
|
|
1150
|
+
"cursor-pointer appearance-none pr-8"
|
|
1151
|
+
),
|
|
1152
|
+
value,
|
|
1153
|
+
onChange: (e) => onChange(e.target.value),
|
|
1154
|
+
children: options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
1155
|
+
}
|
|
1156
|
+
)
|
|
1157
|
+
] });
|
|
1158
|
+
}
|
|
1159
|
+
function SectionCard({
|
|
1160
|
+
title,
|
|
1161
|
+
description,
|
|
1162
|
+
children,
|
|
1163
|
+
className
|
|
1164
|
+
}) {
|
|
1165
|
+
return /* @__PURE__ */ jsxs(
|
|
1166
|
+
"div",
|
|
1167
|
+
{
|
|
1168
|
+
className: cn(
|
|
1169
|
+
"min-w-0 max-w-full overflow-hidden rounded-xl border border-border/70 bg-card/40 p-3 shadow-sm",
|
|
1170
|
+
className
|
|
1171
|
+
),
|
|
1172
|
+
children: [
|
|
1173
|
+
(title || description) && /* @__PURE__ */ jsxs("div", { className: "mb-3 space-y-0.5", children: [
|
|
1174
|
+
title ? /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: title }) : null,
|
|
1175
|
+
description ? /* @__PURE__ */ jsx("p", { className: fieldHintClass, children: description }) : null
|
|
1176
|
+
] }),
|
|
1177
|
+
children
|
|
1178
|
+
]
|
|
1179
|
+
}
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
function CollapsibleCard({
|
|
1183
|
+
title,
|
|
1184
|
+
description,
|
|
1185
|
+
defaultOpen = false,
|
|
1186
|
+
children
|
|
1187
|
+
}) {
|
|
1188
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
1189
|
+
return /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-xl border border-border/70 bg-card/40 shadow-sm", children: [
|
|
1190
|
+
/* @__PURE__ */ jsxs(
|
|
1191
|
+
"button",
|
|
1192
|
+
{
|
|
1193
|
+
type: "button",
|
|
1194
|
+
className: "flex w-full min-w-0 items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-muted/30",
|
|
1195
|
+
"aria-expanded": open,
|
|
1196
|
+
onClick: () => setOpen((v) => !v),
|
|
1197
|
+
children: [
|
|
1198
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
|
|
1199
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: title }),
|
|
1200
|
+
description ? /* @__PURE__ */ jsx("p", { className: fieldHintClass, children: description }) : null
|
|
1201
|
+
] }),
|
|
1202
|
+
/* @__PURE__ */ jsx(
|
|
1203
|
+
ChevronDown,
|
|
1204
|
+
{
|
|
1205
|
+
className: cn(
|
|
1206
|
+
"h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200",
|
|
1207
|
+
open && "rotate-180"
|
|
1208
|
+
),
|
|
1209
|
+
"aria-hidden": true
|
|
1210
|
+
}
|
|
1211
|
+
)
|
|
1212
|
+
]
|
|
1213
|
+
}
|
|
1214
|
+
),
|
|
1215
|
+
open ? /* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-4 border-t border-border/60 px-3 py-3", children }) : null
|
|
1216
|
+
] });
|
|
1217
|
+
}
|
|
1218
|
+
function FixedBackgroundToggle({
|
|
1219
|
+
checked,
|
|
1220
|
+
onChange
|
|
1221
|
+
}) {
|
|
1222
|
+
return /* @__PURE__ */ jsx(SectionCard, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [
|
|
1223
|
+
/* @__PURE__ */ jsx(
|
|
1224
|
+
"span",
|
|
1225
|
+
{
|
|
1226
|
+
className: cn(
|
|
1227
|
+
"mt-0.5 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border",
|
|
1228
|
+
checked ? "border-primary/30 bg-primary/10 text-primary" : "border-border/70 bg-muted/30 text-muted-foreground"
|
|
1229
|
+
),
|
|
1230
|
+
children: /* @__PURE__ */ jsx(Pin, { className: "h-3.5 w-3.5", "aria-hidden": true })
|
|
1231
|
+
}
|
|
1232
|
+
),
|
|
1233
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
1234
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
1235
|
+
/* @__PURE__ */ jsx(
|
|
1236
|
+
"label",
|
|
1237
|
+
{
|
|
1238
|
+
htmlFor: "bg-fixed-toggle",
|
|
1239
|
+
className: "text-xs font-semibold text-foreground",
|
|
1240
|
+
children: "Fixed background"
|
|
1241
|
+
}
|
|
1242
|
+
),
|
|
1243
|
+
/* @__PURE__ */ jsx(
|
|
1244
|
+
Switch,
|
|
1245
|
+
{
|
|
1246
|
+
id: "bg-fixed-toggle",
|
|
1247
|
+
checked,
|
|
1248
|
+
onCheckedChange: onChange,
|
|
1249
|
+
"aria-describedby": "bg-fixed-hint"
|
|
1250
|
+
}
|
|
1251
|
+
)
|
|
1252
|
+
] }),
|
|
1253
|
+
/* @__PURE__ */ jsx("p", { id: "bg-fixed-hint", className: fieldHintClass, children: "Keep the background still while the page scrolls \u2014 content moves over a static surface." })
|
|
1254
|
+
] })
|
|
1255
|
+
] }) });
|
|
1256
|
+
}
|
|
1257
|
+
function BackgroundPreview({ value }) {
|
|
1258
|
+
const imageUrl = value.type === "image" && typeof value.media?.file === "string" ? value.media.file : void 0;
|
|
1259
|
+
const previewStyle = (() => {
|
|
1260
|
+
if (value.type === "color") {
|
|
1261
|
+
return {
|
|
1262
|
+
backgroundColor: value.backgroundColor || "var(--muted)"
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
if (value.type === "image") {
|
|
1266
|
+
return {
|
|
1267
|
+
backgroundColor: "var(--muted)",
|
|
1268
|
+
backgroundImage: imageUrl ? `url(${imageUrl})` : void 0,
|
|
1269
|
+
backgroundSize: value.backgroundSize || "cover",
|
|
1270
|
+
backgroundPosition: value.backgroundPosition || "center",
|
|
1271
|
+
backgroundRepeat: value.backgroundRepeat || "no-repeat"
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
return {
|
|
1275
|
+
backgroundColor: "var(--background)",
|
|
1276
|
+
backgroundImage: value.backgroundImage,
|
|
1277
|
+
backgroundSize: value.gradientBackgroundSize || "cover",
|
|
1278
|
+
backgroundPosition: value.gradientBackgroundPosition || "center",
|
|
1279
|
+
backgroundRepeat: value.gradientBackgroundRepeat || "no-repeat",
|
|
1280
|
+
WebkitMaskImage: value.gradientMaskImage,
|
|
1281
|
+
maskImage: value.gradientMaskImage
|
|
1282
|
+
};
|
|
1283
|
+
})();
|
|
1284
|
+
const typeLabel = BG_TYPES.find((t) => t.type === value.type)?.label ?? "Background";
|
|
1285
|
+
return /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-xl border border-border/70 shadow-sm", children: [
|
|
1286
|
+
/* @__PURE__ */ jsxs(
|
|
1287
|
+
"div",
|
|
1288
|
+
{
|
|
1289
|
+
className: "relative h-14 w-full max-w-full",
|
|
1290
|
+
style: previewStyle,
|
|
1291
|
+
children: [
|
|
1292
|
+
value.type === "image" && imageUrl && (value.blur || 0) > 0 ? /* @__PURE__ */ jsx(
|
|
1293
|
+
"div",
|
|
1294
|
+
{
|
|
1295
|
+
className: "absolute inset-0",
|
|
1296
|
+
style: {
|
|
1297
|
+
backdropFilter: `blur(${value.blur}px)`
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
) : null,
|
|
1301
|
+
value.type === "image" && value.overlay?.color ? /* @__PURE__ */ jsx(
|
|
1302
|
+
"div",
|
|
1303
|
+
{
|
|
1304
|
+
className: "absolute inset-0",
|
|
1305
|
+
style: {
|
|
1306
|
+
backgroundColor: value.overlay.color,
|
|
1307
|
+
opacity: (value.overlay.opacity || 0) / 10,
|
|
1308
|
+
mixBlendMode: value.overlay.blendMode || "normal"
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
) : null,
|
|
1312
|
+
value.type === "image" && !imageUrl ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
1313
|
+
/* @__PURE__ */ jsx(ImageIcon, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
1314
|
+
"No image yet"
|
|
1315
|
+
] }) : null,
|
|
1316
|
+
value.type === "gradient" && !value.backgroundImage ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
1317
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
1318
|
+
"Choose a pattern"
|
|
1319
|
+
] }) : null
|
|
1320
|
+
]
|
|
1321
|
+
}
|
|
1322
|
+
),
|
|
1323
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t border-border/60 bg-muted/20 px-3 py-1.5", children: [
|
|
1324
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "Preview" }),
|
|
1325
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-foreground", children: typeLabel })
|
|
1326
|
+
] })
|
|
1327
|
+
] });
|
|
1328
|
+
}
|
|
1329
|
+
function SectionBackgroundPanel({
|
|
1330
|
+
value = { type: "color" },
|
|
1331
|
+
onChange,
|
|
1332
|
+
className
|
|
1333
|
+
}) {
|
|
1334
|
+
const [patternFilter, setPatternFilter] = useState("all");
|
|
1335
|
+
const handleTypeChange = (type) => {
|
|
1336
|
+
onChange({ ...value, type });
|
|
1337
|
+
};
|
|
1338
|
+
const handleBackgroundChange = (updates) => {
|
|
1339
|
+
onChange({ ...value, ...updates });
|
|
1340
|
+
};
|
|
1341
|
+
const handleOverlayChange = (overlayUpdates) => {
|
|
1342
|
+
onChange({
|
|
1343
|
+
...value,
|
|
1344
|
+
overlay: {
|
|
1345
|
+
color: "",
|
|
1346
|
+
blendMode: "normal",
|
|
1347
|
+
opacity: 0,
|
|
1348
|
+
...value.overlay,
|
|
1349
|
+
...overlayUpdates
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
};
|
|
1353
|
+
const filteredPresets = useMemo(
|
|
1354
|
+
() => patternFilter === "all" ? PATTERN_PRESETS : PATTERN_PRESETS.filter((p) => p.category === patternFilter),
|
|
1355
|
+
[patternFilter]
|
|
1356
|
+
);
|
|
1357
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0 max-w-full space-y-3", className), children: [
|
|
1358
|
+
/* @__PURE__ */ jsx(
|
|
1359
|
+
"div",
|
|
1360
|
+
{
|
|
1361
|
+
role: "tablist",
|
|
1362
|
+
"aria-label": "Background type",
|
|
1363
|
+
className: "grid min-w-0 grid-cols-3 gap-1.5",
|
|
1364
|
+
children: BG_TYPES.map((tab) => {
|
|
1365
|
+
const Icon = tab.icon;
|
|
1366
|
+
const active = value.type === tab.type;
|
|
1367
|
+
return /* @__PURE__ */ jsxs(
|
|
1368
|
+
"button",
|
|
1369
|
+
{
|
|
1370
|
+
type: "button",
|
|
1371
|
+
role: "tab",
|
|
1372
|
+
"aria-selected": active,
|
|
1373
|
+
className: cn(
|
|
1374
|
+
"group flex min-w-0 flex-col items-start gap-1 rounded-xl border px-2 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
1375
|
+
active ? "border-primary/50 bg-primary/8 shadow-sm" : "border-border/80 bg-muted/15 text-muted-foreground hover:border-primary/30 hover:bg-muted/40 hover:text-foreground"
|
|
1376
|
+
),
|
|
1377
|
+
onClick: () => handleTypeChange(tab.type),
|
|
1378
|
+
children: [
|
|
1379
|
+
/* @__PURE__ */ jsx(
|
|
1380
|
+
"span",
|
|
1381
|
+
{
|
|
1382
|
+
className: cn(
|
|
1383
|
+
"inline-flex h-6 w-6 items-center justify-center rounded-md border transition-colors",
|
|
1384
|
+
active ? "border-primary/30 bg-primary/15 text-primary" : "border-border/70 bg-background text-muted-foreground group-hover:text-foreground"
|
|
1385
|
+
),
|
|
1386
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": true })
|
|
1387
|
+
}
|
|
1388
|
+
),
|
|
1389
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 space-y-0.5", children: [
|
|
1390
|
+
/* @__PURE__ */ jsx(
|
|
1391
|
+
"span",
|
|
1392
|
+
{
|
|
1393
|
+
className: cn(
|
|
1394
|
+
"block truncate text-xs font-semibold leading-none",
|
|
1395
|
+
active ? "text-foreground" : "text-inherit"
|
|
1396
|
+
),
|
|
1397
|
+
children: tab.label
|
|
1398
|
+
}
|
|
1399
|
+
),
|
|
1400
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-[10px] leading-tight text-muted-foreground", children: tab.description })
|
|
1401
|
+
] })
|
|
1402
|
+
]
|
|
1403
|
+
},
|
|
1404
|
+
tab.type
|
|
1405
|
+
);
|
|
1406
|
+
})
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
/* @__PURE__ */ jsx(BackgroundPreview, { value }),
|
|
1410
|
+
value.type === "color" && /* @__PURE__ */ jsx(
|
|
1411
|
+
SectionCard,
|
|
1412
|
+
{
|
|
1413
|
+
title: "Fill colors",
|
|
1414
|
+
description: "Solid section background for light and dark appearance.",
|
|
1415
|
+
children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1416
|
+
/* @__PURE__ */ jsx(
|
|
1417
|
+
ColorSelector,
|
|
1418
|
+
{
|
|
1419
|
+
title: "Light mode",
|
|
1420
|
+
value: value.backgroundColor || "",
|
|
1421
|
+
onChange: (color) => handleBackgroundChange({
|
|
1422
|
+
backgroundColor: color
|
|
1423
|
+
}),
|
|
1424
|
+
allowReset: true
|
|
1425
|
+
}
|
|
1426
|
+
),
|
|
1427
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-border/50 pt-4", children: /* @__PURE__ */ jsx(
|
|
1428
|
+
ColorSelector,
|
|
1429
|
+
{
|
|
1430
|
+
title: "Dark mode",
|
|
1431
|
+
value: value.backgroundColorDark || "",
|
|
1432
|
+
onChange: (color) => handleBackgroundChange({
|
|
1433
|
+
backgroundColorDark: color
|
|
1434
|
+
}),
|
|
1435
|
+
allowReset: true,
|
|
1436
|
+
description: "Optional override when the page is in dark mode"
|
|
1437
|
+
}
|
|
1438
|
+
) })
|
|
1439
|
+
] })
|
|
1440
|
+
}
|
|
1441
|
+
),
|
|
1442
|
+
value.type === "image" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1443
|
+
/* @__PURE__ */ jsx(
|
|
1444
|
+
FixedBackgroundToggle,
|
|
1445
|
+
{
|
|
1446
|
+
checked: value.backgroundAttachment === "fixed",
|
|
1447
|
+
onChange: (fixed) => handleBackgroundChange({
|
|
1448
|
+
backgroundAttachment: fixed ? "fixed" : "scroll"
|
|
1449
|
+
})
|
|
1450
|
+
}
|
|
1451
|
+
),
|
|
1452
|
+
/* @__PURE__ */ jsx(
|
|
1453
|
+
SectionCard,
|
|
1454
|
+
{
|
|
1455
|
+
title: "Source",
|
|
1456
|
+
description: "Paste a public image URL to use as the section backdrop.",
|
|
1457
|
+
children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1458
|
+
/* @__PURE__ */ jsx(FieldLabel, { htmlFor: "background-image-url", children: "Image URL" }),
|
|
1459
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1460
|
+
/* @__PURE__ */ jsx(
|
|
1461
|
+
ImageIcon,
|
|
1462
|
+
{
|
|
1463
|
+
className: "pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground",
|
|
1464
|
+
"aria-hidden": true
|
|
1465
|
+
}
|
|
1466
|
+
),
|
|
1467
|
+
/* @__PURE__ */ jsx(
|
|
1468
|
+
PanelInput,
|
|
1469
|
+
{
|
|
1470
|
+
id: "background-image-url",
|
|
1471
|
+
value: value.media?.file || "",
|
|
1472
|
+
onChange: (e) => handleBackgroundChange({
|
|
1473
|
+
media: e.target.value ? { file: e.target.value } : {}
|
|
1474
|
+
}),
|
|
1475
|
+
placeholder: "https://example.com/background.jpg",
|
|
1476
|
+
className: "pl-9"
|
|
1477
|
+
}
|
|
1478
|
+
)
|
|
1479
|
+
] })
|
|
1480
|
+
] })
|
|
1481
|
+
}
|
|
1482
|
+
),
|
|
1483
|
+
/* @__PURE__ */ jsx(SectionCard, { title: "Effects", children: /* @__PURE__ */ jsx(
|
|
1484
|
+
PageBuilderSlider,
|
|
1485
|
+
{
|
|
1486
|
+
title: "Blur",
|
|
1487
|
+
min: 0,
|
|
1488
|
+
max: 20,
|
|
1489
|
+
value: value.blur || 0,
|
|
1490
|
+
onChange: (blur) => handleBackgroundChange({ blur: blur || 0 }),
|
|
1491
|
+
unit: "px",
|
|
1492
|
+
tooltip: "Softens the image behind the content"
|
|
1493
|
+
}
|
|
1494
|
+
) }),
|
|
1495
|
+
/* @__PURE__ */ jsxs(
|
|
1496
|
+
CollapsibleCard,
|
|
1497
|
+
{
|
|
1498
|
+
title: "Overlay",
|
|
1499
|
+
description: "Tint the image for contrast with text",
|
|
1500
|
+
children: [
|
|
1501
|
+
/* @__PURE__ */ jsx(
|
|
1502
|
+
ColorSelector,
|
|
1503
|
+
{
|
|
1504
|
+
title: "Overlay color",
|
|
1505
|
+
value: value.overlay?.color || "",
|
|
1506
|
+
onChange: (color) => handleOverlayChange({ color: color || "" })
|
|
1507
|
+
}
|
|
1508
|
+
),
|
|
1509
|
+
/* @__PURE__ */ jsx(
|
|
1510
|
+
ColorSelector,
|
|
1511
|
+
{
|
|
1512
|
+
title: "Dark mode overlay",
|
|
1513
|
+
value: value.overlay?.colorDark || "",
|
|
1514
|
+
onChange: (color) => handleOverlayChange({ colorDark: color }),
|
|
1515
|
+
allowReset: true,
|
|
1516
|
+
description: "Optional overlay when dark mode is active"
|
|
1517
|
+
}
|
|
1518
|
+
),
|
|
1519
|
+
/* @__PURE__ */ jsx(
|
|
1520
|
+
PageBuilderSlider,
|
|
1521
|
+
{
|
|
1522
|
+
title: "Opacity",
|
|
1523
|
+
min: 0,
|
|
1524
|
+
max: 10,
|
|
1525
|
+
value: value.overlay?.opacity || 0,
|
|
1526
|
+
onChange: (opacity) => handleOverlayChange({
|
|
1527
|
+
opacity: opacity || 0
|
|
1528
|
+
}),
|
|
1529
|
+
unit: "",
|
|
1530
|
+
tooltip: "0 is invisible, 10 is fully solid"
|
|
1531
|
+
}
|
|
1532
|
+
),
|
|
1533
|
+
/* @__PURE__ */ jsx(
|
|
1534
|
+
PanelSelect,
|
|
1535
|
+
{
|
|
1536
|
+
id: "overlay-blend-mode",
|
|
1537
|
+
title: "Blend mode",
|
|
1538
|
+
value: value.overlay?.blendMode || "normal",
|
|
1539
|
+
onChange: (mode) => handleOverlayChange({ blendMode: mode }),
|
|
1540
|
+
options: BLEND_MODES
|
|
1541
|
+
}
|
|
1542
|
+
)
|
|
1543
|
+
]
|
|
1544
|
+
}
|
|
1545
|
+
),
|
|
1546
|
+
/* @__PURE__ */ jsxs(
|
|
1547
|
+
CollapsibleCard,
|
|
1548
|
+
{
|
|
1549
|
+
title: "Advanced",
|
|
1550
|
+
description: "Size, position, repeat, and CSS mask",
|
|
1551
|
+
children: [
|
|
1552
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1553
|
+
/* @__PURE__ */ jsx(
|
|
1554
|
+
FieldLabel,
|
|
1555
|
+
{
|
|
1556
|
+
htmlFor: "image-background-size",
|
|
1557
|
+
hint: "e.g. cover, contain, 100%",
|
|
1558
|
+
children: "Background size"
|
|
1559
|
+
}
|
|
1560
|
+
),
|
|
1561
|
+
/* @__PURE__ */ jsx(
|
|
1562
|
+
PanelInput,
|
|
1563
|
+
{
|
|
1564
|
+
id: "image-background-size",
|
|
1565
|
+
value: value.backgroundSize || "",
|
|
1566
|
+
onChange: (e) => handleBackgroundChange({
|
|
1567
|
+
backgroundSize: e.target.value
|
|
1568
|
+
}),
|
|
1569
|
+
placeholder: "cover"
|
|
1570
|
+
}
|
|
1571
|
+
)
|
|
1572
|
+
] }),
|
|
1573
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1574
|
+
/* @__PURE__ */ jsx(
|
|
1575
|
+
FieldLabel,
|
|
1576
|
+
{
|
|
1577
|
+
htmlFor: "image-background-position",
|
|
1578
|
+
hint: "e.g. center, top left, 50% 50%",
|
|
1579
|
+
children: "Background position"
|
|
1580
|
+
}
|
|
1581
|
+
),
|
|
1582
|
+
/* @__PURE__ */ jsx(
|
|
1583
|
+
PanelInput,
|
|
1584
|
+
{
|
|
1585
|
+
id: "image-background-position",
|
|
1586
|
+
value: value.backgroundPosition || "",
|
|
1587
|
+
onChange: (e) => handleBackgroundChange({
|
|
1588
|
+
backgroundPosition: e.target.value
|
|
1589
|
+
}),
|
|
1590
|
+
placeholder: "center"
|
|
1591
|
+
}
|
|
1592
|
+
)
|
|
1593
|
+
] }),
|
|
1594
|
+
/* @__PURE__ */ jsx(
|
|
1595
|
+
PanelSelect,
|
|
1596
|
+
{
|
|
1597
|
+
id: "image-background-repeat",
|
|
1598
|
+
title: "Background repeat",
|
|
1599
|
+
value: value.backgroundRepeat || "no-repeat",
|
|
1600
|
+
onChange: (repeat) => handleBackgroundChange({
|
|
1601
|
+
backgroundRepeat: repeat
|
|
1602
|
+
}),
|
|
1603
|
+
options: BACKGROUND_REPEAT_OPTIONS
|
|
1604
|
+
}
|
|
1605
|
+
),
|
|
1606
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1607
|
+
/* @__PURE__ */ jsx(
|
|
1608
|
+
FieldLabel,
|
|
1609
|
+
{
|
|
1610
|
+
htmlFor: "mask-image",
|
|
1611
|
+
hint: "CSS mask to fade or crop the image",
|
|
1612
|
+
trailing: /* @__PURE__ */ jsxs(
|
|
1613
|
+
"a",
|
|
1614
|
+
{
|
|
1615
|
+
href: "https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image",
|
|
1616
|
+
target: "_blank",
|
|
1617
|
+
rel: "noopener noreferrer",
|
|
1618
|
+
className: "inline-flex items-center gap-1 text-[10px] font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
|
|
1619
|
+
children: [
|
|
1620
|
+
"Docs",
|
|
1621
|
+
/* @__PURE__ */ jsx(
|
|
1622
|
+
ExternalLink,
|
|
1623
|
+
{
|
|
1624
|
+
className: "h-3 w-3",
|
|
1625
|
+
"aria-hidden": true
|
|
1626
|
+
}
|
|
1627
|
+
)
|
|
1628
|
+
]
|
|
1629
|
+
}
|
|
1630
|
+
),
|
|
1631
|
+
children: "Mask image"
|
|
1632
|
+
}
|
|
1633
|
+
),
|
|
1634
|
+
/* @__PURE__ */ jsx(
|
|
1635
|
+
PanelTextarea,
|
|
1636
|
+
{
|
|
1637
|
+
id: "mask-image",
|
|
1638
|
+
value: value.maskImage || "",
|
|
1639
|
+
onChange: (e) => handleBackgroundChange({
|
|
1640
|
+
maskImage: e.target.value || void 0
|
|
1641
|
+
}),
|
|
1642
|
+
placeholder: "linear-gradient(black, transparent)",
|
|
1643
|
+
rows: 2
|
|
1644
|
+
}
|
|
1645
|
+
)
|
|
1646
|
+
] })
|
|
1647
|
+
]
|
|
1648
|
+
}
|
|
1649
|
+
)
|
|
1650
|
+
] }),
|
|
1651
|
+
value.type === "gradient" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1652
|
+
/* @__PURE__ */ jsx(
|
|
1653
|
+
FixedBackgroundToggle,
|
|
1654
|
+
{
|
|
1655
|
+
checked: value.backgroundAttachment === "fixed",
|
|
1656
|
+
onChange: (fixed) => handleBackgroundChange({
|
|
1657
|
+
backgroundAttachment: fixed ? "fixed" : "scroll"
|
|
1658
|
+
})
|
|
1659
|
+
}
|
|
1660
|
+
),
|
|
1661
|
+
/* @__PURE__ */ jsxs(
|
|
1662
|
+
SectionCard,
|
|
1663
|
+
{
|
|
1664
|
+
title: "Presets",
|
|
1665
|
+
description: "One click fills the CSS fields below \u2014 edit anytime.",
|
|
1666
|
+
children: [
|
|
1667
|
+
/* @__PURE__ */ jsx("div", { className: "mb-3 flex flex-wrap gap-1.5", children: PATTERN_FILTERS.map((filter) => {
|
|
1668
|
+
const active = patternFilter === filter.id;
|
|
1669
|
+
return /* @__PURE__ */ jsx(
|
|
1670
|
+
"button",
|
|
1671
|
+
{
|
|
1672
|
+
type: "button",
|
|
1673
|
+
className: cn(
|
|
1674
|
+
"rounded-full px-2.5 py-1 text-[11px] font-semibold transition-colors",
|
|
1675
|
+
active ? "bg-primary text-primary-foreground shadow-sm" : "bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
1676
|
+
),
|
|
1677
|
+
onClick: () => setPatternFilter(filter.id),
|
|
1678
|
+
children: filter.label
|
|
1679
|
+
},
|
|
1680
|
+
filter.id
|
|
1681
|
+
);
|
|
1682
|
+
}) }),
|
|
1683
|
+
/* @__PURE__ */ jsx("div", { className: "grid max-h-52 min-w-0 grid-cols-3 gap-1.5 overflow-x-hidden overflow-y-auto", children: filteredPresets.map((preset) => {
|
|
1684
|
+
const isSelected = value.backgroundImage === preset.apply.backgroundImage;
|
|
1685
|
+
return /* @__PURE__ */ jsxs(
|
|
1686
|
+
"button",
|
|
1687
|
+
{
|
|
1688
|
+
type: "button",
|
|
1689
|
+
title: preset.description ?? preset.label,
|
|
1690
|
+
"aria-pressed": isSelected,
|
|
1691
|
+
className: cn(
|
|
1692
|
+
"group relative flex min-w-0 flex-col overflow-hidden rounded-lg border text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
1693
|
+
isSelected ? "border-primary/55 bg-primary/5 shadow-sm" : "border-border/80 bg-muted/10 hover:border-primary/35 hover:bg-muted/30"
|
|
1694
|
+
),
|
|
1695
|
+
onClick: () => handleBackgroundChange(preset.apply),
|
|
1696
|
+
children: [
|
|
1697
|
+
/* @__PURE__ */ jsx(
|
|
1698
|
+
"span",
|
|
1699
|
+
{
|
|
1700
|
+
className: "relative h-12 w-full max-w-full bg-background",
|
|
1701
|
+
style: {
|
|
1702
|
+
backgroundImage: preset.apply.backgroundImage,
|
|
1703
|
+
backgroundSize: preset.apply.gradientBackgroundSize,
|
|
1704
|
+
backgroundRepeat: preset.apply.gradientBackgroundRepeat,
|
|
1705
|
+
backgroundPosition: preset.apply.gradientBackgroundPosition,
|
|
1706
|
+
WebkitMaskImage: preset.apply.gradientMaskImage,
|
|
1707
|
+
maskImage: preset.apply.gradientMaskImage
|
|
1708
|
+
},
|
|
1709
|
+
"aria-hidden": true,
|
|
1710
|
+
children: isSelected ? /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm", children: /* @__PURE__ */ jsx(
|
|
1711
|
+
Check,
|
|
1712
|
+
{
|
|
1713
|
+
className: "h-2.5 w-2.5",
|
|
1714
|
+
"aria-hidden": true
|
|
1715
|
+
}
|
|
1716
|
+
) }) : null
|
|
1717
|
+
}
|
|
1718
|
+
),
|
|
1719
|
+
/* @__PURE__ */ jsx("span", { className: "truncate border-t border-border/60 px-1 py-1 text-[10px] font-semibold leading-tight text-foreground", children: preset.label })
|
|
1720
|
+
]
|
|
1721
|
+
},
|
|
1722
|
+
preset.id
|
|
1723
|
+
);
|
|
1724
|
+
}) })
|
|
1725
|
+
]
|
|
1726
|
+
}
|
|
1727
|
+
),
|
|
1728
|
+
/* @__PURE__ */ jsx(
|
|
1729
|
+
SectionCard,
|
|
1730
|
+
{
|
|
1731
|
+
title: "Custom CSS",
|
|
1732
|
+
description: "Override or hand-author background-image values.",
|
|
1733
|
+
children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-3.5", children: [
|
|
1734
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-2", children: [
|
|
1735
|
+
/* @__PURE__ */ jsx(
|
|
1736
|
+
FieldLabel,
|
|
1737
|
+
{
|
|
1738
|
+
htmlFor: "gradient-css",
|
|
1739
|
+
hint: "CSS background-image \u2014 gradients, SVG data URIs, etc.",
|
|
1740
|
+
children: "Light mode"
|
|
1741
|
+
}
|
|
1742
|
+
),
|
|
1743
|
+
/* @__PURE__ */ jsx(
|
|
1744
|
+
PanelTextarea,
|
|
1745
|
+
{
|
|
1746
|
+
id: "gradient-css",
|
|
1747
|
+
value: value.backgroundImage || "",
|
|
1748
|
+
onChange: (e) => handleBackgroundChange({
|
|
1749
|
+
backgroundImage: e.target.value
|
|
1750
|
+
}),
|
|
1751
|
+
placeholder: "linear-gradient(45deg, #ff0000, #0000ff)",
|
|
1752
|
+
rows: 3
|
|
1753
|
+
}
|
|
1754
|
+
)
|
|
1755
|
+
] }),
|
|
1756
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-2", children: [
|
|
1757
|
+
/* @__PURE__ */ jsx(
|
|
1758
|
+
FieldLabel,
|
|
1759
|
+
{
|
|
1760
|
+
htmlFor: "gradient-css-dark",
|
|
1761
|
+
hint: "Optional dark appearance override",
|
|
1762
|
+
children: "Dark mode"
|
|
1763
|
+
}
|
|
1764
|
+
),
|
|
1765
|
+
/* @__PURE__ */ jsx(
|
|
1766
|
+
PanelTextarea,
|
|
1767
|
+
{
|
|
1768
|
+
id: "gradient-css-dark",
|
|
1769
|
+
value: value.backgroundImageDark || "",
|
|
1770
|
+
onChange: (e) => handleBackgroundChange({
|
|
1771
|
+
backgroundImageDark: e.target.value || void 0
|
|
1772
|
+
}),
|
|
1773
|
+
placeholder: "linear-gradient(45deg, #333333, #666666)",
|
|
1774
|
+
rows: 3
|
|
1775
|
+
}
|
|
1776
|
+
)
|
|
1777
|
+
] })
|
|
1778
|
+
] })
|
|
1779
|
+
}
|
|
1780
|
+
),
|
|
1781
|
+
/* @__PURE__ */ jsxs(
|
|
1782
|
+
CollapsibleCard,
|
|
1783
|
+
{
|
|
1784
|
+
title: "Advanced",
|
|
1785
|
+
description: "Tile size, position, repeat, and mask",
|
|
1786
|
+
children: [
|
|
1787
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1788
|
+
/* @__PURE__ */ jsx(
|
|
1789
|
+
FieldLabel,
|
|
1790
|
+
{
|
|
1791
|
+
htmlFor: "gradient-background-size",
|
|
1792
|
+
hint: "e.g. 24px 24px, cover, 100% 100%",
|
|
1793
|
+
children: "Background size"
|
|
1794
|
+
}
|
|
1795
|
+
),
|
|
1796
|
+
/* @__PURE__ */ jsx(
|
|
1797
|
+
PanelInput,
|
|
1798
|
+
{
|
|
1799
|
+
id: "gradient-background-size",
|
|
1800
|
+
value: value.gradientBackgroundSize || "",
|
|
1801
|
+
onChange: (e) => handleBackgroundChange({
|
|
1802
|
+
gradientBackgroundSize: e.target.value
|
|
1803
|
+
}),
|
|
1804
|
+
placeholder: "cover"
|
|
1805
|
+
}
|
|
1806
|
+
)
|
|
1807
|
+
] }),
|
|
1808
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1809
|
+
/* @__PURE__ */ jsx(
|
|
1810
|
+
FieldLabel,
|
|
1811
|
+
{
|
|
1812
|
+
htmlFor: "gradient-background-position",
|
|
1813
|
+
hint: "e.g. center, 0 0, top left",
|
|
1814
|
+
children: "Background position"
|
|
1815
|
+
}
|
|
1816
|
+
),
|
|
1817
|
+
/* @__PURE__ */ jsx(
|
|
1818
|
+
PanelInput,
|
|
1819
|
+
{
|
|
1820
|
+
id: "gradient-background-position",
|
|
1821
|
+
value: value.gradientBackgroundPosition || "",
|
|
1822
|
+
onChange: (e) => handleBackgroundChange({
|
|
1823
|
+
gradientBackgroundPosition: e.target.value
|
|
1824
|
+
}),
|
|
1825
|
+
placeholder: "center"
|
|
1826
|
+
}
|
|
1827
|
+
)
|
|
1828
|
+
] }),
|
|
1829
|
+
/* @__PURE__ */ jsx(
|
|
1830
|
+
PanelSelect,
|
|
1831
|
+
{
|
|
1832
|
+
id: "gradient-background-repeat",
|
|
1833
|
+
title: "Background repeat",
|
|
1834
|
+
value: value.gradientBackgroundRepeat || "no-repeat",
|
|
1835
|
+
onChange: (repeat) => handleBackgroundChange({
|
|
1836
|
+
gradientBackgroundRepeat: repeat
|
|
1837
|
+
}),
|
|
1838
|
+
options: BACKGROUND_REPEAT_OPTIONS
|
|
1839
|
+
}
|
|
1840
|
+
),
|
|
1841
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1842
|
+
/* @__PURE__ */ jsx(
|
|
1843
|
+
FieldLabel,
|
|
1844
|
+
{
|
|
1845
|
+
htmlFor: "gradient-mask-image",
|
|
1846
|
+
hint: "CSS mask to fade edges or vignette",
|
|
1847
|
+
trailing: /* @__PURE__ */ jsxs(
|
|
1848
|
+
"a",
|
|
1849
|
+
{
|
|
1850
|
+
href: "https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image",
|
|
1851
|
+
target: "_blank",
|
|
1852
|
+
rel: "noopener noreferrer",
|
|
1853
|
+
className: "inline-flex items-center gap-1 text-[10px] font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
|
|
1854
|
+
children: [
|
|
1855
|
+
"Docs",
|
|
1856
|
+
/* @__PURE__ */ jsx(
|
|
1857
|
+
ExternalLink,
|
|
1858
|
+
{
|
|
1859
|
+
className: "h-3 w-3",
|
|
1860
|
+
"aria-hidden": true
|
|
1861
|
+
}
|
|
1862
|
+
)
|
|
1863
|
+
]
|
|
1864
|
+
}
|
|
1865
|
+
),
|
|
1866
|
+
children: "Mask image"
|
|
1867
|
+
}
|
|
1868
|
+
),
|
|
1869
|
+
/* @__PURE__ */ jsx(
|
|
1870
|
+
PanelTextarea,
|
|
1871
|
+
{
|
|
1872
|
+
id: "gradient-mask-image",
|
|
1873
|
+
value: value.gradientMaskImage || "",
|
|
1874
|
+
onChange: (e) => handleBackgroundChange({
|
|
1875
|
+
gradientMaskImage: e.target.value || void 0
|
|
1876
|
+
}),
|
|
1877
|
+
placeholder: "linear-gradient(black, transparent)",
|
|
1878
|
+
rows: 2
|
|
1879
|
+
}
|
|
1880
|
+
)
|
|
1881
|
+
] })
|
|
1882
|
+
]
|
|
1883
|
+
}
|
|
1884
|
+
),
|
|
1885
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 rounded-xl border border-border/60 bg-muted/25 px-3 py-2.5", children: [
|
|
1886
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5", "aria-hidden": true }) }),
|
|
1887
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: [
|
|
1888
|
+
"Need more inspiration? Browse",
|
|
1889
|
+
" ",
|
|
1890
|
+
/* @__PURE__ */ jsx(
|
|
1891
|
+
"a",
|
|
1892
|
+
{
|
|
1893
|
+
href: "https://patterncraft.fun",
|
|
1894
|
+
target: "_blank",
|
|
1895
|
+
rel: "noopener noreferrer",
|
|
1896
|
+
className: "font-medium text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground",
|
|
1897
|
+
children: "PatternCraft"
|
|
1898
|
+
}
|
|
1899
|
+
),
|
|
1900
|
+
" ",
|
|
1901
|
+
"or",
|
|
1902
|
+
" ",
|
|
1903
|
+
/* @__PURE__ */ jsx(
|
|
1904
|
+
"a",
|
|
1905
|
+
{
|
|
1906
|
+
href: "https://magicpattern.design/",
|
|
1907
|
+
target: "_blank",
|
|
1908
|
+
rel: "noopener noreferrer",
|
|
1909
|
+
className: "font-medium text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground",
|
|
1910
|
+
children: "MagicPattern"
|
|
1911
|
+
}
|
|
1912
|
+
),
|
|
1913
|
+
" ",
|
|
1914
|
+
"and paste the CSS into Custom CSS."
|
|
1915
|
+
] })
|
|
1916
|
+
] })
|
|
1917
|
+
] })
|
|
1918
|
+
] });
|
|
1919
|
+
}
|
|
1920
|
+
function DesignFieldGroup({
|
|
1921
|
+
settings,
|
|
1922
|
+
theme,
|
|
1923
|
+
update
|
|
1924
|
+
}) {
|
|
1925
|
+
return /* @__PURE__ */ jsxs(FieldGroup, { title: "Section styling", children: [
|
|
1926
|
+
/* @__PURE__ */ jsx(
|
|
1927
|
+
MaxWidthSelector,
|
|
1928
|
+
{
|
|
1929
|
+
value: settings.maxWidth,
|
|
1930
|
+
themeDefault: theme?.structure?.page?.width,
|
|
1931
|
+
onChange: (maxWidth) => update({ maxWidth })
|
|
1932
|
+
}
|
|
1933
|
+
),
|
|
1934
|
+
/* @__PURE__ */ jsx(
|
|
1935
|
+
VerticalPaddingSelector,
|
|
1936
|
+
{
|
|
1937
|
+
value: settings.verticalPadding,
|
|
1938
|
+
themeDefault: theme?.structure?.section?.padding?.y,
|
|
1939
|
+
onChange: (verticalPadding) => update({ verticalPadding })
|
|
1940
|
+
}
|
|
1941
|
+
),
|
|
1942
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-full flex-col gap-3", children: [
|
|
1943
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-0.5", children: [
|
|
1944
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: "Background" }),
|
|
1945
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: "Color, image, or pattern for this section surface." })
|
|
1946
|
+
] }),
|
|
1947
|
+
/* @__PURE__ */ jsx(
|
|
1948
|
+
SectionBackgroundPanel,
|
|
1949
|
+
{
|
|
1950
|
+
value: settings.background,
|
|
1951
|
+
onChange: (background) => update({ background })
|
|
1952
|
+
}
|
|
1953
|
+
)
|
|
1954
|
+
] })
|
|
1955
|
+
] });
|
|
1956
|
+
}
|
|
1957
|
+
var TABS = [
|
|
1958
|
+
{ key: "data", label: "Data" },
|
|
1959
|
+
{ key: "design", label: "Design" }
|
|
1960
|
+
];
|
|
1961
|
+
function BlockSettings({
|
|
1962
|
+
instance,
|
|
1963
|
+
definition,
|
|
1964
|
+
theme,
|
|
1965
|
+
onChange
|
|
1966
|
+
}) {
|
|
1967
|
+
const [tab, setTab] = useState("data");
|
|
1968
|
+
const Editor = definition.editor;
|
|
1969
|
+
const settings = instance.settings ?? {};
|
|
1970
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-full flex-col", children: [
|
|
1971
|
+
/* @__PURE__ */ jsx(
|
|
1972
|
+
"div",
|
|
1973
|
+
{
|
|
1974
|
+
role: "tablist",
|
|
1975
|
+
"aria-label": "Block settings sections",
|
|
1976
|
+
className: "grid min-w-0 grid-cols-2 border-b border-border/80",
|
|
1977
|
+
children: TABS.map(({ key, label }) => {
|
|
1978
|
+
const Icon = key === "data" ? FilePenLine : Brush;
|
|
1979
|
+
return /* @__PURE__ */ jsxs(
|
|
1980
|
+
"button",
|
|
1981
|
+
{
|
|
1982
|
+
type: "button",
|
|
1983
|
+
role: "tab",
|
|
1984
|
+
"aria-selected": tab === key,
|
|
1985
|
+
className: cn(
|
|
1986
|
+
"relative inline-flex h-11 min-w-0 items-center justify-center gap-2 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
1987
|
+
tab === key ? "text-foreground after:absolute after:inset-x-3 after:-bottom-px after:h-0.5 after:rounded-full after:bg-primary" : "text-muted-foreground hover:text-foreground"
|
|
1988
|
+
),
|
|
1989
|
+
onClick: () => setTab(key),
|
|
1990
|
+
children: [
|
|
1991
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1992
|
+
label
|
|
1993
|
+
]
|
|
1994
|
+
},
|
|
1995
|
+
key
|
|
1996
|
+
);
|
|
1997
|
+
})
|
|
1998
|
+
}
|
|
1999
|
+
),
|
|
2000
|
+
/* @__PURE__ */ jsx("div", { className: "pt-5", children: tab === "data" && Editor && /* @__PURE__ */ jsx(
|
|
2001
|
+
Editor,
|
|
2002
|
+
{
|
|
2003
|
+
settings,
|
|
2004
|
+
theme,
|
|
2005
|
+
onChange
|
|
2006
|
+
}
|
|
2007
|
+
) }),
|
|
2008
|
+
tab === "design" && /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full space-y-5 pt-5", children: [
|
|
2009
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2010
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: "Refine the look" }),
|
|
2011
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: "Adjust layout and surface styles for this block." })
|
|
2012
|
+
] }),
|
|
2013
|
+
definition.layouts && definition.layouts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm", children: [
|
|
2014
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2015
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "Layout" }),
|
|
2016
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Choose how this block is arranged." })
|
|
2017
|
+
] }),
|
|
2018
|
+
/* @__PURE__ */ jsx(
|
|
2019
|
+
"div",
|
|
2020
|
+
{
|
|
2021
|
+
className: "grid gap-2.5",
|
|
2022
|
+
style: {
|
|
2023
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(112px, 1fr))"
|
|
2024
|
+
},
|
|
2025
|
+
children: definition.layouts.map((layout, index) => {
|
|
2026
|
+
const active = settings.layout === layout.id || settings.layout == null && index === 0;
|
|
2027
|
+
return /* @__PURE__ */ jsxs(
|
|
2028
|
+
"button",
|
|
2029
|
+
{
|
|
2030
|
+
type: "button",
|
|
2031
|
+
"aria-pressed": active,
|
|
2032
|
+
className: cn(
|
|
2033
|
+
"group relative flex min-h-16 items-center justify-between gap-2 rounded-xl border px-3 py-2.5 text-left text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
2034
|
+
active ? "border-primary/60 bg-primary/8 text-foreground shadow-sm" : "border-border bg-muted/20 text-muted-foreground hover:border-primary/35 hover:bg-muted/60 hover:text-foreground"
|
|
2035
|
+
),
|
|
2036
|
+
onClick: () => onChange({
|
|
2037
|
+
...settings,
|
|
2038
|
+
layout: layout.id
|
|
2039
|
+
}),
|
|
2040
|
+
children: [
|
|
2041
|
+
/* @__PURE__ */ jsx("span", { children: layout.name }),
|
|
2042
|
+
active && /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(
|
|
2043
|
+
Check,
|
|
2044
|
+
{
|
|
2045
|
+
className: "h-3 w-3",
|
|
2046
|
+
"aria-hidden": "true"
|
|
2047
|
+
}
|
|
2048
|
+
) })
|
|
2049
|
+
]
|
|
2050
|
+
},
|
|
2051
|
+
layout.id
|
|
2052
|
+
);
|
|
2053
|
+
})
|
|
2054
|
+
}
|
|
2055
|
+
)
|
|
2056
|
+
] }),
|
|
2057
|
+
/* @__PURE__ */ jsx(
|
|
2058
|
+
DesignFieldGroup,
|
|
2059
|
+
{
|
|
2060
|
+
settings,
|
|
2061
|
+
theme: theme.theme,
|
|
2062
|
+
update: (partial) => onChange({ ...settings, ...partial })
|
|
2063
|
+
}
|
|
2064
|
+
)
|
|
2065
|
+
] })
|
|
2066
|
+
] });
|
|
2067
|
+
}
|
|
2068
|
+
function CardButton({
|
|
2069
|
+
label,
|
|
2070
|
+
disabled,
|
|
2071
|
+
onClick,
|
|
2072
|
+
children
|
|
2073
|
+
}) {
|
|
2074
|
+
return /* @__PURE__ */ jsx(
|
|
2075
|
+
"button",
|
|
2076
|
+
{
|
|
2077
|
+
type: "button",
|
|
2078
|
+
title: label,
|
|
2079
|
+
"aria-label": label,
|
|
2080
|
+
disabled,
|
|
2081
|
+
className: cn(
|
|
2082
|
+
"inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background shadow-sm transition-colors",
|
|
2083
|
+
disabled ? "cursor-not-allowed opacity-60" : "hover:bg-muted"
|
|
2084
|
+
),
|
|
2085
|
+
onClick: (e) => {
|
|
2086
|
+
e.stopPropagation();
|
|
2087
|
+
onClick?.();
|
|
2088
|
+
},
|
|
2089
|
+
children
|
|
2090
|
+
}
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
function ThemeCard({
|
|
2094
|
+
name,
|
|
2095
|
+
palette,
|
|
2096
|
+
selected = false,
|
|
2097
|
+
active = false,
|
|
2098
|
+
onPreview,
|
|
2099
|
+
onUse,
|
|
2100
|
+
onEdit,
|
|
2101
|
+
className
|
|
2102
|
+
}) {
|
|
2103
|
+
return /* @__PURE__ */ jsxs(
|
|
2104
|
+
"div",
|
|
2105
|
+
{
|
|
2106
|
+
role: "button",
|
|
2107
|
+
tabIndex: 0,
|
|
2108
|
+
"aria-pressed": selected,
|
|
2109
|
+
"aria-label": `Preview ${name}`,
|
|
2110
|
+
className: cn(
|
|
2111
|
+
"flex w-full cursor-pointer flex-col gap-2 rounded-lg bg-background p-2 text-left transition-colors",
|
|
2112
|
+
selected ? "border-2 border-primary shadow-md" : "border border-muted shadow-none hover:bg-muted",
|
|
2113
|
+
className
|
|
2114
|
+
),
|
|
2115
|
+
onClick: onPreview,
|
|
2116
|
+
onKeyDown: (event) => {
|
|
2117
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
2118
|
+
event.preventDefault();
|
|
2119
|
+
onPreview?.();
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
children: [
|
|
2123
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-[32px] items-center justify-between gap-2", children: [
|
|
2124
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: name }),
|
|
2125
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
2126
|
+
onEdit && /* @__PURE__ */ jsx(CardButton, { label: `Edit ${name}`, onClick: onEdit, children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" }) }),
|
|
2127
|
+
onUse && /* @__PURE__ */ jsx(
|
|
2128
|
+
CardButton,
|
|
2129
|
+
{
|
|
2130
|
+
label: `Use ${name}`,
|
|
2131
|
+
disabled: active,
|
|
2132
|
+
onClick: onUse,
|
|
2133
|
+
children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
|
|
2134
|
+
}
|
|
2135
|
+
)
|
|
2136
|
+
] })
|
|
2137
|
+
] }),
|
|
2138
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2", children: palette.map((color, i) => /* @__PURE__ */ jsx(
|
|
2139
|
+
"span",
|
|
2140
|
+
{
|
|
2141
|
+
className: "h-4 w-4 rounded-full border",
|
|
2142
|
+
style: { backgroundColor: color }
|
|
2143
|
+
},
|
|
2144
|
+
i
|
|
2145
|
+
)) })
|
|
2146
|
+
]
|
|
2147
|
+
}
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
function PanelLabel({
|
|
2151
|
+
htmlFor,
|
|
2152
|
+
children
|
|
2153
|
+
}) {
|
|
2154
|
+
return /* @__PURE__ */ jsx(
|
|
2155
|
+
"label",
|
|
2156
|
+
{
|
|
2157
|
+
htmlFor,
|
|
2158
|
+
className: "text-xs font-medium text-foreground",
|
|
2159
|
+
children
|
|
2160
|
+
}
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
function PanelInput2(props) {
|
|
2164
|
+
const { className, ...rest } = props;
|
|
2165
|
+
return /* @__PURE__ */ jsx(
|
|
2166
|
+
"input",
|
|
2167
|
+
{
|
|
2168
|
+
...rest,
|
|
2169
|
+
className: cn(
|
|
2170
|
+
"h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring",
|
|
2171
|
+
className
|
|
2172
|
+
)
|
|
2173
|
+
}
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
function PanelSelect2({
|
|
2177
|
+
title,
|
|
2178
|
+
value,
|
|
2179
|
+
onChange,
|
|
2180
|
+
options,
|
|
2181
|
+
placeholder = "Select\u2026"
|
|
2182
|
+
}) {
|
|
2183
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2184
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: title }),
|
|
2185
|
+
/* @__PURE__ */ jsxs(
|
|
2186
|
+
"select",
|
|
2187
|
+
{
|
|
2188
|
+
className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring",
|
|
2189
|
+
value,
|
|
2190
|
+
onChange: (e) => onChange(e.target.value),
|
|
2191
|
+
children: [
|
|
2192
|
+
!value && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
|
|
2193
|
+
options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
2194
|
+
]
|
|
2195
|
+
}
|
|
2196
|
+
)
|
|
2197
|
+
] });
|
|
2198
|
+
}
|
|
2199
|
+
function AccordionSection({
|
|
2200
|
+
title,
|
|
2201
|
+
children,
|
|
2202
|
+
defaultOpen = false
|
|
2203
|
+
}) {
|
|
2204
|
+
return /* @__PURE__ */ jsxs("details", { className: "group rounded-md border", open: defaultOpen, children: [
|
|
2205
|
+
/* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer select-none items-center justify-between rounded-t-md px-2 py-3 text-xs font-semibold transition-colors hover:bg-muted group-open:border-b [&::-webkit-details-marker]:hidden", children: [
|
|
2206
|
+
title,
|
|
2207
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground transition-transform group-open:rotate-180" })
|
|
2208
|
+
] }),
|
|
2209
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 pb-4 pt-2", children })
|
|
2210
|
+
] });
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
// src/builder/theme-editor/tailwind-to-human-readable.ts
|
|
2214
|
+
var paddingOptions = {
|
|
2215
|
+
x: [
|
|
2216
|
+
{ label: "None", value: "px-0" },
|
|
2217
|
+
{ label: "Extra Small", value: "px-4" },
|
|
2218
|
+
{ label: "Small", value: "px-6" },
|
|
2219
|
+
{ label: "Medium", value: "px-8" },
|
|
2220
|
+
{ label: "Large", value: "px-10" },
|
|
2221
|
+
{ label: "Extra Large", value: "px-12" },
|
|
2222
|
+
{ label: "2X Large", value: "px-16" },
|
|
2223
|
+
{ label: "3X Large", value: "px-20" },
|
|
2224
|
+
{ label: "4X Large", value: "px-24" },
|
|
2225
|
+
{ label: "5X Large", value: "px-32" },
|
|
2226
|
+
{ label: "6X Large", value: "px-40" },
|
|
2227
|
+
{ label: "7X Large", value: "px-48" },
|
|
2228
|
+
{ label: "8X Large", value: "px-56" },
|
|
2229
|
+
{ label: "9X Large", value: "px-64" }
|
|
2230
|
+
],
|
|
2231
|
+
y: [
|
|
2232
|
+
{ label: "None", value: "py-0" },
|
|
2233
|
+
{ label: "Extra Small", value: "py-4" },
|
|
2234
|
+
{ label: "Small", value: "py-6" },
|
|
2235
|
+
{ label: "Medium", value: "py-8" },
|
|
2236
|
+
{ label: "Large", value: "py-10" },
|
|
2237
|
+
{ label: "Extra Large", value: "py-12" },
|
|
2238
|
+
{ label: "2X Large", value: "py-16" },
|
|
2239
|
+
{ label: "3X Large", value: "py-20" },
|
|
2240
|
+
{ label: "4X Large", value: "py-24" },
|
|
2241
|
+
{ label: "5X Large", value: "py-32" },
|
|
2242
|
+
{ label: "6X Large", value: "py-40" },
|
|
2243
|
+
{ label: "7X Large", value: "py-48" },
|
|
2244
|
+
{ label: "8X Large", value: "py-56" },
|
|
2245
|
+
{ label: "9X Large", value: "py-64" }
|
|
2246
|
+
]
|
|
2247
|
+
};
|
|
2248
|
+
var borderWidthOptions = [
|
|
2249
|
+
{ label: "None", value: "border-0" },
|
|
2250
|
+
{ label: "Light", value: "border" },
|
|
2251
|
+
{ label: "Medium", value: "border-2" },
|
|
2252
|
+
{ label: "Heavy", value: "border-4" },
|
|
2253
|
+
{ label: "Extra Heavy", value: "border-8" }
|
|
2254
|
+
];
|
|
2255
|
+
var borderStyleOptions = [
|
|
2256
|
+
{ label: "None", value: "border-none" },
|
|
2257
|
+
{ label: "Solid", value: "border-solid" },
|
|
2258
|
+
{ label: "Dashed", value: "border-dashed" },
|
|
2259
|
+
{ label: "Dotted", value: "border-dotted" },
|
|
2260
|
+
{ label: "Double", value: "border-double" },
|
|
2261
|
+
{ label: "Hidden", value: "border-hidden" }
|
|
2262
|
+
];
|
|
2263
|
+
var borderRadiusOptions = [
|
|
2264
|
+
{ label: "None", value: "rounded-none" },
|
|
2265
|
+
{ label: "Small", value: "rounded-sm" },
|
|
2266
|
+
{ label: "Default", value: "rounded" },
|
|
2267
|
+
{ label: "Medium", value: "rounded-md" },
|
|
2268
|
+
{ label: "Large", value: "rounded-lg" },
|
|
2269
|
+
{ label: "Extra Large", value: "rounded-xl" },
|
|
2270
|
+
{ label: "2X Large", value: "rounded-2xl" },
|
|
2271
|
+
{ label: "3X Large", value: "rounded-3xl" },
|
|
2272
|
+
{ label: "Full", value: "rounded-full" }
|
|
2273
|
+
];
|
|
2274
|
+
var shadowOptions = [
|
|
2275
|
+
{ label: "None", value: "shadow-none" },
|
|
2276
|
+
{ label: "Extra Small", value: "shadow-2xs" },
|
|
2277
|
+
{ label: "Small", value: "shadow-xs" },
|
|
2278
|
+
{ label: "Default", value: "shadow-sm" },
|
|
2279
|
+
{ label: "Medium", value: "shadow-md" },
|
|
2280
|
+
{ label: "Large", value: "shadow-lg" },
|
|
2281
|
+
{ label: "Extra Large", value: "shadow-xl" },
|
|
2282
|
+
{ label: "2X Large", value: "shadow-2xl" }
|
|
2283
|
+
];
|
|
2284
|
+
var fontSizeOptions = [
|
|
2285
|
+
{ label: "Extra Small", value: "text-xs" },
|
|
2286
|
+
{ label: "Small", value: "text-sm" },
|
|
2287
|
+
{ label: "Base", value: "text-base" },
|
|
2288
|
+
{ label: "Large", value: "text-lg" },
|
|
2289
|
+
{ label: "Extra Large", value: "text-xl" },
|
|
2290
|
+
{ label: "2X Large", value: "text-2xl" },
|
|
2291
|
+
{ label: "3X Large", value: "text-3xl" },
|
|
2292
|
+
{ label: "4X Large", value: "text-4xl" },
|
|
2293
|
+
{ label: "5X Large", value: "text-5xl" },
|
|
2294
|
+
{ label: "6X Large", value: "text-6xl" },
|
|
2295
|
+
{ label: "7X Large", value: "text-7xl" },
|
|
2296
|
+
{ label: "8X Large", value: "text-8xl" },
|
|
2297
|
+
{ label: "9X Large", value: "text-9xl" }
|
|
2298
|
+
];
|
|
2299
|
+
var fontWeightOptions = [
|
|
2300
|
+
{ label: "Thin", value: "font-thin" },
|
|
2301
|
+
{ label: "Extra Light", value: "font-extralight" },
|
|
2302
|
+
{ label: "Light", value: "font-light" },
|
|
2303
|
+
{ label: "Normal", value: "font-normal" },
|
|
2304
|
+
{ label: "Medium", value: "font-medium" },
|
|
2305
|
+
{ label: "Semi Bold", value: "font-semibold" },
|
|
2306
|
+
{ label: "Bold", value: "font-bold" },
|
|
2307
|
+
{ label: "Extra Bold", value: "font-extrabold" },
|
|
2308
|
+
{ label: "Black", value: "font-black" }
|
|
2309
|
+
];
|
|
2310
|
+
var lineHeightOptions = [
|
|
2311
|
+
{ label: "None", value: "leading-none" },
|
|
2312
|
+
{ label: "Tight", value: "leading-tight" },
|
|
2313
|
+
{ label: "Snug", value: "leading-snug" },
|
|
2314
|
+
{ label: "Normal", value: "leading-normal" },
|
|
2315
|
+
{ label: "Relaxed", value: "leading-relaxed" },
|
|
2316
|
+
{ label: "Loose", value: "leading-loose" }
|
|
2317
|
+
];
|
|
2318
|
+
var letterSpacingOptions = [
|
|
2319
|
+
{ label: "Tighter", value: "tracking-tighter" },
|
|
2320
|
+
{ label: "Tight", value: "tracking-tight" },
|
|
2321
|
+
{ label: "Normal", value: "tracking-normal" },
|
|
2322
|
+
{ label: "Wide", value: "tracking-wide" },
|
|
2323
|
+
{ label: "Wider", value: "tracking-wider" },
|
|
2324
|
+
{ label: "Widest", value: "tracking-widest" }
|
|
2325
|
+
];
|
|
2326
|
+
var textTransformOptions = [
|
|
2327
|
+
{ label: "None", value: "normal-case" },
|
|
2328
|
+
{ label: "Uppercase", value: "uppercase" },
|
|
2329
|
+
{ label: "Lowercase", value: "lowercase" },
|
|
2330
|
+
{ label: "Capitalize", value: "capitalize" }
|
|
2331
|
+
];
|
|
2332
|
+
var fontFamilyOptions = {
|
|
2333
|
+
Sans: [
|
|
2334
|
+
{ label: "Inter", value: "font-inter" },
|
|
2335
|
+
{ label: "Open Sans", value: "font-open-sans" },
|
|
2336
|
+
{ label: "Source Sans 3", value: "font-source-sans-3" },
|
|
2337
|
+
{ label: "Noto Sans", value: "font-noto-sans" },
|
|
2338
|
+
{ label: "Roboto", value: "font-roboto" },
|
|
2339
|
+
{ label: "Mulish", value: "font-mulish" },
|
|
2340
|
+
{ label: "Nunito", value: "font-nunito" },
|
|
2341
|
+
{ label: "Work Sans", value: "font-work-sans" }
|
|
2342
|
+
],
|
|
2343
|
+
Serif: [
|
|
2344
|
+
{ label: "Merriweather", value: "font-merriweather" },
|
|
2345
|
+
{ label: "Alegreya", value: "font-alegreya" },
|
|
2346
|
+
{ label: "Playfair Display", value: "font-playfair-display" },
|
|
2347
|
+
{ label: "Roboto Slab", value: "font-roboto-slab" },
|
|
2348
|
+
{ label: "Source Serif 4", value: "font-source-serif-4" }
|
|
2349
|
+
],
|
|
2350
|
+
Display: [
|
|
2351
|
+
{ label: "Montserrat", value: "font-montserrat" },
|
|
2352
|
+
{ label: "Poppins", value: "font-poppins" },
|
|
2353
|
+
{ label: "Raleway", value: "font-raleway" },
|
|
2354
|
+
{ label: "Rubik", value: "font-rubik" },
|
|
2355
|
+
{ label: "Oswald", value: "font-oswald" },
|
|
2356
|
+
{ label: "Bebas Neue", value: "font-bebas-neue" }
|
|
2357
|
+
],
|
|
2358
|
+
Modern: [
|
|
2359
|
+
{ label: "Lato", value: "font-lato" },
|
|
2360
|
+
{ label: "PT Sans", value: "font-pt-sans" },
|
|
2361
|
+
{ label: "Quicksand", value: "font-quicksand" }
|
|
2362
|
+
]
|
|
2363
|
+
};
|
|
2364
|
+
function TypographySelector({
|
|
2365
|
+
value,
|
|
2366
|
+
onChange
|
|
2367
|
+
}) {
|
|
2368
|
+
const update = (patch) => onChange({ ...value, ...patch });
|
|
2369
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6 p-2", children: [
|
|
2370
|
+
/* @__PURE__ */ jsx("div", { className: "h-[100px] overflow-auto rounded-lg border p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
2371
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: "Preview" }),
|
|
2372
|
+
/* @__PURE__ */ jsx(
|
|
2373
|
+
"p",
|
|
2374
|
+
{
|
|
2375
|
+
className: cn(
|
|
2376
|
+
value?.fontFamily || "",
|
|
2377
|
+
value?.fontSize || "",
|
|
2378
|
+
value?.fontWeight || "",
|
|
2379
|
+
value?.lineHeight || "",
|
|
2380
|
+
value?.letterSpacing || "",
|
|
2381
|
+
value?.textTransform || "normal-case"
|
|
2382
|
+
),
|
|
2383
|
+
children: "The quick brown fox jumps over the lazy dog"
|
|
2384
|
+
}
|
|
2385
|
+
)
|
|
2386
|
+
] }) }),
|
|
2387
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
2388
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2389
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: "Font Family" }),
|
|
2390
|
+
/* @__PURE__ */ jsxs(
|
|
2391
|
+
"select",
|
|
2392
|
+
{
|
|
2393
|
+
className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus:ring-2 focus:ring-ring",
|
|
2394
|
+
value: value?.fontFamily || "",
|
|
2395
|
+
onChange: (e) => update({ fontFamily: e.target.value }),
|
|
2396
|
+
children: [
|
|
2397
|
+
!value?.fontFamily && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select font family" }),
|
|
2398
|
+
Object.entries(fontFamilyOptions).map(
|
|
2399
|
+
([category, fonts]) => /* @__PURE__ */ jsx("optgroup", { label: category, children: fonts.map((font) => /* @__PURE__ */ jsx(
|
|
2400
|
+
"option",
|
|
2401
|
+
{
|
|
2402
|
+
value: font.value,
|
|
2403
|
+
children: font.label
|
|
2404
|
+
},
|
|
2405
|
+
font.value
|
|
2406
|
+
)) }, category)
|
|
2407
|
+
)
|
|
2408
|
+
]
|
|
2409
|
+
}
|
|
2410
|
+
)
|
|
2411
|
+
] }),
|
|
2412
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2413
|
+
/* @__PURE__ */ jsx(
|
|
2414
|
+
PanelSelect2,
|
|
2415
|
+
{
|
|
2416
|
+
title: "Font Size",
|
|
2417
|
+
value: value?.fontSize || "",
|
|
2418
|
+
onChange: (v) => update({ fontSize: v }),
|
|
2419
|
+
options: fontSizeOptions,
|
|
2420
|
+
placeholder: "Select size"
|
|
2421
|
+
}
|
|
2422
|
+
),
|
|
2423
|
+
/* @__PURE__ */ jsx(
|
|
2424
|
+
PanelSelect2,
|
|
2425
|
+
{
|
|
2426
|
+
title: "Font Weight",
|
|
2427
|
+
value: value?.fontWeight || "",
|
|
2428
|
+
onChange: (v) => update({
|
|
2429
|
+
fontWeight: v
|
|
2430
|
+
}),
|
|
2431
|
+
options: fontWeightOptions,
|
|
2432
|
+
placeholder: "Select weight"
|
|
2433
|
+
}
|
|
2434
|
+
)
|
|
2435
|
+
] }),
|
|
2436
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2437
|
+
/* @__PURE__ */ jsx(
|
|
2438
|
+
PanelSelect2,
|
|
2439
|
+
{
|
|
2440
|
+
title: "Line Height",
|
|
2441
|
+
value: value?.lineHeight || "",
|
|
2442
|
+
onChange: (v) => update({
|
|
2443
|
+
lineHeight: v
|
|
2444
|
+
}),
|
|
2445
|
+
options: lineHeightOptions,
|
|
2446
|
+
placeholder: "Select line height"
|
|
2447
|
+
}
|
|
2448
|
+
),
|
|
2449
|
+
/* @__PURE__ */ jsx(
|
|
2450
|
+
PanelSelect2,
|
|
2451
|
+
{
|
|
2452
|
+
title: "Letter Spacing",
|
|
2453
|
+
value: value?.letterSpacing || "",
|
|
2454
|
+
onChange: (v) => update({
|
|
2455
|
+
letterSpacing: v
|
|
2456
|
+
}),
|
|
2457
|
+
options: letterSpacingOptions,
|
|
2458
|
+
placeholder: "Select spacing"
|
|
2459
|
+
}
|
|
2460
|
+
)
|
|
2461
|
+
] }),
|
|
2462
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4", children: /* @__PURE__ */ jsx(
|
|
2463
|
+
PanelSelect2,
|
|
2464
|
+
{
|
|
2465
|
+
title: "Text Transform",
|
|
2466
|
+
value: value?.textTransform || "normal-case",
|
|
2467
|
+
onChange: (v) => update({
|
|
2468
|
+
textTransform: v
|
|
2469
|
+
}),
|
|
2470
|
+
options: textTransformOptions,
|
|
2471
|
+
placeholder: "Select transform"
|
|
2472
|
+
}
|
|
2473
|
+
) })
|
|
2474
|
+
] })
|
|
2475
|
+
] });
|
|
2476
|
+
}
|
|
2477
|
+
var interactiveDisplayNames = {
|
|
2478
|
+
button: "Button",
|
|
2479
|
+
link: "Link",
|
|
2480
|
+
card: "Card",
|
|
2481
|
+
input: "Input"
|
|
2482
|
+
};
|
|
2483
|
+
function InteractiveSelector({
|
|
2484
|
+
type,
|
|
2485
|
+
theme,
|
|
2486
|
+
onChange
|
|
2487
|
+
}) {
|
|
2488
|
+
const value = theme.interactives[type];
|
|
2489
|
+
const updateInteractive = (patch) => {
|
|
2490
|
+
onChange({
|
|
2491
|
+
...theme,
|
|
2492
|
+
interactives: {
|
|
2493
|
+
...theme.interactives,
|
|
2494
|
+
[type]: {
|
|
2495
|
+
...value,
|
|
2496
|
+
...patch
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
});
|
|
2500
|
+
};
|
|
2501
|
+
const renderDemo = () => {
|
|
2502
|
+
switch (type) {
|
|
2503
|
+
case "button":
|
|
2504
|
+
return /* @__PURE__ */ jsx(Button2, { theme, children: "Preview" });
|
|
2505
|
+
case "link":
|
|
2506
|
+
return /* @__PURE__ */ jsx(Link, { theme, children: "Demo Link" });
|
|
2507
|
+
case "card":
|
|
2508
|
+
return /* @__PURE__ */ jsx(PageCard, { theme, children: /* @__PURE__ */ jsxs(PageCardContent, { theme, children: [
|
|
2509
|
+
/* @__PURE__ */ jsx(PageCardHeader, { theme, children: "Card Title" }),
|
|
2510
|
+
/* @__PURE__ */ jsx("div", { children: "Card content goes here" })
|
|
2511
|
+
] }) });
|
|
2512
|
+
case "input":
|
|
2513
|
+
return /* @__PURE__ */ jsx(
|
|
2514
|
+
Input,
|
|
2515
|
+
{
|
|
2516
|
+
theme,
|
|
2517
|
+
type: "text",
|
|
2518
|
+
placeholder: "Demo Input"
|
|
2519
|
+
}
|
|
2520
|
+
);
|
|
2521
|
+
default:
|
|
2522
|
+
return null;
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
const renderPaddingConfig = (allowedPaddingX, allowedPaddingY) => {
|
|
2526
|
+
const filteredPaddingX = allowedPaddingX ? paddingOptions.x.filter(
|
|
2527
|
+
(option) => allowedPaddingX.includes(option.value)
|
|
2528
|
+
) : paddingOptions.x;
|
|
2529
|
+
const filteredPaddingY = allowedPaddingY ? paddingOptions.y.filter(
|
|
2530
|
+
(option) => allowedPaddingY.includes(option.value)
|
|
2531
|
+
) : paddingOptions.y;
|
|
2532
|
+
return /* @__PURE__ */ jsx(AccordionSection, { title: "Padding", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2533
|
+
/* @__PURE__ */ jsx(
|
|
2534
|
+
PanelSelect2,
|
|
2535
|
+
{
|
|
2536
|
+
title: "Padding X",
|
|
2537
|
+
value: value?.padding?.x || "",
|
|
2538
|
+
onChange: (newValue) => updateInteractive({
|
|
2539
|
+
padding: {
|
|
2540
|
+
...value?.padding,
|
|
2541
|
+
x: newValue
|
|
2542
|
+
}
|
|
2543
|
+
}),
|
|
2544
|
+
options: filteredPaddingX,
|
|
2545
|
+
placeholder: "Select padding"
|
|
2546
|
+
}
|
|
2547
|
+
),
|
|
2548
|
+
/* @__PURE__ */ jsx(
|
|
2549
|
+
PanelSelect2,
|
|
2550
|
+
{
|
|
2551
|
+
title: "Padding Y",
|
|
2552
|
+
value: value?.padding?.y || "",
|
|
2553
|
+
onChange: (newValue) => updateInteractive({
|
|
2554
|
+
padding: {
|
|
2555
|
+
...value?.padding,
|
|
2556
|
+
y: newValue
|
|
2557
|
+
}
|
|
2558
|
+
}),
|
|
2559
|
+
options: filteredPaddingY,
|
|
2560
|
+
placeholder: "Select padding"
|
|
2561
|
+
}
|
|
2562
|
+
)
|
|
2563
|
+
] }) });
|
|
2564
|
+
};
|
|
2565
|
+
const renderBorderConfig = () => /* @__PURE__ */ jsx(AccordionSection, { title: "Border", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
2566
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2567
|
+
/* @__PURE__ */ jsx(
|
|
2568
|
+
PanelSelect2,
|
|
2569
|
+
{
|
|
2570
|
+
title: "Border Radius",
|
|
2571
|
+
value: value?.border?.radius || "",
|
|
2572
|
+
onChange: (newValue) => updateInteractive({
|
|
2573
|
+
border: {
|
|
2574
|
+
...value?.border,
|
|
2575
|
+
radius: newValue
|
|
2576
|
+
}
|
|
2577
|
+
}),
|
|
2578
|
+
options: borderRadiusOptions,
|
|
2579
|
+
placeholder: "Select radius"
|
|
2580
|
+
}
|
|
2581
|
+
),
|
|
2582
|
+
/* @__PURE__ */ jsx(
|
|
2583
|
+
PanelSelect2,
|
|
2584
|
+
{
|
|
2585
|
+
title: "Border Style",
|
|
2586
|
+
value: value?.border?.style || "",
|
|
2587
|
+
onChange: (newValue) => updateInteractive({
|
|
2588
|
+
border: {
|
|
2589
|
+
...value?.border,
|
|
2590
|
+
style: newValue
|
|
2591
|
+
}
|
|
2592
|
+
}),
|
|
2593
|
+
options: borderStyleOptions,
|
|
2594
|
+
placeholder: "Select style"
|
|
2595
|
+
}
|
|
2596
|
+
)
|
|
2597
|
+
] }),
|
|
2598
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4", children: /* @__PURE__ */ jsx(
|
|
2599
|
+
PanelSelect2,
|
|
2600
|
+
{
|
|
2601
|
+
title: "Border Width",
|
|
2602
|
+
value: value?.border?.width || "",
|
|
2603
|
+
onChange: (newValue) => updateInteractive({
|
|
2604
|
+
border: {
|
|
2605
|
+
...value?.border,
|
|
2606
|
+
width: newValue
|
|
2607
|
+
}
|
|
2608
|
+
}),
|
|
2609
|
+
options: borderWidthOptions,
|
|
2610
|
+
placeholder: "Select width"
|
|
2611
|
+
}
|
|
2612
|
+
) })
|
|
2613
|
+
] }) });
|
|
2614
|
+
const renderBoxShadowConfig = () => {
|
|
2615
|
+
if (type === "link") {
|
|
2616
|
+
return null;
|
|
2617
|
+
}
|
|
2618
|
+
return /* @__PURE__ */ jsx(AccordionSection, { title: "Shadow", children: /* @__PURE__ */ jsx(
|
|
2619
|
+
PanelSelect2,
|
|
2620
|
+
{
|
|
2621
|
+
title: "Shadow",
|
|
2622
|
+
value: value?.shadow || "",
|
|
2623
|
+
onChange: (newValue) => updateInteractive({ shadow: newValue }),
|
|
2624
|
+
options: shadowOptions,
|
|
2625
|
+
placeholder: "Select shadow"
|
|
2626
|
+
}
|
|
2627
|
+
) });
|
|
2628
|
+
};
|
|
2629
|
+
const renderTextShadowConfig = () => {
|
|
2630
|
+
if (type !== "link") {
|
|
2631
|
+
return null;
|
|
2632
|
+
}
|
|
2633
|
+
return /* @__PURE__ */ jsx(AccordionSection, { title: "Text Shadow", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2634
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: "Text Shadow" }),
|
|
2635
|
+
/* @__PURE__ */ jsx(
|
|
2636
|
+
PanelInput2,
|
|
2637
|
+
{
|
|
2638
|
+
value: value?.textShadow || "",
|
|
2639
|
+
placeholder: "0px 0px 0px 0px rgba(0, 0, 0, 0.1)",
|
|
2640
|
+
onChange: (e) => updateInteractive({ textShadow: e.target.value })
|
|
2641
|
+
}
|
|
2642
|
+
)
|
|
2643
|
+
] }) });
|
|
2644
|
+
};
|
|
2645
|
+
const renderCustomClassesConfig = () => /* @__PURE__ */ jsx(AccordionSection, { title: "Advanced", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2646
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: "Custom classes" }),
|
|
2647
|
+
/* @__PURE__ */ jsx(
|
|
2648
|
+
PanelInput2,
|
|
2649
|
+
{
|
|
2650
|
+
value: value?.custom || "",
|
|
2651
|
+
onChange: (e) => updateInteractive({ custom: e.target.value }),
|
|
2652
|
+
placeholder: "Enter Tailwind classes"
|
|
2653
|
+
}
|
|
2654
|
+
),
|
|
2655
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
2656
|
+
/* @__PURE__ */ jsx(Info, { className: "h-4 w-4 shrink-0" }),
|
|
2657
|
+
/* @__PURE__ */ jsx("span", { children: "Use any supported Tailwind class here." })
|
|
2658
|
+
] })
|
|
2659
|
+
] }) });
|
|
2660
|
+
const renderConfig = () => {
|
|
2661
|
+
switch (type) {
|
|
2662
|
+
case "button":
|
|
2663
|
+
case "input":
|
|
2664
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2665
|
+
renderPaddingConfig(
|
|
2666
|
+
["px-0", "px-4", "px-6", "px-8", "px-10"],
|
|
2667
|
+
["py-0", "py-4", "py-6", "py-8", "py-10"]
|
|
2668
|
+
),
|
|
2669
|
+
renderBorderConfig(),
|
|
2670
|
+
renderBoxShadowConfig(),
|
|
2671
|
+
renderCustomClassesConfig()
|
|
2672
|
+
] });
|
|
2673
|
+
case "link":
|
|
2674
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2675
|
+
renderTextShadowConfig(),
|
|
2676
|
+
renderCustomClassesConfig()
|
|
2677
|
+
] });
|
|
2678
|
+
case "card":
|
|
2679
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2680
|
+
renderPaddingConfig([
|
|
2681
|
+
"px-0",
|
|
2682
|
+
"px-4",
|
|
2683
|
+
"px-6",
|
|
2684
|
+
"px-8",
|
|
2685
|
+
"px-10",
|
|
2686
|
+
"px-12",
|
|
2687
|
+
"px-16",
|
|
2688
|
+
"px-20",
|
|
2689
|
+
"px-24",
|
|
2690
|
+
"px-32"
|
|
2691
|
+
]),
|
|
2692
|
+
renderBorderConfig(),
|
|
2693
|
+
renderBoxShadowConfig(),
|
|
2694
|
+
renderCustomClassesConfig()
|
|
2695
|
+
] });
|
|
2696
|
+
default:
|
|
2697
|
+
return null;
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6 p-2", children: [
|
|
2701
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-[100px] overflow-auto rounded-lg border p-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
2702
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: "Preview" }),
|
|
2703
|
+
renderDemo()
|
|
2704
|
+
] }) }),
|
|
2705
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-4", children: renderConfig() })
|
|
2706
|
+
] });
|
|
2707
|
+
}
|
|
2708
|
+
var structureDisplayNames = {
|
|
2709
|
+
page: "Page",
|
|
2710
|
+
section: "Section"
|
|
2711
|
+
};
|
|
2712
|
+
function StructureSelector({
|
|
2713
|
+
type,
|
|
2714
|
+
theme,
|
|
2715
|
+
onChange
|
|
2716
|
+
}) {
|
|
2717
|
+
if (type === "page") {
|
|
2718
|
+
const pageValue = theme.structure.page;
|
|
2719
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-6 p-2", children: /* @__PURE__ */ jsx(
|
|
2720
|
+
MaxWidthSelector,
|
|
2721
|
+
{
|
|
2722
|
+
value: pageValue?.width,
|
|
2723
|
+
onChange: (width) => {
|
|
2724
|
+
if (!width) return;
|
|
2725
|
+
onChange({
|
|
2726
|
+
...theme,
|
|
2727
|
+
structure: {
|
|
2728
|
+
...theme.structure,
|
|
2729
|
+
page: { width }
|
|
2730
|
+
}
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
) });
|
|
2735
|
+
}
|
|
2736
|
+
const sectionValue = theme.structure.section;
|
|
2737
|
+
const updatePadding = (patch) => {
|
|
2738
|
+
onChange({
|
|
2739
|
+
...theme,
|
|
2740
|
+
structure: {
|
|
2741
|
+
...theme.structure,
|
|
2742
|
+
section: {
|
|
2743
|
+
...sectionValue,
|
|
2744
|
+
padding: {
|
|
2745
|
+
...sectionValue?.padding,
|
|
2746
|
+
...patch
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
};
|
|
2752
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6 p-2", children: [
|
|
2753
|
+
/* @__PURE__ */ jsx(
|
|
2754
|
+
PanelSelect2,
|
|
2755
|
+
{
|
|
2756
|
+
title: "Horizontal Padding",
|
|
2757
|
+
value: sectionValue?.padding?.x || "",
|
|
2758
|
+
onChange: (x) => updatePadding({ x }),
|
|
2759
|
+
options: paddingOptions.x,
|
|
2760
|
+
placeholder: "Select padding"
|
|
2761
|
+
}
|
|
2762
|
+
),
|
|
2763
|
+
/* @__PURE__ */ jsx(
|
|
2764
|
+
VerticalPaddingSelector,
|
|
2765
|
+
{
|
|
2766
|
+
value: sectionValue?.padding?.y,
|
|
2767
|
+
onChange: (y) => {
|
|
2768
|
+
if (!y) return;
|
|
2769
|
+
updatePadding({ y });
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
)
|
|
2773
|
+
] });
|
|
2774
|
+
}
|
|
2775
|
+
var sections = [
|
|
2776
|
+
{
|
|
2777
|
+
id: "colors",
|
|
2778
|
+
label: "Colors",
|
|
2779
|
+
icon: /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
|
|
2780
|
+
},
|
|
2781
|
+
{
|
|
2782
|
+
id: "typography",
|
|
2783
|
+
label: "Typography",
|
|
2784
|
+
icon: /* @__PURE__ */ jsx(Type, { className: "h-4 w-4" })
|
|
2785
|
+
},
|
|
2786
|
+
{
|
|
2787
|
+
id: "interactives",
|
|
2788
|
+
label: "Interactives",
|
|
2789
|
+
icon: /* @__PURE__ */ jsx(SquareMousePointer, { className: "h-4 w-4" })
|
|
2790
|
+
},
|
|
2791
|
+
{
|
|
2792
|
+
id: "structure",
|
|
2793
|
+
label: "Structure",
|
|
2794
|
+
icon: /* @__PURE__ */ jsx(Columns3, { className: "h-4 w-4" })
|
|
2795
|
+
}
|
|
2796
|
+
];
|
|
2797
|
+
var typographyDisplayNames = {
|
|
2798
|
+
header1: "Header 1",
|
|
2799
|
+
header2: "Header 2",
|
|
2800
|
+
header3: "Header 3",
|
|
2801
|
+
header4: "Header 4",
|
|
2802
|
+
preheader: "Preheader",
|
|
2803
|
+
subheader1: "Subheader 1",
|
|
2804
|
+
subheader2: "Subheader 2",
|
|
2805
|
+
text1: "Text 1",
|
|
2806
|
+
text2: "Text 2",
|
|
2807
|
+
caption: "Caption",
|
|
2808
|
+
link: "Link",
|
|
2809
|
+
button: "Button Text",
|
|
2810
|
+
input: "Input Text"
|
|
2811
|
+
};
|
|
2812
|
+
var typographyCategories = [
|
|
2813
|
+
{
|
|
2814
|
+
name: "Headers",
|
|
2815
|
+
items: ["header1", "header2", "header3", "header4", "preheader"]
|
|
2816
|
+
},
|
|
2817
|
+
{
|
|
2818
|
+
name: "Subheaders",
|
|
2819
|
+
items: ["subheader1", "subheader2"]
|
|
2820
|
+
},
|
|
2821
|
+
{
|
|
2822
|
+
name: "Body Text",
|
|
2823
|
+
items: ["text1", "text2", "caption"]
|
|
2824
|
+
},
|
|
2825
|
+
{
|
|
2826
|
+
name: "Interactive Elements",
|
|
2827
|
+
items: ["link", "button", "input"]
|
|
2828
|
+
}
|
|
2829
|
+
];
|
|
2830
|
+
var colorCategories = [
|
|
2831
|
+
{
|
|
2832
|
+
key: "primary",
|
|
2833
|
+
label: "Primary Colors",
|
|
2834
|
+
colors: [
|
|
2835
|
+
{ name: "primary", displayName: "Primary" },
|
|
2836
|
+
{ name: "primaryForeground", displayName: "Primary Foreground" }
|
|
2837
|
+
]
|
|
2838
|
+
},
|
|
2839
|
+
{
|
|
2840
|
+
key: "secondary",
|
|
2841
|
+
label: "Secondary Colors",
|
|
2842
|
+
colors: [
|
|
2843
|
+
{ name: "secondary", displayName: "Secondary" },
|
|
2844
|
+
{
|
|
2845
|
+
name: "secondaryForeground",
|
|
2846
|
+
displayName: "Secondary Foreground"
|
|
2847
|
+
}
|
|
2848
|
+
]
|
|
2849
|
+
},
|
|
2850
|
+
{
|
|
2851
|
+
key: "accent",
|
|
2852
|
+
label: "Accent Colors",
|
|
2853
|
+
colors: [
|
|
2854
|
+
{ name: "accent", displayName: "Accent" },
|
|
2855
|
+
{ name: "accentForeground", displayName: "Accent Foreground" }
|
|
2856
|
+
]
|
|
2857
|
+
},
|
|
2858
|
+
{
|
|
2859
|
+
key: "base",
|
|
2860
|
+
label: "Base Colors",
|
|
2861
|
+
colors: [
|
|
2862
|
+
{ name: "background", displayName: "Background" },
|
|
2863
|
+
{ name: "foreground", displayName: "Foreground" }
|
|
2864
|
+
]
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
key: "card",
|
|
2868
|
+
label: "Card Colors",
|
|
2869
|
+
colors: [
|
|
2870
|
+
{ name: "card", displayName: "Card" },
|
|
2871
|
+
{ name: "cardForeground", displayName: "Card Foreground" }
|
|
2872
|
+
]
|
|
2873
|
+
},
|
|
2874
|
+
{
|
|
2875
|
+
key: "popover",
|
|
2876
|
+
label: "Popover Colors",
|
|
2877
|
+
colors: [
|
|
2878
|
+
{ name: "popover", displayName: "Popover" },
|
|
2879
|
+
{ name: "popoverForeground", displayName: "Popover Foreground" }
|
|
2880
|
+
]
|
|
2881
|
+
},
|
|
2882
|
+
{
|
|
2883
|
+
key: "muted",
|
|
2884
|
+
label: "Muted Colors",
|
|
2885
|
+
colors: [
|
|
2886
|
+
{ name: "muted", displayName: "Muted" },
|
|
2887
|
+
{ name: "mutedForeground", displayName: "Muted Foreground" }
|
|
2888
|
+
]
|
|
2889
|
+
},
|
|
2890
|
+
{
|
|
2891
|
+
key: "border",
|
|
2892
|
+
label: "Border & Input Colors",
|
|
2893
|
+
colors: [
|
|
2894
|
+
{ name: "border", displayName: "Border" },
|
|
2895
|
+
{ name: "input", displayName: "Input" },
|
|
2896
|
+
{ name: "ring", displayName: "Ring" }
|
|
2897
|
+
]
|
|
2898
|
+
},
|
|
2899
|
+
{
|
|
2900
|
+
key: "destructive",
|
|
2901
|
+
label: "Destructive Colors",
|
|
2902
|
+
colors: [{ name: "destructive", displayName: "Destructive" }]
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
key: "shadows",
|
|
2906
|
+
label: "Shadow Styles",
|
|
2907
|
+
colors: [
|
|
2908
|
+
{ name: "shadow2xs", displayName: "Shadow 2XS" },
|
|
2909
|
+
{ name: "shadowXs", displayName: "Shadow XS" },
|
|
2910
|
+
{ name: "shadowSm", displayName: "Shadow SM" },
|
|
2911
|
+
{ name: "shadowMd", displayName: "Shadow MD" },
|
|
2912
|
+
{ name: "shadowLg", displayName: "Shadow LG" },
|
|
2913
|
+
{ name: "shadowXl", displayName: "Shadow XL" },
|
|
2914
|
+
{ name: "shadow2xl", displayName: "Shadow 2XL" }
|
|
2915
|
+
]
|
|
2916
|
+
}
|
|
2917
|
+
];
|
|
2918
|
+
var paletteColorOrder = [
|
|
2919
|
+
"primary",
|
|
2920
|
+
"accent",
|
|
2921
|
+
"secondary",
|
|
2922
|
+
"border"
|
|
2923
|
+
];
|
|
2924
|
+
function capitalize(value) {
|
|
2925
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
2926
|
+
}
|
|
2927
|
+
function copyName(baseName, taken) {
|
|
2928
|
+
const base = `${baseName} - Copy`;
|
|
2929
|
+
if (!taken.includes(base)) return base;
|
|
2930
|
+
let counter = 2;
|
|
2931
|
+
while (taken.includes(`${base} ${counter}`)) counter++;
|
|
2932
|
+
return `${base} ${counter}`;
|
|
2933
|
+
}
|
|
2934
|
+
function ThemeEditor({
|
|
2935
|
+
theme,
|
|
2936
|
+
appliedThemeId,
|
|
2937
|
+
systemThemes,
|
|
2938
|
+
customThemes,
|
|
2939
|
+
colorMode,
|
|
2940
|
+
onColorModeChange,
|
|
2941
|
+
onPreview,
|
|
2942
|
+
onApply,
|
|
2943
|
+
onCustomThemesChange
|
|
2944
|
+
}) {
|
|
2945
|
+
const [navigationStack, setNavigationStack] = useState(
|
|
2946
|
+
[]
|
|
2947
|
+
);
|
|
2948
|
+
const [collapsedCategories, setCollapsedCategories] = useState(() => {
|
|
2949
|
+
const initial = {};
|
|
2950
|
+
typographyCategories.forEach((category) => {
|
|
2951
|
+
initial[category.name] = true;
|
|
2952
|
+
});
|
|
2953
|
+
return initial;
|
|
2954
|
+
});
|
|
2955
|
+
const navigateTo = useCallback((item) => {
|
|
2956
|
+
setNavigationStack((prev) => [...prev, item]);
|
|
2957
|
+
}, []);
|
|
2958
|
+
const navigateBack = useCallback(() => {
|
|
2959
|
+
setNavigationStack((prev) => prev.slice(0, -1));
|
|
2960
|
+
}, []);
|
|
2961
|
+
const toggleCategory = useCallback((categoryName) => {
|
|
2962
|
+
setCollapsedCategories((prev) => ({
|
|
2963
|
+
...prev,
|
|
2964
|
+
[categoryName]: !prev[categoryName]
|
|
2965
|
+
}));
|
|
2966
|
+
}, []);
|
|
2967
|
+
const applyStyle = useCallback(
|
|
2968
|
+
(style) => {
|
|
2969
|
+
const updated = { ...theme, theme: style };
|
|
2970
|
+
onApply(updated);
|
|
2971
|
+
onCustomThemesChange(
|
|
2972
|
+
customThemes.map((t) => t.id === updated.id ? updated : t)
|
|
2973
|
+
);
|
|
2974
|
+
},
|
|
2975
|
+
[theme, customThemes, onApply, onCustomThemesChange]
|
|
2976
|
+
);
|
|
2977
|
+
const startEditing = useCallback(
|
|
2978
|
+
(themeItem, isSystem) => {
|
|
2979
|
+
let target = themeItem;
|
|
2980
|
+
if (isSystem) {
|
|
2981
|
+
const takenNames = [...systemThemes, ...customThemes].map(
|
|
2982
|
+
(t) => t.name
|
|
2983
|
+
);
|
|
2984
|
+
target = {
|
|
2985
|
+
id: generateUniqueId(),
|
|
2986
|
+
name: copyName(themeItem.name, takenNames),
|
|
2987
|
+
theme: JSON.parse(JSON.stringify(themeItem.theme))
|
|
2988
|
+
};
|
|
2989
|
+
onCustomThemesChange([...customThemes, target]);
|
|
2990
|
+
}
|
|
2991
|
+
onApply(target);
|
|
2992
|
+
setNavigationStack([{ id: "categories", label: target.name }]);
|
|
2993
|
+
},
|
|
2994
|
+
[systemThemes, customThemes, onApply, onCustomThemesChange]
|
|
2995
|
+
);
|
|
2996
|
+
const renderDrillButton = (label, onClick) => /* @__PURE__ */ jsxs(
|
|
2997
|
+
"button",
|
|
2998
|
+
{
|
|
2999
|
+
onClick,
|
|
3000
|
+
className: "group flex w-full items-center justify-between rounded-md px-3 py-2 text-xs transition-colors hover:bg-muted",
|
|
3001
|
+
children: [
|
|
3002
|
+
/* @__PURE__ */ jsx("span", { className: "transition-colors group-hover:text-foreground", children: label }),
|
|
3003
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" })
|
|
3004
|
+
]
|
|
3005
|
+
},
|
|
3006
|
+
label
|
|
3007
|
+
);
|
|
3008
|
+
const renderThemeCard = (themeItem, isSystem) => /* @__PURE__ */ jsx(
|
|
3009
|
+
ThemeCard,
|
|
3010
|
+
{
|
|
3011
|
+
name: themeItem.name,
|
|
3012
|
+
palette: paletteColorOrder.map((key) => themeItem.theme.colors.light[key]).filter(Boolean),
|
|
3013
|
+
selected: themeItem.id === theme.id,
|
|
3014
|
+
active: themeItem.id === appliedThemeId,
|
|
3015
|
+
onPreview: () => onPreview(themeItem),
|
|
3016
|
+
onUse: () => onApply(themeItem),
|
|
3017
|
+
onEdit: () => startEditing(themeItem, isSystem)
|
|
3018
|
+
},
|
|
3019
|
+
themeItem.id
|
|
3020
|
+
);
|
|
3021
|
+
const renderRootView = () => /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3022
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: "System Themes" }),
|
|
3023
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: systemThemes.map(
|
|
3024
|
+
(themeItem) => renderThemeCard(themeItem, true)
|
|
3025
|
+
) }),
|
|
3026
|
+
/* @__PURE__ */ jsx("hr", { className: "my-4 border-muted" }),
|
|
3027
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: "Custom Themes" }),
|
|
3028
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: customThemes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "No custom themes yet" }) : customThemes.map(
|
|
3029
|
+
(themeItem) => renderThemeCard(themeItem, false)
|
|
3030
|
+
) })
|
|
3031
|
+
] });
|
|
3032
|
+
const renderCategoriesView = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: sections.map((section) => /* @__PURE__ */ jsxs(
|
|
3033
|
+
"button",
|
|
3034
|
+
{
|
|
3035
|
+
onClick: () => navigateTo({
|
|
3036
|
+
id: section.id,
|
|
3037
|
+
label: section.label
|
|
3038
|
+
}),
|
|
3039
|
+
className: "group flex w-full items-center justify-between rounded-md px-3 py-2 text-sm transition-colors hover:bg-muted",
|
|
3040
|
+
children: [
|
|
3041
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3042
|
+
/* @__PURE__ */ jsx("div", { className: "text-muted-foreground transition-colors group-hover:text-foreground", children: section.icon }),
|
|
3043
|
+
/* @__PURE__ */ jsx("span", { className: "transition-colors group-hover:text-foreground", children: section.label })
|
|
3044
|
+
] }),
|
|
3045
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" })
|
|
3046
|
+
]
|
|
3047
|
+
},
|
|
3048
|
+
section.id
|
|
3049
|
+
)) });
|
|
3050
|
+
const renderColorsView = () => {
|
|
3051
|
+
const colors = theme.theme.colors[colorMode];
|
|
3052
|
+
const updateColor = (name, value) => {
|
|
3053
|
+
applyStyle({
|
|
3054
|
+
...theme.theme,
|
|
3055
|
+
colors: {
|
|
3056
|
+
...theme.theme.colors,
|
|
3057
|
+
[colorMode]: {
|
|
3058
|
+
...theme.theme.colors[colorMode],
|
|
3059
|
+
[name]: value
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
});
|
|
3063
|
+
};
|
|
3064
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3065
|
+
/* @__PURE__ */ jsx(
|
|
3066
|
+
"div",
|
|
3067
|
+
{
|
|
3068
|
+
role: "tablist",
|
|
3069
|
+
className: "grid w-full grid-cols-2 rounded-md border border-input bg-muted p-0.5",
|
|
3070
|
+
children: ["light", "dark"].map((mode) => /* @__PURE__ */ jsx(
|
|
3071
|
+
"button",
|
|
3072
|
+
{
|
|
3073
|
+
type: "button",
|
|
3074
|
+
role: "tab",
|
|
3075
|
+
"aria-selected": colorMode === mode,
|
|
3076
|
+
className: cn(
|
|
3077
|
+
"rounded-sm px-2 py-1 text-xs font-medium transition-colors",
|
|
3078
|
+
colorMode === mode ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
|
|
3079
|
+
),
|
|
3080
|
+
onClick: () => onColorModeChange(mode),
|
|
3081
|
+
children: capitalize(mode)
|
|
3082
|
+
},
|
|
3083
|
+
mode
|
|
3084
|
+
))
|
|
3085
|
+
}
|
|
3086
|
+
),
|
|
3087
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-4", children: colorCategories.map((category) => /* @__PURE__ */ jsx(
|
|
3088
|
+
AccordionSection,
|
|
3089
|
+
{
|
|
3090
|
+
title: category.label,
|
|
3091
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: category.colors.map(
|
|
3092
|
+
(color) => category.key === "shadows" ? /* @__PURE__ */ jsxs(
|
|
3093
|
+
"div",
|
|
3094
|
+
{
|
|
3095
|
+
className: "space-y-1.5",
|
|
3096
|
+
children: [
|
|
3097
|
+
/* @__PURE__ */ jsx(PanelLabel, { children: color.displayName }),
|
|
3098
|
+
/* @__PURE__ */ jsx(
|
|
3099
|
+
PanelInput2,
|
|
3100
|
+
{
|
|
3101
|
+
value: colors[color.name],
|
|
3102
|
+
onChange: (e) => updateColor(
|
|
3103
|
+
color.name,
|
|
3104
|
+
e.target.value
|
|
3105
|
+
)
|
|
3106
|
+
}
|
|
3107
|
+
)
|
|
3108
|
+
]
|
|
3109
|
+
},
|
|
3110
|
+
color.name
|
|
3111
|
+
) : /* @__PURE__ */ jsx(
|
|
3112
|
+
ColorSelector,
|
|
3113
|
+
{
|
|
3114
|
+
title: color.displayName,
|
|
3115
|
+
value: colors[color.name],
|
|
3116
|
+
onChange: (value) => updateColor(
|
|
3117
|
+
color.name,
|
|
3118
|
+
value ?? ""
|
|
3119
|
+
),
|
|
3120
|
+
allowReset: false
|
|
3121
|
+
},
|
|
3122
|
+
color.name
|
|
3123
|
+
)
|
|
3124
|
+
) })
|
|
3125
|
+
},
|
|
3126
|
+
category.key
|
|
3127
|
+
)) })
|
|
3128
|
+
] });
|
|
3129
|
+
};
|
|
3130
|
+
const renderTypographyList = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: typographyCategories.map((category) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
|
|
3131
|
+
/* @__PURE__ */ jsxs(
|
|
3132
|
+
"button",
|
|
3133
|
+
{
|
|
3134
|
+
type: "button",
|
|
3135
|
+
className: "flex w-full items-center rounded px-2 py-1.5 text-sm transition-colors hover:bg-muted",
|
|
3136
|
+
onClick: () => toggleCategory(category.name),
|
|
3137
|
+
children: [
|
|
3138
|
+
/* @__PURE__ */ jsx(
|
|
3139
|
+
ChevronDown,
|
|
3140
|
+
{
|
|
3141
|
+
className: cn(
|
|
3142
|
+
"mr-2 h-4 w-4 transition-transform",
|
|
3143
|
+
collapsedCategories[category.name] ? "rotate-[-90deg]" : "rotate-0"
|
|
3144
|
+
)
|
|
3145
|
+
}
|
|
3146
|
+
),
|
|
3147
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: category.name })
|
|
3148
|
+
]
|
|
3149
|
+
}
|
|
3150
|
+
),
|
|
3151
|
+
!collapsedCategories[category.name] && /* @__PURE__ */ jsx("div", { children: category.items.map(
|
|
3152
|
+
(typography) => renderDrillButton(
|
|
3153
|
+
typographyDisplayNames[typography] || capitalize(typography),
|
|
3154
|
+
() => navigateTo({
|
|
3155
|
+
id: typography,
|
|
3156
|
+
label: typographyDisplayNames[typography] || capitalize(typography)
|
|
3157
|
+
})
|
|
3158
|
+
)
|
|
3159
|
+
) })
|
|
3160
|
+
] }, category.name)) });
|
|
3161
|
+
const renderInteractivesList = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: Object.keys(interactiveDisplayNames).map(
|
|
3162
|
+
(interactive) => renderDrillButton(
|
|
3163
|
+
interactiveDisplayNames[interactive] || capitalize(interactive),
|
|
3164
|
+
() => navigateTo({
|
|
3165
|
+
id: interactive,
|
|
3166
|
+
label: interactiveDisplayNames[interactive] || capitalize(interactive)
|
|
3167
|
+
})
|
|
3168
|
+
)
|
|
3169
|
+
) });
|
|
3170
|
+
const renderStructureList = () => /* @__PURE__ */ jsx("div", { className: "space-y-1", children: Object.keys(structureDisplayNames).map(
|
|
3171
|
+
(structure) => renderDrillButton(
|
|
3172
|
+
structureDisplayNames[structure] || capitalize(structure),
|
|
3173
|
+
() => navigateTo({
|
|
3174
|
+
id: structure,
|
|
3175
|
+
label: structureDisplayNames[structure] || capitalize(structure)
|
|
3176
|
+
})
|
|
3177
|
+
)
|
|
3178
|
+
) });
|
|
3179
|
+
const getCurrentView = () => {
|
|
3180
|
+
const currentItem = navigationStack[navigationStack.length - 1];
|
|
3181
|
+
const parentItem = navigationStack[navigationStack.length - 2];
|
|
3182
|
+
if (!currentItem) {
|
|
3183
|
+
return renderRootView();
|
|
3184
|
+
}
|
|
3185
|
+
if (parentItem?.id === "typography" && currentItem.id in theme.theme.typography) {
|
|
3186
|
+
const key = currentItem.id;
|
|
3187
|
+
return /* @__PURE__ */ jsx(
|
|
3188
|
+
TypographySelector,
|
|
3189
|
+
{
|
|
3190
|
+
value: theme.theme.typography[key],
|
|
3191
|
+
onChange: (value) => applyStyle({
|
|
3192
|
+
...theme.theme,
|
|
3193
|
+
typography: {
|
|
3194
|
+
...theme.theme.typography,
|
|
3195
|
+
[key]: value
|
|
3196
|
+
}
|
|
3197
|
+
})
|
|
3198
|
+
}
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
if (parentItem?.id === "interactives" && currentItem.id in theme.theme.interactives) {
|
|
3202
|
+
return /* @__PURE__ */ jsx(
|
|
3203
|
+
InteractiveSelector,
|
|
3204
|
+
{
|
|
3205
|
+
type: currentItem.id,
|
|
3206
|
+
theme: theme.theme,
|
|
3207
|
+
onChange: applyStyle
|
|
3208
|
+
}
|
|
3209
|
+
);
|
|
3210
|
+
}
|
|
3211
|
+
if (parentItem?.id === "structure" && currentItem.id in theme.theme.structure) {
|
|
3212
|
+
return /* @__PURE__ */ jsx(
|
|
3213
|
+
StructureSelector,
|
|
3214
|
+
{
|
|
3215
|
+
type: currentItem.id,
|
|
3216
|
+
theme: theme.theme,
|
|
3217
|
+
onChange: applyStyle
|
|
3218
|
+
}
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
switch (currentItem.id) {
|
|
3222
|
+
case "categories":
|
|
3223
|
+
return renderCategoriesView();
|
|
3224
|
+
case "colors":
|
|
3225
|
+
return renderColorsView();
|
|
3226
|
+
case "typography":
|
|
3227
|
+
return renderTypographyList();
|
|
3228
|
+
case "interactives":
|
|
3229
|
+
return renderInteractivesList();
|
|
3230
|
+
case "structure":
|
|
3231
|
+
return renderStructureList();
|
|
3232
|
+
default:
|
|
3233
|
+
return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Coming soon" });
|
|
3234
|
+
}
|
|
3235
|
+
};
|
|
3236
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3237
|
+
navigationStack.length === 0 && /* @__PURE__ */ jsx("div", { className: "space-y-4", children: getCurrentView() }),
|
|
3238
|
+
navigationStack.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3239
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pb-2", children: [
|
|
3240
|
+
/* @__PURE__ */ jsx(
|
|
3241
|
+
"button",
|
|
3242
|
+
{
|
|
3243
|
+
onClick: navigateBack,
|
|
3244
|
+
className: "flex items-center rounded-md p-1 transition-colors hover:bg-muted focus:outline-none",
|
|
3245
|
+
"aria-label": "Back",
|
|
3246
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
3247
|
+
}
|
|
3248
|
+
),
|
|
3249
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: navigationStack[navigationStack.length - 1].label })
|
|
3250
|
+
] }),
|
|
3251
|
+
/* @__PURE__ */ jsx("div", { className: "py-2", children: getCurrentView() })
|
|
3252
|
+
] })
|
|
3253
|
+
] });
|
|
3254
|
+
}
|
|
3255
|
+
function SeoEditor({
|
|
3256
|
+
seo,
|
|
3257
|
+
onChange
|
|
3258
|
+
}) {
|
|
3259
|
+
const update = (next) => onChange({ ...seo, ...next });
|
|
3260
|
+
const imageUrl = seo.socialImage?.file;
|
|
3261
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
3262
|
+
/* @__PURE__ */ jsxs(FieldGroup, { title: "Search results", children: [
|
|
3263
|
+
/* @__PURE__ */ jsx(
|
|
3264
|
+
TextField,
|
|
3265
|
+
{
|
|
3266
|
+
label: "Title",
|
|
3267
|
+
value: seo.title,
|
|
3268
|
+
placeholder: "Page title",
|
|
3269
|
+
onChange: (title) => update({ title })
|
|
3270
|
+
}
|
|
3271
|
+
),
|
|
3272
|
+
/* @__PURE__ */ jsx(
|
|
3273
|
+
TextField,
|
|
3274
|
+
{
|
|
3275
|
+
label: "Description",
|
|
3276
|
+
multiline: true,
|
|
3277
|
+
value: seo.description,
|
|
3278
|
+
placeholder: "A short summary of this page",
|
|
3279
|
+
onChange: (description) => update({ description })
|
|
3280
|
+
}
|
|
3281
|
+
),
|
|
3282
|
+
/* @__PURE__ */ jsx(
|
|
3283
|
+
ToggleField,
|
|
3284
|
+
{
|
|
3285
|
+
label: "Search engine visibility",
|
|
3286
|
+
value: seo.robotsAllowed ?? true,
|
|
3287
|
+
onChange: (robotsAllowed) => update({ robotsAllowed })
|
|
3288
|
+
}
|
|
3289
|
+
)
|
|
3290
|
+
] }),
|
|
3291
|
+
/* @__PURE__ */ jsxs(FieldGroup, { title: "Social card image", children: [
|
|
3292
|
+
/* @__PURE__ */ jsx(
|
|
3293
|
+
TextField,
|
|
3294
|
+
{
|
|
3295
|
+
label: "Image URL",
|
|
3296
|
+
value: imageUrl,
|
|
3297
|
+
placeholder: "https://...",
|
|
3298
|
+
onChange: (file) => update({
|
|
3299
|
+
socialImage: file ? { mediaId: file, file } : void 0
|
|
3300
|
+
})
|
|
3301
|
+
}
|
|
3302
|
+
),
|
|
3303
|
+
imageUrl && /* @__PURE__ */ jsx(
|
|
3304
|
+
"img",
|
|
3305
|
+
{
|
|
3306
|
+
src: imageUrl,
|
|
3307
|
+
alt: "Social card preview",
|
|
3308
|
+
className: "aspect-[1200/630] w-full rounded-md border border-border object-cover"
|
|
3309
|
+
}
|
|
3310
|
+
),
|
|
3311
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Shown when the page is shared on social networks. Recommended size 1200\xD7630." })
|
|
3312
|
+
] })
|
|
3313
|
+
] });
|
|
3314
|
+
}
|
|
3315
|
+
function PageBuilder({
|
|
3316
|
+
initialLayout,
|
|
3317
|
+
initialTheme,
|
|
3318
|
+
initialSeo,
|
|
3319
|
+
themes: providedThemes,
|
|
3320
|
+
pageData,
|
|
3321
|
+
onChange,
|
|
3322
|
+
allowThemeEditing = true,
|
|
3323
|
+
linkComponent,
|
|
3324
|
+
className
|
|
3325
|
+
}) {
|
|
3326
|
+
const [layout, setLayout] = useState(
|
|
3327
|
+
() => initialLayout ? ensureRequiredSharedWidgets(initialLayout) : createDefaultLayout()
|
|
3328
|
+
);
|
|
3329
|
+
const [theme, setTheme] = useState(
|
|
3330
|
+
() => initialTheme ?? getDefaultTheme()
|
|
3331
|
+
);
|
|
3332
|
+
const [previewTheme, setPreviewTheme] = useState(null);
|
|
3333
|
+
const paintedTheme = previewTheme ?? theme;
|
|
3334
|
+
const [seo, setSeo] = useState(
|
|
3335
|
+
() => initialSeo ?? { robotsAllowed: true }
|
|
3336
|
+
);
|
|
3337
|
+
const systemThemes = providedThemes?.system ?? themes;
|
|
3338
|
+
const [customThemes, setCustomThemes] = useState(
|
|
3339
|
+
() => providedThemes?.custom ?? []
|
|
3340
|
+
);
|
|
3341
|
+
const [mode, setMode] = useState("system");
|
|
3342
|
+
const [systemPrefersDark, setSystemPrefersDark] = useState(false);
|
|
3343
|
+
const [device, setDevice] = useState("desktop");
|
|
3344
|
+
const [panel, setPanel] = useState(null);
|
|
3345
|
+
const [insertAfter, setInsertAfter] = useState(null);
|
|
3346
|
+
const [movedWidgetId, setMovedWidgetId] = useState(null);
|
|
3347
|
+
const movedTimeoutRef = useRef(null);
|
|
3348
|
+
useEffect(() => {
|
|
3349
|
+
return () => {
|
|
3350
|
+
if (movedTimeoutRef.current) clearTimeout(movedTimeoutRef.current);
|
|
3351
|
+
};
|
|
3352
|
+
}, []);
|
|
3353
|
+
useEffect(() => {
|
|
3354
|
+
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
3355
|
+
const apply = () => setSystemPrefersDark(media.matches);
|
|
3356
|
+
apply();
|
|
3357
|
+
media.addEventListener("change", apply);
|
|
3358
|
+
return () => media.removeEventListener("change", apply);
|
|
3359
|
+
}, []);
|
|
3360
|
+
const resolvedMode = resolveColorMode(mode, systemPrefersDark);
|
|
3361
|
+
const onChangeRef = useRef(onChange);
|
|
3362
|
+
onChangeRef.current = onChange;
|
|
3363
|
+
const emitChange = useMemo(
|
|
3364
|
+
() => debounce(
|
|
3365
|
+
(state) => onChangeRef.current?.(state),
|
|
3366
|
+
500
|
|
3367
|
+
),
|
|
3368
|
+
[]
|
|
3369
|
+
);
|
|
3370
|
+
useEffect(() => {
|
|
3371
|
+
emitChange({ layout, theme, seo, customThemes });
|
|
3372
|
+
}, [layout, theme, seo, customThemes]);
|
|
3373
|
+
const addableBlocks = useMemo(() => getAddableBlocks(), []);
|
|
3374
|
+
function updateWidgetSettings(widgetId, settings) {
|
|
3375
|
+
setLayout(
|
|
3376
|
+
(prev) => prev.map(
|
|
3377
|
+
(widget) => widget.widgetId === widgetId ? { ...widget, settings } : widget
|
|
3378
|
+
)
|
|
3379
|
+
);
|
|
3380
|
+
}
|
|
3381
|
+
function moveWidget(widgetId, direction) {
|
|
3382
|
+
const index = layout.findIndex((w) => w.widgetId === widgetId);
|
|
3383
|
+
if (index === -1 || ["header", "footer"].includes(layout[index].name) || !layout[index].moveable) {
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
const targetIndex = index + direction;
|
|
3387
|
+
if (targetIndex < 0 || targetIndex >= layout.length || ["header", "footer"].includes(layout[targetIndex].name)) {
|
|
3388
|
+
return;
|
|
3389
|
+
}
|
|
3390
|
+
setLayout((prev) => {
|
|
3391
|
+
const next = prev.slice();
|
|
3392
|
+
[next[index], next[targetIndex]] = [next[targetIndex], next[index]];
|
|
3393
|
+
return next;
|
|
3394
|
+
});
|
|
3395
|
+
if (movedTimeoutRef.current) clearTimeout(movedTimeoutRef.current);
|
|
3396
|
+
setMovedWidgetId(widgetId);
|
|
3397
|
+
movedTimeoutRef.current = setTimeout(
|
|
3398
|
+
() => setMovedWidgetId(null),
|
|
3399
|
+
1500
|
|
3400
|
+
);
|
|
3401
|
+
}
|
|
3402
|
+
function deleteWidget(widgetId) {
|
|
3403
|
+
setLayout((prev) => {
|
|
3404
|
+
const widget = prev.find((item) => item.widgetId === widgetId);
|
|
3405
|
+
if (!widget || !widget.deletable || ["header", "footer"].includes(widget.name)) {
|
|
3406
|
+
return prev;
|
|
3407
|
+
}
|
|
3408
|
+
return prev.filter((item) => item.widgetId !== widgetId);
|
|
3409
|
+
});
|
|
3410
|
+
setPanel(
|
|
3411
|
+
(current) => current?.mode === "block" && current.widgetId === widgetId ? null : current
|
|
3412
|
+
);
|
|
3413
|
+
}
|
|
3414
|
+
function addWidget(name) {
|
|
3415
|
+
const instance = createWidgetInstance(name);
|
|
3416
|
+
setLayout((prev) => {
|
|
3417
|
+
if (insertAfter === "end" || insertAfter === null) {
|
|
3418
|
+
const footerIndex = prev.findIndex((w) => w.name === "footer");
|
|
3419
|
+
if (footerIndex === -1) return [...prev, instance];
|
|
3420
|
+
return [
|
|
3421
|
+
...prev.slice(0, footerIndex),
|
|
3422
|
+
instance,
|
|
3423
|
+
...prev.slice(footerIndex)
|
|
3424
|
+
];
|
|
3425
|
+
}
|
|
3426
|
+
const afterIndex = prev.findIndex(
|
|
3427
|
+
(w) => w.widgetId === insertAfter
|
|
3428
|
+
);
|
|
3429
|
+
if (afterIndex === -1) return [...prev, instance];
|
|
3430
|
+
return [
|
|
3431
|
+
...prev.slice(0, afterIndex + 1),
|
|
3432
|
+
instance,
|
|
3433
|
+
...prev.slice(afterIndex + 1)
|
|
3434
|
+
];
|
|
3435
|
+
});
|
|
3436
|
+
setInsertAfter(null);
|
|
3437
|
+
setPanel({ mode: "block", widgetId: instance.widgetId });
|
|
3438
|
+
}
|
|
3439
|
+
const selectedWidgetId = panel?.mode === "block" ? panel.widgetId : null;
|
|
3440
|
+
const selectedInstance = layout.find(
|
|
3441
|
+
(w) => w.widgetId === selectedWidgetId
|
|
3442
|
+
);
|
|
3443
|
+
const selectedDefinition = selectedInstance ? getWidget(selectedInstance.name) : void 0;
|
|
3444
|
+
const showBlockPanel = Boolean(
|
|
3445
|
+
panel?.mode === "block" && selectedInstance && selectedDefinition?.editor
|
|
3446
|
+
);
|
|
3447
|
+
const showThemePanel = allowThemeEditing && panel?.mode === "theme";
|
|
3448
|
+
const showSeoPanel = panel?.mode === "seo";
|
|
3449
|
+
const panelOpen = showBlockPanel || showThemePanel || showSeoPanel;
|
|
3450
|
+
function closePanel() {
|
|
3451
|
+
setPreviewTheme(null);
|
|
3452
|
+
setPanel(null);
|
|
3453
|
+
}
|
|
3454
|
+
return /* @__PURE__ */ jsxs(
|
|
3455
|
+
"div",
|
|
3456
|
+
{
|
|
3457
|
+
className: cn(
|
|
3458
|
+
// Same token scope as PageRenderer. Builder chrome (top bar,
|
|
3459
|
+
// add-block overlay, device-frame pattern) lives outside the
|
|
3460
|
+
// canvas but still uses bg-background / --muted-foreground.
|
|
3461
|
+
"pb-theme flex h-full w-full flex-col overflow-hidden bg-background text-foreground",
|
|
3462
|
+
resolvedMode === "dark" && "dark",
|
|
3463
|
+
className
|
|
3464
|
+
),
|
|
3465
|
+
children: [
|
|
3466
|
+
/* @__PURE__ */ jsx(
|
|
3467
|
+
BuilderTopBar,
|
|
3468
|
+
{
|
|
3469
|
+
resolvedMode,
|
|
3470
|
+
onModeChange: setMode,
|
|
3471
|
+
device,
|
|
3472
|
+
onDeviceChange: setDevice,
|
|
3473
|
+
onOpenTheme: () => setPanel({ mode: "theme" }),
|
|
3474
|
+
onOpenSeo: () => setPanel({ mode: "seo" }),
|
|
3475
|
+
onAddBlock: () => setInsertAfter("end"),
|
|
3476
|
+
allowThemeEditing
|
|
3477
|
+
}
|
|
3478
|
+
),
|
|
3479
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 w-full min-w-0 flex-1 gap-4 overflow-hidden bg-muted/10 p-4", children: [
|
|
3480
|
+
/* @__PURE__ */ jsx(
|
|
3481
|
+
"div",
|
|
3482
|
+
{
|
|
3483
|
+
className: cn(
|
|
3484
|
+
"flex min-h-0 min-w-0 justify-center overflow-hidden rounded-xl border bg-background/98 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-background/80",
|
|
3485
|
+
panelOpen ? "flex-1" : "w-full"
|
|
3486
|
+
),
|
|
3487
|
+
style: {
|
|
3488
|
+
backgroundImage: "radial-gradient(color-mix(in srgb, var(--muted-foreground) 25%, transparent) 1px, transparent 1px)",
|
|
3489
|
+
backgroundSize: "14px 14px"
|
|
3490
|
+
},
|
|
3491
|
+
children: /* @__PURE__ */ jsx(
|
|
3492
|
+
PreviewFrame,
|
|
3493
|
+
{
|
|
3494
|
+
className: cn(
|
|
3495
|
+
"h-full w-full max-w-full transition-[max-width] duration-300",
|
|
3496
|
+
device === "mobile" && "max-w-[390px] border-x",
|
|
3497
|
+
device === "tablet" && "max-w-[768px] border-x"
|
|
3498
|
+
),
|
|
3499
|
+
children: /* @__PURE__ */ jsx(
|
|
3500
|
+
PageRenderer,
|
|
3501
|
+
{
|
|
3502
|
+
layout,
|
|
3503
|
+
theme: paintedTheme,
|
|
3504
|
+
pageData,
|
|
3505
|
+
nextTheme: resolvedMode,
|
|
3506
|
+
themeMode: mode,
|
|
3507
|
+
onThemeModeChange: setMode,
|
|
3508
|
+
editing: true,
|
|
3509
|
+
selectedWidgetId: selectedWidgetId ?? void 0,
|
|
3510
|
+
highlightedWidgetId: movedWidgetId ?? void 0,
|
|
3511
|
+
onSelectWidget: (id) => setPanel({ mode: "block", widgetId: id }),
|
|
3512
|
+
onMoveWidgetUp: (id) => moveWidget(id, -1),
|
|
3513
|
+
onMoveWidgetDown: (id) => moveWidget(id, 1),
|
|
3514
|
+
onDeleteWidget: deleteWidget,
|
|
3515
|
+
onAddWidgetBelow: (id) => setInsertAfter(id),
|
|
3516
|
+
linkComponent
|
|
3517
|
+
}
|
|
3518
|
+
)
|
|
3519
|
+
}
|
|
3520
|
+
)
|
|
3521
|
+
}
|
|
3522
|
+
),
|
|
3523
|
+
showBlockPanel && selectedInstance && selectedDefinition?.editor && /* @__PURE__ */ jsx(
|
|
3524
|
+
SettingsPanel,
|
|
3525
|
+
{
|
|
3526
|
+
title: selectedDefinition.metadata.displayName,
|
|
3527
|
+
eyebrow: "Block settings",
|
|
3528
|
+
description: selectedDefinition.metadata.description,
|
|
3529
|
+
icon: LayoutTemplate,
|
|
3530
|
+
onClose: closePanel,
|
|
3531
|
+
children: /* @__PURE__ */ jsx(
|
|
3532
|
+
BlockSettings,
|
|
3533
|
+
{
|
|
3534
|
+
instance: selectedInstance,
|
|
3535
|
+
definition: selectedDefinition,
|
|
3536
|
+
theme,
|
|
3537
|
+
onChange: (settings) => updateWidgetSettings(
|
|
3538
|
+
selectedInstance.widgetId,
|
|
3539
|
+
settings
|
|
3540
|
+
)
|
|
3541
|
+
},
|
|
3542
|
+
selectedInstance.widgetId
|
|
3543
|
+
)
|
|
3544
|
+
}
|
|
3545
|
+
),
|
|
3546
|
+
showThemePanel && /* @__PURE__ */ jsx(SettingsPanel, { title: "Theme", onClose: closePanel, children: /* @__PURE__ */ jsx(
|
|
3547
|
+
ThemeEditor,
|
|
3548
|
+
{
|
|
3549
|
+
theme: paintedTheme,
|
|
3550
|
+
appliedThemeId: theme.id,
|
|
3551
|
+
systemThemes,
|
|
3552
|
+
customThemes,
|
|
3553
|
+
colorMode: resolvedMode,
|
|
3554
|
+
onColorModeChange: setMode,
|
|
3555
|
+
onPreview: setPreviewTheme,
|
|
3556
|
+
onApply: (next) => {
|
|
3557
|
+
setPreviewTheme(null);
|
|
3558
|
+
setTheme(next);
|
|
3559
|
+
},
|
|
3560
|
+
onCustomThemesChange: setCustomThemes
|
|
3561
|
+
}
|
|
3562
|
+
) }),
|
|
3563
|
+
showSeoPanel && /* @__PURE__ */ jsx(SettingsPanel, { title: "SEO", onClose: closePanel, children: /* @__PURE__ */ jsx(SeoEditor, { seo, onChange: setSeo }) })
|
|
3564
|
+
] }),
|
|
3565
|
+
insertAfter !== null && /* @__PURE__ */ jsx(
|
|
3566
|
+
BlockPicker,
|
|
3567
|
+
{
|
|
3568
|
+
blocks: addableBlocks,
|
|
3569
|
+
onPick: addWidget,
|
|
3570
|
+
onClose: () => setInsertAfter(null)
|
|
3571
|
+
}
|
|
3572
|
+
)
|
|
3573
|
+
]
|
|
3574
|
+
}
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
export { BlockPicker, BuilderTopBar, ColorSelector, ContentPaddingSelector, MaxWidthSelector, PATTERN_PRESETS, PageBuilder, PageBuilderPropertyHeader, PageBuilderSlider, SectionBackgroundPanel, SeoEditor, SettingsPanel, ThemeEditor, VerticalPaddingSelector, createDefaultLayout, createWidgetInstance, ensureRequiredSharedWidgets, getDefaultTheme, getMaxWidthTWClass, getVerticalPaddingTWClass };
|
|
3579
|
+
//# sourceMappingURL=chunk-MO76KZY2.js.map
|
|
3580
|
+
//# sourceMappingURL=chunk-MO76KZY2.js.map
|