@xiwebdesign/xicms-core 7.31.0 → 8.16.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 (482) hide show
  1. package/dist/XiCMSProvider.js +23 -7
  2. package/dist/XiCMSProvider.js.map +1 -1
  3. package/dist/admin/AdminApp.js +175 -93
  4. package/dist/admin/AdminApp.js.map +1 -1
  5. package/dist/admin/AdminHelp.d.ts +1 -1
  6. package/dist/admin/AdminHelp.js +44 -36
  7. package/dist/admin/AdminHelp.js.map +1 -1
  8. package/dist/admin/CoreVersion.d.ts +6 -3
  9. package/dist/admin/CoreVersion.js +60 -24
  10. package/dist/admin/CoreVersion.js.map +1 -1
  11. package/dist/admin/NewsEditor.d.ts +7 -0
  12. package/dist/admin/NewsEditor.js +117 -75
  13. package/dist/admin/NewsEditor.js.map +1 -1
  14. package/dist/admin/SuperadminPanel.js +1 -1
  15. package/dist/admin/SuperadminPanel.js.map +1 -1
  16. package/dist/admin/ZoekPalet.d.ts +15 -0
  17. package/dist/admin/ZoekPalet.js +123 -0
  18. package/dist/admin/ZoekPalet.js.map +1 -0
  19. package/dist/admin/blockEditors/GenericEditor.js +36 -18
  20. package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
  21. package/dist/admin/blockEditors/StyleEditor.js +37 -3
  22. package/dist/admin/blockEditors/StyleEditor.js.map +1 -1
  23. package/dist/admin/blockEditors/SymbolRefEditor.js +44 -2
  24. package/dist/admin/blockEditors/SymbolRefEditor.js.map +1 -1
  25. package/dist/admin/blockEditors/index.js +154 -73
  26. package/dist/admin/blockEditors/index.js.map +1 -1
  27. package/dist/admin/customBlocks/CustomBlocksPanel.js +18 -7
  28. package/dist/admin/customBlocks/CustomBlocksPanel.js.map +1 -1
  29. package/dist/admin/dashboard/DashboardPanel.js +26 -16
  30. package/dist/admin/dashboard/DashboardPanel.js.map +1 -1
  31. package/dist/admin/editors/RichTextEditor.d.ts +4 -3
  32. package/dist/admin/editors/RichTextEditor.js +19 -60
  33. package/dist/admin/editors/RichTextEditor.js.map +1 -1
  34. package/dist/admin/editors/fontSizeExtension.js +7 -1
  35. package/dist/admin/editors/fontSizeExtension.js.map +1 -1
  36. package/dist/admin/editors/slashMenu.d.ts +18 -0
  37. package/dist/admin/editors/slashMenu.js +34 -0
  38. package/dist/admin/editors/slashMenu.js.map +1 -0
  39. package/dist/admin/editors/useRijkeTekst.d.ts +48 -0
  40. package/dist/admin/editors/useRijkeTekst.js +310 -0
  41. package/dist/admin/editors/useRijkeTekst.js.map +1 -0
  42. package/dist/admin/forms/FormsPanel.d.ts +11 -2
  43. package/dist/admin/forms/FormsPanel.js +218 -65
  44. package/dist/admin/forms/FormsPanel.js.map +1 -1
  45. package/dist/admin/lib/blokInfo.d.ts +17 -0
  46. package/dist/admin/lib/blokInfo.js +80 -0
  47. package/dist/admin/lib/blokInfo.js.map +1 -0
  48. package/dist/admin/lib/foutNaarMens.d.ts +1 -0
  49. package/dist/admin/lib/foutNaarMens.js +22 -0
  50. package/dist/admin/lib/foutNaarMens.js.map +1 -0
  51. package/dist/admin/lib/gewisteVelden.d.ts +13 -0
  52. package/dist/admin/lib/gewisteVelden.js +31 -0
  53. package/dist/admin/lib/gewisteVelden.js.map +1 -0
  54. package/dist/admin/lib/heroHulp.d.ts +11 -0
  55. package/dist/admin/lib/heroHulp.js +18 -0
  56. package/dist/admin/lib/heroHulp.js.map +1 -0
  57. package/dist/admin/lib/kleur.js +2 -3
  58. package/dist/admin/lib/kleur.js.map +1 -1
  59. package/dist/admin/lib/leesbaarheid.d.ts +34 -0
  60. package/dist/admin/lib/leesbaarheid.js +82 -0
  61. package/dist/admin/lib/leesbaarheid.js.map +1 -0
  62. package/dist/admin/lib/lengteAdvies.d.ts +1 -0
  63. package/dist/admin/lib/lengteAdvies.js +16 -0
  64. package/dist/admin/lib/lengteAdvies.js.map +1 -0
  65. package/dist/admin/lib/mediaLijst.d.ts +26 -0
  66. package/dist/admin/lib/mediaLijst.js +42 -0
  67. package/dist/admin/lib/mediaLijst.js.map +1 -0
  68. package/dist/admin/lib/mediaUsage.d.ts +12 -1
  69. package/dist/admin/lib/mediaUsage.js +43 -33
  70. package/dist/admin/lib/mediaUsage.js.map +1 -1
  71. package/dist/admin/lib/menuHulp.d.ts +16 -0
  72. package/dist/admin/lib/menuHulp.js +41 -0
  73. package/dist/admin/lib/menuHulp.js.map +1 -0
  74. package/dist/admin/lib/onopgeslagen.d.ts +10 -0
  75. package/dist/admin/lib/onopgeslagen.js +35 -0
  76. package/dist/admin/lib/onopgeslagen.js.map +1 -0
  77. package/dist/admin/lib/pagesClient.d.ts +55 -2
  78. package/dist/admin/lib/pagesClient.js +126 -28
  79. package/dist/admin/lib/pagesClient.js.map +1 -1
  80. package/dist/admin/lib/paginaGebruik.d.ts +56 -0
  81. package/dist/admin/lib/paginaGebruik.js +109 -0
  82. package/dist/admin/lib/paginaGebruik.js.map +1 -0
  83. package/dist/admin/lib/publicatieCheck.d.ts +25 -0
  84. package/dist/admin/lib/publicatieCheck.js +114 -0
  85. package/dist/admin/lib/publicatieCheck.js.map +1 -0
  86. package/dist/admin/lib/scrollNaarDetail.d.ts +6 -0
  87. package/dist/admin/lib/scrollNaarDetail.js +20 -0
  88. package/dist/admin/lib/scrollNaarDetail.js.map +1 -0
  89. package/dist/admin/lib/symbolsClient.d.ts +10 -0
  90. package/dist/admin/lib/symbolsClient.js +14 -1
  91. package/dist/admin/lib/symbolsClient.js.map +1 -1
  92. package/dist/admin/lib/tekstVeld.d.ts +8 -0
  93. package/dist/admin/lib/tekstVeld.js +10 -0
  94. package/dist/admin/lib/tekstVeld.js.map +1 -0
  95. package/dist/admin/lib/useIsSuperadmin.d.ts +2 -0
  96. package/dist/admin/lib/useIsSuperadmin.js +13 -0
  97. package/dist/admin/lib/useIsSuperadmin.js.map +1 -0
  98. package/dist/admin/lib/veldTaal.d.ts +14 -0
  99. package/dist/admin/lib/veldTaal.js +248 -0
  100. package/dist/admin/lib/veldTaal.js.map +1 -0
  101. package/dist/admin/lib/versieVergelijk.d.ts +17 -0
  102. package/dist/admin/lib/versieVergelijk.js +24 -0
  103. package/dist/admin/lib/versieVergelijk.js.map +1 -0
  104. package/dist/admin/lib/zodFields.js +22 -4
  105. package/dist/admin/lib/zodFields.js.map +1 -1
  106. package/dist/admin/lib/zodFout.d.ts +9 -0
  107. package/dist/admin/lib/zodFout.js +63 -0
  108. package/dist/admin/lib/zodFout.js.map +1 -0
  109. package/dist/admin/media/MediaManager.js +162 -32
  110. package/dist/admin/media/MediaManager.js.map +1 -1
  111. package/dist/admin/media/MediaPicker.d.ts +3 -0
  112. package/dist/admin/media/MediaPicker.js +84 -31
  113. package/dist/admin/media/MediaPicker.js.map +1 -1
  114. package/dist/admin/menu/MenuPanel.js +144 -83
  115. package/dist/admin/menu/MenuPanel.js.map +1 -1
  116. package/dist/admin/navContext.d.ts +3 -0
  117. package/dist/admin/navContext.js +13 -1
  118. package/dist/admin/navContext.js.map +1 -1
  119. package/dist/admin/pages/PageEditor.js +146 -30
  120. package/dist/admin/pages/PageEditor.js.map +1 -1
  121. package/dist/admin/pages/PagesPanel.js +215 -61
  122. package/dist/admin/pages/PagesPanel.js.map +1 -1
  123. package/dist/admin/pages/StarterKiezer.d.ts +12 -0
  124. package/dist/admin/pages/StarterKiezer.js +113 -0
  125. package/dist/admin/pages/StarterKiezer.js.map +1 -0
  126. package/dist/admin/panelRegistry.d.ts +1 -1
  127. package/dist/admin/panelRegistry.js +2 -1
  128. package/dist/admin/panelRegistry.js.map +1 -1
  129. package/dist/admin/patterns/PatternInserter.js +1 -1
  130. package/dist/admin/patterns/PatternInserter.js.map +1 -1
  131. package/dist/admin/settings/BedrijfInstellingen.d.ts +20 -0
  132. package/dist/admin/settings/BedrijfInstellingen.js +159 -0
  133. package/dist/admin/settings/BedrijfInstellingen.js.map +1 -0
  134. package/dist/admin/settings/MailTemplates.js +5 -4
  135. package/dist/admin/settings/MailTemplates.js.map +1 -1
  136. package/dist/admin/settings/SettingsPanel.js +120 -78
  137. package/dist/admin/settings/SettingsPanel.js.map +1 -1
  138. package/dist/admin/settings/SnapshotsCard.js +4 -4
  139. package/dist/admin/settings/SnapshotsCard.js.map +1 -1
  140. package/dist/admin/settings/VorigeTerugzetten.d.ts +15 -0
  141. package/dist/admin/settings/VorigeTerugzetten.js +89 -0
  142. package/dist/admin/settings/VorigeTerugzetten.js.map +1 -0
  143. package/dist/admin/shop/CategoriesTab.js +6 -4
  144. package/dist/admin/shop/CategoriesTab.js.map +1 -1
  145. package/dist/admin/shop/DiscountsTab.js +4 -2
  146. package/dist/admin/shop/DiscountsTab.js.map +1 -1
  147. package/dist/admin/shop/OrdersTab.js +57 -19
  148. package/dist/admin/shop/OrdersTab.js.map +1 -1
  149. package/dist/admin/shop/ProductPaneel.js +2 -1
  150. package/dist/admin/shop/ProductPaneel.js.map +1 -1
  151. package/dist/admin/shop/ProductsTab.js +40 -19
  152. package/dist/admin/shop/ProductsTab.js.map +1 -1
  153. package/dist/admin/shop/ShopPanel.js +13 -1
  154. package/dist/admin/shop/ShopPanel.js.map +1 -1
  155. package/dist/admin/shop/ShopSettingsTab.js +56 -17
  156. package/dist/admin/shop/ShopSettingsTab.js.map +1 -1
  157. package/dist/admin/shop/productClient.js +7 -3
  158. package/dist/admin/shop/productClient.js.map +1 -1
  159. package/dist/admin/stats/NietGevonden.d.ts +10 -0
  160. package/dist/admin/stats/NietGevonden.js +78 -0
  161. package/dist/admin/stats/NietGevonden.js.map +1 -0
  162. package/dist/admin/stats/StatsPanel.js +6 -1
  163. package/dist/admin/stats/StatsPanel.js.map +1 -1
  164. package/dist/admin/stats/lib.d.ts +2 -0
  165. package/dist/admin/stats/lib.js.map +1 -1
  166. package/dist/admin/ui/ConfirmDialog.js +10 -1
  167. package/dist/admin/ui/ConfirmDialog.js.map +1 -1
  168. package/dist/admin/ui/Icon.d.ts +34 -0
  169. package/dist/admin/ui/Icon.js +44 -0
  170. package/dist/admin/ui/Icon.js.map +1 -0
  171. package/dist/admin/ui/ImageField.d.ts +15 -0
  172. package/dist/admin/ui/ImageField.js +67 -0
  173. package/dist/admin/ui/ImageField.js.map +1 -0
  174. package/dist/admin/ui/KleurVeld.d.ts +25 -0
  175. package/dist/admin/ui/KleurVeld.js +76 -0
  176. package/dist/admin/ui/KleurVeld.js.map +1 -0
  177. package/dist/admin/ui/LaadStaat.d.ts +21 -0
  178. package/dist/admin/ui/LaadStaat.js +43 -0
  179. package/dist/admin/ui/LaadStaat.js.map +1 -0
  180. package/dist/admin/ui/LeesbaarheidPaneel.d.ts +10 -0
  181. package/dist/admin/ui/LeesbaarheidPaneel.js +68 -0
  182. package/dist/admin/ui/LeesbaarheidPaneel.js.map +1 -0
  183. package/dist/admin/ui/LinkDialog.d.ts +22 -0
  184. package/dist/admin/ui/LinkDialog.js +131 -0
  185. package/dist/admin/ui/LinkDialog.js.map +1 -0
  186. package/dist/admin/ui/LinkVeld.d.ts +14 -0
  187. package/dist/admin/ui/LinkVeld.js +49 -0
  188. package/dist/admin/ui/LinkVeld.js.map +1 -0
  189. package/dist/admin/ui/PreviewLinkResultaat.d.ts +14 -0
  190. package/dist/admin/ui/PreviewLinkResultaat.js +52 -0
  191. package/dist/admin/ui/PreviewLinkResultaat.js.map +1 -0
  192. package/dist/admin/ui/charts.js +5 -5
  193. package/dist/admin/ui/charts.js.map +1 -1
  194. package/dist/admin/ui/primitives.d.ts +16 -6
  195. package/dist/admin/ui/primitives.js +98 -26
  196. package/dist/admin/ui/primitives.js.map +1 -1
  197. package/dist/admin/ui/select.js +5 -2
  198. package/dist/admin/ui/select.js.map +1 -1
  199. package/dist/admin/ui/switch.d.ts +3 -1
  200. package/dist/admin/ui/switch.js +6 -3
  201. package/dist/admin/ui/switch.js.map +1 -1
  202. package/dist/admin/ui/toast.d.ts +2 -1
  203. package/dist/admin/ui/toast.js +17 -11
  204. package/dist/admin/ui/toast.js.map +1 -1
  205. package/dist/admin/visual/BlokKiezer.d.ts +34 -0
  206. package/dist/admin/visual/BlokKiezer.js +225 -0
  207. package/dist/admin/visual/BlokKiezer.js.map +1 -0
  208. package/dist/admin/visual/InlineGereedschap.d.ts +52 -0
  209. package/dist/admin/visual/InlineGereedschap.js +356 -0
  210. package/dist/admin/visual/InlineGereedschap.js.map +1 -0
  211. package/dist/admin/visual/InlineRijkeTekst.d.ts +23 -0
  212. package/dist/admin/visual/InlineRijkeTekst.js +152 -0
  213. package/dist/admin/visual/InlineRijkeTekst.js.map +1 -0
  214. package/dist/admin/visual/VisualEditor.js +273 -125
  215. package/dist/admin/visual/VisualEditor.js.map +1 -1
  216. package/dist/admin.css +1 -1
  217. package/dist/auth/AuthProvider.js +1 -0
  218. package/dist/auth/AuthProvider.js.map +1 -1
  219. package/dist/bedrijf/bedrijf.d.ts +44 -0
  220. package/dist/bedrijf/bedrijf.js +125 -0
  221. package/dist/bedrijf/bedrijf.js.map +1 -0
  222. package/dist/blocks/BlockRenderer.js +11 -1
  223. package/dist/blocks/BlockRenderer.js.map +1 -1
  224. package/dist/blocks/blockStyle.d.ts +7 -0
  225. package/dist/blocks/blockStyle.js +16 -3
  226. package/dist/blocks/blockStyle.js.map +1 -1
  227. package/dist/blocks/builtinSchemas.js +6 -0
  228. package/dist/blocks/builtinSchemas.js.map +1 -1
  229. package/dist/blocks/builtins/AgendaRender.d.ts +22 -0
  230. package/dist/blocks/builtins/AgendaRender.js +69 -9
  231. package/dist/blocks/builtins/AgendaRender.js.map +1 -1
  232. package/dist/blocks/builtins/Availability.d.ts +1 -0
  233. package/dist/blocks/builtins/Availability.js +23 -0
  234. package/dist/blocks/builtins/Availability.js.map +1 -0
  235. package/dist/blocks/builtins/AvailabilityRender.d.ts +12 -0
  236. package/dist/blocks/builtins/AvailabilityRender.js +105 -0
  237. package/dist/blocks/builtins/AvailabilityRender.js.map +1 -0
  238. package/dist/blocks/builtins/BeforeAfterRender.js +4 -3
  239. package/dist/blocks/builtins/BeforeAfterRender.js.map +1 -1
  240. package/dist/blocks/builtins/Booking.js +1 -1
  241. package/dist/blocks/builtins/Booking.js.map +1 -1
  242. package/dist/blocks/builtins/BreadcrumbsRender.d.ts +4 -3
  243. package/dist/blocks/builtins/BreadcrumbsRender.js +14 -8
  244. package/dist/blocks/builtins/BreadcrumbsRender.js.map +1 -1
  245. package/dist/blocks/builtins/ColumnsRender.d.ts +1 -1
  246. package/dist/blocks/builtins/ColumnsRender.js +3 -1
  247. package/dist/blocks/builtins/ColumnsRender.js.map +1 -1
  248. package/dist/blocks/builtins/Contact.js +1 -1
  249. package/dist/blocks/builtins/Contact.js.map +1 -1
  250. package/dist/blocks/builtins/ContactRender.d.ts +1 -1
  251. package/dist/blocks/builtins/ContactRender.js +37 -8
  252. package/dist/blocks/builtins/ContactRender.js.map +1 -1
  253. package/dist/blocks/builtins/Cta.js +1 -1
  254. package/dist/blocks/builtins/Cta.js.map +1 -1
  255. package/dist/blocks/builtins/CtaRender.d.ts +1 -1
  256. package/dist/blocks/builtins/CtaRender.js +5 -4
  257. package/dist/blocks/builtins/CtaRender.js.map +1 -1
  258. package/dist/blocks/builtins/Embed.js +1 -1
  259. package/dist/blocks/builtins/Embed.js.map +1 -1
  260. package/dist/blocks/builtins/FormBlock.js +1 -0
  261. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  262. package/dist/blocks/builtins/FormBlockRender.d.ts +6 -0
  263. package/dist/blocks/builtins/FormBlockRender.js +88 -16
  264. package/dist/blocks/builtins/FormBlockRender.js.map +1 -1
  265. package/dist/blocks/builtins/GalleryRender.js +3 -2
  266. package/dist/blocks/builtins/GalleryRender.js.map +1 -1
  267. package/dist/blocks/builtins/Hero.js +1 -1
  268. package/dist/blocks/builtins/Hero.js.map +1 -1
  269. package/dist/blocks/builtins/HeroRender.js +7 -3
  270. package/dist/blocks/builtins/HeroRender.js.map +1 -1
  271. package/dist/blocks/builtins/OpenNowBadge.d.ts +4 -2
  272. package/dist/blocks/builtins/OpenNowBadge.js +6 -6
  273. package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
  274. package/dist/blocks/builtins/OpeningHoursRender.d.ts +8 -2
  275. package/dist/blocks/builtins/OpeningHoursRender.js +27 -6
  276. package/dist/blocks/builtins/OpeningHoursRender.js.map +1 -1
  277. package/dist/blocks/builtins/PriceList.d.ts +1 -0
  278. package/dist/blocks/builtins/PriceList.js +25 -0
  279. package/dist/blocks/builtins/PriceList.js.map +1 -0
  280. package/dist/blocks/builtins/PriceListRender.d.ts +17 -0
  281. package/dist/blocks/builtins/PriceListRender.js +87 -0
  282. package/dist/blocks/builtins/PriceListRender.js.map +1 -0
  283. package/dist/blocks/builtins/RichTextBlockRender.js +6 -1
  284. package/dist/blocks/builtins/RichTextBlockRender.js.map +1 -1
  285. package/dist/blocks/builtins/SectionHeading.d.ts +1 -0
  286. package/dist/blocks/builtins/SectionHeading.js +14 -0
  287. package/dist/blocks/builtins/SectionHeading.js.map +1 -0
  288. package/dist/blocks/builtins/SectionHeadingRender.d.ts +7 -0
  289. package/dist/blocks/builtins/SectionHeadingRender.js +14 -0
  290. package/dist/blocks/builtins/SectionHeadingRender.js.map +1 -0
  291. package/dist/blocks/builtins/SliderRender.js +3 -2
  292. package/dist/blocks/builtins/SliderRender.js.map +1 -1
  293. package/dist/blocks/builtins/SymbolRef.js +1 -1
  294. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  295. package/dist/blocks/builtins/TeamRender.js +6 -1
  296. package/dist/blocks/builtins/TeamRender.js.map +1 -1
  297. package/dist/blocks/builtins/Testimonials.js +3 -1
  298. package/dist/blocks/builtins/Testimonials.js.map +1 -1
  299. package/dist/blocks/builtins/TestimonialsRender.js +4 -0
  300. package/dist/blocks/builtins/TestimonialsRender.js.map +1 -1
  301. package/dist/blocks/builtins/WhatsappChatRender.d.ts +1 -1
  302. package/dist/blocks/builtins/WhatsappChatRender.js +3 -1
  303. package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -1
  304. package/dist/blocks/builtins/Widgets.js +2 -2
  305. package/dist/blocks/builtins/Widgets.js.map +1 -1
  306. package/dist/blocks/builtins/WidgetsRender.js +3 -1
  307. package/dist/blocks/builtins/WidgetsRender.js.map +1 -1
  308. package/dist/blocks/builtins/consentEmbed.js +26 -9
  309. package/dist/blocks/builtins/consentEmbed.js.map +1 -1
  310. package/dist/blocks/builtins/index.d.ts +3 -0
  311. package/dist/blocks/builtins/index.js +6 -0
  312. package/dist/blocks/builtins/index.js.map +1 -1
  313. package/dist/blocks/builtins/lib/autoComplete.d.ts +7 -0
  314. package/dist/blocks/builtins/lib/autoComplete.js +20 -0
  315. package/dist/blocks/builtins/lib/autoComplete.js.map +1 -0
  316. package/dist/blocks/builtins/lib/bookingUrl.d.ts +2 -2
  317. package/dist/blocks/builtins/lib/bookingUrl.js +1 -1
  318. package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -1
  319. package/dist/blocks/builtins/lib/ical.d.ts +24 -0
  320. package/dist/blocks/builtins/lib/ical.js +82 -0
  321. package/dist/blocks/builtins/lib/ical.js.map +1 -0
  322. package/dist/blocks/builtins/lib/vandaag.d.ts +8 -0
  323. package/dist/blocks/builtins/lib/vandaag.js +22 -0
  324. package/dist/blocks/builtins/lib/vandaag.js.map +1 -0
  325. package/dist/blocks/registry.d.ts +6 -0
  326. package/dist/blocks/registry.js.map +1 -1
  327. package/dist/blocks/ui/index.js +1 -0
  328. package/dist/blocks/ui/index.js.map +1 -1
  329. package/dist/components/EditBar.js +2 -4
  330. package/dist/components/EditBar.js.map +1 -1
  331. package/dist/components/News.js +26 -18
  332. package/dist/components/News.js.map +1 -1
  333. package/dist/components/PageView.d.ts +7 -1
  334. package/dist/components/PageView.js +17 -0
  335. package/dist/components/PageView.js.map +1 -1
  336. package/dist/components/ResponsiveImage.js +18 -20
  337. package/dist/components/ResponsiveImage.js.map +1 -1
  338. package/dist/components/onderOffset.d.ts +13 -0
  339. package/dist/components/onderOffset.js +74 -0
  340. package/dist/components/onderOffset.js.map +1 -0
  341. package/dist/components/site/Bedrijfsnummers.d.ts +9 -0
  342. package/dist/components/site/Bedrijfsnummers.js +12 -0
  343. package/dist/components/site/Bedrijfsnummers.js.map +1 -0
  344. package/dist/components/site/SiteFooter.d.ts +6 -1
  345. package/dist/components/site/SiteFooter.js +62 -9
  346. package/dist/components/site/SiteFooter.js.map +1 -1
  347. package/dist/components/site/SiteHeader.d.ts +15 -0
  348. package/dist/components/site/SiteHeader.js +70 -0
  349. package/dist/components/site/SiteHeader.js.map +1 -0
  350. package/dist/components/site/SiteNav.d.ts +2 -0
  351. package/dist/components/site/SiteNav.js +29 -5
  352. package/dist/components/site/SiteNav.js.map +1 -1
  353. package/dist/demo/DemoStudio.js +2 -2
  354. package/dist/demo/DemoStudio.js.map +1 -1
  355. package/dist/demo/demoStore.d.ts +13 -0
  356. package/dist/demo/demoStore.js +36 -0
  357. package/dist/demo/demoStore.js.map +1 -1
  358. package/dist/editor/EditorProvider.js +15 -4
  359. package/dist/editor/EditorProvider.js.map +1 -1
  360. package/dist/firebase/functionsClient.js +5 -1
  361. package/dist/firebase/functionsClient.js.map +1 -1
  362. package/dist/index.d.ts +5 -1
  363. package/dist/index.js +20 -1
  364. package/dist/index.js.map +1 -1
  365. package/dist/media/ImageContext.d.ts +17 -0
  366. package/dist/media/ImageContext.js +65 -20
  367. package/dist/media/ImageContext.js.map +1 -1
  368. package/dist/media/altInHtml.d.ts +9 -0
  369. package/dist/media/altInHtml.js +17 -0
  370. package/dist/media/altInHtml.js.map +1 -0
  371. package/dist/media/srcset.d.ts +12 -0
  372. package/dist/media/srcset.js +13 -0
  373. package/dist/media/srcset.js.map +1 -0
  374. package/dist/modules/builtins.d.ts +1 -0
  375. package/dist/modules/builtins.js +22 -9
  376. package/dist/modules/builtins.js.map +1 -1
  377. package/dist/modules/consent/ConsentBanner.d.ts +5 -0
  378. package/dist/modules/consent/ConsentBanner.js +8 -7
  379. package/dist/modules/consent/ConsentBanner.js.map +1 -1
  380. package/dist/modules/consent/standaard.d.ts +12 -0
  381. package/dist/modules/consent/standaard.js +10 -0
  382. package/dist/modules/consent/standaard.js.map +1 -0
  383. package/dist/modules/consent/useConsent.d.ts +4 -0
  384. package/dist/modules/consent/useConsent.js +30 -1
  385. package/dist/modules/consent/useConsent.js.map +1 -1
  386. package/dist/modules/shop/blocks/Cart.js +5 -3
  387. package/dist/modules/shop/blocks/Cart.js.map +1 -1
  388. package/dist/modules/shop/blocks/Checkout.js +56 -35
  389. package/dist/modules/shop/blocks/Checkout.js.map +1 -1
  390. package/dist/modules/shop/blocks/Donation.js +4 -4
  391. package/dist/modules/shop/blocks/Donation.js.map +1 -1
  392. package/dist/modules/shop/blocks/ProductDetail.js +19 -3
  393. package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
  394. package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -1
  395. package/dist/modules/shop/blocks/ProductGrid.js +8 -4
  396. package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
  397. package/dist/modules/shop/blocks/ShopCategories.js +4 -3
  398. package/dist/modules/shop/blocks/ShopCategories.js.map +1 -1
  399. package/dist/modules/shop/lib.d.ts +4 -0
  400. package/dist/modules/shop/lib.js +14 -0
  401. package/dist/modules/shop/lib.js.map +1 -1
  402. package/dist/modules/shop/settings.d.ts +3 -0
  403. package/dist/modules/shop/settings.js +14 -0
  404. package/dist/modules/shop/settings.js.map +1 -1
  405. package/dist/modules/shop/strings.d.ts +4 -0
  406. package/dist/modules/shop/strings.js +8 -0
  407. package/dist/modules/shop/strings.js.map +1 -1
  408. package/dist/next/envCheck.d.ts +42 -0
  409. package/dist/next/envCheck.js +106 -0
  410. package/dist/next/envCheck.js.map +1 -0
  411. package/dist/next/index.cjs +185 -2
  412. package/dist/next/index.d.ts +2 -0
  413. package/dist/next/index.js +8 -1
  414. package/dist/next/index.js.map +1 -1
  415. package/dist/next/securityHeaders.d.ts +36 -0
  416. package/dist/next/securityHeaders.js +74 -0
  417. package/dist/next/securityHeaders.js.map +1 -0
  418. package/dist/patterns/builtins.d.ts +7 -0
  419. package/dist/patterns/builtins.js +138 -3
  420. package/dist/patterns/builtins.js.map +1 -1
  421. package/dist/releaseNotes.js +148 -0
  422. package/dist/releaseNotes.js.map +1 -1
  423. package/dist/render/PublishedPage.d.ts +2 -1
  424. package/dist/render/PublishedPage.js +10 -4
  425. package/dist/render/PublishedPage.js.map +1 -1
  426. package/dist/schema/blocks.d.ts +473 -0
  427. package/dist/schema/blocks.js +105 -5
  428. package/dist/schema/blocks.js.map +1 -1
  429. package/dist/schema/business.d.ts +118 -0
  430. package/dist/schema/business.js +75 -0
  431. package/dist/schema/business.js.map +1 -0
  432. package/dist/schema/fields.d.ts +2 -1
  433. package/dist/schema/fields.js.map +1 -1
  434. package/dist/schema/forms.d.ts +18 -0
  435. package/dist/schema/forms.js +18 -3
  436. package/dist/schema/forms.js.map +1 -1
  437. package/dist/schema/mail.d.ts +10 -0
  438. package/dist/schema/mail.js +7 -0
  439. package/dist/schema/mail.js.map +1 -1
  440. package/dist/schema/settings.d.ts +103 -0
  441. package/dist/schema/settings.js +24 -3
  442. package/dist/schema/settings.js.map +1 -1
  443. package/dist/schema/shop.d.ts +2 -0
  444. package/dist/schema/shop.js +4 -0
  445. package/dist/schema/shop.js.map +1 -1
  446. package/dist/schema/shopGeld.d.ts +1 -0
  447. package/dist/schema/shopGeld.js +1 -0
  448. package/dist/schema/shopGeld.js.map +1 -1
  449. package/dist/schema/siteConfig.d.ts +69 -1
  450. package/dist/schema/siteConfig.js +7 -2
  451. package/dist/schema/siteConfig.js.map +1 -1
  452. package/dist/server/blockData.d.ts +1 -1
  453. package/dist/server/blockData.js +74 -18
  454. package/dist/server/blockData.js.map +1 -1
  455. package/dist/server/cspRapport.d.ts +20 -0
  456. package/dist/server/cspRapport.js +56 -0
  457. package/dist/server/cspRapport.js.map +1 -0
  458. package/dist/server/fonts.d.ts +3 -0
  459. package/dist/server/fonts.js +39 -0
  460. package/dist/server/fonts.js.map +1 -0
  461. package/dist/server/index.d.ts +3 -0
  462. package/dist/server/index.js +8 -0
  463. package/dist/server/index.js.map +1 -1
  464. package/dist/server/sanitize.js +2 -0
  465. package/dist/server/sanitize.js.map +1 -1
  466. package/dist/settings/useSiteSettings.js +4 -1
  467. package/dist/settings/useSiteSettings.js.map +1 -1
  468. package/dist/starters/starters.d.ts +69 -0
  469. package/dist/starters/starters.js +171 -0
  470. package/dist/starters/starters.js.map +1 -0
  471. package/dist/styles.css +1181 -212
  472. package/dist/theme/color.d.ts +10 -0
  473. package/dist/theme/color.js +17 -1
  474. package/dist/theme/color.js.map +1 -1
  475. package/dist/theme/fontFallback.d.ts +23 -0
  476. package/dist/theme/fontFallback.js +58 -0
  477. package/dist/theme/fontFallback.js.map +1 -0
  478. package/dist/theme/fonts.d.ts +11 -0
  479. package/dist/theme/fonts.js +16 -0
  480. package/dist/theme/fonts.js.map +1 -0
  481. package/dist/version.js +1 -1
  482. package/package.json +5 -4
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
4
4
  let visualEditorModule = null;
