@xiwebdesign/xicms-core 7.12.0 → 7.13.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.
Files changed (129) hide show
  1. package/dist/admin/AdminApp.js +2 -0
  2. package/dist/admin/AdminApp.js.map +1 -1
  3. package/dist/admin/lib/zodFields.d.ts +2 -2
  4. package/dist/admin/lib/zodFields.js +2 -0
  5. package/dist/admin/lib/zodFields.js.map +1 -1
  6. package/dist/admin/ui/toast.d.ts +4 -0
  7. package/dist/admin/ui/toast.js +9 -3
  8. package/dist/admin/ui/toast.js.map +1 -1
  9. package/dist/admin/visual/VisualEditor.js +32 -2
  10. package/dist/admin/visual/VisualEditor.js.map +1 -1
  11. package/dist/blocks/builtinSchemas.d.ts +23 -0
  12. package/dist/blocks/builtinSchemas.js +138 -0
  13. package/dist/blocks/builtinSchemas.js.map +1 -0
  14. package/dist/blocks/builtins/Accordion.js +0 -2
  15. package/dist/blocks/builtins/Accordion.js.map +1 -1
  16. package/dist/blocks/builtins/Agenda.js +0 -2
  17. package/dist/blocks/builtins/Agenda.js.map +1 -1
  18. package/dist/blocks/builtins/Banner.js +0 -2
  19. package/dist/blocks/builtins/Banner.js.map +1 -1
  20. package/dist/blocks/builtins/BeforeAfter.js +0 -2
  21. package/dist/blocks/builtins/BeforeAfter.js.map +1 -1
  22. package/dist/blocks/builtins/Booking.js +0 -2
  23. package/dist/blocks/builtins/Booking.js.map +1 -1
  24. package/dist/blocks/builtins/Breadcrumbs.js +0 -2
  25. package/dist/blocks/builtins/Breadcrumbs.js.map +1 -1
  26. package/dist/blocks/builtins/Cards.js +0 -2
  27. package/dist/blocks/builtins/Cards.js.map +1 -1
  28. package/dist/blocks/builtins/Columns.js +0 -2
  29. package/dist/blocks/builtins/Columns.js.map +1 -1
  30. package/dist/blocks/builtins/Contact.d.ts +1 -1
  31. package/dist/blocks/builtins/Contact.js +0 -3
  32. package/dist/blocks/builtins/Contact.js.map +1 -1
  33. package/dist/blocks/builtins/Countdown.js +0 -2
  34. package/dist/blocks/builtins/Countdown.js.map +1 -1
  35. package/dist/blocks/builtins/Cta.js +0 -2
  36. package/dist/blocks/builtins/Cta.js.map +1 -1
  37. package/dist/blocks/builtins/Downloads.js +0 -2
  38. package/dist/blocks/builtins/Downloads.js.map +1 -1
  39. package/dist/blocks/builtins/Elements.js +0 -4
  40. package/dist/blocks/builtins/Elements.js.map +1 -1
  41. package/dist/blocks/builtins/Embed.js +0 -2
  42. package/dist/blocks/builtins/Embed.js.map +1 -1
  43. package/dist/blocks/builtins/Faq.js +0 -2
  44. package/dist/blocks/builtins/Faq.js.map +1 -1
  45. package/dist/blocks/builtins/FormBlock.js +0 -2
  46. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  47. package/dist/blocks/builtins/Gallery.d.ts +1 -1
  48. package/dist/blocks/builtins/Gallery.js +0 -2
  49. package/dist/blocks/builtins/Gallery.js.map +1 -1
  50. package/dist/blocks/builtins/Hero.js +0 -2
  51. package/dist/blocks/builtins/Hero.js.map +1 -1
  52. package/dist/blocks/builtins/ImageBlock.js +0 -2
  53. package/dist/blocks/builtins/ImageBlock.js.map +1 -1
  54. package/dist/blocks/builtins/Jobs.d.ts +1 -1
  55. package/dist/blocks/builtins/Jobs.js +0 -2
  56. package/dist/blocks/builtins/Jobs.js.map +1 -1
  57. package/dist/blocks/builtins/Layout.js +0 -3
  58. package/dist/blocks/builtins/Layout.js.map +1 -1
  59. package/dist/blocks/builtins/MapBlock.js +0 -2
  60. package/dist/blocks/builtins/MapBlock.js.map +1 -1
  61. package/dist/blocks/builtins/NewsListBlock.js +0 -2
  62. package/dist/blocks/builtins/NewsListBlock.js.map +1 -1
  63. package/dist/blocks/builtins/Newsletter.js +0 -2
  64. package/dist/blocks/builtins/Newsletter.js.map +1 -1
  65. package/dist/blocks/builtins/OpeningHours.js +0 -2
  66. package/dist/blocks/builtins/OpeningHours.js.map +1 -1
  67. package/dist/blocks/builtins/PdfBlock.js +0 -2
  68. package/dist/blocks/builtins/PdfBlock.js.map +1 -1
  69. package/dist/blocks/builtins/Portfolio.js +0 -2
  70. package/dist/blocks/builtins/Portfolio.js.map +1 -1
  71. package/dist/blocks/builtins/Pricing.d.ts +1 -1
  72. package/dist/blocks/builtins/Pricing.js +0 -2
  73. package/dist/blocks/builtins/Pricing.js.map +1 -1
  74. package/dist/blocks/builtins/RichTextBlock.js +0 -2
  75. package/dist/blocks/builtins/RichTextBlock.js.map +1 -1
  76. package/dist/blocks/builtins/Search.js +0 -2
  77. package/dist/blocks/builtins/Search.js.map +1 -1
  78. package/dist/blocks/builtins/Sections.js +0 -10
  79. package/dist/blocks/builtins/Sections.js.map +1 -1
  80. package/dist/blocks/builtins/Slider.js +0 -2
  81. package/dist/blocks/builtins/Slider.js.map +1 -1
  82. package/dist/blocks/builtins/Spacer.js +0 -2
  83. package/dist/blocks/builtins/Spacer.js.map +1 -1
  84. package/dist/blocks/builtins/SymbolRef.js +3 -5
  85. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  86. package/dist/blocks/builtins/Team.js +0 -2
  87. package/dist/blocks/builtins/Team.js.map +1 -1
  88. package/dist/blocks/builtins/Testimonials.d.ts +1 -1
  89. package/dist/blocks/builtins/Testimonials.js +0 -2
  90. package/dist/blocks/builtins/Testimonials.js.map +1 -1
  91. package/dist/blocks/builtins/Video.d.ts +1 -1
  92. package/dist/blocks/builtins/Video.js +0 -2
  93. package/dist/blocks/builtins/Video.js.map +1 -1
  94. package/dist/blocks/builtins/WhatsappChat.js +0 -2
  95. package/dist/blocks/builtins/WhatsappChat.js.map +1 -1
  96. package/dist/blocks/builtins/Widgets.js +0 -26
  97. package/dist/blocks/builtins/Widgets.js.map +1 -1
  98. package/dist/blocks/customTemplate/definition.js +1 -1
  99. package/dist/blocks/customTemplate/definition.js.map +1 -1
  100. package/dist/blocks/registry.d.ts +24 -13
  101. package/dist/blocks/registry.js +15 -4
  102. package/dist/blocks/registry.js.map +1 -1
  103. package/dist/media/ImageContext.js +24 -1
  104. package/dist/media/ImageContext.js.map +1 -1
  105. package/dist/schema/blockBase.d.ts +99 -0
  106. package/dist/schema/blockBase.js +22 -0
  107. package/dist/schema/blockBase.js.map +1 -0
  108. package/dist/schema/blocks.d.ts +1 -91
  109. package/dist/schema/blocks.js +64 -77
  110. package/dist/schema/blocks.js.map +1 -1
  111. package/dist/schema/pages.js +1 -1
  112. package/dist/schema/pages.js.map +1 -1
  113. package/dist/schema/shop.js +1 -1
  114. package/dist/schema/shop.js.map +1 -1
  115. package/dist/schema/symbols.js +1 -1
  116. package/dist/schema/symbols.js.map +1 -1
  117. package/dist/server/blockData.js +29 -9
  118. package/dist/server/blockData.js.map +1 -1
  119. package/dist/server/index.d.ts +1 -1
  120. package/dist/server/index.js +2 -1
  121. package/dist/server/index.js.map +1 -1
  122. package/dist/server/pages.d.ts +16 -0
  123. package/dist/server/pages.js +10 -1
  124. package/dist/server/pages.js.map +1 -1
  125. package/dist/server/shop.js +6 -10
  126. package/dist/server/shop.js.map +1 -1
  127. package/dist/styles.css +45 -0
  128. package/dist/version.js +1 -1
  129. package/package.json +1 -1
@@ -2,20 +2,6 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
4
  import { Onvoltooid } from "./lib/Onvoltooid.js";
5
- import {
6
- NewsWidgetSchema,
7
- AgendaWidgetSchema,
8
- OpeningHoursWidgetSchema,
9
- ContactWidgetSchema,
10
- SocialWidgetSchema,
11
- NewsletterWidgetSchema,
12
- CtaWidgetSchema,
13
- GalleryWidgetSchema,
14
- TestimonialsWidgetSchema,
15
- CardsWidgetSchema,
16
- DownloadsWidgetSchema,
17
- CountdownWidgetSchema
18
- } from "../../schema/blocks.js";
19
5
  import { defineBlock } from "../registry.js";
20
6
  import { Btn } from "../ui/index.js";
21
7
  import { useNews } from "../../components/News.js";
@@ -41,7 +27,6 @@ function NewsWidgetRender({ block }) {
41
27
  }
