@commercetools-demo/puck-editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,3238 @@
1
+ // src/PuckEditor.tsx
2
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState5 } from "react";
3
+ import { Puck } from "@measured/puck";
4
+ import "@measured/puck/puck.css";
5
+ import { PuckApiProvider } from "@commercetools-demo/puck-api";
6
+ import { usePuckPage } from "@commercetools-demo/puck-api";
7
+
8
+ // src/config/defaultPuckConfig.ts
9
+ import React4 from "react";
10
+
11
+ // src/components/Hero.tsx
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ var Hero = {
14
+ label: "Hero",
15
+ fields: {
16
+ heading: { type: "text", label: "Heading" },
17
+ subheading: { type: "textarea", label: "Subheading" },
18
+ backgroundImage: { type: "text", label: "Background Image URL" },
19
+ ctaText: { type: "text", label: "CTA Button Text" },
20
+ ctaUrl: { type: "text", label: "CTA Button URL" },
21
+ layout: {
22
+ type: "select",
23
+ label: "Layout",
24
+ options: [
25
+ { value: "centered", label: "Centered" },
26
+ { value: "left-aligned", label: "Left Aligned" }
27
+ ]
28
+ },
29
+ minHeight: { type: "text", label: "Min Height (CSS, e.g. 400px)" }
30
+ },
31
+ defaultProps: {
32
+ heading: "Welcome",
33
+ layout: "centered",
34
+ minHeight: "400px"
35
+ },
36
+ render: ({
37
+ heading,
38
+ subheading,
39
+ backgroundImage,
40
+ ctaText,
41
+ ctaUrl,
42
+ layout,
43
+ minHeight
44
+ }) => {
45
+ const isCenter = layout !== "left-aligned";
46
+ return /* @__PURE__ */ jsxs(
47
+ "section",
48
+ {
49
+ style: {
50
+ position: "relative",
51
+ minHeight: minHeight ?? "400px",
52
+ display: "flex",
53
+ alignItems: "center",
54
+ justifyContent: isCenter ? "center" : "flex-start",
55
+ padding: "48px 32px",
56
+ backgroundImage: backgroundImage ? `url(${backgroundImage})` : void 0,
57
+ backgroundSize: "cover",
58
+ backgroundPosition: "center",
59
+ backgroundColor: backgroundImage ? void 0 : "#1a1a2e",
60
+ color: "#fff",
61
+ boxSizing: "border-box"
62
+ },
63
+ children: [
64
+ backgroundImage && /* @__PURE__ */ jsx(
65
+ "div",
66
+ {
67
+ style: {
68
+ position: "absolute",
69
+ inset: 0,
70
+ background: "rgba(0,0,0,0.45)"
71
+ }
72
+ }
73
+ ),
74
+ /* @__PURE__ */ jsxs(
75
+ "div",
76
+ {
77
+ style: {
78
+ position: "relative",
79
+ maxWidth: "720px",
80
+ textAlign: isCenter ? "center" : "left"
81
+ },
82
+ children: [
83
+ /* @__PURE__ */ jsx("h1", { style: { margin: "0 0 16px", fontSize: "2.5rem", fontWeight: 700 }, children: heading }),
84
+ subheading && /* @__PURE__ */ jsx("p", { style: { margin: "0 0 24px", fontSize: "1.2rem", opacity: 0.85 }, children: subheading }),
85
+ ctaText && ctaUrl && /* @__PURE__ */ jsx(
86
+ "a",
87
+ {
88
+ href: ctaUrl,
89
+ style: {
90
+ display: "inline-block",
91
+ padding: "12px 28px",
92
+ background: "#e94560",
93
+ color: "#fff",
94
+ borderRadius: "4px",
95
+ textDecoration: "none",
96
+ fontWeight: 600
97
+ },
98
+ children: ctaText
99
+ }
100
+ )
101
+ ]
102
+ }
103
+ )
104
+ ]
105
+ }
106
+ );
107
+ }
108
+ };
109
+
110
+ // src/components/RichText.tsx
111
+ import { jsx as jsx2 } from "react/jsx-runtime";
112
+ var RichText = {
113
+ label: "Rich Text",
114
+ fields: {
115
+ content: {
116
+ type: "textarea",
117
+ label: "Content (HTML supported)"
118
+ },
119
+ align: {
120
+ type: "select",
121
+ label: "Text Alignment",
122
+ options: [
123
+ { value: "left", label: "Left" },
124
+ { value: "center", label: "Center" },
125
+ { value: "right", label: "Right" }
126
+ ]
127
+ },
128
+ maxWidth: { type: "text", label: "Max Width (CSS)" },
129
+ padding: { type: "text", label: "Padding (CSS)" }
130
+ },
131
+ defaultProps: {
132
+ content: "<p>Add your content here\u2026</p>",
133
+ align: "left",
134
+ padding: "32px"
135
+ },
136
+ render: ({ content, align, maxWidth, padding }) => /* @__PURE__ */ jsx2(
137
+ "div",
138
+ {
139
+ style: {
140
+ padding: padding ?? "32px",
141
+ textAlign: align ?? "left",
142
+ maxWidth,
143
+ margin: maxWidth ? "0 auto" : void 0,
144
+ boxSizing: "border-box"
145
+ },
146
+ dangerouslySetInnerHTML: { __html: content }
147
+ }
148
+ )
149
+ };
150
+
151
+ // src/components/Columns.tsx
152
+ import { DropZone } from "@measured/puck";
153
+ import { jsx as jsx3 } from "react/jsx-runtime";
154
+ var Columns = {
155
+ label: "Columns",
156
+ fields: {
157
+ columnCount: {
158
+ type: "select",
159
+ label: "Number of Columns",
160
+ options: [
161
+ { value: 2, label: "2 Columns" },
162
+ { value: 3, label: "3 Columns" },
163
+ { value: 4, label: "4 Columns" }
164
+ ]
165
+ },
166
+ gap: { type: "text", label: "Column Gap (CSS)" },
167
+ padding: { type: "text", label: "Padding (CSS)" }
168
+ },
169
+ defaultProps: {
170
+ columnCount: 2,
171
+ gap: "16px",
172
+ padding: "16px"
173
+ },
174
+ render: ({ columnCount = 2, gap = "16px", padding = "16px" }) => {
175
+ const cols = Array.from({ length: columnCount }, (_, i) => i);
176
+ return /* @__PURE__ */ jsx3(
177
+ "div",
178
+ {
179
+ style: {
180
+ display: "grid",
181
+ gridTemplateColumns: `repeat(${columnCount}, 1fr)`,
182
+ gap,
183
+ padding,
184
+ boxSizing: "border-box"
185
+ },
186
+ children: cols.map((i) => /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(DropZone, { zone: `column-${i}` }) }, i))
187
+ }
188
+ );
189
+ }
190
+ };
191
+
192
+ // src/fields/ImagePickerField.tsx
193
+ import {
194
+ useCallback,
195
+ useEffect,
196
+ useRef,
197
+ useState
198
+ } from "react";
199
+ import { useMediaLibrary } from "@commercetools-demo/puck-api";
200
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
201
+ var s = {
202
+ root: {
203
+ display: "flex",
204
+ flexDirection: "column",
205
+ gap: "8px",
206
+ fontFamily: "inherit",
207
+ fontSize: "13px"
208
+ },
209
+ preview: {
210
+ display: "flex",
211
+ alignItems: "center",
212
+ gap: "10px",
213
+ padding: "8px",
214
+ border: "1px solid #d1d5db",
215
+ borderRadius: "6px",
216
+ background: "#f9fafb"
217
+ },
218
+ previewImg: {
219
+ width: "48px",
220
+ height: "48px",
221
+ objectFit: "cover",
222
+ borderRadius: "4px",
223
+ flexShrink: 0
224
+ },
225
+ previewInfo: { flex: 1, minWidth: 0 },
226
+ previewUrl: {
227
+ fontSize: "12px",
228
+ color: "#374151",
229
+ overflow: "hidden",
230
+ textOverflow: "ellipsis",
231
+ whiteSpace: "nowrap"
232
+ },
233
+ btnRow: { display: "flex", gap: "6px", flexWrap: "wrap" },
234
+ btn: (primary) => ({
235
+ padding: "5px 12px",
236
+ borderRadius: "4px",
237
+ border: primary ? "none" : "1px solid #d1d5db",
238
+ background: primary ? "#1a1a2e" : "#fff",
239
+ color: primary ? "#fff" : "#374151",
240
+ fontWeight: 500,
241
+ fontSize: "12px",
242
+ cursor: "pointer",
243
+ whiteSpace: "nowrap"
244
+ }),
245
+ dangerBtn: {
246
+ padding: "5px 12px",
247
+ borderRadius: "4px",
248
+ border: "1px solid #fca5a5",
249
+ background: "#fff",
250
+ color: "#dc2626",
251
+ fontWeight: 500,
252
+ fontSize: "12px",
253
+ cursor: "pointer"
254
+ },
255
+ // Modal
256
+ overlay: {
257
+ position: "fixed",
258
+ inset: 0,
259
+ background: "rgba(0,0,0,0.5)",
260
+ zIndex: 9999,
261
+ display: "flex",
262
+ alignItems: "center",
263
+ justifyContent: "center"
264
+ },
265
+ modal: {
266
+ background: "#fff",
267
+ borderRadius: "8px",
268
+ width: "640px",
269
+ maxWidth: "95vw",
270
+ maxHeight: "85vh",
271
+ display: "flex",
272
+ flexDirection: "column",
273
+ boxShadow: "0 20px 60px rgba(0,0,0,0.3)"
274
+ },
275
+ modalHeader: {
276
+ display: "flex",
277
+ alignItems: "center",
278
+ justifyContent: "space-between",
279
+ padding: "16px 20px",
280
+ borderBottom: "1px solid #e5e7eb"
281
+ },
282
+ modalTitle: { margin: 0, fontSize: "16px", fontWeight: 600 },
283
+ modalClose: {
284
+ background: "none",
285
+ border: "none",
286
+ fontSize: "20px",
287
+ cursor: "pointer",
288
+ color: "#6b7280",
289
+ lineHeight: 1
290
+ },
291
+ modalBody: {
292
+ flex: 1,
293
+ overflow: "auto",
294
+ padding: "20px"
295
+ },
296
+ modalFooter: {
297
+ display: "flex",
298
+ justifyContent: "space-between",
299
+ alignItems: "center",
300
+ padding: "12px 20px",
301
+ borderTop: "1px solid #e5e7eb",
302
+ gap: "12px"
303
+ },
304
+ // Upload form
305
+ formGroup: {
306
+ display: "flex",
307
+ flexDirection: "column",
308
+ gap: "4px",
309
+ marginBottom: "12px"
310
+ },
311
+ label: { fontSize: "12px", fontWeight: 600, color: "#374151" },
312
+ input: {
313
+ padding: "7px 10px",
314
+ border: "1px solid #d1d5db",
315
+ borderRadius: "4px",
316
+ fontSize: "13px",
317
+ width: "100%",
318
+ boxSizing: "border-box"
319
+ },
320
+ dropZone: {
321
+ border: "2px dashed #d1d5db",
322
+ borderRadius: "6px",
323
+ padding: "24px",
324
+ textAlign: "center",
325
+ cursor: "pointer",
326
+ color: "#6b7280",
327
+ fontSize: "13px",
328
+ transition: "border-color .2s"
329
+ },
330
+ dropZoneActive: {
331
+ border: "2px dashed #3b82f6",
332
+ borderRadius: "6px",
333
+ padding: "24px",
334
+ textAlign: "center",
335
+ cursor: "pointer",
336
+ color: "#3b82f6",
337
+ fontSize: "13px"
338
+ },
339
+ // Media grid
340
+ grid: {
341
+ display: "grid",
342
+ gridTemplateColumns: "repeat(auto-fill, minmax(110px, 1fr))",
343
+ gap: "12px",
344
+ marginTop: "4px"
345
+ },
346
+ gridItem: (selected) => ({
347
+ display: "flex",
348
+ flexDirection: "column",
349
+ alignItems: "center",
350
+ cursor: "pointer",
351
+ border: `2px solid ${selected ? "#3b82f6" : "transparent"}`,
352
+ borderRadius: "6px",
353
+ padding: "6px",
354
+ background: selected ? "rgba(59,130,246,0.07)" : "#f9fafb"
355
+ }),
356
+ thumb: {
357
+ width: "80px",
358
+ height: "80px",
359
+ objectFit: "cover",
360
+ borderRadius: "4px",
361
+ background: "#e5e7eb"
362
+ },
363
+ thumbPlaceholder: {
364
+ width: "80px",
365
+ height: "80px",
366
+ borderRadius: "4px",
367
+ background: "#e5e7eb",
368
+ display: "flex",
369
+ alignItems: "center",
370
+ justifyContent: "center",
371
+ fontSize: "28px"
372
+ },
373
+ itemName: {
374
+ marginTop: "6px",
375
+ fontSize: "11px",
376
+ textAlign: "center",
377
+ maxWidth: "100%",
378
+ overflow: "hidden",
379
+ textOverflow: "ellipsis",
380
+ whiteSpace: "nowrap",
381
+ color: "#374151"
382
+ },
383
+ pagination: {
384
+ display: "flex",
385
+ justifyContent: "center",
386
+ alignItems: "center",
387
+ gap: "12px",
388
+ marginTop: "16px",
389
+ fontSize: "12px",
390
+ color: "#6b7280"
391
+ },
392
+ errorMsg: {
393
+ fontSize: "12px",
394
+ color: "#dc2626",
395
+ marginTop: "4px"
396
+ },
397
+ selectedInfo: {
398
+ flex: 1,
399
+ fontSize: "12px",
400
+ color: "#374151",
401
+ overflow: "hidden",
402
+ textOverflow: "ellipsis",
403
+ whiteSpace: "nowrap"
404
+ }
405
+ };
406
+ var UploadModal = ({
407
+ uploading,
408
+ error,
409
+ onUpload,
410
+ onClose
411
+ }) => {
412
+ const [file, setFile] = useState(null);
413
+ const [title, setTitle] = useState("");
414
+ const [description, setDescription] = useState("");
415
+ const [dragging, setDragging] = useState(false);
416
+ const inputRef = useRef(null);
417
+ const handleFile = (f) => {
418
+ setFile(f);
419
+ if (!title) setTitle(f.name);
420
+ };
421
+ const handleDrop = (e) => {
422
+ e.preventDefault();
423
+ setDragging(false);
424
+ const f = e.dataTransfer.files[0];
425
+ if (f) handleFile(f);
426
+ };
427
+ return /* @__PURE__ */ jsx4("div", { style: s.overlay, onClick: (e) => e.target === e.currentTarget && onClose(), children: /* @__PURE__ */ jsxs2("div", { style: s.modal, children: [
428
+ /* @__PURE__ */ jsxs2("div", { style: s.modalHeader, children: [
429
+ /* @__PURE__ */ jsx4("h3", { style: s.modalTitle, children: "Upload a file" }),
430
+ /* @__PURE__ */ jsx4("button", { style: s.modalClose, onClick: onClose, children: "\xD7" })
431
+ ] }),
432
+ /* @__PURE__ */ jsxs2("div", { style: s.modalBody, children: [
433
+ /* @__PURE__ */ jsxs2("div", { style: s.formGroup, children: [
434
+ /* @__PURE__ */ jsx4("label", { style: s.label, children: "Title" }),
435
+ /* @__PURE__ */ jsx4(
436
+ "input",
437
+ {
438
+ style: s.input,
439
+ value: title,
440
+ onChange: (e) => setTitle(e.target.value),
441
+ placeholder: "File title"
442
+ }
443
+ )
444
+ ] }),
445
+ /* @__PURE__ */ jsxs2("div", { style: s.formGroup, children: [
446
+ /* @__PURE__ */ jsx4("label", { style: s.label, children: "Description" }),
447
+ /* @__PURE__ */ jsx4(
448
+ "input",
449
+ {
450
+ style: s.input,
451
+ value: description,
452
+ onChange: (e) => setDescription(e.target.value),
453
+ placeholder: "Optional description"
454
+ }
455
+ )
456
+ ] }),
457
+ /* @__PURE__ */ jsxs2(
458
+ "div",
459
+ {
460
+ style: dragging ? s.dropZoneActive : s.dropZone,
461
+ onClick: () => inputRef.current?.click(),
462
+ onDragOver: (e) => {
463
+ e.preventDefault();
464
+ setDragging(true);
465
+ },
466
+ onDragLeave: () => setDragging(false),
467
+ onDrop: handleDrop,
468
+ children: [
469
+ file ? /* @__PURE__ */ jsxs2(Fragment, { children: [
470
+ /* @__PURE__ */ jsxs2("div", { children: [
471
+ "\u{1F4CE} ",
472
+ file.name
473
+ ] }),
474
+ /* @__PURE__ */ jsxs2("div", { style: { fontSize: "11px", marginTop: "4px", color: "#9ca3af" }, children: [
475
+ (file.size / 1024).toFixed(0),
476
+ " KB"
477
+ ] })
478
+ ] }) : /* @__PURE__ */ jsx4(Fragment, { children: /* @__PURE__ */ jsx4("div", { children: "\u{1F4C1} Click or drag & drop to select a file" }) }),
479
+ /* @__PURE__ */ jsx4(
480
+ "input",
481
+ {
482
+ ref: inputRef,
483
+ type: "file",
484
+ accept: "image/*",
485
+ style: { display: "none" },
486
+ onChange: (e) => {
487
+ const f = e.target.files?.[0];
488
+ if (f) handleFile(f);
489
+ }
490
+ }
491
+ )
492
+ ]
493
+ }
494
+ ),
495
+ error && /* @__PURE__ */ jsx4("div", { style: s.errorMsg, children: error })
496
+ ] }),
497
+ /* @__PURE__ */ jsxs2("div", { style: s.modalFooter, children: [
498
+ /* @__PURE__ */ jsx4("span", { style: s.selectedInfo, children: file?.name ?? "No file selected" }),
499
+ /* @__PURE__ */ jsxs2("div", { style: s.btnRow, children: [
500
+ /* @__PURE__ */ jsx4("button", { style: s.btn(), onClick: onClose, children: "Cancel" }),
501
+ /* @__PURE__ */ jsx4(
502
+ "button",
503
+ {
504
+ style: s.btn(true),
505
+ disabled: !file || uploading,
506
+ onClick: () => file && onUpload(file, title, description),
507
+ children: uploading ? "Uploading\u2026" : "Upload"
508
+ }
509
+ )
510
+ ] })
511
+ ] })
512
+ ] }) });
513
+ };
514
+ var LibraryModal = ({
515
+ files,
516
+ pagination,
517
+ loading,
518
+ error,
519
+ onNextPage,
520
+ onPrevPage,
521
+ onSelect,
522
+ onClose
523
+ }) => {
524
+ const [selected, setSelected] = useState(null);
525
+ const handleConfirm = () => {
526
+ if (selected) onSelect(selected);
527
+ };
528
+ return /* @__PURE__ */ jsx4("div", { style: s.overlay, onClick: (e) => e.target === e.currentTarget && onClose(), children: /* @__PURE__ */ jsxs2("div", { style: s.modal, children: [
529
+ /* @__PURE__ */ jsxs2("div", { style: s.modalHeader, children: [
530
+ /* @__PURE__ */ jsx4("h3", { style: s.modalTitle, children: "Select from Media Library" }),
531
+ /* @__PURE__ */ jsx4("button", { style: s.modalClose, onClick: onClose, children: "\xD7" })
532
+ ] }),
533
+ /* @__PURE__ */ jsxs2("div", { style: s.modalBody, children: [
534
+ loading ? /* @__PURE__ */ jsx4("div", { style: { color: "#6b7280", fontSize: "13px" }, children: "Loading\u2026" }) : files.length === 0 ? /* @__PURE__ */ jsx4("div", { style: { color: "#6b7280", fontSize: "13px" }, children: "No files found." }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
535
+ /* @__PURE__ */ jsx4("div", { style: s.grid, children: files.map((file) => /* @__PURE__ */ jsxs2(
536
+ "div",
537
+ {
538
+ style: s.gridItem(selected?.url === file.url),
539
+ onClick: () => setSelected(file),
540
+ title: file.title ?? file.name,
541
+ children: [
542
+ file.isImage ? /* @__PURE__ */ jsx4("img", { src: file.url, alt: file.name, style: s.thumb }) : /* @__PURE__ */ jsx4("div", { style: s.thumbPlaceholder, children: "\u{1F4C4}" }),
543
+ /* @__PURE__ */ jsx4("div", { style: s.itemName, children: file.title ?? file.name })
544
+ ]
545
+ },
546
+ file.url
547
+ )) }),
548
+ pagination.totalPages > 1 && /* @__PURE__ */ jsxs2("div", { style: s.pagination, children: [
549
+ /* @__PURE__ */ jsx4(
550
+ "button",
551
+ {
552
+ style: s.btn(),
553
+ disabled: pagination.currentPage <= 1,
554
+ onClick: onPrevPage,
555
+ children: "\u2190 Prev"
556
+ }
557
+ ),
558
+ /* @__PURE__ */ jsxs2("span", { children: [
559
+ pagination.currentPage,
560
+ " / ",
561
+ pagination.totalPages
562
+ ] }),
563
+ /* @__PURE__ */ jsx4(
564
+ "button",
565
+ {
566
+ style: s.btn(),
567
+ disabled: pagination.currentPage >= pagination.totalPages,
568
+ onClick: onNextPage,
569
+ children: "Next \u2192"
570
+ }
571
+ )
572
+ ] })
573
+ ] }),
574
+ error && /* @__PURE__ */ jsx4("div", { style: s.errorMsg, children: error })
575
+ ] }),
576
+ /* @__PURE__ */ jsxs2("div", { style: s.modalFooter, children: [
577
+ /* @__PURE__ */ jsx4("span", { style: s.selectedInfo, children: selected ? selected.title ?? selected.name : "Nothing selected" }),
578
+ /* @__PURE__ */ jsxs2("div", { style: s.btnRow, children: [
579
+ /* @__PURE__ */ jsx4("button", { style: s.btn(), onClick: onClose, children: "Cancel" }),
580
+ /* @__PURE__ */ jsx4(
581
+ "button",
582
+ {
583
+ style: s.btn(true),
584
+ disabled: !selected,
585
+ onClick: handleConfirm,
586
+ children: "Select"
587
+ }
588
+ )
589
+ ] })
590
+ ] })
591
+ ] }) });
592
+ };
593
+ var ImagePickerField = ({
594
+ value,
595
+ onChange,
596
+ imagesOnly = true
597
+ }) => {
598
+ const {
599
+ files,
600
+ pagination,
601
+ loading,
602
+ uploading,
603
+ error,
604
+ fetchMedia,
605
+ uploadFile,
606
+ loadNextPage,
607
+ loadPreviousPage
608
+ } = useMediaLibrary();
609
+ const [showUpload, setShowUpload] = useState(false);
610
+ const [showLibrary, setShowLibrary] = useState(false);
611
+ const extensions = imagesOnly ? ["jpg", "jpeg", "png", "gif", "webp", "svg"] : [];
612
+ const openLibrary = useCallback(() => {
613
+ void fetchMedia(extensions, 1, 20);
614
+ setShowLibrary(true);
615
+ }, [fetchMedia, extensions]);
616
+ const handleUpload = useCallback(
617
+ async (file, title, description) => {
618
+ const mediaFile = await uploadFile(file, title, description);
619
+ onChange(mediaFile.url);
620
+ setShowUpload(false);
621
+ },
622
+ [uploadFile, onChange]
623
+ );
624
+ const handleSelect = useCallback(
625
+ (file) => {
626
+ onChange(file.url);
627
+ setShowLibrary(false);
628
+ },
629
+ [onChange]
630
+ );
631
+ useEffect(() => {
632
+ const onKey = (e) => {
633
+ if (e.key === "Escape") {
634
+ setShowUpload(false);
635
+ setShowLibrary(false);
636
+ }
637
+ };
638
+ window.addEventListener("keydown", onKey);
639
+ return () => window.removeEventListener("keydown", onKey);
640
+ }, []);
641
+ return /* @__PURE__ */ jsxs2("div", { style: s.root, children: [
642
+ value ? /* @__PURE__ */ jsxs2("div", { style: s.preview, children: [
643
+ /* @__PURE__ */ jsx4("img", { src: value, alt: "", style: s.previewImg }),
644
+ /* @__PURE__ */ jsx4("div", { style: s.previewInfo, children: /* @__PURE__ */ jsx4("div", { style: s.previewUrl, children: value }) }),
645
+ /* @__PURE__ */ jsx4("button", { style: s.dangerBtn, onClick: () => onChange(""), children: "Remove" })
646
+ ] }) : /* @__PURE__ */ jsx4("div", { style: { color: "#9ca3af", fontSize: "12px", padding: "4px 0" }, children: "No image selected" }),
647
+ /* @__PURE__ */ jsxs2("div", { style: s.btnRow, children: [
648
+ /* @__PURE__ */ jsx4("button", { style: s.btn(), onClick: () => setShowUpload(true), children: "Upload" }),
649
+ /* @__PURE__ */ jsx4("button", { style: s.btn(true), onClick: openLibrary, children: "Media Library" })
650
+ ] }),
651
+ showUpload && /* @__PURE__ */ jsx4(
652
+ UploadModal,
653
+ {
654
+ uploading,
655
+ error,
656
+ onUpload: (file, title, desc) => void handleUpload(file, title, desc),
657
+ onClose: () => setShowUpload(false)
658
+ }
659
+ ),
660
+ showLibrary && /* @__PURE__ */ jsx4(
661
+ LibraryModal,
662
+ {
663
+ files,
664
+ pagination,
665
+ loading,
666
+ error,
667
+ onNextPage: () => void loadNextPage(extensions),
668
+ onPrevPage: () => void loadPreviousPage(extensions),
669
+ onSelect: handleSelect,
670
+ onClose: () => setShowLibrary(false)
671
+ }
672
+ )
673
+ ] });
674
+ };
675
+
676
+ // src/components/Image.tsx
677
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
678
+ var Image = {
679
+ label: "Image",
680
+ fields: {
681
+ src: {
682
+ type: "custom",
683
+ label: "Image",
684
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx5(
685
+ ImagePickerField,
686
+ {
687
+ value,
688
+ onChange,
689
+ imagesOnly: true
690
+ }
691
+ )
692
+ },
693
+ alt: { type: "text", label: "Alt Text" },
694
+ caption: { type: "text", label: "Caption" },
695
+ width: { type: "text", label: "Width (CSS, e.g. 100%)" },
696
+ height: { type: "text", label: "Height (CSS, e.g. 300px)" },
697
+ objectFit: {
698
+ type: "select",
699
+ label: "Object Fit",
700
+ options: [
701
+ { value: "cover", label: "Cover" },
702
+ { value: "contain", label: "Contain" },
703
+ { value: "fill", label: "Fill" }
704
+ ]
705
+ },
706
+ borderRadius: { type: "text", label: "Border Radius (CSS)" },
707
+ align: {
708
+ type: "select",
709
+ label: "Alignment",
710
+ options: [
711
+ { value: "left", label: "Left" },
712
+ { value: "center", label: "Center" },
713
+ { value: "right", label: "Right" }
714
+ ]
715
+ }
716
+ },
717
+ defaultProps: {
718
+ src: "",
719
+ alt: "",
720
+ width: "100%",
721
+ objectFit: "cover",
722
+ align: "center"
723
+ },
724
+ render: ({ src, alt, caption, width, height, objectFit, borderRadius, align }) => /* @__PURE__ */ jsxs3(
725
+ "figure",
726
+ {
727
+ style: {
728
+ margin: 0,
729
+ padding: "16px",
730
+ textAlign: align ?? "center",
731
+ boxSizing: "border-box"
732
+ },
733
+ children: [
734
+ src ? /* @__PURE__ */ jsx5(
735
+ "img",
736
+ {
737
+ src,
738
+ alt: alt ?? "",
739
+ style: {
740
+ width: width ?? "100%",
741
+ height,
742
+ objectFit: objectFit ?? "cover",
743
+ borderRadius,
744
+ display: "inline-block",
745
+ maxWidth: "100%"
746
+ }
747
+ }
748
+ ) : /* @__PURE__ */ jsx5(
749
+ "div",
750
+ {
751
+ style: {
752
+ width: width ?? "100%",
753
+ height: height ?? "200px",
754
+ background: "#e0e0e0",
755
+ display: "flex",
756
+ alignItems: "center",
757
+ justifyContent: "center",
758
+ color: "#888",
759
+ fontSize: "14px",
760
+ borderRadius
761
+ },
762
+ children: "No image selected"
763
+ }
764
+ ),
765
+ caption && /* @__PURE__ */ jsx5(
766
+ "figcaption",
767
+ {
768
+ style: { marginTop: "8px", fontSize: "14px", color: "#666" },
769
+ children: caption
770
+ }
771
+ )
772
+ ]
773
+ }
774
+ )
775
+ };
776
+
777
+ // src/components/Button.tsx
778
+ import { jsx as jsx6 } from "react/jsx-runtime";
779
+ var VARIANT_STYLES = {
780
+ primary: { background: "#1a1a2e", color: "#fff", border: "2px solid #1a1a2e" },
781
+ secondary: { background: "#e94560", color: "#fff", border: "2px solid #e94560" },
782
+ outline: { background: "transparent", color: "#1a1a2e", border: "2px solid #1a1a2e" }
783
+ };
784
+ var SIZE_STYLES = {
785
+ sm: { padding: "6px 16px", fontSize: "14px" },
786
+ md: { padding: "10px 24px", fontSize: "16px" },
787
+ lg: { padding: "14px 32px", fontSize: "18px" }
788
+ };
789
+ var Button = {
790
+ label: "Button",
791
+ fields: {
792
+ label: { type: "text", label: "Button Label" },
793
+ href: { type: "text", label: "Link URL" },
794
+ variant: {
795
+ type: "select",
796
+ label: "Variant",
797
+ options: [
798
+ { value: "primary", label: "Primary" },
799
+ { value: "secondary", label: "Secondary" },
800
+ { value: "outline", label: "Outline" }
801
+ ]
802
+ },
803
+ size: {
804
+ type: "select",
805
+ label: "Size",
806
+ options: [
807
+ { value: "sm", label: "Small" },
808
+ { value: "md", label: "Medium" },
809
+ { value: "lg", label: "Large" }
810
+ ]
811
+ },
812
+ align: {
813
+ type: "select",
814
+ label: "Alignment",
815
+ options: [
816
+ { value: "left", label: "Left" },
817
+ { value: "center", label: "Center" },
818
+ { value: "right", label: "Right" }
819
+ ]
820
+ },
821
+ openInNewTab: { type: "radio", label: "Open in New Tab", options: [
822
+ { value: true, label: "Yes" },
823
+ { value: false, label: "No" }
824
+ ] }
825
+ },
826
+ defaultProps: {
827
+ label: "Click me",
828
+ variant: "primary",
829
+ size: "md",
830
+ align: "left",
831
+ openInNewTab: false
832
+ },
833
+ render: ({ label, href, variant = "primary", size = "md", align = "left", openInNewTab }) => /* @__PURE__ */ jsx6("div", { style: { padding: "12px 16px", textAlign: align, boxSizing: "border-box" }, children: /* @__PURE__ */ jsx6(
834
+ "a",
835
+ {
836
+ href: href ?? "#",
837
+ target: openInNewTab ? "_blank" : void 0,
838
+ rel: openInNewTab ? "noopener noreferrer" : void 0,
839
+ style: {
840
+ display: "inline-block",
841
+ borderRadius: "4px",
842
+ textDecoration: "none",
843
+ fontWeight: 600,
844
+ cursor: "pointer",
845
+ ...VARIANT_STYLES[variant],
846
+ ...SIZE_STYLES[size]
847
+ },
848
+ children: label
849
+ }
850
+ ) })
851
+ };
852
+
853
+ // src/components/Card.tsx
854
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
855
+ var Card = {
856
+ label: "Card",
857
+ fields: {
858
+ title: { type: "text", label: "Title" },
859
+ body: { type: "textarea", label: "Body Text" },
860
+ imageUrl: { type: "text", label: "Image URL" },
861
+ ctaText: { type: "text", label: "CTA Text" },
862
+ ctaUrl: { type: "text", label: "CTA URL" },
863
+ shadow: {
864
+ type: "radio",
865
+ label: "Drop Shadow",
866
+ options: [
867
+ { value: true, label: "Yes" },
868
+ { value: false, label: "No" }
869
+ ]
870
+ },
871
+ borderRadius: { type: "text", label: "Border Radius (CSS)" }
872
+ },
873
+ defaultProps: {
874
+ title: "Card Title",
875
+ body: "Card description goes here.",
876
+ shadow: true,
877
+ borderRadius: "8px"
878
+ },
879
+ render: ({ title, body, imageUrl, ctaText, ctaUrl, shadow, borderRadius }) => /* @__PURE__ */ jsxs4(
880
+ "div",
881
+ {
882
+ style: {
883
+ border: "1px solid #e0e0e0",
884
+ borderRadius: borderRadius ?? "8px",
885
+ overflow: "hidden",
886
+ boxShadow: shadow ? "0 2px 8px rgba(0,0,0,0.12)" : void 0,
887
+ background: "#fff",
888
+ margin: "8px",
889
+ boxSizing: "border-box"
890
+ },
891
+ children: [
892
+ imageUrl && /* @__PURE__ */ jsx7(
893
+ "img",
894
+ {
895
+ src: imageUrl,
896
+ alt: title,
897
+ style: { width: "100%", height: "200px", objectFit: "cover", display: "block" }
898
+ }
899
+ ),
900
+ /* @__PURE__ */ jsxs4("div", { style: { padding: "16px" }, children: [
901
+ /* @__PURE__ */ jsx7("h3", { style: { margin: "0 0 8px", fontSize: "1.1rem" }, children: title }),
902
+ body && /* @__PURE__ */ jsx7("p", { style: { margin: "0 0 16px", color: "#555", fontSize: "14px" }, children: body }),
903
+ ctaText && ctaUrl && /* @__PURE__ */ jsxs4(
904
+ "a",
905
+ {
906
+ href: ctaUrl,
907
+ style: {
908
+ color: "#e94560",
909
+ fontWeight: 600,
910
+ textDecoration: "none",
911
+ fontSize: "14px"
912
+ },
913
+ children: [
914
+ ctaText,
915
+ " \u2192"
916
+ ]
917
+ }
918
+ )
919
+ ] })
920
+ ]
921
+ }
922
+ )
923
+ };
924
+
925
+ // src/components/Spacer.tsx
926
+ import { jsx as jsx8 } from "react/jsx-runtime";
927
+ var Spacer = {
928
+ label: "Spacer",
929
+ fields: {
930
+ height: { type: "text", label: "Height (CSS, e.g. 48px)" },
931
+ showLine: {
932
+ type: "radio",
933
+ label: "Show Divider Line",
934
+ options: [
935
+ { value: true, label: "Yes" },
936
+ { value: false, label: "No" }
937
+ ]
938
+ },
939
+ lineColor: { type: "text", label: "Line Color (CSS color)" }
940
+ },
941
+ defaultProps: {
942
+ height: "48px",
943
+ showLine: false,
944
+ lineColor: "#e0e0e0"
945
+ },
946
+ render: ({ height = "48px", showLine = false, lineColor = "#e0e0e0" }) => /* @__PURE__ */ jsx8(
947
+ "div",
948
+ {
949
+ style: {
950
+ height,
951
+ display: "flex",
952
+ alignItems: "center",
953
+ padding: "0 16px",
954
+ boxSizing: "border-box"
955
+ },
956
+ children: showLine && /* @__PURE__ */ jsx8("hr", { style: { width: "100%", border: "none", borderTop: `1px solid ${lineColor}` } })
957
+ }
958
+ )
959
+ };
960
+
961
+ // src/components/ProductTeaser.tsx
962
+ import { useDatasource } from "@commercetools-demo/puck-api";
963
+
964
+ // src/fields/DatasourceField.tsx
965
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
966
+ var s2 = {
967
+ root: {
968
+ display: "flex",
969
+ flexDirection: "column",
970
+ gap: "10px",
971
+ fontFamily: "inherit",
972
+ fontSize: "13px"
973
+ },
974
+ label: { fontSize: "12px", fontWeight: 600, color: "#374151" },
975
+ select: {
976
+ width: "100%",
977
+ padding: "7px 10px",
978
+ border: "1px solid #d1d5db",
979
+ borderRadius: "4px",
980
+ fontSize: "13px",
981
+ background: "#fff",
982
+ boxSizing: "border-box"
983
+ },
984
+ skuRow: {
985
+ display: "flex",
986
+ alignItems: "center",
987
+ gap: "6px"
988
+ },
989
+ input: {
990
+ flex: 1,
991
+ padding: "7px 10px",
992
+ border: "1px solid #d1d5db",
993
+ borderRadius: "4px",
994
+ fontSize: "13px",
995
+ boxSizing: "border-box"
996
+ },
997
+ removeBtn: {
998
+ padding: "5px 8px",
999
+ border: "1px solid #fca5a5",
1000
+ borderRadius: "4px",
1001
+ background: "#fff",
1002
+ color: "#dc2626",
1003
+ fontWeight: 600,
1004
+ fontSize: "13px",
1005
+ cursor: "pointer",
1006
+ lineHeight: 1,
1007
+ flexShrink: 0
1008
+ },
1009
+ addBtn: {
1010
+ padding: "5px 12px",
1011
+ border: "1px solid #d1d5db",
1012
+ borderRadius: "4px",
1013
+ background: "#fff",
1014
+ color: "#374151",
1015
+ fontWeight: 500,
1016
+ fontSize: "12px",
1017
+ cursor: "pointer",
1018
+ alignSelf: "flex-start"
1019
+ },
1020
+ skuList: {
1021
+ display: "flex",
1022
+ flexDirection: "column",
1023
+ gap: "6px"
1024
+ }
1025
+ };
1026
+ var EMPTY_VALUE = { type: "product-by-sku", skus: [""] };
1027
+ var DatasourceField = ({
1028
+ value,
1029
+ onChange
1030
+ }) => {
1031
+ const current = value ?? EMPTY_VALUE;
1032
+ const handleTypeChange = (e) => {
1033
+ const newType = e.target.value;
1034
+ onChange({ type: newType, skus: newType === "product-by-sku" ? [current.skus[0] ?? ""] : current.skus });
1035
+ };
1036
+ const handleSingleSkuChange = (e) => {
1037
+ onChange({ ...current, skus: [e.target.value] });
1038
+ };
1039
+ const handleMultiSkuChange = (index, val) => {
1040
+ const updated = [...current.skus];
1041
+ updated[index] = val;
1042
+ onChange({ ...current, skus: updated });
1043
+ };
1044
+ const addSku = () => {
1045
+ onChange({ ...current, skus: [...current.skus, ""] });
1046
+ };
1047
+ const removeSku = (index) => {
1048
+ const updated = current.skus.filter((_, i) => i !== index);
1049
+ onChange({ ...current, skus: updated.length > 0 ? updated : [""] });
1050
+ };
1051
+ return /* @__PURE__ */ jsxs5("div", { style: s2.root, children: [
1052
+ /* @__PURE__ */ jsxs5("div", { children: [
1053
+ /* @__PURE__ */ jsx9("div", { style: s2.label, children: "Datasource type" }),
1054
+ /* @__PURE__ */ jsxs5("select", { style: s2.select, value: current.type, onChange: handleTypeChange, children: [
1055
+ /* @__PURE__ */ jsx9("option", { value: "product-by-sku", children: "Product by SKU" }),
1056
+ /* @__PURE__ */ jsx9("option", { value: "products-by-sku", children: "Products by SKU" })
1057
+ ] })
1058
+ ] }),
1059
+ current.type === "product-by-sku" ? /* @__PURE__ */ jsxs5("div", { children: [
1060
+ /* @__PURE__ */ jsx9("div", { style: s2.label, children: "SKU" }),
1061
+ /* @__PURE__ */ jsx9(
1062
+ "input",
1063
+ {
1064
+ style: s2.input,
1065
+ type: "text",
1066
+ placeholder: "Enter product SKU",
1067
+ value: current.skus[0] ?? "",
1068
+ onChange: handleSingleSkuChange
1069
+ }
1070
+ )
1071
+ ] }) : /* @__PURE__ */ jsxs5("div", { children: [
1072
+ /* @__PURE__ */ jsx9("div", { style: s2.label, children: "SKUs" }),
1073
+ /* @__PURE__ */ jsxs5("div", { style: s2.skuList, children: [
1074
+ current.skus.map((sku, i) => /* @__PURE__ */ jsxs5("div", { style: s2.skuRow, children: [
1075
+ /* @__PURE__ */ jsx9(
1076
+ "input",
1077
+ {
1078
+ style: s2.input,
1079
+ type: "text",
1080
+ placeholder: `SKU ${i + 1}`,
1081
+ value: sku,
1082
+ onChange: (e) => handleMultiSkuChange(i, e.target.value)
1083
+ }
1084
+ ),
1085
+ /* @__PURE__ */ jsx9(
1086
+ "button",
1087
+ {
1088
+ style: s2.removeBtn,
1089
+ onClick: () => removeSku(i),
1090
+ title: "Remove SKU",
1091
+ children: "\xD7"
1092
+ }
1093
+ )
1094
+ ] }, i)),
1095
+ /* @__PURE__ */ jsx9("button", { style: s2.addBtn, onClick: addSku, children: "+ Add SKU" })
1096
+ ] })
1097
+ ] })
1098
+ ] });
1099
+ };
1100
+
1101
+ // src/components/ProductTeaser.tsx
1102
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1103
+ var getLocalizedName = (name) => {
1104
+ if (!name) return "";
1105
+ return name["en"] ?? name["en-US"] ?? Object.values(name)[0] ?? "";
1106
+ };
1107
+ var formatPrice = (centAmount, currencyCode, fractionDigits) => {
1108
+ const amount = centAmount / Math.pow(10, fractionDigits);
1109
+ return `${currencyCode} ${amount.toFixed(fractionDigits)}`;
1110
+ };
1111
+ var ProductTeaserRender = ({
1112
+ datasource,
1113
+ richText
1114
+ }) => {
1115
+ const hasPreResolved = datasource?.resolvedData != null;
1116
+ const { data: fetchedData, loading, error } = useDatasource(
1117
+ hasPreResolved ? void 0 : datasource?.type,
1118
+ hasPreResolved ? [] : datasource?.skus ?? []
1119
+ );
1120
+ const data = hasPreResolved ? datasource.resolvedData : fetchedData;
1121
+ const product = data ? Array.isArray(data) ? data[0] ?? null : data : null;
1122
+ const imageUrl = product?.masterVariant?.images?.[0]?.url;
1123
+ const priceValue = product?.masterVariant?.prices?.[0]?.value;
1124
+ const productName = getLocalizedName(product?.name);
1125
+ return /* @__PURE__ */ jsxs6(
1126
+ "div",
1127
+ {
1128
+ style: {
1129
+ display: "flex",
1130
+ gap: "24px",
1131
+ padding: "16px",
1132
+ alignItems: "flex-start",
1133
+ fontFamily: "inherit"
1134
+ },
1135
+ children: [
1136
+ /* @__PURE__ */ jsx10("div", { style: { flexShrink: 0, width: "200px" }, children: loading ? /* @__PURE__ */ jsx10(
1137
+ "div",
1138
+ {
1139
+ style: {
1140
+ width: "200px",
1141
+ height: "200px",
1142
+ background: "#f3f4f6",
1143
+ borderRadius: "8px",
1144
+ display: "flex",
1145
+ alignItems: "center",
1146
+ justifyContent: "center",
1147
+ color: "#9ca3af",
1148
+ fontSize: "13px"
1149
+ },
1150
+ children: "Loading\u2026"
1151
+ }
1152
+ ) : imageUrl ? /* @__PURE__ */ jsx10(
1153
+ "img",
1154
+ {
1155
+ src: imageUrl,
1156
+ alt: productName,
1157
+ style: {
1158
+ width: "200px",
1159
+ height: "200px",
1160
+ objectFit: "cover",
1161
+ borderRadius: "8px",
1162
+ display: "block"
1163
+ }
1164
+ }
1165
+ ) : /* @__PURE__ */ jsx10(
1166
+ "div",
1167
+ {
1168
+ style: {
1169
+ width: "200px",
1170
+ height: "200px",
1171
+ background: "#f3f4f6",
1172
+ borderRadius: "8px",
1173
+ display: "flex",
1174
+ alignItems: "center",
1175
+ justifyContent: "center",
1176
+ color: "#9ca3af",
1177
+ fontSize: "13px"
1178
+ },
1179
+ children: error ? "Error loading product" : "No product selected"
1180
+ }
1181
+ ) }),
1182
+ /* @__PURE__ */ jsxs6(
1183
+ "div",
1184
+ {
1185
+ style: {
1186
+ display: "flex",
1187
+ flexDirection: "column",
1188
+ gap: "12px",
1189
+ flex: 1,
1190
+ minWidth: 0
1191
+ },
1192
+ children: [
1193
+ richText ? /* @__PURE__ */ jsx10("div", { dangerouslySetInnerHTML: { __html: richText } }) : /* @__PURE__ */ jsx10("div", { style: { color: "#9ca3af", fontSize: "13px" }, children: "No description" }),
1194
+ priceValue && /* @__PURE__ */ jsx10(
1195
+ "div",
1196
+ {
1197
+ style: {
1198
+ fontSize: "20px",
1199
+ fontWeight: 700,
1200
+ color: "#111827"
1201
+ },
1202
+ children: formatPrice(
1203
+ priceValue.centAmount,
1204
+ priceValue.currencyCode,
1205
+ priceValue.fractionDigits
1206
+ )
1207
+ }
1208
+ )
1209
+ ]
1210
+ }
1211
+ )
1212
+ ]
1213
+ }
1214
+ );
1215
+ };
1216
+ var ProductTeaser = {
1217
+ label: "Product Teaser",
1218
+ fields: {
1219
+ datasource: {
1220
+ type: "custom",
1221
+ label: "Product Datasource",
1222
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx10(DatasourceField, { value, onChange })
1223
+ },
1224
+ richText: {
1225
+ type: "textarea",
1226
+ label: "Rich Text (HTML)"
1227
+ }
1228
+ },
1229
+ defaultProps: {
1230
+ datasource: { type: "product-by-sku", skus: [""] },
1231
+ richText: ""
1232
+ },
1233
+ render: (props) => /* @__PURE__ */ jsx10(ProductTeaserRender, { ...props })
1234
+ };
1235
+
1236
+ // src/components/cms/HeroBanner.tsx
1237
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
1238
+ var HeroBanner = {
1239
+ label: "Hero Banner",
1240
+ fields: {
1241
+ title: { type: "text", label: "Title" },
1242
+ subtitle: { type: "text", label: "Subtitle" },
1243
+ image: {
1244
+ type: "custom",
1245
+ label: "Image",
1246
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx11(ImagePickerField, { value: value ?? "", onChange })
1247
+ }
1248
+ },
1249
+ defaultProps: { title: "", subtitle: "", image: "" },
1250
+ render: ({ title, subtitle, image }) => /* @__PURE__ */ jsxs7(
1251
+ "div",
1252
+ {
1253
+ style: {
1254
+ position: "relative",
1255
+ width: "100%",
1256
+ paddingBottom: "56.25%",
1257
+ overflow: "hidden",
1258
+ backgroundColor: "#f0f0f0"
1259
+ },
1260
+ children: [
1261
+ image && /* @__PURE__ */ jsx11(
1262
+ "img",
1263
+ {
1264
+ src: image,
1265
+ alt: title || "Hero banner",
1266
+ style: {
1267
+ position: "absolute",
1268
+ top: 0,
1269
+ left: 0,
1270
+ width: "100%",
1271
+ height: "100%",
1272
+ objectFit: "cover",
1273
+ objectPosition: "center"
1274
+ }
1275
+ }
1276
+ ),
1277
+ /* @__PURE__ */ jsx11(
1278
+ "div",
1279
+ {
1280
+ style: {
1281
+ position: "absolute",
1282
+ top: 0,
1283
+ left: 0,
1284
+ width: "100%",
1285
+ height: "100%",
1286
+ background: "rgba(0,0,0,0.3)",
1287
+ zIndex: 1
1288
+ }
1289
+ }
1290
+ ),
1291
+ /* @__PURE__ */ jsxs7(
1292
+ "div",
1293
+ {
1294
+ style: {
1295
+ position: "absolute",
1296
+ top: "50%",
1297
+ left: "50%",
1298
+ transform: "translate(-50%,-50%)",
1299
+ textAlign: "center",
1300
+ color: "white",
1301
+ zIndex: 2,
1302
+ padding: "2rem",
1303
+ maxWidth: "90%"
1304
+ },
1305
+ children: [
1306
+ title && /* @__PURE__ */ jsx11(
1307
+ "h1",
1308
+ {
1309
+ style: {
1310
+ fontSize: "3rem",
1311
+ fontWeight: "bold",
1312
+ margin: "0 0 1rem 0",
1313
+ textShadow: "2px 2px 4px rgba(0,0,0,0.7)"
1314
+ },
1315
+ children: title
1316
+ }
1317
+ ),
1318
+ subtitle && /* @__PURE__ */ jsx11(
1319
+ "p",
1320
+ {
1321
+ style: {
1322
+ fontSize: "1.25rem",
1323
+ margin: 0,
1324
+ textShadow: "1px 1px 2px rgba(0,0,0,0.7)",
1325
+ opacity: 0.95
1326
+ },
1327
+ children: subtitle
1328
+ }
1329
+ )
1330
+ ]
1331
+ }
1332
+ )
1333
+ ]
1334
+ }
1335
+ )
1336
+ };
1337
+
1338
+ // src/components/cms/TextBlock.tsx
1339
+ import { Fragment as Fragment2, jsx as jsx12 } from "react/jsx-runtime";
1340
+ var TextBlock = {
1341
+ label: "Text Block",
1342
+ fields: {
1343
+ content: { type: "textarea", label: "Content (HTML)" }
1344
+ },
1345
+ defaultProps: { content: "" },
1346
+ render: ({ content }) => {
1347
+ if (!content) return /* @__PURE__ */ jsx12(Fragment2, {});
1348
+ return /* @__PURE__ */ jsx12(
1349
+ "div",
1350
+ {
1351
+ dangerouslySetInnerHTML: { __html: content },
1352
+ style: {
1353
+ maxWidth: "720px",
1354
+ margin: "0 auto",
1355
+ padding: "1.5rem 1rem",
1356
+ lineHeight: 1.6,
1357
+ color: "#333"
1358
+ }
1359
+ }
1360
+ );
1361
+ }
1362
+ };
1363
+
1364
+ // src/components/cms/CategoryGrid.tsx
1365
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1366
+ var imgField = (label) => ({
1367
+ type: "custom",
1368
+ label,
1369
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx13(ImagePickerField, { value: value ?? "", onChange })
1370
+ });
1371
+ var CategoryGrid = {
1372
+ label: "Category Grid",
1373
+ fields: {
1374
+ category1Image: imgField("Category 1 Image"),
1375
+ category1Label: { type: "text", label: "Category 1 Label" },
1376
+ category1Link: { type: "text", label: "Category 1 Link" },
1377
+ category2Image: imgField("Category 2 Image"),
1378
+ category2Label: { type: "text", label: "Category 2 Label" },
1379
+ category2Link: { type: "text", label: "Category 2 Link" },
1380
+ category3Image: imgField("Category 3 Image"),
1381
+ category3Label: { type: "text", label: "Category 3 Label" },
1382
+ category3Link: { type: "text", label: "Category 3 Link" },
1383
+ category4Image: imgField("Category 4 Image"),
1384
+ category4Label: { type: "text", label: "Category 4 Label" },
1385
+ category4Link: { type: "text", label: "Category 4 Link" }
1386
+ },
1387
+ defaultProps: {
1388
+ category1Image: "",
1389
+ category1Label: "",
1390
+ category1Link: "",
1391
+ category2Image: "",
1392
+ category2Label: "",
1393
+ category2Link: "",
1394
+ category3Image: "",
1395
+ category3Label: "",
1396
+ category3Link: "",
1397
+ category4Image: "",
1398
+ category4Label: "",
1399
+ category4Link: ""
1400
+ },
1401
+ render: (props) => {
1402
+ const categories = [
1403
+ [props.category1Image, props.category1Label, props.category1Link],
1404
+ [props.category2Image, props.category2Label, props.category2Link],
1405
+ [props.category3Image, props.category3Label, props.category3Link],
1406
+ [props.category4Image, props.category4Label, props.category4Link]
1407
+ ].filter(([, label, link]) => label && link);
1408
+ if (categories.length === 0) return /* @__PURE__ */ jsx13("div", { style: { padding: "2rem", textAlign: "center", color: "#999", fontSize: "13px" }, children: "No categories configured" });
1409
+ return /* @__PURE__ */ jsx13(
1410
+ "div",
1411
+ {
1412
+ style: {
1413
+ display: "grid",
1414
+ gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
1415
+ gap: "1.5rem",
1416
+ padding: "2rem 1rem"
1417
+ },
1418
+ children: categories.map(([image, label, link], i) => /* @__PURE__ */ jsxs8(
1419
+ "a",
1420
+ {
1421
+ href: link,
1422
+ style: {
1423
+ display: "block",
1424
+ textAlign: "center",
1425
+ textDecoration: "none",
1426
+ color: "inherit",
1427
+ borderRadius: "8px",
1428
+ overflow: "hidden",
1429
+ boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
1430
+ },
1431
+ children: [
1432
+ /* @__PURE__ */ jsx13("div", { style: { aspectRatio: "1", background: "#f0f0f0", overflow: "hidden" }, children: image && /* @__PURE__ */ jsx13(
1433
+ "img",
1434
+ {
1435
+ src: image,
1436
+ alt: label,
1437
+ style: { width: "100%", height: "100%", objectFit: "cover" }
1438
+ }
1439
+ ) }),
1440
+ /* @__PURE__ */ jsx13(
1441
+ "span",
1442
+ {
1443
+ style: {
1444
+ display: "block",
1445
+ padding: "1rem",
1446
+ fontWeight: 600,
1447
+ color: "#333"
1448
+ },
1449
+ children: label
1450
+ }
1451
+ )
1452
+ ]
1453
+ },
1454
+ i
1455
+ ))
1456
+ }
1457
+ );
1458
+ }
1459
+ };
1460
+
1461
+ // src/components/cms/CategoryHero.tsx
1462
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
1463
+ var CategoryHero = {
1464
+ label: "Category Hero",
1465
+ fields: {
1466
+ title: { type: "text", label: "Title" },
1467
+ subtitle: { type: "text", label: "Subtitle" },
1468
+ image: {
1469
+ type: "custom",
1470
+ label: "Background Image",
1471
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx14(ImagePickerField, { value: value ?? "", onChange })
1472
+ },
1473
+ ctaText: { type: "text", label: "CTA Text" },
1474
+ ctaLink: { type: "text", label: "CTA Link" }
1475
+ },
1476
+ defaultProps: { title: "", subtitle: "", image: "", ctaText: "", ctaLink: "" },
1477
+ render: ({ title, subtitle, image, ctaText, ctaLink }) => /* @__PURE__ */ jsxs9(
1478
+ "div",
1479
+ {
1480
+ style: {
1481
+ position: "relative",
1482
+ minHeight: "220px",
1483
+ display: "flex",
1484
+ alignItems: "center",
1485
+ justifyContent: "center",
1486
+ background: "#f0f0f0",
1487
+ borderRadius: "8px",
1488
+ overflow: "hidden",
1489
+ margin: "1rem 0"
1490
+ },
1491
+ children: [
1492
+ image && /* @__PURE__ */ jsx14(
1493
+ "img",
1494
+ {
1495
+ src: image,
1496
+ alt: title || "Category",
1497
+ style: {
1498
+ position: "absolute",
1499
+ top: 0,
1500
+ left: 0,
1501
+ width: "100%",
1502
+ height: "100%",
1503
+ objectFit: "cover",
1504
+ opacity: 0.85
1505
+ }
1506
+ }
1507
+ ),
1508
+ /* @__PURE__ */ jsx14("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.25)" } }),
1509
+ /* @__PURE__ */ jsxs9(
1510
+ "div",
1511
+ {
1512
+ style: {
1513
+ position: "relative",
1514
+ zIndex: 1,
1515
+ textAlign: "center",
1516
+ padding: "2rem",
1517
+ color: "white",
1518
+ textShadow: "0 1px 3px rgba(0,0,0,0.5)"
1519
+ },
1520
+ children: [
1521
+ title && /* @__PURE__ */ jsx14("h1", { style: { fontSize: "2.5rem", marginBottom: "0.5rem" }, children: title }),
1522
+ subtitle && /* @__PURE__ */ jsx14("p", { style: { fontSize: "1.1rem", marginBottom: "1rem" }, children: subtitle }),
1523
+ ctaText && ctaLink && /* @__PURE__ */ jsx14(
1524
+ "a",
1525
+ {
1526
+ href: ctaLink,
1527
+ style: {
1528
+ display: "inline-block",
1529
+ background: "white",
1530
+ color: "#2c5530",
1531
+ padding: "0.75rem 2rem",
1532
+ borderRadius: "4px",
1533
+ textDecoration: "none",
1534
+ fontWeight: 600
1535
+ },
1536
+ children: ctaText
1537
+ }
1538
+ )
1539
+ ]
1540
+ }
1541
+ )
1542
+ ]
1543
+ }
1544
+ )
1545
+ };
1546
+
1547
+ // src/components/cms/CheckoutPromoBanner.tsx
1548
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
1549
+ var CheckoutPromoBanner = {
1550
+ label: "Checkout Promo Banner",
1551
+ fields: {
1552
+ title: { type: "text", label: "Title" },
1553
+ message: { type: "text", label: "Message" },
1554
+ ctaText: { type: "text", label: "CTA Text" },
1555
+ ctaLink: { type: "text", label: "CTA Link" }
1556
+ },
1557
+ defaultProps: { title: "", message: "", ctaText: "", ctaLink: "" },
1558
+ render: ({ title, message, ctaText, ctaLink }) => {
1559
+ if (!title && !message && !ctaText) return /* @__PURE__ */ jsx15(Fragment3, {});
1560
+ return /* @__PURE__ */ jsxs10(
1561
+ "div",
1562
+ {
1563
+ style: {
1564
+ padding: "1rem 1.25rem",
1565
+ background: "#f0f7f0",
1566
+ border: "1px solid #c8e6c8",
1567
+ borderRadius: "4px",
1568
+ marginBottom: "1rem",
1569
+ fontSize: "0.95rem"
1570
+ },
1571
+ children: [
1572
+ title && /* @__PURE__ */ jsx15("div", { style: { fontWeight: 600, color: "#2c5530", marginBottom: "0.25rem" }, children: title }),
1573
+ message && /* @__PURE__ */ jsx15("p", { style: { margin: "0 0 0.5rem 0", color: "#333" }, children: message }),
1574
+ ctaText && ctaLink && /* @__PURE__ */ jsx15("a", { href: ctaLink, style: { color: "#2c5530", fontWeight: 600, textDecoration: "none" }, children: ctaText })
1575
+ ]
1576
+ }
1577
+ );
1578
+ }
1579
+ };
1580
+
1581
+ // src/components/cms/CountdownBanner.tsx
1582
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1583
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1584
+ var calcTimeLeft = (endDate) => {
1585
+ if (!endDate) return "";
1586
+ const end = new Date(endDate);
1587
+ const now = /* @__PURE__ */ new Date();
1588
+ if (end <= now) return "Offer ended";
1589
+ const diff = end.getTime() - now.getTime();
1590
+ const d = Math.floor(diff / 864e5);
1591
+ const h = Math.floor(diff % 864e5 / 36e5);
1592
+ const m = Math.floor(diff % 36e5 / 6e4);
1593
+ const s3 = Math.floor(diff % 6e4 / 1e3);
1594
+ if (d > 0) return `${d}d ${h}h ${m}m left`;
1595
+ if (h > 0) return `${h}h ${m}m ${s3}s left`;
1596
+ return `${m}m ${s3}s left`;
1597
+ };
1598
+ var CountdownRender = ({
1599
+ headline,
1600
+ subline,
1601
+ endDate,
1602
+ ctaText,
1603
+ ctaLink,
1604
+ background
1605
+ }) => {
1606
+ const [timeLeft, setTimeLeft] = useState2(() => calcTimeLeft(endDate));
1607
+ useEffect2(() => {
1608
+ if (!endDate) return;
1609
+ const id = setInterval(() => setTimeLeft(calcTimeLeft(endDate)), 1e3);
1610
+ return () => clearInterval(id);
1611
+ }, [endDate]);
1612
+ return /* @__PURE__ */ jsxs11(
1613
+ "div",
1614
+ {
1615
+ style: {
1616
+ background: background || "#2c5530",
1617
+ color: "white",
1618
+ padding: "2rem 1rem",
1619
+ textAlign: "center",
1620
+ borderRadius: "4px",
1621
+ margin: "1rem 0"
1622
+ },
1623
+ children: [
1624
+ headline && /* @__PURE__ */ jsx16("h2", { style: { fontSize: "1.75rem", marginBottom: "0.5rem" }, children: headline }),
1625
+ subline && /* @__PURE__ */ jsx16("p", { style: { fontSize: "1rem", opacity: 0.9, marginBottom: "1rem" }, children: subline }),
1626
+ timeLeft && /* @__PURE__ */ jsx16("p", { style: { fontSize: "1.25rem", fontWeight: 600, margin: "0 0 1rem 0", letterSpacing: "0.05em" }, children: timeLeft }),
1627
+ ctaText && ctaLink && /* @__PURE__ */ jsx16(
1628
+ "a",
1629
+ {
1630
+ href: ctaLink,
1631
+ style: {
1632
+ display: "inline-block",
1633
+ background: "white",
1634
+ color: "#2c5530",
1635
+ padding: "0.75rem 2rem",
1636
+ borderRadius: "4px",
1637
+ textDecoration: "none",
1638
+ fontWeight: 600
1639
+ },
1640
+ children: ctaText
1641
+ }
1642
+ )
1643
+ ]
1644
+ }
1645
+ );
1646
+ };
1647
+ var CountdownBanner = {
1648
+ label: "Countdown Banner",
1649
+ fields: {
1650
+ headline: { type: "text", label: "Headline" },
1651
+ subline: { type: "text", label: "Subline" },
1652
+ endDate: { type: "text", label: "End Date (ISO, e.g. 2025-12-31T23:59:59)" },
1653
+ ctaText: { type: "text", label: "CTA Text" },
1654
+ ctaLink: { type: "text", label: "CTA Link" },
1655
+ background: { type: "text", label: "Background Color" }
1656
+ },
1657
+ defaultProps: {
1658
+ headline: "",
1659
+ subline: "",
1660
+ endDate: "",
1661
+ ctaText: "",
1662
+ ctaLink: "",
1663
+ background: "#2c5530"
1664
+ },
1665
+ render: (props) => /* @__PURE__ */ jsx16(CountdownRender, { ...props })
1666
+ };
1667
+
1668
+ // src/components/cms/CrossSellBlock.tsx
1669
+ import { useDatasource as useDatasource2 } from "@commercetools-demo/puck-api";
1670
+
1671
+ // src/components/cms/shared.ts
1672
+ var getLocalizedText = (obj) => {
1673
+ if (!obj) return "";
1674
+ return obj["en-US"] ?? obj["en"] ?? Object.values(obj)[0] ?? "";
1675
+ };
1676
+ var formatPrice2 = (centAmount, currencyCode = "USD", fractionDigits = 2) => {
1677
+ const amount = centAmount / Math.pow(10, fractionDigits);
1678
+ return new Intl.NumberFormat("en-US", {
1679
+ style: "currency",
1680
+ currency: currencyCode
1681
+ }).format(amount);
1682
+ };
1683
+ var getFirstPrice = (product) => {
1684
+ const p = product;
1685
+ return p?.masterVariant?.prices?.[0]?.value ?? null;
1686
+ };
1687
+ var getProductImage = (product) => {
1688
+ const p = product;
1689
+ return p?.masterVariant?.images?.[0]?.url ?? null;
1690
+ };
1691
+ var getProductSlug = (product) => {
1692
+ const p = product;
1693
+ return getLocalizedText(p?.slug) || p?.id || "#";
1694
+ };
1695
+
1696
+ // src/components/cms/CrossSellBlock.tsx
1697
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
1698
+ var CrossSellRender = ({ title, products, ctaText }) => {
1699
+ const hasPreResolved = products?.resolvedData != null;
1700
+ const { data: fetchedData, loading } = useDatasource2(
1701
+ hasPreResolved ? void 0 : products?.type,
1702
+ hasPreResolved ? [] : products?.skus ?? []
1703
+ );
1704
+ const items = hasPreResolved ? Array.isArray(products.resolvedData) ? products.resolvedData : [] : Array.isArray(fetchedData) ? fetchedData : [];
1705
+ if (!loading && items.length === 0) return null;
1706
+ return /* @__PURE__ */ jsxs12("div", { style: { padding: "2rem 0", borderTop: "1px solid #eee" }, children: [
1707
+ title && /* @__PURE__ */ jsx17("h3", { style: { fontSize: "1.5rem", marginBottom: "1.5rem", color: "#333" }, children: title || "Frequently bought together" }),
1708
+ loading && /* @__PURE__ */ jsx17("div", { style: { color: "#999", padding: "1rem" }, children: "Loading\u2026" }),
1709
+ items.length > 0 && /* @__PURE__ */ jsx17(
1710
+ "div",
1711
+ {
1712
+ style: {
1713
+ display: "grid",
1714
+ gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
1715
+ gap: "1rem"
1716
+ },
1717
+ children: items.map((product, i) => {
1718
+ const name = getLocalizedText(product?.name);
1719
+ const imageUrl = getProductImage(product);
1720
+ const priceVal = getFirstPrice(product);
1721
+ const slug = getProductSlug(product);
1722
+ return /* @__PURE__ */ jsxs12(
1723
+ "a",
1724
+ {
1725
+ href: slug,
1726
+ style: {
1727
+ textAlign: "center",
1728
+ textDecoration: "none",
1729
+ color: "inherit",
1730
+ padding: "1rem",
1731
+ borderRadius: "8px",
1732
+ border: "1px solid #eee"
1733
+ },
1734
+ children: [
1735
+ /* @__PURE__ */ jsx17("div", { style: { height: "120px", marginBottom: "0.75rem", display: "flex", alignItems: "center", justifyContent: "center", background: "#f9f9f9", borderRadius: "4px" }, children: imageUrl ? /* @__PURE__ */ jsx17("img", { src: imageUrl, alt: name, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }) : null }),
1736
+ /* @__PURE__ */ jsx17("div", { style: { fontWeight: 600, fontSize: "0.95rem", marginBottom: "0.25rem" }, children: name }),
1737
+ priceVal && /* @__PURE__ */ jsx17("div", { style: { fontSize: "1.1rem", color: "#2c5530", fontWeight: "bold" }, children: formatPrice2(priceVal.centAmount, priceVal.currencyCode, priceVal.fractionDigits) })
1738
+ ]
1739
+ },
1740
+ product?.id ?? i
1741
+ );
1742
+ })
1743
+ }
1744
+ ),
1745
+ ctaText && /* @__PURE__ */ jsx17("a", { href: "#", style: { display: "inline-block", marginTop: "1rem", color: "#2c5530", fontWeight: 600, textDecoration: "none" }, children: ctaText })
1746
+ ] });
1747
+ };
1748
+ var CrossSellBlock = {
1749
+ label: "Cross-Sell Block",
1750
+ fields: {
1751
+ title: { type: "text", label: "Title" },
1752
+ products: {
1753
+ type: "custom",
1754
+ label: "Products",
1755
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx17(DatasourceField, { value, onChange })
1756
+ },
1757
+ ctaText: { type: "text", label: "CTA Text" }
1758
+ },
1759
+ defaultProps: {
1760
+ title: "Frequently bought together",
1761
+ products: { type: "products-by-sku", skus: [""] },
1762
+ ctaText: ""
1763
+ },
1764
+ render: (props) => /* @__PURE__ */ jsx17(CrossSellRender, { ...props })
1765
+ };
1766
+
1767
+ // src/components/cms/DeliveryMessage.tsx
1768
+ import { Fragment as Fragment4, jsx as jsx18 } from "react/jsx-runtime";
1769
+ var DeliveryMessage = {
1770
+ label: "Delivery Message",
1771
+ fields: {
1772
+ message: { type: "text", label: "Message (use $XX for threshold)" },
1773
+ threshold: { type: "text", label: "Threshold Amount (e.g. 50)" }
1774
+ },
1775
+ defaultProps: { message: "Free delivery on orders over $50", threshold: "" },
1776
+ render: ({ message, threshold }) => {
1777
+ if (!message) return /* @__PURE__ */ jsx18(Fragment4, {});
1778
+ const text = threshold ? message.replace(/\$\d+/g, `$${threshold}`) : message;
1779
+ return /* @__PURE__ */ jsx18(
1780
+ "div",
1781
+ {
1782
+ style: {
1783
+ padding: "0.75rem 1rem",
1784
+ background: "#f0f7f0",
1785
+ borderRadius: "4px",
1786
+ fontSize: "0.95rem",
1787
+ color: "#2c5530"
1788
+ },
1789
+ children: text
1790
+ }
1791
+ );
1792
+ }
1793
+ };
1794
+
1795
+ // src/components/cms/Divider.tsx
1796
+ import { jsx as jsx19 } from "react/jsx-runtime";
1797
+ var Divider = {
1798
+ label: "Divider",
1799
+ fields: {
1800
+ lineStyle: {
1801
+ type: "select",
1802
+ label: "Line Style",
1803
+ options: [
1804
+ { value: "solid", label: "Solid" },
1805
+ { value: "dashed", label: "Dashed" },
1806
+ { value: "dotted", label: "Dotted" }
1807
+ ]
1808
+ },
1809
+ spacing: { type: "text", label: "Spacing (px)" }
1810
+ },
1811
+ defaultProps: { lineStyle: "solid", spacing: "24" },
1812
+ render: ({ lineStyle, spacing }) => {
1813
+ const s3 = parseInt(spacing, 10) || 24;
1814
+ return /* @__PURE__ */ jsx19(
1815
+ "hr",
1816
+ {
1817
+ style: {
1818
+ border: "none",
1819
+ borderTop: `1px ${lineStyle} #ddd`,
1820
+ margin: `${s3 / 2}px 0`
1821
+ }
1822
+ }
1823
+ );
1824
+ }
1825
+ };
1826
+
1827
+ // src/components/cms/EmptyState.tsx
1828
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
1829
+ var EmptyState = {
1830
+ label: "Empty State",
1831
+ fields: {
1832
+ image: {
1833
+ type: "custom",
1834
+ label: "Image",
1835
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx20(ImagePickerField, { value: value ?? "", onChange })
1836
+ },
1837
+ title: { type: "text", label: "Title" },
1838
+ description: { type: "text", label: "Description" },
1839
+ ctaText: { type: "text", label: "CTA Text" },
1840
+ ctaLink: { type: "text", label: "CTA Link" }
1841
+ },
1842
+ defaultProps: { image: "", title: "", description: "", ctaText: "", ctaLink: "" },
1843
+ render: ({ image, title, description, ctaText, ctaLink }) => /* @__PURE__ */ jsxs13("div", { style: { textAlign: "center", padding: "3rem 1rem" }, children: [
1844
+ image && /* @__PURE__ */ jsx20(
1845
+ "img",
1846
+ {
1847
+ src: image,
1848
+ alt: title || "Empty state",
1849
+ style: { maxWidth: "200px", height: "auto", marginBottom: "1.5rem", opacity: 0.8 }
1850
+ }
1851
+ ),
1852
+ title && /* @__PURE__ */ jsx20("h2", { style: { fontSize: "1.5rem", color: "#333", marginBottom: "0.5rem" }, children: title }),
1853
+ description && /* @__PURE__ */ jsx20("p", { style: { color: "#666", marginBottom: "1.5rem", maxWidth: "400px", margin: "0 auto 1.5rem" }, children: description }),
1854
+ ctaText && ctaLink && /* @__PURE__ */ jsx20(
1855
+ "a",
1856
+ {
1857
+ href: ctaLink,
1858
+ style: {
1859
+ display: "inline-block",
1860
+ background: "#2c5530",
1861
+ color: "white",
1862
+ padding: "0.75rem 2rem",
1863
+ borderRadius: "4px",
1864
+ textDecoration: "none",
1865
+ fontWeight: 600
1866
+ },
1867
+ children: ctaText
1868
+ }
1869
+ )
1870
+ ] })
1871
+ };
1872
+
1873
+ // src/components/cms/FAQAccordion.tsx
1874
+ import { useState as useState3 } from "react";
1875
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1876
+ var FAQRender = (props) => {
1877
+ const items = [
1878
+ [props.question1, props.answer1],
1879
+ [props.question2, props.answer2],
1880
+ [props.question3, props.answer3],
1881
+ [props.question4, props.answer4]
1882
+ ].filter(([q]) => q);
1883
+ const [open, setOpen] = useState3(null);
1884
+ if (items.length === 0) return /* @__PURE__ */ jsx21("div", { style: { padding: "1rem", color: "#999", fontSize: "13px" }, children: "No FAQ items configured" });
1885
+ return /* @__PURE__ */ jsx21("div", { style: { border: "1px solid #eee", borderRadius: "8px", overflow: "hidden" }, children: items.map(([question, answer], i) => /* @__PURE__ */ jsxs14("div", { style: { borderBottom: i < items.length - 1 ? "1px solid #eee" : "none" }, children: [
1886
+ /* @__PURE__ */ jsxs14(
1887
+ "button",
1888
+ {
1889
+ onClick: () => setOpen(open === i ? null : i),
1890
+ style: {
1891
+ width: "100%",
1892
+ padding: "1rem 1.25rem",
1893
+ textAlign: "left",
1894
+ fontWeight: 600,
1895
+ fontSize: "1rem",
1896
+ background: "#fafafa",
1897
+ border: "none",
1898
+ cursor: "pointer",
1899
+ display: "flex",
1900
+ justifyContent: "space-between",
1901
+ alignItems: "center"
1902
+ },
1903
+ children: [
1904
+ question,
1905
+ /* @__PURE__ */ jsx21("span", { style: { fontSize: "1.2rem", lineHeight: 1 }, children: open === i ? "\u2212" : "+" })
1906
+ ]
1907
+ }
1908
+ ),
1909
+ open === i && answer && /* @__PURE__ */ jsx21(
1910
+ "div",
1911
+ {
1912
+ dangerouslySetInnerHTML: { __html: answer },
1913
+ style: {
1914
+ padding: "1rem 1.25rem",
1915
+ background: "white",
1916
+ fontSize: "0.95rem",
1917
+ lineHeight: 1.5,
1918
+ color: "#555"
1919
+ }
1920
+ }
1921
+ )
1922
+ ] }, i)) });
1923
+ };
1924
+ var FAQAccordion = {
1925
+ label: "FAQ Accordion",
1926
+ fields: {
1927
+ question1: { type: "text", label: "Question 1" },
1928
+ answer1: { type: "textarea", label: "Answer 1 (HTML)" },
1929
+ question2: { type: "text", label: "Question 2" },
1930
+ answer2: { type: "textarea", label: "Answer 2 (HTML)" },
1931
+ question3: { type: "text", label: "Question 3" },
1932
+ answer3: { type: "textarea", label: "Answer 3 (HTML)" },
1933
+ question4: { type: "text", label: "Question 4" },
1934
+ answer4: { type: "textarea", label: "Answer 4 (HTML)" }
1935
+ },
1936
+ defaultProps: {
1937
+ question1: "",
1938
+ answer1: "",
1939
+ question2: "",
1940
+ answer2: "",
1941
+ question3: "",
1942
+ answer3: "",
1943
+ question4: "",
1944
+ answer4: ""
1945
+ },
1946
+ render: (props) => /* @__PURE__ */ jsx21(FAQRender, { ...props })
1947
+ };
1948
+
1949
+ // src/components/cms/FooterBlock.tsx
1950
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1951
+ var FooterBlock = {
1952
+ label: "Footer Block",
1953
+ fields: {
1954
+ column1: { type: "textarea", label: "Column 1 (HTML)" },
1955
+ column2: { type: "textarea", label: "Column 2 (HTML)" },
1956
+ column3: { type: "textarea", label: "Column 3 (HTML)" },
1957
+ copyright: { type: "text", label: "Copyright Text" }
1958
+ },
1959
+ defaultProps: { column1: "", column2: "", column3: "", copyright: "" },
1960
+ render: ({ column1, column2, column3, copyright }) => {
1961
+ const columns = [column1, column2, column3].filter(Boolean);
1962
+ return /* @__PURE__ */ jsxs15("footer", { style: { background: "#333", color: "#eee", padding: "2rem 1rem", marginTop: "3rem" }, children: [
1963
+ columns.length > 0 && /* @__PURE__ */ jsx22(
1964
+ "div",
1965
+ {
1966
+ style: {
1967
+ display: "grid",
1968
+ gridTemplateColumns: `repeat(${columns.length}, 1fr)`,
1969
+ gap: "2rem",
1970
+ maxWidth: "1200px",
1971
+ margin: "0 auto 2rem"
1972
+ },
1973
+ children: columns.map((html, i) => /* @__PURE__ */ jsx22(
1974
+ "div",
1975
+ {
1976
+ dangerouslySetInnerHTML: { __html: html },
1977
+ style: { fontSize: "0.9rem", lineHeight: 1.6 }
1978
+ },
1979
+ i
1980
+ ))
1981
+ }
1982
+ ),
1983
+ copyright && /* @__PURE__ */ jsx22("p", { style: { textAlign: "center", fontSize: "0.85rem", color: "#999", margin: 0 }, children: copyright })
1984
+ ] });
1985
+ }
1986
+ };
1987
+
1988
+ // src/components/cms/ImageBlock.tsx
1989
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
1990
+ var ImageBlock = {
1991
+ label: "Image Block",
1992
+ fields: {
1993
+ image: {
1994
+ type: "custom",
1995
+ label: "Image",
1996
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx23(ImagePickerField, { value: value ?? "", onChange })
1997
+ },
1998
+ caption: { type: "text", label: "Caption" },
1999
+ link: { type: "text", label: "Link URL" }
2000
+ },
2001
+ defaultProps: { image: "", caption: "", link: "" },
2002
+ render: ({ image, caption, link }) => {
2003
+ if (!image) return /* @__PURE__ */ jsx23("div", { style: { padding: "2rem", textAlign: "center", color: "#999", background: "#f5f5f5", borderRadius: "4px" }, children: "No image selected" });
2004
+ const img = /* @__PURE__ */ jsx23(
2005
+ "img",
2006
+ {
2007
+ src: image,
2008
+ alt: caption || "Image",
2009
+ style: { maxWidth: "100%", height: "auto", borderRadius: "4px" }
2010
+ }
2011
+ );
2012
+ return /* @__PURE__ */ jsxs16("figure", { style: { margin: "1rem 0", textAlign: "center" }, children: [
2013
+ link ? /* @__PURE__ */ jsx23("a", { href: link, style: { display: "inline-block" }, children: img }) : img,
2014
+ caption && /* @__PURE__ */ jsx23("figcaption", { style: { marginTop: "0.5rem", fontSize: "0.9rem", color: "#666" }, children: caption })
2015
+ ] });
2016
+ }
2017
+ };
2018
+
2019
+ // src/components/cms/NewsletterSignup.tsx
2020
+ import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
2021
+ var NewsletterSignup = {
2022
+ label: "Newsletter Signup",
2023
+ fields: {
2024
+ title: { type: "text", label: "Title" },
2025
+ subtitle: { type: "text", label: "Subtitle" },
2026
+ ctaText: { type: "text", label: "Button Text" },
2027
+ placeholder: { type: "text", label: "Input Placeholder" }
2028
+ },
2029
+ defaultProps: { title: "", subtitle: "", ctaText: "Subscribe", placeholder: "Enter your email" },
2030
+ render: ({ title, subtitle, ctaText, placeholder }) => /* @__PURE__ */ jsxs17(
2031
+ "div",
2032
+ {
2033
+ style: {
2034
+ textAlign: "center",
2035
+ padding: "2rem 1rem",
2036
+ background: "#f5f5f5",
2037
+ borderRadius: "8px",
2038
+ maxWidth: "480px",
2039
+ margin: "0 auto"
2040
+ },
2041
+ children: [
2042
+ title && /* @__PURE__ */ jsx24("h3", { style: { fontSize: "1.5rem", marginBottom: "0.5rem", color: "#333" }, children: title }),
2043
+ subtitle && /* @__PURE__ */ jsx24("p", { style: { fontSize: "0.95rem", color: "#666", marginBottom: "1.5rem" }, children: subtitle }),
2044
+ /* @__PURE__ */ jsxs17(
2045
+ "form",
2046
+ {
2047
+ onSubmit: (e) => e.preventDefault(),
2048
+ style: { display: "flex", gap: "0.5rem", flexWrap: "wrap", justifyContent: "center" },
2049
+ children: [
2050
+ /* @__PURE__ */ jsx24(
2051
+ "input",
2052
+ {
2053
+ type: "email",
2054
+ placeholder,
2055
+ "aria-label": "Email",
2056
+ style: {
2057
+ padding: "0.75rem 1rem",
2058
+ border: "1px solid #ddd",
2059
+ borderRadius: "4px",
2060
+ fontSize: "1rem",
2061
+ minWidth: "200px"
2062
+ }
2063
+ }
2064
+ ),
2065
+ /* @__PURE__ */ jsx24(
2066
+ "button",
2067
+ {
2068
+ type: "submit",
2069
+ style: {
2070
+ padding: "0.75rem 1.5rem",
2071
+ background: "#2c5530",
2072
+ color: "white",
2073
+ border: "none",
2074
+ borderRadius: "4px",
2075
+ fontWeight: 600,
2076
+ cursor: "pointer"
2077
+ },
2078
+ children: ctaText || "Subscribe"
2079
+ }
2080
+ )
2081
+ ]
2082
+ }
2083
+ )
2084
+ ]
2085
+ }
2086
+ )
2087
+ };
2088
+
2089
+ // src/components/cms/ProductBanner.tsx
2090
+ import { useDatasource as useDatasource3 } from "@commercetools-demo/puck-api";
2091
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
2092
+ var ProductBannerRender = ({
2093
+ title,
2094
+ description,
2095
+ ctaText,
2096
+ ctaLink,
2097
+ product,
2098
+ productOnLeft,
2099
+ background
2100
+ }) => {
2101
+ const hasPreResolved = product?.resolvedData != null;
2102
+ const { data: fetchedData, loading } = useDatasource3(
2103
+ hasPreResolved ? void 0 : product?.type,
2104
+ hasPreResolved ? [] : product?.skus ?? []
2105
+ );
2106
+ const raw = hasPreResolved ? product.resolvedData : fetchedData;
2107
+ const p = raw;
2108
+ const name = getLocalizedText(p?.name);
2109
+ const description2 = getLocalizedText(p?.description);
2110
+ const imageUrl = p?.masterVariant?.images?.[0]?.url;
2111
+ const sku = p?.masterVariant?.sku;
2112
+ const priceVal = getFirstPrice(p);
2113
+ return /* @__PURE__ */ jsxs18(
2114
+ "div",
2115
+ {
2116
+ style: {
2117
+ display: "flex",
2118
+ alignItems: "center",
2119
+ minHeight: "400px",
2120
+ backgroundColor: background || "#f5f5f5",
2121
+ padding: "2rem",
2122
+ borderRadius: "2px",
2123
+ margin: "1rem 0",
2124
+ gap: "2rem",
2125
+ flexDirection: productOnLeft ? "row-reverse" : "row",
2126
+ flexWrap: "wrap"
2127
+ },
2128
+ children: [
2129
+ /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: "200px" }, children: [
2130
+ title && /* @__PURE__ */ jsx25("h2", { style: { fontSize: "2rem", fontWeight: "bold", marginBottom: "1rem", color: "#333" }, children: title }),
2131
+ description && /* @__PURE__ */ jsx25(
2132
+ "div",
2133
+ {
2134
+ dangerouslySetInnerHTML: { __html: description },
2135
+ style: { marginBottom: "2rem", color: "#555", lineHeight: 1.6 }
2136
+ }
2137
+ ),
2138
+ ctaText && ctaLink && /* @__PURE__ */ jsx25(
2139
+ "a",
2140
+ {
2141
+ href: ctaLink,
2142
+ style: {
2143
+ display: "inline-block",
2144
+ backgroundColor: "#2c5530",
2145
+ color: "white",
2146
+ padding: "0.75rem 2rem",
2147
+ borderRadius: "2px",
2148
+ textDecoration: "none",
2149
+ fontWeight: 600
2150
+ },
2151
+ children: ctaText
2152
+ }
2153
+ )
2154
+ ] }),
2155
+ /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: "200px", display: "flex", flexDirection: "column", alignItems: "center" }, children: [
2156
+ loading && /* @__PURE__ */ jsx25("div", { style: { color: "#999", padding: "2rem" }, children: "Loading product\u2026" }),
2157
+ name && /* @__PURE__ */ jsx25("h3", { style: { fontSize: "1.5rem", fontWeight: 600, marginBottom: "0.5rem", textAlign: "center" }, children: name }),
2158
+ description2 && /* @__PURE__ */ jsx25("p", { style: { fontSize: "0.9rem", color: "#666", textAlign: "center", marginBottom: "1rem", fontStyle: "italic" }, children: description2 }),
2159
+ imageUrl && /* @__PURE__ */ jsx25(
2160
+ "img",
2161
+ {
2162
+ src: imageUrl,
2163
+ alt: name || "Product",
2164
+ style: { maxWidth: "100%", maxHeight: "300px", objectFit: "cover", borderRadius: "2px", marginBottom: "1rem", boxShadow: "0 4px 12px rgba(0,0,0,0.1)" }
2165
+ }
2166
+ ),
2167
+ priceVal && /* @__PURE__ */ jsx25(
2168
+ "span",
2169
+ {
2170
+ style: {
2171
+ fontSize: "1.25rem",
2172
+ fontWeight: "bold",
2173
+ color: "#2c5530",
2174
+ background: "rgba(255,255,255,0.8)",
2175
+ padding: "0.5rem 1rem",
2176
+ borderRadius: "2px",
2177
+ marginBottom: "0.5rem"
2178
+ },
2179
+ children: formatPrice2(priceVal.centAmount, priceVal.currencyCode, priceVal.fractionDigits)
2180
+ }
2181
+ ),
2182
+ sku && /* @__PURE__ */ jsxs18("span", { style: { fontSize: "0.85rem", color: "#666", background: "rgba(255,255,255,0.6)", padding: "0.25rem 0.75rem", borderRadius: "2px" }, children: [
2183
+ "SKU: ",
2184
+ sku
2185
+ ] }),
2186
+ !loading && !p && /* @__PURE__ */ jsx25("div", { style: { color: "#999", fontSize: "13px", padding: "2rem", textAlign: "center" }, children: "No product selected" })
2187
+ ] })
2188
+ ]
2189
+ }
2190
+ );
2191
+ };
2192
+ var ProductBanner = {
2193
+ label: "Product Banner",
2194
+ fields: {
2195
+ title: { type: "text", label: "Title" },
2196
+ description: { type: "textarea", label: "Description (HTML)" },
2197
+ ctaText: { type: "text", label: "CTA Text" },
2198
+ ctaLink: { type: "text", label: "CTA Link" },
2199
+ product: {
2200
+ type: "custom",
2201
+ label: "Product",
2202
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx25(DatasourceField, { value, onChange })
2203
+ },
2204
+ productOnLeft: {
2205
+ type: "radio",
2206
+ label: "Product Position",
2207
+ options: [
2208
+ { value: false, label: "Product on Right" },
2209
+ { value: true, label: "Product on Left" }
2210
+ ]
2211
+ },
2212
+ background: { type: "text", label: "Background Color" }
2213
+ },
2214
+ defaultProps: {
2215
+ title: "",
2216
+ description: "",
2217
+ ctaText: "",
2218
+ ctaLink: "",
2219
+ product: { type: "product-by-sku", skus: [""] },
2220
+ productOnLeft: false,
2221
+ background: "#f5f5f5"
2222
+ },
2223
+ render: (props) => /* @__PURE__ */ jsx25(ProductBannerRender, { ...props })
2224
+ };
2225
+
2226
+ // src/components/cms/ProductGridHeader.tsx
2227
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
2228
+ var ProductGridHeader = {
2229
+ label: "Product Grid Header",
2230
+ fields: {
2231
+ title: { type: "text", label: "Title" },
2232
+ description: { type: "textarea", label: "Description (HTML)" }
2233
+ },
2234
+ defaultProps: { title: "", description: "" },
2235
+ render: ({ title, description }) => /* @__PURE__ */ jsxs19("div", { style: { marginBottom: "2rem", padding: "0 1rem" }, children: [
2236
+ title && /* @__PURE__ */ jsx26("h1", { style: { fontSize: "2rem", fontWeight: "bold", color: "#333", marginBottom: "0.75rem" }, children: title }),
2237
+ description && /* @__PURE__ */ jsx26(
2238
+ "div",
2239
+ {
2240
+ dangerouslySetInnerHTML: { __html: description },
2241
+ style: { fontSize: "1rem", color: "#666", lineHeight: 1.5, maxWidth: "720px" }
2242
+ }
2243
+ )
2244
+ ] })
2245
+ };
2246
+
2247
+ // src/components/cms/ProductSlider.tsx
2248
+ import { useDatasource as useDatasource4 } from "@commercetools-demo/puck-api";
2249
+ import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
2250
+ var ProductSliderRender = ({ title, subtitle, products }) => {
2251
+ const hasPreResolved = products?.resolvedData != null;
2252
+ const { data: fetchedData, loading } = useDatasource4(
2253
+ hasPreResolved ? void 0 : products?.type,
2254
+ hasPreResolved ? [] : products?.skus ?? []
2255
+ );
2256
+ const items = hasPreResolved ? Array.isArray(products.resolvedData) ? products.resolvedData : [] : Array.isArray(fetchedData) ? fetchedData : [];
2257
+ return /* @__PURE__ */ jsxs20("div", { style: { padding: "2rem 0" }, children: [
2258
+ (title || subtitle) && /* @__PURE__ */ jsxs20("div", { style: { textAlign: "center", marginBottom: "1.5rem" }, children: [
2259
+ title && /* @__PURE__ */ jsx27("h2", { style: { fontSize: "2rem", fontWeight: "bold", color: "#333", marginBottom: "0.5rem" }, children: title }),
2260
+ subtitle && /* @__PURE__ */ jsx27("p", { style: { fontSize: "1.1rem", color: "#666", margin: 0 }, children: subtitle })
2261
+ ] }),
2262
+ loading && /* @__PURE__ */ jsx27("div", { style: { textAlign: "center", color: "#999", padding: "2rem" }, children: "Loading products\u2026" }),
2263
+ !loading && items.length === 0 && /* @__PURE__ */ jsx27("div", { style: { textAlign: "center", color: "#999", padding: "2rem", fontSize: "13px" }, children: "No products configured" }),
2264
+ items.length > 0 && /* @__PURE__ */ jsx27(
2265
+ "div",
2266
+ {
2267
+ style: {
2268
+ display: "flex",
2269
+ gap: "1rem",
2270
+ overflowX: "auto",
2271
+ scrollSnapType: "x mandatory",
2272
+ paddingBottom: "1rem"
2273
+ },
2274
+ children: items.map((product, i) => {
2275
+ const name = getLocalizedText(product?.name);
2276
+ const desc = getLocalizedText(product?.description);
2277
+ const imageUrl = getProductImage(product);
2278
+ const priceVal = getFirstPrice(product);
2279
+ const slug = getProductSlug(product);
2280
+ return /* @__PURE__ */ jsxs20(
2281
+ "a",
2282
+ {
2283
+ href: slug,
2284
+ style: {
2285
+ flex: "0 0 280px",
2286
+ background: "white",
2287
+ borderRadius: "12px",
2288
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
2289
+ overflow: "hidden",
2290
+ textDecoration: "none",
2291
+ color: "inherit",
2292
+ scrollSnapAlign: "start"
2293
+ },
2294
+ children: [
2295
+ /* @__PURE__ */ jsx27("div", { style: { width: "100%", height: "200px", background: "#f5f5f5", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden" }, children: imageUrl ? /* @__PURE__ */ jsx27("img", { src: imageUrl, alt: name, style: { width: "100%", height: "100%", objectFit: "contain" } }) : /* @__PURE__ */ jsx27("span", { style: { color: "#999", fontSize: "0.85rem" }, children: "No image" }) }),
2296
+ /* @__PURE__ */ jsxs20("div", { style: { padding: "1.5rem" }, children: [
2297
+ /* @__PURE__ */ jsx27("h3", { style: { fontSize: "1.1rem", fontWeight: 600, color: "#333", marginBottom: "0.5rem", lineHeight: 1.3 }, children: name }),
2298
+ desc && /* @__PURE__ */ jsx27("p", { style: { fontSize: "0.9rem", color: "#666", marginBottom: "1rem", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }, children: desc }),
2299
+ priceVal && /* @__PURE__ */ jsx27("div", { style: { fontSize: "1.2rem", fontWeight: "bold", color: "#2c5530" }, children: formatPrice2(priceVal.centAmount, priceVal.currencyCode, priceVal.fractionDigits) })
2300
+ ] })
2301
+ ]
2302
+ },
2303
+ product?.id ?? i
2304
+ );
2305
+ })
2306
+ }
2307
+ )
2308
+ ] });
2309
+ };
2310
+ var ProductSlider = {
2311
+ label: "Product Slider",
2312
+ fields: {
2313
+ title: { type: "text", label: "Title" },
2314
+ subtitle: { type: "text", label: "Subtitle" },
2315
+ products: {
2316
+ type: "custom",
2317
+ label: "Products",
2318
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx27(DatasourceField, { value, onChange })
2319
+ }
2320
+ },
2321
+ defaultProps: {
2322
+ title: "",
2323
+ subtitle: "",
2324
+ products: { type: "products-by-sku", skus: [""] }
2325
+ },
2326
+ render: (props) => /* @__PURE__ */ jsx27(ProductSliderRender, { ...props })
2327
+ };
2328
+
2329
+ // src/components/cms/PromotionalBanner.tsx
2330
+ import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
2331
+ var PromotionalBanner = {
2332
+ label: "Promotional Banner",
2333
+ fields: {
2334
+ image: {
2335
+ type: "custom",
2336
+ label: "Image",
2337
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx28(ImagePickerField, { value: value ?? "", onChange })
2338
+ },
2339
+ title: { type: "text", label: "Title" },
2340
+ subtitle: { type: "text", label: "Subtitle" },
2341
+ ctaText: { type: "text", label: "CTA Text" },
2342
+ ctaLink: { type: "text", label: "CTA Link" },
2343
+ background: { type: "text", label: "Background Color" }
2344
+ },
2345
+ defaultProps: { image: "", title: "", subtitle: "", ctaText: "", ctaLink: "", background: "#f5f5f5" },
2346
+ render: ({ image, title, subtitle, ctaText, ctaLink, background }) => /* @__PURE__ */ jsxs21(
2347
+ "div",
2348
+ {
2349
+ style: {
2350
+ display: "flex",
2351
+ alignItems: "center",
2352
+ justifyContent: "center",
2353
+ minHeight: "280px",
2354
+ backgroundColor: background || "#f5f5f5",
2355
+ padding: "2rem",
2356
+ borderRadius: "2px",
2357
+ margin: "1rem 0",
2358
+ gap: "2rem",
2359
+ flexWrap: "wrap"
2360
+ },
2361
+ children: [
2362
+ image && /* @__PURE__ */ jsx28("div", { style: { flex: "0 0 auto", maxWidth: "40%" }, children: /* @__PURE__ */ jsx28("img", { src: image, alt: title || "Promo", style: { maxWidth: "100%", height: "auto", borderRadius: "2px" } }) }),
2363
+ /* @__PURE__ */ jsxs21("div", { style: { flex: 1, minWidth: "200px" }, children: [
2364
+ title && /* @__PURE__ */ jsx28("h2", { style: { fontSize: "1.75rem", fontWeight: "bold", marginBottom: "0.5rem", color: "#333" }, children: title }),
2365
+ subtitle && /* @__PURE__ */ jsx28("p", { style: { fontSize: "1rem", color: "#666", marginBottom: "1rem" }, children: subtitle }),
2366
+ ctaText && ctaLink && /* @__PURE__ */ jsx28(
2367
+ "a",
2368
+ {
2369
+ href: ctaLink,
2370
+ style: {
2371
+ display: "inline-block",
2372
+ backgroundColor: "#2c5530",
2373
+ color: "white",
2374
+ padding: "0.75rem 2rem",
2375
+ borderRadius: "2px",
2376
+ textDecoration: "none",
2377
+ fontWeight: 600
2378
+ },
2379
+ children: ctaText
2380
+ }
2381
+ )
2382
+ ] })
2383
+ ]
2384
+ }
2385
+ )
2386
+ };
2387
+
2388
+ // src/components/cms/RelatedProductsSlider.tsx
2389
+ import { useDatasource as useDatasource5 } from "@commercetools-demo/puck-api";
2390
+ import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
2391
+ var RelatedProductsRender = ({ title, subtitle, products }) => {
2392
+ const hasPreResolved = products?.resolvedData != null;
2393
+ const { data: fetchedData, loading } = useDatasource5(
2394
+ hasPreResolved ? void 0 : products?.type,
2395
+ hasPreResolved ? [] : products?.skus ?? []
2396
+ );
2397
+ const items = hasPreResolved ? Array.isArray(products.resolvedData) ? products.resolvedData : [] : Array.isArray(fetchedData) ? fetchedData : [];
2398
+ if (!loading && items.length === 0) return null;
2399
+ return /* @__PURE__ */ jsxs22("div", { style: { padding: "2rem 0" }, children: [
2400
+ (title || subtitle) && /* @__PURE__ */ jsxs22("div", { style: { textAlign: "center", marginBottom: "1.5rem" }, children: [
2401
+ title && /* @__PURE__ */ jsx29("h2", { style: { fontSize: "2rem", fontWeight: "bold", color: "#333", marginBottom: "0.5rem" }, children: title }),
2402
+ subtitle && /* @__PURE__ */ jsx29("p", { style: { fontSize: "1.1rem", color: "#666", margin: 0 }, children: subtitle })
2403
+ ] }),
2404
+ loading && /* @__PURE__ */ jsx29("div", { style: { textAlign: "center", color: "#999", padding: "2rem" }, children: "Loading\u2026" }),
2405
+ items.length > 0 && /* @__PURE__ */ jsx29(
2406
+ "div",
2407
+ {
2408
+ style: {
2409
+ display: "flex",
2410
+ gap: "1rem",
2411
+ overflowX: "auto",
2412
+ scrollSnapType: "x mandatory",
2413
+ paddingBottom: "1rem"
2414
+ },
2415
+ children: items.map((product, i) => {
2416
+ const name = getLocalizedText(product?.name);
2417
+ const imageUrl = getProductImage(product);
2418
+ const priceVal = getFirstPrice(product);
2419
+ const slug = getProductSlug(product);
2420
+ return /* @__PURE__ */ jsxs22(
2421
+ "a",
2422
+ {
2423
+ href: slug,
2424
+ style: {
2425
+ flex: "0 0 280px",
2426
+ background: "white",
2427
+ borderRadius: "12px",
2428
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
2429
+ overflow: "hidden",
2430
+ textDecoration: "none",
2431
+ color: "inherit",
2432
+ scrollSnapAlign: "start"
2433
+ },
2434
+ children: [
2435
+ /* @__PURE__ */ jsx29("div", { style: { width: "100%", height: "200px", background: "#f5f5f5", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden" }, children: imageUrl ? /* @__PURE__ */ jsx29("img", { src: imageUrl, alt: name, style: { width: "100%", height: "100%", objectFit: "contain" } }) : /* @__PURE__ */ jsx29("span", { style: { color: "#999", fontSize: "0.85rem" }, children: "No image" }) }),
2436
+ /* @__PURE__ */ jsxs22("div", { style: { padding: "1.5rem" }, children: [
2437
+ /* @__PURE__ */ jsx29("h3", { style: { fontSize: "1.1rem", fontWeight: 600, color: "#333", marginBottom: "0.5rem" }, children: name }),
2438
+ priceVal && /* @__PURE__ */ jsx29("div", { style: { fontSize: "1.2rem", fontWeight: "bold", color: "#2c5530" }, children: formatPrice2(priceVal.centAmount, priceVal.currencyCode, priceVal.fractionDigits) })
2439
+ ] })
2440
+ ]
2441
+ },
2442
+ product?.id ?? i
2443
+ );
2444
+ })
2445
+ }
2446
+ )
2447
+ ] });
2448
+ };
2449
+ var RelatedProductsSlider = {
2450
+ label: "Related Products",
2451
+ fields: {
2452
+ title: { type: "text", label: "Title" },
2453
+ subtitle: { type: "text", label: "Subtitle" },
2454
+ products: {
2455
+ type: "custom",
2456
+ label: "Products",
2457
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx29(DatasourceField, { value, onChange })
2458
+ }
2459
+ },
2460
+ defaultProps: {
2461
+ title: "Related Products",
2462
+ subtitle: "",
2463
+ products: { type: "products-by-sku", skus: [""] }
2464
+ },
2465
+ render: (props) => /* @__PURE__ */ jsx29(RelatedProductsRender, { ...props })
2466
+ };
2467
+
2468
+ // src/components/cms/SocialLinks.tsx
2469
+ import { Fragment as Fragment5, jsx as jsx30 } from "react/jsx-runtime";
2470
+ var SocialLinks = {
2471
+ label: "Social Links",
2472
+ fields: {
2473
+ link1Label: { type: "text", label: "Link 1 Label" },
2474
+ link1Url: { type: "text", label: "Link 1 URL" },
2475
+ link2Label: { type: "text", label: "Link 2 Label" },
2476
+ link2Url: { type: "text", label: "Link 2 URL" },
2477
+ link3Label: { type: "text", label: "Link 3 Label" },
2478
+ link3Url: { type: "text", label: "Link 3 URL" },
2479
+ link4Label: { type: "text", label: "Link 4 Label" },
2480
+ link4Url: { type: "text", label: "Link 4 URL" }
2481
+ },
2482
+ defaultProps: {
2483
+ link1Label: "",
2484
+ link1Url: "",
2485
+ link2Label: "",
2486
+ link2Url: "",
2487
+ link3Label: "",
2488
+ link3Url: "",
2489
+ link4Label: "",
2490
+ link4Url: ""
2491
+ },
2492
+ render: (props) => {
2493
+ const links = [
2494
+ [props.link1Label, props.link1Url],
2495
+ [props.link2Label, props.link2Url],
2496
+ [props.link3Label, props.link3Url],
2497
+ [props.link4Label, props.link4Url]
2498
+ ].filter(([label, url]) => label && url);
2499
+ if (links.length === 0) return /* @__PURE__ */ jsx30(Fragment5, {});
2500
+ return /* @__PURE__ */ jsx30("div", { style: { display: "flex", flexWrap: "wrap", gap: "1rem", padding: "0.5rem 0" }, children: links.map(([label, url], i) => /* @__PURE__ */ jsx30(
2501
+ "a",
2502
+ {
2503
+ href: url,
2504
+ target: "_blank",
2505
+ rel: "noopener noreferrer",
2506
+ style: { color: "#2c5530", textDecoration: "none", fontWeight: 500, fontSize: "0.95rem" },
2507
+ children: label
2508
+ },
2509
+ i
2510
+ )) });
2511
+ }
2512
+ };
2513
+
2514
+ // src/components/cms/TabContent.tsx
2515
+ import { Fragment as Fragment6, jsx as jsx31, jsxs as jsxs23 } from "react/jsx-runtime";
2516
+ var TabContent = {
2517
+ label: "Tab Content",
2518
+ fields: {
2519
+ tabLabel: { type: "text", label: "Tab Label" },
2520
+ content: { type: "textarea", label: "Content (HTML)" }
2521
+ },
2522
+ defaultProps: { tabLabel: "", content: "" },
2523
+ render: ({ tabLabel, content }) => {
2524
+ if (!content) return /* @__PURE__ */ jsx31(Fragment6, {});
2525
+ return /* @__PURE__ */ jsxs23("div", { style: { padding: "1.5rem 0", lineHeight: 1.6, color: "#333" }, children: [
2526
+ tabLabel && /* @__PURE__ */ jsx31("h3", { style: { marginBottom: "1rem" }, children: tabLabel }),
2527
+ /* @__PURE__ */ jsx31("div", { dangerouslySetInnerHTML: { __html: content } })
2528
+ ] });
2529
+ }
2530
+ };
2531
+
2532
+ // src/components/cms/TestimonialsSlider.tsx
2533
+ import { Fragment as Fragment7, jsx as jsx32, jsxs as jsxs24 } from "react/jsx-runtime";
2534
+ var TestimonialsSlider = {
2535
+ label: "Testimonials",
2536
+ fields: {
2537
+ quote1: { type: "textarea", label: "Quote 1" },
2538
+ author1: { type: "text", label: "Author 1" },
2539
+ role1: { type: "text", label: "Role 1" },
2540
+ quote2: { type: "textarea", label: "Quote 2" },
2541
+ author2: { type: "text", label: "Author 2" },
2542
+ role2: { type: "text", label: "Role 2" },
2543
+ quote3: { type: "textarea", label: "Quote 3" },
2544
+ author3: { type: "text", label: "Author 3" },
2545
+ role3: { type: "text", label: "Role 3" }
2546
+ },
2547
+ defaultProps: {
2548
+ quote1: "",
2549
+ author1: "",
2550
+ role1: "",
2551
+ quote2: "",
2552
+ author2: "",
2553
+ role2: "",
2554
+ quote3: "",
2555
+ author3: "",
2556
+ role3: ""
2557
+ },
2558
+ render: (props) => {
2559
+ const items = [
2560
+ [props.quote1, props.author1, props.role1],
2561
+ [props.quote2, props.author2, props.role2],
2562
+ [props.quote3, props.author3, props.role3]
2563
+ ].filter(([q]) => q);
2564
+ if (items.length === 0) return /* @__PURE__ */ jsx32(Fragment7, {});
2565
+ return /* @__PURE__ */ jsx32("div", { style: { padding: "2rem 1rem", background: "#f9f9f9", borderRadius: "8px", margin: "1rem 0" }, children: items.map(([quote, author, role], i) => /* @__PURE__ */ jsxs24("div", { style: { marginBottom: i < items.length - 1 ? "2rem" : 0 }, children: [
2566
+ /* @__PURE__ */ jsx32(
2567
+ "blockquote",
2568
+ {
2569
+ style: {
2570
+ fontSize: "1.15rem",
2571
+ fontStyle: "italic",
2572
+ color: "#444",
2573
+ margin: "0 0 0.75rem 0",
2574
+ paddingLeft: "1rem",
2575
+ borderLeft: "4px solid #2c5530"
2576
+ },
2577
+ children: quote
2578
+ }
2579
+ ),
2580
+ author && /* @__PURE__ */ jsxs24("cite", { style: { fontStyle: "normal", fontSize: "0.95rem", fontWeight: 600, color: "#333" }, children: [
2581
+ "\u2014 ",
2582
+ author,
2583
+ role && /* @__PURE__ */ jsxs24("span", { style: { fontWeight: "normal", color: "#666" }, children: [
2584
+ " (",
2585
+ role,
2586
+ ")"
2587
+ ] })
2588
+ ] })
2589
+ ] }, i)) });
2590
+ }
2591
+ };
2592
+
2593
+ // src/components/cms/ThankYouContent.tsx
2594
+ import { jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
2595
+ var ThankYouContent = {
2596
+ label: "Thank You Content",
2597
+ fields: {
2598
+ headline: { type: "text", label: "Headline" },
2599
+ message: { type: "textarea", label: "Message (HTML)" },
2600
+ ctaText: { type: "text", label: "CTA Text" },
2601
+ ctaLink: { type: "text", label: "CTA Link" }
2602
+ },
2603
+ defaultProps: { headline: "Thank you for your order!", message: "", ctaText: "", ctaLink: "" },
2604
+ render: ({ headline, message, ctaText, ctaLink }) => /* @__PURE__ */ jsxs25("div", { style: { textAlign: "center", padding: "3rem 1rem", maxWidth: "560px", margin: "0 auto" }, children: [
2605
+ headline && /* @__PURE__ */ jsx33("h1", { style: { fontSize: "2rem", color: "#2c5530", marginBottom: "1rem" }, children: headline }),
2606
+ message && /* @__PURE__ */ jsx33(
2607
+ "div",
2608
+ {
2609
+ dangerouslySetInnerHTML: { __html: message },
2610
+ style: { fontSize: "1rem", lineHeight: 1.6, color: "#555", marginBottom: "2rem" }
2611
+ }
2612
+ ),
2613
+ ctaText && ctaLink && /* @__PURE__ */ jsx33(
2614
+ "a",
2615
+ {
2616
+ href: ctaLink,
2617
+ style: {
2618
+ display: "inline-block",
2619
+ background: "#2c5530",
2620
+ color: "white",
2621
+ padding: "0.75rem 2rem",
2622
+ borderRadius: "4px",
2623
+ textDecoration: "none",
2624
+ fontWeight: 600
2625
+ },
2626
+ children: ctaText
2627
+ }
2628
+ )
2629
+ ] })
2630
+ };
2631
+
2632
+ // src/components/cms/TrustBadges.tsx
2633
+ import { Fragment as Fragment8, jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
2634
+ var iconField = (label) => ({
2635
+ type: "custom",
2636
+ label,
2637
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx34(ImagePickerField, { value: value ?? "", onChange })
2638
+ });
2639
+ var TrustBadges = {
2640
+ label: "Trust Badges",
2641
+ fields: {
2642
+ badge1Icon: iconField("Badge 1 Icon"),
2643
+ badge1Label: { type: "text", label: "Badge 1 Label" },
2644
+ badge2Icon: iconField("Badge 2 Icon"),
2645
+ badge2Label: { type: "text", label: "Badge 2 Label" },
2646
+ badge3Icon: iconField("Badge 3 Icon"),
2647
+ badge3Label: { type: "text", label: "Badge 3 Label" },
2648
+ badge4Icon: iconField("Badge 4 Icon"),
2649
+ badge4Label: { type: "text", label: "Badge 4 Label" }
2650
+ },
2651
+ defaultProps: {
2652
+ badge1Icon: "",
2653
+ badge1Label: "",
2654
+ badge2Icon: "",
2655
+ badge2Label: "",
2656
+ badge3Icon: "",
2657
+ badge3Label: "",
2658
+ badge4Icon: "",
2659
+ badge4Label: ""
2660
+ },
2661
+ render: (props) => {
2662
+ const badges = [
2663
+ [props.badge1Icon, props.badge1Label],
2664
+ [props.badge2Icon, props.badge2Label],
2665
+ [props.badge3Icon, props.badge3Label],
2666
+ [props.badge4Icon, props.badge4Label]
2667
+ ].filter(([, label]) => label);
2668
+ if (badges.length === 0) return /* @__PURE__ */ jsx34(Fragment8, {});
2669
+ return /* @__PURE__ */ jsx34(
2670
+ "div",
2671
+ {
2672
+ style: {
2673
+ display: "flex",
2674
+ flexWrap: "wrap",
2675
+ justifyContent: "center",
2676
+ gap: "2rem",
2677
+ padding: "2rem 1rem",
2678
+ background: "#f9f9f9",
2679
+ borderRadius: "4px"
2680
+ },
2681
+ children: badges.map(([icon, label], i) => /* @__PURE__ */ jsxs26("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem" }, children: [
2682
+ icon && /* @__PURE__ */ jsx34(
2683
+ "img",
2684
+ {
2685
+ src: icon,
2686
+ alt: label,
2687
+ style: { width: "40px", height: "40px", objectFit: "contain" }
2688
+ }
2689
+ ),
2690
+ /* @__PURE__ */ jsx34("span", { style: { fontSize: "0.95rem", fontWeight: 500, color: "#333" }, children: label })
2691
+ ] }, i))
2692
+ }
2693
+ );
2694
+ }
2695
+ };
2696
+
2697
+ // src/components/cms/VideoBlock.tsx
2698
+ import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
2699
+ var VideoBlock = {
2700
+ label: "Video Block",
2701
+ fields: {
2702
+ videoUrl: { type: "text", label: "Video URL" },
2703
+ posterImage: {
2704
+ type: "custom",
2705
+ label: "Poster Image",
2706
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx35(ImagePickerField, { value: value ?? "", onChange })
2707
+ },
2708
+ title: { type: "text", label: "Title" },
2709
+ caption: { type: "text", label: "Caption" }
2710
+ },
2711
+ defaultProps: { videoUrl: "", posterImage: "", title: "", caption: "" },
2712
+ render: ({ videoUrl, posterImage, title, caption }) => {
2713
+ if (!videoUrl) return /* @__PURE__ */ jsx35("div", { style: { padding: "2rem", textAlign: "center", color: "#999", background: "#f5f5f5", borderRadius: "8px" }, children: "No video URL configured" });
2714
+ return /* @__PURE__ */ jsxs27("div", { style: { margin: "1.5rem 0" }, children: [
2715
+ /* @__PURE__ */ jsx35(
2716
+ "div",
2717
+ {
2718
+ style: {
2719
+ position: "relative",
2720
+ paddingBottom: "56.25%",
2721
+ height: 0,
2722
+ overflow: "hidden",
2723
+ background: "#000",
2724
+ borderRadius: "8px"
2725
+ },
2726
+ children: /* @__PURE__ */ jsx35(
2727
+ "video",
2728
+ {
2729
+ src: videoUrl,
2730
+ poster: posterImage || void 0,
2731
+ controls: true,
2732
+ preload: "metadata",
2733
+ style: {
2734
+ position: "absolute",
2735
+ top: 0,
2736
+ left: 0,
2737
+ width: "100%",
2738
+ height: "100%",
2739
+ objectFit: "contain"
2740
+ }
2741
+ }
2742
+ )
2743
+ }
2744
+ ),
2745
+ title && /* @__PURE__ */ jsx35("h4", { style: { marginTop: "0.75rem", fontSize: "1.1rem", color: "#333" }, children: title }),
2746
+ caption && /* @__PURE__ */ jsx35("p", { style: { fontSize: "0.9rem", color: "#666", marginTop: "0.25rem" }, children: caption })
2747
+ ] });
2748
+ }
2749
+ };
2750
+
2751
+ // src/components/cms/WebsiteLogo.tsx
2752
+ import { jsx as jsx36 } from "react/jsx-runtime";
2753
+ var WebsiteLogo = {
2754
+ label: "Website Logo",
2755
+ fields: {
2756
+ logo: {
2757
+ type: "custom",
2758
+ label: "Logo",
2759
+ render: ({ value, onChange }) => /* @__PURE__ */ jsx36(ImagePickerField, { value: value ?? "", onChange })
2760
+ },
2761
+ maxWidth: { type: "text", label: "Max Width (px)" },
2762
+ maxHeight: { type: "text", label: "Max Height (px)" }
2763
+ },
2764
+ defaultProps: { logo: "", maxWidth: "180", maxHeight: "50" },
2765
+ render: ({ logo, maxWidth, maxHeight }) => {
2766
+ if (!logo) return /* @__PURE__ */ jsx36("div", { style: { width: `${parseInt(maxWidth, 10) || 180}px`, height: `${parseInt(maxHeight, 10) || 50}px`, background: "#f0f0f0", borderRadius: "4px" } });
2767
+ return /* @__PURE__ */ jsx36(
2768
+ "div",
2769
+ {
2770
+ style: {
2771
+ maxWidth: `${parseInt(maxWidth, 10) || 180}px`,
2772
+ maxHeight: `${parseInt(maxHeight, 10) || 50}px`
2773
+ },
2774
+ children: /* @__PURE__ */ jsx36(
2775
+ "img",
2776
+ {
2777
+ src: logo,
2778
+ alt: "Logo",
2779
+ style: { width: "100%", height: "100%", objectFit: "contain" }
2780
+ }
2781
+ )
2782
+ }
2783
+ );
2784
+ }
2785
+ };
2786
+
2787
+ // src/config/defaultPuckConfig.ts
2788
+ var defaultPuckConfig = {
2789
+ components: {
2790
+ Hero,
2791
+ RichText,
2792
+ Columns,
2793
+ Image,
2794
+ Button,
2795
+ Card,
2796
+ Spacer,
2797
+ ProductTeaser,
2798
+ // CMS components
2799
+ HeroBanner,
2800
+ TextBlock,
2801
+ CategoryGrid,
2802
+ CategoryHero,
2803
+ CheckoutPromoBanner,
2804
+ CountdownBanner,
2805
+ CrossSellBlock,
2806
+ DeliveryMessage,
2807
+ Divider,
2808
+ EmptyState,
2809
+ FAQAccordion,
2810
+ FooterBlock,
2811
+ ImageBlock,
2812
+ NewsletterSignup,
2813
+ ProductBanner,
2814
+ ProductGridHeader,
2815
+ ProductSlider,
2816
+ PromotionalBanner,
2817
+ RelatedProductsSlider,
2818
+ SocialLinks,
2819
+ TabContent,
2820
+ TestimonialsSlider,
2821
+ ThankYouContent,
2822
+ TrustBadges,
2823
+ VideoBlock,
2824
+ WebsiteLogo
2825
+ },
2826
+ root: {
2827
+ fields: {
2828
+ title: { type: "text", label: "Page Title" },
2829
+ backgroundColor: { type: "text", label: "Background Color (CSS)" }
2830
+ },
2831
+ defaultProps: {
2832
+ title: "New Page",
2833
+ backgroundColor: "#ffffff"
2834
+ },
2835
+ render: ({ children, backgroundColor }) => React4.createElement(
2836
+ "div",
2837
+ {
2838
+ style: {
2839
+ background: backgroundColor ?? "#ffffff",
2840
+ minHeight: "100vh",
2841
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
2842
+ }
2843
+ },
2844
+ children
2845
+ )
2846
+ }
2847
+ };
2848
+
2849
+ // src/toolbar/EditorToolbar.tsx
2850
+ import { jsx as jsx37, jsxs as jsxs28 } from "react/jsx-runtime";
2851
+ var BADGE_STYLES = {
2852
+ saving: { bg: "rgba(251, 191, 36, 0.12)", color: "var(--status-saving)", border: "rgba(251, 191, 36, 0.3)" },
2853
+ unsaved: { bg: "rgba(100, 116, 139, 0.12)", color: "var(--text-muted)", border: "rgba(100, 116, 139, 0.3)" },
2854
+ draft: { bg: "rgba(129, 140, 248, 0.12)", color: "var(--status-draft)", border: "rgba(129, 140, 248, 0.3)" },
2855
+ published: { bg: "rgba(6, 214, 160, 0.12)", color: "var(--status-published)", border: "rgba(6, 214, 160, 0.3)" }
2856
+ };
2857
+ var StatusBadge = ({ label, variant }) => {
2858
+ const s3 = BADGE_STYLES[variant];
2859
+ return /* @__PURE__ */ jsx37(
2860
+ "span",
2861
+ {
2862
+ style: {
2863
+ display: "inline-flex",
2864
+ alignItems: "center",
2865
+ gap: "4px",
2866
+ padding: "2px 10px",
2867
+ borderRadius: "12px",
2868
+ fontSize: "12px",
2869
+ fontWeight: 600,
2870
+ background: s3.bg,
2871
+ color: s3.color,
2872
+ border: `1px solid ${s3.border}`
2873
+ },
2874
+ children: label
2875
+ }
2876
+ );
2877
+ };
2878
+ var EditorToolbar = ({
2879
+ saving,
2880
+ isDirty,
2881
+ states,
2882
+ onSave,
2883
+ onPublish,
2884
+ onRevert,
2885
+ showPublishButton
2886
+ }) => {
2887
+ const hasDraft = Boolean(states.draft);
2888
+ const hasPublished = Boolean(states.published);
2889
+ return /* @__PURE__ */ jsxs28(
2890
+ "div",
2891
+ {
2892
+ style: {
2893
+ display: "flex",
2894
+ alignItems: "center",
2895
+ gap: "12px",
2896
+ flexWrap: "wrap"
2897
+ },
2898
+ children: [
2899
+ /* @__PURE__ */ jsxs28("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
2900
+ saving && /* @__PURE__ */ jsx37(StatusBadge, { label: "Saving\u2026", variant: "saving" }),
2901
+ !saving && isDirty && /* @__PURE__ */ jsx37(StatusBadge, { label: "Unsaved", variant: "unsaved" }),
2902
+ !saving && !isDirty && hasDraft && /* @__PURE__ */ jsx37(StatusBadge, { label: "Draft", variant: "draft" }),
2903
+ hasPublished && /* @__PURE__ */ jsx37(StatusBadge, { label: "Published", variant: "published" })
2904
+ ] }),
2905
+ hasDraft && hasPublished && /* @__PURE__ */ jsx37(
2906
+ "button",
2907
+ {
2908
+ onClick: onRevert,
2909
+ disabled: saving,
2910
+ style: {
2911
+ padding: "6px 14px",
2912
+ borderRadius: "4px",
2913
+ border: "1px solid var(--border-glow)",
2914
+ background: "transparent",
2915
+ color: "var(--text-muted)",
2916
+ fontWeight: 500,
2917
+ fontSize: "13px",
2918
+ cursor: saving ? "not-allowed" : "pointer",
2919
+ opacity: saving ? 0.5 : 1
2920
+ },
2921
+ children: "Revert to Published"
2922
+ }
2923
+ ),
2924
+ /* @__PURE__ */ jsx37(
2925
+ "button",
2926
+ {
2927
+ onClick: onSave,
2928
+ disabled: !isDirty || saving,
2929
+ style: {
2930
+ padding: "6px 14px",
2931
+ borderRadius: "4px",
2932
+ border: "1px solid var(--border-glow)",
2933
+ background: "transparent",
2934
+ color: "var(--text-muted)",
2935
+ fontWeight: 500,
2936
+ fontSize: "13px",
2937
+ cursor: !isDirty || saving ? "not-allowed" : "pointer",
2938
+ opacity: !isDirty || saving ? 0.4 : 1
2939
+ },
2940
+ children: "Save"
2941
+ }
2942
+ ),
2943
+ showPublishButton && /* @__PURE__ */ jsx37(
2944
+ "button",
2945
+ {
2946
+ onClick: onPublish,
2947
+ disabled: saving,
2948
+ style: {
2949
+ padding: "6px 16px",
2950
+ borderRadius: "4px",
2951
+ border: "1px solid var(--accent-cyan)",
2952
+ background: "rgba(0, 212, 255, 0.1)",
2953
+ color: "var(--accent-cyan)",
2954
+ fontWeight: 600,
2955
+ fontSize: "13px",
2956
+ cursor: saving ? "not-allowed" : "pointer",
2957
+ opacity: saving ? 0.5 : 1,
2958
+ boxShadow: "0 0 12px rgba(0, 212, 255, 0.15)"
2959
+ },
2960
+ children: hasPublished ? "Re-publish" : "Publish"
2961
+ }
2962
+ )
2963
+ ]
2964
+ }
2965
+ );
2966
+ };
2967
+
2968
+ // src/overrides/ComponentListSearch.tsx
2969
+ import { createContext, useContext, useState as useState4 } from "react";
2970
+ import { Fragment as Fragment9, jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
2971
+ var ComponentSearchContext = createContext({
2972
+ search: "",
2973
+ setSearch: () => {
2974
+ }
2975
+ });
2976
+ var ComponentSearchProvider = ({ children }) => {
2977
+ const [search, setSearch] = useState4("");
2978
+ return /* @__PURE__ */ jsx38(ComponentSearchContext.Provider, { value: { search, setSearch }, children });
2979
+ };
2980
+ var ComponentsPanel = ({ children }) => {
2981
+ const { search, setSearch } = useContext(ComponentSearchContext);
2982
+ return /* @__PURE__ */ jsxs29("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
2983
+ /* @__PURE__ */ jsx38("style", { children: `[data-puck-dnd] > div:has([data-hidden-component]) { display: none !important; }` }),
2984
+ /* @__PURE__ */ jsx38(
2985
+ "div",
2986
+ {
2987
+ style: {
2988
+ flexShrink: 0,
2989
+ padding: "0",
2990
+ margin: "12px 0",
2991
+ background: "var(--puck-color-white, #fff)",
2992
+ position: "sticky",
2993
+ top: 0,
2994
+ zIndex: 1
2995
+ },
2996
+ children: /* @__PURE__ */ jsx38(
2997
+ "input",
2998
+ {
2999
+ type: "search",
3000
+ placeholder: "Search components\u2026",
3001
+ value: search,
3002
+ onChange: (e) => setSearch(e.target.value),
3003
+ style: {
3004
+ width: "100%",
3005
+ boxSizing: "border-box",
3006
+ padding: "6px 10px",
3007
+ borderRadius: "4px",
3008
+ border: "1px solid var(--puck-color-grey-08, #d1d5db)",
3009
+ background: "var(--puck-color-white, #fff)",
3010
+ fontSize: "13px",
3011
+ outline: "none",
3012
+ color: "inherit"
3013
+ }
3014
+ }
3015
+ )
3016
+ }
3017
+ ),
3018
+ /* @__PURE__ */ jsx38("div", { style: { flex: 1, overflowY: "auto" }, children })
3019
+ ] });
3020
+ };
3021
+ var ComponentItemFilter = ({
3022
+ children,
3023
+ name
3024
+ }) => {
3025
+ const { search } = useContext(ComponentSearchContext);
3026
+ const isHidden = search.trim() !== "" && !name.toLowerCase().includes(search.toLowerCase());
3027
+ if (isHidden) {
3028
+ return /* @__PURE__ */ jsx38("span", { "data-hidden-component": true });
3029
+ }
3030
+ return /* @__PURE__ */ jsx38(Fragment9, { children });
3031
+ };
3032
+
3033
+ // src/PuckEditor.tsx
3034
+ import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
3035
+ var PuckEditorInner = ({
3036
+ pageKey,
3037
+ config,
3038
+ onPublish,
3039
+ onSave,
3040
+ onError,
3041
+ showPublishButton,
3042
+ autoSaveDebounceMs: _autoSaveDebounceMs
3043
+ }) => {
3044
+ const {
3045
+ page,
3046
+ states,
3047
+ saving,
3048
+ loading,
3049
+ error,
3050
+ saveDraft,
3051
+ publish,
3052
+ revertToPublished
3053
+ } = usePuckPage(pageKey);
3054
+ const latestDataRef = useRef2(null);
3055
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState5(false);
3056
+ const handleChange = useCallback2((data) => {
3057
+ latestDataRef.current = data;
3058
+ setHasUnsavedChanges(true);
3059
+ }, []);
3060
+ const handleSave = useCallback2(async () => {
3061
+ const data = latestDataRef.current;
3062
+ if (!data) return;
3063
+ try {
3064
+ await saveDraft(data);
3065
+ setHasUnsavedChanges(false);
3066
+ onSave?.(data);
3067
+ } catch (err) {
3068
+ onError?.(err);
3069
+ }
3070
+ }, [saveDraft, onSave, onError]);
3071
+ const handlePublish = useCallback2(
3072
+ async (data) => {
3073
+ try {
3074
+ await saveDraft(data);
3075
+ setHasUnsavedChanges(false);
3076
+ await publish(false);
3077
+ onPublish?.(data);
3078
+ } catch (err) {
3079
+ onError?.(err);
3080
+ }
3081
+ },
3082
+ [saveDraft, publish, onPublish, onError]
3083
+ );
3084
+ const handleRevert = useCallback2(async () => {
3085
+ try {
3086
+ await revertToPublished();
3087
+ setHasUnsavedChanges(false);
3088
+ } catch (err) {
3089
+ onError?.(err);
3090
+ }
3091
+ }, [revertToPublished, onError]);
3092
+ if (loading) {
3093
+ return /* @__PURE__ */ jsx39(
3094
+ "div",
3095
+ {
3096
+ style: {
3097
+ display: "flex",
3098
+ alignItems: "center",
3099
+ justifyContent: "center",
3100
+ height: "100vh",
3101
+ fontSize: "16px",
3102
+ color: "var(--text-muted)",
3103
+ background: "var(--bg-void)"
3104
+ },
3105
+ children: "Loading editor\u2026"
3106
+ }
3107
+ );
3108
+ }
3109
+ if (error) {
3110
+ return /* @__PURE__ */ jsxs30(
3111
+ "div",
3112
+ {
3113
+ style: {
3114
+ padding: "32px",
3115
+ color: "var(--status-error)",
3116
+ background: "rgba(248, 113, 113, 0.08)",
3117
+ border: "1px solid rgba(248, 113, 113, 0.25)",
3118
+ borderRadius: "8px",
3119
+ margin: "16px"
3120
+ },
3121
+ children: [
3122
+ /* @__PURE__ */ jsx39("strong", { children: "Error loading page:" }),
3123
+ " ",
3124
+ error
3125
+ ]
3126
+ }
3127
+ );
3128
+ }
3129
+ const activeData = states.draft?.puckData ?? page?.puckData ?? {
3130
+ content: [],
3131
+ root: { props: {} }
3132
+ };
3133
+ return /* @__PURE__ */ jsx39(ComponentSearchProvider, { children: /* @__PURE__ */ jsx39(
3134
+ Puck,
3135
+ {
3136
+ config,
3137
+ data: activeData,
3138
+ onChange: handleChange,
3139
+ onPublish: handlePublish,
3140
+ overrides: {
3141
+ headerActions: () => /* @__PURE__ */ jsx39(
3142
+ EditorToolbar,
3143
+ {
3144
+ saving,
3145
+ isDirty: hasUnsavedChanges,
3146
+ states,
3147
+ onSave: () => void handleSave(),
3148
+ onPublish: () => void handlePublish(activeData),
3149
+ onRevert: () => void handleRevert(),
3150
+ showPublishButton
3151
+ }
3152
+ ),
3153
+ components: ({ children }) => /* @__PURE__ */ jsx39(ComponentsPanel, { children }),
3154
+ componentItem: ({ children, name }) => /* @__PURE__ */ jsx39(ComponentItemFilter, { name, children })
3155
+ }
3156
+ }
3157
+ ) });
3158
+ };
3159
+ var PuckEditor = ({
3160
+ baseURL,
3161
+ projectKey,
3162
+ businessUnitKey,
3163
+ jwtToken,
3164
+ pageKey,
3165
+ config = defaultPuckConfig,
3166
+ onPublish,
3167
+ onSave,
3168
+ onError,
3169
+ showPublishButton = true,
3170
+ autoSaveDebounceMs = 1500
3171
+ }) => {
3172
+ return /* @__PURE__ */ jsx39(
3173
+ PuckApiProvider,
3174
+ {
3175
+ baseURL,
3176
+ projectKey,
3177
+ businessUnitKey,
3178
+ jwtToken,
3179
+ children: /* @__PURE__ */ jsx39(
3180
+ PuckEditorInner,
3181
+ {
3182
+ pageKey,
3183
+ config,
3184
+ onPublish,
3185
+ onSave,
3186
+ onError,
3187
+ showPublishButton,
3188
+ autoSaveDebounceMs
3189
+ }
3190
+ )
3191
+ }
3192
+ );
3193
+ };
3194
+ export {
3195
+ Button,
3196
+ Card,
3197
+ CategoryGrid,
3198
+ CategoryHero,
3199
+ CheckoutPromoBanner,
3200
+ Columns,
3201
+ ComponentItemFilter,
3202
+ ComponentSearchProvider,
3203
+ ComponentsPanel,
3204
+ CountdownBanner,
3205
+ CrossSellBlock,
3206
+ DatasourceField,
3207
+ DeliveryMessage,
3208
+ Divider,
3209
+ EditorToolbar,
3210
+ EmptyState,
3211
+ FAQAccordion,
3212
+ FooterBlock,
3213
+ Hero,
3214
+ HeroBanner,
3215
+ Image,
3216
+ ImageBlock,
3217
+ ImagePickerField,
3218
+ NewsletterSignup,
3219
+ ProductBanner,
3220
+ ProductGridHeader,
3221
+ ProductSlider,
3222
+ ProductTeaser,
3223
+ PromotionalBanner,
3224
+ PuckEditor,
3225
+ RelatedProductsSlider,
3226
+ RichText,
3227
+ SocialLinks,
3228
+ Spacer,
3229
+ TabContent,
3230
+ TestimonialsSlider,
3231
+ TextBlock,
3232
+ ThankYouContent,
3233
+ TrustBadges,
3234
+ VideoBlock,
3235
+ WebsiteLogo,
3236
+ defaultPuckConfig
3237
+ };
3238
+ //# sourceMappingURL=index.mjs.map