@xiwebdesign/xicms-core 7.14.0 → 7.15.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 (238) hide show
  1. package/dist/blocks/builtins/Accordion.d.ts +1 -2
  2. package/dist/blocks/builtins/Accordion.js +2 -39
  3. package/dist/blocks/builtins/Accordion.js.map +1 -1
  4. package/dist/blocks/builtins/AccordionRender.d.ts +7 -0
  5. package/dist/blocks/builtins/AccordionRender.js +42 -0
  6. package/dist/blocks/builtins/AccordionRender.js.map +1 -0
  7. package/dist/blocks/builtins/Agenda.d.ts +1 -29
  8. package/dist/blocks/builtins/Agenda.js +3 -94
  9. package/dist/blocks/builtins/Agenda.js.map +1 -1
  10. package/dist/blocks/builtins/AgendaRender.d.ts +34 -0
  11. package/dist/blocks/builtins/AgendaRender.js +96 -0
  12. package/dist/blocks/builtins/AgendaRender.js.map +1 -0
  13. package/dist/blocks/builtins/Banner.d.ts +1 -2
  14. package/dist/blocks/builtins/Banner.js +2 -32
  15. package/dist/blocks/builtins/Banner.js.map +1 -1
  16. package/dist/blocks/builtins/BannerRender.d.ts +11 -0
  17. package/dist/blocks/builtins/BannerRender.js +35 -0
  18. package/dist/blocks/builtins/BannerRender.js.map +1 -0
  19. package/dist/blocks/builtins/BeforeAfter.d.ts +1 -2
  20. package/dist/blocks/builtins/BeforeAfter.js +2 -31
  21. package/dist/blocks/builtins/BeforeAfter.js.map +1 -1
  22. package/dist/blocks/builtins/BeforeAfterRender.d.ts +11 -0
  23. package/dist/blocks/builtins/BeforeAfterRender.js +34 -0
  24. package/dist/blocks/builtins/BeforeAfterRender.js.map +1 -0
  25. package/dist/blocks/builtins/Booking.d.ts +1 -2
  26. package/dist/blocks/builtins/Booking.js +2 -14
  27. package/dist/blocks/builtins/Booking.js.map +1 -1
  28. package/dist/blocks/builtins/BookingRender.d.ts +8 -0
  29. package/dist/blocks/builtins/BookingRender.js +17 -0
  30. package/dist/blocks/builtins/BookingRender.js.map +1 -0
  31. package/dist/blocks/builtins/Breadcrumbs.d.ts +1 -10
  32. package/dist/blocks/builtins/Breadcrumbs.js +3 -69
  33. package/dist/blocks/builtins/Breadcrumbs.js.map +1 -1
  34. package/dist/blocks/builtins/BreadcrumbsRender.d.ts +18 -0
  35. package/dist/blocks/builtins/BreadcrumbsRender.js +71 -0
  36. package/dist/blocks/builtins/BreadcrumbsRender.js.map +1 -0
  37. package/dist/blocks/builtins/Cards.d.ts +1 -2
  38. package/dist/blocks/builtins/Cards.js +2 -74
  39. package/dist/blocks/builtins/Cards.js.map +1 -1
  40. package/dist/blocks/builtins/CardsRender.d.ts +7 -0
  41. package/dist/blocks/builtins/CardsRender.js +77 -0
  42. package/dist/blocks/builtins/CardsRender.js.map +1 -0
  43. package/dist/blocks/builtins/Columns.d.ts +1 -2
  44. package/dist/blocks/builtins/Columns.js +2 -9
  45. package/dist/blocks/builtins/Columns.js.map +1 -1
  46. package/dist/blocks/builtins/ColumnsRender.d.ts +6 -0
  47. package/dist/blocks/builtins/ColumnsRender.js +12 -0
  48. package/dist/blocks/builtins/ColumnsRender.js.map +1 -0
  49. package/dist/blocks/builtins/Contact.d.ts +2 -7
  50. package/dist/blocks/builtins/Contact.js +3 -102
  51. package/dist/blocks/builtins/Contact.js.map +1 -1
  52. package/dist/blocks/builtins/ContactRender.d.ts +9 -0
  53. package/dist/blocks/builtins/ContactRender.js +105 -0
  54. package/dist/blocks/builtins/ContactRender.js.map +1 -0
  55. package/dist/blocks/builtins/Countdown.d.ts +1 -18
  56. package/dist/blocks/builtins/Countdown.js +3 -53
  57. package/dist/blocks/builtins/Countdown.js.map +1 -1
  58. package/dist/blocks/builtins/CountdownRender.d.ts +22 -0
  59. package/dist/blocks/builtins/CountdownRender.js +55 -0
  60. package/dist/blocks/builtins/CountdownRender.js.map +1 -0
  61. package/dist/blocks/builtins/Cta.d.ts +1 -2
  62. package/dist/blocks/builtins/Cta.js +2 -39
  63. package/dist/blocks/builtins/Cta.js.map +1 -1
  64. package/dist/blocks/builtins/CtaRender.d.ts +6 -0
  65. package/dist/blocks/builtins/CtaRender.js +42 -0
  66. package/dist/blocks/builtins/CtaRender.js.map +1 -0
  67. package/dist/blocks/builtins/Downloads.d.ts +1 -2
  68. package/dist/blocks/builtins/Downloads.js +2 -16
  69. package/dist/blocks/builtins/Downloads.js.map +1 -1
  70. package/dist/blocks/builtins/DownloadsRender.d.ts +7 -0
  71. package/dist/blocks/builtins/DownloadsRender.js +19 -0
  72. package/dist/blocks/builtins/DownloadsRender.js.map +1 -0
  73. package/dist/blocks/builtins/Elements.d.ts +3 -4
  74. package/dist/blocks/builtins/Elements.js +4 -38
  75. package/dist/blocks/builtins/Elements.js.map +1 -1
  76. package/dist/blocks/builtins/ElementsRender.d.ts +10 -0
  77. package/dist/blocks/builtins/ElementsRender.js +41 -0
  78. package/dist/blocks/builtins/ElementsRender.js.map +1 -0
  79. package/dist/blocks/builtins/Embed.d.ts +1 -2
  80. package/dist/blocks/builtins/Embed.js +2 -19
  81. package/dist/blocks/builtins/Embed.js.map +1 -1
  82. package/dist/blocks/builtins/EmbedRender.d.ts +7 -0
  83. package/dist/blocks/builtins/EmbedRender.js +22 -0
  84. package/dist/blocks/builtins/EmbedRender.js.map +1 -0
  85. package/dist/blocks/builtins/Faq.d.ts +1 -6
  86. package/dist/blocks/builtins/Faq.js +3 -103
  87. package/dist/blocks/builtins/Faq.js.map +1 -1
  88. package/dist/blocks/builtins/FaqRender.d.ts +10 -0
  89. package/dist/blocks/builtins/FaqRender.js +105 -0
  90. package/dist/blocks/builtins/FaqRender.js.map +1 -0
  91. package/dist/blocks/builtins/FormBlock.d.ts +1 -2
  92. package/dist/blocks/builtins/FormBlock.js +2 -222
  93. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  94. package/dist/blocks/builtins/FormBlockRender.d.ts +6 -0
  95. package/dist/blocks/builtins/FormBlockRender.js +225 -0
  96. package/dist/blocks/builtins/FormBlockRender.js.map +1 -0
  97. package/dist/blocks/builtins/Gallery.d.ts +1 -23
  98. package/dist/blocks/builtins/Gallery.js +2 -164
  99. package/dist/blocks/builtins/Gallery.js.map +1 -1
  100. package/dist/blocks/builtins/GalleryRender.d.ts +25 -0
  101. package/dist/blocks/builtins/GalleryRender.js +167 -0
  102. package/dist/blocks/builtins/GalleryRender.js.map +1 -0
  103. package/dist/blocks/builtins/Hero.d.ts +1 -2
  104. package/dist/blocks/builtins/Hero.js +2 -73
  105. package/dist/blocks/builtins/Hero.js.map +1 -1
  106. package/dist/blocks/builtins/HeroRender.d.ts +6 -0
  107. package/dist/blocks/builtins/HeroRender.js +76 -0
  108. package/dist/blocks/builtins/HeroRender.js.map +1 -0
  109. package/dist/blocks/builtins/ImageBlock.d.ts +1 -2
  110. package/dist/blocks/builtins/ImageBlock.js +2 -22
  111. package/dist/blocks/builtins/ImageBlock.js.map +1 -1
  112. package/dist/blocks/builtins/ImageBlockRender.d.ts +6 -0
  113. package/dist/blocks/builtins/ImageBlockRender.js +25 -0
  114. package/dist/blocks/builtins/ImageBlockRender.js.map +1 -0
  115. package/dist/blocks/builtins/Jobs.d.ts +1 -15
  116. package/dist/blocks/builtins/Jobs.js +3 -69
  117. package/dist/blocks/builtins/Jobs.js.map +1 -1
  118. package/dist/blocks/builtins/JobsRender.d.ts +17 -0
  119. package/dist/blocks/builtins/JobsRender.js +71 -0
  120. package/dist/blocks/builtins/JobsRender.js.map +1 -0
  121. package/dist/blocks/builtins/Layout.d.ts +2 -3
  122. package/dist/blocks/builtins/Layout.js +3 -39
  123. package/dist/blocks/builtins/Layout.js.map +1 -1
  124. package/dist/blocks/builtins/LayoutRender.d.ts +8 -0
  125. package/dist/blocks/builtins/LayoutRender.js +42 -0
  126. package/dist/blocks/builtins/LayoutRender.js.map +1 -0
  127. package/dist/blocks/builtins/MapBlock.d.ts +1 -2
  128. package/dist/blocks/builtins/MapBlock.js +2 -45
  129. package/dist/blocks/builtins/MapBlock.js.map +1 -1
  130. package/dist/blocks/builtins/MapBlockRender.d.ts +6 -0
  131. package/dist/blocks/builtins/MapBlockRender.js +48 -0
  132. package/dist/blocks/builtins/MapBlockRender.js.map +1 -0
  133. package/dist/blocks/builtins/NewsListBlock.d.ts +1 -2
  134. package/dist/blocks/builtins/NewsListBlock.js +2 -11
  135. package/dist/blocks/builtins/NewsListBlock.js.map +1 -1
  136. package/dist/blocks/builtins/NewsListBlockRender.d.ts +6 -0
  137. package/dist/blocks/builtins/NewsListBlockRender.js +14 -0
  138. package/dist/blocks/builtins/NewsListBlockRender.js.map +1 -0
  139. package/dist/blocks/builtins/Newsletter.d.ts +1 -2
  140. package/dist/blocks/builtins/Newsletter.js +2 -89
  141. package/dist/blocks/builtins/Newsletter.js.map +1 -1
  142. package/dist/blocks/builtins/NewsletterRender.d.ts +13 -0
  143. package/dist/blocks/builtins/NewsletterRender.js +92 -0
  144. package/dist/blocks/builtins/NewsletterRender.js.map +1 -0
  145. package/dist/blocks/builtins/OpeningHours.d.ts +1 -2
  146. package/dist/blocks/builtins/OpeningHours.js +2 -28
  147. package/dist/blocks/builtins/OpeningHours.js.map +1 -1
  148. package/dist/blocks/builtins/OpeningHoursRender.d.ts +7 -0
  149. package/dist/blocks/builtins/OpeningHoursRender.js +31 -0
  150. package/dist/blocks/builtins/OpeningHoursRender.js.map +1 -0
  151. package/dist/blocks/builtins/PdfBlock.d.ts +1 -2
  152. package/dist/blocks/builtins/PdfBlock.js +2 -76
  153. package/dist/blocks/builtins/PdfBlock.js.map +1 -1
  154. package/dist/blocks/builtins/PdfBlockRender.d.ts +11 -0
  155. package/dist/blocks/builtins/PdfBlockRender.js +79 -0
  156. package/dist/blocks/builtins/PdfBlockRender.js.map +1 -0
  157. package/dist/blocks/builtins/Portfolio.d.ts +1 -2
  158. package/dist/blocks/builtins/Portfolio.js +2 -75
  159. package/dist/blocks/builtins/Portfolio.js.map +1 -1
  160. package/dist/blocks/builtins/PortfolioRender.d.ts +7 -0
  161. package/dist/blocks/builtins/PortfolioRender.js +78 -0
  162. package/dist/blocks/builtins/PortfolioRender.js.map +1 -0
  163. package/dist/blocks/builtins/Pricing.d.ts +1 -8
  164. package/dist/blocks/builtins/Pricing.js +3 -73
  165. package/dist/blocks/builtins/Pricing.js.map +1 -1
  166. package/dist/blocks/builtins/PricingRender.d.ts +10 -0
  167. package/dist/blocks/builtins/PricingRender.js +75 -0
  168. package/dist/blocks/builtins/PricingRender.js.map +1 -0
  169. package/dist/blocks/builtins/RichTextBlock.d.ts +1 -2
  170. package/dist/blocks/builtins/RichTextBlock.js +2 -5
  171. package/dist/blocks/builtins/RichTextBlock.js.map +1 -1
  172. package/dist/blocks/builtins/RichTextBlockRender.d.ts +6 -0
  173. package/dist/blocks/builtins/RichTextBlockRender.js +8 -0
  174. package/dist/blocks/builtins/RichTextBlockRender.js.map +1 -0
  175. package/dist/blocks/builtins/Search.d.ts +1 -2
  176. package/dist/blocks/builtins/Search.js +2 -80
  177. package/dist/blocks/builtins/Search.js.map +1 -1
  178. package/dist/blocks/builtins/SearchRender.d.ts +7 -0
  179. package/dist/blocks/builtins/SearchRender.js +83 -0
  180. package/dist/blocks/builtins/SearchRender.js.map +1 -0
  181. package/dist/blocks/builtins/Sections.d.ts +4 -5
  182. package/dist/blocks/builtins/Sections.js +5 -142
  183. package/dist/blocks/builtins/Sections.js.map +1 -1
  184. package/dist/blocks/builtins/SectionsRender.d.ts +12 -0
  185. package/dist/blocks/builtins/SectionsRender.js +145 -0
  186. package/dist/blocks/builtins/SectionsRender.js.map +1 -0
  187. package/dist/blocks/builtins/Slider.d.ts +1 -2
  188. package/dist/blocks/builtins/Slider.js +2 -134
  189. package/dist/blocks/builtins/Slider.js.map +1 -1
  190. package/dist/blocks/builtins/SliderRender.d.ts +7 -0
  191. package/dist/blocks/builtins/SliderRender.js +137 -0
  192. package/dist/blocks/builtins/SliderRender.js.map +1 -0
  193. package/dist/blocks/builtins/Spacer.d.ts +1 -2
  194. package/dist/blocks/builtins/Spacer.js +2 -6
  195. package/dist/blocks/builtins/Spacer.js.map +1 -1
  196. package/dist/blocks/builtins/SpacerRender.d.ts +6 -0
  197. package/dist/blocks/builtins/SpacerRender.js +9 -0
  198. package/dist/blocks/builtins/SpacerRender.js.map +1 -0
  199. package/dist/blocks/builtins/SymbolRef.d.ts +1 -2
  200. package/dist/blocks/builtins/SymbolRef.js +2 -76
  201. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  202. package/dist/blocks/builtins/SymbolRefRender.d.ts +13 -0
  203. package/dist/blocks/builtins/SymbolRefRender.js +79 -0
  204. package/dist/blocks/builtins/SymbolRefRender.js.map +1 -0
  205. package/dist/blocks/builtins/Team.d.ts +1 -2
  206. package/dist/blocks/builtins/Team.js +2 -55
  207. package/dist/blocks/builtins/Team.js.map +1 -1
  208. package/dist/blocks/builtins/TeamRender.d.ts +7 -0
  209. package/dist/blocks/builtins/TeamRender.js +58 -0
  210. package/dist/blocks/builtins/TeamRender.js.map +1 -0
  211. package/dist/blocks/builtins/Testimonials.d.ts +1 -8
  212. package/dist/blocks/builtins/Testimonials.js +2 -69
  213. package/dist/blocks/builtins/Testimonials.js.map +1 -1
  214. package/dist/blocks/builtins/TestimonialsRender.d.ts +10 -0
  215. package/dist/blocks/builtins/TestimonialsRender.js +72 -0
  216. package/dist/blocks/builtins/TestimonialsRender.js.map +1 -0
  217. package/dist/blocks/builtins/Video.d.ts +1 -9
  218. package/dist/blocks/builtins/Video.js +2 -64
  219. package/dist/blocks/builtins/Video.js.map +1 -1
  220. package/dist/blocks/builtins/VideoRender.d.ts +10 -0
  221. package/dist/blocks/builtins/VideoRender.js +67 -0
  222. package/dist/blocks/builtins/VideoRender.js.map +1 -0
  223. package/dist/blocks/builtins/WhatsappChat.d.ts +1 -8
  224. package/dist/blocks/builtins/WhatsappChat.js +2 -37
  225. package/dist/blocks/builtins/WhatsappChat.js.map +1 -1
  226. package/dist/blocks/builtins/WhatsappChatRender.d.ts +17 -0
  227. package/dist/blocks/builtins/WhatsappChatRender.js +40 -0
  228. package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -0
  229. package/dist/blocks/builtins/Widgets.d.ts +12 -13
  230. package/dist/blocks/builtins/Widgets.js +13 -175
  231. package/dist/blocks/builtins/Widgets.js.map +1 -1
  232. package/dist/blocks/builtins/WidgetsRender.d.ts +34 -0
  233. package/dist/blocks/builtins/WidgetsRender.js +178 -0
  234. package/dist/blocks/builtins/WidgetsRender.js.map +1 -0
  235. package/dist/modules/contract.js +2 -1
  236. package/dist/modules/contract.js.map +1 -1
  237. package/dist/version.js +1 -1
  238. package/package.json +1 -1
