@xiwebdesign/xicms-core 4.17.1

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 (436) hide show
  1. package/dist/XiCMSProvider.d.ts +58 -0
  2. package/dist/XiCMSProvider.js +165 -0
  3. package/dist/XiCMSProvider.js.map +1 -0
  4. package/dist/admin/AdminApp.d.ts +4 -0
  5. package/dist/admin/AdminApp.js +62 -0
  6. package/dist/admin/AdminApp.js.map +1 -0
  7. package/dist/admin/AdminHelp.d.ts +2 -0
  8. package/dist/admin/AdminHelp.js +65 -0
  9. package/dist/admin/AdminHelp.js.map +1 -0
  10. package/dist/admin/NewsEditor.d.ts +2 -0
  11. package/dist/admin/NewsEditor.js +171 -0
  12. package/dist/admin/NewsEditor.js.map +1 -0
  13. package/dist/admin/SuperadminPanel.d.ts +4 -0
  14. package/dist/admin/SuperadminPanel.js +101 -0
  15. package/dist/admin/SuperadminPanel.js.map +1 -0
  16. package/dist/admin/blockEditors/StyleEditor.d.ts +10 -0
  17. package/dist/admin/blockEditors/StyleEditor.js +78 -0
  18. package/dist/admin/blockEditors/StyleEditor.js.map +1 -0
  19. package/dist/admin/blockEditors/index.d.ts +16 -0
  20. package/dist/admin/blockEditors/index.js +1602 -0
  21. package/dist/admin/blockEditors/index.js.map +1 -0
  22. package/dist/admin/editors/RichTextEditor.d.ts +10 -0
  23. package/dist/admin/editors/RichTextEditor.js +86 -0
  24. package/dist/admin/editors/RichTextEditor.js.map +1 -0
  25. package/dist/admin/forms/FormsPanel.d.ts +3 -0
  26. package/dist/admin/forms/FormsPanel.js +203 -0
  27. package/dist/admin/forms/FormsPanel.js.map +1 -0
  28. package/dist/admin/index.d.ts +21 -0
  29. package/dist/admin/index.js +38 -0
  30. package/dist/admin/index.js.map +1 -0
  31. package/dist/admin/lib/compressImage.d.ts +12 -0
  32. package/dist/admin/lib/compressImage.js +53 -0
  33. package/dist/admin/lib/compressImage.js.map +1 -0
  34. package/dist/admin/lib/mediaUsage.d.ts +9 -0
  35. package/dist/admin/lib/mediaUsage.js +25 -0
  36. package/dist/admin/lib/mediaUsage.js.map +1 -0
  37. package/dist/admin/lib/pagesClient.d.ts +38 -0
  38. package/dist/admin/lib/pagesClient.js +143 -0
  39. package/dist/admin/lib/pagesClient.js.map +1 -0
  40. package/dist/admin/lib/slugify.d.ts +2 -0
  41. package/dist/admin/lib/slugify.js +7 -0
  42. package/dist/admin/lib/slugify.js.map +1 -0
  43. package/dist/admin/lib/upload.d.ts +13 -0
  44. package/dist/admin/lib/upload.js +40 -0
  45. package/dist/admin/lib/upload.js.map +1 -0
  46. package/dist/admin/lib/zodFields.d.ts +27 -0
  47. package/dist/admin/lib/zodFields.js +48 -0
  48. package/dist/admin/lib/zodFields.js.map +1 -0
  49. package/dist/admin/media/MediaManager.d.ts +3 -0
  50. package/dist/admin/media/MediaManager.js +132 -0
  51. package/dist/admin/media/MediaManager.js.map +1 -0
  52. package/dist/admin/media/MediaPicker.d.ts +24 -0
  53. package/dist/admin/media/MediaPicker.js +93 -0
  54. package/dist/admin/media/MediaPicker.js.map +1 -0
  55. package/dist/admin/pages/PageEditor.d.ts +5 -0
  56. package/dist/admin/pages/PageEditor.js +239 -0
  57. package/dist/admin/pages/PageEditor.js.map +1 -0
  58. package/dist/admin/pages/PagesPanel.d.ts +3 -0
  59. package/dist/admin/pages/PagesPanel.js +92 -0
  60. package/dist/admin/pages/PagesPanel.js.map +1 -0
  61. package/dist/admin/panelRegistry.d.ts +20 -0
  62. package/dist/admin/panelRegistry.js +23 -0
  63. package/dist/admin/panelRegistry.js.map +1 -0
  64. package/dist/admin/patterns/PatternInserter.d.ts +10 -0
  65. package/dist/admin/patterns/PatternInserter.js +58 -0
  66. package/dist/admin/patterns/PatternInserter.js.map +1 -0
  67. package/dist/admin/settings/SettingsPanel.d.ts +3 -0
  68. package/dist/admin/settings/SettingsPanel.js +304 -0
  69. package/dist/admin/settings/SettingsPanel.js.map +1 -0
  70. package/dist/admin/settings/moduleSettingsForm.d.ts +12 -0
  71. package/dist/admin/settings/moduleSettingsForm.js +45 -0
  72. package/dist/admin/settings/moduleSettingsForm.js.map +1 -0
  73. package/dist/admin/stats/StatsPanel.d.ts +4 -0
  74. package/dist/admin/stats/StatsPanel.js +80 -0
  75. package/dist/admin/stats/StatsPanel.js.map +1 -0
  76. package/dist/admin/ui/ArrayEditor.d.ts +16 -0
  77. package/dist/admin/ui/ArrayEditor.js +38 -0
  78. package/dist/admin/ui/ArrayEditor.js.map +1 -0
  79. package/dist/admin/ui/ConfirmDialog.d.ts +22 -0
  80. package/dist/admin/ui/ConfirmDialog.js +47 -0
  81. package/dist/admin/ui/ConfirmDialog.js.map +1 -0
  82. package/dist/admin/ui/cn.d.ts +3 -0
  83. package/dist/admin/ui/cn.js +9 -0
  84. package/dist/admin/ui/cn.js.map +1 -0
  85. package/dist/admin/ui/dialog.d.ts +9 -0
  86. package/dist/admin/ui/dialog.js +47 -0
  87. package/dist/admin/ui/dialog.js.map +1 -0
  88. package/dist/admin/ui/index.d.ts +7 -0
  89. package/dist/admin/ui/index.js +29 -0
  90. package/dist/admin/ui/index.js.map +1 -0
  91. package/dist/admin/ui/primitives.d.ts +37 -0
  92. package/dist/admin/ui/primitives.js +137 -0
  93. package/dist/admin/ui/primitives.js.map +1 -0
  94. package/dist/admin/ui/select.d.ts +11 -0
  95. package/dist/admin/ui/select.js +27 -0
  96. package/dist/admin/ui/select.js.map +1 -0
  97. package/dist/admin/ui/switch.d.ts +6 -0
  98. package/dist/admin/ui/switch.js +22 -0
  99. package/dist/admin/ui/switch.js.map +1 -0
  100. package/dist/admin/ui/tabs.d.ts +5 -0
  101. package/dist/admin/ui/tabs.js +36 -0
  102. package/dist/admin/ui/tabs.js.map +1 -0
  103. package/dist/admin/ui/toast.d.ts +19 -0
  104. package/dist/admin/ui/toast.js +24 -0
  105. package/dist/admin/ui/toast.js.map +1 -0
  106. package/dist/admin/visual/VisualEditor.d.ts +10 -0
  107. package/dist/admin/visual/VisualEditor.js +597 -0
  108. package/dist/admin/visual/VisualEditor.js.map +1 -0
  109. package/dist/admin/visual/reorder.d.ts +6 -0
  110. package/dist/admin/visual/reorder.js +12 -0
  111. package/dist/admin/visual/reorder.js.map +1 -0
  112. package/dist/admin.css +1 -0
  113. package/dist/auth/AuthProvider.d.ts +30 -0
  114. package/dist/auth/AuthProvider.js +110 -0
  115. package/dist/auth/AuthProvider.js.map +1 -0
  116. package/dist/auth/LoginForm.d.ts +4 -0
  117. package/dist/auth/LoginForm.js +122 -0
  118. package/dist/auth/LoginForm.js.map +1 -0
  119. package/dist/auth/roles.d.ts +15 -0
  120. package/dist/auth/roles.js +21 -0
  121. package/dist/auth/roles.js.map +1 -0
  122. package/dist/blocks/BlockErrorBoundary.d.ts +32 -0
  123. package/dist/blocks/BlockErrorBoundary.js +39 -0
  124. package/dist/blocks/BlockErrorBoundary.js.map +1 -0
  125. package/dist/blocks/BlockRenderer.d.ts +15 -0
  126. package/dist/blocks/BlockRenderer.js +24 -0
  127. package/dist/blocks/BlockRenderer.js.map +1 -0
  128. package/dist/blocks/blockStyle.d.ts +14 -0
  129. package/dist/blocks/blockStyle.js +14 -0
  130. package/dist/blocks/blockStyle.js.map +1 -0
  131. package/dist/blocks/builtins/Accordion.d.ts +2 -0
  132. package/dist/blocks/builtins/Accordion.js +45 -0
  133. package/dist/blocks/builtins/Accordion.js.map +1 -0
  134. package/dist/blocks/builtins/Agenda.d.ts +23 -0
  135. package/dist/blocks/builtins/Agenda.js +102 -0
  136. package/dist/blocks/builtins/Agenda.js.map +1 -0
  137. package/dist/blocks/builtins/Banner.d.ts +2 -0
  138. package/dist/blocks/builtins/Banner.js +45 -0
  139. package/dist/blocks/builtins/Banner.js.map +1 -0
  140. package/dist/blocks/builtins/BeforeAfter.d.ts +2 -0
  141. package/dist/blocks/builtins/BeforeAfter.js +44 -0
  142. package/dist/blocks/builtins/BeforeAfter.js.map +1 -0
  143. package/dist/blocks/builtins/Booking.d.ts +2 -0
  144. package/dist/blocks/builtins/Booking.js +27 -0
  145. package/dist/blocks/builtins/Booking.js.map +1 -0
  146. package/dist/blocks/builtins/Breadcrumbs.d.ts +10 -0
  147. package/dist/blocks/builtins/Breadcrumbs.js +73 -0
  148. package/dist/blocks/builtins/Breadcrumbs.js.map +1 -0
  149. package/dist/blocks/builtins/Cards.d.ts +2 -0
  150. package/dist/blocks/builtins/Cards.js +94 -0
  151. package/dist/blocks/builtins/Cards.js.map +1 -0
  152. package/dist/blocks/builtins/Columns.d.ts +2 -0
  153. package/dist/blocks/builtins/Columns.js +22 -0
  154. package/dist/blocks/builtins/Columns.js.map +1 -0
  155. package/dist/blocks/builtins/Contact.d.ts +7 -0
  156. package/dist/blocks/builtins/Contact.js +121 -0
  157. package/dist/blocks/builtins/Contact.js.map +1 -0
  158. package/dist/blocks/builtins/Countdown.d.ts +18 -0
  159. package/dist/blocks/builtins/Countdown.js +65 -0
  160. package/dist/blocks/builtins/Countdown.js.map +1 -0
  161. package/dist/blocks/builtins/Cta.d.ts +2 -0
  162. package/dist/blocks/builtins/Cta.js +52 -0
  163. package/dist/blocks/builtins/Cta.js.map +1 -0
  164. package/dist/blocks/builtins/Downloads.d.ts +2 -0
  165. package/dist/blocks/builtins/Downloads.js +32 -0
  166. package/dist/blocks/builtins/Downloads.js.map +1 -0
  167. package/dist/blocks/builtins/Elements.d.ts +4 -0
  168. package/dist/blocks/builtins/Elements.js +64 -0
  169. package/dist/blocks/builtins/Elements.js.map +1 -0
  170. package/dist/blocks/builtins/Embed.d.ts +2 -0
  171. package/dist/blocks/builtins/Embed.js +30 -0
  172. package/dist/blocks/builtins/Embed.js.map +1 -0
  173. package/dist/blocks/builtins/Faq.d.ts +6 -0
  174. package/dist/blocks/builtins/Faq.js +110 -0
  175. package/dist/blocks/builtins/Faq.js.map +1 -0
  176. package/dist/blocks/builtins/FormBlock.d.ts +2 -0
  177. package/dist/blocks/builtins/FormBlock.js +221 -0
  178. package/dist/blocks/builtins/FormBlock.js.map +1 -0
  179. package/dist/blocks/builtins/Gallery.d.ts +23 -0
  180. package/dist/blocks/builtins/Gallery.js +177 -0
  181. package/dist/blocks/builtins/Gallery.js.map +1 -0
  182. package/dist/blocks/builtins/Hero.d.ts +2 -0
  183. package/dist/blocks/builtins/Hero.js +47 -0
  184. package/dist/blocks/builtins/Hero.js.map +1 -0
  185. package/dist/blocks/builtins/ImageBlock.d.ts +2 -0
  186. package/dist/blocks/builtins/ImageBlock.js +24 -0
  187. package/dist/blocks/builtins/ImageBlock.js.map +1 -0
  188. package/dist/blocks/builtins/Jobs.d.ts +8 -0
  189. package/dist/blocks/builtins/Jobs.js +65 -0
  190. package/dist/blocks/builtins/Jobs.js.map +1 -0
  191. package/dist/blocks/builtins/Layout.d.ts +3 -0
  192. package/dist/blocks/builtins/Layout.js +67 -0
  193. package/dist/blocks/builtins/Layout.js.map +1 -0
  194. package/dist/blocks/builtins/MapBlock.d.ts +2 -0
  195. package/dist/blocks/builtins/MapBlock.js +31 -0
  196. package/dist/blocks/builtins/MapBlock.js.map +1 -0
  197. package/dist/blocks/builtins/NewsListBlock.d.ts +2 -0
  198. package/dist/blocks/builtins/NewsListBlock.js +24 -0
  199. package/dist/blocks/builtins/NewsListBlock.js.map +1 -0
  200. package/dist/blocks/builtins/Newsletter.d.ts +2 -0
  201. package/dist/blocks/builtins/Newsletter.js +84 -0
  202. package/dist/blocks/builtins/Newsletter.js.map +1 -0
  203. package/dist/blocks/builtins/OpenNowBadge.d.ts +9 -0
  204. package/dist/blocks/builtins/OpenNowBadge.js +20 -0
  205. package/dist/blocks/builtins/OpenNowBadge.js.map +1 -0
  206. package/dist/blocks/builtins/OpeningHours.d.ts +2 -0
  207. package/dist/blocks/builtins/OpeningHours.js +47 -0
  208. package/dist/blocks/builtins/OpeningHours.js.map +1 -0
  209. package/dist/blocks/builtins/PdfBlock.d.ts +2 -0
  210. package/dist/blocks/builtins/PdfBlock.js +97 -0
  211. package/dist/blocks/builtins/PdfBlock.js.map +1 -0
  212. package/dist/blocks/builtins/Portfolio.d.ts +2 -0
  213. package/dist/blocks/builtins/Portfolio.js +94 -0
  214. package/dist/blocks/builtins/Portfolio.js.map +1 -0
  215. package/dist/blocks/builtins/Pricing.d.ts +6 -0
  216. package/dist/blocks/builtins/Pricing.js +77 -0
  217. package/dist/blocks/builtins/Pricing.js.map +1 -0
  218. package/dist/blocks/builtins/RichTextBlock.d.ts +2 -0
  219. package/dist/blocks/builtins/RichTextBlock.js +18 -0
  220. package/dist/blocks/builtins/RichTextBlock.js.map +1 -0
  221. package/dist/blocks/builtins/Search.d.ts +2 -0
  222. package/dist/blocks/builtins/Search.js +99 -0
  223. package/dist/blocks/builtins/Search.js.map +1 -0
  224. package/dist/blocks/builtins/Sections.d.ts +5 -0
  225. package/dist/blocks/builtins/Sections.js +183 -0
  226. package/dist/blocks/builtins/Sections.js.map +1 -0
  227. package/dist/blocks/builtins/Slider.d.ts +2 -0
  228. package/dist/blocks/builtins/Slider.js +131 -0
  229. package/dist/blocks/builtins/Slider.js.map +1 -0
  230. package/dist/blocks/builtins/Spacer.d.ts +2 -0
  231. package/dist/blocks/builtins/Spacer.js +19 -0
  232. package/dist/blocks/builtins/Spacer.js.map +1 -0
  233. package/dist/blocks/builtins/Team.d.ts +2 -0
  234. package/dist/blocks/builtins/Team.js +70 -0
  235. package/dist/blocks/builtins/Team.js.map +1 -0
  236. package/dist/blocks/builtins/Testimonials.d.ts +8 -0
  237. package/dist/blocks/builtins/Testimonials.js +82 -0
  238. package/dist/blocks/builtins/Testimonials.js.map +1 -0
  239. package/dist/blocks/builtins/Video.d.ts +9 -0
  240. package/dist/blocks/builtins/Video.js +77 -0
  241. package/dist/blocks/builtins/Video.js.map +1 -0
  242. package/dist/blocks/builtins/WhatsappChat.d.ts +8 -0
  243. package/dist/blocks/builtins/WhatsappChat.js +51 -0
  244. package/dist/blocks/builtins/WhatsappChat.js.map +1 -0
  245. package/dist/blocks/builtins/Widgets.d.ts +13 -0
  246. package/dist/blocks/builtins/Widgets.js +308 -0
  247. package/dist/blocks/builtins/Widgets.js.map +1 -0
  248. package/dist/blocks/builtins/consentEmbed.d.ts +17 -0
  249. package/dist/blocks/builtins/consentEmbed.js +31 -0
  250. package/dist/blocks/builtins/consentEmbed.js.map +1 -0
  251. package/dist/blocks/builtins/index.d.ts +46 -0
  252. package/dist/blocks/builtins/index.js +121 -0
  253. package/dist/blocks/builtins/index.js.map +1 -0
  254. package/dist/blocks/builtins/lib/bookingUrl.d.ts +8 -0
  255. package/dist/blocks/builtins/lib/bookingUrl.js +50 -0
  256. package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -0
  257. package/dist/blocks/builtins/lib/fileIcon.d.ts +2 -0
  258. package/dist/blocks/builtins/lib/fileIcon.js +45 -0
  259. package/dist/blocks/builtins/lib/fileIcon.js.map +1 -0
  260. package/dist/blocks/builtins/lib/ics.d.ts +17 -0
  261. package/dist/blocks/builtins/lib/ics.js +50 -0
  262. package/dist/blocks/builtins/lib/ics.js.map +1 -0
  263. package/dist/blocks/builtins/lib/openingHours.d.ts +20 -0
  264. package/dist/blocks/builtins/lib/openingHours.js +79 -0
  265. package/dist/blocks/builtins/lib/openingHours.js.map +1 -0
  266. package/dist/blocks/builtins/lib/searchRank.d.ts +22 -0
  267. package/dist/blocks/builtins/lib/searchRank.js +45 -0
  268. package/dist/blocks/builtins/lib/searchRank.js.map +1 -0
  269. package/dist/blocks/builtins/lib/submitForm.d.ts +6 -0
  270. package/dist/blocks/builtins/lib/submitForm.js +14 -0
  271. package/dist/blocks/builtins/lib/submitForm.js.map +1 -0
  272. package/dist/blocks/context.d.ts +8 -0
  273. package/dist/blocks/context.js +20 -0
  274. package/dist/blocks/context.js.map +1 -0
  275. package/dist/blocks/index.d.ts +9 -0
  276. package/dist/blocks/index.js +21 -0
  277. package/dist/blocks/index.js.map +1 -0
  278. package/dist/blocks/registry.d.ts +75 -0
  279. package/dist/blocks/registry.js +71 -0
  280. package/dist/blocks/registry.js.map +1 -0
  281. package/dist/blocks/ui/index.d.ts +35 -0
  282. package/dist/blocks/ui/index.js +48 -0
  283. package/dist/blocks/ui/index.js.map +1 -0
  284. package/dist/components/EditBar.d.ts +2 -0
  285. package/dist/components/EditBar.js +20 -0
  286. package/dist/components/EditBar.js.map +1 -0
  287. package/dist/components/EditableImage.d.ts +18 -0
  288. package/dist/components/EditableImage.js +62 -0
  289. package/dist/components/EditableImage.js.map +1 -0
  290. package/dist/components/EditableRichText.d.ts +14 -0
  291. package/dist/components/EditableRichText.js +26 -0
  292. package/dist/components/EditableRichText.js.map +1 -0
  293. package/dist/components/EditableText.d.ts +21 -0
  294. package/dist/components/EditableText.js +25 -0
  295. package/dist/components/EditableText.js.map +1 -0
  296. package/dist/components/News.d.ts +38 -0
  297. package/dist/components/News.js +128 -0
  298. package/dist/components/News.js.map +1 -0
  299. package/dist/components/PageView.d.ts +8 -0
  300. package/dist/components/PageView.js +28 -0
  301. package/dist/components/PageView.js.map +1 -0
  302. package/dist/components/ResponsiveImage.d.ts +16 -0
  303. package/dist/components/ResponsiveImage.js +39 -0
  304. package/dist/components/ResponsiveImage.js.map +1 -0
  305. package/dist/components/site/SiteFooter.d.ts +12 -0
  306. package/dist/components/site/SiteFooter.js +34 -0
  307. package/dist/components/site/SiteFooter.js.map +1 -0
  308. package/dist/config.d.ts +8 -0
  309. package/dist/config.js +17 -0
  310. package/dist/config.js.map +1 -0
  311. package/dist/content/ContentProvider.d.ts +23 -0
  312. package/dist/content/ContentProvider.js +86 -0
  313. package/dist/content/ContentProvider.js.map +1 -0
  314. package/dist/content/types.d.ts +41 -0
  315. package/dist/content/types.js +1 -0
  316. package/dist/content/types.js.map +1 -0
  317. package/dist/demo/DemoStudio.d.ts +7 -0
  318. package/dist/demo/DemoStudio.js +54 -0
  319. package/dist/demo/DemoStudio.js.map +1 -0
  320. package/dist/demo/demoStore.d.ts +26 -0
  321. package/dist/demo/demoStore.js +115 -0
  322. package/dist/demo/demoStore.js.map +1 -0
  323. package/dist/editor/EditorProvider.d.ts +12 -0
  324. package/dist/editor/EditorProvider.js +22 -0
  325. package/dist/editor/EditorProvider.js.map +1 -0
  326. package/dist/firebase/admin.d.ts +7 -0
  327. package/dist/firebase/admin.js +31 -0
  328. package/dist/firebase/admin.js.map +1 -0
  329. package/dist/firebase/appcheck.d.ts +6 -0
  330. package/dist/firebase/appcheck.js +23 -0
  331. package/dist/firebase/appcheck.js.map +1 -0
  332. package/dist/firebase/client.d.ts +9 -0
  333. package/dist/firebase/client.js +44 -0
  334. package/dist/firebase/client.js.map +1 -0
  335. package/dist/firebase/region.d.ts +5 -0
  336. package/dist/firebase/region.js +7 -0
  337. package/dist/firebase/region.js.map +1 -0
  338. package/dist/index.d.ts +54 -0
  339. package/dist/index.js +172 -0
  340. package/dist/index.js.map +1 -0
  341. package/dist/layers.css +13 -0
  342. package/dist/lock/DemoLock.d.ts +5 -0
  343. package/dist/lock/DemoLock.js +35 -0
  344. package/dist/lock/DemoLock.js.map +1 -0
  345. package/dist/media/ImageContext.d.ts +41 -0
  346. package/dist/media/ImageContext.js +74 -0
  347. package/dist/media/ImageContext.js.map +1 -0
  348. package/dist/modules/builtins.d.ts +46 -0
  349. package/dist/modules/builtins.js +462 -0
  350. package/dist/modules/builtins.js.map +1 -0
  351. package/dist/modules/consent/ConsentBanner.d.ts +42 -0
  352. package/dist/modules/consent/ConsentBanner.js +119 -0
  353. package/dist/modules/consent/ConsentBanner.js.map +1 -0
  354. package/dist/modules/consent/useConsent.d.ts +28 -0
  355. package/dist/modules/consent/useConsent.js +52 -0
  356. package/dist/modules/consent/useConsent.js.map +1 -0
  357. package/dist/modules/context.d.ts +20 -0
  358. package/dist/modules/context.js +31 -0
  359. package/dist/modules/context.js.map +1 -0
  360. package/dist/modules/contract.d.ts +9 -0
  361. package/dist/modules/contract.js +38 -0
  362. package/dist/modules/contract.js.map +1 -0
  363. package/dist/modules/defineModule.d.ts +11 -0
  364. package/dist/modules/defineModule.js +7 -0
  365. package/dist/modules/defineModule.js.map +1 -0
  366. package/dist/modules/index.d.ts +13 -0
  367. package/dist/modules/index.js +44 -0
  368. package/dist/modules/index.js.map +1 -0
  369. package/dist/modules/overrides.d.ts +17 -0
  370. package/dist/modules/overrides.js +25 -0
  371. package/dist/modules/overrides.js.map +1 -0
  372. package/dist/modules/registry.d.ts +40 -0
  373. package/dist/modules/registry.js +79 -0
  374. package/dist/modules/registry.js.map +1 -0
  375. package/dist/modules/types.d.ts +66 -0
  376. package/dist/modules/types.js +1 -0
  377. package/dist/modules/types.js.map +1 -0
  378. package/dist/patterns/builtins.d.ts +31 -0
  379. package/dist/patterns/builtins.js +415 -0
  380. package/dist/patterns/builtins.js.map +1 -0
  381. package/dist/patterns/index.d.ts +6 -0
  382. package/dist/patterns/index.js +9 -0
  383. package/dist/patterns/index.js.map +1 -0
  384. package/dist/patterns/patterns.d.ts +28 -0
  385. package/dist/patterns/patterns.js +53 -0
  386. package/dist/patterns/patterns.js.map +1 -0
  387. package/dist/render/PageRenderer.d.ts +13 -0
  388. package/dist/render/PageRenderer.js +14 -0
  389. package/dist/render/PageRenderer.js.map +1 -0
  390. package/dist/render/PublishedPage.d.ts +13 -0
  391. package/dist/render/PublishedPage.js +16 -0
  392. package/dist/render/PublishedPage.js.map +1 -0
  393. package/dist/schema/blocks.d.ts +15052 -0
  394. package/dist/schema/blocks.js +794 -0
  395. package/dist/schema/blocks.js.map +1 -0
  396. package/dist/schema/forms.d.ts +111 -0
  397. package/dist/schema/forms.js +47 -0
  398. package/dist/schema/forms.js.map +1 -0
  399. package/dist/schema/index.d.ts +11 -0
  400. package/dist/schema/index.js +16 -0
  401. package/dist/schema/index.js.map +1 -0
  402. package/dist/schema/pages.d.ts +674 -0
  403. package/dist/schema/pages.js +37 -0
  404. package/dist/schema/pages.js.map +1 -0
  405. package/dist/schema/seo.d.ts +29 -0
  406. package/dist/schema/seo.js +17 -0
  407. package/dist/schema/seo.js.map +1 -0
  408. package/dist/schema/settings.d.ts +1126 -0
  409. package/dist/schema/settings.js +128 -0
  410. package/dist/schema/settings.js.map +1 -0
  411. package/dist/schema/siteConfig.d.ts +722 -0
  412. package/dist/schema/siteConfig.js +39 -0
  413. package/dist/schema/siteConfig.js.map +1 -0
  414. package/dist/server/env.d.ts +10 -0
  415. package/dist/server/env.js +36 -0
  416. package/dist/server/env.js.map +1 -0
  417. package/dist/server/index.d.ts +10 -0
  418. package/dist/server/index.js +51 -0
  419. package/dist/server/index.js.map +1 -0
  420. package/dist/server/pages.d.ts +15 -0
  421. package/dist/server/pages.js +99 -0
  422. package/dist/server/pages.js.map +1 -0
  423. package/dist/server/sanitize.d.ts +2 -0
  424. package/dist/server/sanitize.js +55 -0
  425. package/dist/server/sanitize.js.map +1 -0
  426. package/dist/server/seo.d.ts +116 -0
  427. package/dist/server/seo.js +179 -0
  428. package/dist/server/seo.js.map +1 -0
  429. package/dist/settings/useSiteSettings.d.ts +25 -0
  430. package/dist/settings/useSiteSettings.js +68 -0
  431. package/dist/settings/useSiteSettings.js.map +1 -0
  432. package/dist/styles.css +2495 -0
  433. package/dist/theme/presets.d.ts +12 -0
  434. package/dist/theme/presets.js +147 -0
  435. package/dist/theme/presets.js.map +1 -0
  436. package/package.json +112 -0
