@xiwebdesign/xicms-core 7.14.0 → 7.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 (263) hide show
  1. package/dist/admin/AdminApp.js +4 -3
  2. package/dist/admin/AdminApp.js.map +1 -1
  3. package/dist/admin/dashboard/DashboardPanel.d.ts +3 -0
  4. package/dist/admin/dashboard/DashboardPanel.js +316 -0
  5. package/dist/admin/dashboard/DashboardPanel.js.map +1 -0
  6. package/dist/admin/navContext.d.ts +14 -0
  7. package/dist/admin/navContext.js +12 -0
  8. package/dist/admin/navContext.js.map +1 -0
  9. package/dist/admin/pages/PagesPanel.js +38 -12
  10. package/dist/admin/pages/PagesPanel.js.map +1 -1
  11. package/dist/admin/panelRegistry.js +4 -0
  12. package/dist/admin/panelRegistry.js.map +1 -1
  13. package/dist/admin/ui/PanelIcon.js +5 -0
  14. package/dist/admin/ui/PanelIcon.js.map +1 -1
  15. package/dist/admin/visual/VisualEditor.js +56 -20
  16. package/dist/admin/visual/VisualEditor.js.map +1 -1
  17. package/dist/admin.css +1 -1
  18. package/dist/auth/AuthProvider.js +8 -1
  19. package/dist/auth/AuthProvider.js.map +1 -1
  20. package/dist/blocks/builtins/Accordion.d.ts +1 -2
  21. package/dist/blocks/builtins/Accordion.js +2 -39
  22. package/dist/blocks/builtins/Accordion.js.map +1 -1
  23. package/dist/blocks/builtins/AccordionRender.d.ts +7 -0
  24. package/dist/blocks/builtins/AccordionRender.js +42 -0
  25. package/dist/blocks/builtins/AccordionRender.js.map +1 -0
  26. package/dist/blocks/builtins/Agenda.d.ts +1 -29
  27. package/dist/blocks/builtins/Agenda.js +3 -94
  28. package/dist/blocks/builtins/Agenda.js.map +1 -1
  29. package/dist/blocks/builtins/AgendaRender.d.ts +34 -0
  30. package/dist/blocks/builtins/AgendaRender.js +96 -0
  31. package/dist/blocks/builtins/AgendaRender.js.map +1 -0
  32. package/dist/blocks/builtins/Banner.d.ts +1 -2
  33. package/dist/blocks/builtins/Banner.js +2 -32
  34. package/dist/blocks/builtins/Banner.js.map +1 -1
  35. package/dist/blocks/builtins/BannerRender.d.ts +11 -0
  36. package/dist/blocks/builtins/BannerRender.js +35 -0
  37. package/dist/blocks/builtins/BannerRender.js.map +1 -0
  38. package/dist/blocks/builtins/BeforeAfter.d.ts +1 -2
  39. package/dist/blocks/builtins/BeforeAfter.js +2 -31
  40. package/dist/blocks/builtins/BeforeAfter.js.map +1 -1
  41. package/dist/blocks/builtins/BeforeAfterRender.d.ts +11 -0
  42. package/dist/blocks/builtins/BeforeAfterRender.js +34 -0
  43. package/dist/blocks/builtins/BeforeAfterRender.js.map +1 -0
  44. package/dist/blocks/builtins/Booking.d.ts +1 -2
  45. package/dist/blocks/builtins/Booking.js +2 -14
  46. package/dist/blocks/builtins/Booking.js.map +1 -1
  47. package/dist/blocks/builtins/BookingRender.d.ts +8 -0
  48. package/dist/blocks/builtins/BookingRender.js +17 -0
  49. package/dist/blocks/builtins/BookingRender.js.map +1 -0
  50. package/dist/blocks/builtins/Breadcrumbs.d.ts +1 -10
  51. package/dist/blocks/builtins/Breadcrumbs.js +3 -69
  52. package/dist/blocks/builtins/Breadcrumbs.js.map +1 -1
  53. package/dist/blocks/builtins/BreadcrumbsRender.d.ts +18 -0
  54. package/dist/blocks/builtins/BreadcrumbsRender.js +71 -0
  55. package/dist/blocks/builtins/BreadcrumbsRender.js.map +1 -0
  56. package/dist/blocks/builtins/Cards.d.ts +1 -2
  57. package/dist/blocks/builtins/Cards.js +2 -74
  58. package/dist/blocks/builtins/Cards.js.map +1 -1
  59. package/dist/blocks/builtins/CardsRender.d.ts +7 -0
  60. package/dist/blocks/builtins/CardsRender.js +77 -0
  61. package/dist/blocks/builtins/CardsRender.js.map +1 -0
  62. package/dist/blocks/builtins/Columns.d.ts +1 -2
  63. package/dist/blocks/builtins/Columns.js +2 -9
  64. package/dist/blocks/builtins/Columns.js.map +1 -1
  65. package/dist/blocks/builtins/ColumnsRender.d.ts +6 -0
  66. package/dist/blocks/builtins/ColumnsRender.js +12 -0
  67. package/dist/blocks/builtins/ColumnsRender.js.map +1 -0
  68. package/dist/blocks/builtins/Contact.d.ts +2 -7
  69. package/dist/blocks/builtins/Contact.js +3 -102
  70. package/dist/blocks/builtins/Contact.js.map +1 -1
  71. package/dist/blocks/builtins/ContactRender.d.ts +9 -0
  72. package/dist/blocks/builtins/ContactRender.js +105 -0
  73. package/dist/blocks/builtins/ContactRender.js.map +1 -0
  74. package/dist/blocks/builtins/Countdown.d.ts +1 -18
  75. package/dist/blocks/builtins/Countdown.js +3 -53
  76. package/dist/blocks/builtins/Countdown.js.map +1 -1
  77. package/dist/blocks/builtins/CountdownRender.d.ts +22 -0
  78. package/dist/blocks/builtins/CountdownRender.js +61 -0
  79. package/dist/blocks/builtins/CountdownRender.js.map +1 -0
  80. package/dist/blocks/builtins/Cta.d.ts +1 -2
  81. package/dist/blocks/builtins/Cta.js +2 -39
  82. package/dist/blocks/builtins/Cta.js.map +1 -1
  83. package/dist/blocks/builtins/CtaRender.d.ts +6 -0
  84. package/dist/blocks/builtins/CtaRender.js +42 -0
  85. package/dist/blocks/builtins/CtaRender.js.map +1 -0
  86. package/dist/blocks/builtins/Downloads.d.ts +1 -2
  87. package/dist/blocks/builtins/Downloads.js +2 -16
  88. package/dist/blocks/builtins/Downloads.js.map +1 -1
  89. package/dist/blocks/builtins/DownloadsRender.d.ts +7 -0
  90. package/dist/blocks/builtins/DownloadsRender.js +19 -0
  91. package/dist/blocks/builtins/DownloadsRender.js.map +1 -0
  92. package/dist/blocks/builtins/Elements.d.ts +3 -4
  93. package/dist/blocks/builtins/Elements.js +4 -38
  94. package/dist/blocks/builtins/Elements.js.map +1 -1
  95. package/dist/blocks/builtins/ElementsRender.d.ts +10 -0
  96. package/dist/blocks/builtins/ElementsRender.js +41 -0
  97. package/dist/blocks/builtins/ElementsRender.js.map +1 -0
  98. package/dist/blocks/builtins/Embed.d.ts +1 -2
  99. package/dist/blocks/builtins/Embed.js +2 -19
  100. package/dist/blocks/builtins/Embed.js.map +1 -1
  101. package/dist/blocks/builtins/EmbedRender.d.ts +7 -0
  102. package/dist/blocks/builtins/EmbedRender.js +22 -0
  103. package/dist/blocks/builtins/EmbedRender.js.map +1 -0
  104. package/dist/blocks/builtins/Faq.d.ts +1 -6
  105. package/dist/blocks/builtins/Faq.js +3 -103
  106. package/dist/blocks/builtins/Faq.js.map +1 -1
  107. package/dist/blocks/builtins/FaqRender.d.ts +10 -0
  108. package/dist/blocks/builtins/FaqRender.js +105 -0
  109. package/dist/blocks/builtins/FaqRender.js.map +1 -0
  110. package/dist/blocks/builtins/FormBlock.d.ts +1 -2
  111. package/dist/blocks/builtins/FormBlock.js +2 -222
  112. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  113. package/dist/blocks/builtins/FormBlockRender.d.ts +6 -0
  114. package/dist/blocks/builtins/FormBlockRender.js +225 -0
  115. package/dist/blocks/builtins/FormBlockRender.js.map +1 -0
  116. package/dist/blocks/builtins/Gallery.d.ts +1 -23
  117. package/dist/blocks/builtins/Gallery.js +2 -164
  118. package/dist/blocks/builtins/Gallery.js.map +1 -1
  119. package/dist/blocks/builtins/GalleryRender.d.ts +25 -0
  120. package/dist/blocks/builtins/GalleryRender.js +167 -0
  121. package/dist/blocks/builtins/GalleryRender.js.map +1 -0
  122. package/dist/blocks/builtins/Hero.d.ts +1 -2
  123. package/dist/blocks/builtins/Hero.js +2 -73
  124. package/dist/blocks/builtins/Hero.js.map +1 -1
  125. package/dist/blocks/builtins/HeroRender.d.ts +6 -0
  126. package/dist/blocks/builtins/HeroRender.js +76 -0
  127. package/dist/blocks/builtins/HeroRender.js.map +1 -0
  128. package/dist/blocks/builtins/ImageBlock.d.ts +1 -2
  129. package/dist/blocks/builtins/ImageBlock.js +2 -22
  130. package/dist/blocks/builtins/ImageBlock.js.map +1 -1
  131. package/dist/blocks/builtins/ImageBlockRender.d.ts +6 -0
  132. package/dist/blocks/builtins/ImageBlockRender.js +25 -0
  133. package/dist/blocks/builtins/ImageBlockRender.js.map +1 -0
  134. package/dist/blocks/builtins/Jobs.d.ts +1 -15
  135. package/dist/blocks/builtins/Jobs.js +3 -69
  136. package/dist/blocks/builtins/Jobs.js.map +1 -1
  137. package/dist/blocks/builtins/JobsRender.d.ts +17 -0
  138. package/dist/blocks/builtins/JobsRender.js +71 -0
  139. package/dist/blocks/builtins/JobsRender.js.map +1 -0
  140. package/dist/blocks/builtins/Layout.d.ts +2 -3
  141. package/dist/blocks/builtins/Layout.js +3 -39
  142. package/dist/blocks/builtins/Layout.js.map +1 -1
  143. package/dist/blocks/builtins/LayoutRender.d.ts +8 -0
  144. package/dist/blocks/builtins/LayoutRender.js +42 -0
  145. package/dist/blocks/builtins/LayoutRender.js.map +1 -0
  146. package/dist/blocks/builtins/MapBlock.d.ts +1 -2
  147. package/dist/blocks/builtins/MapBlock.js +2 -45
  148. package/dist/blocks/builtins/MapBlock.js.map +1 -1
  149. package/dist/blocks/builtins/MapBlockRender.d.ts +6 -0
  150. package/dist/blocks/builtins/MapBlockRender.js +48 -0
  151. package/dist/blocks/builtins/MapBlockRender.js.map +1 -0
  152. package/dist/blocks/builtins/NewsListBlock.d.ts +1 -2
  153. package/dist/blocks/builtins/NewsListBlock.js +2 -11
  154. package/dist/blocks/builtins/NewsListBlock.js.map +1 -1
  155. package/dist/blocks/builtins/NewsListBlockRender.d.ts +6 -0
  156. package/dist/blocks/builtins/NewsListBlockRender.js +14 -0
  157. package/dist/blocks/builtins/NewsListBlockRender.js.map +1 -0
  158. package/dist/blocks/builtins/Newsletter.d.ts +1 -2
  159. package/dist/blocks/builtins/Newsletter.js +2 -89
  160. package/dist/blocks/builtins/Newsletter.js.map +1 -1
  161. package/dist/blocks/builtins/NewsletterRender.d.ts +13 -0
  162. package/dist/blocks/builtins/NewsletterRender.js +92 -0
  163. package/dist/blocks/builtins/NewsletterRender.js.map +1 -0
  164. package/dist/blocks/builtins/OpeningHours.d.ts +1 -2
  165. package/dist/blocks/builtins/OpeningHours.js +2 -28
  166. package/dist/blocks/builtins/OpeningHours.js.map +1 -1
  167. package/dist/blocks/builtins/OpeningHoursRender.d.ts +7 -0
  168. package/dist/blocks/builtins/OpeningHoursRender.js +31 -0
  169. package/dist/blocks/builtins/OpeningHoursRender.js.map +1 -0
  170. package/dist/blocks/builtins/PdfBlock.d.ts +1 -2
  171. package/dist/blocks/builtins/PdfBlock.js +2 -76
  172. package/dist/blocks/builtins/PdfBlock.js.map +1 -1
  173. package/dist/blocks/builtins/PdfBlockRender.d.ts +11 -0
  174. package/dist/blocks/builtins/PdfBlockRender.js +79 -0
  175. package/dist/blocks/builtins/PdfBlockRender.js.map +1 -0
  176. package/dist/blocks/builtins/Portfolio.d.ts +1 -2
  177. package/dist/blocks/builtins/Portfolio.js +2 -75
  178. package/dist/blocks/builtins/Portfolio.js.map +1 -1
  179. package/dist/blocks/builtins/PortfolioRender.d.ts +7 -0
  180. package/dist/blocks/builtins/PortfolioRender.js +78 -0
  181. package/dist/blocks/builtins/PortfolioRender.js.map +1 -0
  182. package/dist/blocks/builtins/Pricing.d.ts +1 -8
  183. package/dist/blocks/builtins/Pricing.js +3 -73
  184. package/dist/blocks/builtins/Pricing.js.map +1 -1
  185. package/dist/blocks/builtins/PricingRender.d.ts +10 -0
  186. package/dist/blocks/builtins/PricingRender.js +75 -0
  187. package/dist/blocks/builtins/PricingRender.js.map +1 -0
  188. package/dist/blocks/builtins/RichTextBlock.d.ts +1 -2
  189. package/dist/blocks/builtins/RichTextBlock.js +2 -5
  190. package/dist/blocks/builtins/RichTextBlock.js.map +1 -1
  191. package/dist/blocks/builtins/RichTextBlockRender.d.ts +6 -0
  192. package/dist/blocks/builtins/RichTextBlockRender.js +8 -0
  193. package/dist/blocks/builtins/RichTextBlockRender.js.map +1 -0
  194. package/dist/blocks/builtins/Search.d.ts +1 -2
  195. package/dist/blocks/builtins/Search.js +2 -80
  196. package/dist/blocks/builtins/Search.js.map +1 -1
  197. package/dist/blocks/builtins/SearchRender.d.ts +7 -0
  198. package/dist/blocks/builtins/SearchRender.js +83 -0
  199. package/dist/blocks/builtins/SearchRender.js.map +1 -0
  200. package/dist/blocks/builtins/Sections.d.ts +4 -5
  201. package/dist/blocks/builtins/Sections.js +5 -142
  202. package/dist/blocks/builtins/Sections.js.map +1 -1
  203. package/dist/blocks/builtins/SectionsRender.d.ts +12 -0
  204. package/dist/blocks/builtins/SectionsRender.js +145 -0
  205. package/dist/blocks/builtins/SectionsRender.js.map +1 -0
  206. package/dist/blocks/builtins/Slider.d.ts +1 -2
  207. package/dist/blocks/builtins/Slider.js +2 -134
  208. package/dist/blocks/builtins/Slider.js.map +1 -1
  209. package/dist/blocks/builtins/SliderRender.d.ts +7 -0
  210. package/dist/blocks/builtins/SliderRender.js +137 -0
  211. package/dist/blocks/builtins/SliderRender.js.map +1 -0
  212. package/dist/blocks/builtins/Spacer.d.ts +1 -2
  213. package/dist/blocks/builtins/Spacer.js +2 -6
  214. package/dist/blocks/builtins/Spacer.js.map +1 -1
  215. package/dist/blocks/builtins/SpacerRender.d.ts +6 -0
  216. package/dist/blocks/builtins/SpacerRender.js +9 -0
  217. package/dist/blocks/builtins/SpacerRender.js.map +1 -0
  218. package/dist/blocks/builtins/SymbolRef.d.ts +1 -2
  219. package/dist/blocks/builtins/SymbolRef.js +2 -76
  220. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  221. package/dist/blocks/builtins/SymbolRefRender.d.ts +13 -0
  222. package/dist/blocks/builtins/SymbolRefRender.js +79 -0
  223. package/dist/blocks/builtins/SymbolRefRender.js.map +1 -0
  224. package/dist/blocks/builtins/Team.d.ts +1 -2
  225. package/dist/blocks/builtins/Team.js +2 -55
  226. package/dist/blocks/builtins/Team.js.map +1 -1
  227. package/dist/blocks/builtins/TeamRender.d.ts +7 -0
  228. package/dist/blocks/builtins/TeamRender.js +58 -0
  229. package/dist/blocks/builtins/TeamRender.js.map +1 -0
  230. package/dist/blocks/builtins/Testimonials.d.ts +1 -8
  231. package/dist/blocks/builtins/Testimonials.js +2 -69
  232. package/dist/blocks/builtins/Testimonials.js.map +1 -1
  233. package/dist/blocks/builtins/TestimonialsRender.d.ts +10 -0
  234. package/dist/blocks/builtins/TestimonialsRender.js +72 -0
  235. package/dist/blocks/builtins/TestimonialsRender.js.map +1 -0
  236. package/dist/blocks/builtins/Video.d.ts +1 -9
  237. package/dist/blocks/builtins/Video.js +2 -64
  238. package/dist/blocks/builtins/Video.js.map +1 -1
  239. package/dist/blocks/builtins/VideoRender.d.ts +10 -0
  240. package/dist/blocks/builtins/VideoRender.js +67 -0
  241. package/dist/blocks/builtins/VideoRender.js.map +1 -0
  242. package/dist/blocks/builtins/WhatsappChat.d.ts +1 -8
  243. package/dist/blocks/builtins/WhatsappChat.js +2 -37
  244. package/dist/blocks/builtins/WhatsappChat.js.map +1 -1
  245. package/dist/blocks/builtins/WhatsappChatRender.d.ts +17 -0
  246. package/dist/blocks/builtins/WhatsappChatRender.js +40 -0
  247. package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -0
  248. package/dist/blocks/builtins/Widgets.d.ts +12 -13
  249. package/dist/blocks/builtins/Widgets.js +13 -175
  250. package/dist/blocks/builtins/Widgets.js.map +1 -1
  251. package/dist/blocks/builtins/WidgetsRender.d.ts +34 -0
  252. package/dist/blocks/builtins/WidgetsRender.js +178 -0
  253. package/dist/blocks/builtins/WidgetsRender.js.map +1 -0
  254. package/dist/components/barOffset.js +1 -1
  255. package/dist/components/barOffset.js.map +1 -1
  256. package/dist/modules/contract.js +2 -1
  257. package/dist/modules/contract.js.map +1 -1
  258. package/dist/schema/settings.d.ts +4 -0
  259. package/dist/schema/settings.js +10 -1
  260. package/dist/schema/settings.js.map +1 -1
  261. package/dist/styles.css +93 -19
  262. package/dist/version.js +1 -1
  263. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/PdfBlock.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport { Onvoltooid } from \"./lib/Onvoltooid\";\nimport type { PdfBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { VStack, HStack, Heading, Text } from \"../ui\";\n\ntype Pdf = z.infer<typeof PdfBlockSchema>;\n\n/**\n * Inline PDF-lezer voor bv. menukaarten. Eén of meer PDF's; bij meerdere verschijnen tabs\n * (lunchkaart/dinerkaart/wijnkaart…). De klant vervangt de PDF's zelf in de editor (URL of\n * media-upload). In de editor (mode=\"preview\") tonen we een lichte placeholder i.p.v. de iframe.\n */\nfunction PdfRender({ block, mode }: BlockRenderProps<Pdf>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op url.\n const docs = (block.documents ?? []).map((d, i) => ({ ...d, _i: i })).filter((d) => d.url);\n const [active, setActive] = useState(0);\n if (docs.length === 0 && !block.heading) return null;\n const current = docs[Math.min(active, docs.length - 1)];\n\n return (\n <VStack gap={3} as=\"section\">\n {block.heading && <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>}\n\n {/* Gewone toggle-knoppen: een echt tabs-patroon vereist tabpanel-koppeling + pijltjesnavigatie. */}\n {docs.length > 1 && (\n <HStack gap={1} style={{ flexWrap: \"wrap\" }} role=\"group\" aria-label=\"Kies document\">\n {docs.map((d, i) => (\n <button\n key={i}\n type=\"button\"\n aria-pressed={i === active}\n onClick={() => setActive(i)}\n style={{\n padding: \"0.4rem 0.9rem\",\n borderRadius: \"var(--radius-full, 999px)\",\n border: \"1px solid var(--color-border)\",\n cursor: \"pointer\",\n fontWeight: 600,\n fontSize: \"0.9rem\",\n background: i === active ? \"var(--color-accent)\" : \"transparent\",\n color: i === active ? \"var(--color-on-accent)\" : \"var(--color-text-primary)\",\n }}\n >\n <span data-xicms-edit={`documents.${d._i}.label`}>{d.label || `Document ${i + 1}`}</span>\n </button>\n ))}\n </HStack>\n )}\n\n {current ? (\n <div\n style={{\n borderRadius: \"var(--radius-container)\",\n overflow: \"hidden\",\n border: \"1px solid var(--color-border)\",\n background: \"var(--color-background-muted, rgba(0,0,0,0.03))\",\n }}\n >\n {mode === \"preview\" ? (\n <div className=\"xicms-embed-consent xicms-embed-preview\" role=\"note\" style={{ height: Math.min(block.height, 360) }}>\n <p>📄 PDF “{current.label || \"document\"}” — wordt op de live site geladen (niet in de editor).</p>\n </div>\n ) : (\n // <object> i.p.v. <iframe>: browsers zonder inline PDF-viewer (veel mobiel) tonen\n // dan de fallback-inhoud met een open/download-knop i.p.v. een leeg vlak.\n <object\n data={`${current.url}#view=FitH`}\n type=\"application/pdf\"\n aria-label={current.label || block.heading || \"PDF\"}\n style={{ border: 0, width: \"100%\", height: block.height, display: \"block\" }}\n >\n <div style={{ display: \"grid\", placeItems: \"center\", gap: \"0.6rem\", padding: \"2.5rem 1rem\", textAlign: \"center\" }}>\n <Text color=\"secondary\">Je browser kan deze PDF niet inline tonen.</Text>\n <a className=\"xicms-btn\" href={current.url} target=\"_blank\" rel=\"noopener noreferrer\">\n 📄 Open “{current.label || \"PDF\"}”\n </a>\n </div>\n </object>\n )}\n </div>\n ) : (\n <Onvoltooid>Dit PDF-blok is nog leeg: kies een document via het tandwiel. Bezoekers zien dit blok niet.</Onvoltooid>\n )}\n\n {block.showDownload && current && (\n <a href={current.url} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: \"var(--color-accent)\" }}>\n <Text type=\"label\">↗ Open “{current.label || \"PDF\"}” in een nieuw tabblad</Text>\n </a>\n )}\n </VStack>\n );\n}\n\nexport const pdfBlock = defineBlock({\n type: \"pdf\",\n Render: PdfRender,\n label: \"PDF-lezer (menu/document)\",\n icon: \"📄\",\n defaults: () => ({\n heading: \"Onze kaart\",\n documents: [\n { label: \"Lunchkaart\", url: \"\" },\n { label: \"Dinerkaart\", url: \"\" },\n ],\n height: 820,\n showDownload: true,\n }),\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/PdfBlock.tsx"],"sourcesContent":["import dynamic from \"next/dynamic\";\nimport { defineBlock } from \"../registry\";\n\nexport const pdfBlock = defineBlock({\n type: \"pdf\",\n Render: dynamic(() => import(\"./PdfBlockRender\").then((m) => m.PdfRender)),\n label: \"PDF-lezer (menu/document)\",\n icon: \"📄\",\n defaults: () => ({\n heading: \"Onze kaart\",\n documents: [\n { label: \"Lunchkaart\", url: \"\" },\n { label: \"Dinerkaart\", url: \"\" },\n ],\n height: 820,\n showDownload: true,\n }),\n});\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,mBAAmB;AAErB,MAAM,WAAW,YAAY;AAAA,EAClC,MAAM;AAAA,EACN,QAAQ,QAAQ,MAAM,OAAO,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC;AAAA,EACzE,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO;AAAA,IACf,SAAS;AAAA,IACT,WAAW;AAAA,MACT,EAAE,OAAO,cAAc,KAAK,GAAG;AAAA,MAC/B,EAAE,OAAO,cAAc,KAAK,GAAG;AAAA,IACjC;AAAA,IACA,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AACF,CAAC;","names":[]}