5
5
  const OPSLAGSLEUTEL = "xicms-bewerkmodus";
@@ -26,6 +26,8 @@ const Ctx = createContext({
26
26
  function EditorProvider({ children }) {
27
27
  const [modus, setModusState] = useState("live");
28
28
  const [VisualEditor, setVisualEditor] = useState(() => visualEditorModule);
29
+ const [laadFout, setLaadFout] = useState(false);
30
+ const [laadPoging, setLaadPoging] = useState(0);
29
31
  const [draft, setDraft] = useState(null);
30
32
  const [publishedOverride, setPublishedOverride] = useState(null);
31
33
  const [fotoVervanger, setFotoVervanger] = useState(null);
@@ -41,14 +43,18 @@ function EditorProvider({ children }) {
41
43
  useEffect(() => {
42
44
  if (modus !== "bewerken" || VisualEditor) return;
43
45
  let losgekoppeld = false;
44
- void import("../admin/visual/VisualEditor.js").then((m) => {
46
+ setLaadFout(false);
47
+ import("../admin/visual/VisualEditor.js").then((m) => {
45
48
  visualEditorModule = m.default;
46
49
  if (!losgekoppeld) setVisualEditor(() => m.default);
50
+ }).catch((e) => {
51
+ console.error("[xicms] editor laden mislukt:", e);
52
+ if (!losgekoppeld) setLaadFout(true);
47
53
  });
48
54
  return () => {
49
55
  losgekoppeld = true;
50
56
  };
51
- }, [modus, VisualEditor]);
57
+ }, [modus, VisualEditor, laadPoging]);
52
58
  const zetModus = useCallback((next) => {
53
59
  setModusState(next);
54
60
  try {
@@ -80,7 +86,12 @@ function EditorProvider({ children }) {
80
86
  }), [modus, draft, zetModus, publishDraft, publishedOverride, bevestigPublicatie, fotoVervanger, zetFotoVervanger]);
81
87
  return /* @__PURE__ */ jsxs(Ctx.Provider, { value, children: [
82
88
  children,
83
- modus === "bewerken" && VisualEditor && /* @__PURE__ */ jsx(VisualEditor, { onClose: () => zetModus("live") })
89
+ modus === "bewerken" && VisualEditor && /* @__PURE__ */ jsx(VisualEditor, { onClose: () => zetModus("live") }),
90
+ modus === "bewerken" && !VisualEditor && /* @__PURE__ */ jsx("div", { className: "xicms-editor-laden", role: laadFout ? "alert" : "status", "aria-live": "polite", "data-xicms-tooling": "", children: laadFout ? /* @__PURE__ */ jsxs(Fragment, { children: [
91
+ /* @__PURE__ */ jsx("span", { children: "De editor kon niet geladen worden." }),
92
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setLaadPoging((n) => n + 1), children: "Opnieuw proberen" }),
93
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => zetModus("live"), children: "Sluiten" })
94
+ ] }) : /* @__PURE__ */ jsx("span", { children: "Editor laden\u2026" }) })
84
95
  ] });
85
96
  }