@@ -1,61 +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, HStack, Grid, Card, Heading, Text, Avatar } from "../ui/index.js";
5
- import { jsonLdHtml } from "./jsonld.js";
6
- function personJsonLd(members) {
7
- return {
8
- "@context": "https://schema.org",
9
- "@type": "ItemList",
10
- itemListElement: members.filter((m) => m.name).map((m, i) => ({
11
- "@type": "ListItem",
12
- position: i + 1,
13
- item: {
14
- "@type": "Person",
15
- name: m.name,
16
- ...m.role ? { jobTitle: m.role } : {},
17
- ...m.email ? { email: m.email } : {},
18
- ...m.phone ? { telephone: m.phone } : {},
19
- ...m.photoUrl ? { image: m.photoUrl } : {},
20
- ...m.social?.length ? { sameAs: m.social.map((s) => s.href) } : {}
21
- }
22
- }))
23
- };
24
- }
25
- function TeamRender({ block }) {
26
- const members = (block.members ?? []).map((m, i) => ({ ...m, _i: i })).filter((m) => m.name);
27
- if (members.length === 0) return null;
28
- const groups = block.groupByDepartment ? [...new Set(members.map((m) => m.department ?? ""))].sort((a, b) => a === "" ? 1 : b === "" ? -1 : 0).map((dep) => ({ dep, members: members.filter((m) => (m.department ?? "") === dep) })) : [{ dep: "", members }];
29
- const grid = (list) => /* @__PURE__ */ jsx(Grid, { columns: { minWidth: 220, max: 4 }, gap: 4, children: list.map((m) => {
30
- const i = m._i;
31
- return /* @__PURE__ */ jsx(Card, { padding: 4, height: "100%", children: /* @__PURE__ */ jsxs(VStack, { gap: 1, hAlign: "center", style: { textAlign: "center", height: "100%" }, children: [
32
- /* @__PURE__ */ jsx(Avatar, { src: m.photoUrl, name: m.name, size: 96 }),
33
- /* @__PURE__ */ jsx(Heading, { level: 3, "data-xicms-edit": `members.${i}.name`, children: m.name }),
34
- m.role && /* @__PURE__ */ jsx(Text, { type: "label", "data-xicms-edit": `members.${i}.role`, style: { color: "var(--color-accent)" }, children: m.role }),
35
- m.bio && /* @__PURE__ */ jsx(Text, { color: "secondary", "data-xicms-edit": `members.${i}.bio`, children: m.bio }),
36
- m.email && /* @__PURE__ */ jsx("a", { href: `mailto:${m.email}`, style: { color: "var(--color-accent)" }, children: /* @__PURE__ */ jsx(Text, { type: "supporting", "data-xicms-edit": `members.${i}.email`, children: m.email }) }),
37
- m.phone && /* @__PURE__ */ jsx(Text, { type: "supporting", color: "secondary", "data-xicms-edit": `members.${i}.phone`, children: m.phone }),
38
- m.social && m.social.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: 2, hAlign: "center", as: "ul", style: { listStyle: "none", margin: 0, padding: 0, flexWrap: "wrap" }, children: m.social.map((s, j) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: s.href, rel: "noopener noreferrer", target: "_blank", style: { color: "var(--color-accent)" }, children: /* @__PURE__ */ jsx(Text, { type: "supporting", "data-xicms-edit": `members.${i}.social.${j}.platform`, children: s.platform }) }) }, j)) })
39
- ] }) }, i);
40
- }) });
41
- return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-team", children: [
42
- block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, justify: "center", "data-xicms-edit": "heading", style: { textAlign: "center" }, children: block.heading }),
43
- groups.map(({ dep, members: list }) => /* @__PURE__ */ jsxs(VStack, { gap: 3, children: [
44
- block.groupByDepartment && groups.length > 1 && /* @__PURE__ */ jsx(Heading, { level: 3, justify: "center", style: { textAlign: "center" }, children: dep || "Overig" }),
45
- grid(list)
46
- ] }, dep || "_overig")),
47
- block.jsonLd && /* @__PURE__ */ jsx(
48
- "script",
49
- {
50
- type: "application/ld+json",
51
- dangerouslySetInnerHTML: { __html: jsonLdHtml(personJsonLd(members)) }
52
- }
53
- )
54
- ] });
55
- }
56
3
  const teamBlock = defineBlock({
57
4
  type: "team",
58
- Render: TeamRender,
5
+ Render: dynamic(() => import("./TeamRender.js").then((m) => m.TeamRender)),
59
6
  label: "Team",
60
7
  icon: "\u{1F465}",
61
8
  defaults: () => ({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Team.tsx"],"sourcesContent":["\"use client\";\n\nimport type { z } from \"zod\";\nimport type { TeamBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { VStack, HStack, Grid, Card, Heading, Text, Avatar } from \"../ui\";\nimport { jsonLdHtml } from \"./jsonld\";\n\ntype Team = z.infer<typeof TeamBlockSchema>;\n\nfunction personJsonLd(members: Team[\"members\"]): Record<string, unknown> {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"ItemList\",\n itemListElement: members\n .filter((m) => m.name)\n .map((m, i) => ({\n \"@type\": \"ListItem\",\n position: i + 1,\n item: {\n \"@type\": \"Person\",\n name: m.name,\n ...(m.role ? { jobTitle: m.role } : {}),\n ...(m.email ? { email: m.email } : {}),\n ...(m.phone ? { telephone: m.phone } : {}),\n ...(m.photoUrl ? { image: m.photoUrl } : {}),\n ...(m.social?.length ? { sameAs: m.social.map((s) => s.href) } : {}),\n },\n })),\n };\n}\n\n/** Team-/medewerkersoverzicht als kaarten (Astryx), optioneel gegroepeerd per afdeling. */\nfunction TeamRender({ block }: BlockRenderProps<Team>) {\n // _i = index in de OORSPRONKELIJKE array — nodig voor de inline-edit-paden bij groepering.\n const members = (block.members ?? []).map((m, i) => ({ ...m, _i: i })).filter((m) => m.name);\n if (members.length === 0) return null;\n\n // Groepen in volgorde van eerste voorkomen; leden zonder afdeling achteraan onder één kop.\n const groups = block.groupByDepartment\n ? [...new Set(members.map((m) => m.department ?? \"\"))]\n .sort((a, b) => (a === \"\" ? 1 : b === \"\" ? -1 : 0))\n .map((dep) => ({ dep, members: members.filter((m) => (m.department ?? \"\") === dep) }))\n : [{ dep: \"\", members }];\n\n const grid = (list: typeof members) => (\n <Grid columns={{ minWidth: 220, max: 4 }} gap={4}>\n {list.map((m) => {\n const i = m._i;\n return (\n <Card key={i} padding={4} height=\"100%\">\n <VStack gap={1} hAlign=\"center\" style={{ textAlign: \"center\", height: \"100%\" }}>\n <Avatar src={m.photoUrl} name={m.name} size={96} />\n <Heading level={3} data-xicms-edit={`members.${i}.name`}>{m.name}</Heading>\n {m.role && <Text type=\"label\" data-xicms-edit={`members.${i}.role`} style={{ color: \"var(--color-accent)\" }}>{m.role}</Text>}\n {m.bio && <Text color=\"secondary\" data-xicms-edit={`members.${i}.bio`}>{m.bio}</Text>}\n {m.email && (\n <a href={`mailto:${m.email}`} style={{ color: \"var(--color-accent)\" }}>\n <Text type=\"supporting\" data-xicms-edit={`members.${i}.email`}>{m.email}</Text>\n </a>\n )}\n {m.phone && <Text type=\"supporting\" color=\"secondary\" data-xicms-edit={`members.${i}.phone`}>{m.phone}</Text>}\n {m.social && m.social.length > 0 && (\n <HStack gap={2} hAlign=\"center\" as=\"ul\" style={{ listStyle: \"none\", margin: 0, padding: 0, flexWrap: \"wrap\" }}>\n {m.social.map((s, j) => (\n <li key={j}>\n <a href={s.href} rel=\"noopener noreferrer\" target=\"_blank\" style={{ color: \"var(--color-accent)\" }}>\n <Text type=\"supporting\" data-xicms-edit={`members.${i}.social.${j}.platform`}>{s.platform}</Text>\n </a>\n </li>\n ))}\n </HStack>\n )}\n </VStack>\n </Card>\n );\n })}\n </Grid>\n );\n\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-team\">\n {block.heading && (\n <Heading level={2} justify=\"center\" data-xicms-edit=\"heading\" style={{ textAlign: \"center\" }}>{block.heading}</Heading>\n )}\n {groups.map(({ dep, members: list }) => (\n <VStack key={dep || \"_overig\"} gap={3}>\n {block.groupByDepartment && groups.length > 1 && (\n <Heading level={3} justify=\"center\" style={{ textAlign: \"center\" }}>\n {dep || \"Overig\"}\n </Heading>\n )}\n {grid(list)}\n </VStack>\n ))}\n {block.jsonLd && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: jsonLdHtml(personJsonLd(members)) }}\n />\n )}\n </VStack>\n );\n}\n\nexport const teamBlock = defineBlock({\n type: \"team\",\n Render: TeamRender,\n label: \"Team\",\n icon: \"👥\",\n defaults: () => ({\n heading: \"Ons team\",\n members: [{ name: \"Naam Medewerker\", role: \"Functie\" }],\n }),\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Team.tsx"],"sourcesContent":["import dynamic from \"next/dynamic\";\nimport { defineBlock } from \"../registry\";\n\nexport const teamBlock = defineBlock({\n type: \"team\",\n Render: dynamic(() => import(\"./TeamRender\").then((m) => m.TeamRender)),\n label: \"Team\",\n icon: \"👥\",\n defaults: () => ({\n heading: \"Ons team\",\n members: [{ name: \"Naam Medewerker\", role: \"Functie\" }],\n }),\n});\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,mBAAmB;AAErB,MAAM,YAAY,YAAY;AAAA,EACnC,MAAM;AAAA,EACN,QAAQ,QAAQ,MAAM,OAAO,cAAc,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC;AAAA,EACtE,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO;AAAA,IACf,SAAS;AAAA,IACT,SAAS,CAAC,EAAE,MAAM,mBAAmB,MAAM,UAAU,CAAC;AAAA,EACxD;AACF,CAAC;","names":[]}
@@ -0,0 +1,7 @@
1
+ import type { z } from "zod";
2
+ import type { TeamBlockSchema } from "../../schema/blocks.js";
3
+ import type { BlockRenderProps } from "../registry.js";
4
+ type Team = z.infer<typeof TeamBlockSchema>;
5
+ /** Team-/medewerkersoverzicht als kaarten (Astryx), optioneel gegroepeerd per afdeling. */
6
+ export declare function TeamRender({ block }: BlockRenderProps<Team>): import("react").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { VStack, HStack, Grid, Card, Heading, Text, Avatar } from "../ui/index.js";
4
+ import { jsonLdHtml } from "./jsonld.js";
5
+ function personJsonLd(members) {
6
+ return {
7
+ "@context": "https://schema.org",
8
+ "@type": "ItemList",
9
+ itemListElement: members.filter((m) => m.name).map((m, i) => ({
10
+ "@type": "ListItem",
11
+ position: i + 1,
12
+ item: {
13
+ "@type": "Person",
14
+ name: m.name,
15
+ ...m.role ? { jobTitle: m.role } : {},
16
+ ...m.email ? { email: m.email } : {},
17
+ ...m.phone ? { telephone: m.phone } : {},
18
+ ...m.photoUrl ? { image: m.photoUrl } : {},
19
+ ...m.social?.length ? { sameAs: m.social.map((s) => s.href) } : {}
20
+ }
21
+ }))
22
+ };
23
+ }
24
+ function TeamRender({ block }) {
25
+ const members = (block.members ?? []).map((m, i) => ({ ...m, _i: i })).filter((m) => m.name);
26
+ if (members.length === 0) return null;
27
+ const groups = block.groupByDepartment ? [...new Set(members.map((m) => m.department ?? ""))].sort((a, b) => a === "" ? 1 : b === "" ? -1 : 0).map((dep) => ({ dep, members: members.filter((m) => (m.department ?? "") === dep) })) : [{ dep: "", members }];
28
+ const grid = (list) => /* @__PURE__ */ jsx(Grid, { columns: { minWidth: 220, max: 4 }, gap: 4, children: list.map((m) => {
29
+ const i = m._i;
30
+ return /* @__PURE__ */ jsx(Card, { padding: 4, height: "100%", children: /* @__PURE__ */ jsxs(VStack, { gap: 1, hAlign: "center", style: { textAlign: "center", height: "100%" }, children: [
31
+ /* @__PURE__ */ jsx(Avatar, { src: m.photoUrl, name: m.name, size: 96 }),
32
+ /* @__PURE__ */ jsx(Heading, { level: 3, "data-xicms-edit": `members.${i}.name`, children: m.name }),
33
+ m.role && /* @__PURE__ */ jsx(Text, { type: "label", "data-xicms-edit": `members.${i}.role`, style: { color: "var(--color-accent)" }, children: m.role }),
34
+ m.bio && /* @__PURE__ */ jsx(Text, { color: "secondary", "data-xicms-edit": `members.${i}.bio`, children: m.bio }),
35
+ m.email && /* @__PURE__ */ jsx("a", { href: `mailto:${m.email}`, style: { color: "var(--color-accent)" }, children: /* @__PURE__ */ jsx(Text, { type: "supporting", "data-xicms-edit": `members.${i}.email`, children: m.email }) }),
36
+ m.phone && /* @__PURE__ */ jsx(Text, { type: "supporting", color: "secondary", "data-xicms-edit": `members.${i}.phone`, children: m.phone }),
37
+ m.social && m.social.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: 2, hAlign: "center", as: "ul", style: { listStyle: "none", margin: 0, padding: 0, flexWrap: "wrap" }, children: m.social.map((s, j) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: s.href, rel: "noopener noreferrer", target: "_blank", style: { color: "var(--color-accent)" }, children: /* @__PURE__ */ jsx(Text, { type: "supporting", "data-xicms-edit": `members.${i}.social.${j}.platform`, children: s.platform }) }) }, j)) })
38
+ ] }) }, i);
39
+ }) });
40
+ return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-team", children: [
41
+ block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, justify: "center", "data-xicms-edit": "heading", style: { textAlign: "center" }, children: block.heading }),
42
+ groups.map(({ dep, members: list }) => /* @__PURE__ */ jsxs(VStack, { gap: 3, children: [
43
+ block.groupByDepartment && groups.length > 1 && /* @__PURE__ */ jsx(Heading, { level: 3, justify: "center", style: { textAlign: "center" }, children: dep || "Overig" }),
44
+ grid(list)
45
+ ] }, dep || "_overig")),
46
+ block.jsonLd && /* @__PURE__ */ jsx(
47
+ "script",
48
+ {
49
+ type: "application/ld+json",
50
+ dangerouslySetInnerHTML: { __html: jsonLdHtml(personJsonLd(members)) }
51
+ }
52
+ )
53
+ ] });
54
+ }
55
+ export {
56
+ TeamRender
57
+ };
58
+ //# sourceMappingURL=TeamRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/builtins/TeamRender.tsx"],"sourcesContent":["\"use client\";\n\nimport type { z } from \"zod\";\nimport type { TeamBlockSchema } from \"../../schema/blocks\";\nimport type { BlockRenderProps } from \"../registry\";\nimport { VStack, HStack, Grid, Card, Heading, Text, Avatar } from \"../ui\";\nimport { jsonLdHtml } from \"./jsonld\";\n\ntype Team = z.infer<typeof TeamBlockSchema>;\n\nfunction personJsonLd(members: Team[\"members\"]): Record<string, unknown> {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"ItemList\",\n itemListElement: members\n .filter((m) => m.name)\n .map((m, i) => ({\n \"@type\": \"ListItem\",\n position: i + 1,\n item: {\n \"@type\": \"Person\",\n name: m.name,\n ...(m.role ? { jobTitle: m.role } : {}),\n ...(m.email ? { email: m.email } : {}),\n ...(m.phone ? { telephone: m.phone } : {}),\n ...(m.photoUrl ? { image: m.photoUrl } : {}),\n ...(m.social?.length ? { sameAs: m.social.map((s) => s.href) } : {}),\n },\n })),\n };\n}\n\n/** Team-/medewerkersoverzicht als kaarten (Astryx), optioneel gegroepeerd per afdeling. */\nexport function TeamRender({ block }: BlockRenderProps<Team>) {\n // _i = index in de OORSPRONKELIJKE array — nodig voor de inline-edit-paden bij groepering.\n const members = (block.members ?? []).map((m, i) => ({ ...m, _i: i })).filter((m) => m.name);\n if (members.length === 0) return null;\n\n // Groepen in volgorde van eerste voorkomen; leden zonder afdeling achteraan onder één kop.\n const groups = block.groupByDepartment\n ? [...new Set(members.map((m) => m.department ?? \"\"))]\n .sort((a, b) => (a === \"\" ? 1 : b === \"\" ? -1 : 0))\n .map((dep) => ({ dep, members: members.filter((m) => (m.department ?? \"\") === dep) }))\n : [{ dep: \"\", members }];\n\n const grid = (list: typeof members) => (\n <Grid columns={{ minWidth: 220, max: 4 }} gap={4}>\n {list.map((m) => {\n const i = m._i;\n return (\n <Card key={i} padding={4} height=\"100%\">\n <VStack gap={1} hAlign=\"center\" style={{ textAlign: \"center\", height: \"100%\" }}>\n <Avatar src={m.photoUrl} name={m.name} size={96} />\n <Heading level={3} data-xicms-edit={`members.${i}.name`}>{m.name}</Heading>\n {m.role && <Text type=\"label\" data-xicms-edit={`members.${i}.role`} style={{ color: \"var(--color-accent)\" }}>{m.role}</Text>}\n {m.bio && <Text color=\"secondary\" data-xicms-edit={`members.${i}.bio`}>{m.bio}</Text>}\n {m.email && (\n <a href={`mailto:${m.email}`} style={{ color: \"var(--color-accent)\" }}>\n <Text type=\"supporting\" data-xicms-edit={`members.${i}.email`}>{m.email}</Text>\n </a>\n )}\n {m.phone && <Text type=\"supporting\" color=\"secondary\" data-xicms-edit={`members.${i}.phone`}>{m.phone}</Text>}\n {m.social && m.social.length > 0 && (\n <HStack gap={2} hAlign=\"center\" as=\"ul\" style={{ listStyle: \"none\", margin: 0, padding: 0, flexWrap: \"wrap\" }}>\n {m.social.map((s, j) => (\n <li key={j}>\n <a href={s.href} rel=\"noopener noreferrer\" target=\"_blank\" style={{ color: \"var(--color-accent)\" }}>\n <Text type=\"supporting\" data-xicms-edit={`members.${i}.social.${j}.platform`}>{s.platform}</Text>\n </a>\n </li>\n ))}\n </HStack>\n )}\n </VStack>\n </Card>\n );\n })}\n </Grid>\n );\n\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-team\">\n {block.heading && (\n <Heading level={2} justify=\"center\" data-xicms-edit=\"heading\" style={{ textAlign: \"center\" }}>{block.heading}</Heading>\n )}\n {groups.map(({ dep, members: list }) => (\n <VStack key={dep || \"_overig\"} gap={3}>\n {block.groupByDepartment && groups.length > 1 && (\n <Heading level={3} justify=\"center\" style={{ textAlign: \"center\" }}>\n {dep || \"Overig\"}\n </Heading>\n )}\n {grid(list)}\n </VStack>\n ))}\n {block.jsonLd && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: jsonLdHtml(personJsonLd(members)) }}\n />\n )}\n </VStack>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,8 +1 @@
1
- import type { z } from "zod";
2
- import type { TestimonialsBlockSchema } from "../../schema/blocks.js";
3
- type Testimonials = z.infer<typeof TestimonialsBlockSchema>;
4
- /** Gemiddelde beoordeling over de items met een rating — voor de sterren-snippet in Google. */
5
- export declare function aggregateRatingJsonLd(items: Testimonials["items"]): Record<string, unknown> | null;
6
- export declare function reviewJsonLd(items: Testimonials["items"]): Record<string, unknown>;
7
- export declare const testimonialsBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
8
- export {};
1
+ export declare const testimonialsBlock: import("../index.js").BlockDefinition<import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>;
@@ -1,73 +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, HStack, Grid, Card, Heading, Text, Avatar } from "../ui/index.js";
5
- import { jsonLdHtml } from "./jsonld.js";
6
- function aggregateRatingJsonLd(items) {
7
- const rated = items.filter((it) => typeof it.rating === "number");
8
- if (rated.length === 0) return null;
9
- const avg = rated.reduce((sum, it) => sum + it.rating, 0) / rated.length;
10
- return {
11
- "@type": "AggregateRating",
12
- ratingValue: Math.round(avg * 10) / 10,
13
- reviewCount: rated.length,
14
- bestRating: 5
15
- };
16
- }
17
- function reviewJsonLd(items) {
18
- const aggregate = aggregateRatingJsonLd(items);
19
- return {
20
- "@context": "https://schema.org",
21
- "@type": "LocalBusiness",
22
- "@id": "#business",
23
- ...aggregate ? { aggregateRating: aggregate } : {},
24
- review: items.filter((it) => it.quote && it.author).map((it) => ({
25
- "@type": "Review",
26
- reviewBody: it.quote,
27
- author: { "@type": "Person", name: it.author },
28
- ...it.rating ? { reviewRating: { "@type": "Rating", ratingValue: it.rating, bestRating: 5 } } : {}
29
- }))
30
- };
31
- }
32
- function TestimonialsRender({ block }) {
33
- const items = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.quote);
34
- if (items.length === 0) return null;
35
- const cols = block.layout === "list" ? { minWidth: 320, max: 1 } : { minWidth: 280, max: 3 };
36
- return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-testimonials", children: [
37
- block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, justify: "center", "data-xicms-edit": "heading", style: { textAlign: "center" }, children: block.heading }),
38
- /* @__PURE__ */ jsx(Grid, { columns: cols, gap: 4, children: items.map((it) => {
39
- const i = it._i;
40
- return /* @__PURE__ */ jsx(Card, { padding: 4, height: "100%", className: "xicms-testimonials__card", children: /* @__PURE__ */ jsxs(VStack, { gap: 2, style: { height: "100%" }, children: [
41
- typeof it.rating === "number" && /* @__PURE__ */ jsxs(Text, { className: "xicms-testimonials__rating", style: { color: "var(--xicms-star, #f59e0b)", letterSpacing: "0.1em" }, "aria-label": `${it.rating} van 5 sterren`, children: [
42
- "\u2605".repeat(it.rating),
43
- /* @__PURE__ */ jsx("span", { style: { opacity: 0.25 }, children: "\u2605".repeat(5 - it.rating) })
44
- ] }),
45
- /* @__PURE__ */ jsxs(Text, { type: "large", className: "xicms-testimonials__quote", style: { flex: 1 }, children: [
46
- "\u201C",
47
- /* @__PURE__ */ jsx("span", { "data-xicms-edit": `items.${i}.quote`, children: it.quote }),
48
- "\u201D"
49
- ] }),
50
- /* @__PURE__ */ jsxs(HStack, { gap: 1.5, vAlign: "center", children: [
51
- it.avatarUrl && /* @__PURE__ */ jsx(Avatar, { src: it.avatarUrl, name: it.author, size: 40 }),
52
- /* @__PURE__ */ jsxs(VStack, { gap: 0, children: [
53
- /* @__PURE__ */ jsx(Text, { weight: "bold", className: "xicms-testimonials__author", "data-xicms-edit": `items.${i}.author`, children: it.author }),
54
- it.role && /* @__PURE__ */ jsx(Text, { type: "supporting", color: "secondary", className: "xicms-testimonials__role", "data-xicms-edit": `items.${i}.role`, children: it.role })
55
- ] })
56
- ] })
57
- ] }) }, i);
58
- }) }),
59
- block.jsonLd && /* @__PURE__ */ jsx(
60
- "script",
61
- {
62
- type: "application/ld+json",
63
- dangerouslySetInnerHTML: { __html: jsonLdHtml(reviewJsonLd(items)) }
64
- }
65
- )
66
- ] });
67
- }
68
3
  const testimonialsBlock = defineBlock({
69
4
  type: "testimonials",
70
- Render: TestimonialsRender,
5
+ Render: dynamic(() => import("./TestimonialsRender.js").then((m) => m.TestimonialsRender)),
71
6
  label: "Reviews",
72
7
  icon: "\u2B50",
73
8
  defaults: () => ({
@@ -77,8 +12,6 @@ const testimonialsBlock = defineBlock({
77
12
  })
78
13
  });
79
14
  export {
80
- aggregateRatingJsonLd,
81
- reviewJsonLd,
82
15
  testimonialsBlock
83
16
  };
84
17
  //# sourceMappingURL=Testimonials.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Testimonials.tsx"],"sourcesContent":["\"use client\";\n\nimport type { z } from \"zod\";\nimport type { TestimonialsBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { VStack, HStack, Grid, Card, Heading, Text, Avatar } from \"../ui\";\nimport { jsonLdHtml } from \"./jsonld\";\n\ntype Testimonials = z.infer<typeof TestimonialsBlockSchema>;\n\n/** Gemiddelde beoordeling over de items met een rating — voor de sterren-snippet in Google. */\nexport function aggregateRatingJsonLd(items: Testimonials[\"items\"]): Record<string, unknown> | null {\n const rated = items.filter((it) => typeof it.rating === \"number\");\n if (rated.length === 0) return null;\n const avg = rated.reduce((sum, it) => sum + (it.rating as number), 0) / rated.length;\n return {\n \"@type\": \"AggregateRating\",\n ratingValue: Math.round(avg * 10) / 10,\n reviewCount: rated.length,\n bestRating: 5,\n };\n}\n\nexport function reviewJsonLd(items: Testimonials[\"items\"]): Record<string, unknown> {\n const aggregate = aggregateRatingJsonLd(items);\n // Reviews/AggregateRating zijn voor rich results alleen geldig aan een \"ding\" (bedrijf/product);\n // een losse ItemList negeert Google. We hangen ze aan dezelfde #business-entiteit als de\n // contact-/openingstijden-JSON-LD, zodat alles samenvloeit tot één LocalBusiness.\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"LocalBusiness\",\n \"@id\": \"#business\",\n ...(aggregate ? { aggregateRating: aggregate } : {}),\n review: items\n .filter((it) => it.quote && it.author)\n .map((it) => ({\n \"@type\": \"Review\",\n reviewBody: it.quote,\n author: { \"@type\": \"Person\", name: it.author },\n ...(it.rating\n ? { reviewRating: { \"@type\": \"Rating\", ratingValue: it.rating, bestRating: 5 } }\n : {}),\n })),\n };\n}\n\n/** Klant­quotes / reviews als kaarten (Astryx). */\nfunction TestimonialsRender({ block }: BlockRenderProps<Testimonials>) {\n // _i = index in de OORSPRONKELIJKE array — inline-edit-paden (items.N.quote) moeten daarheen wijzen.\n const items = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.quote);\n if (items.length === 0) return null;\n const cols = block.layout === \"list\" ? { minWidth: 320, max: 1 as const } : { minWidth: 280, max: 3 as const };\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-testimonials\">\n {block.heading && (\n <Heading level={2} justify=\"center\" data-xicms-edit=\"heading\" style={{ textAlign: \"center\" }}>{block.heading}</Heading>\n )}\n <Grid columns={cols} gap={4}>\n {items.map((it) => {\n const i = it._i;\n return (\n <Card key={i} padding={4} height=\"100%\" className=\"xicms-testimonials__card\">\n <VStack gap={2} style={{ height: \"100%\" }}>\n {typeof it.rating === \"number\" && (\n <Text className=\"xicms-testimonials__rating\" style={{ color: \"var(--xicms-star, #f59e0b)\", letterSpacing: \"0.1em\" }} aria-label={`${it.rating} van 5 sterren`}>\n {\"★\".repeat(it.rating)}\n <span style={{ opacity: 0.25 }}>{\"★\".repeat(5 - it.rating)}</span>\n </Text>\n )}\n <Text type=\"large\" className=\"xicms-testimonials__quote\" style={{ flex: 1 }}>“<span data-xicms-edit={`items.${i}.quote`}>{it.quote}</span>”</Text>\n <HStack gap={1.5} vAlign=\"center\">\n {it.avatarUrl && <Avatar src={it.avatarUrl} name={it.author} size={40} />}\n <VStack gap={0}>\n <Text weight=\"bold\" className=\"xicms-testimonials__author\" data-xicms-edit={`items.${i}.author`}>{it.author}</Text>\n {it.role && <Text type=\"supporting\" color=\"secondary\" className=\"xicms-testimonials__role\" data-xicms-edit={`items.${i}.role`}>{it.role}</Text>}\n </VStack>\n </HStack>\n </VStack>\n </Card>\n );\n })}\n </Grid>\n {block.jsonLd && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: jsonLdHtml(reviewJsonLd(items)) }}\n />\n )}\n </VStack>\n );\n}\n\nexport const testimonialsBlock = defineBlock({\n type: \"testimonials\",\n Render: TestimonialsRender,\n label: \"Reviews\",\n icon: \"⭐\",\n defaults: () => ({\n heading: \"Wat klanten zeggen\",\n items: [{ quote: \"Top geholpen!\", author: \"Tevreden klant\", rating: 5 }],\n jsonLd: true,\n }),\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Testimonials.tsx"],"sourcesContent":["import dynamic from \"next/dynamic\";\nimport { defineBlock } from \"../registry\";\n\nexport const testimonialsBlock = defineBlock({\n type: \"testimonials\",\n Render: dynamic(() => import(\"./TestimonialsRender\").then((m) => m.TestimonialsRender)),\n label: \"Reviews\",\n icon: \"⭐\",\n defaults: () => ({\n heading: \"Wat klanten zeggen\",\n items: [{ quote: \"Top geholpen!\", author: \"Tevreden klant\", rating: 5 }],\n jsonLd: true,\n }),\n});\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,mBAAmB;AAErB,MAAM,oBAAoB,YAAY;AAAA,EAC3C,MAAM;AAAA,EACN,QAAQ,QAAQ,MAAM,OAAO,sBAAsB,EAAE,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC;AAAA,EACtF,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO;AAAA,IACf,SAAS;AAAA,IACT,OAAO,CAAC,EAAE,OAAO,iBAAiB,QAAQ,kBAAkB,QAAQ,EAAE,CAAC;AAAA,IACvE,QAAQ;AAAA,EACV;AACF,CAAC;","names":[]}
@@ -0,0 +1,10 @@
1
+ import type { z } from "zod";
2
+ import type { TestimonialsBlockSchema } from "../../schema/blocks.js";
3
+ import type { BlockRenderProps } from "../registry.js";
4
+ type Testimonials = z.infer<typeof TestimonialsBlockSchema>;
5
+ /** Gemiddelde beoordeling over de items met een rating — voor de sterren-snippet in Google. */
6
+ export declare function aggregateRatingJsonLd(items: Testimonials["items"]): Record<string, unknown> | null;
7
+ export declare function reviewJsonLd(items: Testimonials["items"]): Record<string, unknown>;
8
+ /** Klant­quotes / reviews als kaarten (Astryx). */
9
+ export declare function TestimonialsRender({ block }: BlockRenderProps<Testimonials>): import("react").JSX.Element | null;
10
+ export {};
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { VStack, HStack, Grid, Card, Heading, Text, Avatar } from "../ui/index.js";
4
+ import { jsonLdHtml } from "./jsonld.js";
5
+ function aggregateRatingJsonLd(items) {
6
+ const rated = items.filter((it) => typeof it.rating === "number");
7
+ if (rated.length === 0) return null;
8
+ const avg = rated.reduce((sum, it) => sum + it.rating, 0) / rated.length;
9
+ return {
10
+ "@type": "AggregateRating",
11
+ ratingValue: Math.round(avg * 10) / 10,
12
+ reviewCount: rated.length,
13
+ bestRating: 5
14
+ };
15
+ }
16
+ function reviewJsonLd(items) {
17
+ const aggregate = aggregateRatingJsonLd(items);
18
+ return {
19
+ "@context": "https://schema.org",
20
+ "@type": "LocalBusiness",
21
+ "@id": "#business",
22
+ ...aggregate ? { aggregateRating: aggregate } : {},
23
+ review: items.filter((it) => it.quote && it.author).map((it) => ({
24
+ "@type": "Review",
25
+ reviewBody: it.quote,
26
+ author: { "@type": "Person", name: it.author },
27
+ ...it.rating ? { reviewRating: { "@type": "Rating", ratingValue: it.rating, bestRating: 5 } } : {}
28
+ }))
29
+ };
30
+ }
31
+ function TestimonialsRender({ block }) {
32
+ const items = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.quote);
33
+ if (items.length === 0) return null;
34
+ const cols = block.layout === "list" ? { minWidth: 320, max: 1 } : { minWidth: 280, max: 3 };
35
+ return /* @__PURE__ */ jsxs(VStack, { gap: 4, as: "section", className: "xicms-testimonials", children: [
36
+ block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, justify: "center", "data-xicms-edit": "heading", style: { textAlign: "center" }, children: block.heading }),
37
+ /* @__PURE__ */ jsx(Grid, { columns: cols, gap: 4, children: items.map((it) => {
38
+ const i = it._i;
39
+ return /* @__PURE__ */ jsx(Card, { padding: 4, height: "100%", className: "xicms-testimonials__card", children: /* @__PURE__ */ jsxs(VStack, { gap: 2, style: { height: "100%" }, children: [
40
+ typeof it.rating === "number" && /* @__PURE__ */ jsxs(Text, { className: "xicms-testimonials__rating", style: { color: "var(--xicms-star, #f59e0b)", letterSpacing: "0.1em" }, "aria-label": `${it.rating} van 5 sterren`, children: [
41
+ "\u2605".repeat(it.rating),
42
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.25 }, children: "\u2605".repeat(5 - it.rating) })
43
+ ] }),
44
+ /* @__PURE__ */ jsxs(Text, { type: "large", className: "xicms-testimonials__quote", style: { flex: 1 }, children: [
45
+ "\u201C",
46
+ /* @__PURE__ */ jsx("span", { "data-xicms-edit": `items.${i}.quote`, children: it.quote }),
47
+ "\u201D"
48
+ ] }),
49
+ /* @__PURE__ */ jsxs(HStack, { gap: 1.5, vAlign: "center", children: [
50
+ it.avatarUrl && /* @__PURE__ */ jsx(Avatar, { src: it.avatarUrl, name: it.author, size: 40 }),
51
+ /* @__PURE__ */ jsxs(VStack, { gap: 0, children: [
52
+ /* @__PURE__ */ jsx(Text, { weight: "bold", className: "xicms-testimonials__author", "data-xicms-edit": `items.${i}.author`, children: it.author }),
53
+ it.role && /* @__PURE__ */ jsx(Text, { type: "supporting", color: "secondary", className: "xicms-testimonials__role", "data-xicms-edit": `items.${i}.role`, children: it.role })
54
+ ] })
55
+ ] })
56
+ ] }) }, i);
57
+ }) }),
58
+ block.jsonLd && /* @__PURE__ */ jsx(
59
+ "script",
60
+ {
61
+ type: "application/ld+json",
62
+ dangerouslySetInnerHTML: { __html: jsonLdHtml(reviewJsonLd(items)) }
63
+ }
64
+ )
65
+ ] });
66
+ }
67
+ export {
68
+ TestimonialsRender,
69
+ aggregateRatingJsonLd,
70
+ reviewJsonLd
71
+ };
72
+ //# sourceMappingURL=TestimonialsRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/builtins/TestimonialsRender.tsx"],"sourcesContent":["\"use client\";\n\nimport type { z } from \"zod\";\nimport type { TestimonialsBlockSchema } from \"../../schema/blocks\";\nimport type { BlockRenderProps } from \"../registry\";\nimport { VStack, HStack, Grid, Card, Heading, Text, Avatar } from \"../ui\";\nimport { jsonLdHtml } from \"./jsonld\";\n\ntype Testimonials = z.infer<typeof TestimonialsBlockSchema>;\n\n/** Gemiddelde beoordeling over de items met een rating — voor de sterren-snippet in Google. */\nexport function aggregateRatingJsonLd(items: Testimonials[\"items\"]): Record<string, unknown> | null {\n const rated = items.filter((it) => typeof it.rating === \"number\");\n if (rated.length === 0) return null;\n const avg = rated.reduce((sum, it) => sum + (it.rating as number), 0) / rated.length;\n return {\n \"@type\": \"AggregateRating\",\n ratingValue: Math.round(avg * 10) / 10,\n reviewCount: rated.length,\n bestRating: 5,\n };\n}\n\nexport function reviewJsonLd(items: Testimonials[\"items\"]): Record<string, unknown> {\n const aggregate = aggregateRatingJsonLd(items);\n // Reviews/AggregateRating zijn voor rich results alleen geldig aan een \"ding\" (bedrijf/product);\n // een losse ItemList negeert Google. We hangen ze aan dezelfde #business-entiteit als de\n // contact-/openingstijden-JSON-LD, zodat alles samenvloeit tot één LocalBusiness.\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"LocalBusiness\",\n \"@id\": \"#business\",\n ...(aggregate ? { aggregateRating: aggregate } : {}),\n review: items\n .filter((it) => it.quote && it.author)\n .map((it) => ({\n \"@type\": \"Review\",\n reviewBody: it.quote,\n author: { \"@type\": \"Person\", name: it.author },\n ...(it.rating\n ? { reviewRating: { \"@type\": \"Rating\", ratingValue: it.rating, bestRating: 5 } }\n : {}),\n })),\n };\n}\n\n/** Klant­quotes / reviews als kaarten (Astryx). */\nexport function TestimonialsRender({ block }: BlockRenderProps<Testimonials>) {\n // _i = index in de OORSPRONKELIJKE array — inline-edit-paden (items.N.quote) moeten daarheen wijzen.\n const items = (block.items ?? []).map((it, i) => ({ ...it, _i: i })).filter((i) => i.quote);\n if (items.length === 0) return null;\n const cols = block.layout === \"list\" ? { minWidth: 320, max: 1 as const } : { minWidth: 280, max: 3 as const };\n return (\n <VStack gap={4} as=\"section\" className=\"xicms-testimonials\">\n {block.heading && (\n <Heading level={2} justify=\"center\" data-xicms-edit=\"heading\" style={{ textAlign: \"center\" }}>{block.heading}</Heading>\n )}\n <Grid columns={cols} gap={4}>\n {items.map((it) => {\n const i = it._i;\n return (\n <Card key={i} padding={4} height=\"100%\" className=\"xicms-testimonials__card\">\n <VStack gap={2} style={{ height: \"100%\" }}>\n {typeof it.rating === \"number\" && (\n <Text className=\"xicms-testimonials__rating\" style={{ color: \"var(--xicms-star, #f59e0b)\", letterSpacing: \"0.1em\" }} aria-label={`${it.rating} van 5 sterren`}>\n {\"★\".repeat(it.rating)}\n <span style={{ opacity: 0.25 }}>{\"★\".repeat(5 - it.rating)}</span>\n </Text>\n )}\n <Text type=\"large\" className=\"xicms-testimonials__quote\" style={{ flex: 1 }}>“<span data-xicms-edit={`items.${i}.quote`}>{it.quote}</span>”</Text>\n <HStack gap={1.5} vAlign=\"center\">\n {it.avatarUrl && <Avatar src={it.avatarUrl} name={it.author} size={40} />}\n <VStack gap={0}>\n <Text weight=\"bold\" className=\"xicms-testimonials__author\" data-xicms-edit={`items.${i}.author`}>{it.author}</Text>\n {it.role && <Text type=\"supporting\" color=\"secondary\" className=\"xicms-testimonials__role\" data-xicms-edit={`items.${i}.role`}>{it.role}</Text>}\n </VStack>\n </HStack>\n </VStack>\n </Card>\n );\n })}\n </Grid>\n {block.jsonLd && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: jsonLdHtml(reviewJsonLd(items)) }}\n />\n )}\n </VStack>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,9 +1 @@
1
- import type { z } from "zod";
2
- import type { VideoBlockSchema } from "../../schema/blocks.js";
3
- type Video = z.infer<typeof VideoBlockSchema>;
4
- /** Bouwt een embed-URL uit een id of volledige URL. */
5
- export declare function embedUrl(provider: Video["provider"], src: string, autoplay?: boolean): string;
6
- /** Thumbnail voor de click-to-play-facade: eigen poster of de YouTube-thumb. */
7
- export declare function facadeThumb(block: Pick<Video, "provider" | "src" | "posterUrl">): string | null;
8
- export declare const videoBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
9
- export {};
1
+ export declare const videoBlock: import("../index.js").BlockDefinition<import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>;
@@ -1,75 +1,13 @@
1
- "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
1
+ import dynamic from "next/dynamic";
4
2
  import { defineBlock } from "../registry.js";