@@ -0,0 +1,11 @@
1
+ import type { z } from "zod";
2
+ import type { PdfBlockSchema } from "../../schema/blocks.js";
3
+ import type { BlockRenderProps } from "../registry.js";
4
+ type Pdf = z.infer<typeof PdfBlockSchema>;
5
+ /**
6
+ * Inline PDF-lezer voor bv. menukaarten. Eén of meer PDF's; bij meerdere verschijnen tabs
7
+ * (lunchkaart/dinerkaart/wijnkaart…). De klant vervangt de PDF's zelf in de editor (URL of
8
+ * media-upload). In de editor (mode="preview") tonen we een lichte placeholder i.p.v. de iframe.
9
+ */
10
+ export declare function PdfRender({ block, mode }: BlockRenderProps<Pdf>): import("react").JSX.Element | null;
11
+ export {};
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Onvoltooid } from "./lib/Onvoltooid.js";
5
+ import { VStack, HStack, Heading, Text } from "../ui/index.js";
6
+ function PdfRender({ block, mode }) {
7
+ const docs = (block.documents ?? []).map((d, i) => ({ ...d, _i: i })).filter((d) => d.url);
8
+ const [active, setActive] = useState(0);
9
+ if (docs.length === 0 && !block.heading) return null;
10
+ const current = docs[Math.min(active, docs.length - 1)];
11
+ return /* @__PURE__ */ jsxs(VStack, { gap: 3, as: "section", children: [
12
+ block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, "data-xicms-edit": "heading", children: block.heading }),
13
+ docs.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: 1, style: { flexWrap: "wrap" }, role: "group", "aria-label": "Kies document", children: docs.map((d, i) => /* @__PURE__ */ jsx(
14
+ "button",
15
+ {
16
+ type: "button",
17
+ "aria-pressed": i === active,
18
+ onClick: () => setActive(i),
19
+ style: {
20
+ padding: "0.4rem 0.9rem",
21
+ borderRadius: "var(--radius-full, 999px)",
22
+ border: "1px solid var(--color-border)",
23
+ cursor: "pointer",
24
+ fontWeight: 600,
25
+ fontSize: "0.9rem",
26
+ background: i === active ? "var(--color-accent)" : "transparent",
27
+ color: i === active ? "var(--color-on-accent)" : "var(--color-text-primary)"
28
+ },
29
+ children: /* @__PURE__ */ jsx("span", { "data-xicms-edit": `documents.${d._i}.label`, children: d.label || `Document ${i + 1}` })
30
+ },
31
+ i
32
+ )) }),
33
+ current ? /* @__PURE__ */ jsx(
34
+ "div",
35
+ {
36
+ style: {
37
+ borderRadius: "var(--radius-container)",
38
+ overflow: "hidden",
39
+ border: "1px solid var(--color-border)",
40
+ background: "var(--color-background-muted, rgba(0,0,0,0.03))"
41
+ },
42
+ children: mode === "preview" ? /* @__PURE__ */ jsx("div", { className: "xicms-embed-consent xicms-embed-preview", role: "note", style: { height: Math.min(block.height, 360) }, children: /* @__PURE__ */ jsxs("p", { children: [
43
+ "\u{1F4C4} PDF \u201C",
44
+ current.label || "document",
45
+ "\u201D \u2014 wordt op de live site geladen (niet in de editor)."
46
+ ] }) }) : (
47
+ // <object> i.p.v. <iframe>: browsers zonder inline PDF-viewer (veel mobiel) tonen
48
+ // dan de fallback-inhoud met een open/download-knop i.p.v. een leeg vlak.
49
+ /* @__PURE__ */ jsx(
50
+ "object",
51
+ {
52
+ data: `${current.url}#view=FitH`,
53
+ type: "application/pdf",
54
+ "aria-label": current.label || block.heading || "PDF",
55
+ style: { border: 0, width: "100%", height: block.height, display: "block" },
56
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "grid", placeItems: "center", gap: "0.6rem", padding: "2.5rem 1rem", textAlign: "center" }, children: [
57
+ /* @__PURE__ */ jsx(Text, { color: "secondary", children: "Je browser kan deze PDF niet inline tonen." }),
58
+ /* @__PURE__ */ jsxs("a", { className: "xicms-btn", href: current.url, target: "_blank", rel: "noopener noreferrer", children: [
59
+ "\u{1F4C4} Open \u201C",
60
+ current.label || "PDF",
61
+ "\u201D"
62
+ ] })
63
+ ] })
64
+ }
65
+ )
66
+ )
67
+ }
68
+ ) : /* @__PURE__ */ jsx(Onvoltooid, { children: "Dit PDF-blok is nog leeg: kies een document via het tandwiel. Bezoekers zien dit blok niet." }),
69
+ block.showDownload && current && /* @__PURE__ */ jsx("a", { href: current.url, target: "_blank", rel: "noopener noreferrer", style: { color: "var(--color-accent)" }, children: /* @__PURE__ */ jsxs(Text, { type: "label", children: [
70
+ "\u2197 Open \u201C",
71
+ current.label || "PDF",
72
+ "\u201D in een nieuw tabblad"
73
+ ] }) })
74
+ ] });
75
+ }
76
+ export {
77
+ PdfRender
78
+ };
79
+ //# sourceMappingURL=PdfBlockRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/builtins/PdfBlockRender.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport { Onvoltooid } from \"./lib/Onvoltooid\";\nimport type { PdfBlockSchema } from \"../../schema/blocks\";\nimport type { BlockRenderProps } from \"../registry\";\nimport { VStack, HStack, Heading, Text } from \"../ui\";\n\ntype Pdf = z.infer<typeof PdfBlockSchema>;\n\n/**\n * Inline PDF-lezer voor bv. menukaarten. Eén of meer PDF's; bij meerdere verschijnen tabs\n * (lunchkaart/dinerkaart/wijnkaart…). De klant vervangt de PDF's zelf in de editor (URL of\n * media-upload). In de editor (mode=\"preview\") tonen we een lichte placeholder i.p.v. de iframe.\n */\nexport function PdfRender({ block, mode }: BlockRenderProps<Pdf>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op url.\n const docs = (block.documents ?? []).map((d, i) => ({ ...d, _i: i })).filter((d) => d.url);\n const [active, setActive] = useState(0);\n if (docs.length === 0 && !block.heading) return null;\n const current = docs[Math.min(active, docs.length - 1)];\n\n return (\n <VStack gap={3} as=\"section\">\n {block.heading && <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>}\n\n {/* Gewone toggle-knoppen: een echt tabs-patroon vereist tabpanel-koppeling + pijltjesnavigatie. */}\n {docs.length > 1 && (\n <HStack gap={1} style={{ flexWrap: \"wrap\" }} role=\"group\" aria-label=\"Kies document\">\n {docs.map((d, i) => (\n <button\n key={i}\n type=\"button\"\n aria-pressed={i === active}\n onClick={() => setActive(i)}\n style={{\n padding: \"0.4rem 0.9rem\",\n borderRadius: \"var(--radius-full, 999px)\",\n border: \"1px solid var(--color-border)\",\n cursor: \"pointer\",\n fontWeight: 600,\n fontSize: \"0.9rem\",\n background: i === active ? \"var(--color-accent)\" : \"transparent\",\n color: i === active ? \"var(--color-on-accent)\" : \"var(--color-text-primary)\",\n }}\n >\n <span data-xicms-edit={`documents.${d._i}.label`}>{d.label || `Document ${i + 1}`}</span>\n </button>\n ))}\n </HStack>\n )}\n\n {current ? (\n <div\n style={{\n borderRadius: \"var(--radius-container)\",\n overflow: \"hidden\",\n border: \"1px solid var(--color-border)\",\n background: \"var(--color-background-muted, rgba(0,0,0,0.03))\",\n }}\n >\n {mode === \"preview\" ? (\n <div className=\"xicms-embed-consent xicms-embed-preview\" role=\"note\" style={{ height: Math.min(block.height, 360) }}>\n <p>📄 PDF “{current.label || \"document\"}” — wordt op de live site geladen (niet in de editor).</p>\n </div>\n ) : (\n // <object> i.p.v. <iframe>: browsers zonder inline PDF-viewer (veel mobiel) tonen\n // dan de fallback-inhoud met een open/download-knop i.p.v. een leeg vlak.\n <object\n data={`${current.url}#view=FitH`}\n type=\"application/pdf\"\n aria-label={current.label || block.heading || \"PDF\"}\n style={{ border: 0, width: \"100%\", height: block.height, display: \"block\" }}\n >\n <div style={{ display: \"grid\", placeItems: \"center\", gap: \"0.6rem\", padding: \"2.5rem 1rem\", textAlign: \"center\" }}>\n <Text color=\"secondary\">Je browser kan deze PDF niet inline tonen.</Text>\n <a className=\"xicms-btn\" href={current.url} target=\"_blank\" rel=\"noopener noreferrer\">\n 📄 Open “{current.label || \"PDF\"}”\n </a>\n </div>\n </object>\n )}\n </div>\n ) : (\n <Onvoltooid>Dit PDF-blok is nog leeg: kies een document via het tandwiel. Bezoekers zien dit blok niet.</Onvoltooid>\n )}\n\n {block.showDownload && current && (\n <a href={current.url} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: \"var(--color-accent)\" }}>\n <Text type=\"label\">↗ Open “{current.label || \"PDF\"}” in een nieuw tabblad</Text>\n </a>\n )}\n </VStack>\n );\n}\n"],"mappings":";AAyBwB,cAuCV,YAvCU;AAvBxB,SAAS,gBAAgB;AAEzB,SAAS,kBAAkB;AAG3B,SAAS,QAAQ,QAAQ,SAAS,YAAY;AASvC,SAAS,UAAU,EAAE,OAAO,KAAK,GAA0B;AAEhE,QAAM,QAAQ,MAAM,aAAa,CAAC,GAAG,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG;AACzF,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,CAAC;AACtC,MAAI,KAAK,WAAW,KAAK,CAAC,MAAM,QAAS,QAAO;AAChD,QAAM,UAAU,KAAK,KAAK,IAAI,QAAQ,KAAK,SAAS,CAAC,CAAC;AAEtD,SACE,qBAAC,UAAO,KAAK,GAAG,IAAG,WAChB;AAAA,UAAM,WAAW,oBAAC,WAAQ,OAAO,GAAG,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,IAG7E,KAAK,SAAS,KACb,oBAAC,UAAO,KAAK,GAAG,OAAO,EAAE,UAAU,OAAO,GAAG,MAAK,SAAQ,cAAW,iBAClE,eAAK,IAAI,CAAC,GAAG,MACZ;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,gBAAc,MAAM;AAAA,QACpB,SAAS,MAAM,UAAU,CAAC;AAAA,QAC1B,OAAO;AAAA,UACL,SAAS;AAAA,UACT,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,UAAU;AAAA,UACV,YAAY,MAAM,SAAS,wBAAwB;AAAA,UACnD,OAAO,MAAM,SAAS,2BAA2B;AAAA,QACnD;AAAA,QAEA,8BAAC,UAAK,mBAAiB,aAAa,EAAE,EAAE,UAAW,YAAE,SAAS,YAAY,IAAI,CAAC,IAAG;AAAA;AAAA,MAf7E;AAAA,IAgBP,CACD,GACH;AAAA,IAGD,UACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,cAAc;AAAA,UACd,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,YAAY;AAAA,QACd;AAAA,QAEC,mBAAS,YACR,oBAAC,SAAI,WAAU,2CAA0C,MAAK,QAAO,OAAO,EAAE,QAAQ,KAAK,IAAI,MAAM,QAAQ,GAAG,EAAE,GAChH,+BAAC,OAAE;AAAA;AAAA,UAAS,QAAQ,SAAS;AAAA,UAAW;AAAA,WAAsD,GAChG;AAAA;AAAA;AAAA,UAIA;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,GAAG,QAAQ,GAAG;AAAA,cACpB,MAAK;AAAA,cACL,cAAY,QAAQ,SAAS,MAAM,WAAW;AAAA,cAC9C,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,QAAQ,MAAM,QAAQ,SAAS,QAAQ;AAAA,cAE1E,+BAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,UAAU,SAAS,eAAe,WAAW,SAAS,GAC9G;AAAA,oCAAC,QAAK,OAAM,aAAY,wDAA0C;AAAA,gBAClE,qBAAC,OAAE,WAAU,aAAY,MAAM,QAAQ,KAAK,QAAO,UAAS,KAAI,uBAAsB;AAAA;AAAA,kBAC1E,QAAQ,SAAS;AAAA,kBAAM;AAAA,mBACnC;AAAA,iBACF;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IAEJ,IAEA,oBAAC,cAAW,yGAA2F;AAAA,IAGxG,MAAM,gBAAgB,WACrB,oBAAC,OAAE,MAAM,QAAQ,KAAK,QAAO,UAAS,KAAI,uBAAsB,OAAO,EAAE,OAAO,sBAAsB,GACpG,+BAAC,QAAK,MAAK,SAAQ;AAAA;AAAA,MAAS,QAAQ,SAAS;AAAA,MAAM;AAAA,OAAsB,GAC3E;AAAA,KAEJ;AAEJ;","names":[]}
@@ -1,2 +1 @@
1
- import type { z } from "zod";
2
- export declare const portfolioBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
1
+ export declare const portfolioBlock: import("../index.js").BlockDefinition<import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>;
@@ -1,81 +1,8 @@
1
- "use client";
2
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useRef, useState } from "react";
1
+ import dynamic from "next/dynamic";
4
2
  import { defineBlock } from "../registry.js";