86
97
  function useEditor() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/editor/EditorProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\n// Het bloktype van de PAGINA zelf, zodat wat hier langskomt per definitie past in wat\n// `PublishedPage` rendert. Een eigen definitie zou hier stil uit de pas kunnen lopen.\nimport type { Page } from \"../schema/pages\";\n\ntype PaginaBlokken = Page[\"blocks\"];\n\n/**\n * Lichte runtime-context voor het inline (visuele) bewerken op de live pagina. De echte editor\n * (TipTap/Radix, zwaar) wordt PAS geladen als een editor hem opent — publieke bezoekers downloaden\n * hem nooit. De editor leeft in de admin-entry; we laden 'm via een dynamische import (eigen chunk).\n *\n * ── WAAROM DE BLOKKEN HIER LANGSKOMEN ─────────────────────────────────────────────────────\n *\n * De editor tekende de pagina vroeger zélf, in een eigen canvas binnen een schermvullende overlay.\n * Je klikte \"Pagina bewerken\" en je site verdween achter een donker paneel met een nabootsing erin.\n * Dat voelde niet als je site bewerken maar als een ander programma openen.\n *\n * Nu blijft de PAGINA de pagina en wordt alleen de DATA vervangen: de editor laadt de werkversie en\n * zet die hier neer, waarna `PublishedPage` de werkversie rendert in plaats van de gepubliceerde\n * blokken. Dezelfde componenten, dezelfde huisstijl, dezelfde layout — alleen andere inhoud.\n *\n * Die omweg via de data is wezenlijk, geen implementatiedetail. Alleen potloodjes over de live\n * pagina leggen zou betekenen dat je de WERKVERSIE bewerkt terwijl je de GEPUBLICEERDE tekst ziet.\n * Zodra je iets wijzigt en nog niet publiceert, lopen die twee uit elkaar en bewerk je iets anders\n * dan er staat. Wat je ziet moet zijn wat je bewerkt.\n *\n * `editPath` hoort erbij: een site kan meerdere gerenderde pagina's in beeld hebben (een\n * ingekaderde pagina, een preview). Alleen het pad dat de editor open heeft mag zijn blokken\n * verwisselen.\n *\n * ── WAAROM DE MODUS EEN REFRESH MOET OVERLEVEN (7.8) ──────────────────────────────────────\n *\n * Bewerken was een toestand die alleen in het geheugen van één render-boom bestond. Een per ongeluk\n * ingedrukte F5 — of een klik op een link in je eigen site, want dat is een volledige navigatie —\n * zette je terug op de live site. Je wijzigingen waren niet weg (die staan in de werkversie in\n * Firestore), maar je zág ze niet meer, en dat is niet te onderscheiden van kwijt zijn.\n *\n * Daarom staat de modus in `sessionStorage`: hij overleeft een refresh en het navigeren door de\n * site, en verdwijnt vanzelf zodra het tabblad dichtgaat. Bewust niet `localStorage` — dan zit je\n * morgen in een nieuw venster nog steeds in de bewerkmodus zonder dat je erom vroeg.\n */\n/**\n * BEWUST GEEN `lazy()` + `<Suspense>`. De chunk-splitsing is dezelfde (de dynamische import\n * hieronder maakt zijn eigen chunk), maar Suspense heeft een gedraging die ons hier de kop kostte:\n * als de boom suspendt, koppelt React de al-gemonteerde children tijdelijk los en weer vast\n * (disconnect/reconnect van de passive effects). In dat her-aansluitpad draaide Astryx'\n * `useScrollLock`-effect dubbel zonder tussenliggende cleanup, waardoor zijn module-brede teller\n * nooit meer op nul kwam — en een teller boven nul betekent: `body { position: fixed }`. De hele\n * site stond stil: niet meer scrollen, dus ook geen bewerkknoppen bij blokken onder de vouw.\n * Zelf laden in een effect en pas renderen als de module er is, heeft dat Suspense-pad niet.\n */\ntype VisualEditorComponent = ComponentType<{ onClose: () => void }>;\nlet visualEditorModule: VisualEditorComponent | null = null;\n\n/** \"live\" = precies wat een bezoeker ziet. \"bewerken\" = dezelfde pagina, met werkversie en gereedschap. */\nexport type Bewerkmodus = \"live\" | \"bewerken\";\n\nconst OPSLAGSLEUTEL = \"xicms-bewerkmodus\";\n\ninterface EditorCtx {\n modus: Bewerkmodus;\n /** Blijft bestaan naast `modus` omdat het door de hele codebase heen gelezen wordt. */\n open: boolean;\n zetModus: (modus: Bewerkmodus) => void;\n openEditor: () => void;\n closeEditor: () => void;\n /** De blokken van de werkversie die de pagina moet tonen; `null` = toon gewoon het gepubliceerde. */\n draftBlocks: PaginaBlokken | null;\n /** Het pad waarvoor `draftBlocks` geldt. */\n editPath: string | null;\n /** Door de editor aangeroepen zodra de werkversie laadt of wijzigt. */\n publishDraft: (path: string, blocks: PaginaBlokken | null) => void;\n /**\n * Net gepubliceerde blokken voor `path` — `PublishedPage` gebruikt dit als het TERUGVALT op de\n * `page`-prop (bewerkmodus dicht/nog niet aan), zodat \"Live site\" bekijken vlak na publiceren\n * niet de VEROUDERDE server-props laat zien (die zijn maar één keer geladen, bij het openen van\n * de pagina, en publiceren ververst ze niet). Zie `bevestigPublicatie`.\n */\n publishedOverride: { path: string; blocks: PaginaBlokken } | null;\n /** Door de editor aangeroepen direct na een geslaagde `publishPage`-call. */\n bevestigPublicatie: (path: string, blocks: PaginaBlokken) => void;\n /**\n * \"Vervang de foto in veld X van blok Y\" — gezet door de VisualEditor zolang hij open is.\n *\n * De 📷-knopjes op de foto's zitten in de RENDER van de pagina (Img), de mediakiezer zit in de\n * EDITOR. Die twee delen geen boom meer sinds het canvas weg is; dit is de brug. `null` buiten\n * de bewerkmodus, en dan tekent Img geen knopjes.\n */\n fotoVervanger: ((field: string, blockKey?: string) => void) | null;\n zetFotoVervanger: (fn: ((field: string, blockKey?: string) => void) | null) => void;\n}\n\nconst Ctx = createContext<EditorCtx>({\n modus: \"live\",\n open: false,\n zetModus: () => {},\n openEditor: () => {},\n closeEditor: () => {},\n draftBlocks: null,\n editPath: null,\n publishDraft: () => {},\n publishedOverride: null,\n bevestigPublicatie: () => {},\n fotoVervanger: null,\n zetFotoVervanger: () => {},\n});\n\nexport function EditorProvider({ children }: { children: ReactNode }) {\n /**\n * Start altijd op \"live\", ook als de opslag iets anders zegt.\n *\n * De server rendert deze boom zonder toegang tot `sessionStorage`. Zou de eerste client-render\n * meteen \"bewerken\" teruggeven, dan wijkt hij af van de HTML die er al staat en gooit React de\n * boom weg (hydration mismatch). Vandaar: eerst hetzelfde als de server, daarna in een effect\n * bijstellen. Eén frame \"live\" is niet zichtbaar; een kapotte hydration wel.\n */\n const [modus, setModusState] = useState<Bewerkmodus>(\"live\");\n // De editor-component zodra zijn chunk binnen is; module-cache zodat een tweede keer openen\n // (of een andere pagina in dezelfde sessie) niet opnieuw op het netwerk wacht.\n const [VisualEditor, setVisualEditor] = useState<VisualEditorComponent | null>(() => visualEditorModule);\n const [draft, setDraft] = useState<{ path: string; blocks: PaginaBlokken } | null>(null);\n const [publishedOverride, setPublishedOverride] = useState<{ path: string; blocks: PaginaBlokken } | null>(null);\n const [fotoVervanger, setFotoVervanger] = useState<((field: string, blockKey?: string) => void) | null>(null);\n // In state gewikkeld: een functie rechtstreeks aan een setState geven zou hem AANROEPEN\n // (functionele updater) in plaats van opslaan.\n const zetFotoVervanger = useCallback((fn: ((field: string, blockKey?: string) => void) | null) => {\n setFotoVervanger(() => fn);\n }, []);\n\n useEffect(() => {\n try {\n if (sessionStorage.getItem(OPSLAGSLEUTEL) === \"bewerken\") setModusState(\"bewerken\");\n } catch {\n /* privémodus of geblokkeerde opslag: dan gewoon op de live site beginnen */\n }\n }, []);\n\n // De editor-chunk laden zodra de bewerkmodus aangaat (zie het commentaar bij\n // `visualEditorModule` waarom dit met de hand gebeurt en niet via lazy/Suspense).\n useEffect(() => {\n if (modus !== \"bewerken\" || VisualEditor) return;\n let losgekoppeld = false;\n void import(\"../admin/visual/VisualEditor\").then((m) => {\n visualEditorModule = m.default;\n if (!losgekoppeld) setVisualEditor(() => m.default);\n });\n return () => {\n losgekoppeld = true;\n };\n }, [modus, VisualEditor]);\n\n const zetModus = useCallback((next: Bewerkmodus) => {\n setModusState(next);\n try {\n if (next === \"bewerken\") sessionStorage.setItem(OPSLAGSLEUTEL, \"bewerken\");\n else sessionStorage.removeItem(OPSLAGSLEUTEL);\n } catch {\n /* de modus werkt ook zonder opslag; alleen een refresh valt dan terug op live */\n }\n // De werkversie hoort bij de bewerkmodus. Blijft hij staan na het sluiten, dan ziet de bezoeker\n // ongepubliceerde tekst terwijl het gereedschap weg is — een pagina die iets toont wat niet\n // live is. De werkversie zelf blijft in Firestore staan; alleen deze weergave stopt.\n if (next === \"live\") setDraft(null);\n }, []);\n\n const publishDraft = useCallback((path: string, blocks: PaginaBlokken | null) => {\n setDraft(blocks ? { path, blocks } : null);\n }, []);\n\n const bevestigPublicatie = useCallback((path: string, blocks: PaginaBlokken) => {\n setPublishedOverride({ path, blocks });\n }, []);\n\n const value = useMemo<EditorCtx>(() => ({\n modus,\n open: modus === \"bewerken\",\n zetModus,\n openEditor: () => zetModus(\"bewerken\"),\n closeEditor: () => zetModus(\"live\"),\n draftBlocks: draft?.blocks ?? null,\n editPath: draft?.path ?? null,\n publishDraft,\n publishedOverride,\n bevestigPublicatie,\n fotoVervanger,\n zetFotoVervanger,\n }), [modus, draft, zetModus, publishDraft, publishedOverride, bevestigPublicatie, fotoVervanger, zetFotoVervanger]);\n\n return (\n <Ctx.Provider value={value}>\n {children}\n {modus === \"bewerken\" && VisualEditor && <VisualEditor onClose={() => zetModus(\"live\")} />}\n </Ctx.Provider>\n );\n}\n\n/** Open/sluit de visuele pagina-editor. */\nexport function useEditor(): EditorCtx {\n return useContext(Ctx);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/editor/EditorProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\n// Het bloktype van de PAGINA zelf, zodat wat hier langskomt per definitie past in wat\n// `PublishedPage` rendert. Een eigen definitie zou hier stil uit de pas kunnen lopen.\nimport type { Page } from \"../schema/pages\";\n\ntype PaginaBlokken = Page[\"blocks\"];\n\n/**\n * Lichte runtime-context voor het inline (visuele) bewerken op de live pagina. De echte editor\n * (TipTap/Radix, zwaar) wordt PAS geladen als een editor hem opent — publieke bezoekers downloaden\n * hem nooit. De editor leeft in de admin-entry; we laden 'm via een dynamische import (eigen chunk).\n *\n * ── WAAROM DE BLOKKEN HIER LANGSKOMEN ─────────────────────────────────────────────────────\n *\n * De editor tekende de pagina vroeger zélf, in een eigen canvas binnen een schermvullende overlay.\n * Je klikte \"Pagina bewerken\" en je site verdween achter een donker paneel met een nabootsing erin.\n * Dat voelde niet als je site bewerken maar als een ander programma openen.\n *\n * Nu blijft de PAGINA de pagina en wordt alleen de DATA vervangen: de editor laadt de werkversie en\n * zet die hier neer, waarna `PublishedPage` de werkversie rendert in plaats van de gepubliceerde\n * blokken. Dezelfde componenten, dezelfde huisstijl, dezelfde layout — alleen andere inhoud.\n *\n * Die omweg via de data is wezenlijk, geen implementatiedetail. Alleen potloodjes over de live\n * pagina leggen zou betekenen dat je de WERKVERSIE bewerkt terwijl je de GEPUBLICEERDE tekst ziet.\n * Zodra je iets wijzigt en nog niet publiceert, lopen die twee uit elkaar en bewerk je iets anders\n * dan er staat. Wat je ziet moet zijn wat je bewerkt.\n *\n * `editPath` hoort erbij: een site kan meerdere gerenderde pagina's in beeld hebben (een\n * ingekaderde pagina, een preview). Alleen het pad dat de editor open heeft mag zijn blokken\n * verwisselen.\n *\n * ── WAAROM DE MODUS EEN REFRESH MOET OVERLEVEN (7.8) ──────────────────────────────────────\n *\n * Bewerken was een toestand die alleen in het geheugen van één render-boom bestond. Een per ongeluk\n * ingedrukte F5 — of een klik op een link in je eigen site, want dat is een volledige navigatie —\n * zette je terug op de live site. Je wijzigingen waren niet weg (die staan in de werkversie in\n * Firestore), maar je zág ze niet meer, en dat is niet te onderscheiden van kwijt zijn.\n *\n * Daarom staat de modus in `sessionStorage`: hij overleeft een refresh en het navigeren door de\n * site, en verdwijnt vanzelf zodra het tabblad dichtgaat. Bewust niet `localStorage` — dan zit je\n * morgen in een nieuw venster nog steeds in de bewerkmodus zonder dat je erom vroeg.\n */\n/**\n * BEWUST GEEN `lazy()` + `<Suspense>`. De chunk-splitsing is dezelfde (de dynamische import\n * hieronder maakt zijn eigen chunk), maar Suspense heeft een gedraging die ons hier de kop kostte:\n * als de boom suspendt, koppelt React de al-gemonteerde children tijdelijk los en weer vast\n * (disconnect/reconnect van de passive effects). In dat her-aansluitpad draaide Astryx'\n * `useScrollLock`-effect dubbel zonder tussenliggende cleanup, waardoor zijn module-brede teller\n * nooit meer op nul kwam — en een teller boven nul betekent: `body { position: fixed }`. De hele\n * site stond stil: niet meer scrollen, dus ook geen bewerkknoppen bij blokken onder de vouw.\n * Zelf laden in een effect en pas renderen als de module er is, heeft dat Suspense-pad niet.\n */\ntype VisualEditorComponent = ComponentType<{ onClose: () => void }>;\nlet visualEditorModule: VisualEditorComponent | null = null;\n\n/** \"live\" = precies wat een bezoeker ziet. \"bewerken\" = dezelfde pagina, met werkversie en gereedschap. */\nexport type Bewerkmodus = \"live\" | \"bewerken\";\n\nconst OPSLAGSLEUTEL = \"xicms-bewerkmodus\";\n\ninterface EditorCtx {\n modus: Bewerkmodus;\n /** Blijft bestaan naast `modus` omdat het door de hele codebase heen gelezen wordt. */\n open: boolean;\n zetModus: (modus: Bewerkmodus) => void;\n openEditor: () => void;\n closeEditor: () => void;\n /** De blokken van de werkversie die de pagina moet tonen; `null` = toon gewoon het gepubliceerde. */\n draftBlocks: PaginaBlokken | null;\n /** Het pad waarvoor `draftBlocks` geldt. */\n editPath: string | null;\n /** Door de editor aangeroepen zodra de werkversie laadt of wijzigt. */\n publishDraft: (path: string, blocks: PaginaBlokken | null) => void;\n /**\n * Net gepubliceerde blokken voor `path` — `PublishedPage` gebruikt dit als het TERUGVALT op de\n * `page`-prop (bewerkmodus dicht/nog niet aan), zodat \"Live site\" bekijken vlak na publiceren\n * niet de VEROUDERDE server-props laat zien (die zijn maar één keer geladen, bij het openen van\n * de pagina, en publiceren ververst ze niet). Zie `bevestigPublicatie`.\n */\n publishedOverride: { path: string; blocks: PaginaBlokken } | null;\n /** Door de editor aangeroepen direct na een geslaagde `publishPage`-call. */\n bevestigPublicatie: (path: string, blocks: PaginaBlokken) => void;\n /**\n * \"Vervang de foto in veld X van blok Y\" — gezet door de VisualEditor zolang hij open is.\n *\n * De 📷-knopjes op de foto's zitten in de RENDER van de pagina (Img), de mediakiezer zit in de\n * EDITOR. Die twee delen geen boom meer sinds het canvas weg is; dit is de brug. `null` buiten\n * de bewerkmodus, en dan tekent Img geen knopjes.\n */\n fotoVervanger: ((field: string, blockKey?: string) => void) | null;\n zetFotoVervanger: (fn: ((field: string, blockKey?: string) => void) | null) => void;\n}\n\nconst Ctx = createContext<EditorCtx>({\n modus: \"live\",\n open: false,\n zetModus: () => {},\n openEditor: () => {},\n closeEditor: () => {},\n draftBlocks: null,\n editPath: null,\n publishDraft: () => {},\n publishedOverride: null,\n bevestigPublicatie: () => {},\n fotoVervanger: null,\n zetFotoVervanger: () => {},\n});\n\nexport function EditorProvider({ children }: { children: ReactNode }) {\n /**\n * Start altijd op \"live\", ook als de opslag iets anders zegt.\n *\n * De server rendert deze boom zonder toegang tot `sessionStorage`. Zou de eerste client-render\n * meteen \"bewerken\" teruggeven, dan wijkt hij af van de HTML die er al staat en gooit React de\n * boom weg (hydration mismatch). Vandaar: eerst hetzelfde als de server, daarna in een effect\n * bijstellen. Eén frame \"live\" is niet zichtbaar; een kapotte hydration wel.\n */\n const [modus, setModusState] = useState<Bewerkmodus>(\"live\");\n // De editor-component zodra zijn chunk binnen is; module-cache zodat een tweede keer openen\n // (of een andere pagina in dezelfde sessie) niet opnieuw op het netwerk wacht.\n const [VisualEditor, setVisualEditor] = useState<VisualEditorComponent | null>(() => visualEditorModule);\n /** 8.2: mislukt het laden van de editor-chunk (slechte verbinding, nieuwe deploy), zeg dat dan. */\n const [laadFout, setLaadFout] = useState(false);\n const [laadPoging, setLaadPoging] = useState(0);\n const [draft, setDraft] = useState<{ path: string; blocks: PaginaBlokken } | null>(null);\n const [publishedOverride, setPublishedOverride] = useState<{ path: string; blocks: PaginaBlokken } | null>(null);\n const [fotoVervanger, setFotoVervanger] = useState<((field: string, blockKey?: string) => void) | null>(null);\n // In state gewikkeld: een functie rechtstreeks aan een setState geven zou hem AANROEPEN\n // (functionele updater) in plaats van opslaan.\n const zetFotoVervanger = useCallback((fn: ((field: string, blockKey?: string) => void) | null) => {\n setFotoVervanger(() => fn);\n }, []);\n\n useEffect(() => {\n try {\n if (sessionStorage.getItem(OPSLAGSLEUTEL) === \"bewerken\") setModusState(\"bewerken\");\n } catch {\n /* privémodus of geblokkeerde opslag: dan gewoon op de live site beginnen */\n }\n }, []);\n\n // De editor-chunk laden zodra de bewerkmodus aangaat (zie het commentaar bij\n // `visualEditorModule` waarom dit met de hand gebeurt en niet via lazy/Suspense).\n useEffect(() => {\n if (modus !== \"bewerken\" || VisualEditor) return;\n let losgekoppeld = false;\n setLaadFout(false);\n import(\"../admin/visual/VisualEditor\")\n .then((m) => {\n visualEditorModule = m.default;\n if (!losgekoppeld) setVisualEditor(() => m.default);\n })\n .catch((e) => {\n console.error(\"[xicms] editor laden mislukt:\", e);\n if (!losgekoppeld) setLaadFout(true);\n });\n return () => {\n losgekoppeld = true;\n };\n }, [modus, VisualEditor, laadPoging]);\n\n const zetModus = useCallback((next: Bewerkmodus) => {\n setModusState(next);\n try {\n if (next === \"bewerken\") sessionStorage.setItem(OPSLAGSLEUTEL, \"bewerken\");\n else sessionStorage.removeItem(OPSLAGSLEUTEL);\n } catch {\n /* de modus werkt ook zonder opslag; alleen een refresh valt dan terug op live */\n }\n // De werkversie hoort bij de bewerkmodus. Blijft hij staan na het sluiten, dan ziet de bezoeker\n // ongepubliceerde tekst terwijl het gereedschap weg is — een pagina die iets toont wat niet\n // live is. De werkversie zelf blijft in Firestore staan; alleen deze weergave stopt.\n if (next === \"live\") setDraft(null);\n }, []);\n\n const publishDraft = useCallback((path: string, blocks: PaginaBlokken | null) => {\n setDraft(blocks ? { path, blocks } : null);\n }, []);\n\n const bevestigPublicatie = useCallback((path: string, blocks: PaginaBlokken) => {\n setPublishedOverride({ path, blocks });\n }, []);\n\n const value = useMemo<EditorCtx>(() => ({\n modus,\n open: modus === \"bewerken\",\n zetModus,\n openEditor: () => zetModus(\"bewerken\"),\n closeEditor: () => zetModus(\"live\"),\n draftBlocks: draft?.blocks ?? null,\n editPath: draft?.path ?? null,\n publishDraft,\n publishedOverride,\n bevestigPublicatie,\n fotoVervanger,\n zetFotoVervanger,\n }), [modus, draft, zetModus, publishDraft, publishedOverride, bevestigPublicatie, fotoVervanger, zetFotoVervanger]);\n\n return (\n <Ctx.Provider value={value}>\n {children}\n {modus === \"bewerken\" && VisualEditor && <VisualEditor onClose={() => zetModus(\"live\")} />}\n {modus === \"bewerken\" && !VisualEditor && (\n <div className=\"xicms-editor-laden\" role={laadFout ? \"alert\" : \"status\"} aria-live=\"polite\" data-xicms-tooling=\"\">\n {laadFout ? (\n <>\n <span>De editor kon niet geladen worden.</span>\n <button type=\"button\" onClick={() => setLaadPoging((n) => n + 1)}>Opnieuw proberen</button>\n <button type=\"button\" onClick={() => zetModus(\"live\")}>Sluiten</button>\n </>\n ) : (\n <span>Editor laden…</span>\n )}\n </div>\n )}\n </Ctx.Provider>\n );\n}\n\n/** Open/sluit de visuele pagina-editor. */\nexport function useEditor(): EditorCtx {\n return useContext(Ctx);\n}\n"],"mappings":"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","names":[]}