5
- import { ConsentEmbed } from "./consentEmbed.js";
6
- function embedUrl(provider, src, autoplay = false) {
7
- if (provider === "youtube") {
8
- const id = src.match(/(?:v=|youtu\.be\/|embed\/)([\w-]{6,})/)?.[1] ?? src;
9
- return `https://www.youtube-nocookie.com/embed/${id}${autoplay ? "?autoplay=1" : ""}`;
10
- }
11
- if (provider === "vimeo") {
12
- const id = src.match(/(?:vimeo\.com\/)?(\d{6,})/)?.[1] ?? src;
13
- return `https://player.vimeo.com/video/${id}${autoplay ? "?autoplay=1" : ""}`;
14
- }
15
- return src;
16
- }
17
- function facadeThumb(block) {
18
- if (block.posterUrl) return block.posterUrl;
19
- if (block.provider === "youtube") {
20
- const id = block.src.match(/(?:v=|youtu\.be\/|embed\/)([\w-]{6,})/)?.[1] ?? block.src;
21
- return id ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : null;
22
- }
23
- return null;
24
- }
25
- function VideoRender({ block, mode }) {
26
- const [activated, setActivated] = useState(false);
27
- if (!block.src) return null;
28
- const useFacade = block.facade && block.provider !== "file" && !activated;
29
- const thumb = useFacade ? facadeThumb(block) : null;
30
- const media = block.provider === "file" ? /* @__PURE__ */ jsx("video", { className: "xicms-video__player", src: block.src, controls: true, poster: block.posterUrl || void 0 }) : useFacade ? (
31
- // Click-to-play: geen third-party iframe tot de bezoeker klikt (sneller + privacyvriendelijker).
32
- /* @__PURE__ */ jsxs(
33
- "button",
34
- {
35
- type: "button",
36
- className: "xicms-video__facade",
37
- onClick: () => mode !== "preview" && setActivated(true),
38
- "aria-label": `Video afspelen: ${block.title ?? "video"}`,
39
- children: [
40
- thumb ? (
41
- // eslint-disable-next-line @next/next/no-img-element
42
- /* @__PURE__ */ jsx("img", { src: thumb, alt: "", loading: "lazy" })
43
- ) : /* @__PURE__ */ jsx("span", { className: "xicms-video__facade-bg", "aria-hidden": true }),
44
- /* @__PURE__ */ jsx("span", { className: "xicms-video__play", "aria-hidden": true, children: "\u25B6" })
45
- ]
46
- }
47
- )
48
- ) : /* @__PURE__ */ jsx("div", { className: "xicms-video__frame", children: /* @__PURE__ */ jsx(
49
- "iframe",
50
- {
51
- src: embedUrl(block.provider, block.src, activated),
52
- title: block.title ?? "Video",
53
- loading: "lazy",
54
- allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
55
- allowFullScreen: true
56
- }
57
- ) });
58
- return /* @__PURE__ */ jsxs("figure", { className: "xicms-video", children: [
59
- /* @__PURE__ */ jsx(ConsentEmbed, { gated: block.consentGated && !useFacade && block.provider !== "file", preview: mode === "preview", label: "Video", children: media }),
60
- block.caption && /* @__PURE__ */ jsx("figcaption", { "data-xicms-edit": "caption", children: block.caption })
61
- ] });
62
- }
63
3
  const videoBlock = defineBlock({
64
4
  type: "video",
65
- Render: VideoRender,
5
+ Render: dynamic(() => import("./VideoRender.js").then((m) => m.VideoRender)),
66
6
  label: "Video",
67
7
  icon: "\u{1F3AC}",
68
8
  defaults: () => ({ provider: "youtube", src: "", consentGated: true })
69
9
  });