42
28
  const newsWidget = defineBlock({
43
29
  type: "newsWidget",
44
- schema: NewsWidgetSchema,
45
30
  Render: NewsWidgetRender,
46
31
  label: "Laatste nieuws",
47
32
  icon: "\u{1F5DE}\uFE0F",
@@ -61,7 +46,6 @@ function AgendaWidgetRender({ block }) {
61
46
  }
62
47
  const agendaWidget = defineBlock({
63
48
  type: "agendaWidget",
64
- schema: AgendaWidgetSchema,
65
49
  Render: AgendaWidgetRender,
66
50
  label: "Komende agenda",
67
51
  icon: "\u{1F4C5}",
@@ -85,7 +69,6 @@ function OpeningHoursWidgetRender({ block }) {
85
69
  }
86
70
  const openingHoursWidget = defineBlock({
87
71
  type: "openingHoursWidget",
88
- schema: OpeningHoursWidgetSchema,
89
72
  Render: OpeningHoursWidgetRender,
90
73
  label: "Openingstijden (compact)",
91
74
  icon: "\u{1F552}",
@@ -113,7 +96,6 @@ function ContactWidgetRender({ block }) {
113
96
  }
114
97
  const contactWidget = defineBlock({
115
98
  type: "contactWidget",
116
- schema: ContactWidgetSchema,
117
99
  Render: ContactWidgetRender,
118
100
  label: "Contact (compact)",
119
101
  icon: "\u{1F4C7}",
@@ -130,7 +112,6 @@ function SocialWidgetRender({ block }) {
130
112
  }
131
113
  const socialWidget = defineBlock({
132
114
  type: "socialWidget",
133
- schema: SocialWidgetSchema,
134
115
  Render: SocialWidgetRender,
135
116
  label: "Social (compact)",
136
117
  icon: "\u{1F517}",
@@ -150,7 +131,6 @@ function NewsletterWidgetRender({ block }) {
150
131
  }
151
132
  const newsletterWidget = defineBlock({
152
133
  type: "newsletterWidget",
153
- schema: NewsletterWidgetSchema,
154
134
  Render: NewsletterWidgetRender,
155
135
  label: "Nieuwsbrief (compact)",
156
136
  icon: "\u2709\uFE0F",
@@ -167,7 +147,6 @@ function CtaWidgetRender({ block }) {
167
147
  }
168
148
  const ctaWidget = defineBlock({
169
149
  type: "ctaWidget",
170
- schema: CtaWidgetSchema,
171
150
  Render: CtaWidgetRender,
172
151
  label: "Call-to-action (compact)",
173
152
  icon: "\u{1F449}",
@@ -187,7 +166,6 @@ function GalleryWidgetRender({ block }) {
187
166
  }
188
167
  const galleryWidget = defineBlock({
189
168
  type: "galleryWidget",
190
- schema: GalleryWidgetSchema,
191
169
  Render: GalleryWidgetRender,
192
170
  label: "Fotostrip (compact)",
193
171
  icon: "\u{1F5BC}\uFE0F",
@@ -218,7 +196,6 @@ function TestimonialsWidgetRender({ block }) {
218
196
  }
219
197
  const testimonialsWidget = defineBlock({
220
198
  type: "testimonialsWidget",
221
- schema: TestimonialsWidgetSchema,
222
199
  Render: TestimonialsWidgetRender,
223
200
  label: "Review (compact)",
224
201
  icon: "\u2B50",
@@ -244,7 +221,6 @@ function CardsWidgetRender({ block }) {
244
221
  }
245
222
  const cardsWidget = defineBlock({
246
223
  type: "cardsWidget",
247
- schema: CardsWidgetSchema,
248
224
  Render: CardsWidgetRender,
249
225
  label: "Diensten (compact)",
250
226
  icon: "\u{1F9E9}",
@@ -268,7 +244,6 @@ function DownloadsWidgetRender({ block }) {
268
244
  }
269
245
  const downloadsWidget = defineBlock({
270
246
  type: "downloadsWidget",
271
- schema: DownloadsWidgetSchema,
272
247
  Render: DownloadsWidgetRender,
273
248
  label: "Downloads (compact)",
274
249
  icon: "\u{1F4E5}",
@@ -285,7 +260,6 @@ function CountdownWidgetRender({ block }) {
285
260
  }
286
261
  const countdownWidget = defineBlock({
287
262
  type: "countdownWidget",
288
- schema: CountdownWidgetSchema,
289
263
  Render: CountdownWidgetRender,
290
264
  label: "Aftelteller (compact)",
291
265
  icon: "\u23F3",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Widgets.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { z } from \"zod\";\nimport { Onvoltooid } from \"./lib/Onvoltooid\";\nimport {\n NewsWidgetSchema,\n AgendaWidgetSchema,\n OpeningHoursWidgetSchema,\n ContactWidgetSchema,\n SocialWidgetSchema,\n NewsletterWidgetSchema,\n CtaWidgetSchema,\n GalleryWidgetSchema,\n TestimonialsWidgetSchema,\n CardsWidgetSchema,\n DownloadsWidgetSchema,\n CountdownWidgetSchema,\n} from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { Btn } from \"../ui\";\nimport { useNews } from \"../../components/News\";\nimport { selectAgendaEvents, formatEventDate } from \"./Agenda\";\nimport { parseDays } from \"./lib/openingHours\";\nimport { useCountdown, CountdownDigits } from \"./Countdown\";\nimport { TextInput } from \"@astryxdesign/core/TextInput\";\nimport { Button } from \"@astryxdesign/core/Button\";\n\n/**\n * Widgets = compacte zijbalk-varianten van de modules. Elke widget hergebruikt de gegevens en\n * logica van de volwaardige versie (zelfde nieuws-collectie, zelfde agenda-selectie, zelfde\n * countdown-hook) en rendert die alleen compacter. Plaats ze in het \"Met zijbalk\"-blok of in een\n * smalle kolom naast je hoofdinhoud.\n */\n\n/* ── Laatste nieuws ────────────────────────────────────────────────────────── */\ntype NewsW = z.infer<typeof NewsWidgetSchema>;\nfunction NewsWidgetRender({ block }: BlockRenderProps<NewsW>) {\n const { articles, loading } = useNews({ max: block.limit });\n const shown = articles.slice(0, block.limit);\n return (\n <div className=\"xicms-widget xicms-widget--news\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Laatste nieuws\"}</h3>\n {loading ? (\n <p className=\"xicms-widget__empty\">Nieuws laden…</p>\n ) : shown.length === 0 ? (\n <p className=\"xicms-widget__empty\">Nog geen nieuws.</p>\n ) : (\n <ul className=\"xicms-widget__list\">\n {shown.map((a) => (\n <li key={a.id} className=\"xicms-widget__item\">\n {block.showImages && a.coverUrl && (\n // eslint-disable-next-line @next/next/no-img-element\n <img className=\"xicms-widget__thumb\" src={a.coverUrl} alt=\"\" loading=\"lazy\" />\n )}\n <div>\n <a className=\"xicms-widget__link\" href={`${block.basePath}/${a.slug}`}>{a.title}</a>\n <span className=\"xicms-widget__meta\">{a.date}</span>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\nexport const newsWidget = defineBlock({\n type: \"newsWidget\",\n schema: NewsWidgetSchema,\n Render: NewsWidgetRender,\n label: \"Laatste nieuws\",\n icon: \"🗞️\",\n category: \"widget\",\n defaults: () => ({ heading: \"Laatste nieuws\", limit: 5, basePath: \"/nieuws\", showImages: false }),\n});\n\n/* ── Komende agenda ────────────────────────────────────────────────────────── */\ntype AgendaW = z.infer<typeof AgendaWidgetSchema>;\nfunction AgendaWidgetRender({ block }: BlockRenderProps<AgendaW>) {\n const events = selectAgendaEvents((block.events ?? []).map((e, i) => ({ ...e, _i: i })), { hidePast: block.hidePast, limit: block.limit });\n return (\n <div className=\"xicms-widget xicms-widget--agenda\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Komende evenementen\"}</h3>\n {events.length === 0 ? (\n <p className=\"xicms-widget__empty\">Geen geplande evenementen.</p>\n ) : (\n <ul className=\"xicms-widget__list\">\n {events.map((e) => (\n <li key={e._i} className=\"xicms-widget__item xicms-widget__item--event\">\n <time className=\"xicms-widget__meta\" dateTime={e.start}>{formatEventDate(e.start)}</time>\n <span className=\"xicms-widget__link\">\n {e.url\n ? <a href={e.url} data-xicms-edit={`events.${e._i}.title`}>{e.title}</a>\n : <span data-xicms-edit={`events.${e._i}.title`}>{e.title}</span>}\n </span>\n {e.location && <span className=\"xicms-widget__sub\" data-xicms-edit={`events.${e._i}.location`}>{e.location}</span>}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\nexport const agendaWidget = defineBlock({\n type: \"agendaWidget\",\n schema: AgendaWidgetSchema,\n Render: AgendaWidgetRender,\n label: \"Komende agenda\",\n icon: \"📅\",\n category: \"widget\",\n defaults: () => ({ heading: \"Komende evenementen\", limit: 5, events: [], hidePast: true }),\n});\n\n/* ── Openingstijden (vandaag gemarkeerd) ───────────────────────────────────── */\ntype OpeningW = z.infer<typeof OpeningHoursWidgetSchema>;\nfunction OpeningHoursWidgetRender({ block }: BlockRenderProps<OpeningW>) {\n // \"Vandaag\" client-side bepalen (voorkomt hydration-mismatch t.o.v. de servertijd).\n const [todayIdx, setTodayIdx] = useState<number | null>(null);\n useEffect(() => setTodayIdx(new Date().getDay()), []);\n const rows = block.rows ?? [];\n return (\n <div className=\"xicms-widget xicms-widget--hours\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Openingstijden\"}</h3>\n <table className=\"xicms-widget__hours\">\n <tbody>\n {rows.map((r, i) => {\n // parseDays begrijpt ook bereiken (\"Ma-vr\", \"Maandag t/m vrijdag\") - een woensdag\n // hoort dan net zo goed gemarkeerd als de letterlijk genoemde dagen.\n const isToday =\n block.highlightToday && todayIdx !== null && parseDays(r.day).includes(todayIdx);\n return (\n <tr key={i} className={isToday ? \"xicms-widget__hours-today\" : undefined}>\n <th scope=\"row\" data-xicms-edit={`rows.${i}.day`}>{r.day}</th>\n <td data-xicms-edit={`rows.${i}.hours`}>{r.hours}</td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\nexport const openingHoursWidget = defineBlock({\n type: \"openingHoursWidget\",\n schema: OpeningHoursWidgetSchema,\n Render: OpeningHoursWidgetRender,\n label: \"Openingstijden (compact)\",\n icon: \"🕒\",\n category: \"widget\",\n defaults: () => ({\n heading: \"Openingstijden\",\n highlightToday: true,\n rows: [\n { day: \"Maandag t/m vrijdag\", hours: \"09:00 – 17:00\" },\n { day: \"Zaterdag\", hours: \"10:00 – 16:00\" },\n { day: \"Zondag\", hours: \"Gesloten\" },\n ],\n }),\n});\n\n/* ── Contact (compact) ─────────────────────────────────────────────────────── */\ntype ContactW = z.infer<typeof ContactWidgetSchema>;\nfunction ContactWidgetRender({ block }: BlockRenderProps<ContactW>) {\n if (!block.address && !block.phone && !block.email) return null;\n return (\n <div className=\"xicms-widget xicms-widget--contact\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Contact\"}</h3>\n <ul className=\"xicms-widget__list\">\n {block.address && <li className=\"xicms-widget__item\" data-xicms-edit=\"address\">{block.address}</li>}\n {block.phone && (\n <li className=\"xicms-widget__item\"><a href={`tel:${block.phone.replace(/\\s+/g, \"\")}`} data-xicms-edit=\"phone\">{block.phone}</a></li>\n )}\n {block.email && (\n <li className=\"xicms-widget__item\"><a href={`mailto:${block.email}`} data-xicms-edit=\"email\">{block.email}</a></li>\n )}\n </ul>\n </div>\n );\n}\nexport const contactWidget = defineBlock({\n type: \"contactWidget\",\n schema: ContactWidgetSchema,\n Render: ContactWidgetRender,\n label: \"Contact (compact)\",\n icon: \"📇\",\n category: \"widget\",\n defaults: () => ({ heading: \"Contact\" }),\n});\n\n/* ── Social (compact) ──────────────────────────────────────────────────────── */\ntype SocialW = z.infer<typeof SocialWidgetSchema>;\nfunction SocialWidgetRender({ block }: BlockRenderProps<SocialW>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op href && platform.\n const items = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.href && i.platform);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--social\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <ul className=\"xicms-widget__social\">\n {items.map((it) => (\n <li key={it._i}><a href={it.href} rel=\"noopener noreferrer\" target=\"_blank\" data-xicms-edit={`items.${it._i}.platform`}>{it.platform}</a></li>\n ))}\n </ul>\n </div>\n );\n}\nexport const socialWidget = defineBlock({\n type: \"socialWidget\",\n schema: SocialWidgetSchema,\n Render: SocialWidgetRender,\n label: \"Social (compact)\",\n icon: \"🔗\",\n category: \"widget\",\n defaults: () => ({ heading: \"Volg ons\", items: [{ platform: \"Instagram\", href: \"\" }] }),\n});\n\n/* ── Nieuwsbrief (compact) ─────────────────────────────────────────────────── */\ntype NewsletterW = z.infer<typeof NewsletterWidgetSchema>;\nfunction NewsletterWidgetRender({ block }: BlockRenderProps<NewsletterW>) {\n const [email, setEmail] = useState(\"\");\n return (\n <div className=\"xicms-widget xicms-widget--newsletter\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Nieuwsbrief\"}</h3>\n {block.text && <p className=\"xicms-widget__sub\" data-xicms-edit=\"text\">{block.text}</p>}\n {block.actionUrl ? (\n <form className=\"xicms-widget__form\" action={block.actionUrl} method=\"post\" target=\"_blank\" style={{ display: \"grid\", gap: \"0.6rem\" }}>\n <TextInput label=\"E-mailadres\" isLabelHidden type=\"email\" htmlName=\"EMAIL\" isRequired placeholder=\"je@email.nl\" value={email} onChange={setEmail} />\n <Button label={block.buttonLabel} variant=\"primary\" type=\"submit\">\n <span data-xicms-edit=\"buttonLabel\">{block.buttonLabel}</span>\n </Button>\n </form>\n ) : (\n <Onvoltooid>Deze aanmeldwidget mist nog een aanmeld-URL (tandwiel). Bezoekers zien dit blok niet.</Onvoltooid>\n )}\n </div>\n );\n}\nexport const newsletterWidget = defineBlock({\n type: \"newsletterWidget\",\n schema: NewsletterWidgetSchema,\n Render: NewsletterWidgetRender,\n label: \"Nieuwsbrief (compact)\",\n icon: \"✉️\",\n category: \"widget\",\n defaults: () => ({ heading: \"Blijf op de hoogte\", buttonLabel: \"Aanmelden\" }),\n});\n\n/* ── CTA (compact) ─────────────────────────────────────────────────────────── */\ntype CtaW = z.infer<typeof CtaWidgetSchema>;\nfunction CtaWidgetRender({ block }: BlockRenderProps<CtaW>) {\n if (!block.title && !block.text) return null;\n return (\n <div className=\"xicms-widget xicms-widget--cta\" style={block.backgroundColor ? { background: block.backgroundColor } : undefined}>\n {block.title && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"title\">{block.title}</h3>}\n {block.text && <p className=\"xicms-widget__sub\" data-xicms-edit=\"text\">{block.text}</p>}\n {block.buttonLabel && (\n <Btn label={block.buttonLabel} href={block.buttonHref} variant=\"primary\" size=\"md\" editField=\"buttonLabel\" />\n )}\n </div>\n );\n}\nexport const ctaWidget = defineBlock({\n type: \"ctaWidget\",\n schema: CtaWidgetSchema,\n Render: CtaWidgetRender,\n label: \"Call-to-action (compact)\",\n icon: \"👉\",\n category: \"widget\",\n defaults: () => ({ title: \"Vragen?\", text: \"Neem gerust contact op.\", buttonLabel: \"Contact\", buttonHref: \"#contact\" }),\n});\n\n/* ── Galerij (compacte strip) ──────────────────────────────────────────────── */\ntype GalleryW = z.infer<typeof GalleryWidgetSchema>;\nfunction GalleryWidgetRender({ block }: BlockRenderProps<GalleryW>) {\n const items = (block.items ?? []).filter((i) => i.url);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--gallery\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <div className=\"xicms-widget__grid\" data-cols={block.columns}>\n {items.map((it, i) => (\n // eslint-disable-next-line @next/next/no-img-element\n <img key={i} className=\"xicms-widget__photo\" src={it.url} alt={it.alt ?? \"\"} loading=\"lazy\" />\n ))}\n </div>\n </div>\n );\n}\nexport const galleryWidget = defineBlock({\n type: \"galleryWidget\",\n schema: GalleryWidgetSchema,\n Render: GalleryWidgetRender,\n label: \"Fotostrip (compact)\",\n icon: \"🖼️\",\n category: \"widget\",\n defaults: () => ({ heading: \"Foto's\", columns: 3, items: [] }),\n});\n\n/* ── Review (compact) ──────────────────────────────────────────────────────── */\ntype TestimonialsW = z.infer<typeof TestimonialsWidgetSchema>;\nfunction TestimonialsWidgetRender({ block }: BlockRenderProps<TestimonialsW>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op quote.\n const items = (block.items ?? []).map((t, i) => ({ ...t, _i: i })).filter((t) => t.quote);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--testimonial\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <ul className=\"xicms-widget__list\">\n {items.map((t) => (\n <li key={t._i} className=\"xicms-widget__quote\">\n {typeof t.rating === \"number\" && (\n <span className=\"xicms-widget__stars\" aria-label={`${t.rating} van 5`}>{\"★\".repeat(t.rating)}{\"☆\".repeat(5 - t.rating)}</span>\n )}\n <p>“<span data-xicms-edit={`items.${t._i}.quote`}>{t.quote}</span>”</p>\n {t.author && <span className=\"xicms-widget__meta\">— <span data-xicms-edit={`items.${t._i}.author`}>{t.author}</span></span>}\n </li>\n ))}\n </ul>\n </div>\n );\n}\nexport const testimonialsWidget = defineBlock({\n type: \"testimonialsWidget\",\n schema: TestimonialsWidgetSchema,\n Render: TestimonialsWidgetRender,\n label: \"Review (compact)\",\n icon: \"⭐\",\n category: \"widget\",\n defaults: () => ({ heading: \"Wat klanten zeggen\", items: [{ quote: \"Echt een aanrader!\", author: \"Tevreden klant\", rating: 5 }] }),\n});\n\n/* ── Diensten (compact) ────────────────────────────────────────────────────── */\ntype CardsW = z.infer<typeof CardsWidgetSchema>;\nfunction CardsWidgetRender({ block }: BlockRenderProps<CardsW>) {\n const items = (block.items ?? []).filter((c) => c.title);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--cards\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <ul className=\"xicms-widget__list\">\n {items.map((c, i) => {\n const inner = (\n <>\n {c.icon && <span className=\"xicms-widget__cardicon\" aria-hidden=\"true\">{c.icon}</span>}\n <span>\n <span className=\"xicms-widget__link\">{c.title}</span>\n {c.text && <span className=\"xicms-widget__sub\">{c.text}</span>}\n </span>\n </>\n );\n return (\n <li key={i} className=\"xicms-widget__item xicms-widget__item--card\">\n {c.href ? <a href={c.href} className=\"xicms-widget__cardlink\">{inner}</a> : inner}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\nexport const cardsWidget = defineBlock({\n type: \"cardsWidget\",\n schema: CardsWidgetSchema,\n Render: CardsWidgetRender,\n label: \"Diensten (compact)\",\n icon: \"🧩\",\n category: \"widget\",\n defaults: () => ({ heading: \"Uitgelicht\", items: [{ icon: \"⭐\", title: \"Dienst\", text: \"Korte omschrijving.\" }] }),\n});\n\n/* ── Downloads (compact) ───────────────────────────────────────────────────── */\ntype DownloadsW = z.infer<typeof DownloadsWidgetSchema>;\nfunction DownloadsWidgetRender({ block }: BlockRenderProps<DownloadsW>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd én afgekapt op `limit`.\n const items = (block.items ?? []).map((d, i) => ({ ...d, _i: i })).filter((d) => d.label && d.href).slice(0, block.limit);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--downloads\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Downloads\"}</h3>\n <ul className=\"xicms-widget__list\">\n {items.map((d) => (\n <li key={d._i} className=\"xicms-widget__item\">\n <a className=\"xicms-widget__link\" href={d.href} download>\n <span aria-hidden=\"true\">📄</span> <span data-xicms-edit={`items.${d._i}.label`}>{d.label}</span>\n </a>\n {d.meta && <span className=\"xicms-widget__meta\" data-xicms-edit={`items.${d._i}.meta`}>{d.meta}</span>}\n </li>\n ))}\n </ul>\n </div>\n );\n}\nexport const downloadsWidget = defineBlock({\n type: \"downloadsWidget\",\n schema: DownloadsWidgetSchema,\n Render: DownloadsWidgetRender,\n label: \"Downloads (compact)\",\n icon: \"📥\",\n category: \"widget\",\n defaults: () => ({ heading: \"Downloads\", limit: 5, items: [] }),\n});\n\n/* ── Aftelteller (compact) ─────────────────────────────────────────────────── */\ntype CountdownW = z.infer<typeof CountdownWidgetSchema>;\nfunction CountdownWidgetRender({ block }: BlockRenderProps<CountdownW>) {\n const r = useCountdown(block.target);\n if (!block.target) return null;\n return (\n <div className=\"xicms-widget xicms-widget--countdown\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n {r === null ? (\n <p className=\"xicms-widget__empty\" aria-hidden=\"true\">…</p>\n ) : r.done ? (\n <p className=\"xicms-countdown__expired\" data-xicms-edit=\"expiredText\">{block.expiredText}</p>\n ) : (\n <CountdownDigits r={r} showLabels />\n )}\n </div>\n );\n}\nexport const countdownWidget = defineBlock({\n type: \"countdownWidget\",\n schema: CountdownWidgetSchema,\n Render: CountdownWidgetRender,\n label: \"Aftelteller (compact)\",\n icon: \"⏳\",\n category: \"widget\",\n defaults: () => ({ heading: \"Aftellen\", target: \"\", expiredText: \"Het is zover!\" }),\n});\n\n// Let op: exporteer GEEN aggregaat-array vanuit dit \"use client\"-bestand. Een array die hier\n// wordt opgebouwd is niet server-importeerbaar (de individuele block-exports wél). De volledige\n// lijst staat in blocks/builtins/index.ts.\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Widgets.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { z } from \"zod\";\nimport { Onvoltooid } from \"./lib/Onvoltooid\";\nimport type {\n NewsWidgetSchema,\n AgendaWidgetSchema,\n OpeningHoursWidgetSchema,\n ContactWidgetSchema,\n SocialWidgetSchema,\n NewsletterWidgetSchema,\n CtaWidgetSchema,\n GalleryWidgetSchema,\n TestimonialsWidgetSchema,\n CardsWidgetSchema,\n DownloadsWidgetSchema,\n CountdownWidgetSchema,\n} from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { Btn } from \"../ui\";\nimport { useNews } from \"../../components/News\";\nimport { selectAgendaEvents, formatEventDate } from \"./Agenda\";\nimport { parseDays } from \"./lib/openingHours\";\nimport { useCountdown, CountdownDigits } from \"./Countdown\";\nimport { TextInput } from \"@astryxdesign/core/TextInput\";\nimport { Button } from \"@astryxdesign/core/Button\";\n\n/**\n * Widgets = compacte zijbalk-varianten van de modules. Elke widget hergebruikt de gegevens en\n * logica van de volwaardige versie (zelfde nieuws-collectie, zelfde agenda-selectie, zelfde\n * countdown-hook) en rendert die alleen compacter. Plaats ze in het \"Met zijbalk\"-blok of in een\n * smalle kolom naast je hoofdinhoud.\n */\n\n/* ── Laatste nieuws ────────────────────────────────────────────────────────── */\ntype NewsW = z.infer<typeof NewsWidgetSchema>;\nfunction NewsWidgetRender({ block }: BlockRenderProps<NewsW>) {\n const { articles, loading } = useNews({ max: block.limit });\n const shown = articles.slice(0, block.limit);\n return (\n <div className=\"xicms-widget xicms-widget--news\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Laatste nieuws\"}</h3>\n {loading ? (\n <p className=\"xicms-widget__empty\">Nieuws laden…</p>\n ) : shown.length === 0 ? (\n <p className=\"xicms-widget__empty\">Nog geen nieuws.</p>\n ) : (\n <ul className=\"xicms-widget__list\">\n {shown.map((a) => (\n <li key={a.id} className=\"xicms-widget__item\">\n {block.showImages && a.coverUrl && (\n // eslint-disable-next-line @next/next/no-img-element\n <img className=\"xicms-widget__thumb\" src={a.coverUrl} alt=\"\" loading=\"lazy\" />\n )}\n <div>\n <a className=\"xicms-widget__link\" href={`${block.basePath}/${a.slug}`}>{a.title}</a>\n <span className=\"xicms-widget__meta\">{a.date}</span>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\nexport const newsWidget = defineBlock({\n type: \"newsWidget\",\n Render: NewsWidgetRender,\n label: \"Laatste nieuws\",\n icon: \"🗞️\",\n category: \"widget\",\n defaults: () => ({ heading: \"Laatste nieuws\", limit: 5, basePath: \"/nieuws\", showImages: false }),\n});\n\n/* ── Komende agenda ────────────────────────────────────────────────────────── */\ntype AgendaW = z.infer<typeof AgendaWidgetSchema>;\nfunction AgendaWidgetRender({ block }: BlockRenderProps<AgendaW>) {\n const events = selectAgendaEvents((block.events ?? []).map((e, i) => ({ ...e, _i: i })), { hidePast: block.hidePast, limit: block.limit });\n return (\n <div className=\"xicms-widget xicms-widget--agenda\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Komende evenementen\"}</h3>\n {events.length === 0 ? (\n <p className=\"xicms-widget__empty\">Geen geplande evenementen.</p>\n ) : (\n <ul className=\"xicms-widget__list\">\n {events.map((e) => (\n <li key={e._i} className=\"xicms-widget__item xicms-widget__item--event\">\n <time className=\"xicms-widget__meta\" dateTime={e.start}>{formatEventDate(e.start)}</time>\n <span className=\"xicms-widget__link\">\n {e.url\n ? <a href={e.url} data-xicms-edit={`events.${e._i}.title`}>{e.title}</a>\n : <span data-xicms-edit={`events.${e._i}.title`}>{e.title}</span>}\n </span>\n {e.location && <span className=\"xicms-widget__sub\" data-xicms-edit={`events.${e._i}.location`}>{e.location}</span>}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\nexport const agendaWidget = defineBlock({\n type: \"agendaWidget\",\n Render: AgendaWidgetRender,\n label: \"Komende agenda\",\n icon: \"📅\",\n category: \"widget\",\n defaults: () => ({ heading: \"Komende evenementen\", limit: 5, events: [], hidePast: true }),\n});\n\n/* ── Openingstijden (vandaag gemarkeerd) ───────────────────────────────────── */\ntype OpeningW = z.infer<typeof OpeningHoursWidgetSchema>;\nfunction OpeningHoursWidgetRender({ block }: BlockRenderProps<OpeningW>) {\n // \"Vandaag\" client-side bepalen (voorkomt hydration-mismatch t.o.v. de servertijd).\n const [todayIdx, setTodayIdx] = useState<number | null>(null);\n useEffect(() => setTodayIdx(new Date().getDay()), []);\n const rows = block.rows ?? [];\n return (\n <div className=\"xicms-widget xicms-widget--hours\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Openingstijden\"}</h3>\n <table className=\"xicms-widget__hours\">\n <tbody>\n {rows.map((r, i) => {\n // parseDays begrijpt ook bereiken (\"Ma-vr\", \"Maandag t/m vrijdag\") - een woensdag\n // hoort dan net zo goed gemarkeerd als de letterlijk genoemde dagen.\n const isToday =\n block.highlightToday && todayIdx !== null && parseDays(r.day).includes(todayIdx);\n return (\n <tr key={i} className={isToday ? \"xicms-widget__hours-today\" : undefined}>\n <th scope=\"row\" data-xicms-edit={`rows.${i}.day`}>{r.day}</th>\n <td data-xicms-edit={`rows.${i}.hours`}>{r.hours}</td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\nexport const openingHoursWidget = defineBlock({\n type: \"openingHoursWidget\",\n Render: OpeningHoursWidgetRender,\n label: \"Openingstijden (compact)\",\n icon: \"🕒\",\n category: \"widget\",\n defaults: () => ({\n heading: \"Openingstijden\",\n highlightToday: true,\n rows: [\n { day: \"Maandag t/m vrijdag\", hours: \"09:00 – 17:00\" },\n { day: \"Zaterdag\", hours: \"10:00 – 16:00\" },\n { day: \"Zondag\", hours: \"Gesloten\" },\n ],\n }),\n});\n\n/* ── Contact (compact) ─────────────────────────────────────────────────────── */\ntype ContactW = z.infer<typeof ContactWidgetSchema>;\nfunction ContactWidgetRender({ block }: BlockRenderProps<ContactW>) {\n if (!block.address && !block.phone && !block.email) return null;\n return (\n <div className=\"xicms-widget xicms-widget--contact\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Contact\"}</h3>\n <ul className=\"xicms-widget__list\">\n {block.address && <li className=\"xicms-widget__item\" data-xicms-edit=\"address\">{block.address}</li>}\n {block.phone && (\n <li className=\"xicms-widget__item\"><a href={`tel:${block.phone.replace(/\\s+/g, \"\")}`} data-xicms-edit=\"phone\">{block.phone}</a></li>\n )}\n {block.email && (\n <li className=\"xicms-widget__item\"><a href={`mailto:${block.email}`} data-xicms-edit=\"email\">{block.email}</a></li>\n )}\n </ul>\n </div>\n );\n}\nexport const contactWidget = defineBlock({\n type: \"contactWidget\",\n Render: ContactWidgetRender,\n label: \"Contact (compact)\",\n icon: \"📇\",\n category: \"widget\",\n defaults: () => ({ heading: \"Contact\" }),\n});\n\n/* ── Social (compact) ──────────────────────────────────────────────────────── */\ntype SocialW = z.infer<typeof SocialWidgetSchema>;\nfunction SocialWidgetRender({ block }: BlockRenderProps<SocialW>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op href && platform.\n const items = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.href && i.platform);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--social\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <ul className=\"xicms-widget__social\">\n {items.map((it) => (\n <li key={it._i}><a href={it.href} rel=\"noopener noreferrer\" target=\"_blank\" data-xicms-edit={`items.${it._i}.platform`}>{it.platform}</a></li>\n ))}\n </ul>\n </div>\n );\n}\nexport const socialWidget = defineBlock({\n type: \"socialWidget\",\n Render: SocialWidgetRender,\n label: \"Social (compact)\",\n icon: \"🔗\",\n category: \"widget\",\n defaults: () => ({ heading: \"Volg ons\", items: [{ platform: \"Instagram\", href: \"\" }] }),\n});\n\n/* ── Nieuwsbrief (compact) ─────────────────────────────────────────────────── */\ntype NewsletterW = z.infer<typeof NewsletterWidgetSchema>;\nfunction NewsletterWidgetRender({ block }: BlockRenderProps<NewsletterW>) {\n const [email, setEmail] = useState(\"\");\n return (\n <div className=\"xicms-widget xicms-widget--newsletter\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Nieuwsbrief\"}</h3>\n {block.text && <p className=\"xicms-widget__sub\" data-xicms-edit=\"text\">{block.text}</p>}\n {block.actionUrl ? (\n <form className=\"xicms-widget__form\" action={block.actionUrl} method=\"post\" target=\"_blank\" style={{ display: \"grid\", gap: \"0.6rem\" }}>\n <TextInput label=\"E-mailadres\" isLabelHidden type=\"email\" htmlName=\"EMAIL\" isRequired placeholder=\"je@email.nl\" value={email} onChange={setEmail} />\n <Button label={block.buttonLabel} variant=\"primary\" type=\"submit\">\n <span data-xicms-edit=\"buttonLabel\">{block.buttonLabel}</span>\n </Button>\n </form>\n ) : (\n <Onvoltooid>Deze aanmeldwidget mist nog een aanmeld-URL (tandwiel). Bezoekers zien dit blok niet.</Onvoltooid>\n )}\n </div>\n );\n}\nexport const newsletterWidget = defineBlock({\n type: \"newsletterWidget\",\n Render: NewsletterWidgetRender,\n label: \"Nieuwsbrief (compact)\",\n icon: \"✉️\",\n category: \"widget\",\n defaults: () => ({ heading: \"Blijf op de hoogte\", buttonLabel: \"Aanmelden\" }),\n});\n\n/* ── CTA (compact) ─────────────────────────────────────────────────────────── */\ntype CtaW = z.infer<typeof CtaWidgetSchema>;\nfunction CtaWidgetRender({ block }: BlockRenderProps<CtaW>) {\n if (!block.title && !block.text) return null;\n return (\n <div className=\"xicms-widget xicms-widget--cta\" style={block.backgroundColor ? { background: block.backgroundColor } : undefined}>\n {block.title && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"title\">{block.title}</h3>}\n {block.text && <p className=\"xicms-widget__sub\" data-xicms-edit=\"text\">{block.text}</p>}\n {block.buttonLabel && (\n <Btn label={block.buttonLabel} href={block.buttonHref} variant=\"primary\" size=\"md\" editField=\"buttonLabel\" />\n )}\n </div>\n );\n}\nexport const ctaWidget = defineBlock({\n type: \"ctaWidget\",\n Render: CtaWidgetRender,\n label: \"Call-to-action (compact)\",\n icon: \"👉\",\n category: \"widget\",\n defaults: () => ({ title: \"Vragen?\", text: \"Neem gerust contact op.\", buttonLabel: \"Contact\", buttonHref: \"#contact\" }),\n});\n\n/* ── Galerij (compacte strip) ──────────────────────────────────────────────── */\ntype GalleryW = z.infer<typeof GalleryWidgetSchema>;\nfunction GalleryWidgetRender({ block }: BlockRenderProps<GalleryW>) {\n const items = (block.items ?? []).filter((i) => i.url);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--gallery\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <div className=\"xicms-widget__grid\" data-cols={block.columns}>\n {items.map((it, i) => (\n // eslint-disable-next-line @next/next/no-img-element\n <img key={i} className=\"xicms-widget__photo\" src={it.url} alt={it.alt ?? \"\"} loading=\"lazy\" />\n ))}\n </div>\n </div>\n );\n}\nexport const galleryWidget = defineBlock({\n type: \"galleryWidget\",\n Render: GalleryWidgetRender,\n label: \"Fotostrip (compact)\",\n icon: \"🖼️\",\n category: \"widget\",\n defaults: () => ({ heading: \"Foto's\", columns: 3, items: [] }),\n});\n\n/* ── Review (compact) ──────────────────────────────────────────────────────── */\ntype TestimonialsW = z.infer<typeof TestimonialsWidgetSchema>;\nfunction TestimonialsWidgetRender({ block }: BlockRenderProps<TestimonialsW>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op quote.\n const items = (block.items ?? []).map((t, i) => ({ ...t, _i: i })).filter((t) => t.quote);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--testimonial\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <ul className=\"xicms-widget__list\">\n {items.map((t) => (\n <li key={t._i} className=\"xicms-widget__quote\">\n {typeof t.rating === \"number\" && (\n <span className=\"xicms-widget__stars\" aria-label={`${t.rating} van 5`}>{\"★\".repeat(t.rating)}{\"☆\".repeat(5 - t.rating)}</span>\n )}\n <p>“<span data-xicms-edit={`items.${t._i}.quote`}>{t.quote}</span>”</p>\n {t.author && <span className=\"xicms-widget__meta\">— <span data-xicms-edit={`items.${t._i}.author`}>{t.author}</span></span>}\n </li>\n ))}\n </ul>\n </div>\n );\n}\nexport const testimonialsWidget = defineBlock({\n type: \"testimonialsWidget\",\n Render: TestimonialsWidgetRender,\n label: \"Review (compact)\",\n icon: \"⭐\",\n category: \"widget\",\n defaults: () => ({ heading: \"Wat klanten zeggen\", items: [{ quote: \"Echt een aanrader!\", author: \"Tevreden klant\", rating: 5 }] }),\n});\n\n/* ── Diensten (compact) ────────────────────────────────────────────────────── */\ntype CardsW = z.infer<typeof CardsWidgetSchema>;\nfunction CardsWidgetRender({ block }: BlockRenderProps<CardsW>) {\n const items = (block.items ?? []).filter((c) => c.title);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--cards\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n <ul className=\"xicms-widget__list\">\n {items.map((c, i) => {\n const inner = (\n <>\n {c.icon && <span className=\"xicms-widget__cardicon\" aria-hidden=\"true\">{c.icon}</span>}\n <span>\n <span className=\"xicms-widget__link\">{c.title}</span>\n {c.text && <span className=\"xicms-widget__sub\">{c.text}</span>}\n </span>\n </>\n );\n return (\n <li key={i} className=\"xicms-widget__item xicms-widget__item--card\">\n {c.href ? <a href={c.href} className=\"xicms-widget__cardlink\">{inner}</a> : inner}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\nexport const cardsWidget = defineBlock({\n type: \"cardsWidget\",\n Render: CardsWidgetRender,\n label: \"Diensten (compact)\",\n icon: \"🧩\",\n category: \"widget\",\n defaults: () => ({ heading: \"Uitgelicht\", items: [{ icon: \"⭐\", title: \"Dienst\", text: \"Korte omschrijving.\" }] }),\n});\n\n/* ── Downloads (compact) ───────────────────────────────────────────────────── */\ntype DownloadsW = z.infer<typeof DownloadsWidgetSchema>;\nfunction DownloadsWidgetRender({ block }: BlockRenderProps<DownloadsW>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd én afgekapt op `limit`.\n const items = (block.items ?? []).map((d, i) => ({ ...d, _i: i })).filter((d) => d.label && d.href).slice(0, block.limit);\n if (items.length === 0) return null;\n return (\n <div className=\"xicms-widget xicms-widget--downloads\">\n <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading ?? \"Downloads\"}</h3>\n <ul className=\"xicms-widget__list\">\n {items.map((d) => (\n <li key={d._i} className=\"xicms-widget__item\">\n <a className=\"xicms-widget__link\" href={d.href} download>\n <span aria-hidden=\"true\">📄</span> <span data-xicms-edit={`items.${d._i}.label`}>{d.label}</span>\n </a>\n {d.meta && <span className=\"xicms-widget__meta\" data-xicms-edit={`items.${d._i}.meta`}>{d.meta}</span>}\n </li>\n ))}\n </ul>\n </div>\n );\n}\nexport const downloadsWidget = defineBlock({\n type: \"downloadsWidget\",\n Render: DownloadsWidgetRender,\n label: \"Downloads (compact)\",\n icon: \"📥\",\n category: \"widget\",\n defaults: () => ({ heading: \"Downloads\", limit: 5, items: [] }),\n});\n\n/* ── Aftelteller (compact) ─────────────────────────────────────────────────── */\ntype CountdownW = z.infer<typeof CountdownWidgetSchema>;\nfunction CountdownWidgetRender({ block }: BlockRenderProps<CountdownW>) {\n const r = useCountdown(block.target);\n if (!block.target) return null;\n return (\n <div className=\"xicms-widget xicms-widget--countdown\">\n {block.heading && <h3 className=\"xicms-widget__title\" data-xicms-edit=\"heading\">{block.heading}</h3>}\n {r === null ? (\n <p className=\"xicms-widget__empty\" aria-hidden=\"true\">…</p>\n ) : r.done ? (\n <p className=\"xicms-countdown__expired\" data-xicms-edit=\"expiredText\">{block.expiredText}</p>\n ) : (\n <CountdownDigits r={r} showLabels />\n )}\n </div>\n );\n}\nexport const countdownWidget = defineBlock({\n type: \"countdownWidget\",\n Render: CountdownWidgetRender,\n label: \"Aftelteller (compact)\",\n icon: \"⏳\",\n category: \"widget\",\n defaults: () => ({ heading: \"Aftellen\", target: \"\", expiredText: \"Het is zover!\" }),\n});\n\n// Let op: exporteer GEEN aggregaat-array vanuit dit \"use client\"-bestand. Een array die hier\n// wordt opgebouwd is niet server-importeerbaar (de individuele block-exports wél). De volledige\n// lijst staat in blocks/builtins/index.ts.\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as z from "zod/mini";
3
- import { blockBase } from "../../schema/blocks.js";
3
+ import { blockBase } from "../../schema/blockBase.js";
4
4
  import { fieldsToZod, fieldsDefaults } from "../../schema/fields.js";
5
5
  import { defineBlock } from "../registry.js";
6
6
  import { interpretTemplate, scopeCss } from "./interpret.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/customTemplate/definition.tsx"],"sourcesContent":["/**\n * Bouwt een volwaardige BlockDefinition uit een opgeslagen custom block (5.0). Server-safe:\n * de Render interpreteert de veilige template naar HTML (SSR-vriendelijk, geen hooks); de\n * waarden zijn gevalideerd via `fieldsToZod` — runtime-gedefinieerde blokken krijgen dus\n * dezelfde harde validatie als code-blokken. De editor is de generieke FieldsForm: GenericEditor\n * herkent `customFields` op de definitie (zie GenericEditor.tsx).\n */\nimport * as z from \"zod/mini\";\nimport { blockBase } from \"../../schema/blocks\";\nimport { fieldsToZod, fieldsDefaults, type Field } from \"../../schema/fields\";\nimport type { CustomBlock } from \"../../schema/customBlocks\";\nimport { defineBlock, type BlockDefinition, type BlockRenderProps } from \"../registry\";\nimport { interpretTemplate, scopeCss } from \"./interpret\";\n\nfunction CustomBlockRender(def: CustomBlock) {\n const scopedCss = def.css ? scopeCss(def.css, def.type) : \"\";\n return function Render({ block }: BlockRenderProps<Record<string, unknown>>) {\n const html = interpretTemplate(def.template, block);\n return (\n <div data-xicms-cb={def.type} className={`xicms-custom xicms-${def.type}`}>\n {scopedCss ? <style dangerouslySetInnerHTML={{ __html: scopedCss }} /> : null}\n {/* Template is opslag-tijd gesanitized; interpolaties zijn render-tijd ge-escaped. */}\n <div dangerouslySetInnerHTML={{ __html: html }} />\n </div>\n );\n };\n}\n\n/** Custom block-document → BlockDefinition (voor mergeRegistry, server én client). */\nexport function customBlockToDefinition(def: CustomBlock): BlockDefinition & { customFields: Field[] } {\n // Zod 4 heeft `.merge()` afgeschaft; `z.extend(schema, shape)` is de opvolger.\n const valueSchema = z.extend(z.object({ ...blockBase, _type: z.literal(def.type) }), fieldsToZod(def.fields).shape);\n const definition = defineBlock({\n type: def.type,\n schema: valueSchema,\n Render: CustomBlockRender(def),\n label: def.label,\n icon: def.icon || \"🧩\",\n category: \"content\",\n defaults: () => fieldsDefaults(def.fields),\n }) as BlockDefinition & { customFields: Field[] };\n // De opgeslagen Field-IR meegeven zodat de GenericEditor die direct rendert (incl. showIf/date/\n // repeater-metadata die uit een Zod-schema niet volledig terug te winnen is).\n definition.customFields = def.fields;\n return definition;\n}\n"],"mappings":"AAmBM,SACe,KADf;AAZN,YAAY,OAAO;AACnB,SAAS,iBAAiB;AAC1B,SAAS,aAAa,sBAAkC;AAExD,SAAS,mBAAgE;AACzE,SAAS,mBAAmB,gBAAgB;AAE5C,SAAS,kBAAkB,KAAkB;AAC3C,QAAM,YAAY,IAAI,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI;AAC1D,SAAO,SAAS,OAAO,EAAE,MAAM,GAA8C;AAC3E,UAAM,OAAO,kBAAkB,IAAI,UAAU,KAAK;AAClD,WACE,qBAAC,SAAI,iBAAe,IAAI,MAAM,WAAW,sBAAsB,IAAI,IAAI,IACpE;AAAA,kBAAY,oBAAC,WAAM,yBAAyB,EAAE,QAAQ,UAAU,GAAG,IAAK;AAAA,MAEzE,oBAAC,SAAI,yBAAyB,EAAE,QAAQ,KAAK,GAAG;AAAA,OAClD;AAAA,EAEJ;AACF;AAGO,SAAS,wBAAwB,KAA+D;AAErG,QAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,WAAW,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAC,GAAG,YAAY,IAAI,MAAM,EAAE,KAAK;AAClH,QAAM,aAAa,YAAY;AAAA,IAC7B,MAAM,IAAI;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ,kBAAkB,GAAG;AAAA,IAC7B,OAAO,IAAI;AAAA,IACX,MAAM,IAAI,QAAQ;AAAA,IAClB,UAAU;AAAA,IACV,UAAU,MAAM,eAAe,IAAI,MAAM;AAAA,EAC3C,CAAC;AAGD,aAAW,eAAe,IAAI;AAC9B,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/customTemplate/definition.tsx"],"sourcesContent":["/**\n * Bouwt een volwaardige BlockDefinition uit een opgeslagen custom block (5.0). Server-safe:\n * de Render interpreteert de veilige template naar HTML (SSR-vriendelijk, geen hooks); de\n * waarden zijn gevalideerd via `fieldsToZod` — runtime-gedefinieerde blokken krijgen dus\n * dezelfde harde validatie als code-blokken. De editor is de generieke FieldsForm: GenericEditor\n * herkent `customFields` op de definitie (zie GenericEditor.tsx).\n */\nimport * as z from \"zod/mini\";\nimport { blockBase } from \"../../schema/blockBase\";\nimport { fieldsToZod, fieldsDefaults, type Field } from \"../../schema/fields\";\nimport type { CustomBlock } from \"../../schema/customBlocks\";\nimport { defineBlock, type BlockDefinition, type BlockRenderProps } from \"../registry\";\nimport { interpretTemplate, scopeCss } from \"./interpret\";\n\nfunction CustomBlockRender(def: CustomBlock) {\n const scopedCss = def.css ? scopeCss(def.css, def.type) : \"\";\n return function Render({ block }: BlockRenderProps<Record<string, unknown>>) {\n const html = interpretTemplate(def.template, block);\n return (\n <div data-xicms-cb={def.type} className={`xicms-custom xicms-${def.type}`}>\n {scopedCss ? <style dangerouslySetInnerHTML={{ __html: scopedCss }} /> : null}\n {/* Template is opslag-tijd gesanitized; interpolaties zijn render-tijd ge-escaped. */}\n <div dangerouslySetInnerHTML={{ __html: html }} />\n </div>\n );\n };\n}\n\n/** Custom block-document → BlockDefinition (voor mergeRegistry, server én client). */\nexport function customBlockToDefinition(def: CustomBlock): BlockDefinition & { customFields: Field[] } {\n // Zod 4 heeft `.merge()` afgeschaft; `z.extend(schema, shape)` is de opvolger.\n const valueSchema = z.extend(z.object({ ...blockBase, _type: z.literal(def.type) }), fieldsToZod(def.fields).shape);\n const definition = defineBlock({\n type: def.type,\n schema: valueSchema,\n Render: CustomBlockRender(def),\n label: def.label,\n icon: def.icon || \"🧩\",\n category: \"content\",\n defaults: () => fieldsDefaults(def.fields),\n }) as BlockDefinition & { customFields: Field[] };\n // De opgeslagen Field-IR meegeven zodat de GenericEditor die direct rendert (incl. showIf/date/\n // repeater-metadata die uit een Zod-schema niet volledig terug te winnen is).\n definition.customFields = def.fields;\n return definition;\n}\n"],"mappings":"AAmBM,SACe,KADf;AAZN,YAAY,OAAO;AACnB,SAAS,iBAAiB;AAC1B,SAAS,aAAa,sBAAkC;AAExD,SAAS,mBAAgE;AACzE,SAAS,mBAAmB,gBAAgB;AAE5C,SAAS,kBAAkB,KAAkB;AAC3C,QAAM,YAAY,IAAI,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI;AAC1D,SAAO,SAAS,OAAO,EAAE,MAAM,GAA8C;AAC3E,UAAM,OAAO,kBAAkB,IAAI,UAAU,KAAK;AAClD,WACE,qBAAC,SAAI,iBAAe,IAAI,MAAM,WAAW,sBAAsB,IAAI,IAAI,IACpE;AAAA,kBAAY,oBAAC,WAAM,yBAAyB,EAAE,QAAQ,UAAU,GAAG,IAAK;AAAA,MAEzE,oBAAC,SAAI,yBAAyB,EAAE,QAAQ,KAAK,GAAG;AAAA,OAClD;AAAA,EAEJ;AACF;AAGO,SAAS,wBAAwB,KAA+D;AAErG,QAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,WAAW,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAC,GAAG,YAAY,IAAI,MAAM,EAAE,KAAK;AAClH,QAAM,aAAa,YAAY;AAAA,IAC7B,MAAM,IAAI;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ,kBAAkB,GAAG;AAAA,IAC7B,OAAO,IAAI;AAAA,IACX,MAAM,IAAI,QAAQ;AAAA,IAClB,UAAU;AAAA,IACV,UAAU,MAAM,eAAe,IAAI,MAAM;AAAA,EAC3C,CAAC;AAGD,aAAW,eAAe,IAAI;AAC9B,SAAO;AACT;","names":[]}
@@ -6,18 +6,9 @@
6
6
  * Pure logica + types (geen "use client"): bruikbaar op server en client.
7
7
  */
8
8
  import type { ComponentType } from "react";
9
- import { type $ZodType } from "zod/v4/core";
9
+ import type { $ZodType } from "zod/v4/core";
10
10
  import type { BlockBase } from "../schema/blocks.js";
11
11
  import type { FieldHints } from "../schema/fields.js";
12
- /**
13
- * Het uitvoertype van een blok-schema, onafhankelijk van de Zod-façade.
14
- *
15
- * Core draait op `zod/mini` (zie schema/) omdat de classic-façade niet tree-shaket en ~300 KB aan
16
- * de publieke bundel toevoegde. De grens hieronder is daarom `$ZodType` uit `zod/v4/core` — de
17
- * gedeelde bovengrens van BEIDE smaken. Gevolg: een klantrepo mag zijn eigen blok-schema's zowel
18
- * met `zod` als met `zod/mini` schrijven; `defineBlock` accepteert allebei.
19
- */
20
- type InferBlock<S extends $ZodType> = S["_zod"]["output"];
21
12
  export interface BlockRenderProps<T = unknown> {
22
13
  block: T;
23
14
  siteId: string;
@@ -39,13 +30,20 @@ export interface BlockEditorProps<T = unknown> {
39
30
  export type BlockCategory = "content" | "widget" | "layout";
40
31
  export interface BlockDefinition<S extends $ZodType = $ZodType> {
41
32
  type: string;
42
- schema: S;
43
- Render: ComponentType<BlockRenderProps<InferBlock<S>>>;
33
+ /**
34
+ * Zod-schema van het blok. Voor de INGEBOUWDE blokken is dit veld leeg en wordt het schema
35
+ * lazy geregistreerd via `blocks/builtinSchemas` (7.13): de schema's zijn editor-kennis, en
36
+ * mét dit veld reisden álle schema's plus de zod-runtime (±73 KB gzip) mee naar elke publieke
37
+ * bezoeker die nooit iets bewerkt. Klant-blokken (defineBlock in een klantrepo) mogen hun
38
+ * schema gewoon hier blijven meegeven — dat is klein en houdt de API eenvoudig.
39
+ */
40
+ schema?: S;
41
+ Render: ComponentType<BlockRenderProps<any>>;
44
42
  /**
45
43
  * Admin-editor (lazy/client). Optioneel; ontbreekt = automatische schema-editor (GenericEditor):
46
44
  * die genereert een formulier uit het Zod-schema, dus een blok zonder eigen editor blijft bewerkbaar.
47
45
  */
48
- Editor?: ComponentType<BlockEditorProps<InferBlock<S>>>;
46
+ Editor?: ComponentType<BlockEditorProps<any>>;
49
47
  label: string;
50
48
  icon?: string;
51
49
  /** Groep in het toevoeg-menu (content/widget/layout). Standaard "content". */
@@ -89,5 +87,18 @@ export declare function mergeRegistry(base: BlockRegistry, extra: BlockDefinitio
89
87
  * per-type gevalideerd. Hun schema's valideren kinderen bewust alleen als `BlockBaseSchema`
90
88
  * (passthrough) zodat klant-blokken erin mogen; de strikte controle gebeurt hier.
91
89
  */
90
+ /**
91
+ * De zod-parser wordt hier INGEPLUGD in plaats van geïmporteerd: een statische `safeParse`-import
92
+ * zou de zod-runtime alsnog in elke publieke bundel trekken, ook nu de schema's zelf lazy zijn.
93
+ * `blocks/builtinSchemas` (geladen door de admin/editor-chunks) zet hem; klant-blokken die hun
94
+ * schema meegeven krijgen daarmee ook pas volledige client-validatie zodra de editor laadt —
95
+ * publiek geldt voor hen dezelfde lichte vormcheck als voor de ingebouwde blokken.
96
+ */
97
+ type SchemaParser = (schema: $ZodType, raw: unknown) => {
98
+ success: boolean;
99
+ data?: unknown;
100
+ issues?: unknown;
101
+ };
102
+ export declare function setSchemaParser(fn: SchemaParser): void;
92
103
  export declare function validateBlocks(blocks: unknown[] | undefined, registry: BlockRegistry): BlockBase[];
93
104
  export {};
@@ -1,4 +1,3 @@
1
- import { safeParse } from "zod/v4/core";
2
1
  function defineBlock(def) {
3
2
  return def;
4
3
  }
@@ -17,6 +16,10 @@ function mergeRegistry(base, extra) {
17
16
  for (const d of extra) m.set(d.type, d);
18
17
  return m;
19
18
  }
19
+ let schemaParser = null;
20
+ function setSchemaParser(fn) {
21
+ schemaParser = fn;
22
+ }
20
23
  function validateBlocks(blocks, registry) {
21
24
  const out = [];
22
25
  for (const raw of blocks ?? []) {
@@ -26,9 +29,16 @@ function validateBlocks(blocks, registry) {
26
29
  warn(`onbekend block-type "${String(type)}" overgeslagen (geen registratie).`);
27
30
  continue;
28
31
  }
29
- const res = safeParse(def.schema, raw);
30
- if (res.success) out.push(validateNestedBlocks(res.data, registry));
31
- else warn(`block "${type}" voldoet niet aan zijn schema en is overgeslagen.`, res.error.issues);
32
+ const parser = def.schema ? schemaParser : null;
33
+ if (def.schema && parser) {
34
+ const res = parser(def.schema, raw);
35
+ if (res.success) out.push(validateNestedBlocks(res.data, registry));
36
+ else warn(`block "${type}" voldoet niet aan zijn schema en is overgeslagen.`, res.issues);
37
+ } else if (raw && typeof raw === "object" && typeof raw._type === "string") {
38
+ out.push(validateNestedBlocks(raw, registry));
39
+ } else {
40
+ warn(`block "${String(type)}" heeft geen geldige vorm en is overgeslagen.`);
41
+ }
32
42
  }
33
43
  return out;
34
44
  }
@@ -67,6 +77,7 @@ export {
67
77
  createRegistry,
68
78
  defineBlock,
69
79
  mergeRegistry,
80
+ setSchemaParser,
70
81
  validateBlocks
71
82
  };
72
83
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/blocks/registry.ts"],"sourcesContent":["/**\n * Block-registry: koppelt een `_type` aan zijn Zod-schema, een (server-safe) Render-component\n * en een optionele (client/admin) Editor. Klant-blokken worden hier ingebracht via `defineBlock`\n * — zo blijft de core klant-onafhankelijk: de klant levert de render-component, core de structuur.\n *\n * Pure logica + types (geen \"use client\"): bruikbaar op server en client.\n */\nimport type { ComponentType } from \"react\";\nimport { safeParse, type $ZodType } from \"zod/v4/core\";\nimport type { BlockBase } from \"../schema/blocks\";\nimport type { FieldHints } from \"../schema/fields\";\n\n/**\n * Het uitvoertype van een blok-schema, onafhankelijk van de Zod-façade.\n *\n * Core draait op `zod/mini` (zie schema/) omdat de classic-façade niet tree-shaket en ~300 KB aan\n * de publieke bundel toevoegde. De grens hieronder is daarom `$ZodType` uit `zod/v4/core` — de\n * gedeelde bovengrens van BEIDE smaken. Gevolg: een klantrepo mag zijn eigen blok-schema's zowel\n * met `zod` als met `zod/mini` schrijven; `defineBlock` accepteert allebei.\n */\ntype InferBlock<S extends $ZodType> = S[\"_zod\"][\"output\"];\n\nexport interface BlockRenderProps<T = unknown> {\n block: T;\n siteId: string;\n /** \"view\" = publiek; \"preview\" = draft-weergave in de editor. */\n mode?: \"view\" | \"preview\";\n}\n\nexport interface BlockEditorProps<T = unknown> {\n block: T;\n siteId: string;\n onChange: (next: T) => void;\n}\n\n/**\n * Indeling van een block in het toevoeg-menu:\n * - `content` (default): een gewoon blok dat je op de pagina plaatst.\n * - `widget`: een compacte variant die je naast een blok (in een zijbalk/kolom) zet — haalt z'n\n * gegevens uit dezelfde module als de volwaardige versie (bv. \"laatste nieuws\", \"komende agenda\").\n * - `layout`: een indelingsblok dat andere blokken bevat (kolommen, zijbalk, tabs).\n */\nexport type BlockCategory = \"content\" | \"widget\" | \"layout\";\n\nexport interface BlockDefinition<S extends $ZodType = $ZodType> {\n type: string;\n schema: S;\n Render: ComponentType<BlockRenderProps<InferBlock<S>>>;\n /**\n * Admin-editor (lazy/client). Optioneel; ontbreekt = automatische schema-editor (GenericEditor):\n * die genereert een formulier uit het Zod-schema, dus een blok zonder eigen editor blijft bewerkbaar.\n */\n Editor?: ComponentType<BlockEditorProps<InferBlock<S>>>;\n label: string;\n icon?: string;\n /** Groep in het toevoeg-menu (content/widget/layout). Standaard \"content\". */\n category?: BlockCategory;\n /** Defaultwaarden voor een nieuw block (zonder _key/_type). */\n defaults?: () => Record<string, unknown>;\n /**\n * Optionele hints voor het automatisch gegenereerde formulier (GenericEditor 2.0, 5.0):\n * per (dotted) veldsleutel een verfijning van de uit het Zod-schema afgeleide Field-IR —\n * bv. `{ \"items.date\": { type: \"date\" }, cta: { showIf: { field: \"showCta\" } } }`.\n * Zie `introspectToFields` (admin/lib/zodFields.ts) en `schema/fields.ts`.\n */\n fields?: FieldHints;\n}\n\n/**\n * Registreert een block-type.\n *\n * UPDATE-VEILIGHEID: voeg in een nieuwe versie nooit een VERPLICHT veld toe aan een bestaand\n * block-schema zonder `.default()` of `.optional()`. Anders faalt validatie op al opgeslagen\n * content en wordt die overgeslagen. Met defaults/optioneel blijft oude content gewoon werken.\n */\nexport function defineBlock<S extends $ZodType>(def: BlockDefinition<S>): BlockDefinition {\n // Type-erased return: het schema-type wordt alleen gebruikt om Render/Editor-props te\n // inferreren bij definitie. Daarna behandelen we blokken generiek in de registry.\n return def as unknown as BlockDefinition;\n}\n\nexport type BlockRegistry = Map<string, BlockDefinition>;\n\n/**\n * Bouwt de opties voor het \"blok toevoegen\"-menu, gegroepeerd op categorie: eerst gewone blokken,\n * dan indeling (kolommen/zijbalk/tabs), dan widgets. Widgets/indeling krijgen een suffix zodat de\n * groep duidelijk is in een platte keuzelijst.\n */\nexport function buildBlockAddOptions(defs: Iterable<BlockDefinition>): { value: string; label: string }[] {\n const order: Record<BlockCategory, number> = { content: 0, layout: 1, widget: 2 };\n const suffix: Record<BlockCategory, string> = { content: \"\", layout: \" · indeling\", widget: \" · widget\" };\n return [...defs]\n .map((d) => ({ def: d, cat: d.category ?? \"content\" }))\n .sort((a, b) => order[a.cat] - order[b.cat])\n .map(({ def, cat }) => ({ value: def.type, label: `${def.icon ?? \"▫\"} ${def.label}${suffix[cat]}` }));\n}\n\nexport function createRegistry(defs: BlockDefinition[]): BlockRegistry {\n const m: BlockRegistry = new Map();\n for (const d of defs) m.set(d.type, d);\n return m;\n}\n\nexport function mergeRegistry(base: BlockRegistry, extra: BlockDefinition[]): BlockRegistry {\n const m: BlockRegistry = new Map(base);\n for (const d of extra) m.set(d.type, d);\n return m;\n}\n\n/**\n * Strikte per-type validatie van een blocks-array tegen de registry. Onbekende types en\n * blocks die hun schema niet halen worden weggelaten (defensief — nooit half-rendered content).\n * Gebruikt bij publiceren (server) en vóór renderen.\n *\n * Recursief: geneste blokken in de indelingsblokken (kolommen, \"Met zijbalk\", tabs) worden óók\n * per-type gevalideerd. Hun schema's valideren kinderen bewust alleen als `BlockBaseSchema`\n * (passthrough) zodat klant-blokken erin mogen; de strikte controle gebeurt hier.\n */\nexport function validateBlocks(blocks: unknown[] | undefined, registry: BlockRegistry): BlockBase[] {\n const out: BlockBase[] = [];\n for (const raw of blocks ?? []) {\n const type = (raw as { _type?: unknown } | null)?._type;\n const def = typeof type === \"string\" ? registry.get(type) : undefined;\n if (!def) {\n warn(`onbekend block-type \"${String(type)}\" overgeslagen (geen registratie).`);\n continue;\n }\n const res = safeParse(def.schema, raw);\n if (res.success) out.push(validateNestedBlocks(res.data as BlockBase, registry));\n else warn(`block \"${type}\" voldoet niet aan zijn schema en is overgeslagen.`, res.error.issues);\n }\n return out;\n}\n\n/** Valideert de geneste blok-lijsten van de bekende indelingsvormen (immutable). */\nfunction validateNestedBlocks(block: BlockBase, registry: BlockRegistry): BlockBase {\n const b = block as BlockBase & Record<string, unknown>;\n if (b._type === \"columns\" && Array.isArray(b.columns)) {\n return {\n ...b,\n columns: b.columns.map((col) =>\n col && typeof col === \"object\" && Array.isArray((col as { blocks?: unknown }).blocks)\n ? { ...col, blocks: validateBlocks((col as { blocks: unknown[] }).blocks, registry) }\n : col,\n ),\n };\n }\n if (b._type === \"withSidebar\" && (Array.isArray(b.main) || Array.isArray(b.aside))) {\n return {\n ...b,\n main: Array.isArray(b.main) ? validateBlocks(b.main, registry) : b.main,\n aside: Array.isArray(b.aside) ? validateBlocks(b.aside, registry) : b.aside,\n };\n }\n if (b._type === \"tabs\" && Array.isArray(b.tabs)) {\n return {\n ...b,\n tabs: b.tabs.map((tab) =>\n tab && typeof tab === \"object\" && Array.isArray((tab as { blocks?: unknown }).blocks)\n ? { ...tab, blocks: validateBlocks((tab as { blocks: unknown[] }).blocks, registry) }\n : tab,\n ),\n };\n }\n return block;\n}\n\nfunction warn(msg: string, detail?: unknown): void {\n // Geen stil verlies: log waarom een block niet rendert (helpt bij schema-wijzigingen).\n if (typeof console !== \"undefined\") console.warn(`[xicms] ${msg}`, detail ?? \"\");\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/blocks/registry.ts"],"sourcesContent":["/**\n * Block-registry: koppelt een `_type` aan zijn Zod-schema, een (server-safe) Render-component\n * en een optionele (client/admin) Editor. Klant-blokken worden hier ingebracht via `defineBlock`\n * — zo blijft de core klant-onafhankelijk: de klant levert de render-component, core de structuur.\n *\n * Pure logica + types (geen \"use client\"): bruikbaar op server en client.\n */\nimport type { ComponentType } from \"react\";\nimport type { $ZodType } from \"zod/v4/core\";\nimport type { BlockBase } from \"../schema/blocks\";\nimport type { FieldHints } from \"../schema/fields\";\n\n/**\n * Het uitvoertype van een blok-schema, onafhankelijk van de Zod-façade.\n *\n * Core draait op `zod/mini` (zie schema/) omdat de classic-façade niet tree-shaket en ~300 KB aan\n * de publieke bundel toevoegde. De grens hieronder is daarom `$ZodType` uit `zod/v4/core` — de\n * gedeelde bovengrens van BEIDE smaken. Gevolg: een klantrepo mag zijn eigen blok-schema's zowel\n * met `zod` als met `zod/mini` schrijven; `defineBlock` accepteert allebei.\n */\ntype InferBlock<S extends $ZodType> = S[\"_zod\"][\"output\"];\n\nexport interface BlockRenderProps<T = unknown> {\n block: T;\n siteId: string;\n /** \"view\" = publiek; \"preview\" = draft-weergave in de editor. */\n mode?: \"view\" | \"preview\";\n}\n\nexport interface BlockEditorProps<T = unknown> {\n block: T;\n siteId: string;\n onChange: (next: T) => void;\n}\n\n/**\n * Indeling van een block in het toevoeg-menu:\n * - `content` (default): een gewoon blok dat je op de pagina plaatst.\n * - `widget`: een compacte variant die je naast een blok (in een zijbalk/kolom) zet — haalt z'n\n * gegevens uit dezelfde module als de volwaardige versie (bv. \"laatste nieuws\", \"komende agenda\").\n * - `layout`: een indelingsblok dat andere blokken bevat (kolommen, zijbalk, tabs).\n */\nexport type BlockCategory = \"content\" | \"widget\" | \"layout\";\n\nexport interface BlockDefinition<S extends $ZodType = $ZodType> {\n type: string;\n /**\n * Zod-schema van het blok. Voor de INGEBOUWDE blokken is dit veld leeg en wordt het schema\n * lazy geregistreerd via `blocks/builtinSchemas` (7.13): de schema's zijn editor-kennis, en\n * mét dit veld reisden álle schema's plus de zod-runtime (±73 KB gzip) mee naar elke publieke\n * bezoeker die nooit iets bewerkt. Klant-blokken (defineBlock in een klantrepo) mogen hun\n * schema gewoon hier blijven meegeven — dat is klein en houdt de API eenvoudig.\n */\n schema?: S;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- registry-grens: het blok\n // typeert zijn eigen props; hier is de data per definitie \"wat de database gaf\".\n Render: ComponentType<BlockRenderProps<any>>;\n /**\n * Admin-editor (lazy/client). Optioneel; ontbreekt = automatische schema-editor (GenericEditor):\n * die genereert een formulier uit het Zod-schema, dus een blok zonder eigen editor blijft bewerkbaar.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- zie Render.\n Editor?: ComponentType<BlockEditorProps<any>>;\n label: string;\n icon?: string;\n /** Groep in het toevoeg-menu (content/widget/layout). Standaard \"content\". */\n category?: BlockCategory;\n /** Defaultwaarden voor een nieuw block (zonder _key/_type). */\n defaults?: () => Record<string, unknown>;\n /**\n * Optionele hints voor het automatisch gegenereerde formulier (GenericEditor 2.0, 5.0):\n * per (dotted) veldsleutel een verfijning van de uit het Zod-schema afgeleide Field-IR —\n * bv. `{ \"items.date\": { type: \"date\" }, cta: { showIf: { field: \"showCta\" } } }`.\n * Zie `introspectToFields` (admin/lib/zodFields.ts) en `schema/fields.ts`.\n */\n fields?: FieldHints;\n}\n\n/**\n * Registreert een block-type.\n *\n * UPDATE-VEILIGHEID: voeg in een nieuwe versie nooit een VERPLICHT veld toe aan een bestaand\n * block-schema zonder `.default()` of `.optional()`. Anders faalt validatie op al opgeslagen\n * content en wordt die overgeslagen. Met defaults/optioneel blijft oude content gewoon werken.\n */\nexport function defineBlock<S extends $ZodType>(def: BlockDefinition<S>): BlockDefinition {\n // Type-erased return: het schema-type wordt alleen gebruikt om Render/Editor-props te\n // inferreren bij definitie. Daarna behandelen we blokken generiek in de registry.\n return def as unknown as BlockDefinition;\n}\n\nexport type BlockRegistry = Map<string, BlockDefinition>;\n\n/**\n * Bouwt de opties voor het \"blok toevoegen\"-menu, gegroepeerd op categorie: eerst gewone blokken,\n * dan indeling (kolommen/zijbalk/tabs), dan widgets. Widgets/indeling krijgen een suffix zodat de\n * groep duidelijk is in een platte keuzelijst.\n */\nexport function buildBlockAddOptions(defs: Iterable<BlockDefinition>): { value: string; label: string }[] {\n const order: Record<BlockCategory, number> = { content: 0, layout: 1, widget: 2 };\n const suffix: Record<BlockCategory, string> = { content: \"\", layout: \" · indeling\", widget: \" · widget\" };\n return [...defs]\n .map((d) => ({ def: d, cat: d.category ?? \"content\" }))\n .sort((a, b) => order[a.cat] - order[b.cat])\n .map(({ def, cat }) => ({ value: def.type, label: `${def.icon ?? \"▫\"} ${def.label}${suffix[cat]}` }));\n}\n\nexport function createRegistry(defs: BlockDefinition[]): BlockRegistry {\n const m: BlockRegistry = new Map();\n for (const d of defs) m.set(d.type, d);\n return m;\n}\n\nexport function mergeRegistry(base: BlockRegistry, extra: BlockDefinition[]): BlockRegistry {\n const m: BlockRegistry = new Map(base);\n for (const d of extra) m.set(d.type, d);\n return m;\n}\n\n/**\n * Strikte per-type validatie van een blocks-array tegen de registry. Onbekende types en\n * blocks die hun schema niet halen worden weggelaten (defensief — nooit half-rendered content).\n * Gebruikt bij publiceren (server) en vóór renderen.\n *\n * Recursief: geneste blokken in de indelingsblokken (kolommen, \"Met zijbalk\", tabs) worden óók\n * per-type gevalideerd. Hun schema's valideren kinderen bewust alleen als `BlockBaseSchema`\n * (passthrough) zodat klant-blokken erin mogen; de strikte controle gebeurt hier.\n */\n/**\n * De zod-parser wordt hier INGEPLUGD in plaats van geïmporteerd: een statische `safeParse`-import\n * zou de zod-runtime alsnog in elke publieke bundel trekken, ook nu de schema's zelf lazy zijn.\n * `blocks/builtinSchemas` (geladen door de admin/editor-chunks) zet hem; klant-blokken die hun\n * schema meegeven krijgen daarmee ook pas volledige client-validatie zodra de editor laadt —\n * publiek geldt voor hen dezelfde lichte vormcheck als voor de ingebouwde blokken.\n */\ntype SchemaParser = (schema: $ZodType, raw: unknown) => { success: boolean; data?: unknown; issues?: unknown };\nlet schemaParser: SchemaParser | null = null;\nexport function setSchemaParser(fn: SchemaParser): void {\n schemaParser = fn;\n}\n\nexport function validateBlocks(blocks: unknown[] | undefined, registry: BlockRegistry): BlockBase[] {\n const out: BlockBase[] = [];\n for (const raw of blocks ?? []) {\n const type = (raw as { _type?: unknown } | null)?._type;\n const def = typeof type === \"string\" ? registry.get(type) : undefined;\n if (!def) {\n warn(`onbekend block-type \"${String(type)}\" overgeslagen (geen registratie).`);\n continue;\n }\n /**\n * Twee niveaus van controle, afhankelijk van wat er geladen is:\n * - Schema + parser aanwezig (editor/admin, of een klant-blok dat zijn schema meedraagt):\n * volledige zod-validatie, zoals altijd.\n * - Geen schema (publieke render van ingebouwde blokken): een lichte vormcheck volstaat —\n * de inhoudelijke garantie is dan al geleverd door de publiceer-pijplijn (functions\n * saniteert en valideert bij publishPage). Zo blijft zod uit de bezoekersbundel.\n */\n const parser = def.schema ? schemaParser : null;\n if (def.schema && parser) {\n const res = parser(def.schema, raw);\n if (res.success) out.push(validateNestedBlocks(res.data as BlockBase, registry));\n else warn(`block \"${type}\" voldoet niet aan zijn schema en is overgeslagen.`, res.issues);\n } else if (raw && typeof raw === \"object\" && typeof (raw as { _type?: unknown })._type === \"string\") {\n out.push(validateNestedBlocks(raw as BlockBase, registry));\n } else {\n warn(`block \"${String(type)}\" heeft geen geldige vorm en is overgeslagen.`);\n }\n }\n return out;\n}\n\n/** Valideert de geneste blok-lijsten van de bekende indelingsvormen (immutable). */\nfunction validateNestedBlocks(block: BlockBase, registry: BlockRegistry): BlockBase {\n const b = block as BlockBase & Record<string, unknown>;\n if (b._type === \"columns\" && Array.isArray(b.columns)) {\n return {\n ...b,\n columns: b.columns.map((col) =>\n col && typeof col === \"object\" && Array.isArray((col as { blocks?: unknown }).blocks)\n ? { ...col, blocks: validateBlocks((col as { blocks: unknown[] }).blocks, registry) }\n : col,\n ),\n };\n }\n if (b._type === \"withSidebar\" && (Array.isArray(b.main) || Array.isArray(b.aside))) {\n return {\n ...b,\n main: Array.isArray(b.main) ? validateBlocks(b.main, registry) : b.main,\n aside: Array.isArray(b.aside) ? validateBlocks(b.aside, registry) : b.aside,\n };\n }\n if (b._type === \"tabs\" && Array.isArray(b.tabs)) {\n return {\n ...b,\n tabs: b.tabs.map((tab) =>\n tab && typeof tab === \"object\" && Array.isArray((tab as { blocks?: unknown }).blocks)\n ? { ...tab, blocks: validateBlocks((tab as { blocks: unknown[] }).blocks, registry) }\n : tab,\n ),\n };\n }\n return block;\n}\n\nfunction warn(msg: string, detail?: unknown): void {\n // Geen stil verlies: log waarom een block niet rendert (helpt bij schema-wijzigingen).\n if (typeof console !== \"undefined\") console.warn(`[xicms] ${msg}`, detail ?? \"\");\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { createContext, useContext } from "react";
4
+ import { useBlockData } from "../blocks/context.js";
4
5
  const ImageContext = createContext(null);
5
6
  function ImageProvider({ component, children }) {
6
7
  return /* @__PURE__ */ jsx(ImageContext.Provider, { value: component ?? null, children });
@@ -18,7 +19,29 @@ function InlineImageEditProvider({
18
19
  function Img({ src, alt, width, height, sizes, priority, className, style, editField, editFill }) {
19
20
  const ImageComponent = useImageComponent();
20
21
  const onReplace = useContext(InlineImageEditContext);
21
- const imageEl = ImageComponent ? /* @__PURE__ */ jsx(
22
+ const mediaSeed = useBlockData("media");
23
+ const eigenLadder = mediaSeed?.[src];
24
+ const ladder = eigenLadder?.variants?.sizes ?? [];
25
+ const webp = eigenLadder?.variants?.webp;
26
+ const imageEl = ladder.length > 0 ? /* @__PURE__ */ jsxs("picture", { children: [
27
+ webp && /* @__PURE__ */ jsx("source", { type: "image/webp", srcSet: `${webp.url} ${webp.width}w`, sizes }),
28
+ /* @__PURE__ */ jsx(
29
+ "img",
30
+ {
31
+ src,
32
+ srcSet: ladder.map((v) => `${v.url} ${v.width}w`).join(", "),
33
+ sizes,
34
+ alt,
35
+ width: eigenLadder?.width ?? width,
36
+ height: eigenLadder?.height ?? height,
37
+ loading: priority ? "eager" : "lazy",
38
+ fetchPriority: priority ? "high" : void 0,
39
+ decoding: "async",
40
+ className,
41
+ style
42
+ }
43
+ )
44
+ ] }) : ImageComponent ? /* @__PURE__ */ jsx(
22
45
  ImageComponent,
23
46
  {
24
47
  src,
@@ -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\";\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 const imageEl = 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":";AAgBS,SAsGiB,UAtGjB,KAsGiB,YAtGjB;AAdT,SAAS,eAAe,kBAAwE;AAWhG,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;AACnD,QAAM,UAAU,iBACd;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 */\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":[]}