@@ -5,6 +5,7 @@ const verbonden = /* @__PURE__ */ new WeakSet();
5
5
  function zonderUndefined(data) {
6
6
  return data === void 0 ? void 0 : JSON.parse(JSON.stringify(data));
7
7
  }
8
+ const ZONDER_APPCHECK = /* @__PURE__ */ new Set(["trackView", "reportClientError"]);
8
9
  function callable(app, naam) {
9
10
  const fns = getFunctions(app, functionsRegion());
10
11
  if (!verbonden.has(fns)) {
@@ -13,7 +14,10 @@ function callable(app, naam) {
13
14
  if (em) connectFunctionsEmulator(fns, em.host, em.functions);
14
15
  }
15
16
  const fn = httpsCallable(fns, naam);
16
- return ((data) => fn(zonderUndefined(data)));
17
+ return (async (data) => {
18
+ if (!ZONDER_APPCHECK.has(naam)) await (await import("./appcheck.js")).initAppCheck();
19
+ return fn(zonderUndefined(data));
20
+ });
17
21
  }
18
22
  export {
19
23
  callable
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/firebase/functionsClient.ts"],"sourcesContent":["/**\n * Eén plek waar een Cloud-Function-callable vandaan komt.\n *\n * Hiervoor stond op eenentwintig plekken hetzelfde regeltje\n * `httpsCallable(getFunctions(app, functionsRegion()), \"naam\")`. Dat was al duplicatie, maar het\n * werd een risico toen de emulators erbij kwamen: verbind je Firestore lokaal en laat je de\n * callables ongemoeid, dan lees je uit de emulator terwijl je SCHRIJFT naar het echte project —\n * en dat merk je pas als de productiedata al veranderd is. Door de callables hier te maken kan\n * die scheefstand niet ontstaan.\n *\n * Dit bestand importeert `firebase/functions` statisch, en wordt daarom zélf overal dynamisch\n * geïmporteerd waar het publieke pad langskomt (PageView, submitForm) — zo blijft de SDK uit de\n * bundel van een gewone bezoeker.\n */\nimport type { FirebaseApp } from \"firebase/app\";\nimport { getFunctions, httpsCallable, connectFunctionsEmulator, type HttpsCallable } from \"firebase/functions\";\nimport { functionsRegion } from \"./region\";\nimport { emulatorHosts } from \"./emulators\";\n\n// `getFunctions(app, regio)` geeft per app+regio dezelfde instantie terug, dus één verbinding volstaat.\nconst verbonden = new WeakSet<object>();\n\n/**\n * `undefined`-velden strippen vóór verzenden.\n *\n * De functions-SDK encodeert `undefined` als `null` over de draad, en de zod-schema's op de\n * server zeggen `.optional()` — dat accepteert een ONTBREKEND veld, maar geen `null`. Elke\n * payload met een `veld: kanLeegZijn ? waarde : undefined`-patroon werd daardoor server-side\n * geweigerd: zo faalde \"+ Nieuw product\" ALTIJD (\"Ongeldige productgegevens\" — `productId:\n * undefined` kwam als `null` aan, live bewezen op christelijkkraamcadeau met een handmatige\n * call: zónder het veld 200, mét `null` 400) en \"als verzonden markeren\" zonder tracking-URL\n * net zo. Eén JSON-roundtrip hier dicht die hele klasse voor álle callables: de payloads zijn\n * plain JSON (geen Timestamps of klasse-instanties), dus dit is verliesvrij.\n */\nfunction zonderUndefined(data: unknown): unknown {\n return data === undefined ? undefined : JSON.parse(JSON.stringify(data));\n}\n\n/** Een getypeerde callable voor `naam`, in de juiste regio en (lokaal) tegen de emulator. */\nexport function callable<Req = unknown, Res = unknown>(app: FirebaseApp, naam: string): HttpsCallable<Req, Res> {\n const fns = getFunctions(app, functionsRegion());\n if (!verbonden.has(fns)) {\n verbonden.add(fns);\n const em = emulatorHosts();\n if (em) connectFunctionsEmulator(fns, em.host, em.functions);\n }\n const fn = httpsCallable<Req, Res>(fns, naam);\n return ((data?: Req) => fn(zonderUndefined(data) as Req)) as HttpsCallable<Req, Res>;\n}\n"],"mappings":"AAeA,SAAS,cAAc,eAAe,gCAAoD;AAC1F,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAG9B,MAAM,YAAY,oBAAI,QAAgB;AActC,SAAS,gBAAgB,MAAwB;AAC/C,SAAO,SAAS,SAAY,SAAY,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;AACzE;AAGO,SAAS,SAAuC,KAAkB,MAAuC;AAC9G,QAAM,MAAM,aAAa,KAAK,gBAAgB,CAAC;AAC/C,MAAI,CAAC,UAAU,IAAI,GAAG,GAAG;AACvB,cAAU,IAAI,GAAG;AACjB,UAAM,KAAK,cAAc;AACzB,QAAI,GAAI,0BAAyB,KAAK,GAAG,MAAM,GAAG,SAAS;AAAA,EAC7D;AACA,QAAM,KAAK,cAAwB,KAAK,IAAI;AAC5C,UAAQ,CAAC,SAAe,GAAG,gBAAgB,IAAI,CAAQ;AACzD;","names":[]}
1
+ {"version":3,"sources":["../../src/firebase/functionsClient.ts"],"sourcesContent":["/**\n * Eén plek waar een Cloud-Function-callable vandaan komt.\n *\n * Hiervoor stond op eenentwintig plekken hetzelfde regeltje\n * `httpsCallable(getFunctions(app, functionsRegion()), \"naam\")`. Dat was al duplicatie, maar het\n * werd een risico toen de emulators erbij kwamen: verbind je Firestore lokaal en laat je de\n * callables ongemoeid, dan lees je uit de emulator terwijl je SCHRIJFT naar het echte project —\n * en dat merk je pas als de productiedata al veranderd is. Door de callables hier te maken kan\n * die scheefstand niet ontstaan.\n *\n * Dit bestand importeert `firebase/functions` statisch, en wordt daarom zélf overal dynamisch\n * geïmporteerd waar het publieke pad langskomt (PageView, submitForm) — zo blijft de SDK uit de\n * bundel van een gewone bezoeker.\n */\nimport type { FirebaseApp } from \"firebase/app\";\nimport { getFunctions, httpsCallable, connectFunctionsEmulator, type HttpsCallable } from \"firebase/functions\";\nimport { functionsRegion } from \"./region\";\nimport { emulatorHosts } from \"./emulators\";\n\n// `getFunctions(app, regio)` geeft per app+regio dezelfde instantie terug, dus één verbinding volstaat.\nconst verbonden = new WeakSet<object>();\n\n/**\n * `undefined`-velden strippen vóór verzenden.\n *\n * De functions-SDK encodeert `undefined` als `null` over de draad, en de zod-schema's op de\n * server zeggen `.optional()` — dat accepteert een ONTBREKEND veld, maar geen `null`. Elke\n * payload met een `veld: kanLeegZijn ? waarde : undefined`-patroon werd daardoor server-side\n * geweigerd: zo faalde \"+ Nieuw product\" ALTIJD (\"Ongeldige productgegevens\" — `productId:\n * undefined` kwam als `null` aan, live bewezen op christelijkkraamcadeau met een handmatige\n * call: zónder het veld 200, mét `null` 400) en \"als verzonden markeren\" zonder tracking-URL\n * net zo. Eén JSON-roundtrip hier dicht die hele klasse voor álle callables: de payloads zijn\n * plain JSON (geen Timestamps of klasse-instanties), dus dit is verliesvrij.\n */\nfunction zonderUndefined(data: unknown): unknown {\n return data === undefined ? undefined : JSON.parse(JSON.stringify(data));\n}\n\n/**\n * 8.16: callables die GEEN App Check nodig hebben. De bezoekersteller en de foutmelder draaien bij\n * gewone bezoekers; zij starten reCAPTCHA (en dus scripts van Google) niet. Ze zijn op de server\n * begrensd per IP en vragen ook daar geen App Check meer (functions 3.30).\n */\nconst ZONDER_APPCHECK = new Set([\"trackView\", \"reportClientError\"]);\n\n/** Een getypeerde callable voor `naam`, in de juiste regio en (lokaal) tegen de emulator. */\nexport function callable<Req = unknown, Res = unknown>(app: FirebaseApp, naam: string): HttpsCallable<Req, Res> {\n const fns = getFunctions(app, functionsRegion());\n if (!verbonden.has(fns)) {\n verbonden.add(fns);\n const em = emulatorHosts();\n if (em) connectFunctionsEmulator(fns, em.host, em.functions);\n }\n const fn = httpsCallable<Req, Res>(fns, naam);\n return (async (data?: Req) => {\n // App Check pas nu: vlak vóór een formulier, bestelling of inlogmail (no-op zonder sleutel of\n // als het al draait). De functions-SDK pikt het token daarna vanzelf op.\n if (!ZONDER_APPCHECK.has(naam)) await (await import(\"./appcheck\")).initAppCheck();\n return fn(zonderUndefined(data) as Req);\n }) as HttpsCallable<Req, Res>;\n}\n"],"mappings":"AAeA,SAAS,cAAc,eAAe,gCAAoD;AAC1F,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAG9B,MAAM,YAAY,oBAAI,QAAgB;AActC,SAAS,gBAAgB,MAAwB;AAC/C,SAAO,SAAS,SAAY,SAAY,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;AACzE;AAOA,MAAM,kBAAkB,oBAAI,IAAI,CAAC,aAAa,mBAAmB,CAAC;AAG3D,SAAS,SAAuC,KAAkB,MAAuC;AAC9G,QAAM,MAAM,aAAa,KAAK,gBAAgB,CAAC;AAC/C,MAAI,CAAC,UAAU,IAAI,GAAG,GAAG;AACvB,cAAU,IAAI,GAAG;AACjB,UAAM,KAAK,cAAc;AACzB,QAAI,GAAI,0BAAyB,KAAK,GAAG,MAAM,GAAG,SAAS;AAAA,EAC7D;AACA,QAAM,KAAK,cAAwB,KAAK,IAAI;AAC5C,UAAQ,OAAO,SAAe;AAG5B,QAAI,CAAC,gBAAgB,IAAI,IAAI,EAAG,QAAO,MAAM,OAAO,YAAY,GAAG,aAAa;AAChF,WAAO,GAAG,gBAAgB,IAAI,CAAQ;AAAA,EACxC;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export * from "./blocks/ui/index.js";
16
16
  export { SiteSettingsProvider, useSiteSettings, useMergedSiteConfig, } from "./settings/useSiteSettings.js";
17
17
  export { SiteFooter } from "./components/site/SiteFooter.js";
18
18
  export { SiteNav } from "./components/site/SiteNav.js";
19
+ export { SiteHeader } from "./components/site/SiteHeader.js";
20
+ export { Bedrijfsnummers } from "./components/site/Bedrijfsnummers.js";
19
21
  export { EditorProvider, useEditor } from "./editor/EditorProvider.js";
20
22
  export { definePattern, instantiatePattern, collectPatternTypes, builtinPatterns, type Pattern, type PatternBlock, } from "./patterns/index.js";
21
23
  export { builtinThemePresets, type ThemePreset } from "./theme/presets.js";
@@ -34,6 +36,8 @@ export type { Translations, LocaleConfig } from "./i18n/overlay.js";
34
36
  export type { ThemeTokens, NavItem, NavLeaf, Redirect, SiteSettings, PublicSiteSettings, FormsSender } from "./schema/settings.js";
35
37
  export { zoekRedirect, normaliseerPad, navHref, isExterneLink } from "./schema/settings.js";
36
38
  export type { Seo } from "./schema/seo.js";
39
+ export { BusinessSchema, BEDRIJFSTYPES, type Business, type BusinessException } from "./schema/business.js";
40
+ export { bedrijfsnummersTekst, bedrijfsnummerAdvies, adresRegels, telLink, routeLink, afwijkingOp, komendeAfwijkingen, afwijkingDatumTekst, isOpenNuMetAfwijkingen, heeftProfiel } from "./bedrijf/bedrijf.js";
37
41
  export type { Page, PageStatus } from "./schema/pages.js";
38
42
  export type { FormDefinition, FormField } from "./schema/forms.js";
39
43
  export { AuthProvider, useAuth } from "./auth/AuthProvider.js";
@@ -43,7 +47,7 @@ export type { NewsArticle, MediaItem, MediaVariant } from "./content/types.js";
43
47
  export { EditBar } from "./components/EditBar.js";
44
48
  export { ExternEmbed } from "./components/ExternEmbed.js";
45
49
  export { ResponsiveImage } from "./components/ResponsiveImage.js";
46
- export { PageView } from "./components/PageView.js";
50
+ export { PageView, NietGevondenMelder } from "./components/PageView.js";
47
51
  export { NewsList, NewsArticleView, useNews } from "./components/News.js";
48
52
  export { defineBlock, createRegistry, mergeRegistry, validateBlocks, buildBlockAddOptions, type BlockDefinition, type BlockRegistry, type BlockRenderProps, type BlockEditorProps, type BlockCategory, } from "./blocks/registry.js";
49
53
  export { BlockRenderer } from "./blocks/BlockRenderer.js";
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@ import {
11
11
  } from "./settings/useSiteSettings.js";
12
12
  import { SiteFooter } from "./components/site/SiteFooter.js";
13
13
  import { SiteNav } from "./components/site/SiteNav.js";
14
+ import { SiteHeader } from "./components/site/SiteHeader.js";
15
+ import { Bedrijfsnummers } from "./components/site/Bedrijfsnummers.js";
14
16
  import { EditorProvider, useEditor } from "./editor/EditorProvider.js";
15
17
  import {
16
18
  definePattern,
@@ -35,6 +37,8 @@ import {
35
37
  TranslationsSchema
36
38
  } from "./i18n/overlay.js";
37
39
  import { zoekRedirect, normaliseerPad, navHref, isExterneLink } from "./schema/settings.js";
40
+ import { BusinessSchema, BEDRIJFSTYPES } from "./schema/business.js";
41
+ import { bedrijfsnummersTekst, bedrijfsnummerAdvies, adresRegels, telLink, routeLink, afwijkingOp, komendeAfwijkingen, afwijkingDatumTekst, isOpenNuMetAfwijkingen, heeftProfiel } from "./bedrijf/bedrijf.js";
38
42
  import { AuthProvider, useAuth } from "./auth/AuthProvider.js";
39
43
  import { LoginForm } from "./auth/LoginForm.js";
40
44
  import {
@@ -46,7 +50,7 @@ import {
46
50
  import { EditBar } from "./components/EditBar.js";
47
51
  import { ExternEmbed } from "./components/ExternEmbed.js";
48
52
  import { ResponsiveImage } from "./components/ResponsiveImage.js";
49
- import { PageView } from "./components/PageView.js";
53
+ import { PageView, NietGevondenMelder } from "./components/PageView.js";
50
54
  import { NewsList, NewsArticleView, useNews } from "./components/News.js";
51
55
  import {
52
56
  defineBlock,
@@ -91,8 +95,11 @@ import { SiteChrome } from "./SiteChrome.js";
91
95
  import { CORE_VERSION, CORE_PACKAGE, isNieuwer } from "./version.js";
92
96
  export {
93
97
  AuthProvider,
98
+ BEDRIJFSTYPES,
99
+ Bedrijfsnummers,
94
100
  BlockDataProvider,
95
101
  BlockRenderer,
102
+ BusinessSchema,
96
103
  CORE_PACKAGE,
97
104
  CORE_VERSION,
98
105
  ConsentBanner,
@@ -114,6 +121,7 @@ export {
114
121
  ModulesProvider,
115
122
  NewsArticleView,
116
123
  NewsList,
124
+ NietGevondenMelder,
117
125
  PageRenderer,
118
126
  PageView,
119
127
  PublishedPage,
@@ -121,13 +129,19 @@ export {
121
129
  ResponsiveImage,
122
130
  SiteChrome,
123
131
  SiteFooter,
132
+ SiteHeader,
124
133
  SiteNav,
125
134
  SiteSettingsProvider,
126
135
  SymbolSchema,
127
136
  TranslationsSchema,
128
137
  XiCMSProvider,
138
+ adresRegels,
139
+ afwijkingDatumTekst,
140
+ afwijkingOp,
129
141
  applyBlockOverrides,
130
142
  assertModuleContract,
143
+ bedrijfsnummerAdvies,
144
+ bedrijfsnummersTekst,
131
145
  buildBlockAddOptions,
132
146
  builtinBlocks,
133
147
  builtinModules,
@@ -149,6 +163,7 @@ export {
149
163
  getDb,
150
164
  getFirebaseApp,
151
165
  getFirebaseAuth,
166
+ heeftProfiel,
152
167
  initAppCheck,
153
168
  instantiatePattern,
154
169
  interpretTemplate,
@@ -157,8 +172,10 @@ export {
157
172
  isFirebaseConfigured,
158
173
  isModuleActive,
159
174
  isNieuwer,
175
+ isOpenNuMetAfwijkingen,
160
176
  isRichTextKey,
161
177
  isSuperadmin,
178
+ komendeAfwijkingen,
162
179
  localizeBlocks,
163
180
  localizePage,
164
181
  localizedPath,
@@ -170,8 +187,10 @@ export {
170
187
  pruneTranslations,
171
188
  resolveModules,
172
189
  roleForSite,
190
+ routeLink,
173
191
  scopeCss,
174
192
  siteLocales,
193
+ telLink,
175
194
  useAuth,
176
195
  useBlockData,
177
196
  useConsent,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Publieke runtime-API van @xiwebdesign/xicms-core (lichte client/runtime laag).\n * - Admin (TipTap/Radix) zit in \"@xiwebdesign/xicms-core/admin\".\n * - Server-only code (firebase-admin, sanitize, SEO) zit in \"@xiwebdesign/xicms-core/server\".\n * - Pure schema's in \"@xiwebdesign/xicms-core/schema\".\n */\n\n// Provider & config\nexport { XiCMSProvider } from \"./XiCMSProvider\";\nexport { useSiteConfig } from \"./config\";\n\n// Theming & design-primitieven (maatwerk zonder core te forken).\n// - `defineTheme` maakt een eigen Astryx-thema (tokens + component-overrides) → geef door als\n// XiCMSProvider `astryxTheme`-prop.\n// - De `ui`-primitieven (Card/Stack/Grid/Heading/Text/Btn…) zijn de bouwstenen waarop de ingebouwde\n// blokken draaien; gebruik ze in eigen blokken voor een consistent maar te herstijlen uiterlijk.\nexport { defineTheme, useTheme } from \"@astryxdesign/core/theme\";\nexport type { DefinedTheme } from \"@astryxdesign/core/theme\";\n// Framework-router-integratie: geef `next/link` door als XiCMSProvider `linkComponent`-prop\n// (soepele client-nav + prefetch op alle Astryx-knoppen/links). `useLinkComponent` voor eigen blokken.\nexport { LinkProvider, useLinkComponent } from \"@astryxdesign/core/Link\";\nexport type { LinkComponentType } from \"@astryxdesign/core/Link\";\n// Beeldoptimalisatie: geef `next/image` door als XiCMSProvider `imageComponent`-prop. `Img` is de\n// wrapper die de blokken gebruiken; `useImageComponent` voor eigen blokken.\nexport { Img, ImageProvider, useImageComponent, InlineImageEditProvider } from \"./media/ImageContext\";\nexport type { ImageComponentType, ImgProps } from \"./media/ImageContext\";\nexport * from \"./blocks/ui\";\nexport {\n SiteSettingsProvider,\n useSiteSettings,\n useMergedSiteConfig,\n} from \"./settings/useSiteSettings\";\nexport { SiteFooter } from \"./components/site/SiteFooter\";\n// 7.24: de beheerde hoofdnavigatie — de enige plek die `settings.nav` leest. Zie SiteNav.tsx.\nexport { SiteNav } from \"./components/site/SiteNav\";\nexport { EditorProvider, useEditor } from \"./editor/EditorProvider\";\nexport {\n definePattern,\n instantiatePattern,\n collectPatternTypes,\n builtinPatterns,\n type Pattern,\n type PatternBlock,\n} from \"./patterns\";\nexport { builtinThemePresets, type ThemePreset } from \"./theme/presets\";\nexport { defineSiteConfig } from \"./schema/siteConfig\";\nexport type { SiteConfig, SiteMode } from \"./schema/siteConfig\";\n// 5.0: Field-IR (primo-port), symbolen en custom blocks.\nexport { FieldSchema, FieldsSchema, fieldsToZod, fieldsDefaults, isFieldVisible, isRichTextKey } from \"./schema/fields\";\nexport type { Field, FieldHints, ShowIf } from \"./schema/fields\";\nexport { SymbolSchema } from \"./schema/symbols\";\nexport type { SymbolDefinition, StoredSymbol } from \"./schema/symbols\";\nexport { CustomBlockSchema } from \"./schema/customBlocks\";\nexport type { CustomBlock, CustomBlock as CustomBlockData } from \"./schema/customBlocks\";\nexport { customBlockToDefinition } from \"./blocks/customTemplate/definition\";\nexport { interpretTemplate, scopeCss } from \"./blocks/customTemplate/interpret\";\n// 5.0: localisatie — overlay-model (primo's locale-per-waarde, additief vertaald).\nexport {\n siteLocales,\n parseLocaleSlugs,\n localizedPath,\n localizePage,\n localizeBlocks,\n pruneTranslations,\n TranslationsSchema,\n} from \"./i18n/overlay\";\nexport type { Translations, LocaleConfig } from \"./i18n/overlay\";\nexport type { ThemeTokens, NavItem, NavLeaf, Redirect, SiteSettings, PublicSiteSettings, FormsSender } from \"./schema/settings\";\nexport { zoekRedirect, normaliseerPad, navHref, isExterneLink } from \"./schema/settings\";\nexport type { Seo } from \"./schema/seo\";\nexport type { Page, PageStatus } from \"./schema/pages\";\nexport type { FormDefinition, FormField } from \"./schema/forms\";\n\n// Auth\nexport { AuthProvider, useAuth } from \"./auth/AuthProvider\";\nexport { LoginForm } from \"./auth/LoginForm\";\nexport {\n roleForSite,\n canEdit,\n canManageSite,\n isSuperadmin,\n type Role,\n type XiClaims,\n} from \"./auth/roles\";\n\n// De legacy inline-laag (ContentProvider/useContent + EditableText/EditableRichText/EditableImage,\n// het v1 platte `cms/content`-model) is in 5.0 VERWIJDERD, zoals bij de deprecatie aangekondigd.\n// Inline bewerken loopt via het block-model: markeer tekstvelden met `data-xicms-edit` in de Render.\nexport type { NewsArticle, MediaItem, MediaVariant } from \"./content/types\";\n\nexport { EditBar } from \"./components/EditBar\";\nexport { ExternEmbed } from \"./components/ExternEmbed\";\nexport { ResponsiveImage } from \"./components/ResponsiveImage\";\nexport { PageView } from \"./components/PageView\";\nexport { NewsList, NewsArticleView, useNews } from \"./components/News\";\n\n// Block-systeem (runtime: registry + renderer; editors zitten in /admin)\nexport {\n defineBlock,\n createRegistry,\n mergeRegistry,\n validateBlocks,\n buildBlockAddOptions,\n type BlockDefinition,\n type BlockRegistry,\n type BlockRenderProps,\n type BlockEditorProps,\n type BlockCategory,\n} from \"./blocks/registry\";\nexport { BlockRenderer } from \"./blocks/BlockRenderer\";\nexport { RegistryProvider, useRegistry, BlockDataProvider, useBlockData } from \"./blocks/context\";\nexport { builtinBlocks, ConsentEmbed } from \"./blocks/builtins\";\nexport { PageRenderer } from \"./render/PageRenderer\";\nexport { PublishedPage } from \"./render/PublishedPage\";\n\n// Module-systeem (XiCMS 3.0): modules bundelen blocks + instellingen; per site aan/uit + te stylen.\nexport {\n defineModule,\n resolveModules,\n isModuleActive,\n ModulesProvider,\n useModules,\n useModuleActive,\n useModuleSettings,\n applyBlockOverrides,\n moduleIdForBlockType,\n builtinModules,\n validateModuleContract,\n assertModuleContract,\n useConsent,\n ConsentBanner,\n ConsentGate,\n ConsentReopenLink,\n type ConsentState,\n type ConsentSettings,\n type ConsentCategory,\n type ModuleDefinition,\n type ModuleSiteSetting,\n type ModulesSiteConfig,\n type Capability,\n type ResolvedModules,\n type BlockOverride,\n type BlockOverrides,\n} from \"./modules\";\n\n// Demo-lock\nexport { DemoLock } from \"./lock/DemoLock\";\n\n// Demo-bewerkmodus (lokaal-only) leeft op het subpad `@xiwebdesign/xicms-core/demo` — dit zijn\n// demo-store-internals die niet in de publieke runtime-API thuishoren (klantsites gebruiken ze nooit;\n// alleen de XiCMS.nl-showcase). DemoStudio wordt intern door XiCMSProvider gerenderd.\n\n// Firebase (client)\nexport {\n getFirebaseApp,\n getFirebaseAuth,\n getDb,\n getBucket,\n isFirebaseConfigured,\n} from \"./firebase/client\";\nexport { initAppCheck } from \"./firebase/appcheck\";\nexport { SiteChrome } from \"./SiteChrome\";\nexport { CORE_VERSION, CORE_PACKAGE, isNieuwer } from \"./version\";\n"],"mappings":"AAQA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,aAAa,gBAAgB;AAItC,SAAS,cAAc,wBAAwB;AAI/C,SAAS,KAAK,eAAe,mBAAmB,+BAA+B;AAE/E,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAE3B,SAAS,eAAe;AACxB,SAAS,gBAAgB,iBAAiB;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,2BAA6C;AACtD,SAAS,wBAAwB;AAGjC,SAAS,aAAa,cAAc,aAAa,gBAAgB,gBAAgB,qBAAqB;AAEtG,SAAS,oBAAoB;AAE7B,SAAS,yBAAyB;AAElC,SAAS,+BAA+B;AACxC,SAAS,mBAAmB,gBAAgB;AAE5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,cAAc,gBAAgB,SAAS,qBAAqB;AAMrE,SAAS,cAAc,eAAe;AACtC,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAOP,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,SAAS,UAAU,iBAAiB,eAAe;AAGnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,aAAa,mBAAmB,oBAAoB;AAC/E,SAAS,eAAe,oBAAoB;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,gBAAgB;AAOzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,cAAc,cAAc,iBAAiB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Publieke runtime-API van @xiwebdesign/xicms-core (lichte client/runtime laag).\n * - Admin (TipTap/Radix) zit in \"@xiwebdesign/xicms-core/admin\".\n * - Server-only code (firebase-admin, sanitize, SEO) zit in \"@xiwebdesign/xicms-core/server\".\n * - Pure schema's in \"@xiwebdesign/xicms-core/schema\".\n */\n\n// Provider & config\nexport { XiCMSProvider } from \"./XiCMSProvider\";\nexport { useSiteConfig } from \"./config\";\n\n// Theming & design-primitieven (maatwerk zonder core te forken).\n// - `defineTheme` maakt een eigen Astryx-thema (tokens + component-overrides) → geef door als\n// XiCMSProvider `astryxTheme`-prop.\n// - De `ui`-primitieven (Card/Stack/Grid/Heading/Text/Btn…) zijn de bouwstenen waarop de ingebouwde\n// blokken draaien; gebruik ze in eigen blokken voor een consistent maar te herstijlen uiterlijk.\nexport { defineTheme, useTheme } from \"@astryxdesign/core/theme\";\nexport type { DefinedTheme } from \"@astryxdesign/core/theme\";\n// Framework-router-integratie: geef `next/link` door als XiCMSProvider `linkComponent`-prop\n// (soepele client-nav + prefetch op alle Astryx-knoppen/links). `useLinkComponent` voor eigen blokken.\nexport { LinkProvider, useLinkComponent } from \"@astryxdesign/core/Link\";\nexport type { LinkComponentType } from \"@astryxdesign/core/Link\";\n// Beeldoptimalisatie: geef `next/image` door als XiCMSProvider `imageComponent`-prop. `Img` is de\n// wrapper die de blokken gebruiken; `useImageComponent` voor eigen blokken.\nexport { Img, ImageProvider, useImageComponent, InlineImageEditProvider } from \"./media/ImageContext\";\nexport type { ImageComponentType, ImgProps } from \"./media/ImageContext\";\nexport * from \"./blocks/ui\";\nexport {\n SiteSettingsProvider,\n useSiteSettings,\n useMergedSiteConfig,\n} from \"./settings/useSiteSettings\";\nexport { SiteFooter } from \"./components/site/SiteFooter\";\n// 7.24: de beheerde hoofdnavigatie — de enige plek die `settings.nav` leest. Zie SiteNav.tsx.\nexport { SiteNav } from \"./components/site/SiteNav\";\n// 8.13: kop met logo, menu, actieknop, hamburgermenu en mobiele belbalk. Zie SiteHeader.tsx.\nexport { SiteHeader } from \"./components/site/SiteHeader\";\n// 8.16: KvK- en btw-nummer uit het bedrijfsprofiel, voor eigen footers.\nexport { Bedrijfsnummers } from \"./components/site/Bedrijfsnummers\";\nexport { EditorProvider, useEditor } from \"./editor/EditorProvider\";\nexport {\n definePattern,\n instantiatePattern,\n collectPatternTypes,\n builtinPatterns,\n type Pattern,\n type PatternBlock,\n} from \"./patterns\";\nexport { builtinThemePresets, type ThemePreset } from \"./theme/presets\";\nexport { defineSiteConfig } from \"./schema/siteConfig\";\nexport type { SiteConfig, SiteMode } from \"./schema/siteConfig\";\n// 5.0: Field-IR (primo-port), symbolen en custom blocks.\nexport { FieldSchema, FieldsSchema, fieldsToZod, fieldsDefaults, isFieldVisible, isRichTextKey } from \"./schema/fields\";\nexport type { Field, FieldHints, ShowIf } from \"./schema/fields\";\nexport { SymbolSchema } from \"./schema/symbols\";\nexport type { SymbolDefinition, StoredSymbol } from \"./schema/symbols\";\nexport { CustomBlockSchema } from \"./schema/customBlocks\";\nexport type { CustomBlock, CustomBlock as CustomBlockData } from \"./schema/customBlocks\";\nexport { customBlockToDefinition } from \"./blocks/customTemplate/definition\";\nexport { interpretTemplate, scopeCss } from \"./blocks/customTemplate/interpret\";\n// 5.0: localisatie — overlay-model (primo's locale-per-waarde, additief vertaald).\nexport {\n siteLocales,\n parseLocaleSlugs,\n localizedPath,\n localizePage,\n localizeBlocks,\n pruneTranslations,\n TranslationsSchema,\n} from \"./i18n/overlay\";\nexport type { Translations, LocaleConfig } from \"./i18n/overlay\";\nexport type { ThemeTokens, NavItem, NavLeaf, Redirect, SiteSettings, PublicSiteSettings, FormsSender } from \"./schema/settings\";\nexport { zoekRedirect, normaliseerPad, navHref, isExterneLink } from \"./schema/settings\";\nexport type { Seo } from \"./schema/seo\";\n// 8.13: bedrijfsprofiel (adres, telefoon, openingstijden) + hulpjes voor eigen headers/footers.\nexport { BusinessSchema, BEDRIJFSTYPES, type Business, type BusinessException } from \"./schema/business\";\nexport { bedrijfsnummersTekst, bedrijfsnummerAdvies, adresRegels, telLink, routeLink, afwijkingOp, komendeAfwijkingen, afwijkingDatumTekst, isOpenNuMetAfwijkingen, heeftProfiel } from \"./bedrijf/bedrijf\";\nexport type { Page, PageStatus } from \"./schema/pages\";\nexport type { FormDefinition, FormField } from \"./schema/forms\";\n\n// Auth\nexport { AuthProvider, useAuth } from \"./auth/AuthProvider\";\nexport { LoginForm } from \"./auth/LoginForm\";\nexport {\n roleForSite,\n canEdit,\n canManageSite,\n isSuperadmin,\n type Role,\n type XiClaims,\n} from \"./auth/roles\";\n\n// De legacy inline-laag (ContentProvider/useContent + EditableText/EditableRichText/EditableImage,\n// het v1 platte `cms/content`-model) is in 5.0 VERWIJDERD, zoals bij de deprecatie aangekondigd.\n// Inline bewerken loopt via het block-model: markeer tekstvelden met `data-xicms-edit` in de Render.\nexport type { NewsArticle, MediaItem, MediaVariant } from \"./content/types\";\n\nexport { EditBar } from \"./components/EditBar\";\nexport { ExternEmbed } from \"./components/ExternEmbed\";\nexport { ResponsiveImage } from \"./components/ResponsiveImage\";\nexport { PageView, NietGevondenMelder } from \"./components/PageView\";\nexport { NewsList, NewsArticleView, useNews } from \"./components/News\";\n\n// Block-systeem (runtime: registry + renderer; editors zitten in /admin)\nexport {\n defineBlock,\n createRegistry,\n mergeRegistry,\n validateBlocks,\n buildBlockAddOptions,\n type BlockDefinition,\n type BlockRegistry,\n type BlockRenderProps,\n type BlockEditorProps,\n type BlockCategory,\n} from \"./blocks/registry\";\nexport { BlockRenderer } from \"./blocks/BlockRenderer\";\nexport { RegistryProvider, useRegistry, BlockDataProvider, useBlockData } from \"./blocks/context\";\nexport { builtinBlocks, ConsentEmbed } from \"./blocks/builtins\";\nexport { PageRenderer } from \"./render/PageRenderer\";\nexport { PublishedPage } from \"./render/PublishedPage\";\n\n// Module-systeem (XiCMS 3.0): modules bundelen blocks + instellingen; per site aan/uit + te stylen.\nexport {\n defineModule,\n resolveModules,\n isModuleActive,\n ModulesProvider,\n useModules,\n useModuleActive,\n useModuleSettings,\n applyBlockOverrides,\n moduleIdForBlockType,\n builtinModules,\n validateModuleContract,\n assertModuleContract,\n useConsent,\n ConsentBanner,\n ConsentGate,\n ConsentReopenLink,\n type ConsentState,\n type ConsentSettings,\n type ConsentCategory,\n type ModuleDefinition,\n type ModuleSiteSetting,\n type ModulesSiteConfig,\n type Capability,\n type ResolvedModules,\n type BlockOverride,\n type BlockOverrides,\n} from \"./modules\";\n\n// Demo-lock\nexport { DemoLock } from \"./lock/DemoLock\";\n\n// Demo-bewerkmodus (lokaal-only) leeft op het subpad `@xiwebdesign/xicms-core/demo` — dit zijn\n// demo-store-internals die niet in de publieke runtime-API thuishoren (klantsites gebruiken ze nooit;\n// alleen de XiCMS.nl-showcase). DemoStudio wordt intern door XiCMSProvider gerenderd.\n\n// Firebase (client)\nexport {\n getFirebaseApp,\n getFirebaseAuth,\n getDb,\n getBucket,\n isFirebaseConfigured,\n} from \"./firebase/client\";\nexport { initAppCheck } from \"./firebase/appcheck\";\nexport { SiteChrome } from \"./SiteChrome\";\nexport { CORE_VERSION, CORE_PACKAGE, isNieuwer } from \"./version\";\n"],"mappings":"AAQA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,aAAa,gBAAgB;AAItC,SAAS,cAAc,wBAAwB;AAI/C,SAAS,KAAK,eAAe,mBAAmB,+BAA+B;AAE/E,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAE3B,SAAS,eAAe;AAExB,SAAS,kBAAkB;AAE3B,SAAS,uBAAuB;AAChC,SAAS,gBAAgB,iBAAiB;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,2BAA6C;AACtD,SAAS,wBAAwB;AAGjC,SAAS,aAAa,cAAc,aAAa,gBAAgB,gBAAgB,qBAAqB;AAEtG,SAAS,oBAAoB;AAE7B,SAAS,yBAAyB;AAElC,SAAS,+BAA+B;AACxC,SAAS,mBAAmB,gBAAgB;AAE5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,cAAc,gBAAgB,SAAS,qBAAqB;AAGrE,SAAS,gBAAgB,qBAA4D;AACrF,SAAS,sBAAsB,sBAAsB,aAAa,SAAS,WAAW,aAAa,oBAAoB,qBAAqB,wBAAwB,oBAAoB;AAKxL,SAAS,cAAc,eAAe;AACtC,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAOP,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,UAAU,0BAA0B;AAC7C,SAAS,UAAU,iBAAiB,eAAe;AAGnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,aAAa,mBAAmB,oBAAoB;AAC/E,SAAS,eAAe,oBAAoB;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,gBAAgB;AAOzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,cAAc,cAAc,iBAAiB;","names":[]}
@@ -34,3 +34,20 @@ export interface ImgProps {
34
34
  editFill?: boolean;
35
35
  }
36
36
  export declare function Img({ src, alt, width, height, sizes, priority, className, style: styleProp, editField, editFill }: ImgProps): import("react").JSX.Element;
37
+ /**
38
+ * Lichte beeldtag voor plekken waar `Img` te veel is (productkaarten, winkelwagen, voor/na,
39
+ * lightbox) — 8.3. Gebruikt de ladder uit de media-seed als die er is, en is anders precies de
40
+ * kale `<img>` die er al stond: géén next/image-terugval, dus geen betaalde transformaties.
41
+ */
42
+ export declare function SeedImg({ src, alt, sizes, className, style, priority, }: {
43
+ src: string;
44
+ alt: string;
45
+ sizes?: string;
46
+ className?: string;
47
+ style?: CSSProperties;
48
+ priority?: boolean;
49
+ }): import("react").JSX.Element;
50
+ /** De kleinste variant die minstens `minBreedte` breed is (bv. een avatar van 96px op 2x). */
51
+ export declare function useVariantVoor(src: string | undefined, minBreedte: number): string | undefined;
52
+ /** De grootste beschikbare versie (lightbox): volle WebP, anders de hoogste trede, anders het origineel. */
53
+ export declare function useGrootsteVariant(src: string): string;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { bronSet } from "./srcset.js";
3
4
  import { createContext, useContext } from "react";
4
5
  import { useBlockData } from "../blocks/context.js";
5
6
  const ImageContext = createContext(null);
@@ -24,30 +25,28 @@ function Img({ src, alt, width, height, sizes, priority, className, style: style
24
25
  const onReplace = useContext(InlineImageEditContext);
25
26
  const mediaSeed = useBlockData("media");
26
27
  const eigenLadder = mediaSeed?.[src];
28
+ alt = alt || eigenLadder?.alt || "";
27
29
  const ladder = eigenLadder?.variants?.sizes ?? [];
28
30
  const webp = eigenLadder?.variants?.webp;
29
31
  const focal = eigenLadder?.focal;
30
32
  const style = focal && !style_heeftPositie(styleProp) ? { ...styleProp, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` } : styleProp;
31
33
  const editHook = editField ? { "data-xicms-edit": editField } : void 0;
32
- const imageEl = ladder.length > 0 ? /* @__PURE__ */ jsxs("picture", { ...editHook, children: [
33
- webp && /* @__PURE__ */ jsx("source", { type: "image/webp", srcSet: `${webp.url} ${webp.width}w`, sizes }),
34
- /* @__PURE__ */ jsx(
35
- "img",
36
- {
37
- src,
38
- srcSet: ladder.map((v) => `${v.url} ${v.width}w`).join(", "),
39
- sizes,
40
- alt,
41
- width: eigenLadder?.width ?? width,
42
- height: eigenLadder?.height ?? height,
43
- loading: priority ? "eager" : "lazy",
44
- fetchPriority: priority ? "high" : void 0,
45
- decoding: "async",
46
- className,
47
- style
48
- }
49
- )
50
- ] }) : ImageComponent ? /* @__PURE__ */ jsx(
34
+ const imageEl = ladder.length > 0 ? /* @__PURE__ */ jsx("picture", { ...editHook, children: /* @__PURE__ */ jsx(
35
+ "img",
36
+ {
37
+ src,
38
+ srcSet: bronSet(ladder, webp),
39
+ sizes,
40
+ alt,
41
+ width: eigenLadder?.width ?? width,
42
+ height: eigenLadder?.height ?? height,
43
+ loading: priority ? "eager" : "lazy",
44
+ fetchPriority: priority ? "high" : void 0,
45
+ decoding: "async",
46
+ className,
47
+ style
48
+ }
49
+ ) }) : ImageComponent ? /* @__PURE__ */ jsx(
51
50
  ImageComponent,
52
51
  {
53
52
  src,
@@ -98,10 +97,56 @@ function Img({ src, alt, width, height, sizes, priority, className, style: style
98
97
  }
99
98
  return imageEl;
100
99
  }
100
+ function SeedImg({
101
+ src,
102
+ alt,
103
+ sizes,
104
+ className,
105
+ style,
106
+ priority
107
+ }) {
108
+ const seed = useBlockData("media")?.[src];
109
+ const srcSet = seed ? bronSet(seed.variants?.sizes, seed.variants?.webp) : void 0;
110
+ const focal = seed?.focal;
111
+ const stijl = focal && !style_heeftPositie(style) ? { ...style, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` } : style;
112
+ return (
113
+ // eslint-disable-next-line @next/next/no-img-element
114
+ /* @__PURE__ */ jsx(
115
+ "img",
116
+ {
117
+ src,
118
+ srcSet,
119
+ sizes: srcSet ? sizes : void 0,
120
+ alt: alt || seed?.alt || "",
121
+ width: seed?.width,
122
+ height: seed?.height,
123
+ loading: priority ? "eager" : "lazy",
124
+ decoding: "async",
125
+ className,
126
+ style: stijl
127
+ }
128
+ )
129
+ );
130
+ }
131
+ function useVariantVoor(src, minBreedte) {
132
+ const seed = useBlockData("media");
133
+ if (!src) return src;
134
+ const ladder = [...seed?.[src]?.variants?.sizes ?? []].sort((a, b) => a.width - b.width);
135
+ return ladder.find((v) => v.width >= minBreedte)?.url ?? src;
136
+ }
137
+ function useGrootsteVariant(src) {
138
+ const v = useBlockData("media")?.[src]?.variants;
139
+ if (v?.webp?.url) return v.webp.url;
140
+ const ladder = [...v?.sizes ?? []].sort((a, b) => b.width - a.width);
141
+ return ladder[0]?.url ?? src;
142
+ }
101
143
  export {
102
144
  ImageProvider,
103
145
  Img,
104
146
  InlineImageEditProvider,
105
- useImageComponent
147
+ SeedImg,
148
+ useGrootsteVariant,
149
+ useImageComponent,
150
+ useVariantVoor
106
151
  };
107
152
  //# sourceMappingURL=ImageContext.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/media/ImageContext.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, type CSSProperties, type ElementType, type ReactNode } from \"react\";\nimport { useBlockData } from \"../blocks/context\";\nimport type { MediaItem } from \"../content/types\";\n\n/**\n * Optionele framework-beeldcomponent (bv. Next `Image`) voor automatische optimalisatie: responsive\n * `srcset`, moderne formaten (AVIF/WebP), correcte `width/height` (geen layout-shift), lazy-loading.\n * Core importeert `next/image` niet zelf (optionele peer) — de site injecteert 'm via de\n * `imageComponent`-prop op `XiCMSProvider`, net als `linkComponent`. Zonder injectie valt alles terug\n * op een gewone `<img>` (huidig gedrag, werkt overal).\n */\nexport type ImageComponentType = ElementType;\n\nconst ImageContext = createContext<ImageComponentType | null>(null);\n\nexport function ImageProvider({ component, children }: { component?: ImageComponentType; children: ReactNode }) {\n return <ImageContext.Provider value={component ?? null}>{children}</ImageContext.Provider>;\n}\n\nexport function useImageComponent(): ImageComponentType | null {\n return useContext(ImageContext);\n}\n\n/**\n * Inline beeld-bewerken: de visuele editor levert via deze context een `onReplace(field)`-callback\n * voor het GESELECTEERDE blok. Is die actief én geeft een `<Img>` een `editField` mee, dan toont het\n * beeld een vervang-icoontje. Buiten de editor (context = null) rendert `<Img>` exact als voorheen.\n */\n/**\n * `blockKey` komt sinds 7.9 uit de DOM, niet uit een per-blok provider.\n *\n * Tot 7.4 wikkelde de editor élk blok in zijn canvas apart in deze provider, met de blok-key in de\n * closure. Dat canvas is weg — de pagina van de klant ís de editor — en één provider om de hele\n * pagina weet niet meer bij welk blok een foto hoort. De knop lost dat op het moment van klikken\n * op: elk blok draagt al `data-xicms-block` (BlockRenderer), dus `closest()` vanaf de knop is\n * betrouwbaar en kost niets zolang er niemand klikt.\n */\nconst InlineImageEditContext = createContext<((field: string, blockKey?: string) => void) | null>(null);\n\nexport function InlineImageEditProvider({\n onReplace,\n children,\n}: {\n onReplace: ((field: string, blockKey?: string) => void) | null;\n children: ReactNode;\n}) {\n return <InlineImageEditContext.Provider value={onReplace}>{children}</InlineImageEditContext.Provider>;\n}\n\nexport interface ImgProps {\n src: string;\n alt: string;\n /** Intrinsieke breedte/hoogte (verhouding) → nodig voor next/image én tegen CLS. */\n width: number;\n height: number;\n /** Hint voor responsive selectie, bv. \"(max-width: 720px) 100vw, 33vw\". */\n sizes?: string;\n /** Above-the-fold (hero): eager + hoge prioriteit voor LCP. */\n priority?: boolean;\n className?: string;\n style?: CSSProperties;\n /** Blokveld (pad) met de URL — schakelt inline vervangen in de visuele editor in (bv. \"imageUrl\"). */\n editField?: string;\n /** Beeld vult absoluut z'n parent (bv. hero-achtergrond): geen wrapper-box, knop absoluut t.o.v. het blok. */\n editFill?: boolean;\n}\n\n/**\n * Eén beeld-wrapper voor alle blokken. Gebruikt de geïnjecteerde `imageComponent` (next/image) als\n * die er is, anders een gewone `<img>`. De display-grootte regel je met CSS (`style`/`className`,\n * bv. `width:100%;height:100%;object-fit:cover`); `width`/`height` zijn de verhouding.\n */\nfunction style_heeftPositie(style?: CSSProperties): boolean {\n return Boolean(style && \"objectPosition\" in style && style.objectPosition);\n}\n\nexport function Img({ src, alt, width, height, sizes, priority, className, style: styleProp, editField, editFill }: ImgProps) {\n const ImageComponent = useImageComponent();\n const onReplace = useContext(InlineImageEditContext);\n /**\n * Eigen srcset-ladder vóór next/image (7.13). De upload-pipeline (onMediaUpload, sharp) maakt\n * bij élke upload al webp + een maten-ladder; next/image liet dat werk links liggen en\n * transformeerde dezelfde beelden nóg een keer bij Vercel — betaalde image-transformations,\n * plus transformatie-latency op de eerste hit. De route levert daarom per pagina een\n * `media`-seed (url → varianten, zie server/blockData.ts); staat de URL van dit beeld daarin,\n * dan renderen we een kale <picture> met de eigen ladder en blijft Vercel er helemaal buiten.\n * Geen seed (editor, oudere site, extern beeld zoals Unsplash) → het bestaande pad.\n */\n const mediaSeed = useBlockData<Record<string, Pick<MediaItem, \"variants\" | \"width\" | \"height\" | \"focal\">>>(\"media\");\n const eigenLadder = mediaSeed?.[src];\n const ladder = eigenLadder?.variants?.sizes ?? [];\n const webp = eigenLadder?.variants?.webp;\n // Focuspunt (7.24): staat er in het beheer een focuspunt op deze foto, dan snijdt `object-fit:\n // cover` rond dát punt bij in plaats van rond het midden. Alleen als het blok zelf geen\n // object-position zet — een blok dat bewust \"top\" wil, wint.\n const focal = eigenLadder?.focal;\n const style = focal && !style_heeftPositie(styleProp)\n ? { ...styleProp, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` }\n : styleProp;\n // De rode wijzigings-highlight (visuele editor) zoekt op `data-xicms-edit=\"<veld>\"` binnen het\n // geselecteerde blok — voor foto's stond dat hook er nergens op (alleen tekstvelden hadden 'm),\n // dus een vervangen foto bleef onopgemerkt tussen de andere, wél gemarkeerde wijzigingen. Eén\n // plek hier lost het voor elk blok met een `<Img editField=…>` tegelijk op.\n const editHook = editField ? { \"data-xicms-edit\": editField } : undefined;\n const imageEl = ladder.length > 0 ? (\n <picture {...editHook}>\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 {...editHook}\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 {...editHook}\n />\n );\n\n // In de visuele editor (geselecteerd blok): toon een vervang-icoontje over het beeld. We wijzigen\n // alleen de URL → de grootte/stijl blijft (die komt uit het blok), dus blok én beeld behouden hun maat.\n if (onReplace && editField) {\n const replace = (e: { preventDefault: () => void; stopPropagation: () => void; currentTarget?: unknown }) => {\n e.preventDefault();\n e.stopPropagation();\n const el = e.currentTarget as HTMLElement | undefined;\n const key = el?.closest?.(\"[data-xicms-block]\")?.getAttribute(\"data-xicms-block\") ?? undefined;\n onReplace(editField, key);\n };\n const btn = (\n <button type=\"button\" className={`xicms-ve-img__btn${editFill ? \" xicms-ve-img__btn--fill\" : \"\"}`} title=\"Klik om de foto te wijzigen\" onClick={replace}>\n 📷 Foto wijzigen\n </button>\n );\n // Vullend (hero-achtergrond): geen wrapper-box (zou het absoluut gepositioneerde beeld breken);\n // de knop is absoluut t.o.v. het blok-element zelf (dat is `position:relative`).\n if (editFill) return (<>{imageEl}{btn}</>);\n // In-flow beeld: hele beeld klikbaar (duidelijke affordance) + knop rechtsboven.\n return (\n <span className=\"xicms-ve-img\" style={{ position: \"relative\", display: \"block\" }} title=\"Klik om de foto te wijzigen\" onClick={replace}>\n {imageEl}\n {btn}\n </span>\n );\n }\n return imageEl;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/media/ImageContext.tsx"],"sourcesContent":["\"use client\";\n\nimport { bronSet } from \"./srcset\";\nimport { createContext, useContext, type CSSProperties, type ElementType, type ReactNode } from \"react\";\nimport { useBlockData } from \"../blocks/context\";\nimport type { MediaItem } from \"../content/types\";\n\n/**\n * Optionele framework-beeldcomponent (bv. Next `Image`) voor automatische optimalisatie: responsive\n * `srcset`, moderne formaten (AVIF/WebP), correcte `width/height` (geen layout-shift), lazy-loading.\n * Core importeert `next/image` niet zelf (optionele peer) — de site injecteert 'm via de\n * `imageComponent`-prop op `XiCMSProvider`, net als `linkComponent`. Zonder injectie valt alles terug\n * op een gewone `<img>` (huidig gedrag, werkt overal).\n */\nexport type ImageComponentType = ElementType;\n\nconst ImageContext = createContext<ImageComponentType | null>(null);\n\nexport function ImageProvider({ component, children }: { component?: ImageComponentType; children: ReactNode }) {\n return <ImageContext.Provider value={component ?? null}>{children}</ImageContext.Provider>;\n}\n\nexport function useImageComponent(): ImageComponentType | null {\n return useContext(ImageContext);\n}\n\n/**\n * Inline beeld-bewerken: de visuele editor levert via deze context een `onReplace(field)`-callback\n * voor het GESELECTEERDE blok. Is die actief én geeft een `<Img>` een `editField` mee, dan toont het\n * beeld een vervang-icoontje. Buiten de editor (context = null) rendert `<Img>` exact als voorheen.\n */\n/**\n * `blockKey` komt sinds 7.9 uit de DOM, niet uit een per-blok provider.\n *\n * Tot 7.4 wikkelde de editor élk blok in zijn canvas apart in deze provider, met de blok-key in de\n * closure. Dat canvas is weg — de pagina van de klant ís de editor — en één provider om de hele\n * pagina weet niet meer bij welk blok een foto hoort. De knop lost dat op het moment van klikken\n * op: elk blok draagt al `data-xicms-block` (BlockRenderer), dus `closest()` vanaf de knop is\n * betrouwbaar en kost niets zolang er niemand klikt.\n */\nconst InlineImageEditContext = createContext<((field: string, blockKey?: string) => void) | null>(null);\n\nexport function InlineImageEditProvider({\n onReplace,\n children,\n}: {\n onReplace: ((field: string, blockKey?: string) => void) | null;\n children: ReactNode;\n}) {\n return <InlineImageEditContext.Provider value={onReplace}>{children}</InlineImageEditContext.Provider>;\n}\n\nexport interface ImgProps {\n src: string;\n alt: string;\n /** Intrinsieke breedte/hoogte (verhouding) → nodig voor next/image én tegen CLS. */\n width: number;\n height: number;\n /** Hint voor responsive selectie, bv. \"(max-width: 720px) 100vw, 33vw\". */\n sizes?: string;\n /** Above-the-fold (hero): eager + hoge prioriteit voor LCP. */\n priority?: boolean;\n className?: string;\n style?: CSSProperties;\n /** Blokveld (pad) met de URL — schakelt inline vervangen in de visuele editor in (bv. \"imageUrl\"). */\n editField?: string;\n /** Beeld vult absoluut z'n parent (bv. hero-achtergrond): geen wrapper-box, knop absoluut t.o.v. het blok. */\n editFill?: boolean;\n}\n\n/**\n * Eén beeld-wrapper voor alle blokken. Gebruikt de geïnjecteerde `imageComponent` (next/image) als\n * die er is, anders een gewone `<img>`. De display-grootte regel je met CSS (`style`/`className`,\n * bv. `width:100%;height:100%;object-fit:cover`); `width`/`height` zijn de verhouding.\n */\nfunction style_heeftPositie(style?: CSSProperties): boolean {\n return Boolean(style && \"objectPosition\" in style && style.objectPosition);\n}\n\nexport function Img({ src, alt, width, height, sizes, priority, className, style: styleProp, editField, editFill }: ImgProps) {\n const ImageComponent = useImageComponent();\n const onReplace = useContext(InlineImageEditContext);\n /**\n * Eigen srcset-ladder vóór next/image (7.13). De upload-pipeline (onMediaUpload, sharp) maakt\n * bij élke upload al webp + een maten-ladder; next/image liet dat werk links liggen en\n * transformeerde dezelfde beelden nóg een keer bij Vercel — betaalde image-transformations,\n * plus transformatie-latency op de eerste hit. De route levert daarom per pagina een\n * `media`-seed (url → varianten, zie server/blockData.ts); staat de URL van dit beeld daarin,\n * dan renderen we een kale <picture> met de eigen ladder en blijft Vercel er helemaal buiten.\n * Geen seed (editor, oudere site, extern beeld zoals Unsplash) → het bestaande pad.\n */\n const mediaSeed = useBlockData<Record<string, Pick<MediaItem, \"variants\" | \"width\" | \"height\" | \"focal\" | \"alt\">>>(\"media\");\n const eigenLadder = mediaSeed?.[src];\n // 8.4: geen alt in het blok? Dan de beschrijving uit de mediabibliotheek (als die er is).\n alt = alt || eigenLadder?.alt || \"\";\n const ladder = eigenLadder?.variants?.sizes ?? [];\n const webp = eigenLadder?.variants?.webp;\n // Focuspunt (7.24): staat er in het beheer een focuspunt op deze foto, dan snijdt `object-fit:\n // cover` rond dát punt bij in plaats van rond het midden. Alleen als het blok zelf geen\n // object-position zet — een blok dat bewust \"top\" wil, wint.\n const focal = eigenLadder?.focal;\n const style = focal && !style_heeftPositie(styleProp)\n ? { ...styleProp, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` }\n : styleProp;\n // De rode wijzigings-highlight (visuele editor) zoekt op `data-xicms-edit=\"<veld>\"` binnen het\n // geselecteerde blok — voor foto's stond dat hook er nergens op (alleen tekstvelden hadden 'm),\n // dus een vervangen foto bleef onopgemerkt tussen de andere, wél gemarkeerde wijzigingen. Eén\n // plek hier lost het voor elk blok met een `<Img editField=…>` tegelijk op.\n const editHook = editField ? { \"data-xicms-edit\": editField } : undefined;\n const imageEl = ladder.length > 0 ? (\n <picture {...editHook}>\n {/* 8.3: geen losse <source> meer — die liet de browser altijd de volle WebP kiezen. */}\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={src}\n srcSet={bronSet(ladder, webp)}\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 {...editHook}\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 {...editHook}\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\ntype MediaSeed = Record<string, Pick<MediaItem, \"variants\" | \"width\" | \"height\" | \"focal\" | \"alt\">>;\n\n/**\n * Lichte beeldtag voor plekken waar `Img` te veel is (productkaarten, winkelwagen, voor/na,\n * lightbox) — 8.3. Gebruikt de ladder uit de media-seed als die er is, en is anders precies de\n * kale `<img>` die er al stond: géén next/image-terugval, dus geen betaalde transformaties.\n */\nexport function SeedImg({\n src,\n alt,\n sizes,\n className,\n style,\n priority,\n}: {\n src: string;\n alt: string;\n sizes?: string;\n className?: string;\n style?: CSSProperties;\n priority?: boolean;\n}) {\n const seed = useBlockData<MediaSeed>(\"media\")?.[src];\n const srcSet = seed ? bronSet(seed.variants?.sizes, seed.variants?.webp) : undefined;\n const focal = seed?.focal;\n const stijl = focal && !style_heeftPositie(style)\n ? { ...style, objectPosition: `${Math.round(focal.x * 100)}% ${Math.round(focal.y * 100)}%` }\n : style;\n return (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={src}\n srcSet={srcSet}\n sizes={srcSet ? sizes : undefined}\n alt={alt || seed?.alt || \"\"}\n width={seed?.width}\n height={seed?.height}\n loading={priority ? \"eager\" : \"lazy\"}\n decoding=\"async\"\n className={className}\n style={stijl}\n />\n );\n}\n\n/** De kleinste variant die minstens `minBreedte` breed is (bv. een avatar van 96px op 2x). */\nexport function useVariantVoor(src: string | undefined, minBreedte: number): string | undefined {\n const seed = useBlockData<MediaSeed>(\"media\");\n if (!src) return src;\n const ladder = [...(seed?.[src]?.variants?.sizes ?? [])].sort((a, b) => a.width - b.width);\n return ladder.find((v) => v.width >= minBreedte)?.url ?? src;\n}\n\n/** De grootste beschikbare versie (lightbox): volle WebP, anders de hoogste trede, anders het origineel. */\nexport function useGrootsteVariant(src: string): string {\n const v = useBlockData<MediaSeed>(\"media\")?.[src]?.variants;\n if (v?.webp?.url) return v.webp.url;\n const ladder = [...(v?.sizes ?? [])].sort((a, b) => b.width - a.width);\n return ladder[0]?.url ?? src;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Alt-tekst uit de mediabibliotheek invullen in rich text (8.10).
3
+ *
4
+ * Een foto die je in een tekstblok plakt of sleept krijgt `alt=""` in de HTML, en de melding zei
5
+ * "geef hem in Media een beschrijving". Dat hielp niet: de beschrijving in Media kwam nooit in die
6
+ * HTML terecht. Nu wel, bij het tonen: een `<img>` zonder alt krijgt de beschrijving uit de
7
+ * bibliotheek (dezelfde media-seed die blokken al gebruiken). Een eigen alt wint altijd.
8
+ */
9
+ export declare function vulAltIn(html: string, altVoor: (src: string) => string | undefined): string;
@@ -0,0 +1,17 @@
1
+ function vulAltIn(html, altVoor) {
2
+ if (!html || !html.includes("<img")) return html;
3
+ return html.replace(/<img\b[^>]*>/gi, (tag) => {
4
+ const src = /\ssrc\s*=\s*"([^"]*)"/i.exec(tag)?.[1];
5
+ if (!src) return tag;
6
+ const huidig = /\salt\s*=\s*"([^"]*)"/i.exec(tag);
7
+ if (huidig && huidig[1].trim()) return tag;
8
+ const alt = altVoor(src.replace(/&amp;/g, "&"));
9
+ if (!alt) return tag;
10
+ const veilig = alt.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
11
+ return huidig ? tag.replace(huidig[0], ` alt="${veilig}"`) : tag.replace(/<img\b/i, `<img alt="${veilig}"`);
12
+ });
13
+ }
14
+ export {
15
+ vulAltIn
16
+ };
17
+ //# sourceMappingURL=altInHtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/media/altInHtml.ts"],"sourcesContent":["/**\n * Alt-tekst uit de mediabibliotheek invullen in rich text (8.10).\n *\n * Een foto die je in een tekstblok plakt of sleept krijgt `alt=\"\"` in de HTML, en de melding zei\n * \"geef hem in Media een beschrijving\". Dat hielp niet: de beschrijving in Media kwam nooit in die\n * HTML terecht. Nu wel, bij het tonen: een `<img>` zonder alt krijgt de beschrijving uit de\n * bibliotheek (dezelfde media-seed die blokken al gebruiken). Een eigen alt wint altijd.\n */\nexport function vulAltIn(html: string, altVoor: (src: string) => string | undefined): string {\n if (!html || !html.includes(\"<img\")) return html;\n return html.replace(/<img\\b[^>]*>/gi, (tag) => {\n const src = /\\ssrc\\s*=\\s*\"([^\"]*)\"/i.exec(tag)?.[1];\n if (!src) return tag;\n const huidig = /\\salt\\s*=\\s*\"([^\"]*)\"/i.exec(tag);\n if (huidig && huidig[1].trim()) return tag;\n const alt = altVoor(src.replace(/&amp;/g, \"&\"));\n if (!alt) return tag;\n const veilig = alt.replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n return huidig ? tag.replace(huidig[0], ` alt=\"${veilig}\"`) : tag.replace(/<img\\b/i, `<img alt=\"${veilig}\"`);\n });\n}\n"],"mappings":"AAQO,SAAS,SAAS,MAAc,SAAsD;AAC3F,MAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAG,QAAO;AAC5C,SAAO,KAAK,QAAQ,kBAAkB,CAAC,QAAQ;AAC7C,UAAM,MAAM,yBAAyB,KAAK,GAAG,IAAI,CAAC;AAClD,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,SAAS,yBAAyB,KAAK,GAAG;AAChD,QAAI,UAAU,OAAO,CAAC,EAAE,KAAK,EAAG,QAAO;AACvC,UAAM,MAAM,QAAQ,IAAI,QAAQ,UAAU,GAAG,CAAC;AAC9C,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,SAAS,IAAI,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,QAAQ,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AAC5G,WAAO,SAAS,IAAI,QAAQ,OAAO,CAAC,GAAG,SAAS,MAAM,GAAG,IAAI,IAAI,QAAQ,WAAW,aAAa,MAAM,GAAG;AAAA,EAC5G,CAAC;AACH;","names":[]}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * De srcset voor een geüpload beeld (8.3). De upload-pipeline maakt een WebP-ladder (320…1280) én
3
+ * een volle WebP (tot 2048). Tot 8.3 stond die volle WebP als enige kandidaat in een
4
+ * `<source type="image/webp">` — en elke moderne browser kiest de `<source>`, dus de ladder op de
5
+ * `<img>` werd nooit gebruikt: een telefoon downloadde het 2048-beeld voor een kaartje van 300px.
6
+ * Nu één srcset met de hele ladder plus de volle versie als grootste trede; de browser kiest zelf.
7
+ */
8
+ export interface Variant {
9
+ url: string;
10
+ width: number;
11
+ }
12
+ export declare function bronSet(ladder: Variant[] | undefined, volle?: Variant | null): string | undefined;
@@ -0,0 +1,13 @@
1
+ function bronSet(ladder, volle) {
2
+ const treden = [...ladder ?? []].filter((v) => v.url && v.width > 0);
3
+ const grootste = treden.reduce((m, v) => Math.max(m, v.width), 0);
4
+ if (volle?.url && volle.width > grootste) treden.push(volle);
5
+ if (treden.length === 0) return void 0;
6
+ const uniek = /* @__PURE__ */ new Map();
7
+ for (const v of treden) if (!uniek.has(v.width)) uniek.set(v.width, v);
8
+ return [...uniek.values()].sort((a, b) => a.width - b.width).map((v) => `${v.url} ${v.width}w`).join(", ");
9
+ }
10
+ export {
11
+ bronSet
12
+ };
13
+ //# sourceMappingURL=srcset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/media/srcset.ts"],"sourcesContent":["/**\n * De srcset voor een geüpload beeld (8.3). De upload-pipeline maakt een WebP-ladder (320…1280) én\n * een volle WebP (tot 2048). Tot 8.3 stond die volle WebP als enige kandidaat in een\n * `<source type=\"image/webp\">` — en elke moderne browser kiest de `<source>`, dus de ladder op de\n * `<img>` werd nooit gebruikt: een telefoon downloadde het 2048-beeld voor een kaartje van 300px.\n * Nu één srcset met de hele ladder plus de volle versie als grootste trede; de browser kiest zelf.\n */\nexport interface Variant {\n url: string;\n width: number;\n}\n\nexport function bronSet(ladder: Variant[] | undefined, volle?: Variant | null): string | undefined {\n const treden = [...(ladder ?? [])].filter((v) => v.url && v.width > 0);\n const grootste = treden.reduce((m, v) => Math.max(m, v.width), 0);\n if (volle?.url && volle.width > grootste) treden.push(volle);\n if (treden.length === 0) return undefined;\n const uniek = new Map<number, Variant>();\n for (const v of treden) if (!uniek.has(v.width)) uniek.set(v.width, v);\n return [...uniek.values()]\n .sort((a, b) => a.width - b.width)\n .map((v) => `${v.url} ${v.width}w`)\n .join(\", \");\n}\n"],"mappings":"AAYO,SAAS,QAAQ,QAA+B,OAA4C;AACjG,QAAM,SAAS,CAAC,GAAI,UAAU,CAAC,CAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC;AACrE,QAAM,WAAW,OAAO,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,EAAE,KAAK,GAAG,CAAC;AAChE,MAAI,OAAO,OAAO,MAAM,QAAQ,SAAU,QAAO,KAAK,KAAK;AAC3D,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAM,QAAQ,oBAAI,IAAqB;AACvC,aAAW,KAAK,OAAQ,KAAI,CAAC,MAAM,IAAI,EAAE,KAAK,EAAG,OAAM,IAAI,EAAE,OAAO,CAAC;AACrE,SAAO,CAAC,GAAG,MAAM,OAAO,CAAC,EACtB,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,GAAG,EACjC,KAAK,IAAI;AACd;","names":[]}
@@ -27,6 +27,7 @@ export declare const videoModule: ModuleDefinition;
27
27
  export declare const bannerModule: ModuleDefinition;
28
28
  export declare const contactModule: ModuleDefinition;
29
29
  export declare const newsletterModule: ModuleDefinition;
30
+ export declare const verhuurModule: ModuleDefinition;
30
31
  export declare const bookingModule: ModuleDefinition;
31
32
  export declare const accordionModule: ModuleDefinition;
32
33
  export declare const embedModule: ModuleDefinition;