@xiwebdesign/xicms-core 7.6.1 → 7.8.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/admin/AdminApp.js +3 -1
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/lib/pagesClient.d.ts +13 -0
- package/dist/admin/lib/pagesClient.js +13 -0
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/shop/ProductPaneel.d.ts +15 -0
- package/dist/admin/shop/ProductPaneel.js +55 -0
- package/dist/admin/shop/ProductPaneel.js.map +1 -0
- package/dist/admin/shop/ProductsTab.d.ts +27 -0
- package/dist/admin/shop/ProductsTab.js +3 -2
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/productClient.d.ts +32 -0
- package/dist/admin/shop/productClient.js +31 -0
- package/dist/admin/shop/productClient.js.map +1 -0
- package/dist/admin/visual/VisualEditor.js +296 -43
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin/visual/wijzigingen.d.ts +46 -0
- package/dist/admin/visual/wijzigingen.js +78 -0
- package/dist/admin/visual/wijzigingen.js.map +1 -0
- package/dist/auth/AuthProvider.js +22 -3
- package/dist/auth/AuthProvider.js.map +1 -1
- package/dist/auth/LoginForm.js +7 -2
- package/dist/auth/LoginForm.js.map +1 -1
- package/dist/components/EditBar.d.ts +30 -0
- package/dist/components/EditBar.js +32 -6
- package/dist/components/EditBar.js.map +1 -1
- package/dist/editor/EditorProvider.d.ts +5 -0
- package/dist/editor/EditorProvider.js +28 -11
- package/dist/editor/EditorProvider.js.map +1 -1
- package/dist/modules/shop/blocks/ProductDetail.js +3 -3
- package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
- package/dist/modules/shop/blocks/ProductGrid.js +5 -2
- package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
- package/dist/styles.css +163 -46
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/shop/productClient.ts"],"sourcesContent":["\"use client\";\n\nimport { doc, getDoc, onSnapshot, updateDoc } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport type { Product } from \"../../schema/shop\";\n\nexport type StoredProduct = Product & { id: string };\n\n/**\n * Eén product, live — voor het paneel dat de bewerkmodus opent bij een productblok.\n *\n * Een abonnement en geen eenmalige lezing: hetzelfde product kan tegelijk openstaan in het\n * Webwinkel-paneel of in een tweede tabblad, en dan hoort dit paneel niet met een oude prijs te\n * blijven staan.\n */\nexport function subscribeProduct(\n siteId: string,\n productId: string,\n cb: (p: StoredProduct | null) => void,\n): () => void {\n const db = getDb();\n if (!db) {\n cb(null);\n return () => {};\n }\n return onSnapshot(doc(db, \"sites\", siteId, \"products\", productId), (snap) => {\n cb(snap.exists() ? ({ id: snap.id, ...(snap.data() as Product) }) : null);\n });\n}\n\n/**\n * Schrijf één veld van een product weg — de inline-tegenhanger van het volledige formulier.\n *\n * ── WAAROM DIT ANDERS IS DAN EEN TEKST OP EEN PAGINA ──────────────────────────────────────\n *\n * Een blokveld gaat naar de WERKVERSIE van die ene pagina en wordt pas zichtbaar als je\n * publiceert. Een product heeft geen werkversie: het staat in de productencollectie en wordt door\n * elke pagina gelezen die het toont. Wie hier de naam wijzigt, wijzigt hem dus METEEN en OVERAL —\n * op de cataloguspagina, in de zoekresultaten, op de bestelbevestiging.\n *\n * Dat verschil is niet weg te poetsen zonder een tweede publicatiestroom voor producten te bouwen,\n * en dat zou de webwinkel ingewikkelder maken dan hij is. Het staat daarom expliciet in de tekst\n * bij het potlood, zodat het een keuze is en geen verrassing.\n *\n * `updateDoc` en niet `setDoc`: een product heeft velden die dit pad niet kent (varianten,\n * voorraad, personalisatie). Een hele-document-schrijfactie zou die stilzwijgend wissen.\n */\nexport async function setProductField(\n siteId: string,\n productId: string,\n field: string,\n value: unknown,\n): Promise<void> {\n const db = getDb();\n if (!db) throw new Error(\"Firebase niet geconfigureerd.\");\n await updateDoc(doc(db, \"sites\", siteId, \"products\", productId), { [field]: value });\n}\n\n/** Eenmalige lezing — voor het moment waarop de editor moet weten wat er nú staat. */\nexport async function getProduct(siteId: string, productId: string): Promise<StoredProduct | null> {\n const db = getDb();\n if (!db) return null;\n const snap = await getDoc(doc(db, \"sites\", siteId, \"products\", productId));\n return snap.exists() ? ({ id: snap.id, ...(snap.data() as Product) }) : null;\n}\n"],"mappings":";AAEA,SAAS,KAAK,QAAQ,YAAY,iBAAiB;AACnD,SAAS,aAAa;AAYf,SAAS,iBACd,QACA,WACA,IACY;AACZ,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,IAAI;AACP,OAAG,IAAI;AACP,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,SAAO,WAAW,IAAI,IAAI,SAAS,QAAQ,YAAY,SAAS,GAAG,CAAC,SAAS;AAC3E,OAAG,KAAK,OAAO,IAAK,EAAE,IAAI,KAAK,IAAI,GAAI,KAAK,KAAK,EAAc,IAAK,IAAI;AAAA,EAC1E,CAAC;AACH;AAmBA,eAAsB,gBACpB,QACA,WACA,OACA,OACe;AACf,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,+BAA+B;AACxD,QAAM,UAAU,IAAI,IAAI,SAAS,QAAQ,YAAY,SAAS,GAAG,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC;AACrF;AAGA,eAAsB,WAAW,QAAgB,WAAkD;AACjG,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,OAAO,MAAM,OAAO,IAAI,IAAI,SAAS,QAAQ,YAAY,SAAS,CAAC;AACzE,SAAO,KAAK,OAAO,IAAK,EAAE,IAAI,KAAK,IAAI,GAAI,KAAK,KAAK,EAAc,IAAK;AAC1E;","names":[]}
|
|
@@ -12,12 +12,16 @@ import { useNestedDialogOpen } from "../lib/useNestedDialog.js";
|
|
|
12
12
|
import { useThemeVars } from "../../XiCMSProvider.js";
|
|
13
13
|
import { buildBlockAddOptions } from "../../blocks/registry.js";
|
|
14
14
|
import DOMPurify from "dompurify";
|
|
15
|
-
import { createPage, getDraftByPath, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion } from "../lib/pagesClient.js";
|
|
15
|
+
import { createPage, getDraftByPath, getPublishedPage, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion } from "../lib/pagesClient.js";
|
|
16
16
|
import { isDemoActive } from "../../demo/demoStore.js";
|
|
17
|
+
import { setProductField } from "../shop/productClient.js";
|
|
18
|
+
import { ProductPaneel } from "../shop/ProductPaneel.js";
|
|
17
19
|
import { builtinBlockEditors, GenericEditor } from "../blockEditors/index.js";
|
|
18
20
|
import { StyleEditor } from "../blockEditors/StyleEditor.js";
|
|
19
21
|
import { siteLocales, parseLocaleSlugs, localizeBlocks } from "../../i18n/overlay.js";
|
|
20
22
|
import { useEditor } from "../../editor/EditorProvider.js";
|
|
23
|
+
import { AdminModes } from "../../components/EditBar.js";
|
|
24
|
+
import { aantalWijzigingen, bepaalWijzigingen, GEEN_WIJZIGINGEN } from "./wijzigingen.js";
|
|
21
25
|
import { TranslationPanel } from "./TranslationPanel.js";
|
|
22
26
|
import { PatternInserter } from "../patterns/PatternInserter.js";
|
|
23
27
|
import { Button, Field, Input, Textarea } from "../ui/primitives.js";
|
|
@@ -97,11 +101,13 @@ function VisualEditorInner({ onClose }) {
|
|
|
97
101
|
const [panelOpen, setPanelOpen] = useState(false);
|
|
98
102
|
const [status, setStatus] = useState("loading");
|
|
99
103
|
const [selectedKey, setSelectedKey] = useState(null);
|
|
104
|
+
const [selectedProduct, setSelectedProduct] = useState(null);
|
|
100
105
|
const [publishing, setPublishing] = useState(false);
|
|
101
106
|
const [imgPicker, setImgPicker] = useState(null);
|
|
102
107
|
const firstSave = useRef(true);
|
|
103
108
|
const canvasRef = useRef(null);
|
|
104
109
|
const [conflict, setConflict] = useState(false);
|
|
110
|
+
const [liveBlocks, setLiveBlocks] = useState(void 0);
|
|
105
111
|
const { vars: themeVars, darkAuto } = useThemeVars();
|
|
106
112
|
const nestedDialogOpen = useNestedDialogOpen();
|
|
107
113
|
const { containerRef, focusFirst } = useFocusTrap({ isActive: !nestedDialogOpen });
|
|
@@ -215,6 +221,20 @@ function VisualEditorInner({ onClose }) {
|
|
|
215
221
|
active = false;
|
|
216
222
|
};
|
|
217
223
|
}, [siteId, path]);
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
const id = draft?.id;
|
|
226
|
+
if (!id) {
|
|
227
|
+
setLiveBlocks(void 0);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
let active = true;
|
|
231
|
+
getPublishedPage(siteId, id).then((pagina) => {
|
|
232
|
+
if (active) setLiveBlocks(pagina?.blocks ?? null);
|
|
233
|
+
}).catch(() => active && setLiveBlocks(void 0));
|
|
234
|
+
return () => {
|
|
235
|
+
active = false;
|
|
236
|
+
};
|
|
237
|
+
}, [siteId, draft?.id]);
|
|
218
238
|
const handleCreatePage = useCallback(async () => {
|
|
219
239
|
setStatus("loading");
|
|
220
240
|
try {
|
|
@@ -254,6 +274,12 @@ function VisualEditorInner({ onClose }) {
|
|
|
254
274
|
() => isTranslating ? localizeBlocks(blocks, overlay) : blocks,
|
|
255
275
|
[blocks, isTranslating, overlay]
|
|
256
276
|
);
|
|
277
|
+
const nietLiveRef = useRef(false);
|
|
278
|
+
const wijzigingen = useMemo(
|
|
279
|
+
() => liveBlocks === void 0 ? GEEN_WIJZIGINGEN : bepaalWijzigingen(blocks, liveBlocks),
|
|
280
|
+
[blocks, liveBlocks]
|
|
281
|
+
);
|
|
282
|
+
nietLiveRef.current = wijzigingen.iets;
|
|
257
283
|
const { publishDraft } = useEditor();
|
|
258
284
|
const laatstGepubliceerd = useRef("");
|
|
259
285
|
useEffect(() => {
|
|
@@ -320,28 +346,41 @@ function VisualEditorInner({ onClose }) {
|
|
|
320
346
|
);
|
|
321
347
|
const registryRef = useRef(registry);
|
|
322
348
|
registryRef.current = registry;
|
|
323
|
-
|
|
349
|
+
function veiligeBovenkant() {
|
|
350
|
+
let onderkant = 0;
|
|
351
|
+
for (const el of Array.from(document.querySelectorAll("body *"))) {
|
|
352
|
+
const pos = getComputedStyle(el).position;
|
|
353
|
+
if (pos !== "fixed" && pos !== "sticky") continue;
|
|
354
|
+
const r = el.getBoundingClientRect();
|
|
355
|
+
if (r.height === 0 || r.top > 4) continue;
|
|
356
|
+
onderkant = Math.max(onderkant, r.bottom);
|
|
357
|
+
}
|
|
358
|
+
return onderkant;
|
|
359
|
+
}
|
|
360
|
+
const [blokknoppen, setBlokknoppen] = useState([]);
|
|
324
361
|
useEffect(() => {
|
|
325
362
|
if (!draft) {
|
|
326
|
-
|
|
363
|
+
setBlokknoppen((v) => v.length === 0 ? v : []);
|
|
327
364
|
return;
|
|
328
365
|
}
|
|
329
366
|
const meet = () => {
|
|
367
|
+
const veilig = veiligeBovenkant();
|
|
330
368
|
const gevonden = [];
|
|
331
369
|
for (const el of Array.from(document.querySelectorAll("[data-xicms-block]"))) {
|
|
332
370
|
const key = el.dataset.xicmsBlock;
|
|
333
371
|
if (!key) continue;
|
|
334
372
|
const r = el.getBoundingClientRect();
|
|
335
373
|
if (r.width === 0 && r.height === 0) continue;
|
|
374
|
+
if (r.bottom < veilig || r.top > window.innerHeight) continue;
|
|
336
375
|
const type = el.dataset.xicmsBlockType ?? "";
|
|
337
376
|
gevonden.push({
|
|
338
377
|
key,
|
|
339
|
-
top: Math.max(r.top + 8,
|
|
340
|
-
left: Math.max(r.right -
|
|
378
|
+
top: Math.max(r.top + 8, veilig + 8),
|
|
379
|
+
left: Math.max(r.right - 62, 8),
|
|
341
380
|
label: registryRef.current.get(type)?.label ?? type
|
|
342
381
|
});
|
|
343
382
|
}
|
|
344
|
-
|
|
383
|
+
setBlokknoppen((vorige) => {
|
|
345
384
|
if (vorige.length === gevonden.length && vorige.every((v, i) => v.key === gevonden[i].key && v.top === gevonden[i].top && v.left === gevonden[i].left)) {
|
|
346
385
|
return vorige;
|
|
347
386
|
}
|
|
@@ -356,9 +395,117 @@ function VisualEditorInner({ onClose }) {
|
|
|
356
395
|
window.removeEventListener("resize", meet);
|
|
357
396
|
};
|
|
358
397
|
}, [draft, displayBlocks, registryRef]);
|
|
398
|
+
const [productKnop, setProductKnop] = useState(null);
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
if (!draft) return;
|
|
401
|
+
const onOver = (e) => {
|
|
402
|
+
const doel = e.target?.closest?.("[data-xicms-product]");
|
|
403
|
+
if (!doel) {
|
|
404
|
+
const naarKnop = e.target?.closest?.(".xicms-ve-blokknoppen");
|
|
405
|
+
if (!naarKnop) setProductKnop(null);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const id = doel.dataset.xicmsProduct;
|
|
409
|
+
if (!id) return;
|
|
410
|
+
const r = doel.getBoundingClientRect();
|
|
411
|
+
setProductKnop(
|
|
412
|
+
(v) => v && v.id === id && v.top === Math.max(r.top + 8, 44) && v.left === Math.max(r.right - 62, 8) ? v : { id, top: Math.max(r.top + 8, 44), left: Math.max(r.right - 62, 8) }
|
|
413
|
+
);
|
|
414
|
+
};
|
|
415
|
+
document.addEventListener("mouseover", onOver, true);
|
|
416
|
+
return () => document.removeEventListener("mouseover", onOver, true);
|
|
417
|
+
}, [draft]);
|
|
418
|
+
const productBewerkRef = useRef(null);
|
|
419
|
+
const startProductInline = useCallback(
|
|
420
|
+
(productId) => {
|
|
421
|
+
const host = document.querySelector(`[data-xicms-product="${CSS.escape(productId)}"]`);
|
|
422
|
+
const el = host?.querySelector("[data-xicms-product-edit]");
|
|
423
|
+
if (!el) {
|
|
424
|
+
toast({
|
|
425
|
+
title: "Hier valt niets te typen",
|
|
426
|
+
description: "Gebruik het tandwiel voor prijs, foto's, varianten en voorraad."
|
|
427
|
+
});
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const veld = el.dataset.xicmsProductEdit;
|
|
431
|
+
if (!veld) return;
|
|
432
|
+
const origineel = el.textContent ?? "";
|
|
433
|
+
el.setAttribute("contenteditable", "true");
|
|
434
|
+
el.setAttribute("spellcheck", "true");
|
|
435
|
+
el.classList.add("is-editing");
|
|
436
|
+
productBewerkRef.current = el;
|
|
437
|
+
const stop = () => {
|
|
438
|
+
el.removeAttribute("contenteditable");
|
|
439
|
+
el.classList.remove("is-editing");
|
|
440
|
+
el.removeEventListener("blur", opBlur);
|
|
441
|
+
el.removeEventListener("keydown", opToets);
|
|
442
|
+
if (productBewerkRef.current === el) productBewerkRef.current = null;
|
|
443
|
+
};
|
|
444
|
+
const opBlur = () => {
|
|
445
|
+
const waarde = el.textContent ?? "";
|
|
446
|
+
stop();
|
|
447
|
+
if (waarde === origineel) return;
|
|
448
|
+
void setProductField(siteId, productId, veld, waarde).then(
|
|
449
|
+
() => toast({
|
|
450
|
+
title: "Product bijgewerkt",
|
|
451
|
+
description: "Producten hebben geen werkversie: dit staat meteen live, op elke pagina.",
|
|
452
|
+
tone: "success"
|
|
453
|
+
})
|
|
454
|
+
).catch((err) => {
|
|
455
|
+
el.textContent = origineel;
|
|
456
|
+
toast({ title: "Opslaan mislukt", description: err.message, tone: "error" });
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
const opToets = (ev) => {
|
|
460
|
+
if (ev.key === "Escape") {
|
|
461
|
+
ev.stopPropagation();
|
|
462
|
+
el.textContent = origineel;
|
|
463
|
+
el.blur();
|
|
464
|
+
} else if (ev.key === "Enter") {
|
|
465
|
+
ev.preventDefault();
|
|
466
|
+
el.blur();
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
el.addEventListener("blur", opBlur);
|
|
470
|
+
el.addEventListener("keydown", opToets);
|
|
471
|
+
el.focus();
|
|
472
|
+
const selection = window.getSelection();
|
|
473
|
+
const range = document.createRange();
|
|
474
|
+
range.selectNodeContents(el);
|
|
475
|
+
range.collapse(false);
|
|
476
|
+
selection?.removeAllRanges();
|
|
477
|
+
selection?.addRange(range);
|
|
478
|
+
},
|
|
479
|
+
[siteId, toast]
|
|
480
|
+
);
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
if (!draft) return;
|
|
483
|
+
const gemarkeerd = [];
|
|
484
|
+
const markeer = (el, klasse) => {
|
|
485
|
+
if (!el) return;
|
|
486
|
+
el.classList.add(klasse);
|
|
487
|
+
gemarkeerd.push(el);
|
|
488
|
+
};
|
|
489
|
+
for (const key of wijzigingen.blokken) {
|
|
490
|
+
markeer(document.querySelector(`[data-xicms-block="${CSS.escape(key)}"]`), "xicms-ve-nieuw");
|
|
491
|
+
}
|
|
492
|
+
for (const [key, velden] of wijzigingen.velden) {
|
|
493
|
+
const host = document.querySelector(`[data-xicms-block="${CSS.escape(key)}"]`);
|
|
494
|
+
if (!host) continue;
|
|
495
|
+
for (const veld of velden) {
|
|
496
|
+
host.querySelectorAll(`[data-xicms-edit="${CSS.escape(veld)}"]`).forEach((el) => markeer(el, "xicms-ve-gewijzigd"));
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return () => {
|
|
500
|
+
for (const el of gemarkeerd) {
|
|
501
|
+
el.classList.remove("xicms-ve-nieuw", "xicms-ve-gewijzigd");
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}, [draft, wijzigingen, displayBlocks]);
|
|
359
505
|
const [spots, setSpots] = useState([]);
|
|
360
506
|
const targetsRef = useRef([]);
|
|
361
507
|
const editingRef = useRef(null);
|
|
508
|
+
const wilInlineRef = useRef(false);
|
|
362
509
|
const stopInline = useCallback((el) => {
|
|
363
510
|
el.removeAttribute("contenteditable");
|
|
364
511
|
el.classList.remove("is-editing");
|
|
@@ -420,8 +567,10 @@ function VisualEditorInner({ onClose }) {
|
|
|
420
567
|
});
|
|
421
568
|
}
|
|
422
569
|
el.addEventListener("keydown", (e) => {
|
|
423
|
-
if (e.key === "Escape")
|
|
424
|
-
|
|
570
|
+
if (e.key === "Escape") {
|
|
571
|
+
e.stopPropagation();
|
|
572
|
+
el.blur();
|
|
573
|
+
} else if (!html && e.key === "Enter") {
|
|
425
574
|
e.preventDefault();
|
|
426
575
|
el.blur();
|
|
427
576
|
}
|
|
@@ -454,6 +603,16 @@ function VisualEditorInner({ onClose }) {
|
|
|
454
603
|
measure();
|
|
455
604
|
};
|
|
456
605
|
apply();
|
|
606
|
+
if (wilInlineRef.current) {
|
|
607
|
+
wilInlineRef.current = false;
|
|
608
|
+
if (targetsRef.current.length > 0) startInline(0);
|
|
609
|
+
else {
|
|
610
|
+
toast({
|
|
611
|
+
title: "Dit blok heeft geen eigen tekst",
|
|
612
|
+
description: "Wijs een product aan voor zijn naam, of gebruik het tandwiel voor de instellingen van dit blok."
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
}
|
|
457
616
|
const obs = new MutationObserver(() => {
|
|
458
617
|
if (!draggingRef.current) apply();
|
|
459
618
|
});
|
|
@@ -478,7 +637,7 @@ function VisualEditorInner({ onClose }) {
|
|
|
478
637
|
targetsRef.current = [];
|
|
479
638
|
setSpots([]);
|
|
480
639
|
};
|
|
481
|
-
}, [selectedKey, commitText, positionFmtBar, stopInline]);
|
|
640
|
+
}, [selectedKey, commitText, positionFmtBar, stopInline, startInline, toast]);
|
|
482
641
|
const dirtySinceRef = useRef(null);
|
|
483
642
|
useEffect(() => {
|
|
484
643
|
if (!draft) return;
|
|
@@ -507,7 +666,7 @@ function VisualEditorInner({ onClose }) {
|
|
|
507
666
|
}, [draft, siteId, conflict]);
|
|
508
667
|
useEffect(() => {
|
|
509
668
|
const beforeUnload = (e) => {
|
|
510
|
-
if (dirtyRef.current) {
|
|
669
|
+
if (dirtyRef.current || nietLiveRef.current) {
|
|
511
670
|
e.preventDefault();
|
|
512
671
|
e.returnValue = "";
|
|
513
672
|
}
|
|
@@ -648,12 +807,24 @@ function VisualEditorInner({ onClose }) {
|
|
|
648
807
|
setConflict(false);
|
|
649
808
|
}, [siteId, path, mutateDraft]);
|
|
650
809
|
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "xicms-ve__shell", role: "group", "aria-label": `Pagina bewerken: ${path}`, children: [
|
|
651
|
-
/* @__PURE__ */ jsxs("header", { className: "xicms-ve__bar", children: [
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
810
|
+
/* @__PURE__ */ jsxs("header", { className: "xicms-ve__bar", role: "toolbar", "aria-label": "Beheer", children: [
|
|
811
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-editbar__brand", children: isDemoActive() ? "XiCMS \xB7 demo" : "XiCMS" }),
|
|
812
|
+
/* @__PURE__ */ jsx(
|
|
813
|
+
AdminModes,
|
|
814
|
+
{
|
|
815
|
+
actief: "bewerken",
|
|
816
|
+
voorSwitch: (m) => {
|
|
817
|
+
if (m === "live" && nietLiveRef.current) {
|
|
818
|
+
toast({
|
|
819
|
+
title: "Nog niet gepubliceerd",
|
|
820
|
+
description: `${aantalWijzigingen(wijzigingen)} blijft bewaard als werkversie. Je ziet ze weer zodra je terug naar Bewerken gaat.`
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
),
|
|
826
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-ve__sep", "aria-hidden": "true" }),
|
|
827
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-ve__path", title: `Je bewerkt ${name} \u2014 ${path}`, children: path }),
|
|
657
828
|
localeCfg.locales.length > 1 && /* @__PURE__ */ jsxs("span", { className: "xicms-ve__locale", title: "Taal \u2014 in een andere taal dan de standaard bewerk je vertalingen", children: [
|
|
658
829
|
"\u{1F310}",
|
|
659
830
|
/* @__PURE__ */ jsx(
|
|
@@ -695,13 +866,9 @@ function VisualEditorInner({ onClose }) {
|
|
|
695
866
|
] }),
|
|
696
867
|
/* @__PURE__ */ jsxs("span", { className: "xicms-ve__status", "aria-live": "polite", children: [
|
|
697
868
|
status === "loading" ? "Werkversie laden\u2026" : null,
|
|
698
|
-
status === "saving" ? "Opslaan\u2026" : status === "saved" ? "\u2713
|
|
869
|
+
status === "saving" ? "Opslaan\u2026" : status === "saved" ? "\u2713 Bewaard als werkversie" : status === "error" ? "\u26A0 Niet opgeslagen" : ""
|
|
699
870
|
] }),
|
|
700
|
-
status === "error" && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: () => void flushSave().catch(() => setStatus("error")), children: "Opnieuw opslaan" })
|
|
701
|
-
/* @__PURE__ */ jsxs("div", { className: "xicms-ve__bar-actions", children: [
|
|
702
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", disabled: publishing || !draft, onClick: onPublish, children: publishing ? "Bezig\u2026" : "Publiceren" }),
|
|
703
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: handleClose, children: "Sluiten" })
|
|
704
|
-
] })
|
|
871
|
+
status === "error" && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: () => void flushSave().catch(() => setStatus("error")), children: "Opnieuw opslaan" })
|
|
705
872
|
] }),
|
|
706
873
|
conflict && /* @__PURE__ */ jsxs("div", { className: "xicms-ve__conflict", role: "alert", children: [
|
|
707
874
|
/* @__PURE__ */ jsx("span", { children: "\u26A0 Deze pagina is intussen ergens anders gewijzigd (andere tab of collega). Doorwerken kan die wijzigingen overschrijven." }),
|
|
@@ -718,7 +885,16 @@ function VisualEditorInner({ onClose }) {
|
|
|
718
885
|
/* @__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 }) }),
|
|
719
886
|
/* @__PURE__ */ jsx(PatternInserter, { onInsert: insertAfterSelected })
|
|
720
887
|
] }),
|
|
721
|
-
|
|
888
|
+
selectedProduct ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
889
|
+
/* @__PURE__ */ jsxs("div", { className: "xicms-ve__panel-head", children: [
|
|
890
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: "Webwinkel" }),
|
|
891
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
|
|
892
|
+
setSelectedProduct(null);
|
|
893
|
+
setPanelOpen(false);
|
|
894
|
+
}, children: "\u2715 Sluiten" })
|
|
895
|
+
] }),
|
|
896
|
+
/* @__PURE__ */ jsx(ProductPaneel, { siteId, productId: selectedProduct })
|
|
897
|
+
] }) : selected && isTranslating ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
722
898
|
/* @__PURE__ */ jsxs("h3", { className: "text-sm font-semibold", children: [
|
|
723
899
|
registry.get(selected._type)?.label ?? selected._type,
|
|
724
900
|
" \u2014 vertaling (",
|
|
@@ -792,26 +968,46 @@ function VisualEditorInner({ onClose }) {
|
|
|
792
968
|
}
|
|
793
969
|
}
|
|
794
970
|
),
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
/* @__PURE__ */
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
971
|
+
blokknoppen.map((b) => /* @__PURE__ */ jsxs("div", { className: "xicms-ve-blokknoppen", style: { top: b.top, left: b.left }, children: [
|
|
972
|
+
/* @__PURE__ */ jsx(
|
|
973
|
+
"button",
|
|
974
|
+
{
|
|
975
|
+
type: "button",
|
|
976
|
+
className: `xicms-ve-blokknop${b.key === selectedKey ? " is-on" : ""}`,
|
|
977
|
+
title: `Tekst van ${b.label} bewerken`,
|
|
978
|
+
"aria-label": `Tekst van ${b.label} bewerken`,
|
|
979
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
980
|
+
onClick: () => {
|
|
981
|
+
wilInlineRef.current = true;
|
|
982
|
+
setSelectedProduct(null);
|
|
983
|
+
setSelectedKey(b.key);
|
|
984
|
+
},
|
|
985
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
986
|
+
/* @__PURE__ */ jsx("path", { d: "M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z" }),
|
|
987
|
+
/* @__PURE__ */ jsx("path", { d: "M14.5 6.5l3 3" })
|
|
988
|
+
] })
|
|
989
|
+
}
|
|
990
|
+
),
|
|
991
|
+
/* @__PURE__ */ jsx(
|
|
992
|
+
"button",
|
|
993
|
+
{
|
|
994
|
+
type: "button",
|
|
995
|
+
className: `xicms-ve-blokknop${b.key === selectedKey && panelOpen ? " is-on" : ""}`,
|
|
996
|
+
title: `Instellingen van ${b.label}`,
|
|
997
|
+
"aria-label": `Instellingen van ${b.label}`,
|
|
998
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
999
|
+
onClick: () => {
|
|
1000
|
+
setSelectedProduct(null);
|
|
1001
|
+
setSelectedKey(b.key);
|
|
1002
|
+
setPanelOpen(true);
|
|
1003
|
+
},
|
|
1004
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
1005
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
1006
|
+
/* @__PURE__ */ jsx("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" })
|
|
1007
|
+
] })
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
] }, b.key)),
|
|
815
1011
|
spots.map((s, i) => /* @__PURE__ */ jsx(
|
|
816
1012
|
"button",
|
|
817
1013
|
{
|
|
@@ -831,6 +1027,63 @@ function VisualEditorInner({ onClose }) {
|
|
|
831
1027
|
},
|
|
832
1028
|
i
|
|
833
1029
|
)),
|
|
1030
|
+
productKnop && /* @__PURE__ */ jsxs(
|
|
1031
|
+
"div",
|
|
1032
|
+
{
|
|
1033
|
+
className: "xicms-ve-blokknoppen is-product",
|
|
1034
|
+
style: { top: productKnop.top, left: productKnop.left },
|
|
1035
|
+
children: [
|
|
1036
|
+
/* @__PURE__ */ jsx(
|
|
1037
|
+
"button",
|
|
1038
|
+
{
|
|
1039
|
+
type: "button",
|
|
1040
|
+
className: "xicms-ve-blokknop",
|
|
1041
|
+
title: "Productnaam bewerken (staat meteen live)",
|
|
1042
|
+
"aria-label": "Productnaam bewerken",
|
|
1043
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1044
|
+
onClick: () => startProductInline(productKnop.id),
|
|
1045
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
1046
|
+
/* @__PURE__ */ jsx("path", { d: "M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z" }),
|
|
1047
|
+
/* @__PURE__ */ jsx("path", { d: "M14.5 6.5l3 3" })
|
|
1048
|
+
] })
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
/* @__PURE__ */ jsx(
|
|
1052
|
+
"button",
|
|
1053
|
+
{
|
|
1054
|
+
type: "button",
|
|
1055
|
+
className: `xicms-ve-blokknop${selectedProduct === productKnop.id ? " is-on" : ""}`,
|
|
1056
|
+
title: "Product bewerken: prijs, foto's, varianten, voorraad",
|
|
1057
|
+
"aria-label": "Product bewerken",
|
|
1058
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1059
|
+
onClick: () => {
|
|
1060
|
+
setSelectedKey(null);
|
|
1061
|
+
setSelectedProduct(productKnop.id);
|
|
1062
|
+
setPanelOpen(true);
|
|
1063
|
+
},
|
|
1064
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
1065
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
1066
|
+
/* @__PURE__ */ jsx("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" })
|
|
1067
|
+
] })
|
|
1068
|
+
}
|
|
1069
|
+
)
|
|
1070
|
+
]
|
|
1071
|
+
}
|
|
1072
|
+
),
|
|
1073
|
+
draft && /* @__PURE__ */ jsxs(
|
|
1074
|
+
"button",
|
|
1075
|
+
{
|
|
1076
|
+
type: "button",
|
|
1077
|
+
className: `xicms-ve-publiceer${wijzigingen.iets ? " is-klaar" : ""}`,
|
|
1078
|
+
disabled: publishing || !wijzigingen.iets,
|
|
1079
|
+
onClick: () => void onPublish(),
|
|
1080
|
+
title: wijzigingen.iets ? "Zet deze wijzigingen op de live site" : "Er is niets veranderd sinds de laatste publicatie",
|
|
1081
|
+
children: [
|
|
1082
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-ve-publiceer__label", children: publishing ? "Bezig\u2026" : "Publiceren" }),
|
|
1083
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-ve-publiceer__sub", children: wijzigingen.iets ? aantalWijzigingen(wijzigingen) : "niets gewijzigd" })
|
|
1084
|
+
]
|
|
1085
|
+
}
|
|
1086
|
+
),
|
|
834
1087
|
fmtBar && /* @__PURE__ */ jsxs("div", { className: "xicms-ve-fmtbar", style: { top: fmtBar.top, left: fmtBar.left }, role: "toolbar", "aria-label": "Tekstopmaak", children: [
|
|
835
1088
|
/* @__PURE__ */ jsx(FmtBtn, { label: /* @__PURE__ */ jsx("b", { children: "B" }), title: "Vet (Ctrl/Cmd+B)", onRun: () => document.execCommand("bold") }),
|
|
836
1089
|
/* @__PURE__ */ jsx(FmtBtn, { label: /* @__PURE__ */ jsx("i", { children: "I" }), title: "Cursief (Ctrl/Cmd+I)", onRun: () => document.execCommand("italic") }),
|