70
10
  export {
71
- embedUrl,
72
- facadeThumb,
73
11
  videoBlock
74
12
  };
75
13
  //# sourceMappingURL=Video.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Video.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport type { VideoBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { ConsentEmbed } from \"./consentEmbed\";\n\ntype Video = z.infer<typeof VideoBlockSchema>;\n\n/** Bouwt een embed-URL uit een id of volledige URL. */\nexport function embedUrl(provider: Video[\"provider\"], src: string, autoplay = false): string {\n if (provider === \"youtube\") {\n const id = src.match(/(?:v=|youtu\\.be\\/|embed\\/)([\\w-]{6,})/)?.[1] ?? src;\n return `https://www.youtube-nocookie.com/embed/${id}${autoplay ? \"?autoplay=1\" : \"\"}`;\n }\n if (provider === \"vimeo\") {\n const id = src.match(/(?:vimeo\\.com\\/)?(\\d{6,})/)?.[1] ?? src;\n return `https://player.vimeo.com/video/${id}${autoplay ? \"?autoplay=1\" : \"\"}`;\n }\n return src;\n}\n\n/** Thumbnail voor de click-to-play-facade: eigen poster of de YouTube-thumb. */\nexport function facadeThumb(block: Pick<Video, \"provider\" | \"src\" | \"posterUrl\">): string | null {\n if (block.posterUrl) return block.posterUrl;\n if (block.provider === \"youtube\") {\n const id = block.src.match(/(?:v=|youtu\\.be\\/|embed\\/)([\\w-]{6,})/)?.[1] ?? block.src;\n return id ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : null;\n }\n return null; // vimeo-thumbs vereisen een API-call; zonder poster geen facade-beeld\n}\n\nfunction VideoRender({ block, mode }: BlockRenderProps<Video>) {\n const [activated, setActivated] = useState(false);\n if (!block.src) return null;\n\n const useFacade = block.facade && block.provider !== \"file\" && !activated;\n const thumb = useFacade ? facadeThumb(block) : null;\n\n const media =\n block.provider === \"file\" ? (\n <video className=\"xicms-video__player\" src={block.src} controls poster={block.posterUrl || undefined} />\n ) : useFacade ? (\n // Click-to-play: geen third-party iframe tot de bezoeker klikt (sneller + privacyvriendelijker).\n <button\n type=\"button\"\n className=\"xicms-video__facade\"\n onClick={() => mode !== \"preview\" && setActivated(true)}\n aria-label={`Video afspelen: ${block.title ?? \"video\"}`}\n >\n {thumb ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={thumb} alt=\"\" loading=\"lazy\" />\n ) : (\n <span className=\"xicms-video__facade-bg\" aria-hidden />\n )}\n <span className=\"xicms-video__play\" aria-hidden>▶</span>\n </button>\n ) : (\n <div className=\"xicms-video__frame\">\n <iframe\n src={embedUrl(block.provider, block.src, activated)}\n title={block.title ?? \"Video\"}\n loading=\"lazy\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n </div>\n );\n return (\n <figure className=\"xicms-video\">\n {/* Zelf-gehost bestand (provider \"file\") doet géén third-party-request en zet geen cookies —\n daar is geen cookie-toestemming voor nodig; alleen youtube/vimeo-embeds gaten. */}\n <ConsentEmbed gated={block.consentGated && !useFacade && block.provider !== \"file\"} preview={mode === \"preview\"} label=\"Video\">{media}</ConsentEmbed>\n {block.caption && <figcaption data-xicms-edit=\"caption\">{block.caption}</figcaption>}\n </figure>\n );\n}\n\nexport const videoBlock = defineBlock({\n type: \"video\",\n Render: VideoRender,\n label: \"Video\",\n icon: \"🎬\",\n defaults: () => ({ provider: \"youtube\", src: \"\", consentGated: true }),\n});\n"],"mappings":";AA0CM,cAGA,YAHA;AAxCN,SAAS,gBAAgB;AAGzB,SAAS,mBAA0C;AACnD,SAAS,oBAAoB;AAKtB,SAAS,SAAS,UAA6B,KAAa,WAAW,OAAe;AAC3F,MAAI,aAAa,WAAW;AAC1B,UAAM,KAAK,IAAI,MAAM,uCAAuC,IAAI,CAAC,KAAK;AACtE,WAAO,0CAA0C,EAAE,GAAG,WAAW,gBAAgB,EAAE;AAAA,EACrF;AACA,MAAI,aAAa,SAAS;AACxB,UAAM,KAAK,IAAI,MAAM,2BAA2B,IAAI,CAAC,KAAK;AAC1D,WAAO,kCAAkC,EAAE,GAAG,WAAW,gBAAgB,EAAE;AAAA,EAC7E;AACA,SAAO;AACT;AAGO,SAAS,YAAY,OAAqE;AAC/F,MAAI,MAAM,UAAW,QAAO,MAAM;AAClC,MAAI,MAAM,aAAa,WAAW;AAChC,UAAM,KAAK,MAAM,IAAI,MAAM,uCAAuC,IAAI,CAAC,KAAK,MAAM;AAClF,WAAO,KAAK,0BAA0B,EAAE,mBAAmB;AAAA,EAC7D;AACA,SAAO;AACT;AAEA,SAAS,YAAY,EAAE,OAAO,KAAK,GAA4B;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,MAAI,CAAC,MAAM,IAAK,QAAO;AAEvB,QAAM,YAAY,MAAM,UAAU,MAAM,aAAa,UAAU,CAAC;AAChE,QAAM,QAAQ,YAAY,YAAY,KAAK,IAAI;AAE/C,QAAM,QACJ,MAAM,aAAa,SACjB,oBAAC,WAAM,WAAU,uBAAsB,KAAK,MAAM,KAAK,UAAQ,MAAC,QAAQ,MAAM,aAAa,QAAW,IACpG;AAAA;AAAA,IAEF;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,SAAS,aAAa,aAAa,IAAI;AAAA,QACtD,cAAY,mBAAmB,MAAM,SAAS,OAAO;AAAA,QAEpD;AAAA;AAAA;AAAA,YAEC,oBAAC,SAAI,KAAK,OAAO,KAAI,IAAG,SAAQ,QAAO;AAAA,cAEvC,oBAAC,UAAK,WAAU,0BAAyB,eAAW,MAAC;AAAA,UAEvD,oBAAC,UAAK,WAAU,qBAAoB,eAAW,MAAC,oBAAC;AAAA;AAAA;AAAA,IACnD;AAAA,MAEA,oBAAC,SAAI,WAAU,sBACb;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,SAAS,MAAM,UAAU,MAAM,KAAK,SAAS;AAAA,MAClD,OAAO,MAAM,SAAS;AAAA,MACtB,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,iBAAe;AAAA;AAAA,EACjB,GACF;AAEJ,SACE,qBAAC,YAAO,WAAU,eAGhB;AAAA,wBAAC,gBAAa,OAAO,MAAM,gBAAgB,CAAC,aAAa,MAAM,aAAa,QAAQ,SAAS,SAAS,WAAW,OAAM,SAAS,iBAAM;AAAA,IACrI,MAAM,WAAW,oBAAC,gBAAW,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,KACzE;AAEJ;AAEO,MAAM,aAAa,YAAY;AAAA,EACpC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,UAAU,WAAW,KAAK,IAAI,cAAc,KAAK;AACtE,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Video.tsx"],"sourcesContent":["import dynamic from \"next/dynamic\";\nimport { defineBlock } from \"../registry\";\n\nexport const videoBlock = defineBlock({\n type: \"video\",\n Render: dynamic(() => import(\"./VideoRender\").then((m) => m.VideoRender)),\n label: \"Video\",\n icon: \"🎬\",\n defaults: () => ({ provider: \"youtube\", src: \"\", consentGated: true }),\n});\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,mBAAmB;AAErB,MAAM,aAAa,YAAY;AAAA,EACpC,MAAM;AAAA,EACN,QAAQ,QAAQ,MAAM,OAAO,eAAe,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC;AAAA,EACxE,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,UAAU,WAAW,KAAK,IAAI,cAAc,KAAK;AACtE,CAAC;","names":[]}
