@xiwebdesign/xicms-core 7.23.0 → 7.24.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 +2 -1
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/CoreVersion.d.ts +6 -0
- package/dist/admin/CoreVersion.js +42 -1
- package/dist/admin/CoreVersion.js.map +1 -1
- package/dist/admin/SuperadminPanel.js +19 -3
- package/dist/admin/SuperadminPanel.js.map +1 -1
- package/dist/admin/dashboard/DashboardPanel.js +46 -9
- package/dist/admin/dashboard/DashboardPanel.js.map +1 -1
- package/dist/admin/forms/FormsPanel.d.ts +9 -0
- package/dist/admin/forms/FormsPanel.js +102 -16
- package/dist/admin/forms/FormsPanel.js.map +1 -1
- package/dist/admin/forms/gezien.d.ts +3 -0
- package/dist/admin/forms/gezien.js +22 -0
- package/dist/admin/forms/gezien.js.map +1 -0
- package/dist/admin/lib/kleur.d.ts +32 -0
- package/dist/admin/lib/kleur.js +60 -0
- package/dist/admin/lib/kleur.js.map +1 -0
- package/dist/admin/lib/pagesClient.d.ts +32 -0
- package/dist/admin/lib/pagesClient.js +60 -1
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/lib/seoHulp.d.ts +28 -0
- package/dist/admin/lib/seoHulp.js +33 -0
- package/dist/admin/lib/seoHulp.js.map +1 -0
- package/dist/admin/media/MediaManager.js +133 -51
- package/dist/admin/media/MediaManager.js.map +1 -1
- package/dist/admin/pages/PageEditor.d.ts +14 -0
- package/dist/admin/pages/PageEditor.js +192 -123
- package/dist/admin/pages/PageEditor.js.map +1 -1
- package/dist/admin/pages/PagesPanel.js +88 -40
- package/dist/admin/pages/PagesPanel.js.map +1 -1
- package/dist/admin/settings/SettingsPanel.js +100 -19
- package/dist/admin/settings/SettingsPanel.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +1 -1
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/ShopPanel.js +5 -2
- package/dist/admin/shop/ShopPanel.js.map +1 -1
- package/dist/admin/stats/StatsPanel.js +30 -2
- package/dist/admin/stats/StatsPanel.js.map +1 -1
- package/dist/admin/stats/lib.d.ts +14 -1
- package/dist/admin/stats/lib.js +46 -2
- package/dist/admin/stats/lib.js.map +1 -1
- package/dist/admin/visual/VisualEditor.js +80 -4
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin.css +1 -1
- package/dist/blocks/builtins/FormBlockRender.js +8 -2
- package/dist/blocks/builtins/FormBlockRender.js.map +1 -1
- package/dist/blocks/builtins/lib/submitForm.d.ts +3 -1
- package/dist/blocks/builtins/lib/submitForm.js +2 -2
- package/dist/blocks/builtins/lib/submitForm.js.map +1 -1
- package/dist/components/EditBar.d.ts +3 -1
- package/dist/components/EditBar.js +30 -4
- package/dist/components/EditBar.js.map +1 -1
- package/dist/components/News.js +8 -4
- package/dist/components/News.js.map +1 -1
- package/dist/components/PageView.d.ts +5 -0
- package/dist/components/PageView.js +24 -2
- package/dist/components/PageView.js.map +1 -1
- package/dist/components/ResponsiveImage.d.ts +1 -1
- package/dist/components/ResponsiveImage.js +3 -1
- package/dist/components/ResponsiveImage.js.map +1 -1
- package/dist/components/site/SiteNav.d.ts +29 -0
- package/dist/components/site/SiteNav.js +82 -0
- package/dist/components/site/SiteNav.js.map +1 -0
- package/dist/content/types.d.ts +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/media/ImageContext.d.ts +1 -6
- package/dist/media/ImageContext.js +6 -1
- package/dist/media/ImageContext.js.map +1 -1
- package/dist/releaseNotes.d.ts +15 -0
- package/dist/releaseNotes.js +28 -0
- package/dist/releaseNotes.js.map +1 -0
- package/dist/schema/settings.d.ts +105 -0
- package/dist/schema/settings.js +45 -5
- package/dist/schema/settings.js.map +1 -1
- package/dist/schema/siteConfig.d.ts +9 -0
- package/dist/server/blockData.js +6 -2
- package/dist/server/blockData.js.map +1 -1
- package/dist/server/draft.d.ts +11 -0
- package/dist/server/draft.js +20 -1
- package/dist/server/draft.js.map +1 -1
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.js +7 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/pages.js +4 -1
- package/dist/server/pages.js.map +1 -1
- package/dist/server/previewToken.d.ts +34 -0
- package/dist/server/previewToken.js +44 -0
- package/dist/server/previewToken.js.map +1 -0
- package/dist/server/redirects.d.ts +15 -0
- package/dist/server/redirects.js +15 -0
- package/dist/server/redirects.js.map +1 -0
- package/dist/styles.css +110 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import type { MediaItem } from "../content/types.js";
|
|
|
5
5
|
* Server-safe. Valt terug op de originele `url` als er nog geen varianten zijn.
|
|
6
6
|
*/
|
|
7
7
|
export declare function ResponsiveImage({ media, url, alt, className, style, sizes, priority, }: {
|
|
8
|
-
media?: Pick<MediaItem, "url" | "alt" | "width" | "height" | "variants">;
|
|
8
|
+
media?: Pick<MediaItem, "url" | "alt" | "width" | "height" | "variants" | "focal">;
|
|
9
9
|
url?: string;
|
|
10
10
|
alt?: string;
|
|
11
11
|
className?: string;
|
|
@@ -13,6 +13,8 @@ function ResponsiveImage({
|
|
|
13
13
|
const altText = alt ?? media?.alt ?? "";
|
|
14
14
|
const ladder = media?.variants?.sizes ?? [];
|
|
15
15
|
const webp = media?.variants?.webp;
|
|
16
|
+
const focal = media?.focal;
|
|
17
|
+
const stijl = focal && !(style && style.objectPosition) ? { ...style, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` } : style;
|
|
16
18
|
const srcSet = ladder.length ? ladder.map((v) => `${v.url} ${v.width}w`).join(", ") : void 0;
|
|
17
19
|
return /* @__PURE__ */ jsxs("picture", { children: [
|
|
18
20
|
webp && /* @__PURE__ */ jsx("source", { type: "image/webp", srcSet: `${webp.url} ${webp.width}w`, sizes }),
|
|
@@ -28,7 +30,7 @@ function ResponsiveImage({
|
|
|
28
30
|
loading: priority ? "eager" : "lazy",
|
|
29
31
|
fetchPriority: priority ? "high" : void 0,
|
|
30
32
|
className,
|
|
31
|
-
style
|
|
33
|
+
style: stijl
|
|
32
34
|
}
|
|
33
35
|
)
|
|
34
36
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ResponsiveImage.tsx"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { MediaItem } from \"../content/types\";\n\n/**\n * Rendert een afbeelding met srcset/webp uit de door de pipeline gegenereerde varianten.\n * Server-safe. Valt terug op de originele `url` als er nog geen varianten zijn.\n */\nexport function ResponsiveImage({\n media,\n url,\n alt,\n className,\n style,\n sizes = \"100vw\",\n priority = false,\n}: {\n media?: Pick<MediaItem, \"url\" | \"alt\" | \"width\" | \"height\" | \"variants\">;\n url?: string;\n alt?: string;\n className?: string;\n style?: CSSProperties;\n sizes?: string;\n /** Zet dit op een above-the-fold beeld (bv. hero): eager laden + hoge fetch-prioriteit voor LCP. */\n priority?: boolean;\n}) {\n const src = media?.url ?? url;\n if (!src) return null;\n const altText = alt ?? media?.alt ?? \"\";\n const ladder = media?.variants?.sizes ?? [];\n const webp = media?.variants?.webp;\n\n const srcSet = ladder.length\n ? ladder.map((v) => `${v.url} ${v.width}w`).join(\", \")\n : undefined;\n\n return (\n <picture>\n {webp && <source type=\"image/webp\" srcSet={`${webp.url} ${webp.width}w`} sizes={sizes} />}\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={src}\n srcSet={srcSet}\n sizes={srcSet ? sizes : undefined}\n alt={altText}\n width={media?.width}\n height={media?.height}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : undefined}\n className={className}\n style={
|
|
1
|
+
{"version":3,"sources":["../../src/components/ResponsiveImage.tsx"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { MediaItem } from \"../content/types\";\n\n/**\n * Rendert een afbeelding met srcset/webp uit de door de pipeline gegenereerde varianten.\n * Server-safe. Valt terug op de originele `url` als er nog geen varianten zijn.\n */\nexport function ResponsiveImage({\n media,\n url,\n alt,\n className,\n style,\n sizes = \"100vw\",\n priority = false,\n}: {\n media?: Pick<MediaItem, \"url\" | \"alt\" | \"width\" | \"height\" | \"variants\" | \"focal\">;\n url?: string;\n alt?: string;\n className?: string;\n style?: CSSProperties;\n sizes?: string;\n /** Zet dit op een above-the-fold beeld (bv. hero): eager laden + hoge fetch-prioriteit voor LCP. */\n priority?: boolean;\n}) {\n const src = media?.url ?? url;\n if (!src) return null;\n const altText = alt ?? media?.alt ?? \"\";\n const ladder = media?.variants?.sizes ?? [];\n const webp = media?.variants?.webp;\n // Focuspunt (7.24): bijsnijden rond het gekozen punt i.p.v. het midden (zie MediaItem.focal).\n const focal = media?.focal;\n const stijl = focal && !(style && style.objectPosition)\n ? { ...style, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` }\n : style;\n\n const srcSet = ladder.length\n ? ladder.map((v) => `${v.url} ${v.width}w`).join(\", \")\n : undefined;\n\n return (\n <picture>\n {webp && <source type=\"image/webp\" srcSet={`${webp.url} ${webp.width}w`} sizes={sizes} />}\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={src}\n srcSet={srcSet}\n sizes={srcSet ? sizes : undefined}\n alt={altText}\n width={media?.width}\n height={media?.height}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : undefined}\n className={className}\n style={stijl}\n />\n </picture>\n );\n}\n"],"mappings":"AAyCI,SACW,KADX;AAlCG,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AACb,GASG;AACD,QAAM,MAAM,OAAO,OAAO;AAC1B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,UAAU,OAAO,OAAO,OAAO;AACrC,QAAM,SAAS,OAAO,UAAU,SAAS,CAAC;AAC1C,QAAM,OAAO,OAAO,UAAU;AAE9B,QAAM,QAAQ,OAAO;AACrB,QAAM,QAAQ,SAAS,EAAE,SAAS,MAAM,kBACpC,EAAE,GAAG,OAAO,gBAAgB,GAAG,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,IAC1F;AAEJ,QAAM,SAAS,OAAO,SAClB,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,KAAK,IAAI,IACnD;AAEJ,SACE,qBAAC,aACE;AAAA,YAAQ,oBAAC,YAAO,MAAK,cAAa,QAAQ,GAAG,KAAK,GAAG,IAAI,KAAK,KAAK,KAAK,OAAc;AAAA,IAEvF;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,OAAO,SAAS,QAAQ;AAAA,QACxB,KAAK;AAAA,QACL,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,SAAS,WAAW,UAAU;AAAA,QAC9B,eAAe,WAAW,SAAS;AAAA,QACnC;AAAA,QACA,OAAO;AAAA;AAAA,IACT;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type NavItem } from "../../schema/settings.js";
|
|
2
|
+
/**
|
|
3
|
+
* De hoofdnavigatie zoals de klant hem in /beheer → Menu beheert (7.24).
|
|
4
|
+
*
|
|
5
|
+
* Tot 7.24 schreef het Menu-paneel netjes naar `settings.nav`, meldde "De navigatie is
|
|
6
|
+
* bijgewerkt" — en las geen enkele header dat veld. De template en de klantsites renderden
|
|
7
|
+
* `siteConfig.nav` rechtstreeks uit Git, dus wat de klant in het beheer deed kwam nooit op de
|
|
8
|
+
* site. Deze component is de ontbrekende consument: hij leest de samengevoegde config
|
|
9
|
+
* (Firestore wint, Git is de terugval) en is daarmee de énige plek die een menu hoort te tekenen.
|
|
10
|
+
*
|
|
11
|
+
* SSR-veilig: de settings zitten in de server-seed (`settingsData` op XiCMSProvider), dus het menu
|
|
12
|
+
* staat al in de HTML en er is geen flits van het oude menu.
|
|
13
|
+
*
|
|
14
|
+
* Unstyled-by-default, net als `SiteFooter`: alle opmaak hangt aan `xicms-nav*`-klassen en de
|
|
15
|
+
* thema-variabelen, zodat elke site hem kan herstijlen zonder de logica te forken. De klassen die
|
|
16
|
+
* de template gebruikte (`.site-header nav a`) blijven werken doordat dit een gewone `<nav>` met
|
|
17
|
+
* `<a>`'s blijft.
|
|
18
|
+
*
|
|
19
|
+
* Submenu's (één niveau): een item mét kinderen is zélf een link naar zijn overzichtspagina, met
|
|
20
|
+
* ernaast een knop die het submenu openklapt (`aria-expanded`). Zo werkt het menu zonder
|
|
21
|
+
* JavaScript (alle links staan in de HTML), met het toetsenbord (knop = Enter/Spatie, Escape
|
|
22
|
+
* sluit) en op touch (geen hover nodig).
|
|
23
|
+
*/
|
|
24
|
+
export declare function SiteNav({ className, ariaLabel, items, }: {
|
|
25
|
+
className?: string;
|
|
26
|
+
ariaLabel?: string;
|
|
27
|
+
/** Overschrijft de items (bv. voor een footer-menu); standaard de beheerde hoofdnavigatie. */
|
|
28
|
+
items?: NavItem[];
|
|
29
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { useLinkComponent } from "@astryxdesign/core/Link";
|
|
5
|
+
import { useMergedSiteConfig } from "../../settings/useSiteSettings.js";
|
|
6
|
+
import { isExterneLink, navHref } from "../../schema/settings.js";
|
|
7
|
+
function SiteNav({
|
|
8
|
+
className,
|
|
9
|
+
ariaLabel = "Hoofdnavigatie",
|
|
10
|
+
items
|
|
11
|
+
}) {
|
|
12
|
+
const cfg = useMergedSiteConfig();
|
|
13
|
+
const lijst = items ?? cfg.nav ?? [];
|
|
14
|
+
if (lijst.length === 0) return null;
|
|
15
|
+
return /* @__PURE__ */ jsx("nav", { className: className ?? "xicms-nav", "aria-label": ariaLabel, children: /* @__PURE__ */ jsx("ul", { className: "xicms-nav__list", children: lijst.map((item, i) => /* @__PURE__ */ jsx(NavEntry, { item }, `${item.href}-${i}`)) }) });
|
|
16
|
+
}
|
|
17
|
+
function NavEntry({ item }) {
|
|
18
|
+
const LinkC = useLinkComponent();
|
|
19
|
+
const kinderen = item.children ?? [];
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
|
+
const id = useId();
|
|
22
|
+
const ref = useRef(null);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!open) return;
|
|
25
|
+
const onDown = (e) => {
|
|
26
|
+
if (!ref.current?.contains(e.target)) setOpen(false);
|
|
27
|
+
};
|
|
28
|
+
const onKey = (e) => {
|
|
29
|
+
if (e.key === "Escape") setOpen(false);
|
|
30
|
+
};
|
|
31
|
+
document.addEventListener("pointerdown", onDown);
|
|
32
|
+
document.addEventListener("keydown", onKey);
|
|
33
|
+
return () => {
|
|
34
|
+
document.removeEventListener("pointerdown", onDown);
|
|
35
|
+
document.removeEventListener("keydown", onKey);
|
|
36
|
+
};
|
|
37
|
+
}, [open]);
|
|
38
|
+
const extern = isExterneLink(item.href);
|
|
39
|
+
const linkProps = {
|
|
40
|
+
href: navHref(item),
|
|
41
|
+
target: item.target === "_blank" ? "_blank" : void 0,
|
|
42
|
+
rel: extern && item.target === "_blank" ? "noopener noreferrer" : void 0
|
|
43
|
+
};
|
|
44
|
+
if (kinderen.length === 0) {
|
|
45
|
+
return /* @__PURE__ */ jsx("li", { className: "xicms-nav__item", children: /* @__PURE__ */ jsx(LinkC, { className: "xicms-nav__link", ...linkProps, children: item.label }) });
|
|
46
|
+
}
|
|
47
|
+
return /* @__PURE__ */ jsxs("li", { ref, className: `xicms-nav__item xicms-nav__item--sub${open ? " is-open" : ""}`, children: [
|
|
48
|
+
/* @__PURE__ */ jsxs("span", { className: "xicms-nav__pair", children: [
|
|
49
|
+
/* @__PURE__ */ jsx(LinkC, { className: "xicms-nav__link", ...linkProps, children: item.label }),
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
"button",
|
|
52
|
+
{
|
|
53
|
+
type: "button",
|
|
54
|
+
className: "xicms-nav__toggle",
|
|
55
|
+
"aria-expanded": open,
|
|
56
|
+
"aria-controls": id,
|
|
57
|
+
"aria-label": `Submenu van ${item.label} ${open ? "sluiten" : "openen"}`,
|
|
58
|
+
onClick: () => setOpen((v) => !v),
|
|
59
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" }) })
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
] }),
|
|
63
|
+
/* @__PURE__ */ jsx("ul", { id, className: "xicms-nav__sub", hidden: !open, children: kinderen.map((kind, j) => {
|
|
64
|
+
const kExtern = isExterneLink(kind.href);
|
|
65
|
+
return /* @__PURE__ */ jsx("li", { className: "xicms-nav__subitem", children: /* @__PURE__ */ jsx(
|
|
66
|
+
LinkC,
|
|
67
|
+
{
|
|
68
|
+
className: "xicms-nav__sublink",
|
|
69
|
+
href: navHref(kind),
|
|
70
|
+
target: kind.target === "_blank" ? "_blank" : void 0,
|
|
71
|
+
rel: kExtern && kind.target === "_blank" ? "noopener noreferrer" : void 0,
|
|
72
|
+
onClick: () => setOpen(false),
|
|
73
|
+
children: kind.label
|
|
74
|
+
}
|
|
75
|
+
) }, `${kind.href}-${j}`);
|
|
76
|
+
}) })
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
SiteNav
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=SiteNav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/site/SiteNav.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { useLinkComponent } from \"@astryxdesign/core/Link\";\nimport { useMergedSiteConfig } from \"../../settings/useSiteSettings\";\nimport { isExterneLink, navHref, type NavItem } from \"../../schema/settings\";\n\n/**\n * De hoofdnavigatie zoals de klant hem in /beheer → Menu beheert (7.24).\n *\n * Tot 7.24 schreef het Menu-paneel netjes naar `settings.nav`, meldde \"De navigatie is\n * bijgewerkt\" — en las geen enkele header dat veld. De template en de klantsites renderden\n * `siteConfig.nav` rechtstreeks uit Git, dus wat de klant in het beheer deed kwam nooit op de\n * site. Deze component is de ontbrekende consument: hij leest de samengevoegde config\n * (Firestore wint, Git is de terugval) en is daarmee de énige plek die een menu hoort te tekenen.\n *\n * SSR-veilig: de settings zitten in de server-seed (`settingsData` op XiCMSProvider), dus het menu\n * staat al in de HTML en er is geen flits van het oude menu.\n *\n * Unstyled-by-default, net als `SiteFooter`: alle opmaak hangt aan `xicms-nav*`-klassen en de\n * thema-variabelen, zodat elke site hem kan herstijlen zonder de logica te forken. De klassen die\n * de template gebruikte (`.site-header nav a`) blijven werken doordat dit een gewone `<nav>` met\n * `<a>`'s blijft.\n *\n * Submenu's (één niveau): een item mét kinderen is zélf een link naar zijn overzichtspagina, met\n * ernaast een knop die het submenu openklapt (`aria-expanded`). Zo werkt het menu zonder\n * JavaScript (alle links staan in de HTML), met het toetsenbord (knop = Enter/Spatie, Escape\n * sluit) en op touch (geen hover nodig).\n */\nexport function SiteNav({\n className,\n ariaLabel = \"Hoofdnavigatie\",\n items,\n}: {\n className?: string;\n ariaLabel?: string;\n /** Overschrijft de items (bv. voor een footer-menu); standaard de beheerde hoofdnavigatie. */\n items?: NavItem[];\n}) {\n const cfg = useMergedSiteConfig();\n const lijst = items ?? cfg.nav ?? [];\n if (lijst.length === 0) return null;\n return (\n <nav className={className ?? \"xicms-nav\"} aria-label={ariaLabel}>\n <ul className=\"xicms-nav__list\">\n {lijst.map((item, i) => (\n <NavEntry key={`${item.href}-${i}`} item={item} />\n ))}\n </ul>\n </nav>\n );\n}\n\nfunction NavEntry({ item }: { item: NavItem }) {\n const LinkC = useLinkComponent();\n const kinderen = item.children ?? [];\n const [open, setOpen] = useState(false);\n const id = useId();\n const ref = useRef<HTMLLIElement>(null);\n\n // Buiten klikken of Escape sluit het submenu — anders blijft het hangen tot je een ander opent.\n useEffect(() => {\n if (!open) return;\n const onDown = (e: PointerEvent) => {\n if (!ref.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"pointerdown\", onDown);\n document.addEventListener(\"keydown\", onKey);\n return () => {\n document.removeEventListener(\"pointerdown\", onDown);\n document.removeEventListener(\"keydown\", onKey);\n };\n }, [open]);\n\n const extern = isExterneLink(item.href);\n const linkProps = {\n href: navHref(item),\n target: item.target === \"_blank\" ? \"_blank\" : undefined,\n rel: extern && item.target === \"_blank\" ? \"noopener noreferrer\" : undefined,\n };\n\n if (kinderen.length === 0) {\n return (\n <li className=\"xicms-nav__item\">\n <LinkC className=\"xicms-nav__link\" {...linkProps}>{item.label}</LinkC>\n </li>\n );\n }\n\n return (\n <li ref={ref} className={`xicms-nav__item xicms-nav__item--sub${open ? \" is-open\" : \"\"}`}>\n <span className=\"xicms-nav__pair\">\n <LinkC className=\"xicms-nav__link\" {...linkProps}>{item.label}</LinkC>\n <button\n type=\"button\"\n className=\"xicms-nav__toggle\"\n aria-expanded={open}\n aria-controls={id}\n aria-label={`Submenu van ${item.label} ${open ? \"sluiten\" : \"openen\"}`}\n onClick={() => setOpen((v) => !v)}\n >\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n </button>\n </span>\n <ul id={id} className=\"xicms-nav__sub\" hidden={!open}>\n {kinderen.map((kind, j) => {\n const kExtern = isExterneLink(kind.href);\n return (\n <li key={`${kind.href}-${j}`} className=\"xicms-nav__subitem\">\n <LinkC\n className=\"xicms-nav__sublink\"\n href={navHref(kind)}\n target={kind.target === \"_blank\" ? \"_blank\" : undefined}\n rel={kExtern && kind.target === \"_blank\" ? \"noopener noreferrer\" : undefined}\n onClick={() => setOpen(false)}\n >\n {kind.label}\n </LinkC>\n </li>\n );\n })}\n </ul>\n </li>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/content/types.d.ts
CHANGED
|
@@ -38,4 +38,12 @@ export interface MediaItem {
|
|
|
38
38
|
};
|
|
39
39
|
/** "processing" tot de pipeline klaar is, daarna "ready". */
|
|
40
40
|
status?: "processing" | "ready";
|
|
41
|
+
/**
|
|
42
|
+
* Focuspunt (7.24): welk deel van de foto in beeld moet blijven als hij bijgesneden wordt
|
|
43
|
+
* (hero op mobiel, vierkante kaart). Fracties 0–1 van links/boven; afwezig = het midden.
|
|
44
|
+
*/
|
|
45
|
+
focal?: {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
};
|
|
41
49
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type { ImageComponentType, ImgProps } from "./media/ImageContext.js";
|
|
|
15
15
|
export * from "./blocks/ui/index.js";
|
|
16
16
|
export { SiteSettingsProvider, useSiteSettings, useMergedSiteConfig, } from "./settings/useSiteSettings.js";
|
|
17
17
|
export { SiteFooter } from "./components/site/SiteFooter.js";
|
|
18
|
+
export { SiteNav } from "./components/site/SiteNav.js";
|
|
18
19
|
export { EditorProvider, useEditor } from "./editor/EditorProvider.js";
|
|
19
20
|
export { definePattern, instantiatePattern, collectPatternTypes, builtinPatterns, type Pattern, type PatternBlock, } from "./patterns/index.js";
|
|
20
21
|
export { builtinThemePresets, type ThemePreset } from "./theme/presets.js";
|
|
@@ -30,7 +31,8 @@ export { customBlockToDefinition } from "./blocks/customTemplate/definition.js";
|
|
|
30
31
|
export { interpretTemplate, scopeCss } from "./blocks/customTemplate/interpret.js";
|
|
31
32
|
export { siteLocales, parseLocaleSlugs, localizedPath, localizePage, localizeBlocks, pruneTranslations, TranslationsSchema, } from "./i18n/overlay.js";
|
|
32
33
|
export type { Translations, LocaleConfig } from "./i18n/overlay.js";
|
|
33
|
-
export type { ThemeTokens, NavItem, SiteSettings, PublicSiteSettings, FormsSender } from "./schema/settings.js";
|
|
34
|
+
export type { ThemeTokens, NavItem, NavLeaf, Redirect, SiteSettings, PublicSiteSettings, FormsSender } from "./schema/settings.js";
|
|
35
|
+
export { zoekRedirect, normaliseerPad, navHref, isExterneLink } from "./schema/settings.js";
|
|
34
36
|
export type { Seo } from "./schema/seo.js";
|
|
35
37
|
export type { Page, PageStatus } from "./schema/pages.js";
|
|
36
38
|
export type { FormDefinition, FormField } from "./schema/forms.js";
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
useMergedSiteConfig
|
|
11
11
|
} from "./settings/useSiteSettings.js";
|
|
12
12
|
import { SiteFooter } from "./components/site/SiteFooter.js";
|
|
13
|
+
import { SiteNav } from "./components/site/SiteNav.js";
|
|
13
14
|
import { EditorProvider, useEditor } from "./editor/EditorProvider.js";
|
|
14
15
|
import {
|
|
15
16
|
definePattern,
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
pruneTranslations,
|
|
34
35
|
TranslationsSchema
|
|
35
36
|
} from "./i18n/overlay.js";
|
|
37
|
+
import { zoekRedirect, normaliseerPad, navHref, isExterneLink } from "./schema/settings.js";
|
|
36
38
|
import { AuthProvider, useAuth } from "./auth/AuthProvider.js";
|
|
37
39
|
import { LoginForm } from "./auth/LoginForm.js";
|
|
38
40
|
import {
|
|
@@ -119,6 +121,7 @@ export {
|
|
|
119
121
|
ResponsiveImage,
|
|
120
122
|
SiteChrome,
|
|
121
123
|
SiteFooter,
|
|
124
|
+
SiteNav,
|
|
122
125
|
SiteSettingsProvider,
|
|
123
126
|
SymbolSchema,
|
|
124
127
|
TranslationsSchema,
|
|
@@ -149,6 +152,7 @@ export {
|
|
|
149
152
|
initAppCheck,
|
|
150
153
|
instantiatePattern,
|
|
151
154
|
interpretTemplate,
|
|
155
|
+
isExterneLink,
|
|
152
156
|
isFieldVisible,
|
|
153
157
|
isFirebaseConfigured,
|
|
154
158
|
isModuleActive,
|
|
@@ -160,6 +164,8 @@ export {
|
|
|
160
164
|
localizedPath,
|
|
161
165
|
mergeRegistry,
|
|
162
166
|
moduleIdForBlockType,
|
|
167
|
+
navHref,
|
|
168
|
+
normaliseerPad,
|
|
163
169
|
parseLocaleSlugs,
|
|
164
170
|
pruneTranslations,
|
|
165
171
|
resolveModules,
|
|
@@ -182,6 +188,7 @@ export {
|
|
|
182
188
|
useSiteSettings,
|
|
183
189
|
useTheme,
|
|
184
190
|
validateBlocks,
|
|
185
|
-
validateModuleContract
|
|
191
|
+
validateModuleContract,
|
|
192
|
+
zoekRedirect
|
|
186
193
|
};
|
|
187
194
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Publieke runtime-API van @xiwebdesign/xicms-core (lichte client/runtime laag).\n * - Admin (TipTap/Radix) zit in \"@xiwebdesign/xicms-core/admin\".\n * - Server-only code (firebase-admin, sanitize, SEO) zit in \"@xiwebdesign/xicms-core/server\".\n * - Pure schema's in \"@xiwebdesign/xicms-core/schema\".\n */\n\n// Provider & config\nexport { XiCMSProvider } from \"./XiCMSProvider\";\nexport { useSiteConfig } from \"./config\";\n\n// Theming & design-primitieven (maatwerk zonder core te forken).\n// - `defineTheme` maakt een eigen Astryx-thema (tokens + component-overrides) → geef door als\n// XiCMSProvider `astryxTheme`-prop.\n// - De `ui`-primitieven (Card/Stack/Grid/Heading/Text/Btn…) zijn de bouwstenen waarop de ingebouwde\n// blokken draaien; gebruik ze in eigen blokken voor een consistent maar te herstijlen uiterlijk.\nexport { defineTheme, useTheme } from \"@astryxdesign/core/theme\";\nexport type { DefinedTheme } from \"@astryxdesign/core/theme\";\n// Framework-router-integratie: geef `next/link` door als XiCMSProvider `linkComponent`-prop\n// (soepele client-nav + prefetch op alle Astryx-knoppen/links). `useLinkComponent` voor eigen blokken.\nexport { LinkProvider, useLinkComponent } from \"@astryxdesign/core/Link\";\nexport type { LinkComponentType } from \"@astryxdesign/core/Link\";\n// Beeldoptimalisatie: geef `next/image` door als XiCMSProvider `imageComponent`-prop. `Img` is de\n// wrapper die de blokken gebruiken; `useImageComponent` voor eigen blokken.\nexport { Img, ImageProvider, useImageComponent, InlineImageEditProvider } from \"./media/ImageContext\";\nexport type { ImageComponentType, ImgProps } from \"./media/ImageContext\";\nexport * from \"./blocks/ui\";\nexport {\n SiteSettingsProvider,\n useSiteSettings,\n useMergedSiteConfig,\n} from \"./settings/useSiteSettings\";\nexport { SiteFooter } from \"./components/site/SiteFooter\";\nexport { EditorProvider, useEditor } from \"./editor/EditorProvider\";\nexport {\n definePattern,\n instantiatePattern,\n collectPatternTypes,\n builtinPatterns,\n type Pattern,\n type PatternBlock,\n} from \"./patterns\";\nexport { builtinThemePresets, type ThemePreset } from \"./theme/presets\";\nexport { defineSiteConfig } from \"./schema/siteConfig\";\nexport type { SiteConfig, SiteMode } from \"./schema/siteConfig\";\n// 5.0: Field-IR (primo-port), symbolen en custom blocks.\nexport { FieldSchema, FieldsSchema, fieldsToZod, fieldsDefaults, isFieldVisible, isRichTextKey } from \"./schema/fields\";\nexport type { Field, FieldHints, ShowIf } from \"./schema/fields\";\nexport { SymbolSchema } from \"./schema/symbols\";\nexport type { SymbolDefinition, StoredSymbol } from \"./schema/symbols\";\nexport { CustomBlockSchema } from \"./schema/customBlocks\";\nexport type { CustomBlock, CustomBlock as CustomBlockData } from \"./schema/customBlocks\";\nexport { customBlockToDefinition } from \"./blocks/customTemplate/definition\";\nexport { interpretTemplate, scopeCss } from \"./blocks/customTemplate/interpret\";\n// 5.0: localisatie — overlay-model (primo's locale-per-waarde, additief vertaald).\nexport {\n siteLocales,\n parseLocaleSlugs,\n localizedPath,\n localizePage,\n localizeBlocks,\n pruneTranslations,\n TranslationsSchema,\n} from \"./i18n/overlay\";\nexport type { Translations, LocaleConfig } from \"./i18n/overlay\";\nexport type { ThemeTokens, NavItem, SiteSettings, PublicSiteSettings, FormsSender } from \"./schema/settings\";\nexport type { Seo } from \"./schema/seo\";\nexport type { Page, PageStatus } from \"./schema/pages\";\nexport type { FormDefinition, FormField } from \"./schema/forms\";\n\n// Auth\nexport { AuthProvider, useAuth } from \"./auth/AuthProvider\";\nexport { LoginForm } from \"./auth/LoginForm\";\nexport {\n roleForSite,\n canEdit,\n canManageSite,\n isSuperadmin,\n type Role,\n type XiClaims,\n} from \"./auth/roles\";\n\n// De legacy inline-laag (ContentProvider/useContent + EditableText/EditableRichText/EditableImage,\n// het v1 platte `cms/content`-model) is in 5.0 VERWIJDERD, zoals bij de deprecatie aangekondigd.\n// Inline bewerken loopt via het block-model: markeer tekstvelden met `data-xicms-edit` in de Render.\nexport type { NewsArticle, MediaItem, MediaVariant } from \"./content/types\";\n\nexport { EditBar } from \"./components/EditBar\";\nexport { ExternEmbed } from \"./components/ExternEmbed\";\nexport { ResponsiveImage } from \"./components/ResponsiveImage\";\nexport { PageView } from \"./components/PageView\";\nexport { NewsList, NewsArticleView, useNews } from \"./components/News\";\n\n// Block-systeem (runtime: registry + renderer; editors zitten in /admin)\nexport {\n defineBlock,\n createRegistry,\n mergeRegistry,\n validateBlocks,\n buildBlockAddOptions,\n type BlockDefinition,\n type BlockRegistry,\n type BlockRenderProps,\n type BlockEditorProps,\n type BlockCategory,\n} from \"./blocks/registry\";\nexport { BlockRenderer } from \"./blocks/BlockRenderer\";\nexport { RegistryProvider, useRegistry, BlockDataProvider, useBlockData } from \"./blocks/context\";\nexport { builtinBlocks, ConsentEmbed } from \"./blocks/builtins\";\nexport { PageRenderer } from \"./render/PageRenderer\";\nexport { PublishedPage } from \"./render/PublishedPage\";\n\n// Module-systeem (XiCMS 3.0): modules bundelen blocks + instellingen; per site aan/uit + te stylen.\nexport {\n defineModule,\n resolveModules,\n isModuleActive,\n ModulesProvider,\n useModules,\n useModuleActive,\n useModuleSettings,\n applyBlockOverrides,\n moduleIdForBlockType,\n builtinModules,\n validateModuleContract,\n assertModuleContract,\n useConsent,\n ConsentBanner,\n ConsentGate,\n ConsentReopenLink,\n type ConsentState,\n type ConsentSettings,\n type ConsentCategory,\n type ModuleDefinition,\n type ModuleSiteSetting,\n type ModulesSiteConfig,\n type Capability,\n type ResolvedModules,\n type BlockOverride,\n type BlockOverrides,\n} from \"./modules\";\n\n// Demo-lock\nexport { DemoLock } from \"./lock/DemoLock\";\n\n// Demo-bewerkmodus (lokaal-only) leeft op het subpad `@xiwebdesign/xicms-core/demo` — dit zijn\n// demo-store-internals die niet in de publieke runtime-API thuishoren (klantsites gebruiken ze nooit;\n// alleen de XiCMS.nl-showcase). DemoStudio wordt intern door XiCMSProvider gerenderd.\n\n// Firebase (client)\nexport {\n getFirebaseApp,\n getFirebaseAuth,\n getDb,\n getBucket,\n isFirebaseConfigured,\n} from \"./firebase/client\";\nexport { initAppCheck } from \"./firebase/appcheck\";\nexport { SiteChrome } from \"./SiteChrome\";\nexport { CORE_VERSION, CORE_PACKAGE, isNieuwer } from \"./version\";\n"],"mappings":"AAQA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,aAAa,gBAAgB;AAItC,SAAS,cAAc,wBAAwB;AAI/C,SAAS,KAAK,eAAe,mBAAmB,+BAA+B;AAE/E,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Publieke runtime-API van @xiwebdesign/xicms-core (lichte client/runtime laag).\n * - Admin (TipTap/Radix) zit in \"@xiwebdesign/xicms-core/admin\".\n * - Server-only code (firebase-admin, sanitize, SEO) zit in \"@xiwebdesign/xicms-core/server\".\n * - Pure schema's in \"@xiwebdesign/xicms-core/schema\".\n */\n\n// Provider & config\nexport { XiCMSProvider } from \"./XiCMSProvider\";\nexport { useSiteConfig } from \"./config\";\n\n// Theming & design-primitieven (maatwerk zonder core te forken).\n// - `defineTheme` maakt een eigen Astryx-thema (tokens + component-overrides) → geef door als\n// XiCMSProvider `astryxTheme`-prop.\n// - De `ui`-primitieven (Card/Stack/Grid/Heading/Text/Btn…) zijn de bouwstenen waarop de ingebouwde\n// blokken draaien; gebruik ze in eigen blokken voor een consistent maar te herstijlen uiterlijk.\nexport { defineTheme, useTheme } from \"@astryxdesign/core/theme\";\nexport type { DefinedTheme } from \"@astryxdesign/core/theme\";\n// Framework-router-integratie: geef `next/link` door als XiCMSProvider `linkComponent`-prop\n// (soepele client-nav + prefetch op alle Astryx-knoppen/links). `useLinkComponent` voor eigen blokken.\nexport { LinkProvider, useLinkComponent } from \"@astryxdesign/core/Link\";\nexport type { LinkComponentType } from \"@astryxdesign/core/Link\";\n// Beeldoptimalisatie: geef `next/image` door als XiCMSProvider `imageComponent`-prop. `Img` is de\n// wrapper die de blokken gebruiken; `useImageComponent` voor eigen blokken.\nexport { Img, ImageProvider, useImageComponent, InlineImageEditProvider } from \"./media/ImageContext\";\nexport type { ImageComponentType, ImgProps } from \"./media/ImageContext\";\nexport * from \"./blocks/ui\";\nexport {\n SiteSettingsProvider,\n useSiteSettings,\n useMergedSiteConfig,\n} from \"./settings/useSiteSettings\";\nexport { SiteFooter } from \"./components/site/SiteFooter\";\n// 7.24: de beheerde hoofdnavigatie — de enige plek die `settings.nav` leest. Zie SiteNav.tsx.\nexport { SiteNav } from \"./components/site/SiteNav\";\nexport { EditorProvider, useEditor } from \"./editor/EditorProvider\";\nexport {\n definePattern,\n instantiatePattern,\n collectPatternTypes,\n builtinPatterns,\n type Pattern,\n type PatternBlock,\n} from \"./patterns\";\nexport { builtinThemePresets, type ThemePreset } from \"./theme/presets\";\nexport { defineSiteConfig } from \"./schema/siteConfig\";\nexport type { SiteConfig, SiteMode } from \"./schema/siteConfig\";\n// 5.0: Field-IR (primo-port), symbolen en custom blocks.\nexport { FieldSchema, FieldsSchema, fieldsToZod, fieldsDefaults, isFieldVisible, isRichTextKey } from \"./schema/fields\";\nexport type { Field, FieldHints, ShowIf } from \"./schema/fields\";\nexport { SymbolSchema } from \"./schema/symbols\";\nexport type { SymbolDefinition, StoredSymbol } from \"./schema/symbols\";\nexport { CustomBlockSchema } from \"./schema/customBlocks\";\nexport type { CustomBlock, CustomBlock as CustomBlockData } from \"./schema/customBlocks\";\nexport { customBlockToDefinition } from \"./blocks/customTemplate/definition\";\nexport { interpretTemplate, scopeCss } from \"./blocks/customTemplate/interpret\";\n// 5.0: localisatie — overlay-model (primo's locale-per-waarde, additief vertaald).\nexport {\n siteLocales,\n parseLocaleSlugs,\n localizedPath,\n localizePage,\n localizeBlocks,\n pruneTranslations,\n TranslationsSchema,\n} from \"./i18n/overlay\";\nexport type { Translations, LocaleConfig } from \"./i18n/overlay\";\nexport type { ThemeTokens, NavItem, NavLeaf, Redirect, SiteSettings, PublicSiteSettings, FormsSender } from \"./schema/settings\";\nexport { zoekRedirect, normaliseerPad, navHref, isExterneLink } from \"./schema/settings\";\nexport type { Seo } from \"./schema/seo\";\nexport type { Page, PageStatus } from \"./schema/pages\";\nexport type { FormDefinition, FormField } from \"./schema/forms\";\n\n// Auth\nexport { AuthProvider, useAuth } from \"./auth/AuthProvider\";\nexport { LoginForm } from \"./auth/LoginForm\";\nexport {\n roleForSite,\n canEdit,\n canManageSite,\n isSuperadmin,\n type Role,\n type XiClaims,\n} from \"./auth/roles\";\n\n// De legacy inline-laag (ContentProvider/useContent + EditableText/EditableRichText/EditableImage,\n// het v1 platte `cms/content`-model) is in 5.0 VERWIJDERD, zoals bij de deprecatie aangekondigd.\n// Inline bewerken loopt via het block-model: markeer tekstvelden met `data-xicms-edit` in de Render.\nexport type { NewsArticle, MediaItem, MediaVariant } from \"./content/types\";\n\nexport { EditBar } from \"./components/EditBar\";\nexport { ExternEmbed } from \"./components/ExternEmbed\";\nexport { ResponsiveImage } from \"./components/ResponsiveImage\";\nexport { PageView } from \"./components/PageView\";\nexport { NewsList, NewsArticleView, useNews } from \"./components/News\";\n\n// Block-systeem (runtime: registry + renderer; editors zitten in /admin)\nexport {\n defineBlock,\n createRegistry,\n mergeRegistry,\n validateBlocks,\n buildBlockAddOptions,\n type BlockDefinition,\n type BlockRegistry,\n type BlockRenderProps,\n type BlockEditorProps,\n type BlockCategory,\n} from \"./blocks/registry\";\nexport { BlockRenderer } from \"./blocks/BlockRenderer\";\nexport { RegistryProvider, useRegistry, BlockDataProvider, useBlockData } from \"./blocks/context\";\nexport { builtinBlocks, ConsentEmbed } from \"./blocks/builtins\";\nexport { PageRenderer } from \"./render/PageRenderer\";\nexport { PublishedPage } from \"./render/PublishedPage\";\n\n// Module-systeem (XiCMS 3.0): modules bundelen blocks + instellingen; per site aan/uit + te stylen.\nexport {\n defineModule,\n resolveModules,\n isModuleActive,\n ModulesProvider,\n useModules,\n useModuleActive,\n useModuleSettings,\n applyBlockOverrides,\n moduleIdForBlockType,\n builtinModules,\n validateModuleContract,\n assertModuleContract,\n useConsent,\n ConsentBanner,\n ConsentGate,\n ConsentReopenLink,\n type ConsentState,\n type ConsentSettings,\n type ConsentCategory,\n type ModuleDefinition,\n type ModuleSiteSetting,\n type ModulesSiteConfig,\n type Capability,\n type ResolvedModules,\n type BlockOverride,\n type BlockOverrides,\n} from \"./modules\";\n\n// Demo-lock\nexport { DemoLock } from \"./lock/DemoLock\";\n\n// Demo-bewerkmodus (lokaal-only) leeft op het subpad `@xiwebdesign/xicms-core/demo` — dit zijn\n// demo-store-internals die niet in de publieke runtime-API thuishoren (klantsites gebruiken ze nooit;\n// alleen de XiCMS.nl-showcase). DemoStudio wordt intern door XiCMSProvider gerenderd.\n\n// Firebase (client)\nexport {\n getFirebaseApp,\n getFirebaseAuth,\n getDb,\n getBucket,\n isFirebaseConfigured,\n} from \"./firebase/client\";\nexport { initAppCheck } from \"./firebase/appcheck\";\nexport { SiteChrome } from \"./SiteChrome\";\nexport { CORE_VERSION, CORE_PACKAGE, isNieuwer } from \"./version\";\n"],"mappings":"AAQA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,aAAa,gBAAgB;AAItC,SAAS,cAAc,wBAAwB;AAI/C,SAAS,KAAK,eAAe,mBAAmB,+BAA+B;AAE/E,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAE3B,SAAS,eAAe;AACxB,SAAS,gBAAgB,iBAAiB;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,2BAA6C;AACtD,SAAS,wBAAwB;AAGjC,SAAS,aAAa,cAAc,aAAa,gBAAgB,gBAAgB,qBAAqB;AAEtG,SAAS,oBAAoB;AAE7B,SAAS,yBAAyB;AAElC,SAAS,+BAA+B;AACxC,SAAS,mBAAmB,gBAAgB;AAE5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,cAAc,gBAAgB,SAAS,qBAAqB;AAMrE,SAAS,cAAc,eAAe;AACtC,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAOP,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,SAAS,UAAU,iBAAiB,eAAe;AAGnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,aAAa,mBAAmB,oBAAoB;AAC/E,SAAS,eAAe,oBAAoB;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,gBAAgB;AAOzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,cAAc,cAAc,iBAAiB;","names":[]}
|
|
@@ -33,9 +33,4 @@ export interface ImgProps {
|
|
|
33
33
|
/** Beeld vult absoluut z'n parent (bv. hero-achtergrond): geen wrapper-box, knop absoluut t.o.v. het blok. */
|
|
34
34
|
editFill?: boolean;
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
* Eén beeld-wrapper voor alle blokken. Gebruikt de geïnjecteerde `imageComponent` (next/image) als
|
|
38
|
-
* die er is, anders een gewone `<img>`. De display-grootte regel je met CSS (`style`/`className`,
|
|
39
|
-
* bv. `width:100%;height:100%;object-fit:cover`); `width`/`height` zijn de verhouding.
|
|
40
|
-
*/
|
|
41
|
-
export declare function Img({ src, alt, width, height, sizes, priority, className, style, editField, editFill }: ImgProps): import("react").JSX.Element;
|
|
36
|
+
export declare function Img({ src, alt, width, height, sizes, priority, className, style: styleProp, editField, editFill }: ImgProps): import("react").JSX.Element;
|
|
@@ -16,13 +16,18 @@ function InlineImageEditProvider({
|
|
|
16
16
|
}) {
|
|
17
17
|
return /* @__PURE__ */ jsx(InlineImageEditContext.Provider, { value: onReplace, children });
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function style_heeftPositie(style) {
|
|
20
|
+
return Boolean(style && "objectPosition" in style && style.objectPosition);
|
|
21
|
+
}
|
|
22
|
+
function Img({ src, alt, width, height, sizes, priority, className, style: styleProp, editField, editFill }) {
|
|
20
23
|
const ImageComponent = useImageComponent();
|
|
21
24
|
const onReplace = useContext(InlineImageEditContext);
|
|
22
25
|
const mediaSeed = useBlockData("media");
|
|
23
26
|
const eigenLadder = mediaSeed?.[src];
|
|
24
27
|
const ladder = eigenLadder?.variants?.sizes ?? [];
|
|
25
28
|
const webp = eigenLadder?.variants?.webp;
|
|
29
|
+
const focal = eigenLadder?.focal;
|
|
30
|
+
const style = focal && !style_heeftPositie(styleProp) ? { ...styleProp, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` } : styleProp;
|
|
26
31
|
const imageEl = ladder.length > 0 ? /* @__PURE__ */ jsxs("picture", { children: [
|
|
27
32
|
webp && /* @__PURE__ */ jsx("source", { type: "image/webp", srcSet: `${webp.url} ${webp.width}w`, sizes }),
|
|
28
33
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/media/ImageContext.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, type CSSProperties, type ElementType, type ReactNode } from \"react\";\nimport { useBlockData } from \"../blocks/context\";\nimport type { MediaItem } from \"../content/types\";\n\n/**\n * Optionele framework-beeldcomponent (bv. Next `Image`) voor automatische optimalisatie: responsive\n * `srcset`, moderne formaten (AVIF/WebP), correcte `width/height` (geen layout-shift), lazy-loading.\n * Core importeert `next/image` niet zelf (optionele peer) — de site injecteert 'm via de\n * `imageComponent`-prop op `XiCMSProvider`, net als `linkComponent`. Zonder injectie valt alles terug\n * op een gewone `<img>` (huidig gedrag, werkt overal).\n */\nexport type ImageComponentType = ElementType;\n\nconst ImageContext = createContext<ImageComponentType | null>(null);\n\nexport function ImageProvider({ component, children }: { component?: ImageComponentType; children: ReactNode }) {\n return <ImageContext.Provider value={component ?? null}>{children}</ImageContext.Provider>;\n}\n\nexport function useImageComponent(): ImageComponentType | null {\n return useContext(ImageContext);\n}\n\n/**\n * Inline beeld-bewerken: de visuele editor levert via deze context een `onReplace(field)`-callback\n * voor het GESELECTEERDE blok. Is die actief én geeft een `<Img>` een `editField` mee, dan toont het\n * beeld een vervang-icoontje. Buiten de editor (context = null) rendert `<Img>` exact als voorheen.\n */\n/**\n * `blockKey` komt sinds 7.9 uit de DOM, niet uit een per-blok provider.\n *\n * Tot 7.4 wikkelde de editor élk blok in zijn canvas apart in deze provider, met de blok-key in de\n * closure. Dat canvas is weg — de pagina van de klant ís de editor — en één provider om de hele\n * pagina weet niet meer bij welk blok een foto hoort. De knop lost dat op het moment van klikken\n * op: elk blok draagt al `data-xicms-block` (BlockRenderer), dus `closest()` vanaf de knop is\n * betrouwbaar en kost niets zolang er niemand klikt.\n */\nconst InlineImageEditContext = createContext<((field: string, blockKey?: string) => void) | null>(null);\n\nexport function InlineImageEditProvider({\n onReplace,\n children,\n}: {\n onReplace: ((field: string, blockKey?: string) => void) | null;\n children: ReactNode;\n}) {\n return <InlineImageEditContext.Provider value={onReplace}>{children}</InlineImageEditContext.Provider>;\n}\n\nexport interface ImgProps {\n src: string;\n alt: string;\n /** Intrinsieke breedte/hoogte (verhouding) → nodig voor next/image én tegen CLS. */\n width: number;\n height: number;\n /** Hint voor responsive selectie, bv. \"(max-width: 720px) 100vw, 33vw\". */\n sizes?: string;\n /** Above-the-fold (hero): eager + hoge prioriteit voor LCP. */\n priority?: boolean;\n className?: string;\n style?: CSSProperties;\n /** Blokveld (pad) met de URL — schakelt inline vervangen in de visuele editor in (bv. \"imageUrl\"). */\n editField?: string;\n /** Beeld vult absoluut z'n parent (bv. hero-achtergrond): geen wrapper-box, knop absoluut t.o.v. het blok. */\n editFill?: boolean;\n}\n\n/**\n * Eén beeld-wrapper voor alle blokken. Gebruikt de geïnjecteerde `imageComponent` (next/image) als\n * die er is, anders een gewone `<img>`. De display-grootte regel je met CSS (`style`/`className`,\n * bv. `width:100%;height:100%;object-fit:cover`); `width`/`height` zijn de verhouding.\n */\nexport function Img({ src, alt, width, height, sizes, priority, className, style, editField, editFill }: ImgProps) {\n const ImageComponent = useImageComponent();\n const onReplace = useContext(InlineImageEditContext);\n /**\n * Eigen srcset-ladder vóór next/image (7.13). De upload-pipeline (onMediaUpload, sharp) maakt\n * bij élke upload al webp + een maten-ladder; next/image liet dat werk links liggen en\n * transformeerde dezelfde beelden nóg een keer bij Vercel — betaalde image-transformations,\n * plus transformatie-latency op de eerste hit. De route levert daarom per pagina een\n * `media`-seed (url → varianten, zie server/blockData.ts); staat de URL van dit beeld daarin,\n * dan renderen we een kale <picture> met de eigen ladder en blijft Vercel er helemaal buiten.\n * Geen seed (editor, oudere site, extern beeld zoals Unsplash) → het bestaande pad.\n */\n const mediaSeed = useBlockData<Record<string, Pick<MediaItem, \"variants\" | \"width\" | \"height\">>>(\"media\");\n const eigenLadder = mediaSeed?.[src];\n const ladder = eigenLadder?.variants?.sizes ?? [];\n const webp = eigenLadder?.variants?.webp;\n const imageEl = ladder.length > 0 ? (\n <picture>\n {webp && <source type=\"image/webp\" srcSet={`${webp.url} ${webp.width}w`} sizes={sizes} />}\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={src}\n srcSet={ladder.map((v) => `${v.url} ${v.width}w`).join(\", \")}\n sizes={sizes}\n alt={alt}\n width={eigenLadder?.width ?? width}\n height={eigenLadder?.height ?? height}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : undefined}\n decoding=\"async\"\n className={className}\n style={style}\n />\n </picture>\n ) : ImageComponent ? (\n <ImageComponent\n src={src}\n alt={alt}\n width={width}\n height={height}\n sizes={sizes}\n priority={priority || undefined}\n className={className}\n style={style}\n />\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n sizes={sizes}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : undefined}\n className={className}\n style={style}\n />\n );\n\n // In de visuele editor (geselecteerd blok): toon een vervang-icoontje over het beeld. We wijzigen\n // alleen de URL → de grootte/stijl blijft (die komt uit het blok), dus blok én beeld behouden hun maat.\n if (onReplace && editField) {\n const replace = (e: { preventDefault: () => void; stopPropagation: () => void; currentTarget?: unknown }) => {\n e.preventDefault();\n e.stopPropagation();\n const el = e.currentTarget as HTMLElement | undefined;\n const key = el?.closest?.(\"[data-xicms-block]\")?.getAttribute(\"data-xicms-block\") ?? undefined;\n onReplace(editField, key);\n };\n const btn = (\n <button type=\"button\" className={`xicms-ve-img__btn${editFill ? \" xicms-ve-img__btn--fill\" : \"\"}`} title=\"Klik om de foto te wijzigen\" onClick={replace}>\n 📷 Foto wijzigen\n </button>\n );\n // Vullend (hero-achtergrond): geen wrapper-box (zou het absoluut gepositioneerde beeld breken);\n // de knop is absoluut t.o.v. het blok-element zelf (dat is `position:relative`).\n if (editFill) return (<>{imageEl}{btn}</>);\n // In-flow beeld: hele beeld klikbaar (duidelijke affordance) + knop rechtsboven.\n return (\n <span className=\"xicms-ve-img\" style={{ position: \"relative\", display: \"block\" }} title=\"Klik om de foto te wijzigen\" onClick={replace}>\n {imageEl}\n {btn}\n </span>\n );\n }\n return imageEl;\n}\n"],"mappings":";AAkBS,SAqIiB,UArIjB,KAyEL,YAzEK;AAhBT,SAAS,eAAe,kBAAwE;AAChG,SAAS,oBAAoB;AAY7B,MAAM,eAAe,cAAyC,IAAI;AAE3D,SAAS,cAAc,EAAE,WAAW,SAAS,GAA4D;AAC9G,SAAO,oBAAC,aAAa,UAAb,EAAsB,OAAO,aAAa,MAAO,UAAS;AACpE;AAEO,SAAS,oBAA+C;AAC7D,SAAO,WAAW,YAAY;AAChC;AAgBA,MAAM,yBAAyB,cAAmE,IAAI;AAE/F,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAGG;AACD,SAAO,oBAAC,uBAAuB,UAAvB,EAAgC,OAAO,WAAY,UAAS;AACtE;AAyBO,SAAS,IAAI,EAAE,KAAK,KAAK,OAAO,QAAQ,OAAO,UAAU,WAAW,OAAO,WAAW,SAAS,GAAa;AACjH,QAAM,iBAAiB,kBAAkB;AACzC,QAAM,YAAY,WAAW,sBAAsB;AAUnD,QAAM,YAAY,aAA+E,OAAO;AACxG,QAAM,cAAc,YAAY,GAAG;AACnC,QAAM,SAAS,aAAa,UAAU,SAAS,CAAC;AAChD,QAAM,OAAO,aAAa,UAAU;AACpC,QAAM,UAAU,OAAO,SAAS,IAC9B,qBAAC,aACE;AAAA,YAAQ,oBAAC,YAAO,MAAK,cAAa,QAAQ,GAAG,KAAK,GAAG,IAAI,KAAK,KAAK,KAAK,OAAc;AAAA,IAEvF;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,QAAQ,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,KAAK,IAAI;AAAA,QAC3D;AAAA,QACA;AAAA,QACA,OAAO,aAAa,SAAS;AAAA,QAC7B,QAAQ,aAAa,UAAU;AAAA,QAC/B,SAAS,WAAW,UAAU;AAAA,QAC9B,eAAe,WAAW,SAAS;AAAA,QACnC,UAAS;AAAA,QACT;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF,IACE,iBACF;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,YAAY;AAAA,MACtB;AAAA,MACA;AAAA;AAAA,EACF;AAAA;AAAA,IAGA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,WAAW,UAAU;AAAA,QAC9B,eAAe,WAAW,SAAS;AAAA,QACnC;AAAA,QACA;AAAA;AAAA,IACF;AAAA;AAKF,MAAI,aAAa,WAAW;AAC1B,UAAM,UAAU,CAAC,MAA4F;AAC3G,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,YAAM,KAAK,EAAE;AACb,YAAM,MAAM,IAAI,UAAU,oBAAoB,GAAG,aAAa,kBAAkB,KAAK;AACrF,gBAAU,WAAW,GAAG;AAAA,IAC1B;AACA,UAAM,MACJ,oBAAC,YAAO,MAAK,UAAS,WAAW,oBAAoB,WAAW,6BAA6B,EAAE,IAAI,OAAM,+BAA8B,SAAS,SAAS,qCAEzJ;AAIF,QAAI,SAAU,QAAQ,iCAAG;AAAA;AAAA,MAAS;AAAA,OAAI;AAEtC,WACE,qBAAC,UAAK,WAAU,gBAAe,OAAO,EAAE,UAAU,YAAY,SAAS,QAAQ,GAAG,OAAM,+BAA8B,SAAS,SAC5H;AAAA;AAAA,MACA;AAAA,OACH;AAAA,EAEJ;AACA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/media/ImageContext.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, type CSSProperties, type ElementType, type ReactNode } from \"react\";\nimport { useBlockData } from \"../blocks/context\";\nimport type { MediaItem } from \"../content/types\";\n\n/**\n * Optionele framework-beeldcomponent (bv. Next `Image`) voor automatische optimalisatie: responsive\n * `srcset`, moderne formaten (AVIF/WebP), correcte `width/height` (geen layout-shift), lazy-loading.\n * Core importeert `next/image` niet zelf (optionele peer) — de site injecteert 'm via de\n * `imageComponent`-prop op `XiCMSProvider`, net als `linkComponent`. Zonder injectie valt alles terug\n * op een gewone `<img>` (huidig gedrag, werkt overal).\n */\nexport type ImageComponentType = ElementType;\n\nconst ImageContext = createContext<ImageComponentType | null>(null);\n\nexport function ImageProvider({ component, children }: { component?: ImageComponentType; children: ReactNode }) {\n return <ImageContext.Provider value={component ?? null}>{children}</ImageContext.Provider>;\n}\n\nexport function useImageComponent(): ImageComponentType | null {\n return useContext(ImageContext);\n}\n\n/**\n * Inline beeld-bewerken: de visuele editor levert via deze context een `onReplace(field)`-callback\n * voor het GESELECTEERDE blok. Is die actief én geeft een `<Img>` een `editField` mee, dan toont het\n * beeld een vervang-icoontje. Buiten de editor (context = null) rendert `<Img>` exact als voorheen.\n */\n/**\n * `blockKey` komt sinds 7.9 uit de DOM, niet uit een per-blok provider.\n *\n * Tot 7.4 wikkelde de editor élk blok in zijn canvas apart in deze provider, met de blok-key in de\n * closure. Dat canvas is weg — de pagina van de klant ís de editor — en één provider om de hele\n * pagina weet niet meer bij welk blok een foto hoort. De knop lost dat op het moment van klikken\n * op: elk blok draagt al `data-xicms-block` (BlockRenderer), dus `closest()` vanaf de knop is\n * betrouwbaar en kost niets zolang er niemand klikt.\n */\nconst InlineImageEditContext = createContext<((field: string, blockKey?: string) => void) | null>(null);\n\nexport function InlineImageEditProvider({\n onReplace,\n children,\n}: {\n onReplace: ((field: string, blockKey?: string) => void) | null;\n children: ReactNode;\n}) {\n return <InlineImageEditContext.Provider value={onReplace}>{children}</InlineImageEditContext.Provider>;\n}\n\nexport interface ImgProps {\n src: string;\n alt: string;\n /** Intrinsieke breedte/hoogte (verhouding) → nodig voor next/image én tegen CLS. */\n width: number;\n height: number;\n /** Hint voor responsive selectie, bv. \"(max-width: 720px) 100vw, 33vw\". */\n sizes?: string;\n /** Above-the-fold (hero): eager + hoge prioriteit voor LCP. */\n priority?: boolean;\n className?: string;\n style?: CSSProperties;\n /** Blokveld (pad) met de URL — schakelt inline vervangen in de visuele editor in (bv. \"imageUrl\"). */\n editField?: string;\n /** Beeld vult absoluut z'n parent (bv. hero-achtergrond): geen wrapper-box, knop absoluut t.o.v. het blok. */\n editFill?: boolean;\n}\n\n/**\n * Eén beeld-wrapper voor alle blokken. Gebruikt de geïnjecteerde `imageComponent` (next/image) als\n * die er is, anders een gewone `<img>`. De display-grootte regel je met CSS (`style`/`className`,\n * bv. `width:100%;height:100%;object-fit:cover`); `width`/`height` zijn de verhouding.\n */\nfunction style_heeftPositie(style?: CSSProperties): boolean {\n return Boolean(style && \"objectPosition\" in style && style.objectPosition);\n}\n\nexport function Img({ src, alt, width, height, sizes, priority, className, style: styleProp, editField, editFill }: ImgProps) {\n const ImageComponent = useImageComponent();\n const onReplace = useContext(InlineImageEditContext);\n /**\n * Eigen srcset-ladder vóór next/image (7.13). De upload-pipeline (onMediaUpload, sharp) maakt\n * bij élke upload al webp + een maten-ladder; next/image liet dat werk links liggen en\n * transformeerde dezelfde beelden nóg een keer bij Vercel — betaalde image-transformations,\n * plus transformatie-latency op de eerste hit. De route levert daarom per pagina een\n * `media`-seed (url → varianten, zie server/blockData.ts); staat de URL van dit beeld daarin,\n * dan renderen we een kale <picture> met de eigen ladder en blijft Vercel er helemaal buiten.\n * Geen seed (editor, oudere site, extern beeld zoals Unsplash) → het bestaande pad.\n */\n const mediaSeed = useBlockData<Record<string, Pick<MediaItem, \"variants\" | \"width\" | \"height\" | \"focal\">>>(\"media\");\n const eigenLadder = mediaSeed?.[src];\n const ladder = eigenLadder?.variants?.sizes ?? [];\n const webp = eigenLadder?.variants?.webp;\n // Focuspunt (7.24): staat er in het beheer een focuspunt op deze foto, dan snijdt `object-fit:\n // cover` rond dát punt bij in plaats van rond het midden. Alleen als het blok zelf geen\n // object-position zet — een blok dat bewust \"top\" wil, wint.\n const focal = eigenLadder?.focal;\n const style = focal && !style_heeftPositie(styleProp)\n ? { ...styleProp, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` }\n : styleProp;\n const imageEl = ladder.length > 0 ? (\n <picture>\n {webp && <source type=\"image/webp\" srcSet={`${webp.url} ${webp.width}w`} sizes={sizes} />}\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={src}\n srcSet={ladder.map((v) => `${v.url} ${v.width}w`).join(\", \")}\n sizes={sizes}\n alt={alt}\n width={eigenLadder?.width ?? width}\n height={eigenLadder?.height ?? height}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : undefined}\n decoding=\"async\"\n className={className}\n style={style}\n />\n </picture>\n ) : ImageComponent ? (\n <ImageComponent\n src={src}\n alt={alt}\n width={width}\n height={height}\n sizes={sizes}\n priority={priority || undefined}\n className={className}\n style={style}\n />\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n sizes={sizes}\n loading={priority ? \"eager\" : \"lazy\"}\n fetchPriority={priority ? \"high\" : undefined}\n className={className}\n style={style}\n />\n );\n\n // In de visuele editor (geselecteerd blok): toon een vervang-icoontje over het beeld. We wijzigen\n // alleen de URL → de grootte/stijl blijft (die komt uit het blok), dus blok én beeld behouden hun maat.\n if (onReplace && editField) {\n const replace = (e: { preventDefault: () => void; stopPropagation: () => void; currentTarget?: unknown }) => {\n e.preventDefault();\n e.stopPropagation();\n const el = e.currentTarget as HTMLElement | undefined;\n const key = el?.closest?.(\"[data-xicms-block]\")?.getAttribute(\"data-xicms-block\") ?? undefined;\n onReplace(editField, key);\n };\n const btn = (\n <button type=\"button\" className={`xicms-ve-img__btn${editFill ? \" xicms-ve-img__btn--fill\" : \"\"}`} title=\"Klik om de foto te wijzigen\" onClick={replace}>\n 📷 Foto wijzigen\n </button>\n );\n // Vullend (hero-achtergrond): geen wrapper-box (zou het absoluut gepositioneerde beeld breken);\n // de knop is absoluut t.o.v. het blok-element zelf (dat is `position:relative`).\n if (editFill) return (<>{imageEl}{btn}</>);\n // In-flow beeld: hele beeld klikbaar (duidelijke affordance) + knop rechtsboven.\n return (\n <span className=\"xicms-ve-img\" style={{ position: \"relative\", display: \"block\" }} title=\"Klik om de foto te wijzigen\" onClick={replace}>\n {imageEl}\n {btn}\n </span>\n );\n }\n return imageEl;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Wat is nieuw" (7.24) — korte, klantgerichte notities per release, getoond in /beheer.
|
|
3
|
+
*
|
|
4
|
+
* Features die je bouwt zonder dat de klant het hoort, bestaan voor die klant niet. Dit is de
|
|
5
|
+
* enige plek waar dat verteld wordt; `CoreVersion` toont de notitie van de huidige minor één keer.
|
|
6
|
+
* Alleen wat de KLANT merkt, in zijn woorden — geen interne refactors.
|
|
7
|
+
*/
|
|
8
|
+
export interface ReleaseNote {
|
|
9
|
+
/** major.minor — patches tellen niet als "nieuw". */
|
|
10
|
+
versie: string;
|
|
11
|
+
punten: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare const RELEASE_NOTES: ReleaseNote[];
|
|
14
|
+
/** De notitie die bij deze engine-versie hoort (op major.minor). */
|
|
15
|
+
export declare function releaseNoteVoor(versie: string): ReleaseNote | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const RELEASE_NOTES = [
|
|
2
|
+
{
|
|
3
|
+
versie: "7.24",
|
|
4
|
+
punten: [
|
|
5
|
+
"Het menu dat je onder Menu instelt staat nu \xE9cht op de site \u2014 met submenu's.",
|
|
6
|
+
"Bij Pagina's zie je meteen welke pagina's wijzigingen hebben die nog niet live staan.",
|
|
7
|
+
"Een pagina kun je offline halen (en later weer publiceren) of naar de prullenbak doen \u2014 30 dagen te herstellen.",
|
|
8
|
+
"Verander je het adres van een pagina, dan verwijst het oude adres automatisch door.",
|
|
9
|
+
"Deel een preview-link van een werkversie met iemand zonder account.",
|
|
10
|
+
"Kies een focuspunt op een foto zodat het juiste deel in beeld blijft op mobiel.",
|
|
11
|
+
"Formulieren: inzendingen exporteren als CSV, verwijderen, en zien wat nieuw is.",
|
|
12
|
+
"Statistieken tonen nu ook waar bezoekers vandaan komen en op welk apparaat."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
versie: "7.23",
|
|
17
|
+
punten: ["Per productoptie (bijvoorbeeld Print of Kleur) kun je aangeven dat elke waarde een eigen foto heeft."]
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
function releaseNoteVoor(versie) {
|
|
21
|
+
const mm = versie.split(".").slice(0, 2).join(".");
|
|
22
|
+
return RELEASE_NOTES.find((n) => n.versie === mm) ?? null;
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
RELEASE_NOTES,
|
|
26
|
+
releaseNoteVoor
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=releaseNotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/releaseNotes.ts"],"sourcesContent":["/**\n * \"Wat is nieuw\" (7.24) — korte, klantgerichte notities per release, getoond in /beheer.\n *\n * Features die je bouwt zonder dat de klant het hoort, bestaan voor die klant niet. Dit is de\n * enige plek waar dat verteld wordt; `CoreVersion` toont de notitie van de huidige minor één keer.\n * Alleen wat de KLANT merkt, in zijn woorden — geen interne refactors.\n */\nexport interface ReleaseNote {\n /** major.minor — patches tellen niet als \"nieuw\". */\n versie: string;\n punten: string[];\n}\n\nexport const RELEASE_NOTES: ReleaseNote[] = [\n {\n versie: \"7.24\",\n punten: [\n \"Het menu dat je onder Menu instelt staat nu écht op de site — met submenu's.\",\n \"Bij Pagina's zie je meteen welke pagina's wijzigingen hebben die nog niet live staan.\",\n \"Een pagina kun je offline halen (en later weer publiceren) of naar de prullenbak doen — 30 dagen te herstellen.\",\n \"Verander je het adres van een pagina, dan verwijst het oude adres automatisch door.\",\n \"Deel een preview-link van een werkversie met iemand zonder account.\",\n \"Kies een focuspunt op een foto zodat het juiste deel in beeld blijft op mobiel.\",\n \"Formulieren: inzendingen exporteren als CSV, verwijderen, en zien wat nieuw is.\",\n \"Statistieken tonen nu ook waar bezoekers vandaan komen en op welk apparaat.\",\n ],\n },\n {\n versie: \"7.23\",\n punten: [\"Per productoptie (bijvoorbeeld Print of Kleur) kun je aangeven dat elke waarde een eigen foto heeft.\"],\n },\n];\n\n/** De notitie die bij deze engine-versie hoort (op major.minor). */\nexport function releaseNoteVoor(versie: string): ReleaseNote | null {\n const mm = versie.split(\".\").slice(0, 2).join(\".\");\n return RELEASE_NOTES.find((n) => n.versie === mm) ?? null;\n}\n"],"mappings":"AAaO,MAAM,gBAA+B;AAAA,EAC1C;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ,CAAC,sGAAsG;AAAA,EACjH;AACF;AAGO,SAAS,gBAAgB,QAAoC;AAClE,QAAM,KAAK,OAAO,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACjD,SAAO,cAAc,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK;AACvD;","names":[]}
|
|
@@ -11,6 +11,15 @@ import * as z from "zod/mini";
|
|
|
11
11
|
* die uit de pas kan lopen met de href. Het beheerscherm mag de gebruiker wél die keuze voorleggen;
|
|
12
12
|
* wat er wordt opgeslagen is één ondubbelzinnig adres.
|
|
13
13
|
*/
|
|
14
|
+
declare const NavLeafSchema: z.ZodMiniObject<{
|
|
15
|
+
label: z.ZodMiniString<string>;
|
|
16
|
+
href: z.ZodMiniString<string>;
|
|
17
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
18
|
+
_blank: "_blank";
|
|
19
|
+
_embed: "_embed";
|
|
20
|
+
_self: "_self";
|
|
21
|
+
}>>;
|
|
22
|
+
}, z.core.$strip>;
|
|
14
23
|
export declare const NavItemSchema: z.ZodMiniObject<{
|
|
15
24
|
label: z.ZodMiniString<string>;
|
|
16
25
|
href: z.ZodMiniString<string>;
|
|
@@ -19,10 +28,43 @@ export declare const NavItemSchema: z.ZodMiniObject<{
|
|
|
19
28
|
_embed: "_embed";
|
|
20
29
|
_self: "_self";
|
|
21
30
|
}>>;
|
|
31
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
32
|
+
label: z.ZodMiniString<string>;
|
|
33
|
+
href: z.ZodMiniString<string>;
|
|
34
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
35
|
+
_blank: "_blank";
|
|
36
|
+
_embed: "_embed";
|
|
37
|
+
_self: "_self";
|
|
38
|
+
}>>;
|
|
39
|
+
}, z.core.$strip>>>;
|
|
22
40
|
}, z.core.$strip>;
|
|
23
41
|
export type NavItem = z.infer<typeof NavItemSchema>;
|
|
42
|
+
export type NavLeaf = z.infer<typeof NavLeafSchema>;
|
|
24
43
|
/** Verwijst dit item naar buiten? Afgeleid uit de href, zodat er geen tweede waarheid ontstaat. */
|
|
25
44
|
export declare function isExterneLink(href: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Eén doorverwijzing (7.24): een oud pad dat permanent naar een nieuw pad wijst.
|
|
47
|
+
*
|
|
48
|
+
* Ontstaat vanzelf wanneer een klant het pad van een pagina wijzigt — de oude URL staat in
|
|
49
|
+
* zoekmachines, in mails en op visitekaartjes, en zonder redirect wordt dat een 404. Alleen
|
|
50
|
+
* interne paden (beginnen met `/`): een open redirect naar buiten is een phishing-hulpmiddel.
|
|
51
|
+
*/
|
|
52
|
+
export declare const RedirectSchema: z.ZodMiniObject<{
|
|
53
|
+
from: z.ZodMiniString<string>;
|
|
54
|
+
to: z.ZodMiniString<string>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
export type Redirect = z.infer<typeof RedirectSchema>;
|
|
57
|
+
/**
|
|
58
|
+
* Normaliseert een pad zoals de catch-all dat doet ("/over-ons/" → "/over-ons", "" → "/"), zodat
|
|
59
|
+
* een redirect matcht ongeacht een afsluitende slash.
|
|
60
|
+
*/
|
|
61
|
+
export declare function normaliseerPad(pad: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Zoekt de bestemming voor een pad in de redirect-lijst. Volgt ketens (a→b, b→c) tot een diepte
|
|
64
|
+
* van vijf en weigert lussen: een klant die eerst /a naar /b hernoemt en later terug, mag daar
|
|
65
|
+
* geen eindeloze redirect aan overhouden. `null` = geen doorverwijzing.
|
|
66
|
+
*/
|
|
67
|
+
export declare function zoekRedirect(pad: string, redirects: Redirect[] | undefined): string | null;
|
|
26
68
|
/** Het pad waarop een ingekaderde externe pagina wordt getoond. */
|
|
27
69
|
export declare const EMBED_PAD = "/extern";
|
|
28
70
|
/**
|
|
@@ -115,6 +157,15 @@ export declare const FooterColumnSchema: z.ZodMiniObject<{
|
|
|
115
157
|
_embed: "_embed";
|
|
116
158
|
_self: "_self";
|
|
117
159
|
}>>;
|
|
160
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
161
|
+
label: z.ZodMiniString<string>;
|
|
162
|
+
href: z.ZodMiniString<string>;
|
|
163
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
164
|
+
_blank: "_blank";
|
|
165
|
+
_embed: "_embed";
|
|
166
|
+
_self: "_self";
|
|
167
|
+
}>>;
|
|
168
|
+
}, z.core.$strip>>>;
|
|
118
169
|
}, z.core.$strip>>>;
|
|
119
170
|
}, z.core.$strip>;
|
|
120
171
|
/** Bewerkbare footer (klant past dit aan in /beheer; core levert de unstyled `SiteFooter`). */
|
|
@@ -129,6 +180,15 @@ export declare const FooterSettingsSchema: z.ZodMiniObject<{
|
|
|
129
180
|
_embed: "_embed";
|
|
130
181
|
_self: "_self";
|
|
131
182
|
}>>;
|
|
183
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
184
|
+
label: z.ZodMiniString<string>;
|
|
185
|
+
href: z.ZodMiniString<string>;
|
|
186
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
187
|
+
_blank: "_blank";
|
|
188
|
+
_embed: "_embed";
|
|
189
|
+
_self: "_self";
|
|
190
|
+
}>>;
|
|
191
|
+
}, z.core.$strip>>>;
|
|
132
192
|
}, z.core.$strip>>>;
|
|
133
193
|
}, z.core.$strip>>>;
|
|
134
194
|
social: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
@@ -157,6 +217,15 @@ export declare const SiteSettingsSchema: z.ZodMiniObject<{
|
|
|
157
217
|
_embed: "_embed";
|
|
158
218
|
_self: "_self";
|
|
159
219
|
}>>;
|
|
220
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
221
|
+
label: z.ZodMiniString<string>;
|
|
222
|
+
href: z.ZodMiniString<string>;
|
|
223
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
224
|
+
_blank: "_blank";
|
|
225
|
+
_embed: "_embed";
|
|
226
|
+
_self: "_self";
|
|
227
|
+
}>>;
|
|
228
|
+
}, z.core.$strip>>>;
|
|
160
229
|
}, z.core.$strip>>>;
|
|
161
230
|
theme: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
162
231
|
primary: z.ZodMiniString<string>;
|
|
@@ -223,6 +292,15 @@ export declare const SiteSettingsSchema: z.ZodMiniObject<{
|
|
|
223
292
|
_embed: "_embed";
|
|
224
293
|
_self: "_self";
|
|
225
294
|
}>>;
|
|
295
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
296
|
+
label: z.ZodMiniString<string>;
|
|
297
|
+
href: z.ZodMiniString<string>;
|
|
298
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
299
|
+
_blank: "_blank";
|
|
300
|
+
_embed: "_embed";
|
|
301
|
+
_self: "_self";
|
|
302
|
+
}>>;
|
|
303
|
+
}, z.core.$strip>>>;
|
|
226
304
|
}, z.core.$strip>>>;
|
|
227
305
|
}, z.core.$strip>>>;
|
|
228
306
|
social: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
@@ -240,6 +318,10 @@ export declare const SiteSettingsSchema: z.ZodMiniObject<{
|
|
|
240
318
|
label: z.ZodMiniString<string>;
|
|
241
319
|
href: z.ZodMiniString<string>;
|
|
242
320
|
}, z.core.$strip>>>;
|
|
321
|
+
redirects: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
322
|
+
from: z.ZodMiniString<string>;
|
|
323
|
+
to: z.ZodMiniString<string>;
|
|
324
|
+
}, z.core.$strip>>>;
|
|
243
325
|
}, z.core.$strip>;
|
|
244
326
|
export type SiteSettings = z.infer<typeof SiteSettingsSchema>;
|
|
245
327
|
/**
|
|
@@ -257,6 +339,15 @@ export declare const PublicSiteSettingsSchema: z.ZodMiniObject<{
|
|
|
257
339
|
_embed: "_embed";
|
|
258
340
|
_self: "_self";
|
|
259
341
|
}>>;
|
|
342
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
343
|
+
label: z.ZodMiniString<string>;
|
|
344
|
+
href: z.ZodMiniString<string>;
|
|
345
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
346
|
+
_blank: "_blank";
|
|
347
|
+
_embed: "_embed";
|
|
348
|
+
_self: "_self";
|
|
349
|
+
}>>;
|
|
350
|
+
}, z.core.$strip>>>;
|
|
260
351
|
}, z.core.$strip>>>;
|
|
261
352
|
theme: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
262
353
|
primary: z.ZodMiniString<string>;
|
|
@@ -314,6 +405,15 @@ export declare const PublicSiteSettingsSchema: z.ZodMiniObject<{
|
|
|
314
405
|
_embed: "_embed";
|
|
315
406
|
_self: "_self";
|
|
316
407
|
}>>;
|
|
408
|
+
children: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
409
|
+
label: z.ZodMiniString<string>;
|
|
410
|
+
href: z.ZodMiniString<string>;
|
|
411
|
+
target: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
412
|
+
_blank: "_blank";
|
|
413
|
+
_embed: "_embed";
|
|
414
|
+
_self: "_self";
|
|
415
|
+
}>>;
|
|
416
|
+
}, z.core.$strip>>>;
|
|
317
417
|
}, z.core.$strip>>>;
|
|
318
418
|
}, z.core.$strip>>>;
|
|
319
419
|
social: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
@@ -327,7 +427,12 @@ export declare const PublicSiteSettingsSchema: z.ZodMiniObject<{
|
|
|
327
427
|
enabled: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
328
428
|
config: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniUnknown>>;
|
|
329
429
|
}, z.core.$strip>]>>>;
|
|
430
|
+
redirects: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
431
|
+
from: z.ZodMiniString<string>;
|
|
432
|
+
to: z.ZodMiniString<string>;
|
|
433
|
+
}, z.core.$strip>>>;
|
|
330
434
|
}, z.core.$strip>;
|
|
331
435
|
export type PublicSiteSettings = z.infer<typeof PublicSiteSettingsSchema>;
|
|
332
436
|
/** Haalt de publiek-veilige subset uit de volledige settings (voor het `settings/public`-doc). */
|
|
333
437
|
export declare function toPublicSettings(s: SiteSettings): PublicSiteSettings;
|
|
438
|
+
export {};
|