5
- import { VStack, Grid, Heading, Text } from "../ui/index.js";
6
- import { Img } from "../../media/ImageContext.js";
7
- import { LightboxOverlay } from "./Gallery.js";
8
- function PortfolioRender({ block }) {
9
- const allItems = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.imageUrl);
10
- const [activeCat, setActiveCat] = useState(null);
11
- const [open, setOpen] = useState(false);
12
- const [index, setIndex] = useState(0);
13
- const triggerRef = useRef(null);
14
- const cats = block.showFilter ? [...new Set(allItems.map((i) => i.category).filter((c) => Boolean(c)))] : [];
15
- const items = activeCat ? allItems.filter((i) => i.category === activeCat) : allItems;
16
- if (allItems.length === 0) return null;
17
- const lightboxItems = items.map((i) => ({ url: i.imageUrl, alt: i.title, caption: i.description ? `${i.title} \u2014 ${i.description}` : i.title }));
18
- const close = () => {
19
- setOpen(false);
20
- triggerRef.current?.focus({ preventScroll: true });
21
- };
22
- return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-portfolio", children: [
23
- (block.heading || block.intro) && /* @__PURE__ */ jsxs(VStack, { gap: 1, hAlign: "center", style: { textAlign: "center" }, children: [
24
- block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, "data-xicms-edit": "heading", children: block.heading }),
25
- block.intro && /* @__PURE__ */ jsx(Text, { type: "large", color: "secondary", "data-xicms-edit": "intro", children: block.intro })
26
- ] }),
27
- cats.length > 0 && /* @__PURE__ */ jsxs("div", { className: "xicms-gallery__filter", role: "group", "aria-label": "Filter op categorie", style: { justifyContent: "center", marginBottom: 0 }, children: [
28
- /* @__PURE__ */ jsx("button", { type: "button", className: "xicms-gallery__filterbtn", "aria-pressed": activeCat === null, "data-active": activeCat === null ? "1" : "0", onClick: () => setActiveCat(null), children: "Alles" }),
29
- cats.map((c) => /* @__PURE__ */ jsx("button", { type: "button", className: "xicms-gallery__filterbtn", "aria-pressed": activeCat === c, "data-active": activeCat === c ? "1" : "0", onClick: () => setActiveCat(c), children: c }, c))
30
- ] }),
31
- /* @__PURE__ */ jsx(Grid, { columns: { minWidth: 240, max: block.columns }, gap: 3, children: items.map((it, idx) => {
32
- const media = /* @__PURE__ */ jsx(
33
- Img,
34
- {
35
- src: it.imageUrl,
36
- alt: it.title,
37
- width: 720,
38
- height: 540,
39
- sizes: "(max-width: 720px) 100vw, 33vw",
40
- style: { width: "100%", aspectRatio: "4 / 3", objectFit: "cover", display: "block" }
41
- }
42
- );
43
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
44
- media,
45
- /* @__PURE__ */ jsxs("div", { className: "xicms-portfolio__body", children: [
46
- it.category && /* @__PURE__ */ jsx("span", { className: "xicms-portfolio__cat", "data-xicms-edit": `items.${it._i}.category`, children: it.category }),
47
- /* @__PURE__ */ jsx("strong", { className: "xicms-portfolio__title", "data-xicms-edit": `items.${it._i}.title`, children: it.title }),
48
- it.description && /* @__PURE__ */ jsx("span", { className: "xicms-portfolio__desc", "data-xicms-edit": `items.${it._i}.description`, children: it.description })
49
- ] })
50
- ] });
51
- if (it.href) {
52
- return /* @__PURE__ */ jsx("a", { className: "xicms-portfolio__item", href: it.href, children: body }, idx);
53
- }
54
- if (block.lightbox) {
55
- return /* @__PURE__ */ jsx(
56
- "button",
57
- {
58
- type: "button",
59
- className: "xicms-portfolio__item",
60
- onClick: (e) => {
61
- triggerRef.current = e.currentTarget;
62
- setIndex(idx);
63
- setOpen(true);
64
- },
65
- "aria-label": `${it.title} vergroten`,
66
- children: body
67
- },
68
- idx
69
- );
70
- }
71
- return /* @__PURE__ */ jsx("div", { className: "xicms-portfolio__item", children: body }, idx);
72
- }) }),
73
- block.lightbox && open && /* @__PURE__ */ jsx(LightboxOverlay, { items: lightboxItems, index, onIndex: setIndex, onClose: close })
74
- ] });
75
- }
76
3
  const portfolioBlock = defineBlock({
77
4
  type: "portfolio",
78
- Render: PortfolioRender,
5
+ Render: dynamic(() => import("./PortfolioRender.js").then((m) => m.PortfolioRender)),
79
6
  label: "Portfolio / projecten",
80
7
  icon: "\u{1F5C2}\uFE0F",
81
8
  defaults: () => ({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Portfolio.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport type { z } from \"zod\";\nimport type { PortfolioBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { VStack, Grid, Heading, Text } from \"../ui\";\nimport { Img } from \"../../media/ImageContext\";\nimport { LightboxOverlay } from \"./Gallery\";\n\ntype Portfolio = z.infer<typeof PortfolioBlockSchema>;\n\n/** Projecten/cases-grid met categorie-filter en lightbox (hergebruikt de galerij-lightbox). */\nfunction PortfolioRender({ block }: BlockRenderProps<Portfolio>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op imageUrl én op de\n // gekozen categorie, dus de render-index wijst niet naar block.items[i].\n const allItems = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.imageUrl);\n const [activeCat, setActiveCat] = useState<string | null>(null);\n const [open, setOpen] = useState(false);\n const [index, setIndex] = useState(0);\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n\n const cats = block.showFilter ? [...new Set(allItems.map((i) => i.category).filter((c): c is string => Boolean(c)))] : [];\n const items = activeCat ? allItems.filter((i) => i.category === activeCat) : allItems;\n if (allItems.length === 0) return null;\n\n const lightboxItems = items.map((i) => ({ url: i.imageUrl, alt: i.title, caption: i.description ? `${i.title} — ${i.description}` : i.title }));\n const close = () => {\n setOpen(false);\n triggerRef.current?.focus({ preventScroll: true });\n };\n\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-portfolio\">\n {(block.heading || block.intro) && (\n <VStack gap={1} hAlign=\"center\" style={{ textAlign: \"center\" }}>\n {block.heading && <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>}\n {block.intro && <Text type=\"large\" color=\"secondary\" data-xicms-edit=\"intro\">{block.intro}</Text>}\n </VStack>\n )}\n {cats.length > 0 && (\n <div className=\"xicms-gallery__filter\" role=\"group\" aria-label=\"Filter op categorie\" style={{ justifyContent: \"center\", marginBottom: 0 }}>\n <button type=\"button\" className=\"xicms-gallery__filterbtn\" aria-pressed={activeCat === null} data-active={activeCat === null ? \"1\" : \"0\"} onClick={() => setActiveCat(null)}>\n Alles\n </button>\n {cats.map((c) => (\n <button key={c} type=\"button\" className=\"xicms-gallery__filterbtn\" aria-pressed={activeCat === c} data-active={activeCat === c ? \"1\" : \"0\"} onClick={() => setActiveCat(c)}>\n {c}\n </button>\n ))}\n </div>\n )}\n <Grid columns={{ minWidth: 240, max: block.columns }} gap={3}>\n {items.map((it, idx) => {\n const media = (\n <Img\n src={it.imageUrl}\n alt={it.title}\n width={720}\n height={540}\n sizes=\"(max-width: 720px) 100vw, 33vw\"\n style={{ width: \"100%\", aspectRatio: \"4 / 3\", objectFit: \"cover\", display: \"block\" }}\n />\n );\n const body = (\n <>\n {media}\n <div className=\"xicms-portfolio__body\">\n {it.category && <span className=\"xicms-portfolio__cat\" data-xicms-edit={`items.${it._i}.category`}>{it.category}</span>}\n <strong className=\"xicms-portfolio__title\" data-xicms-edit={`items.${it._i}.title`}>{it.title}</strong>\n {it.description && <span className=\"xicms-portfolio__desc\" data-xicms-edit={`items.${it._i}.description`}>{it.description}</span>}\n </div>\n </>\n );\n if (it.href) {\n return (\n <a key={idx} className=\"xicms-portfolio__item\" href={it.href}>\n {body}\n </a>\n );\n }\n if (block.lightbox) {\n return (\n <button\n key={idx}\n type=\"button\"\n className=\"xicms-portfolio__item\"\n onClick={(e) => { triggerRef.current = e.currentTarget; setIndex(idx); setOpen(true); }}\n aria-label={`${it.title} vergroten`}\n >\n {body}\n </button>\n );\n }\n return (\n <div key={idx} className=\"xicms-portfolio__item\">\n {body}\n </div>\n );\n })}\n </Grid>\n {block.lightbox && open && (\n <LightboxOverlay items={lightboxItems} index={index} onIndex={setIndex} onClose={close} />\n )}\n </VStack>\n );\n}\n\nexport const portfolioBlock = defineBlock({\n type: \"portfolio\",\n Render: PortfolioRender,\n label: \"Portfolio / projecten\",\n icon: \"🗂️\",\n defaults: () => ({\n heading: \"Ons werk\",\n columns: 3,\n items: [{ imageUrl: \"\", title: \"Project\", category: \"\" }],\n showFilter: true,\n lightbox: true,\n }),\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Portfolio.tsx"],"sourcesContent":["import dynamic from \"next/dynamic\";\nimport { defineBlock } from \"../registry\";\n\nexport const portfolioBlock = defineBlock({\n type: \"portfolio\",\n Render: dynamic(() => import(\"./PortfolioRender\").then((m) => m.PortfolioRender)),\n label: \"Portfolio / projecten\",\n icon: \"🗂️\",\n defaults: () => ({\n heading: \"Ons werk\",\n columns: 3,\n items: [{ imageUrl: \"\", title: \"Project\", category: \"\" }],\n showFilter: true,\n lightbox: true,\n }),\n});\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,mBAAmB;AAErB,MAAM,iBAAiB,YAAY;AAAA,EACxC,MAAM;AAAA,EACN,QAAQ,QAAQ,MAAM,OAAO,mBAAmB,EAAE,KAAK,CAAC,MAAM,EAAE,eAAe,CAAC;AAAA,EAChF,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO;AAAA,IACf,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO,CAAC,EAAE,UAAU,IAAI,OAAO,WAAW,UAAU,GAAG,CAAC;AAAA,IACxD,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ;AACF,CAAC;","names":[]}
@@ -0,0 +1,7 @@
1
+ import type { z } from "zod";
2
+ import type { PortfolioBlockSchema } from "../../schema/blocks.js";
3
+ import type { BlockRenderProps } from "../registry.js";
4
+ type Portfolio = z.infer<typeof PortfolioBlockSchema>;
5
+ /** Projecten/cases-grid met categorie-filter en lightbox (hergebruikt de galerij-lightbox). */
6
+ export declare function PortfolioRender({ block }: BlockRenderProps<Portfolio>): import("react").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useRef, useState } from "react";
4
+ import { VStack, Grid, Heading, Text } from "../ui/index.js";
5
+ import { Img } from "../../media/ImageContext.js";
6
+ import { LightboxOverlay } from "./GalleryRender.js";
7
+ function PortfolioRender({ block }) {
8
+ const allItems = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.imageUrl);
9
+ const [activeCat, setActiveCat] = useState(null);
10
+ const [open, setOpen] = useState(false);
11
+ const [index, setIndex] = useState(0);
12
+ const triggerRef = useRef(null);
13
+ const cats = block.showFilter ? [...new Set(allItems.map((i) => i.category).filter((c) => Boolean(c)))] : [];
14
+ const items = activeCat ? allItems.filter((i) => i.category === activeCat) : allItems;
15
+ if (allItems.length === 0) return null;
16
+ const lightboxItems = items.map((i) => ({ url: i.imageUrl, alt: i.title, caption: i.description ? `${i.title} \u2014 ${i.description}` : i.title }));
17
+ const close = () => {
18
+ setOpen(false);
19
+ triggerRef.current?.focus({ preventScroll: true });
20
+ };
21
+ return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-portfolio", children: [
22
+ (block.heading || block.intro) && /* @__PURE__ */ jsxs(VStack, { gap: 1, hAlign: "center", style: { textAlign: "center" }, children: [
23
+ block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, "data-xicms-edit": "heading", children: block.heading }),
24
+ block.intro && /* @__PURE__ */ jsx(Text, { type: "large", color: "secondary", "data-xicms-edit": "intro", children: block.intro })
25
+ ] }),
26
+ cats.length > 0 && /* @__PURE__ */ jsxs("div", { className: "xicms-gallery__filter", role: "group", "aria-label": "Filter op categorie", style: { justifyContent: "center", marginBottom: 0 }, children: [
27
+ /* @__PURE__ */ jsx("button", { type: "button", className: "xicms-gallery__filterbtn", "aria-pressed": activeCat === null, "data-active": activeCat === null ? "1" : "0", onClick: () => setActiveCat(null), children: "Alles" }),
28
+ cats.map((c) => /* @__PURE__ */ jsx("button", { type: "button", className: "xicms-gallery__filterbtn", "aria-pressed": activeCat === c, "data-active": activeCat === c ? "1" : "0", onClick: () => setActiveCat(c), children: c }, c))
29
+ ] }),
30
+ /* @__PURE__ */ jsx(Grid, { columns: { minWidth: 240, max: block.columns }, gap: 3, children: items.map((it, idx) => {
31
+ const media = /* @__PURE__ */ jsx(
32
+ Img,
33
+ {
34
+ src: it.imageUrl,
35
+ alt: it.title,
36
+ width: 720,
37
+ height: 540,
38
+ sizes: "(max-width: 720px) 100vw, 33vw",
39
+ style: { width: "100%", aspectRatio: "4 / 3", objectFit: "cover", display: "block" }
40
+ }
41
+ );
42
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
43
+ media,
44
+ /* @__PURE__ */ jsxs("div", { className: "xicms-portfolio__body", children: [
45
+ it.category && /* @__PURE__ */ jsx("span", { className: "xicms-portfolio__cat", "data-xicms-edit": `items.${it._i}.category`, children: it.category }),
46
+ /* @__PURE__ */ jsx("strong", { className: "xicms-portfolio__title", "data-xicms-edit": `items.${it._i}.title`, children: it.title }),
47
+ it.description && /* @__PURE__ */ jsx("span", { className: "xicms-portfolio__desc", "data-xicms-edit": `items.${it._i}.description`, children: it.description })
48
+ ] })
49
+ ] });
50
+ if (it.href) {
51
+ return /* @__PURE__ */ jsx("a", { className: "xicms-portfolio__item", href: it.href, children: body }, idx);
52
+ }
53
+ if (block.lightbox) {
54
+ return /* @__PURE__ */ jsx(
55
+ "button",
56
+ {
57
+ type: "button",
58
+ className: "xicms-portfolio__item",
59
+ onClick: (e) => {
60
+ triggerRef.current = e.currentTarget;
61
+ setIndex(idx);
62
+ setOpen(true);
63
+ },
64
+ "aria-label": `${it.title} vergroten`,
65
+ children: body
66
+ },
67
+ idx
68
+ );
69
+ }
70
+ return /* @__PURE__ */ jsx("div", { className: "xicms-portfolio__item", children: body }, idx);
71
+ }) }),
72
+ block.lightbox && open && /* @__PURE__ */ jsx(LightboxOverlay, { items: lightboxItems, index, onIndex: setIndex, onClose: close })
73
+ ] });
74
+ }
75
+ export {
76
+ PortfolioRender
77
+ };
78
+ //# sourceMappingURL=PortfolioRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/builtins/PortfolioRender.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport type { z } from \"zod\";\nimport type { PortfolioBlockSchema } from \"../../schema/blocks\";\nimport type { BlockRenderProps } from \"../registry\";\nimport { VStack, Grid, Heading, Text } from \"../ui\";\nimport { Img } from \"../../media/ImageContext\";\nimport { LightboxOverlay } from \"./GalleryRender\";\n\ntype Portfolio = z.infer<typeof PortfolioBlockSchema>;\n\n/** Projecten/cases-grid met categorie-filter en lightbox (hergebruikt de galerij-lightbox). */\nexport function PortfolioRender({ block }: BlockRenderProps<Portfolio>) {\n // _i = index in de OORSPRONKELIJKE array — de lijst wordt gefilterd op imageUrl én op de\n // gekozen categorie, dus de render-index wijst niet naar block.items[i].\n const allItems = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.imageUrl);\n const [activeCat, setActiveCat] = useState<string | null>(null);\n const [open, setOpen] = useState(false);\n const [index, setIndex] = useState(0);\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n\n const cats = block.showFilter ? [...new Set(allItems.map((i) => i.category).filter((c): c is string => Boolean(c)))] : [];\n const items = activeCat ? allItems.filter((i) => i.category === activeCat) : allItems;\n if (allItems.length === 0) return null;\n\n const lightboxItems = items.map((i) => ({ url: i.imageUrl, alt: i.title, caption: i.description ? `${i.title} — ${i.description}` : i.title }));\n const close = () => {\n setOpen(false);\n triggerRef.current?.focus({ preventScroll: true });\n };\n\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-portfolio\">\n {(block.heading || block.intro) && (\n <VStack gap={1} hAlign=\"center\" style={{ textAlign: \"center\" }}>\n {block.heading && <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>}\n {block.intro && <Text type=\"large\" color=\"secondary\" data-xicms-edit=\"intro\">{block.intro}</Text>}\n </VStack>\n )}\n {cats.length > 0 && (\n <div className=\"xicms-gallery__filter\" role=\"group\" aria-label=\"Filter op categorie\" style={{ justifyContent: \"center\", marginBottom: 0 }}>\n <button type=\"button\" className=\"xicms-gallery__filterbtn\" aria-pressed={activeCat === null} data-active={activeCat === null ? \"1\" : \"0\"} onClick={() => setActiveCat(null)}>\n Alles\n </button>\n {cats.map((c) => (\n <button key={c} type=\"button\" className=\"xicms-gallery__filterbtn\" aria-pressed={activeCat === c} data-active={activeCat === c ? \"1\" : \"0\"} onClick={() => setActiveCat(c)}>\n {c}\n </button>\n ))}\n </div>\n )}\n <Grid columns={{ minWidth: 240, max: block.columns }} gap={3}>\n {items.map((it, idx) => {\n const media = (\n <Img\n src={it.imageUrl}\n alt={it.title}\n width={720}\n height={540}\n sizes=\"(max-width: 720px) 100vw, 33vw\"\n style={{ width: \"100%\", aspectRatio: \"4 / 3\", objectFit: \"cover\", display: \"block\" }}\n />\n );\n const body = (\n <>\n {media}\n <div className=\"xicms-portfolio__body\">\n {it.category && <span className=\"xicms-portfolio__cat\" data-xicms-edit={`items.${it._i}.category`}>{it.category}</span>}\n <strong className=\"xicms-portfolio__title\" data-xicms-edit={`items.${it._i}.title`}>{it.title}</strong>\n {it.description && <span className=\"xicms-portfolio__desc\" data-xicms-edit={`items.${it._i}.description`}>{it.description}</span>}\n </div>\n </>\n );\n if (it.href) {\n return (\n <a key={idx} className=\"xicms-portfolio__item\" href={it.href}>\n {body}\n </a>\n );\n }\n if (block.lightbox) {\n return (\n <button\n key={idx}\n type=\"button\"\n className=\"xicms-portfolio__item\"\n onClick={(e) => { triggerRef.current = e.currentTarget; setIndex(idx); setOpen(true); }}\n aria-label={`${it.title} vergroten`}\n >\n {body}\n </button>\n );\n }\n return (\n <div key={idx} className=\"xicms-portfolio__item\">\n {body}\n </div>\n );\n })}\n </Grid>\n {block.lightbox && open && (\n <LightboxOverlay items={lightboxItems} index={index} onIndex={setIndex} onClose={close} />\n )}\n </VStack>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,8 +1 @@
1
- import type { z } from "zod";
2
- import type { PricingBlockSchema } from "../../schema/blocks.js";
3
- type Pricing = z.infer<typeof PricingBlockSchema>;
4
- /** "€ 120", "120,50 p/m" → "120.50"; null als er geen bedrag in te herkennen is. */
5
- export declare function parsePriceAmount(price: string): string | null;
6
- export declare function pricingJsonLd(items: Pricing["items"], currency?: string): Record<string, unknown>;
7
- export declare const pricingBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
8
- export {};
1
+ export declare const pricingBlock: import("../index.js").BlockDefinition<import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>;
@@ -1,76 +1,8 @@
1
- "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import dynamic from "next/dynamic";
3
2
  import { defineBlock } from "../registry.js";