@@ -0,0 +1,10 @@
1
+ import type { z } from "zod";
2
+ import type { VideoBlockSchema } from "../../schema/blocks.js";
3
+ import type { BlockRenderProps } from "../registry.js";
4
+ type Video = z.infer<typeof VideoBlockSchema>;
5
+ /** Bouwt een embed-URL uit een id of volledige URL. */
6
+ export declare function embedUrl(provider: Video["provider"], src: string, autoplay?: boolean): string;
7
+ /** Thumbnail voor de click-to-play-facade: eigen poster of de YouTube-thumb. */
8
+ export declare function facadeThumb(block: Pick<Video, "provider" | "src" | "posterUrl">): string | null;
9
+ export declare function VideoRender({ block, mode }: BlockRenderProps<Video>): import("react").JSX.Element | null;
10
+ export {};
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { ConsentEmbed } from "./consentEmbed.js";
5
+ function embedUrl(provider, src, autoplay = false) {
6
+ if (provider === "youtube") {
7
+ const id = src.match(/(?:v=|youtu\.be\/|embed\/)([\w-]{6,})/)?.[1] ?? src;
8
+ return `https://www.youtube-nocookie.com/embed/${id}${autoplay ? "?autoplay=1" : ""}`;
9
+ }
10
+ if (provider === "vimeo") {
11
+ const id = src.match(/(?:vimeo\.com\/)?(\d{6,})/)?.[1] ?? src;
12
+ return `https://player.vimeo.com/video/${id}${autoplay ? "?autoplay=1" : ""}`;
13
+ }
14
+ return src;
15
+ }
16
+ function facadeThumb(block) {
17
+ if (block.posterUrl) return block.posterUrl;
18
+ if (block.provider === "youtube") {
19
+ const id = block.src.match(/(?:v=|youtu\.be\/|embed\/)([\w-]{6,})/)?.[1] ?? block.src;
20
+ return id ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : null;
21
+ }
22
+ return null;
23
+ }
24
+ function VideoRender({ block, mode }) {
25
+ const [activated, setActivated] = useState(false);
26
+ if (!block.src) return null;
27
+ const useFacade = block.facade && block.provider !== "file" && !activated;
28
+ const thumb = useFacade ? facadeThumb(block) : null;
29
+ const media = block.provider === "file" ? /* @__PURE__ */ jsx("video", { className: "xicms-video__player", src: block.src, controls: true, poster: block.posterUrl || void 0 }) : useFacade ? (
30
+ // Click-to-play: geen third-party iframe tot de bezoeker klikt (sneller + privacyvriendelijker).
31
+ /* @__PURE__ */ jsxs(
32
+ "button",
33
+ {
34
+ type: "button",
35
+ className: "xicms-video__facade",
36
+ onClick: () => mode !== "preview" && setActivated(true),
37
+ "aria-label": `Video afspelen: ${block.title ?? "video"}`,
38
+ children: [
39
+ thumb ? (
40
+ // eslint-disable-next-line @next/next/no-img-element
41
+ /* @__PURE__ */ jsx("img", { src: thumb, alt: "", loading: "lazy" })
42
+ ) : /* @__PURE__ */ jsx("span", { className: "xicms-video__facade-bg", "aria-hidden": true }),
43
+ /* @__PURE__ */ jsx("span", { className: "xicms-video__play", "aria-hidden": true, children: "\u25B6" })
44
+ ]
45
+ }
46
+ )
47
+ ) : /* @__PURE__ */ jsx("div", { className: "xicms-video__frame", children: /* @__PURE__ */ jsx(
48
+ "iframe",
49
+ {
50
+ src: embedUrl(block.provider, block.src, activated),
51
+ title: block.title ?? "Video",
52
+ loading: "lazy",
53
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
54
+ allowFullScreen: true
55
+ }
56
+ ) });
57
+ return /* @__PURE__ */ jsxs("figure", { className: "xicms-video", children: [
58
+ /* @__PURE__ */ jsx(ConsentEmbed, { gated: block.consentGated && !useFacade && block.provider !== "file", preview: mode === "preview", label: "Video", children: media }),
59
+ block.caption && /* @__PURE__ */ jsx("figcaption", { "data-xicms-edit": "caption", children: block.caption })
60
+ ] });
61
+ }
62
+ export {
63
+ VideoRender,
64
+ embedUrl,
65
+ facadeThumb
66
+ };
67
+ //# sourceMappingURL=VideoRender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/builtins/VideoRender.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport type { VideoBlockSchema } from \"../../schema/blocks\";\nimport type { BlockRenderProps } from \"../registry\";\nimport { ConsentEmbed } from \"./consentEmbed\";\n\ntype Video = z.infer<typeof VideoBlockSchema>;\n\n/** Bouwt een embed-URL uit een id of volledige URL. */\nexport function embedUrl(provider: Video[\"provider\"], src: string, autoplay = false): string {\n if (provider === \"youtube\") {\n const id = src.match(/(?:v=|youtu\\.be\\/|embed\\/)([\\w-]{6,})/)?.[1] ?? src;\n return `https://www.youtube-nocookie.com/embed/${id}${autoplay ? \"?autoplay=1\" : \"\"}`;\n }\n if (provider === \"vimeo\") {\n const id = src.match(/(?:vimeo\\.com\\/)?(\\d{6,})/)?.[1] ?? src;\n return `https://player.vimeo.com/video/${id}${autoplay ? \"?autoplay=1\" : \"\"}`;\n }\n return src;\n}\n\n/** Thumbnail voor de click-to-play-facade: eigen poster of de YouTube-thumb. */\nexport function facadeThumb(block: Pick<Video, \"provider\" | \"src\" | \"posterUrl\">): string | null {\n if (block.posterUrl) return block.posterUrl;\n if (block.provider === \"youtube\") {\n const id = block.src.match(/(?:v=|youtu\\.be\\/|embed\\/)([\\w-]{6,})/)?.[1] ?? block.src;\n return id ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : null;\n }\n return null; // vimeo-thumbs vereisen een API-call; zonder poster geen facade-beeld\n}\n\nexport function VideoRender({ block, mode }: BlockRenderProps<Video>) {\n const [activated, setActivated] = useState(false);\n if (!block.src) return null;\n\n const useFacade = block.facade && block.provider !== \"file\" && !activated;\n const thumb = useFacade ? facadeThumb(block) : null;\n\n const media =\n block.provider === \"file\" ? (\n <video className=\"xicms-video__player\" src={block.src} controls poster={block.posterUrl || undefined} />\n ) : useFacade ? (\n // Click-to-play: geen third-party iframe tot de bezoeker klikt (sneller + privacyvriendelijker).\n <button\n type=\"button\"\n className=\"xicms-video__facade\"\n onClick={() => mode !== \"preview\" && setActivated(true)}\n aria-label={`Video afspelen: ${block.title ?? \"video\"}`}\n >\n {thumb ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={thumb} alt=\"\" loading=\"lazy\" />\n ) : (\n <span className=\"xicms-video__facade-bg\" aria-hidden />\n )}\n <span className=\"xicms-video__play\" aria-hidden>▶</span>\n </button>\n ) : (\n <div className=\"xicms-video__frame\">\n <iframe\n src={embedUrl(block.provider, block.src, activated)}\n title={block.title ?? \"Video\"}\n loading=\"lazy\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n </div>\n );\n return (\n <figure className=\"xicms-video\">\n {/* Zelf-gehost bestand (provider \"file\") doet géén third-party-request en zet geen cookies —\n daar is geen cookie-toestemming voor nodig; alleen youtube/vimeo-embeds gaten. */}\n <ConsentEmbed gated={block.consentGated && !useFacade && block.provider !== \"file\"} preview={mode === \"preview\"} label=\"Video\">{media}</ConsentEmbed>\n {block.caption && <figcaption data-xicms-edit=\"caption\">{block.caption}</figcaption>}\n </figure>\n );\n}\n"],"mappings":";AA0CM,cAGA,YAHA;AAxCN,SAAS,gBAAgB;AAIzB,SAAS,oBAAoB;AAKtB,SAAS,SAAS,UAA6B,KAAa,WAAW,OAAe;AAC3F,MAAI,aAAa,WAAW;AAC1B,UAAM,KAAK,IAAI,MAAM,uCAAuC,IAAI,CAAC,KAAK;AACtE,WAAO,0CAA0C,EAAE,GAAG,WAAW,gBAAgB,EAAE;AAAA,EACrF;AACA,MAAI,aAAa,SAAS;AACxB,UAAM,KAAK,IAAI,MAAM,2BAA2B,IAAI,CAAC,KAAK;AAC1D,WAAO,kCAAkC,EAAE,GAAG,WAAW,gBAAgB,EAAE;AAAA,EAC7E;AACA,SAAO;AACT;AAGO,SAAS,YAAY,OAAqE;AAC/F,MAAI,MAAM,UAAW,QAAO,MAAM;AAClC,MAAI,MAAM,aAAa,WAAW;AAChC,UAAM,KAAK,MAAM,IAAI,MAAM,uCAAuC,IAAI,CAAC,KAAK,MAAM;AAClF,WAAO,KAAK,0BAA0B,EAAE,mBAAmB;AAAA,EAC7D;AACA,SAAO;AACT;AAEO,SAAS,YAAY,EAAE,OAAO,KAAK,GAA4B;AACpE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,MAAI,CAAC,MAAM,IAAK,QAAO;AAEvB,QAAM,YAAY,MAAM,UAAU,MAAM,aAAa,UAAU,CAAC;AAChE,QAAM,QAAQ,YAAY,YAAY,KAAK,IAAI;AAE/C,QAAM,QACJ,MAAM,aAAa,SACjB,oBAAC,WAAM,WAAU,uBAAsB,KAAK,MAAM,KAAK,UAAQ,MAAC,QAAQ,MAAM,aAAa,QAAW,IACpG;AAAA;AAAA,IAEF;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,SAAS,aAAa,aAAa,IAAI;AAAA,QACtD,cAAY,mBAAmB,MAAM,SAAS,OAAO;AAAA,QAEpD;AAAA;AAAA;AAAA,YAEC,oBAAC,SAAI,KAAK,OAAO,KAAI,IAAG,SAAQ,QAAO;AAAA,cAEvC,oBAAC,UAAK,WAAU,0BAAyB,eAAW,MAAC;AAAA,UAEvD,oBAAC,UAAK,WAAU,qBAAoB,eAAW,MAAC,oBAAC;AAAA;AAAA;AAAA,IACnD;AAAA,MAEA,oBAAC,SAAI,WAAU,sBACb;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,SAAS,MAAM,UAAU,MAAM,KAAK,SAAS;AAAA,MAClD,OAAO,MAAM,SAAS;AAAA,MACtB,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,iBAAe;AAAA;AAAA,EACjB,GACF;AAEJ,SACE,qBAAC,YAAO,WAAU,eAGhB;AAAA,wBAAC,gBAAa,OAAO,MAAM,gBAAgB,CAAC,aAAa,MAAM,aAAa,QAAQ,SAAS,SAAS,WAAW,OAAM,SAAS,iBAAM;AAAA,IACrI,MAAM,WAAW,oBAAC,gBAAW,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,KACzE;AAEJ;","names":[]}