@xiwebdesign/xicms-core 7.2.0 → 7.3.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/XiCMSProvider.d.ts +2 -8
- package/dist/XiCMSProvider.js +1 -8
- package/dist/XiCMSProvider.js.map +1 -1
- package/dist/admin/AdminApp.js +7 -12
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/NewsEditor.d.ts +8 -1
- package/dist/admin/NewsEditor.js +19 -18
- package/dist/admin/NewsEditor.js.map +1 -1
- package/dist/admin/SuperadminPanel.js +6 -7
- package/dist/admin/SuperadminPanel.js.map +1 -1
- package/dist/admin/customBlocks/CustomBlocksPanel.js +3 -4
- package/dist/admin/customBlocks/CustomBlocksPanel.js.map +1 -1
- package/dist/admin/lib/pagesClient.js +3 -4
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/lib/symbolsClient.js +3 -4
- package/dist/admin/lib/symbolsClient.js.map +1 -1
- package/dist/admin/pages/PagesPanel.js +5 -2
- package/dist/admin/pages/PagesPanel.js.map +1 -1
- package/dist/admin/settings/MailSettings.js +2 -3
- package/dist/admin/settings/MailSettings.js.map +1 -1
- package/dist/admin/settings/MailTemplates.js +2 -3
- package/dist/admin/settings/MailTemplates.js.map +1 -1
- package/dist/admin/settings/SettingsPanel.js +250 -225
- package/dist/admin/settings/SettingsPanel.js.map +1 -1
- package/dist/admin/settings/SnapshotsCard.js +3 -4
- package/dist/admin/settings/SnapshotsCard.js.map +1 -1
- package/dist/admin/shop/lib.js +2 -3
- package/dist/admin/shop/lib.js.map +1 -1
- package/dist/admin/visual/VisualEditor.js +239 -103
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin.css +1 -1
- package/dist/blocks/builtins/Agenda.js +6 -6
- package/dist/blocks/builtins/Agenda.js.map +1 -1
- package/dist/blocks/builtins/Banner.js +1 -1
- package/dist/blocks/builtins/Banner.js.map +1 -1
- package/dist/blocks/builtins/Breadcrumbs.js +5 -2
- package/dist/blocks/builtins/Breadcrumbs.js.map +1 -1
- package/dist/blocks/builtins/Cards.js +2 -2
- package/dist/blocks/builtins/Cards.js.map +1 -1
- package/dist/blocks/builtins/Contact.js +3 -3
- package/dist/blocks/builtins/Contact.js.map +1 -1
- package/dist/blocks/builtins/Cta.js +3 -3
- package/dist/blocks/builtins/Cta.js.map +1 -1
- package/dist/blocks/builtins/Downloads.js +5 -5
- package/dist/blocks/builtins/Downloads.js.map +1 -1
- package/dist/blocks/builtins/Elements.js +8 -5
- package/dist/blocks/builtins/Elements.js.map +1 -1
- package/dist/blocks/builtins/Hero.js +1 -1
- package/dist/blocks/builtins/Hero.js.map +1 -1
- package/dist/blocks/builtins/Jobs.js +4 -1
- package/dist/blocks/builtins/Jobs.js.map +1 -1
- package/dist/blocks/builtins/MapBlock.js +11 -11
- package/dist/blocks/builtins/MapBlock.js.map +1 -1
- package/dist/blocks/builtins/Newsletter.js +3 -2
- package/dist/blocks/builtins/Newsletter.js.map +1 -1
- package/dist/blocks/builtins/OpeningHours.js +2 -2
- package/dist/blocks/builtins/OpeningHours.js.map +1 -1
- package/dist/blocks/builtins/PdfBlock.js +2 -2
- package/dist/blocks/builtins/PdfBlock.js.map +1 -1
- package/dist/blocks/builtins/Portfolio.js +4 -4
- package/dist/blocks/builtins/Portfolio.js.map +1 -1
- package/dist/blocks/builtins/Pricing.js +3 -3
- package/dist/blocks/builtins/Pricing.js.map +1 -1
- package/dist/blocks/builtins/Sections.js +7 -7
- package/dist/blocks/builtins/Sections.js.map +1 -1
- package/dist/blocks/builtins/Team.js +3 -3
- package/dist/blocks/builtins/Team.js.map +1 -1
- package/dist/blocks/builtins/Widgets.js +28 -28
- package/dist/blocks/builtins/Widgets.js.map +1 -1
- package/dist/blocks/builtins/lib/submitForm.js +3 -4
- package/dist/blocks/builtins/lib/submitForm.js.map +1 -1
- package/dist/blocks/ui/index.d.ts +8 -1
- package/dist/blocks/ui/index.js +3 -2
- package/dist/blocks/ui/index.js.map +1 -1
- package/dist/components/EditBar.d.ts +9 -1
- package/dist/components/EditBar.js +6 -5
- package/dist/components/EditBar.js.map +1 -1
- package/dist/components/PageView.js +3 -5
- package/dist/components/PageView.js.map +1 -1
- package/dist/firebase/admin.js +4 -0
- package/dist/firebase/admin.js.map +1 -1
- package/dist/firebase/client.js +23 -5
- package/dist/firebase/client.js.map +1 -1
- package/dist/firebase/emulators.d.ts +9 -0
- package/dist/firebase/emulators.js +24 -0
- package/dist/firebase/emulators.js.map +1 -0
- package/dist/firebase/functionsClient.d.ts +18 -0
- package/dist/firebase/functionsClient.js +17 -0
- package/dist/firebase/functionsClient.js.map +1 -0
- package/dist/lock/DemoLock.js +19 -2
- package/dist/lock/DemoLock.js.map +1 -1
- package/dist/modules/shop/cartStore.js +2 -1
- package/dist/modules/shop/cartStore.js.map +1 -1
- package/dist/modules/shop/lib.js +9 -10
- package/dist/modules/shop/lib.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/pages.d.ts +11 -0
- package/dist/server/pages.js +47 -0
- package/dist/server/pages.js.map +1 -1
- package/dist/styles.css +130 -29
- package/dist/theme/color.d.ts +17 -0
- package/dist/theme/color.js +12 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -38,10 +38,14 @@ const GLOBAL_INLINE = [
|
|
|
38
38
|
{ selector: "[data-xicms-edit]", field: "", html: false, fromData: true }
|
|
39
39
|
];
|
|
40
40
|
const inlineCoverageWarned = /* @__PURE__ */ new Set();
|
|
41
|
-
function warnInlineCoverage(type, def, host) {
|
|
41
|
+
function warnInlineCoverage(type, def, host, values) {
|
|
42
42
|
if (process.env.NODE_ENV === "production" || !def || inlineCoverageWarned.has(type)) return;
|
|
43
43
|
inlineCoverageWarned.add(type);
|
|
44
|
-
const
|
|
44
|
+
const zichtbaar = host.textContent ?? "";
|
|
45
|
+
const missing = introspectToFields(def.schema, def.fields).filter((f) => f.type === "text" || f.type === "textarea").filter((f) => {
|
|
46
|
+
const waarde = getPath(values, f.key);
|
|
47
|
+
return typeof waarde === "string" && waarde.trim().length > 1 && zichtbaar.includes(waarde.trim());
|
|
48
|
+
}).map((f) => f.key).filter((key) => !host.querySelector(`[data-xicms-edit="${key}"], [data-xicms-edit$=".${key}"]`));
|
|
45
49
|
if (missing.length > 0) {
|
|
46
50
|
console.warn(
|
|
47
51
|
`[xicms] blok "${type}": tekstveld(en) zonder inline-haak: ${missing.join(", ")} \u2014 voeg data-xicms-edit="<veld>" toe aan de Render zodat ze direct op de pagina bewerkbaar zijn.`
|
|
@@ -93,6 +97,7 @@ function VisualEditorInner({ onClose }) {
|
|
|
93
97
|
const [locale, setLocale] = useState(urlLocale);
|
|
94
98
|
const isTranslating = locale !== localeCfg.defaultLocale;
|
|
95
99
|
const [draft, setDraft] = useState(null);
|
|
100
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
96
101
|
const [status, setStatus] = useState("loading");
|
|
97
102
|
const [selectedKey, setSelectedKey] = useState(null);
|
|
98
103
|
const [publishing, setPublishing] = useState(false);
|
|
@@ -245,6 +250,8 @@ function VisualEditorInner({ onClose }) {
|
|
|
245
250
|
});
|
|
246
251
|
}, [siteId, draftId]);
|
|
247
252
|
const blocks = draft?.blocks ?? [];
|
|
253
|
+
const blocksRef = useRef(blocks);
|
|
254
|
+
blocksRef.current = blocks;
|
|
248
255
|
const overlay = draft?.translations?.[locale];
|
|
249
256
|
const displayBlocks = useMemo(
|
|
250
257
|
() => isTranslating ? localizeBlocks(blocks, overlay) : blocks,
|
|
@@ -305,21 +312,59 @@ function VisualEditorInner({ onClose }) {
|
|
|
305
312
|
},
|
|
306
313
|
[isTranslating, setTranslation, commitInline]
|
|
307
314
|
);
|
|
315
|
+
const [spots, setSpots] = useState([]);
|
|
316
|
+
const targetsRef = useRef([]);
|
|
317
|
+
const editingRef = useRef(null);
|
|
318
|
+
const stopInline = useCallback((el) => {
|
|
319
|
+
el.removeAttribute("contenteditable");
|
|
320
|
+
el.classList.remove("is-editing");
|
|
321
|
+
if (editingRef.current === el) editingRef.current = null;
|
|
322
|
+
}, []);
|
|
323
|
+
const startInline = useCallback((index) => {
|
|
324
|
+
const target = targetsRef.current[index];
|
|
325
|
+
if (!target) return;
|
|
326
|
+
const { el } = target;
|
|
327
|
+
if (editingRef.current && editingRef.current !== el) editingRef.current.blur();
|
|
328
|
+
el.setAttribute("contenteditable", "true");
|
|
329
|
+
el.setAttribute("spellcheck", "true");
|
|
330
|
+
el.classList.add("is-editing");
|
|
331
|
+
editingRef.current = el;
|
|
332
|
+
el.focus();
|
|
333
|
+
const selection = window.getSelection();
|
|
334
|
+
const range = document.createRange();
|
|
335
|
+
range.selectNodeContents(el);
|
|
336
|
+
range.collapse(false);
|
|
337
|
+
selection?.removeAllRanges();
|
|
338
|
+
selection?.addRange(range);
|
|
339
|
+
}, []);
|
|
308
340
|
useEffect(() => {
|
|
309
|
-
if (!selectedKey)
|
|
341
|
+
if (!selectedKey) {
|
|
342
|
+
targetsRef.current = [];
|
|
343
|
+
setSpots([]);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
310
346
|
const host = canvasRef.current?.querySelector(`[data-ve-content="${selectedKey}"]`);
|
|
311
347
|
if (!host) return;
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
|
|
348
|
+
const geselecteerd = blocksRef.current.find((b) => b._key === selectedKey);
|
|
349
|
+
const type = geselecteerd?._type ?? "";
|
|
350
|
+
const def = registry.get(String(type));
|
|
351
|
+
warnInlineCoverage(String(type), def, host, geselecteerd ?? {});
|
|
352
|
+
const labels = /* @__PURE__ */ new Map();
|
|
353
|
+
if (def) {
|
|
354
|
+
try {
|
|
355
|
+
for (const f of introspectToFields(def.schema, def.fields)) labels.set(f.key, f.label);
|
|
356
|
+
} catch {
|
|
357
|
+
}
|
|
358
|
+
}
|
|
315
359
|
const bound = /* @__PURE__ */ new Set();
|
|
316
360
|
const bind = (el, field, html) => {
|
|
317
361
|
if (el.dataset.veInline === "1") return;
|
|
318
362
|
el.dataset.veInline = "1";
|
|
319
|
-
el.setAttribute("contenteditable", "true");
|
|
320
|
-
el.setAttribute("spellcheck", "true");
|
|
321
363
|
el.classList.add("xicms-ve-inline");
|
|
322
|
-
el.addEventListener("blur", () =>
|
|
364
|
+
el.addEventListener("blur", () => {
|
|
365
|
+
commitText(selectedKey, field, html ? DOMPurify.sanitize(el.innerHTML) : el.textContent ?? "");
|
|
366
|
+
stopInline(el);
|
|
367
|
+
});
|
|
323
368
|
if (html) {
|
|
324
369
|
el.addEventListener("focus", () => {
|
|
325
370
|
fmtElRef.current = el;
|
|
@@ -339,30 +384,57 @@ function VisualEditorInner({ onClose }) {
|
|
|
339
384
|
});
|
|
340
385
|
bound.add(el);
|
|
341
386
|
};
|
|
387
|
+
const measure = () => {
|
|
388
|
+
const next = targetsRef.current.map(({ el, field }) => {
|
|
389
|
+
const r = el.getBoundingClientRect();
|
|
390
|
+
return {
|
|
391
|
+
top: Math.min(window.innerHeight - 22, Math.max(4, r.top + r.height / 2 - 9)),
|
|
392
|
+
left: Math.min(window.innerWidth - 22, Math.max(4, r.right + 6)),
|
|
393
|
+
title: `Bewerk: ${labels.get(field) ?? field}`
|
|
394
|
+
};
|
|
395
|
+
});
|
|
396
|
+
setSpots(
|
|
397
|
+
(prev) => prev.length === next.length && prev.every((p, i) => p.top === next[i].top && p.left === next[i].left && p.title === next[i].title) ? prev : next
|
|
398
|
+
);
|
|
399
|
+
};
|
|
342
400
|
const apply = () => {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
)
|
|
349
|
-
);
|
|
401
|
+
const found = [];
|
|
402
|
+
for (const m of GLOBAL_INLINE) {
|
|
403
|
+
host.querySelectorAll(m.selector).forEach((el) => {
|
|
404
|
+
const field = el.dataset.xicmsEdit ?? m.field;
|
|
405
|
+
bind(el, field, !!m.html);
|
|
406
|
+
found.push({ el, field, html: !!m.html });
|
|
407
|
+
});
|
|
350
408
|
}
|
|
409
|
+
targetsRef.current = found;
|
|
410
|
+
measure();
|
|
351
411
|
};
|
|
352
412
|
apply();
|
|
353
413
|
const obs = new MutationObserver(() => {
|
|
354
414
|
if (!draggingRef.current) apply();
|
|
355
415
|
});
|
|
356
416
|
obs.observe(host, { childList: true, subtree: true });
|
|
417
|
+
let frame = 0;
|
|
418
|
+
const onMove = () => {
|
|
419
|
+
cancelAnimationFrame(frame);
|
|
420
|
+
frame = requestAnimationFrame(measure);
|
|
421
|
+
};
|
|
422
|
+
window.addEventListener("scroll", onMove, { capture: true, passive: true });
|
|
423
|
+
window.addEventListener("resize", onMove, { passive: true });
|
|
357
424
|
return () => {
|
|
358
425
|
obs.disconnect();
|
|
426
|
+
cancelAnimationFrame(frame);
|
|
427
|
+
window.removeEventListener("scroll", onMove, { capture: true });
|
|
428
|
+
window.removeEventListener("resize", onMove);
|
|
359
429
|
bound.forEach((el) => {
|
|
360
|
-
el
|
|
430
|
+
stopInline(el);
|
|
361
431
|
el.classList.remove("xicms-ve-inline");
|
|
362
432
|
delete el.dataset.veInline;
|
|
363
433
|
});
|
|
434
|
+
targetsRef.current = [];
|
|
435
|
+
setSpots([]);
|
|
364
436
|
};
|
|
365
|
-
}, [selectedKey,
|
|
437
|
+
}, [selectedKey, commitText, positionFmtBar, stopInline]);
|
|
366
438
|
const dirtySinceRef = useRef(null);
|
|
367
439
|
useEffect(() => {
|
|
368
440
|
if (!draft) return;
|
|
@@ -544,11 +616,33 @@ function VisualEditorInner({ onClose }) {
|
|
|
544
616
|
Select,
|
|
545
617
|
{
|
|
546
618
|
value: locale,
|
|
547
|
-
onValueChange:
|
|
619
|
+
onValueChange: (l) => {
|
|
620
|
+
setLocale(l);
|
|
621
|
+
if (l !== localeCfg.defaultLocale) setPanelOpen(true);
|
|
622
|
+
},
|
|
548
623
|
options: localeCfg.locales.map((l) => ({ value: l, label: l === localeCfg.defaultLocale ? `${l} (standaard)` : l }))
|
|
549
624
|
}
|
|
550
625
|
)
|
|
551
626
|
] }),
|
|
627
|
+
/* @__PURE__ */ jsx(
|
|
628
|
+
"button",
|
|
629
|
+
{
|
|
630
|
+
type: "button",
|
|
631
|
+
className: `xicms-ve__histbtn${panelOpen && !selectedKey ? " is-on" : ""}`,
|
|
632
|
+
onClick: () => {
|
|
633
|
+
if (panelOpen && !selectedKey) {
|
|
634
|
+
setPanelOpen(false);
|
|
635
|
+
} else {
|
|
636
|
+
setSelectedKey(null);
|
|
637
|
+
setPanelOpen(true);
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
title: "Pagina-instellingen (titel, SEO, versies)",
|
|
641
|
+
"aria-label": "Pagina-instellingen",
|
|
642
|
+
"aria-pressed": panelOpen && !selectedKey,
|
|
643
|
+
children: "\u2699"
|
|
644
|
+
}
|
|
645
|
+
),
|
|
552
646
|
/* @__PURE__ */ jsx("button", { type: "button", className: "xicms-ve__histbtn", disabled: !canUndo, onClick: undo, title: "Ongedaan maken (Ctrl+Z)", "aria-label": "Ongedaan maken (Ctrl+Z)", children: "\u21B6" }),
|
|
553
647
|
/* @__PURE__ */ jsx("button", { type: "button", className: "xicms-ve__histbtn", disabled: !canRedo, onClick: redo, title: "Opnieuw (Ctrl+Shift+Z)", "aria-label": "Opnieuw (Ctrl+Shift+Z)", children: "\u21B7" }),
|
|
554
648
|
/* @__PURE__ */ jsx("span", { className: "xicms-ve__status", "aria-live": "polite", children: status === "saving" ? "Opslaan\u2026" : status === "saved" ? "\u2713 Opgeslagen" : status === "error" ? "\u26A0 Niet opgeslagen" : "" }),
|
|
@@ -563,7 +657,7 @@ function VisualEditorInner({ onClose }) {
|
|
|
563
657
|
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => void reloadRemote(), children: "Nieuwste versie laden" }),
|
|
564
658
|
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: () => setConflict(false), children: "Mijn versie behouden" })
|
|
565
659
|
] }),
|
|
566
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
660
|
+
/* @__PURE__ */ jsxs("div", { className: `xicms-ve__body${panelOpen ? " is-panel-open" : ""}`, children: [
|
|
567
661
|
/* @__PURE__ */ jsxs("main", { className: "xicms-ve__canvas xicms-root", "data-dark": darkAuto ? "auto" : void 0, style: themeVars, children: [
|
|
568
662
|
status === "loading" && /* @__PURE__ */ jsx("p", { className: "xicms-ve__hint", children: "Laden\u2026" }),
|
|
569
663
|
status === "none" && /* @__PURE__ */ jsxs("div", { className: "xicms-ve__empty", children: [
|
|
@@ -575,87 +669,110 @@ function VisualEditorInner({ onClose }) {
|
|
|
575
669
|
description: `Voor deze pagina (${path}) bestaat nog geen werkversie. Maak hem hier direct aan, of ga naar /beheer \u2192 Pagina's.`
|
|
576
670
|
}
|
|
577
671
|
),
|
|
578
|
-
/* @__PURE__ */ jsx("p", { style: { textAlign: "center", marginTop: "1rem" }, children: /* @__PURE__ */ jsx(
|
|
672
|
+
/* @__PURE__ */ jsx("p", { style: { textAlign: "center", marginTop: "1rem" }, children: /* @__PURE__ */ jsx(Button, { onClick: () => void handleCreatePage(), children: "+ Deze pagina aanmaken" }) })
|
|
579
673
|
] }),
|
|
580
|
-
draft && /* @__PURE__ */ jsxs(
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
674
|
+
draft && /* @__PURE__ */ jsxs(
|
|
675
|
+
"div",
|
|
676
|
+
{
|
|
677
|
+
className: "container",
|
|
678
|
+
ref: canvasRef,
|
|
679
|
+
onClickCapture: (e) => {
|
|
680
|
+
const el = e.target;
|
|
681
|
+
if (el?.closest?.("a[href]")) e.preventDefault();
|
|
682
|
+
},
|
|
683
|
+
children: [
|
|
684
|
+
blocks.length === 0 && /* @__PURE__ */ jsx(EmptyState, { icon: "\u{1F9F1}", title: "Nog geen blokken", description: "Voeg hieronder je eerste blok toe." }),
|
|
685
|
+
displayBlocks.map((b, i) => {
|
|
686
|
+
const def = registry.get(b._type);
|
|
687
|
+
const Render = def?.Render;
|
|
688
|
+
const safe = (() => {
|
|
689
|
+
const parsed = def?.schema ? safeParse(def.schema, b) : void 0;
|
|
690
|
+
return parsed?.success ? parsed.data : b;
|
|
691
|
+
})();
|
|
692
|
+
const dropClass = dropIndex === i ? " is-drop-before" : dropIndex === i + 1 ? " is-drop-after" : "";
|
|
693
|
+
return /* @__PURE__ */ jsxs(
|
|
694
|
+
"div",
|
|
695
|
+
{
|
|
696
|
+
className: `xicms-ve-block${b._key === selectedKey ? " is-selected" : ""}${dragIndex === i ? " is-dragging" : ""}${dropClass}`,
|
|
697
|
+
onDragOver: (e) => {
|
|
698
|
+
if (dragIndex === null) return;
|
|
699
|
+
e.preventDefault();
|
|
700
|
+
e.dataTransfer.dropEffect = "move";
|
|
701
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
702
|
+
setDropIndex(e.clientY < rect.top + rect.height / 2 ? i : i + 1);
|
|
703
|
+
},
|
|
704
|
+
onDrop: (e) => {
|
|
705
|
+
if (dragIndex === null || dropIndex === null) return;
|
|
706
|
+
e.preventDefault();
|
|
707
|
+
setBlocks(reorderBlocks(blocks, dragIndex, dropIndex));
|
|
708
|
+
endDrag();
|
|
709
|
+
},
|
|
710
|
+
children: [
|
|
711
|
+
/* @__PURE__ */ jsxs("div", { className: "xicms-ve-block__toolbar", children: [
|
|
712
|
+
/* @__PURE__ */ jsx(
|
|
713
|
+
"button",
|
|
714
|
+
{
|
|
715
|
+
type: "button",
|
|
716
|
+
className: "xicms-ve-block__drag",
|
|
717
|
+
draggable: true,
|
|
718
|
+
onDragStart: (e) => {
|
|
719
|
+
draggingRef.current = true;
|
|
720
|
+
setDragIndex(i);
|
|
721
|
+
e.dataTransfer.effectAllowed = "move";
|
|
722
|
+
e.dataTransfer.setData("text/plain", String(i));
|
|
723
|
+
const blockEl = e.currentTarget.closest(".xicms-ve-block");
|
|
724
|
+
if (blockEl) e.dataTransfer.setDragImage(blockEl, 24, 24);
|
|
725
|
+
},
|
|
726
|
+
onDragEnd: endDrag,
|
|
727
|
+
"aria-label": `Versleep ${def?.label ?? b._type}`,
|
|
728
|
+
title: "Slepen om te verplaatsen",
|
|
729
|
+
children: "\u22EE\u22EE"
|
|
730
|
+
}
|
|
731
|
+
),
|
|
732
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-ve-block__label", children: def?.label ?? b._type }),
|
|
733
|
+
/* @__PURE__ */ jsx(
|
|
734
|
+
"button",
|
|
735
|
+
{
|
|
736
|
+
type: "button",
|
|
737
|
+
onClick: () => {
|
|
738
|
+
setSelectedKey(b._key);
|
|
739
|
+
setPanelOpen(true);
|
|
740
|
+
},
|
|
741
|
+
"aria-label": `Instellingen van ${def?.label ?? b._type}`,
|
|
742
|
+
title: "Instellingen (link, kleur, opties)",
|
|
743
|
+
children: "\u2699"
|
|
744
|
+
}
|
|
745
|
+
),
|
|
746
|
+
/* @__PURE__ */ jsx("button", { type: "button", disabled: i === 0, onClick: () => move(i, -1), "aria-label": "Omhoog", children: "\u2191" }),
|
|
747
|
+
/* @__PURE__ */ jsx("button", { type: "button", disabled: i === blocks.length - 1, onClick: () => move(i, 1), "aria-label": "Omlaag", children: "\u2193" }),
|
|
748
|
+
b._type === "symbolRef" && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void detachSymbol(i), "aria-label": "Symbool loskoppelen", title: "Loskoppelen (losse kopie)", children: "\u{1F517}\u2702" }),
|
|
749
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => duplicate(i), "aria-label": "Dupliceren", children: "\u2398" }),
|
|
750
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void remove(i), "aria-label": "Verwijderen", children: "\u{1F5D1}" })
|
|
751
|
+
] }),
|
|
752
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "xicms-ve-block__select", "aria-label": `Selecteer ${def?.label ?? b._type}`, onClick: () => setSelectedKey(b._key) }),
|
|
753
|
+
/* @__PURE__ */ jsx("div", { className: "xicms-ve-block__content", "data-ve-content": b._key, children: Render ? /* @__PURE__ */ jsx(BlockErrorBoundary, { mode: "preview", type: b._type, blockKey: b._key, children: /* @__PURE__ */ jsx(InlineImageEditProvider, { onReplace: b._key === selectedKey ? (field) => setImgPicker({ key: b._key, field }) : null, children: /* @__PURE__ */ jsx(Render, { block: safe, siteId, mode: "preview" }) }) }) : /* @__PURE__ */ jsxs("p", { className: "xicms-ve__hint", children: [
|
|
754
|
+
"Onbekend blok: ",
|
|
755
|
+
b._type
|
|
756
|
+
] }) })
|
|
757
|
+
]
|
|
758
|
+
},
|
|
759
|
+
b._key
|
|
760
|
+
);
|
|
761
|
+
}),
|
|
762
|
+
/* @__PURE__ */ jsxs("div", { className: "xicms-ve__inserter", children: [
|
|
763
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
764
|
+
"Toevoegen",
|
|
765
|
+
selected ? " (na selectie)" : "",
|
|
766
|
+
":"
|
|
767
|
+
] }),
|
|
768
|
+
/* @__PURE__ */ jsx("div", { className: "xicms-ve__inserter-select", children: /* @__PURE__ */ jsx(Select, { value: "", placeholder: "Kies een blok\u2026", onValueChange: (v) => v && addBlock(v), options: addOptions }) }),
|
|
769
|
+
/* @__PURE__ */ jsx(PatternInserter, { onInsert: insertAfterSelected })
|
|
770
|
+
] })
|
|
771
|
+
]
|
|
772
|
+
}
|
|
773
|
+
)
|
|
657
774
|
] }),
|
|
658
|
-
/* @__PURE__ */ jsx("aside", { className: "xicms-ve__panel", children: selected && isTranslating ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
775
|
+
panelOpen && /* @__PURE__ */ jsx("aside", { className: "xicms-ve__panel", children: selected && isTranslating ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
659
776
|
/* @__PURE__ */ jsxs("h3", { className: "text-sm font-semibold", children: [
|
|
660
777
|
registry.get(selected._type)?.label ?? selected._type,
|
|
661
778
|
" \u2014 vertaling (",
|
|
@@ -677,15 +794,15 @@ function VisualEditorInner({ onClose }) {
|
|
|
677
794
|
] }) : selected ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
678
795
|
/* @__PURE__ */ jsxs("div", { className: "xicms-ve__panel-head", children: [
|
|
679
796
|
/* @__PURE__ */ jsx(
|
|
680
|
-
|
|
797
|
+
Button,
|
|
681
798
|
{
|
|
682
|
-
|
|
683
|
-
|
|
799
|
+
size: "sm",
|
|
800
|
+
variant: status === "saving" || status === "error" ? "primary" : "ghost",
|
|
684
801
|
onClick: () => void flushSave().catch(() => setStatus("error")),
|
|
685
802
|
children: status === "saving" ? "Opslaan\u2026" : status === "error" ? "\u26A0 Opnieuw opslaan" : "\u2713 Opgeslagen"
|
|
686
803
|
}
|
|
687
804
|
),
|
|
688
|
-
/* @__PURE__ */ jsx(
|
|
805
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => setPanelOpen(false), children: "\u2715 Sluiten" })
|
|
689
806
|
] }),
|
|
690
807
|
/* @__PURE__ */ jsx(SelectedEditor, { block: selected, siteId, registry, onChange: (next) => updateByKey(selected._key, next) }, selected._key)
|
|
691
808
|
] }) : draft && isTranslating ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
|
|
@@ -729,6 +846,25 @@ function VisualEditorInner({ onClose }) {
|
|
|
729
846
|
}
|
|
730
847
|
}
|
|
731
848
|
),
|
|
849
|
+
spots.map((s, i) => /* @__PURE__ */ jsx(
|
|
850
|
+
"button",
|
|
851
|
+
{
|
|
852
|
+
type: "button",
|
|
853
|
+
className: "xicms-ve-pencil",
|
|
854
|
+
style: { top: s.top, left: s.left },
|
|
855
|
+
title: s.title,
|
|
856
|
+
"aria-label": s.title,
|
|
857
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
858
|
+
onMouseEnter: () => targetsRef.current[i]?.el.classList.add("is-hover"),
|
|
859
|
+
onMouseLeave: () => targetsRef.current[i]?.el.classList.remove("is-hover"),
|
|
860
|
+
onClick: () => startInline(i),
|
|
861
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
862
|
+
/* @__PURE__ */ jsx("path", { d: "M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z" }),
|
|
863
|
+
/* @__PURE__ */ jsx("path", { d: "M14.5 6.5l3 3" })
|
|
864
|
+
] })
|
|
865
|
+
},
|
|
866
|
+
i
|
|
867
|
+
)),
|
|
732
868
|
fmtBar && /* @__PURE__ */ jsxs("div", { className: "xicms-ve-fmtbar", style: { top: fmtBar.top, left: fmtBar.left }, role: "toolbar", "aria-label": "Tekstopmaak", children: [
|
|
733
869
|
/* @__PURE__ */ jsx(FmtBtn, { label: /* @__PURE__ */ jsx("b", { children: "B" }), title: "Vet (Ctrl/Cmd+B)", onRun: () => document.execCommand("bold") }),
|
|
734
870
|
/* @__PURE__ */ jsx(FmtBtn, { label: /* @__PURE__ */ jsx("i", { children: "I" }), title: "Cursief (Ctrl/Cmd+I)", onRun: () => document.execCommand("italic") }),
|