4
- import { VStack, Grid, Card, Heading, Text, Btn } from "../ui/index.js";
5
- import { jsonLdHtml } from "./jsonld.js";
6
- function parsePriceAmount(price) {
7
- const m = price.replace(/\./g, "").replace(",", ".").match(/(\d+(?:\.\d{1,2})?)/);
8
- return m ? m[1] : null;
9
- }
10
- function pricingJsonLd(items, currency = "EUR") {
11
- return {
12
- "@context": "https://schema.org",
13
- "@type": "ItemList",
14
- itemListElement: items.filter((it) => it.label).map((it, i) => {
15
- const amount = parsePriceAmount(it.price);
16
- return {
17
- "@type": "ListItem",
18
- position: i + 1,
19
- item: {
20
- "@type": "Product",
21
- name: it.label,
22
- ...it.detail ? { description: it.detail } : {},
23
- ...amount ? { offers: { "@type": "Offer", price: amount, priceCurrency: currency } } : {}
24
- }
25
- };
26
- })
27
- };
28
- }
29
- function PricingRender({ block }) {
30
- const items = block.items ?? [];
31
- return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-pricing", children: [
32
- block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, justify: "center", "data-xicms-edit": "heading", style: { textAlign: "center" }, children: block.heading }),
33
- /* @__PURE__ */ jsx(Grid, { columns: { minWidth: 240, max: 4 }, gap: 4, children: items.map((it, i) => /* @__PURE__ */ jsx(
34
- Card,
35
- {
36
- padding: 5,
37
- height: "100%",
38
- className: `xicms-pricing__card${it.highlighted ? " xicms-pricing__card--highlighted" : ""}`,
39
- style: it.highlighted ? { outline: "2px solid var(--color-accent)", outlineOffset: -2 } : void 0,
40
- children: /* @__PURE__ */ jsxs(VStack, { gap: 2, style: { height: "100%" }, children: [
41
- /* @__PURE__ */ jsx(Text, { type: "label", color: "secondary", className: "xicms-pricing__label", "data-xicms-edit": `items.${i}.label`, style: { textTransform: "uppercase", letterSpacing: "0.05em" }, children: it.label }),
42
- /* @__PURE__ */ jsxs(VStack, { gap: 0, children: [
43
- /* @__PURE__ */ jsx(Heading, { level: 3, className: "xicms-pricing__price", "data-xicms-edit": `items.${i}.price`, style: { fontSize: "clamp(1.75rem, 4vw, 2.25rem)", lineHeight: 1.1, fontWeight: 700 }, children: it.price }),
44
- it.billingPeriod && /* @__PURE__ */ jsx(Text, { type: "supporting", color: "secondary", "data-xicms-edit": `items.${i}.billingPeriod`, children: it.billingPeriod })
45
- ] }),
46
- it.detail && /* @__PURE__ */ jsx(Text, { color: "secondary", "data-xicms-edit": `items.${i}.detail`, children: it.detail }),
47
- it.features && it.features.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: 1, as: "ul", style: { listStyle: "none", margin: 0, padding: 0, flex: 1 }, children: it.features.map((f, j) => (
48
- // Echte <li> binnen de <ul> (geldige HTML; screenreaders kondigen de lijst aan).
49
- // Inline edit op een span BINNEN de tekst: anders zou het ✓-teken mee worden
50
- // teruggeschreven in de opgeslagen feature (zelfde patroon als Testimonials).
51
- /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Text, { display: "block", children: [
52
- "\u2713 ",
53
- /* @__PURE__ */ jsx("span", { "data-xicms-edit": `items.${i}.features.${j}`, children: f })
54
- ] }) }, j)
55
- )) }),
56
- it.ctaLabel && /* @__PURE__ */ jsx(Btn, { label: it.ctaLabel, href: it.ctaHref ?? "#", variant: it.highlighted ? "primary" : "outline", editField: `items.${i}.ctaLabel` })
57
- ] })
58
- },
59
- i
60
- )) }),
61
- block.note && /* @__PURE__ */ jsx(Text, { color: "secondary", justify: "center", style: { textAlign: "center" }, "data-xicms-edit": "note", children: block.note }),
62
- block.jsonLd && /* @__PURE__ */ jsx(
63
- "script",
64
- {
65
- type: "application/ld+json",
66
- dangerouslySetInnerHTML: { __html: jsonLdHtml(pricingJsonLd(items, block.currency)) }
67
- }
68
- )
69
- ] });
70
- }
71
3
  const pricingBlock = defineBlock({
72
4
  type: "pricing",
73
- Render: PricingRender,
5
+ Render: dynamic(() => import("./PricingRender.js").then((m) => m.PricingRender)),
74
6
  label: "Prijzen",
75
7
  icon: "\u{1F4B6}",
76
8
  defaults: () => ({
@@ -79,8 +11,6 @@ const pricingBlock = defineBlock({
79
11
  })
80
12
  });
81
13
  export {
82
- parsePriceAmount,
83
- pricingBlock,
84
- pricingJsonLd
14
+ pricingBlock
85
15
  };
86
16
  //# sourceMappingURL=Pricing.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Pricing.tsx"],"sourcesContent":["\"use client\";\n\nimport type { z } from \"zod\";\nimport type { PricingBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { VStack, Grid, Card, Heading, Text, Btn } from \"../ui\";\nimport { jsonLdHtml } from \"./jsonld\";\n\ntype Pricing = z.infer<typeof PricingBlockSchema>;\n\n/** \"€ 120\", \"120,50 p/m\" → \"120.50\"; null als er geen bedrag in te herkennen is. */\nexport function parsePriceAmount(price: string): string | null {\n const m = price.replace(/\\./g, \"\").replace(\",\", \".\").match(/(\\d+(?:\\.\\d{1,2})?)/);\n return m ? m[1] : null;\n}\n\nexport function pricingJsonLd(items: Pricing[\"items\"], currency = \"EUR\"): Record<string, unknown> {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"ItemList\",\n itemListElement: items\n .filter((it) => it.label)\n .map((it, i) => {\n // schema.org-`price` moet een kaal getal zijn (\"120\" of \"120.50\"); vrije tekst zoals\n // \"€ 120/mnd\" keurt Google af. Geen herkenbaar bedrag → dan geen Offer.\n const amount = parsePriceAmount(it.price);\n return {\n \"@type\": \"ListItem\",\n position: i + 1,\n item: {\n \"@type\": \"Product\",\n name: it.label,\n ...(it.detail ? { description: it.detail } : {}),\n ...(amount ? { offers: { \"@type\": \"Offer\", price: amount, priceCurrency: currency } } : {}),\n },\n };\n }),\n };\n}\n\n/** Prijzen als kaarten met optionele features, uitgelichte tier en per-tier knop (Astryx). */\nfunction PricingRender({ block }: BlockRenderProps<Pricing>) {\n const items = block.items ?? [];\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-pricing\">\n {block.heading && (\n <Heading level={2} justify=\"center\" data-xicms-edit=\"heading\" style={{ textAlign: \"center\" }}>{block.heading}</Heading>\n )}\n <Grid columns={{ minWidth: 240, max: 4 }} gap={4}>\n {items.map((it, i) => (\n <Card\n key={i}\n padding={5}\n height=\"100%\"\n className={`xicms-pricing__card${it.highlighted ? \" xicms-pricing__card--highlighted\" : \"\"}`}\n style={it.highlighted ? { outline: \"2px solid var(--color-accent)\", outlineOffset: -2 } : undefined}\n >\n <VStack gap={2} style={{ height: \"100%\" }}>\n <Text type=\"label\" color=\"secondary\" className=\"xicms-pricing__label\" data-xicms-edit={`items.${i}.label`} style={{ textTransform: \"uppercase\", letterSpacing: \"0.05em\" }}>\n {it.label}\n </Text>\n <VStack gap={0}>\n <Heading level={3} className=\"xicms-pricing__price\" data-xicms-edit={`items.${i}.price`} style={{ fontSize: \"clamp(1.75rem, 4vw, 2.25rem)\", lineHeight: 1.1, fontWeight: 700 }}>\n {it.price}\n </Heading>\n {it.billingPeriod && <Text type=\"supporting\" color=\"secondary\" data-xicms-edit={`items.${i}.billingPeriod`}>{it.billingPeriod}</Text>}\n </VStack>\n {it.detail && <Text color=\"secondary\" data-xicms-edit={`items.${i}.detail`}>{it.detail}</Text>}\n {it.features && it.features.length > 0 && (\n <VStack gap={1} as=\"ul\" style={{ listStyle: \"none\", margin: 0, padding: 0, flex: 1 }}>\n {it.features.map((f, j) => (\n // Echte <li> binnen de <ul> (geldige HTML; screenreaders kondigen de lijst aan).\n // Inline edit op een span BINNEN de tekst: anders zou het ✓-teken mee worden\n // teruggeschreven in de opgeslagen feature (zelfde patroon als Testimonials).\n <li key={j}>\n <Text display=\"block\">✓ <span data-xicms-edit={`items.${i}.features.${j}`}>{f}</span></Text>\n </li>\n ))}\n </VStack>\n )}\n {it.ctaLabel && (\n <Btn label={it.ctaLabel} href={it.ctaHref ?? \"#\"} variant={it.highlighted ? \"primary\" : \"outline\"} editField={`items.${i}.ctaLabel`} />\n )}\n </VStack>\n </Card>\n ))}\n </Grid>\n {block.note && <Text color=\"secondary\" justify=\"center\" style={{ textAlign: \"center\" }} data-xicms-edit=\"note\">{block.note}</Text>}\n {block.jsonLd && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: jsonLdHtml(pricingJsonLd(items, block.currency)) }}\n />\n )}\n </VStack>\n );\n}\n\nexport const pricingBlock = defineBlock({\n type: \"pricing\",\n Render: PricingRender,\n label: \"Prijzen\",\n icon: \"💶\",\n defaults: () => ({\n heading: \"Prijzen\",\n items: [{ label: \"Pakket\", price: \"€ 00\", billingPeriod: \"/maand\", features: [], highlighted: false }],\n }),\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Pricing.tsx"],"sourcesContent":["import dynamic from \"next/dynamic\";\nimport { defineBlock } from \"../registry\";\n\nexport const pricingBlock = defineBlock({\n type: \"pricing\",\n Render: dynamic(() => import(\"./PricingRender\").then((m) => m.PricingRender)),\n label: \"Prijzen\",\n icon: \"💶\",\n defaults: () => ({\n heading: \"Prijzen\",\n items: [{ label: \"Pakket\", price: \"€ 00\", billingPeriod: \"/maand\", features: [], highlighted: false }],\n }),\n});\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,mBAAmB;AAErB,MAAM,eAAe,YAAY;AAAA,EACtC,MAAM;AAAA,EACN,QAAQ,QAAQ,MAAM,OAAO,iBAAiB,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC;AAAA,EAC5E,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO;AAAA,IACf,SAAS;AAAA,IACT,OAAO,CAAC,EAAE,OAAO,UAAU,OAAO,aAAQ,eAAe,UAAU,UAAU,CAAC,GAAG,aAAa,MAAM,CAAC;AAAA,EACvG;AACF,CAAC;","names":[]}
@@ -0,0 +1,10 @@
1
+ import type { z } from "zod";
2
+ import type { PricingBlockSchema } from "../../schema/blocks.js";
3
+ import type { BlockRenderProps } from "../registry.js";
4
+ type Pricing = z.infer<typeof PricingBlockSchema>;
5
+ /** "€ 120", "120,50 p/m" → "120.50"; null als er geen bedrag in te herkennen is. */
6
+ export declare function parsePriceAmount(price: string): string | null;
7
+ export declare function pricingJsonLd(items: Pricing["items"], currency?: string): Record<string, unknown>;
8
+ /** Prijzen als kaarten met optionele features, uitgelichte tier en per-tier knop (Astryx). */
9
+ export declare function PricingRender({ block }: BlockRenderProps<Pricing>): import("react").JSX.Element;
10
+ export {};
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { VStack, Grid, Card, Heading, Text, Btn } from "../ui/index.js";
4
+ import { jsonLdHtml } from "./jsonld.js";
5
+ function parsePriceAmount(price) {
6
+ const m = price.replace(/\./g, "").replace(",", ".").match(/(\d+(?:\.\d{1,2})?)/);
7
+ return m ? m[1] : null;
8
+ }
9
+ function pricingJsonLd(items, currency = "EUR") {
10
+ return {
11
+ "@context": "https://schema.org",
12
+ "@type": "ItemList",
13
+ itemListElement: items.filter((it) => it.label).map((it, i) => {
14
+ const amount = parsePriceAmount(it.price);
15
+ return {
16
+ "@type": "ListItem",
17
+ position: i + 1,
18
+ item: {
19
+ "@type": "Product",
20
+ name: it.label,
21
+ ...it.detail ? { description: it.detail } : {},
22
+ ...amount ? { offers: { "@type": "Offer", price: amount, priceCurrency: currency } } : {}
23
+ }
24
+ };
25
+ })
26
+ };
27
+ }
28
+ function PricingRender({ block }) {
29
+ const items = block.items ?? [];
30
+ return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-pricing", children: [
31
+ block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, justify: "center", "data-xicms-edit": "heading", style: { textAlign: "center" }, children: block.heading }),
32
+ /* @__PURE__ */ jsx(Grid, { columns: { minWidth: 240, max: 4 }, gap: 4, children: items.map((it, i) => /* @__PURE__ */ jsx(
33
+ Card,
34
+ {
35
+ padding: 5,
36
+ height: "100%",
37
+ className: `xicms-pricing__card${it.highlighted ? " xicms-pricing__card--highlighted" : ""}`,
38
+ style: it.highlighted ? { outline: "2px solid var(--color-accent)", outlineOffset: -2 } : void 0,
39
+ children: /* @__PURE__ */ jsxs(VStack, { gap: 2, style: { height: "100%" }, children: [
40
+ /* @__PURE__ */ jsx(Text, { type: "label", color: "secondary", className: "xicms-pricing__label", "data-xicms-edit": `items.${i}.label`, style: { textTransform: "uppercase", letterSpacing: "0.05em" }, children: it.label }),
41
+ /* @__PURE__ */ jsxs(VStack, { gap: 0, children: [
42
+ /* @__PURE__ */ jsx(Heading, { level: 3, className: "xicms-pricing__price", "data-xicms-edit": `items.${i}.price`, style: { fontSize: "clamp(1.75rem, 4vw, 2.25rem)", lineHeight: 1.1, fontWeight: 700 }, children: it.price }),
43
+ it.billingPeriod && /* @__PURE__ */ jsx(Text, { type: "supporting", color: "secondary", "data-xicms-edit": `items.${i}.billingPeriod`, children: it.billingPeriod })
44
+ ] }),
45
+ it.detail && /* @__PURE__ */ jsx(Text, { color: "secondary", "data-xicms-edit": `items.${i}.detail`, children: it.detail }),
46
+ it.features && it.features.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: 1, as: "ul", style: { listStyle: "none", margin: 0, padding: 0, flex: 1 }, children: it.features.map((f, j) => (
47
+ // Echte <li> binnen de <ul> (geldige HTML; screenreaders kondigen de lijst aan).
48
+ // Inline edit op een span BINNEN de tekst: anders zou het ✓-teken mee worden
49
+ // teruggeschreven in de opgeslagen feature (zelfde patroon als Testimonials).
50
+ /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Text, { display: "block", children: [
51
+ "\u2713 ",
52
+ /* @__PURE__ */ jsx("span", { "data-xicms-edit": `items.${i}.features.${j}`, children: f })
53
+ ] }) }, j)
54
+ )) }),
55
+ it.ctaLabel && /* @__PURE__ */ jsx(Btn, { label: it.ctaLabel, href: it.ctaHref ?? "#", variant: it.highlighted ? "primary" : "outline", editField: `items.${i}.ctaLabel` })
56
+ ] })
57
+ },
58
+ i
59
+ )) }),
60
+ block.note && /* @__PURE__ */ jsx(Text, { color: "secondary", justify: "center", style: { textAlign: "center" }, "data-xicms-edit": "note", children: block.note }),
61
+ block.jsonLd && /* @__PURE__ */ jsx(
62
+ "script",
63
+ {
64
+ type: "application/ld+json",
65
+ dangerouslySetInnerHTML: { __html: jsonLdHtml(pricingJsonLd(items, block.currency)) }
66
+ }
67
+ )
68
+ ] });
69
+ }
70
+ export {
71
+ PricingRender,
72
+ parsePriceAmount,
73
+ pricingJsonLd
74
+ };
75
+ //# sourceMappingURL=PricingRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/builtins/PricingRender.tsx"],"sourcesContent":["\"use client\";\n\nimport type { z } from \"zod\";\nimport type { PricingBlockSchema } from \"../../schema/blocks\";\nimport type { BlockRenderProps } from \"../registry\";\nimport { VStack, Grid, Card, Heading, Text, Btn } from \"../ui\";\nimport { jsonLdHtml } from \"./jsonld\";\n\ntype Pricing = z.infer<typeof PricingBlockSchema>;\n\n/** \"€ 120\", \"120,50 p/m\" → \"120.50\"; null als er geen bedrag in te herkennen is. */\nexport function parsePriceAmount(price: string): string | null {\n const m = price.replace(/\\./g, \"\").replace(\",\", \".\").match(/(\\d+(?:\\.\\d{1,2})?)/);\n return m ? m[1] : null;\n}\n\nexport function pricingJsonLd(items: Pricing[\"items\"], currency = \"EUR\"): Record<string, unknown> {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"ItemList\",\n itemListElement: items\n .filter((it) => it.label)\n .map((it, i) => {\n // schema.org-`price` moet een kaal getal zijn (\"120\" of \"120.50\"); vrije tekst zoals\n // \"€ 120/mnd\" keurt Google af. Geen herkenbaar bedrag → dan geen Offer.\n const amount = parsePriceAmount(it.price);\n return {\n \"@type\": \"ListItem\",\n position: i + 1,\n item: {\n \"@type\": \"Product\",\n name: it.label,\n ...(it.detail ? { description: it.detail } : {}),\n ...(amount ? { offers: { \"@type\": \"Offer\", price: amount, priceCurrency: currency } } : {}),\n },\n };\n }),\n };\n}\n\n/** Prijzen als kaarten met optionele features, uitgelichte tier en per-tier knop (Astryx). */\nexport function PricingRender({ block }: BlockRenderProps<Pricing>) {\n const items = block.items ?? [];\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-pricing\">\n {block.heading && (\n <Heading level={2} justify=\"center\" data-xicms-edit=\"heading\" style={{ textAlign: \"center\" }}>{block.heading}</Heading>\n )}\n <Grid columns={{ minWidth: 240, max: 4 }} gap={4}>\n {items.map((it, i) => (\n <Card\n key={i}\n padding={5}\n height=\"100%\"\n className={`xicms-pricing__card${it.highlighted ? \" xicms-pricing__card--highlighted\" : \"\"}`}\n style={it.highlighted ? { outline: \"2px solid var(--color-accent)\", outlineOffset: -2 } : undefined}\n >\n <VStack gap={2} style={{ height: \"100%\" }}>\n <Text type=\"label\" color=\"secondary\" className=\"xicms-pricing__label\" data-xicms-edit={`items.${i}.label`} style={{ textTransform: \"uppercase\", letterSpacing: \"0.05em\" }}>\n {it.label}\n </Text>\n <VStack gap={0}>\n <Heading level={3} className=\"xicms-pricing__price\" data-xicms-edit={`items.${i}.price`} style={{ fontSize: \"clamp(1.75rem, 4vw, 2.25rem)\", lineHeight: 1.1, fontWeight: 700 }}>\n {it.price}\n </Heading>\n {it.billingPeriod && <Text type=\"supporting\" color=\"secondary\" data-xicms-edit={`items.${i}.billingPeriod`}>{it.billingPeriod}</Text>}\n </VStack>\n {it.detail && <Text color=\"secondary\" data-xicms-edit={`items.${i}.detail`}>{it.detail}</Text>}\n {it.features && it.features.length > 0 && (\n <VStack gap={1} as=\"ul\" style={{ listStyle: \"none\", margin: 0, padding: 0, flex: 1 }}>\n {it.features.map((f, j) => (\n // Echte <li> binnen de <ul> (geldige HTML; screenreaders kondigen de lijst aan).\n // Inline edit op een span BINNEN de tekst: anders zou het ✓-teken mee worden\n // teruggeschreven in de opgeslagen feature (zelfde patroon als Testimonials).\n <li key={j}>\n <Text display=\"block\">✓ <span data-xicms-edit={`items.${i}.features.${j}`}>{f}</span></Text>\n </li>\n ))}\n </VStack>\n )}\n {it.ctaLabel && (\n <Btn label={it.ctaLabel} href={it.ctaHref ?? \"#\"} variant={it.highlighted ? \"primary\" : \"outline\"} editField={`items.${i}.ctaLabel`} />\n )}\n </VStack>\n </Card>\n ))}\n </Grid>\n {block.note && <Text color=\"secondary\" justify=\"center\" style={{ textAlign: \"center\" }} data-xicms-edit=\"note\">{block.note}</Text>}\n {block.jsonLd && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: jsonLdHtml(pricingJsonLd(items, block.currency)) }}\n />\n )}\n </VStack>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,2 +1 @@
1
- import type { z } from "zod";
2
- export declare const richTextBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
1
+ export declare const richTextBlock: import("../index.js").BlockDefinition<import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>;