@@ -0,0 +1,7 @@
1
+ function slugify(s) {
2
+ return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
3
+ }
4
+ export {
5
+ slugify
6
+ };
7
+ //# sourceMappingURL=slugify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/lib/slugify.ts"],"sourcesContent":["/** Maakt een URL-veilige slug van een titel (gedeeld door de pagina- en nieuws-editor). */\nexport function slugify(s: string): string {\n return s\n .toLowerCase()\n .normalize(\"NFD\")\n .replace(/[̀-ͯ]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n"],"mappings":"AACO,SAAS,QAAQ,GAAmB;AACzC,SAAO,EACJ,YAAY,EACZ,UAAU,KAAK,EACf,QAAQ,UAAU,EAAE,EACpB,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AAC3B;","names":[]}
@@ -0,0 +1,13 @@
1
+ /** Maximale bestandsgrootte vóór compressie (foolproof: voorkomt vastlopen op enorme bestanden). */
2
+ export declare const MAX_UPLOAD_BYTES: number;
3
+ /**
4
+ * Comprimeert een afbeelding (client-side, webp + resize) en uploadt 'm naar
5
+ * `sites/{siteId}/media/...`. Registreert het media-doc (status "processing" — de sharp-Cloud-
6
+ * Function vult srcset-varianten + zet "ready") en geeft de download-URL + media-id terug.
7
+ * Door vóór upload te comprimeren bewaren we nooit het grote origineel → flinke storagebesparing.
8
+ */
9
+ export declare function uploadImage(siteId: string, file: File): Promise<{
10
+ url: string;
11
+ mediaId: string;
12
+ path: string;
13
+ }>;
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { ref as storageRef, uploadBytes, getDownloadURL } from "firebase/storage";
3
+ import { addDoc, collection, serverTimestamp } from "firebase/firestore";
4
+ import { getBucket, getDb } from "../../firebase/client";
5
+ import { compressImage } from "./compressImage";
6
+ const MAX_UPLOAD_BYTES = 15 * 1024 * 1024;
7
+ async function uploadImage(siteId, file) {
8
+ const bucket = getBucket();
9
+ const db = getDb();
10
+ if (!bucket) throw new Error("Firebase Storage is niet geconfigureerd.");
11
+ if (!file.type.startsWith("image/")) throw new Error("Alleen afbeeldingen zijn toegestaan.");
12
+ if (file.size > MAX_UPLOAD_BYTES) {
13
+ throw new Error(`Bestand te groot (max ${Math.round(MAX_UPLOAD_BYTES / 1024 / 1024)} MB).`);
14
+ }
15
+ const optimized = await compressImage(file);
16
+ const path = `sites/${siteId}/media/${Date.now()}-${optimized.name.replace(/[^\w.-]+/g, "_")}`;
17
+ const sref = storageRef(bucket, path);
18
+ await uploadBytes(sref, optimized);
19
+ const url = await getDownloadURL(sref);
20
+ let mediaId = "";
21
+ if (db) {
22
+ const docRef = await addDoc(collection(db, "sites", siteId, "media"), {
23
+ url,
24
+ path,
25
+ name: optimized.name,
26
+ contentType: optimized.type,
27
+ size: optimized.size,
28
+ alt: "",
29
+ status: "processing",
30
+ createdAt: serverTimestamp()
31
+ });
32
+ mediaId = docRef.id;
33
+ }
34
+ return { url, mediaId, path };
35
+ }
36
+ export {
37
+ MAX_UPLOAD_BYTES,
38
+ uploadImage
39
+ };
40
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/lib/upload.ts"],"sourcesContent":["\"use client\";\n\nimport { ref as storageRef, uploadBytes, getDownloadURL } from \"firebase/storage\";\nimport { addDoc, collection, serverTimestamp } from \"firebase/firestore\";\nimport { getBucket, getDb } from \"../../firebase/client\";\nimport { compressImage } from \"./compressImage\";\n\n/** Maximale bestandsgrootte vóór compressie (foolproof: voorkomt vastlopen op enorme bestanden). */\nexport const MAX_UPLOAD_BYTES = 15 * 1024 * 1024; // 15 MB\n\n/**\n * Comprimeert een afbeelding (client-side, webp + resize) en uploadt 'm naar\n * `sites/{siteId}/media/...`. Registreert het media-doc (status \"processing\" — de sharp-Cloud-\n * Function vult srcset-varianten + zet \"ready\") en geeft de download-URL + media-id terug.\n * Door vóór upload te comprimeren bewaren we nooit het grote origineel → flinke storagebesparing.\n */\nexport async function uploadImage(\n siteId: string,\n file: File,\n): Promise<{ url: string; mediaId: string; path: string }> {\n const bucket = getBucket();\n const db = getDb();\n if (!bucket) throw new Error(\"Firebase Storage is niet geconfigureerd.\");\n if (!file.type.startsWith(\"image/\")) throw new Error(\"Alleen afbeeldingen zijn toegestaan.\");\n if (file.size > MAX_UPLOAD_BYTES) {\n throw new Error(`Bestand te groot (max ${Math.round(MAX_UPLOAD_BYTES / 1024 / 1024)} MB).`);\n }\n\n const optimized = await compressImage(file);\n const path = `sites/${siteId}/media/${Date.now()}-${optimized.name.replace(/[^\\w.-]+/g, \"_\")}`;\n const sref = storageRef(bucket, path);\n await uploadBytes(sref, optimized);\n const url = await getDownloadURL(sref);\n let mediaId = \"\";\n if (db) {\n const docRef = await addDoc(collection(db, \"sites\", siteId, \"media\"), {\n url,\n path,\n name: optimized.name,\n contentType: optimized.type,\n size: optimized.size,\n alt: \"\",\n status: \"processing\",\n createdAt: serverTimestamp(),\n });\n mediaId = docRef.id;\n }\n return { url, mediaId, path };\n}\n"],"mappings":";AAEA,SAAS,OAAO,YAAY,aAAa,sBAAsB;AAC/D,SAAS,QAAQ,YAAY,uBAAuB;AACpD,SAAS,WAAW,aAAa;AACjC,SAAS,qBAAqB;AAGvB,MAAM,mBAAmB,KAAK,OAAO;AAQ5C,eAAsB,YACpB,QACA,MACyD;AACzD,QAAM,SAAS,UAAU;AACzB,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,0CAA0C;AACvE,MAAI,CAAC,KAAK,KAAK,WAAW,QAAQ,EAAG,OAAM,IAAI,MAAM,sCAAsC;AAC3F,MAAI,KAAK,OAAO,kBAAkB;AAChC,UAAM,IAAI,MAAM,yBAAyB,KAAK,MAAM,mBAAmB,OAAO,IAAI,CAAC,OAAO;AAAA,EAC5F;AAEA,QAAM,YAAY,MAAM,cAAc,IAAI;AAC1C,QAAM,OAAO,SAAS,MAAM,UAAU,KAAK,IAAI,CAAC,IAAI,UAAU,KAAK,QAAQ,aAAa,GAAG,CAAC;AAC5F,QAAM,OAAO,WAAW,QAAQ,IAAI;AACpC,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,MAAM,MAAM,eAAe,IAAI;AACrC,MAAI,UAAU;AACd,MAAI,IAAI;AACN,UAAM,SAAS,MAAM,OAAO,WAAW,IAAI,SAAS,QAAQ,OAAO,GAAG;AAAA,MACpE;AAAA,MACA;AAAA,MACA,MAAM,UAAU;AAAA,MAChB,aAAa,UAAU;AAAA,MACvB,MAAM,UAAU;AAAA,MAChB,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,WAAW,gBAAgB;AAAA,IAC7B,CAAC;AACD,cAAU,OAAO;AAAA,EACnB;AACA,SAAO,EAAE,KAAK,SAAS,KAAK;AAC9B;","names":[]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Zod-introspectie voor automatisch gegenereerde formulieren. Gedeeld door:
3
+ * - de `GenericEditor` (fallback-editor voor blokken zonder eigen `Editor`), en
4
+ * - het `ModuleSettingsForm` (instellingen van een module in /beheer).
5
+ *
6
+ * Een veld krijgt zijn label uit `.describe("…")` op het schema; zonder beschrijving valt het
7
+ * terug op de veldsleutel. Puur (geen React) — apart testbaar.
8
+ */
9
+ import type { ZodTypeAny } from "zod";
10
+ export interface SchemaField {
11
+ key: string;
12
+ kind: "number" | "boolean" | "string" | "enum" | "json";
13
+ /** Leesbaar label: `.describe()` van het schema, anders de veldsleutel. */
14
+ label: string;
15
+ options?: string[];
16
+ /** Max-lengte van een string (bepaalt Input vs Textarea in de UI). */
17
+ maxLength?: number;
18
+ /** Het volledige veld-schema — voor validatie van json-velden vóór commit. */
19
+ schema: ZodTypeAny;
20
+ }
21
+ /**
22
+ * Leidt een formulier-beschrijving af uit een Zod-objectschema. Interne velden (`_key`/`_type`/
23
+ * `_style`) worden overgeslagen — die beheren de editor-chrome en de StyleEditor. Complexe velden
24
+ * (array/object/record) worden als `json` teruggegeven zodat een fallback-editor ze tóch kan
25
+ * aanbieden; een aanroeper die alleen simpele velden wil, filtert op `kind !== "json"`.
26
+ */
27
+ export declare function introspectObjectSchema(schema: ZodTypeAny): SchemaField[];
@@ -0,0 +1,48 @@
1
+ function unwrap(schema) {
2
+ let s = schema;
3
+ let description = s?._def?.description;
4
+ while (s?._def?.typeName === "ZodDefault" || s?._def?.typeName === "ZodOptional" || s?._def?.typeName === "ZodNullable") {
5
+ s = s._def.innerType;
6
+ description = description ?? s?._def?.description;
7
+ }
8
+ return { def: s?._def, description };
9
+ }
10
+ function introspectObjectSchema(schema) {
11
+ const def = schema._def;
12
+ if (def?.typeName !== "ZodObject" || typeof def.shape !== "function") return [];
13
+ const out = [];
14
+ for (const [key, field] of Object.entries(def.shape())) {
15
+ if (key.startsWith("_")) continue;
16
+ const { def: inner, description } = unwrap(field);
17
+ const label = description ?? key;
18
+ const fieldSchema = field;
19
+ switch (inner?.typeName) {
20
+ case "ZodNumber":
21
+ out.push({ key, kind: "number", label, schema: fieldSchema });
22
+ break;
23
+ case "ZodBoolean":
24
+ out.push({ key, kind: "boolean", label, schema: fieldSchema });
25
+ break;
26
+ case "ZodEnum":
27
+ out.push({ key, kind: "enum", label, options: inner.values, schema: fieldSchema });
28
+ break;
29
+ case "ZodString": {
30
+ const max = inner.checks?.find((c) => c.kind === "max")?.value;
31
+ out.push({ key, kind: "string", label, maxLength: max, schema: fieldSchema });
32
+ break;
33
+ }
34
+ case "ZodArray":
35
+ case "ZodObject":
36
+ case "ZodRecord":
37
+ out.push({ key, kind: "json", label, schema: fieldSchema });
38
+ break;
39
+ default:
40
+ break;
41
+ }
42
+ }
43
+ return out;
44
+ }
45
+ export {
46
+ introspectObjectSchema
47
+ };
48
+ //# sourceMappingURL=zodFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/lib/zodFields.ts"],"sourcesContent":["/**\n * Zod-introspectie voor automatisch gegenereerde formulieren. Gedeeld door:\n * - de `GenericEditor` (fallback-editor voor blokken zonder eigen `Editor`), en\n * - het `ModuleSettingsForm` (instellingen van een module in /beheer).\n *\n * Een veld krijgt zijn label uit `.describe(\"…\")` op het schema; zonder beschrijving valt het\n * terug op de veldsleutel. Puur (geen React) — apart testbaar.\n */\nimport type { ZodTypeAny } from \"zod\";\n\nexport interface SchemaField {\n key: string;\n kind: \"number\" | \"boolean\" | \"string\" | \"enum\" | \"json\";\n /** Leesbaar label: `.describe()` van het schema, anders de veldsleutel. */\n label: string;\n options?: string[];\n /** Max-lengte van een string (bepaalt Input vs Textarea in de UI). */\n maxLength?: number;\n /** Het volledige veld-schema — voor validatie van json-velden vóór commit. */\n schema: ZodTypeAny;\n}\n\ninterface AnyDef {\n typeName?: string;\n innerType?: unknown;\n values?: string[];\n description?: string;\n checks?: { kind: string; value?: number }[];\n shape?: () => Record<string, unknown>;\n}\n\n/** Pakt het binnenste type uit ZodDefault/ZodOptional/ZodNullable; bewaart de buitenste beschrijving. */\nfunction unwrap(schema: unknown): { def?: AnyDef; description?: string } {\n let s = schema as { _def?: AnyDef };\n let description = s?._def?.description;\n while (\n s?._def?.typeName === \"ZodDefault\" ||\n s?._def?.typeName === \"ZodOptional\" ||\n s?._def?.typeName === \"ZodNullable\"\n ) {\n s = s._def.innerType as typeof s;\n description = description ?? s?._def?.description;\n }\n return { def: s?._def, description };\n}\n\n/**\n * Leidt een formulier-beschrijving af uit een Zod-objectschema. Interne velden (`_key`/`_type`/\n * `_style`) worden overgeslagen — die beheren de editor-chrome en de StyleEditor. Complexe velden\n * (array/object/record) worden als `json` teruggegeven zodat een fallback-editor ze tóch kan\n * aanbieden; een aanroeper die alleen simpele velden wil, filtert op `kind !== \"json\"`.\n */\nexport function introspectObjectSchema(schema: ZodTypeAny): SchemaField[] {\n const def = (schema as { _def?: AnyDef })._def;\n if (def?.typeName !== \"ZodObject\" || typeof def.shape !== \"function\") return [];\n const out: SchemaField[] = [];\n for (const [key, field] of Object.entries(def.shape())) {\n if (key.startsWith(\"_\")) continue;\n const { def: inner, description } = unwrap(field);\n const label = description ?? key;\n const fieldSchema = field as ZodTypeAny;\n switch (inner?.typeName) {\n case \"ZodNumber\":\n out.push({ key, kind: \"number\", label, schema: fieldSchema });\n break;\n case \"ZodBoolean\":\n out.push({ key, kind: \"boolean\", label, schema: fieldSchema });\n break;\n case \"ZodEnum\":\n out.push({ key, kind: \"enum\", label, options: inner.values, schema: fieldSchema });\n break;\n case \"ZodString\": {\n const max = inner.checks?.find((c) => c.kind === \"max\")?.value;\n out.push({ key, kind: \"string\", label, maxLength: max, schema: fieldSchema });\n break;\n }\n case \"ZodArray\":\n case \"ZodObject\":\n case \"ZodRecord\":\n out.push({ key, kind: \"json\", label, schema: fieldSchema });\n break;\n default:\n // ZodLiteral (bv. _type), unions e.d.: geen zinvol generiek veld.\n break;\n }\n }\n return out;\n}\n"],"mappings":"AAgCA,SAAS,OAAO,QAAyD;AACvE,MAAI,IAAI;AACR,MAAI,cAAc,GAAG,MAAM;AAC3B,SACE,GAAG,MAAM,aAAa,gBACtB,GAAG,MAAM,aAAa,iBACtB,GAAG,MAAM,aAAa,eACtB;AACA,QAAI,EAAE,KAAK;AACX,kBAAc,eAAe,GAAG,MAAM;AAAA,EACxC;AACA,SAAO,EAAE,KAAK,GAAG,MAAM,YAAY;AACrC;AAQO,SAAS,uBAAuB,QAAmC;AACxE,QAAM,MAAO,OAA6B;AAC1C,MAAI,KAAK,aAAa,eAAe,OAAO,IAAI,UAAU,WAAY,QAAO,CAAC;AAC9E,QAAM,MAAqB,CAAC;AAC5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,MAAM,CAAC,GAAG;AACtD,QAAI,IAAI,WAAW,GAAG,EAAG;AACzB,UAAM,EAAE,KAAK,OAAO,YAAY,IAAI,OAAO,KAAK;AAChD,UAAM,QAAQ,eAAe;AAC7B,UAAM,cAAc;AACpB,YAAQ,OAAO,UAAU;AAAA,MACvB,KAAK;AACH,YAAI,KAAK,EAAE,KAAK,MAAM,UAAU,OAAO,QAAQ,YAAY,CAAC;AAC5D;AAAA,MACF,KAAK;AACH,YAAI,KAAK,EAAE,KAAK,MAAM,WAAW,OAAO,QAAQ,YAAY,CAAC;AAC7D;AAAA,MACF,KAAK;AACH,YAAI,KAAK,EAAE,KAAK,MAAM,QAAQ,OAAO,SAAS,MAAM,QAAQ,QAAQ,YAAY,CAAC;AACjF;AAAA,MACF,KAAK,aAAa;AAChB,cAAM,MAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,GAAG;AACzD,YAAI,KAAK,EAAE,KAAK,MAAM,UAAU,OAAO,WAAW,KAAK,QAAQ,YAAY,CAAC;AAC5E;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,YAAI,KAAK,EAAE,KAAK,MAAM,QAAQ,OAAO,QAAQ,YAAY,CAAC;AAC1D;AAAA,MACF;AAEE;AAAA,IACJ;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,3 @@
1
+ export declare function MediaManager({ siteId }: {
2
+ siteId: string;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,132 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import {
5
+ collection,
6
+ deleteDoc,
7
+ doc,
8
+ onSnapshot,
9
+ orderBy,
10
+ query,
11
+ updateDoc
12
+ } from "firebase/firestore";
13
+ import { deleteObject, ref as storageRef } from "firebase/storage";
14
+ import { getBucket, getDb } from "../../firebase/client";
15
+ import { Button, Card, Badge, Input } from "../ui/primitives";
16
+ import { useToast } from "../ui/toast";
17
+ import { useConfirm } from "../ui/ConfirmDialog";
18
+ import { uploadImage } from "../lib/upload";
19
+ import { findMediaUsage } from "../lib/mediaUsage";
20
+ function MediaManager({ siteId }) {
21
+ const { toast } = useToast();
22
+ const confirm = useConfirm();
23
+ const [items, setItems] = useState([]);
24
+ const [busy, setBusy] = useState(false);
25
+ const inputRef = useRef(null);
26
+ useEffect(() => {
27
+ const db = getDb();
28
+ if (!db) return;
29
+ const q = query(collection(db, "sites", siteId, "media"), orderBy("createdAt", "desc"));
30
+ return onSnapshot(
31
+ q,
32
+ (snap) => setItems(snap.docs.map((d) => ({ id: d.id, ...d.data() })))
33
+ );
34
+ }, [siteId]);
35
+ async function onFiles(files) {
36
+ setBusy(true);
37
+ let ok = 0;
38
+ const errors = [];
39
+ for (const file of Array.from(files)) {
40
+ try {
41
+ await uploadImage(siteId, file);
42
+ ok++;
43
+ } catch (e) {
44
+ errors.push(`${file.name}: ${e.message}`);
45
+ }
46
+ }
47
+ if (ok > 0) toast({ title: `${ok} foto('s) ge\xFCpload`, description: "Automatisch verkleind; varianten worden op de achtergrond gemaakt.", tone: "success" });
48
+ if (errors.length) toast({ title: "Sommige uploads mislukten", description: errors.join(" \xB7 "), tone: "error" });
49
+ setBusy(false);
50
+ }
51
+ async function saveAlt(item, alt) {
52
+ const db = getDb();
53
+ if (!db) return;
54
+ await updateDoc(doc(db, "sites", siteId, "media", item.id), { alt });
55
+ }
56
+ async function remove(item) {
57
+ const db = getDb();
58
+ const bucket = getBucket();
59
+ if (!db) return;
60
+ const usedOn = await findMediaUsage(siteId, item);
61
+ const ok = await confirm({
62
+ title: "Afbeelding verwijderen?",
63
+ description: usedOn.length > 0 ? `Let op: deze afbeelding wordt gebruikt op ${usedOn.length} gepubliceerde pagina('s) (${usedOn.join(", ")}). Daar verschijnt dan een gebroken afbeelding.` : "De afbeelding wordt definitief verwijderd.",
64
+ confirmLabel: "Verwijderen",
65
+ tone: "danger"
66
+ });
67
+ if (!ok) return;
68
+ await deleteDoc(doc(db, "sites", siteId, "media", item.id));
69
+ if (bucket && item.path) {
70
+ try {
71
+ await deleteObject(storageRef(bucket, item.path));
72
+ } catch {
73
+ }
74
+ }
75
+ toast({ title: "Verwijderd" });
76
+ }
77
+ return /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
78
+ /* @__PURE__ */ jsxs("div", { children: [
79
+ /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => inputRef.current?.click(), children: busy ? "Uploaden\u2026" : "Foto's uploaden" }),
80
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500", children: "Je kunt meerdere foto's tegelijk kiezen. Ze worden automatisch verkleind en geoptimaliseerd (webp) \u2014 dat houdt je site snel en bespaart opslag. Max. 15 MB per bestand." }),
81
+ /* @__PURE__ */ jsx(
82
+ "input",
83
+ {
84
+ ref: inputRef,
85
+ type: "file",
86
+ accept: "image/*",
87
+ multiple: true,
88
+ hidden: true,
89
+ onChange: (e) => {
90
+ if (e.target.files?.length) void onFiles(e.target.files);
91
+ e.target.value = "";
92
+ }
93
+ }
94
+ )
95
+ ] }),
96
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4", children: [
97
+ items.map((m) => /* @__PURE__ */ jsxs(Card, { className: "p-3", children: [
98
+ /* @__PURE__ */ jsx("img", { src: m.url, alt: m.alt ?? m.name, className: "mb-2 h-28 w-full rounded-md object-cover" }),
99
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-1", children: [
100
+ /* @__PURE__ */ jsxs("span", { className: "truncate text-xs text-slate-500", children: [
101
+ m.name,
102
+ m.width && /* @__PURE__ */ jsxs("span", { className: "text-slate-400", children: [
103
+ " \xB7 ",
104
+ m.width,
105
+ "\xD7",
106
+ m.height
107
+ ] })
108
+ ] }),
109
+ m.status === "processing" ? /* @__PURE__ */ jsx(Badge, { tone: "amber", children: "verwerken" }) : /* @__PURE__ */ jsx(Badge, { tone: "green", children: "klaar" })
110
+ ] }),
111
+ /* @__PURE__ */ jsx(
112
+ Input,
113
+ {
114
+ defaultValue: m.alt ?? "",
115
+ placeholder: "Alt-tekst",
116
+ className: "mb-2 h-8 text-xs",
117
+ onBlur: (e) => void saveAlt(m, e.target.value)
118
+ }
119
+ ),
120
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
121
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "text-xs", onClick: () => void navigator.clipboard?.writeText(m.url), children: "Kopieer URL" }),
122
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "text-xs", onClick: () => void remove(m), children: "Verwijder" })
123
+ ] })
124
+ ] }, m.id)),
125
+ items.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen media." })
126
+ ] })
127
+ ] });
128
+ }
129
+ export {
130
+ MediaManager
131
+ };
132
+ //# sourceMappingURL=MediaManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/media/MediaManager.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n collection,\n deleteDoc,\n doc,\n onSnapshot,\n orderBy,\n query,\n updateDoc,\n} from \"firebase/firestore\";\nimport { deleteObject, ref as storageRef } from \"firebase/storage\";\nimport { getBucket, getDb } from \"../../firebase/client\";\nimport type { MediaItem } from \"../../content/types\";\nimport { Button, Card, Badge, Input } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { uploadImage } from \"../lib/upload\";\nimport { findMediaUsage } from \"../lib/mediaUsage\";\n\nexport function MediaManager({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [items, setItems] = useState<MediaItem[]>([]);\n const [busy, setBusy] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const q = query(collection(db, \"sites\", siteId, \"media\"), orderBy(\"createdAt\", \"desc\"));\n return onSnapshot(q, (snap) =>\n setItems(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<MediaItem, \"id\">) }))),\n );\n }, [siteId]);\n\n async function onFiles(files: FileList) {\n setBusy(true);\n let ok = 0;\n const errors: string[] = [];\n for (const file of Array.from(files)) {\n try {\n await uploadImage(siteId, file);\n ok++;\n } catch (e) {\n errors.push(`${file.name}: ${(e as Error).message}`);\n }\n }\n if (ok > 0) toast({ title: `${ok} foto('s) geüpload`, description: \"Automatisch verkleind; varianten worden op de achtergrond gemaakt.\", tone: \"success\" });\n if (errors.length) toast({ title: \"Sommige uploads mislukten\", description: errors.join(\" · \"), tone: \"error\" });\n setBusy(false);\n }\n\n async function saveAlt(item: MediaItem, alt: string) {\n const db = getDb();\n if (!db) return;\n await updateDoc(doc(db, \"sites\", siteId, \"media\", item.id), { alt });\n }\n\n async function remove(item: MediaItem) {\n const db = getDb();\n const bucket = getBucket();\n if (!db) return;\n const usedOn = await findMediaUsage(siteId, item);\n const ok = await confirm({\n title: \"Afbeelding verwijderen?\",\n description:\n usedOn.length > 0\n ? `Let op: deze afbeelding wordt gebruikt op ${usedOn.length} gepubliceerde pagina('s) (${usedOn.join(\", \")}). Daar verschijnt dan een gebroken afbeelding.`\n : \"De afbeelding wordt definitief verwijderd.\",\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n await deleteDoc(doc(db, \"sites\", siteId, \"media\", item.id));\n if (bucket && item.path) {\n try {\n await deleteObject(storageRef(bucket, item.path));\n } catch {\n /* al weg — negeren */\n }\n }\n toast({ title: \"Verwijderd\" });\n }\n\n return (\n <div className=\"grid gap-4\">\n <div>\n <Button disabled={busy} onClick={() => inputRef.current?.click()}>\n {busy ? \"Uploaden…\" : \"Foto's uploaden\"}\n </Button>\n <p className=\"mt-1 text-xs text-slate-500\">\n Je kunt meerdere foto&apos;s tegelijk kiezen. Ze worden automatisch verkleind en geoptimaliseerd\n (webp) — dat houdt je site snel en bespaart opslag. Max. 15 MB per bestand.\n </p>\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n hidden\n onChange={(e) => {\n if (e.target.files?.length) void onFiles(e.target.files);\n e.target.value = \"\";\n }}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4\">\n {items.map((m) => (\n <Card key={m.id} className=\"p-3\">\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img src={m.url} alt={m.alt ?? m.name} className=\"mb-2 h-28 w-full rounded-md object-cover\" />\n <div className=\"mb-2 flex items-center justify-between gap-1\">\n <span className=\"truncate text-xs text-slate-500\">\n {m.name}\n {(m as { width?: number; height?: number }).width && (\n <span className=\"text-slate-400\"> · {(m as { width?: number }).width}×{(m as { height?: number }).height}</span>\n )}\n </span>\n {m.status === \"processing\" ? <Badge tone=\"amber\">verwerken</Badge> : <Badge tone=\"green\">klaar</Badge>}\n </div>\n <Input\n defaultValue={m.alt ?? \"\"}\n placeholder=\"Alt-tekst\"\n className=\"mb-2 h-8 text-xs\"\n onBlur={(e) => void saveAlt(m, e.target.value)}\n />\n <div className=\"flex gap-1\">\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => void navigator.clipboard?.writeText(m.url)}>\n Kopieer URL\n </Button>\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => void remove(m)}>\n Verwijder\n </Button>\n </div>\n </Card>\n ))}\n {items.length === 0 && <p className=\"text-sm text-slate-500\">Nog geen media.</p>}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,24 @@
1
+ export interface PickedMedia {
2
+ url: string;
3
+ mediaId: string;
4
+ alt?: string;
5
+ }
6
+ /** Gecontroleerde mediabibliotheek-kiezer (open/onOpenChange extern) — zoek + klik om te kiezen, of
7
+ * upload direct. Gebruikt door de knop hieronder én door de inline beeld-vervangen in de editor. */
8
+ export declare function MediaPickerDialog({ siteId, open, onOpenChange, onSelect, }: {
9
+ siteId: string;
10
+ open: boolean;
11
+ onOpenChange: (v: boolean) => void;
12
+ onSelect: (m: PickedMedia) => void;
13
+ }): import("react").JSX.Element;
14
+ /**
15
+ * Knop die de mediabibliotheek-kiezer opent: zoek + klik om een bestaande afbeelding te kiezen, of
16
+ * upload er direct een nieuwe in. Vervangt het plakken van losse URL's.
17
+ */
18
+ export declare function MediaPickerButton({ siteId, onSelect, label, size, variant, }: {
19
+ siteId: string;
20
+ onSelect: (m: PickedMedia) => void;
21
+ label?: string;
22
+ size?: "sm" | "md";
23
+ variant?: "primary" | "secondary" | "ghost";
24
+ }): import("react").JSX.Element;
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import { collection, onSnapshot, orderBy, query } from "firebase/firestore";
5
+ import { getDb } from "../../firebase/client";
6
+ import { Button, Input } from "../ui/primitives";
7
+ import { Dialog, DialogContent } from "../ui/dialog";
8
+ import { useToast } from "../ui/toast";
9
+ import { uploadImage } from "../lib/upload";
10
+ function MediaPickerDialog({
11
+ siteId,
12
+ open,
13
+ onOpenChange,
14
+ onSelect
15
+ }) {
16
+ const { toast } = useToast();
17
+ const [items, setItems] = useState([]);
18
+ const [q, setQ] = useState("");
19
+ const [busy, setBusy] = useState(false);
20
+ const inputRef = useRef(null);
21
+ useEffect(() => {
22
+ if (!open) return;
23
+ const db = getDb();
24
+ if (!db) return;
25
+ const qy = query(collection(db, "sites", siteId, "media"), orderBy("createdAt", "desc"));
26
+ return onSnapshot(
27
+ qy,
28
+ (snap) => setItems(snap.docs.map((d) => ({ id: d.id, ...d.data() })))
29
+ );
30
+ }, [open, siteId]);
31
+ const filtered = useMemo(() => {
32
+ const term = q.trim().toLowerCase();
33
+ if (!term) return items;
34
+ return items.filter((m) => `${m.name ?? ""} ${m.alt ?? ""}`.toLowerCase().includes(term));
35
+ }, [items, q]);
36
+ function pick(m) {
37
+ onSelect({ url: m.url, mediaId: m.id, alt: m.alt });
38
+ onOpenChange(false);
39
+ }
40
+ async function onUpload(files) {
41
+ const f = files?.[0];
42
+ if (!f) return;
43
+ setBusy(true);
44
+ try {
45
+ const { url, mediaId } = await uploadImage(siteId, f);
46
+ onSelect({ url, mediaId, alt: "" });
47
+ onOpenChange(false);
48
+ } catch (err) {
49
+ toast({ title: "Upload mislukt", description: err.message, tone: "error" });
50
+ } finally {
51
+ setBusy(false);
52
+ }
53
+ }
54
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Afbeelding kiezen", className: "max-w-2xl", children: [
55
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
56
+ /* @__PURE__ */ jsx(Input, { value: q, onChange: (e) => setQ(e.target.value), placeholder: "Zoek in bibliotheek\u2026", className: "h-9" }),
57
+ /* @__PURE__ */ jsx(Button, { type: "button", disabled: busy, onClick: () => inputRef.current?.click(), children: busy ? "Uploaden\u2026" : "Upload nieuwe" }),
58
+ /* @__PURE__ */ jsx("input", { ref: inputRef, type: "file", accept: "image/*", hidden: true, onChange: (e) => {
59
+ void onUpload(e.target.files);
60
+ e.target.value = "";
61
+ } })
62
+ ] }),
63
+ /* @__PURE__ */ jsx("div", { className: "max-h-[55vh] overflow-y-auto", children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-8 text-center text-sm text-slate-500", children: items.length === 0 ? "Nog geen afbeeldingen \u2014 upload er een." : "Geen resultaten." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 sm:grid-cols-4", children: filtered.map((m) => /* @__PURE__ */ jsx(
64
+ "button",
65
+ {
66
+ type: "button",
67
+ onClick: () => pick(m),
68
+ title: m.alt || m.name,
69
+ className: "group relative overflow-hidden rounded-md border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500",
70
+ children: /* @__PURE__ */ jsx("img", { src: m.url, alt: m.alt ?? m.name, className: "aspect-square w-full object-cover transition group-hover:opacity-80" })
71
+ },
72
+ m.id
73
+ )) }) })
74
+ ] }) });
75
+ }
76
+ function MediaPickerButton({
77
+ siteId,
78
+ onSelect,
79
+ label = "Kies / upload",
80
+ size = "sm",
81
+ variant = "secondary"
82
+ }) {
83
+ const [open, setOpen] = useState(false);
84
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
85
+ /* @__PURE__ */ jsx(Button, { type: "button", variant, size, onClick: () => setOpen(true), children: label }),
86
+ /* @__PURE__ */ jsx(MediaPickerDialog, { siteId, open, onOpenChange: setOpen, onSelect })
87
+ ] });
88
+ }
89
+ export {
90
+ MediaPickerButton,
91
+ MediaPickerDialog
92
+ };
93
+ //# sourceMappingURL=MediaPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/media/MediaPicker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { collection, onSnapshot, orderBy, query } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport type { MediaItem } from \"../../content/types\";\nimport { Button, Input } from \"../ui/primitives\";\nimport { Dialog, DialogContent } from \"../ui/dialog\";\nimport { useToast } from \"../ui/toast\";\nimport { uploadImage } from \"../lib/upload\";\n\nexport interface PickedMedia {\n url: string;\n mediaId: string;\n alt?: string;\n}\n\n/** Gecontroleerde mediabibliotheek-kiezer (open/onOpenChange extern) — zoek + klik om te kiezen, of\n * upload direct. Gebruikt door de knop hieronder én door de inline beeld-vervangen in de editor. */\nexport function MediaPickerDialog({\n siteId,\n open,\n onOpenChange,\n onSelect,\n}: {\n siteId: string;\n open: boolean;\n onOpenChange: (v: boolean) => void;\n onSelect: (m: PickedMedia) => void;\n}) {\n const { toast } = useToast();\n const [items, setItems] = useState<MediaItem[]>([]);\n const [q, setQ] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const db = getDb();\n if (!db) return;\n const qy = query(collection(db, \"sites\", siteId, \"media\"), orderBy(\"createdAt\", \"desc\"));\n return onSnapshot(qy, (snap) =>\n setItems(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<MediaItem, \"id\">) }))),\n );\n }, [open, siteId]);\n\n const filtered = useMemo(() => {\n const term = q.trim().toLowerCase();\n if (!term) return items;\n return items.filter((m) => `${m.name ?? \"\"} ${m.alt ?? \"\"}`.toLowerCase().includes(term));\n }, [items, q]);\n\n function pick(m: MediaItem) {\n onSelect({ url: m.url, mediaId: m.id, alt: m.alt });\n onOpenChange(false);\n }\n\n async function onUpload(files: FileList | null) {\n const f = files?.[0];\n if (!f) return;\n setBusy(true);\n try {\n const { url, mediaId } = await uploadImage(siteId, f);\n onSelect({ url, mediaId, alt: \"\" });\n onOpenChange(false);\n } catch (err) {\n toast({ title: \"Upload mislukt\", description: (err as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent title=\"Afbeelding kiezen\" className=\"max-w-2xl\">\n <div className=\"mb-3 flex items-center gap-2\">\n <Input value={q} onChange={(e) => setQ(e.target.value)} placeholder=\"Zoek in bibliotheek…\" className=\"h-9\" />\n <Button type=\"button\" disabled={busy} onClick={() => inputRef.current?.click()}>\n {busy ? \"Uploaden…\" : \"Upload nieuwe\"}\n </Button>\n <input ref={inputRef} type=\"file\" accept=\"image/*\" hidden onChange={(e) => { void onUpload(e.target.files); e.target.value = \"\"; }} />\n </div>\n <div className=\"max-h-[55vh] overflow-y-auto\">\n {filtered.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-slate-500\">\n {items.length === 0 ? \"Nog geen afbeeldingen — upload er een.\" : \"Geen resultaten.\"}\n </p>\n ) : (\n <div className=\"grid grid-cols-3 gap-2 sm:grid-cols-4\">\n {filtered.map((m) => (\n <button\n key={m.id}\n type=\"button\"\n onClick={() => pick(m)}\n title={m.alt || m.name}\n className=\"group relative overflow-hidden rounded-md border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img src={m.url} alt={m.alt ?? m.name} className=\"aspect-square w-full object-cover transition group-hover:opacity-80\" />\n </button>\n ))}\n </div>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Knop die de mediabibliotheek-kiezer opent: zoek + klik om een bestaande afbeelding te kiezen, of\n * upload er direct een nieuwe in. Vervangt het plakken van losse URL's.\n */\nexport function MediaPickerButton({\n siteId,\n onSelect,\n label = \"Kies / upload\",\n size = \"sm\",\n variant = \"secondary\",\n}: {\n siteId: string;\n onSelect: (m: PickedMedia) => void;\n label?: string;\n size?: \"sm\" | \"md\";\n variant?: \"primary\" | \"secondary\" | \"ghost\";\n}) {\n const [open, setOpen] = useState(false);\n return (\n <>\n <Button type=\"button\" variant={variant} size={size} onClick={() => setOpen(true)}>{label}</Button>\n <MediaPickerDialog siteId={siteId} open={open} onOpenChange={setOpen} onSelect={onSelect} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ import { type DraftPage } from "../lib/pagesClient";
2
+ export declare function PageEditor({ siteId, page }: {
3
+ siteId: string;
4
+ page: DraftPage;
5
+ }): import("react").JSX.Element;