@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.
- package/dist/XiCMSProvider.d.ts +58 -0
- package/dist/XiCMSProvider.js +165 -0
- package/dist/XiCMSProvider.js.map +1 -0
- package/dist/admin/AdminApp.d.ts +4 -0
- package/dist/admin/AdminApp.js +62 -0
- package/dist/admin/AdminApp.js.map +1 -0
- package/dist/admin/AdminHelp.d.ts +2 -0
- package/dist/admin/AdminHelp.js +65 -0
- package/dist/admin/AdminHelp.js.map +1 -0
- package/dist/admin/NewsEditor.d.ts +2 -0
- package/dist/admin/NewsEditor.js +171 -0
- package/dist/admin/NewsEditor.js.map +1 -0
- package/dist/admin/SuperadminPanel.d.ts +4 -0
- package/dist/admin/SuperadminPanel.js +101 -0
- package/dist/admin/SuperadminPanel.js.map +1 -0
- package/dist/admin/blockEditors/StyleEditor.d.ts +10 -0
- package/dist/admin/blockEditors/StyleEditor.js +78 -0
- package/dist/admin/blockEditors/StyleEditor.js.map +1 -0
- package/dist/admin/blockEditors/index.d.ts +16 -0
- package/dist/admin/blockEditors/index.js +1602 -0
- package/dist/admin/blockEditors/index.js.map +1 -0
- package/dist/admin/editors/RichTextEditor.d.ts +10 -0
- package/dist/admin/editors/RichTextEditor.js +86 -0
- package/dist/admin/editors/RichTextEditor.js.map +1 -0
- package/dist/admin/forms/FormsPanel.d.ts +3 -0
- package/dist/admin/forms/FormsPanel.js +203 -0
- package/dist/admin/forms/FormsPanel.js.map +1 -0
- package/dist/admin/index.d.ts +21 -0
- package/dist/admin/index.js +38 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/lib/compressImage.d.ts +12 -0
- package/dist/admin/lib/compressImage.js +53 -0
- package/dist/admin/lib/compressImage.js.map +1 -0
- package/dist/admin/lib/mediaUsage.d.ts +9 -0
- package/dist/admin/lib/mediaUsage.js +25 -0
- package/dist/admin/lib/mediaUsage.js.map +1 -0
- package/dist/admin/lib/pagesClient.d.ts +38 -0
- package/dist/admin/lib/pagesClient.js +143 -0
- package/dist/admin/lib/pagesClient.js.map +1 -0
- package/dist/admin/lib/slugify.d.ts +2 -0
- package/dist/admin/lib/slugify.js +7 -0
- package/dist/admin/lib/slugify.js.map +1 -0
- package/dist/admin/lib/upload.d.ts +13 -0
- package/dist/admin/lib/upload.js +40 -0
- package/dist/admin/lib/upload.js.map +1 -0
- package/dist/admin/lib/zodFields.d.ts +27 -0
- package/dist/admin/lib/zodFields.js +48 -0
- package/dist/admin/lib/zodFields.js.map +1 -0
- package/dist/admin/media/MediaManager.d.ts +3 -0
- package/dist/admin/media/MediaManager.js +132 -0
- package/dist/admin/media/MediaManager.js.map +1 -0
- package/dist/admin/media/MediaPicker.d.ts +24 -0
- package/dist/admin/media/MediaPicker.js +93 -0
- package/dist/admin/media/MediaPicker.js.map +1 -0
- package/dist/admin/pages/PageEditor.d.ts +5 -0
- package/dist/admin/pages/PageEditor.js +239 -0
- package/dist/admin/pages/PageEditor.js.map +1 -0
- package/dist/admin/pages/PagesPanel.d.ts +3 -0
- package/dist/admin/pages/PagesPanel.js +92 -0
- package/dist/admin/pages/PagesPanel.js.map +1 -0
- package/dist/admin/panelRegistry.d.ts +20 -0
- package/dist/admin/panelRegistry.js +23 -0
- package/dist/admin/panelRegistry.js.map +1 -0
- package/dist/admin/patterns/PatternInserter.d.ts +10 -0
- package/dist/admin/patterns/PatternInserter.js +58 -0
- package/dist/admin/patterns/PatternInserter.js.map +1 -0
- package/dist/admin/settings/SettingsPanel.d.ts +3 -0
- package/dist/admin/settings/SettingsPanel.js +304 -0
- package/dist/admin/settings/SettingsPanel.js.map +1 -0
- package/dist/admin/settings/moduleSettingsForm.d.ts +12 -0
- package/dist/admin/settings/moduleSettingsForm.js +45 -0
- package/dist/admin/settings/moduleSettingsForm.js.map +1 -0
- package/dist/admin/stats/StatsPanel.d.ts +4 -0
- package/dist/admin/stats/StatsPanel.js +80 -0
- package/dist/admin/stats/StatsPanel.js.map +1 -0
- package/dist/admin/ui/ArrayEditor.d.ts +16 -0
- package/dist/admin/ui/ArrayEditor.js +38 -0
- package/dist/admin/ui/ArrayEditor.js.map +1 -0
- package/dist/admin/ui/ConfirmDialog.d.ts +22 -0
- package/dist/admin/ui/ConfirmDialog.js +47 -0
- package/dist/admin/ui/ConfirmDialog.js.map +1 -0
- package/dist/admin/ui/cn.d.ts +3 -0
- package/dist/admin/ui/cn.js +9 -0
- package/dist/admin/ui/cn.js.map +1 -0
- package/dist/admin/ui/dialog.d.ts +9 -0
- package/dist/admin/ui/dialog.js +47 -0
- package/dist/admin/ui/dialog.js.map +1 -0
- package/dist/admin/ui/index.d.ts +7 -0
- package/dist/admin/ui/index.js +29 -0
- package/dist/admin/ui/index.js.map +1 -0
- package/dist/admin/ui/primitives.d.ts +37 -0
- package/dist/admin/ui/primitives.js +137 -0
- package/dist/admin/ui/primitives.js.map +1 -0
- package/dist/admin/ui/select.d.ts +11 -0
- package/dist/admin/ui/select.js +27 -0
- package/dist/admin/ui/select.js.map +1 -0
- package/dist/admin/ui/switch.d.ts +6 -0
- package/dist/admin/ui/switch.js +22 -0
- package/dist/admin/ui/switch.js.map +1 -0
- package/dist/admin/ui/tabs.d.ts +5 -0
- package/dist/admin/ui/tabs.js +36 -0
- package/dist/admin/ui/tabs.js.map +1 -0
- package/dist/admin/ui/toast.d.ts +19 -0
- package/dist/admin/ui/toast.js +24 -0
- package/dist/admin/ui/toast.js.map +1 -0
- package/dist/admin/visual/VisualEditor.d.ts +10 -0
- package/dist/admin/visual/VisualEditor.js +597 -0
- package/dist/admin/visual/VisualEditor.js.map +1 -0
- package/dist/admin/visual/reorder.d.ts +6 -0
- package/dist/admin/visual/reorder.js +12 -0
- package/dist/admin/visual/reorder.js.map +1 -0
- package/dist/admin.css +1 -0
- package/dist/auth/AuthProvider.d.ts +30 -0
- package/dist/auth/AuthProvider.js +110 -0
- package/dist/auth/AuthProvider.js.map +1 -0
- package/dist/auth/LoginForm.d.ts +4 -0
- package/dist/auth/LoginForm.js +122 -0
- package/dist/auth/LoginForm.js.map +1 -0
- package/dist/auth/roles.d.ts +15 -0
- package/dist/auth/roles.js +21 -0
- package/dist/auth/roles.js.map +1 -0
- package/dist/blocks/BlockErrorBoundary.d.ts +32 -0
- package/dist/blocks/BlockErrorBoundary.js +39 -0
- package/dist/blocks/BlockErrorBoundary.js.map +1 -0
- package/dist/blocks/BlockRenderer.d.ts +15 -0
- package/dist/blocks/BlockRenderer.js +24 -0
- package/dist/blocks/BlockRenderer.js.map +1 -0
- package/dist/blocks/blockStyle.d.ts +14 -0
- package/dist/blocks/blockStyle.js +14 -0
- package/dist/blocks/blockStyle.js.map +1 -0
- package/dist/blocks/builtins/Accordion.d.ts +2 -0
- package/dist/blocks/builtins/Accordion.js +45 -0
- package/dist/blocks/builtins/Accordion.js.map +1 -0
- package/dist/blocks/builtins/Agenda.d.ts +23 -0
- package/dist/blocks/builtins/Agenda.js +102 -0
- package/dist/blocks/builtins/Agenda.js.map +1 -0
- package/dist/blocks/builtins/Banner.d.ts +2 -0
- package/dist/blocks/builtins/Banner.js +45 -0
- package/dist/blocks/builtins/Banner.js.map +1 -0
- package/dist/blocks/builtins/BeforeAfter.d.ts +2 -0
- package/dist/blocks/builtins/BeforeAfter.js +44 -0
- package/dist/blocks/builtins/BeforeAfter.js.map +1 -0
- package/dist/blocks/builtins/Booking.d.ts +2 -0
- package/dist/blocks/builtins/Booking.js +27 -0
- package/dist/blocks/builtins/Booking.js.map +1 -0
- package/dist/blocks/builtins/Breadcrumbs.d.ts +10 -0
- package/dist/blocks/builtins/Breadcrumbs.js +73 -0
- package/dist/blocks/builtins/Breadcrumbs.js.map +1 -0
- package/dist/blocks/builtins/Cards.d.ts +2 -0
- package/dist/blocks/builtins/Cards.js +94 -0
- package/dist/blocks/builtins/Cards.js.map +1 -0
- package/dist/blocks/builtins/Columns.d.ts +2 -0
- package/dist/blocks/builtins/Columns.js +22 -0
- package/dist/blocks/builtins/Columns.js.map +1 -0
- package/dist/blocks/builtins/Contact.d.ts +7 -0
- package/dist/blocks/builtins/Contact.js +121 -0
- package/dist/blocks/builtins/Contact.js.map +1 -0
- package/dist/blocks/builtins/Countdown.d.ts +18 -0
- package/dist/blocks/builtins/Countdown.js +65 -0
- package/dist/blocks/builtins/Countdown.js.map +1 -0
- package/dist/blocks/builtins/Cta.d.ts +2 -0
- package/dist/blocks/builtins/Cta.js +52 -0
- package/dist/blocks/builtins/Cta.js.map +1 -0
- package/dist/blocks/builtins/Downloads.d.ts +2 -0
- package/dist/blocks/builtins/Downloads.js +32 -0
- package/dist/blocks/builtins/Downloads.js.map +1 -0
- package/dist/blocks/builtins/Elements.d.ts +4 -0
- package/dist/blocks/builtins/Elements.js +64 -0
- package/dist/blocks/builtins/Elements.js.map +1 -0
- package/dist/blocks/builtins/Embed.d.ts +2 -0
- package/dist/blocks/builtins/Embed.js +30 -0
- package/dist/blocks/builtins/Embed.js.map +1 -0
- package/dist/blocks/builtins/Faq.d.ts +6 -0
- package/dist/blocks/builtins/Faq.js +110 -0
- package/dist/blocks/builtins/Faq.js.map +1 -0
- package/dist/blocks/builtins/FormBlock.d.ts +2 -0
- package/dist/blocks/builtins/FormBlock.js +221 -0
- package/dist/blocks/builtins/FormBlock.js.map +1 -0
- package/dist/blocks/builtins/Gallery.d.ts +23 -0
- package/dist/blocks/builtins/Gallery.js +177 -0
- package/dist/blocks/builtins/Gallery.js.map +1 -0
- package/dist/blocks/builtins/Hero.d.ts +2 -0
- package/dist/blocks/builtins/Hero.js +47 -0
- package/dist/blocks/builtins/Hero.js.map +1 -0
- package/dist/blocks/builtins/ImageBlock.d.ts +2 -0
- package/dist/blocks/builtins/ImageBlock.js +24 -0
- package/dist/blocks/builtins/ImageBlock.js.map +1 -0
- package/dist/blocks/builtins/Jobs.d.ts +8 -0
- package/dist/blocks/builtins/Jobs.js +65 -0
- package/dist/blocks/builtins/Jobs.js.map +1 -0
- package/dist/blocks/builtins/Layout.d.ts +3 -0
- package/dist/blocks/builtins/Layout.js +67 -0
- package/dist/blocks/builtins/Layout.js.map +1 -0
- package/dist/blocks/builtins/MapBlock.d.ts +2 -0
- package/dist/blocks/builtins/MapBlock.js +31 -0
- package/dist/blocks/builtins/MapBlock.js.map +1 -0
- package/dist/blocks/builtins/NewsListBlock.d.ts +2 -0
- package/dist/blocks/builtins/NewsListBlock.js +24 -0
- package/dist/blocks/builtins/NewsListBlock.js.map +1 -0
- package/dist/blocks/builtins/Newsletter.d.ts +2 -0
- package/dist/blocks/builtins/Newsletter.js +84 -0
- package/dist/blocks/builtins/Newsletter.js.map +1 -0
- package/dist/blocks/builtins/OpenNowBadge.d.ts +9 -0
- package/dist/blocks/builtins/OpenNowBadge.js +20 -0
- package/dist/blocks/builtins/OpenNowBadge.js.map +1 -0
- package/dist/blocks/builtins/OpeningHours.d.ts +2 -0
- package/dist/blocks/builtins/OpeningHours.js +47 -0
- package/dist/blocks/builtins/OpeningHours.js.map +1 -0
- package/dist/blocks/builtins/PdfBlock.d.ts +2 -0
- package/dist/blocks/builtins/PdfBlock.js +97 -0
- package/dist/blocks/builtins/PdfBlock.js.map +1 -0
- package/dist/blocks/builtins/Portfolio.d.ts +2 -0
- package/dist/blocks/builtins/Portfolio.js +94 -0
- package/dist/blocks/builtins/Portfolio.js.map +1 -0
- package/dist/blocks/builtins/Pricing.d.ts +6 -0
- package/dist/blocks/builtins/Pricing.js +77 -0
- package/dist/blocks/builtins/Pricing.js.map +1 -0
- package/dist/blocks/builtins/RichTextBlock.d.ts +2 -0
- package/dist/blocks/builtins/RichTextBlock.js +18 -0
- package/dist/blocks/builtins/RichTextBlock.js.map +1 -0
- package/dist/blocks/builtins/Search.d.ts +2 -0
- package/dist/blocks/builtins/Search.js +99 -0
- package/dist/blocks/builtins/Search.js.map +1 -0
- package/dist/blocks/builtins/Sections.d.ts +5 -0
- package/dist/blocks/builtins/Sections.js +183 -0
- package/dist/blocks/builtins/Sections.js.map +1 -0
- package/dist/blocks/builtins/Slider.d.ts +2 -0
- package/dist/blocks/builtins/Slider.js +131 -0
- package/dist/blocks/builtins/Slider.js.map +1 -0
- package/dist/blocks/builtins/Spacer.d.ts +2 -0
- package/dist/blocks/builtins/Spacer.js +19 -0
- package/dist/blocks/builtins/Spacer.js.map +1 -0
- package/dist/blocks/builtins/Team.d.ts +2 -0
- package/dist/blocks/builtins/Team.js +70 -0
- package/dist/blocks/builtins/Team.js.map +1 -0
- package/dist/blocks/builtins/Testimonials.d.ts +8 -0
- package/dist/blocks/builtins/Testimonials.js +82 -0
- package/dist/blocks/builtins/Testimonials.js.map +1 -0
- package/dist/blocks/builtins/Video.d.ts +9 -0
- package/dist/blocks/builtins/Video.js +77 -0
- package/dist/blocks/builtins/Video.js.map +1 -0
- package/dist/blocks/builtins/WhatsappChat.d.ts +8 -0
- package/dist/blocks/builtins/WhatsappChat.js +51 -0
- package/dist/blocks/builtins/WhatsappChat.js.map +1 -0
- package/dist/blocks/builtins/Widgets.d.ts +13 -0
- package/dist/blocks/builtins/Widgets.js +308 -0
- package/dist/blocks/builtins/Widgets.js.map +1 -0
- package/dist/blocks/builtins/consentEmbed.d.ts +17 -0
- package/dist/blocks/builtins/consentEmbed.js +31 -0
- package/dist/blocks/builtins/consentEmbed.js.map +1 -0
- package/dist/blocks/builtins/index.d.ts +46 -0
- package/dist/blocks/builtins/index.js +121 -0
- package/dist/blocks/builtins/index.js.map +1 -0
- package/dist/blocks/builtins/lib/bookingUrl.d.ts +8 -0
- package/dist/blocks/builtins/lib/bookingUrl.js +50 -0
- package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -0
- package/dist/blocks/builtins/lib/fileIcon.d.ts +2 -0
- package/dist/blocks/builtins/lib/fileIcon.js +45 -0
- package/dist/blocks/builtins/lib/fileIcon.js.map +1 -0
- package/dist/blocks/builtins/lib/ics.d.ts +17 -0
- package/dist/blocks/builtins/lib/ics.js +50 -0
- package/dist/blocks/builtins/lib/ics.js.map +1 -0
- package/dist/blocks/builtins/lib/openingHours.d.ts +20 -0
- package/dist/blocks/builtins/lib/openingHours.js +79 -0
- package/dist/blocks/builtins/lib/openingHours.js.map +1 -0
- package/dist/blocks/builtins/lib/searchRank.d.ts +22 -0
- package/dist/blocks/builtins/lib/searchRank.js +45 -0
- package/dist/blocks/builtins/lib/searchRank.js.map +1 -0
- package/dist/blocks/builtins/lib/submitForm.d.ts +6 -0
- package/dist/blocks/builtins/lib/submitForm.js +14 -0
- package/dist/blocks/builtins/lib/submitForm.js.map +1 -0
- package/dist/blocks/context.d.ts +8 -0
- package/dist/blocks/context.js +20 -0
- package/dist/blocks/context.js.map +1 -0
- package/dist/blocks/index.d.ts +9 -0
- package/dist/blocks/index.js +21 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +75 -0
- package/dist/blocks/registry.js +71 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/ui/index.d.ts +35 -0
- package/dist/blocks/ui/index.js +48 -0
- package/dist/blocks/ui/index.js.map +1 -0
- package/dist/components/EditBar.d.ts +2 -0
- package/dist/components/EditBar.js +20 -0
- package/dist/components/EditBar.js.map +1 -0
- package/dist/components/EditableImage.d.ts +18 -0
- package/dist/components/EditableImage.js +62 -0
- package/dist/components/EditableImage.js.map +1 -0
- package/dist/components/EditableRichText.d.ts +14 -0
- package/dist/components/EditableRichText.js +26 -0
- package/dist/components/EditableRichText.js.map +1 -0
- package/dist/components/EditableText.d.ts +21 -0
- package/dist/components/EditableText.js +25 -0
- package/dist/components/EditableText.js.map +1 -0
- package/dist/components/News.d.ts +38 -0
- package/dist/components/News.js +128 -0
- package/dist/components/News.js.map +1 -0
- package/dist/components/PageView.d.ts +8 -0
- package/dist/components/PageView.js +28 -0
- package/dist/components/PageView.js.map +1 -0
- package/dist/components/ResponsiveImage.d.ts +16 -0
- package/dist/components/ResponsiveImage.js +39 -0
- package/dist/components/ResponsiveImage.js.map +1 -0
- package/dist/components/site/SiteFooter.d.ts +12 -0
- package/dist/components/site/SiteFooter.js +34 -0
- package/dist/components/site/SiteFooter.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/content/ContentProvider.d.ts +23 -0
- package/dist/content/ContentProvider.js +86 -0
- package/dist/content/ContentProvider.js.map +1 -0
- package/dist/content/types.d.ts +41 -0
- package/dist/content/types.js +1 -0
- package/dist/content/types.js.map +1 -0
- package/dist/demo/DemoStudio.d.ts +7 -0
- package/dist/demo/DemoStudio.js +54 -0
- package/dist/demo/DemoStudio.js.map +1 -0
- package/dist/demo/demoStore.d.ts +26 -0
- package/dist/demo/demoStore.js +115 -0
- package/dist/demo/demoStore.js.map +1 -0
- package/dist/editor/EditorProvider.d.ts +12 -0
- package/dist/editor/EditorProvider.js +22 -0
- package/dist/editor/EditorProvider.js.map +1 -0
- package/dist/firebase/admin.d.ts +7 -0
- package/dist/firebase/admin.js +31 -0
- package/dist/firebase/admin.js.map +1 -0
- package/dist/firebase/appcheck.d.ts +6 -0
- package/dist/firebase/appcheck.js +23 -0
- package/dist/firebase/appcheck.js.map +1 -0
- package/dist/firebase/client.d.ts +9 -0
- package/dist/firebase/client.js +44 -0
- package/dist/firebase/client.js.map +1 -0
- package/dist/firebase/region.d.ts +5 -0
- package/dist/firebase/region.js +7 -0
- package/dist/firebase/region.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/dist/layers.css +13 -0
- package/dist/lock/DemoLock.d.ts +5 -0
- package/dist/lock/DemoLock.js +35 -0
- package/dist/lock/DemoLock.js.map +1 -0
- package/dist/media/ImageContext.d.ts +41 -0
- package/dist/media/ImageContext.js +74 -0
- package/dist/media/ImageContext.js.map +1 -0
- package/dist/modules/builtins.d.ts +46 -0
- package/dist/modules/builtins.js +462 -0
- package/dist/modules/builtins.js.map +1 -0
- package/dist/modules/consent/ConsentBanner.d.ts +42 -0
- package/dist/modules/consent/ConsentBanner.js +119 -0
- package/dist/modules/consent/ConsentBanner.js.map +1 -0
- package/dist/modules/consent/useConsent.d.ts +28 -0
- package/dist/modules/consent/useConsent.js +52 -0
- package/dist/modules/consent/useConsent.js.map +1 -0
- package/dist/modules/context.d.ts +20 -0
- package/dist/modules/context.js +31 -0
- package/dist/modules/context.js.map +1 -0
- package/dist/modules/contract.d.ts +9 -0
- package/dist/modules/contract.js +38 -0
- package/dist/modules/contract.js.map +1 -0
- package/dist/modules/defineModule.d.ts +11 -0
- package/dist/modules/defineModule.js +7 -0
- package/dist/modules/defineModule.js.map +1 -0
- package/dist/modules/index.d.ts +13 -0
- package/dist/modules/index.js +44 -0
- package/dist/modules/index.js.map +1 -0
- package/dist/modules/overrides.d.ts +17 -0
- package/dist/modules/overrides.js +25 -0
- package/dist/modules/overrides.js.map +1 -0
- package/dist/modules/registry.d.ts +40 -0
- package/dist/modules/registry.js +79 -0
- package/dist/modules/registry.js.map +1 -0
- package/dist/modules/types.d.ts +66 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/types.js.map +1 -0
- package/dist/patterns/builtins.d.ts +31 -0
- package/dist/patterns/builtins.js +415 -0
- package/dist/patterns/builtins.js.map +1 -0
- package/dist/patterns/index.d.ts +6 -0
- package/dist/patterns/index.js +9 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/patterns.d.ts +28 -0
- package/dist/patterns/patterns.js +53 -0
- package/dist/patterns/patterns.js.map +1 -0
- package/dist/render/PageRenderer.d.ts +13 -0
- package/dist/render/PageRenderer.js +14 -0
- package/dist/render/PageRenderer.js.map +1 -0
- package/dist/render/PublishedPage.d.ts +13 -0
- package/dist/render/PublishedPage.js +16 -0
- package/dist/render/PublishedPage.js.map +1 -0
- package/dist/schema/blocks.d.ts +15052 -0
- package/dist/schema/blocks.js +794 -0
- package/dist/schema/blocks.js.map +1 -0
- package/dist/schema/forms.d.ts +111 -0
- package/dist/schema/forms.js +47 -0
- package/dist/schema/forms.js.map +1 -0
- package/dist/schema/index.d.ts +11 -0
- package/dist/schema/index.js +16 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/pages.d.ts +674 -0
- package/dist/schema/pages.js +37 -0
- package/dist/schema/pages.js.map +1 -0
- package/dist/schema/seo.d.ts +29 -0
- package/dist/schema/seo.js +17 -0
- package/dist/schema/seo.js.map +1 -0
- package/dist/schema/settings.d.ts +1126 -0
- package/dist/schema/settings.js +128 -0
- package/dist/schema/settings.js.map +1 -0
- package/dist/schema/siteConfig.d.ts +722 -0
- package/dist/schema/siteConfig.js +39 -0
- package/dist/schema/siteConfig.js.map +1 -0
- package/dist/server/env.d.ts +10 -0
- package/dist/server/env.js +36 -0
- package/dist/server/env.js.map +1 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.js +51 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/pages.d.ts +15 -0
- package/dist/server/pages.js +99 -0
- package/dist/server/pages.js.map +1 -0
- package/dist/server/sanitize.d.ts +2 -0
- package/dist/server/sanitize.js +55 -0
- package/dist/server/sanitize.js.map +1 -0
- package/dist/server/seo.d.ts +116 -0
- package/dist/server/seo.js +179 -0
- package/dist/server/seo.js.map +1 -0
- package/dist/settings/useSiteSettings.d.ts +25 -0
- package/dist/settings/useSiteSettings.js +68 -0
- package/dist/settings/useSiteSettings.js.map +1 -0
- package/dist/styles.css +2495 -0
- package/dist/theme/presets.d.ts +12 -0
- package/dist/theme/presets.js +147 -0
- package/dist/theme/presets.js.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { useRegistry } from "../../blocks/context";
|
|
5
|
+
import { buildBlockAddOptions } from "../../blocks/registry";
|
|
6
|
+
import { useModules } from "../../modules/context";
|
|
7
|
+
import { moduleIdForBlockType } from "../../modules/registry";
|
|
8
|
+
import { Button, Card, Field, Input, Textarea, Badge } from "../ui/primitives";
|
|
9
|
+
import { Select } from "../ui/select";
|
|
10
|
+
import { useToast } from "../ui/toast";
|
|
11
|
+
import { useConfirm } from "../ui/ConfirmDialog";
|
|
12
|
+
import { builtinBlockEditors, GenericEditor } from "../blockEditors";
|
|
13
|
+
import { StyleEditor } from "../blockEditors/StyleEditor";
|
|
14
|
+
import { PatternInserter } from "../patterns/PatternInserter";
|
|
15
|
+
import { saveDraft, publishPage, subscribeVersions, restoreVersion } from "../lib/pagesClient";
|
|
16
|
+
import { slugify } from "../lib/slugify";
|
|
17
|
+
function newKey() {
|
|
18
|
+
return (globalThis.crypto?.randomUUID?.() ?? `b${Date.now()}${Math.random()}`).slice(0, 12);
|
|
19
|
+
}
|
|
20
|
+
function formatVersionDate(createdAt) {
|
|
21
|
+
const d = createdAt && typeof createdAt.toDate === "function" ? createdAt.toDate() : typeof createdAt === "string" ? new Date(createdAt) : null;
|
|
22
|
+
if (!d || Number.isNaN(d.getTime())) return "";
|
|
23
|
+
return d.toLocaleString("nl-NL", { dateStyle: "medium", timeStyle: "short" });
|
|
24
|
+
}
|
|
25
|
+
function PageEditor({ siteId, page }) {
|
|
26
|
+
const registry = useRegistry();
|
|
27
|
+
const { activeIds, defs } = useModules();
|
|
28
|
+
const { toast } = useToast();
|
|
29
|
+
const confirm = useConfirm();
|
|
30
|
+
const [draft, setDraft] = useState(page);
|
|
31
|
+
const [busy, setBusy] = useState(false);
|
|
32
|
+
const [saveState, setSaveState] = useState("idle");
|
|
33
|
+
const [versions, setVersions] = useState([]);
|
|
34
|
+
const firstRender = useRef(true);
|
|
35
|
+
const slugTouched = useRef(Boolean(page.slug));
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setDraft(page);
|
|
38
|
+
firstRender.current = true;
|
|
39
|
+
slugTouched.current = Boolean(page.slug);
|
|
40
|
+
}, [page.id]);
|
|
41
|
+
useEffect(() => subscribeVersions(siteId, page.id, (v) => setVersions(v)), [siteId, page.id]);
|
|
42
|
+
function persist(d) {
|
|
43
|
+
setSaveState("saving");
|
|
44
|
+
return saveDraft(siteId, d.id, {
|
|
45
|
+
title: d.title,
|
|
46
|
+
slug: d.slug,
|
|
47
|
+
path: d.path,
|
|
48
|
+
seo: d.seo,
|
|
49
|
+
blocks: d.blocks,
|
|
50
|
+
order: d.order
|
|
51
|
+
}).then(() => setSaveState("saved")).catch(() => {
|
|
52
|
+
setSaveState("error");
|
|
53
|
+
toast({ title: "Opslaan mislukt", description: "Wijzigingen zijn nog niet opgeslagen.", tone: "error" });
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (firstRender.current) {
|
|
58
|
+
firstRender.current = false;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const t = setTimeout(() => void persist(draft), 800);
|
|
62
|
+
return () => clearTimeout(t);
|
|
63
|
+
}, [draft]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const onBeforeUnload = (e) => {
|
|
66
|
+
if (saveState === "saving" || saveState === "error") {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
e.returnValue = "";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
72
|
+
return () => window.removeEventListener("beforeunload", onBeforeUnload);
|
|
73
|
+
}, [saveState]);
|
|
74
|
+
const blocks = draft.blocks ?? [];
|
|
75
|
+
function update(next) {
|
|
76
|
+
setDraft(next);
|
|
77
|
+
}
|
|
78
|
+
function setBlocks(next) {
|
|
79
|
+
update({ ...draft, blocks: next });
|
|
80
|
+
}
|
|
81
|
+
function setTitle(title) {
|
|
82
|
+
const slug = slugTouched.current ? draft.slug : slugify(title);
|
|
83
|
+
update({ ...draft, title, slug });
|
|
84
|
+
}
|
|
85
|
+
function addBlock(type) {
|
|
86
|
+
const def = registry.get(type);
|
|
87
|
+
const base = def?.defaults?.() ?? {};
|
|
88
|
+
setBlocks([...blocks, { _key: newKey(), _type: type, ...base }]);
|
|
89
|
+
}
|
|
90
|
+
function addBlocks(newBlocks) {
|
|
91
|
+
setBlocks([...blocks, ...newBlocks]);
|
|
92
|
+
}
|
|
93
|
+
function updateBlock(i, next) {
|
|
94
|
+
const copy = [...blocks];
|
|
95
|
+
copy[i] = { ...next, _key: blocks[i]._key, _type: blocks[i]._type };
|
|
96
|
+
setBlocks(copy);
|
|
97
|
+
}
|
|
98
|
+
async function removeBlock(i) {
|
|
99
|
+
const ok = await confirm({
|
|
100
|
+
title: "Blok verwijderen?",
|
|
101
|
+
description: "Dit blok wordt uit de pagina verwijderd.",
|
|
102
|
+
confirmLabel: "Verwijderen",
|
|
103
|
+
tone: "danger"
|
|
104
|
+
});
|
|
105
|
+
if (!ok) return;
|
|
106
|
+
setBlocks(blocks.filter((_, idx) => idx !== i));
|
|
107
|
+
}
|
|
108
|
+
function move(i, dir) {
|
|
109
|
+
const j = i + dir;
|
|
110
|
+
if (j < 0 || j >= blocks.length) return;
|
|
111
|
+
const copy = [...blocks];
|
|
112
|
+
[copy[i], copy[j]] = [copy[j], copy[i]];
|
|
113
|
+
setBlocks(copy);
|
|
114
|
+
}
|
|
115
|
+
async function onPublish() {
|
|
116
|
+
const ok = await confirm({
|
|
117
|
+
title: "Pagina publiceren?",
|
|
118
|
+
description: `De gepubliceerde site wordt bijgewerkt (${draft.path}).`,
|
|
119
|
+
confirmLabel: "Publiceren"
|
|
120
|
+
});
|
|
121
|
+
if (!ok) return;
|
|
122
|
+
setBusy(true);
|
|
123
|
+
try {
|
|
124
|
+
await saveDraft(siteId, draft.id, { title: draft.title, slug: draft.slug, path: draft.path, seo: draft.seo, blocks: draft.blocks });
|
|
125
|
+
await publishPage(siteId, draft.id);
|
|
126
|
+
toast({ title: "Gepubliceerd", description: draft.path, tone: "success" });
|
|
127
|
+
} catch (e) {
|
|
128
|
+
toast({ title: "Publiceren mislukt", description: e.message, tone: "error" });
|
|
129
|
+
} finally {
|
|
130
|
+
setBusy(false);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function onRestore(versionId) {
|
|
134
|
+
const ok = await confirm({
|
|
135
|
+
title: "Versie herstellen?",
|
|
136
|
+
description: "De huidige werkversie wordt overschreven met deze versie. Publiceer daarna om het live te zetten.",
|
|
137
|
+
confirmLabel: "Herstellen"
|
|
138
|
+
});
|
|
139
|
+
if (!ok) return;
|
|
140
|
+
try {
|
|
141
|
+
const restored = await restoreVersion(siteId, draft.id, versionId);
|
|
142
|
+
setDraft({ ...draft, ...restored });
|
|
143
|
+
firstRender.current = true;
|
|
144
|
+
toast({ title: "Versie hersteld", description: "Controleer en publiceer om live te zetten.", tone: "success" });
|
|
145
|
+
} catch (e) {
|
|
146
|
+
toast({ title: "Herstellen mislukt", description: e.message, tone: "error" });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const addOptions = useMemo(() => buildBlockAddOptions(registry.values()), [registry]);
|
|
150
|
+
const disabledBlocks = useMemo(() => {
|
|
151
|
+
const seen = /* @__PURE__ */ new Set();
|
|
152
|
+
const out = [];
|
|
153
|
+
for (const b of blocks) {
|
|
154
|
+
if (registry.get(b._type)) continue;
|
|
155
|
+
const modId = moduleIdForBlockType(defs, b._type);
|
|
156
|
+
if (modId && !activeIds.has(modId) && !seen.has(b._type)) {
|
|
157
|
+
seen.add(b._type);
|
|
158
|
+
out.push(b._type);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}, [blocks, registry, defs, activeIds]);
|
|
163
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
|
|
164
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
165
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [
|
|
166
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
167
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: "Pagina-instellingen" }),
|
|
168
|
+
draft.published ? /* @__PURE__ */ jsx(Badge, { tone: "green", children: "gepubliceerd" }) : /* @__PURE__ */ jsx(Badge, { tone: "amber", children: "concept" })
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
171
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-slate-500", "aria-live": "polite", children: saveState === "saving" ? "Opslaan\u2026" : saveState === "saved" ? "\u2713 Opgeslagen" : "" }),
|
|
172
|
+
saveState === "error" && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: () => void persist(draft), children: "Opnieuw opslaan" }),
|
|
173
|
+
/* @__PURE__ */ jsx(Button, { onClick: onPublish, disabled: busy, children: busy ? "Bezig\u2026" : "Publiceren" })
|
|
174
|
+
] })
|
|
175
|
+
] }),
|
|
176
|
+
saveState === "error" && /* @__PURE__ */ jsx("div", { className: "mb-3 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700", children: "Niet alle wijzigingen zijn opgeslagen. Controleer je verbinding en klik \u201COpnieuw opslaan\u201D." }),
|
|
177
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
178
|
+
/* @__PURE__ */ jsx(Field, { label: "Titel", children: /* @__PURE__ */ jsx(Input, { value: draft.title, onChange: (e) => setTitle(e.target.value) }) }),
|
|
179
|
+
/* @__PURE__ */ jsx(Field, { label: "Pad", hint: "bv. / of /over-ons", children: /* @__PURE__ */ jsx(Input, { value: draft.path, onChange: (e) => update({ ...draft, path: e.target.value }) }) }),
|
|
180
|
+
/* @__PURE__ */ jsx(Field, { label: "SEO-titel", children: /* @__PURE__ */ jsx(Input, { value: draft.seo?.title ?? "", onChange: (e) => update({ ...draft, seo: { ...draft.seo, title: e.target.value } }) }) }),
|
|
181
|
+
/* @__PURE__ */ jsx(Field, { label: "SEO-omschrijving", children: /* @__PURE__ */ jsx(Textarea, { rows: 2, value: draft.seo?.description ?? "", onChange: (e) => update({ ...draft, seo: { ...draft.seo, description: e.target.value } }) }) })
|
|
182
|
+
] })
|
|
183
|
+
] }),
|
|
184
|
+
disabledBlocks.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800", children: [
|
|
185
|
+
"Deze pagina bevat blokken van uitgeschakelde modules (",
|
|
186
|
+
disabledBlocks.join(", "),
|
|
187
|
+
"). Ze worden",
|
|
188
|
+
/* @__PURE__ */ jsx("strong", { children: " niet" }),
|
|
189
|
+
" op de live site getoond. Zet de module aan onder Instellingen \u2192 Modules, of verwijder de blokken."
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
192
|
+
blocks.map((b, i) => {
|
|
193
|
+
const def = registry.get(b._type);
|
|
194
|
+
const Editor = def?.Editor ?? builtinBlockEditors[b._type] ?? GenericEditor;
|
|
195
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
196
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
197
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: def?.label ?? b._type }),
|
|
198
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
199
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(i, -1), "aria-label": "Omhoog", children: "\u2191" }),
|
|
200
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(i, 1), "aria-label": "Omlaag", children: "\u2193" }),
|
|
201
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => void removeBlock(i), "aria-label": "Verwijderen", children: "\u{1F5D1}" })
|
|
202
|
+
] })
|
|
203
|
+
] }),
|
|
204
|
+
/* @__PURE__ */ jsx(Editor, { block: b, siteId, onChange: (next) => updateBlock(i, next) }),
|
|
205
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
|
|
206
|
+
StyleEditor,
|
|
207
|
+
{
|
|
208
|
+
value: b._style,
|
|
209
|
+
onChange: (style) => updateBlock(i, { ...b, _style: style })
|
|
210
|
+
}
|
|
211
|
+
) })
|
|
212
|
+
] }, b._key);
|
|
213
|
+
}),
|
|
214
|
+
blocks.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen blokken. Voeg er hieronder een toe." })
|
|
215
|
+
] }),
|
|
216
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
217
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-slate-600", children: "Toevoegen:" }),
|
|
218
|
+
/* @__PURE__ */ jsx("div", { className: "w-56", children: /* @__PURE__ */ jsx(Select, { value: "", placeholder: "Kies een blok\u2026", onValueChange: (v) => v && addBlock(v), options: addOptions }) }),
|
|
219
|
+
/* @__PURE__ */ jsx(PatternInserter, { onInsert: addBlocks })
|
|
220
|
+
] }),
|
|
221
|
+
versions.length > 0 && /* @__PURE__ */ jsxs(Card, { children: [
|
|
222
|
+
/* @__PURE__ */ jsx("h4", { className: "mb-2 text-sm font-semibold", children: "Versiegeschiedenis" }),
|
|
223
|
+
/* @__PURE__ */ jsx("ul", { className: "grid gap-1 text-sm text-slate-600", children: versions.slice(0, 10).map((v) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between border-b border-slate-100 py-1 last:border-0", children: [
|
|
224
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
225
|
+
v.label ?? v.kind ?? "snapshot",
|
|
226
|
+
formatVersionDate(v.createdAt) && /* @__PURE__ */ jsxs("span", { className: "text-slate-400", children: [
|
|
227
|
+
" \xB7 ",
|
|
228
|
+
formatVersionDate(v.createdAt)
|
|
229
|
+
] })
|
|
230
|
+
] }),
|
|
231
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => void onRestore(v.id), children: "Herstellen" })
|
|
232
|
+
] }, v.id)) })
|
|
233
|
+
] })
|
|
234
|
+
] });
|
|
235
|
+
}
|
|
236
|
+
export {
|
|
237
|
+
PageEditor
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=PageEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/pages/PageEditor.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState, type ComponentType } from \"react\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { buildBlockAddOptions } from \"../../blocks/registry\";\nimport { useModules } from \"../../modules/context\";\nimport { moduleIdForBlockType } from \"../../modules/registry\";\nimport { Button, Card, Field, Input, Textarea, Badge } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { builtinBlockEditors, GenericEditor, type BlockEditorProps } from \"../blockEditors\";\nimport { StyleEditor } from \"../blockEditors/StyleEditor\";\nimport { PatternInserter } from \"../patterns/PatternInserter\";\nimport type { BlockStyle } from \"../../schema/blocks\";\nimport { saveDraft, publishPage, subscribeVersions, restoreVersion, type DraftPage } from \"../lib/pagesClient\";\nimport { slugify } from \"../lib/slugify\";\n\ntype Block = Record<string, unknown> & { _key: string; _type: string };\ntype SaveState = \"idle\" | \"saving\" | \"saved\" | \"error\";\n\ninterface VersionRow {\n id: string;\n createdAt?: unknown;\n kind?: string;\n label?: string;\n}\n\nfunction newKey() {\n return (globalThis.crypto?.randomUUID?.() ?? `b${Date.now()}${Math.random()}`).slice(0, 12);\n}\n\nfunction formatVersionDate(createdAt: unknown): string {\n const d =\n createdAt && typeof (createdAt as { toDate?: () => Date }).toDate === \"function\"\n ? (createdAt as { toDate: () => Date }).toDate()\n : typeof createdAt === \"string\"\n ? new Date(createdAt)\n : null;\n if (!d || Number.isNaN(d.getTime())) return \"\";\n return d.toLocaleString(\"nl-NL\", { dateStyle: \"medium\", timeStyle: \"short\" });\n}\n\nexport function PageEditor({ siteId, page }: { siteId: string; page: DraftPage }) {\n const registry = useRegistry();\n const { activeIds, defs } = useModules();\n const { toast } = useToast();\n const confirm = useConfirm();\n const [draft, setDraft] = useState<DraftPage>(page);\n const [busy, setBusy] = useState(false);\n const [saveState, setSaveState] = useState<SaveState>(\"idle\");\n const [versions, setVersions] = useState<VersionRow[]>([]);\n const firstRender = useRef(true);\n const slugTouched = useRef(Boolean(page.slug));\n\n // Wissel van pagina → herlaad lokale state.\n useEffect(() => {\n setDraft(page);\n firstRender.current = true;\n slugTouched.current = Boolean(page.slug);\n }, [page.id]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => subscribeVersions(siteId, page.id, (v) => setVersions(v)), [siteId, page.id]);\n\n function persist(d: DraftPage) {\n setSaveState(\"saving\");\n return saveDraft(siteId, d.id, {\n title: d.title,\n slug: d.slug,\n path: d.path,\n seo: d.seo,\n blocks: d.blocks,\n order: d.order,\n })\n .then(() => setSaveState(\"saved\"))\n .catch(() => {\n setSaveState(\"error\");\n toast({ title: \"Opslaan mislukt\", description: \"Wijzigingen zijn nog niet opgeslagen.\", tone: \"error\" });\n });\n }\n\n // Autosave (debounced) bij wijzigingen.\n useEffect(() => {\n if (firstRender.current) {\n firstRender.current = false;\n return;\n }\n const t = setTimeout(() => void persist(draft), 800);\n return () => clearTimeout(t);\n }, [draft]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Waarschuw bij verlaten van de pagina met niet-opgeslagen/mislukte wijzigingen.\n useEffect(() => {\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (saveState === \"saving\" || saveState === \"error\") {\n e.preventDefault();\n e.returnValue = \"\";\n }\n };\n window.addEventListener(\"beforeunload\", onBeforeUnload);\n return () => window.removeEventListener(\"beforeunload\", onBeforeUnload);\n }, [saveState]);\n\n const blocks = (draft.blocks ?? []) as Block[];\n\n function update(next: DraftPage) {\n setDraft(next);\n }\n function setBlocks(next: Block[]) {\n update({ ...draft, blocks: next });\n }\n function setTitle(title: string) {\n // Auto-slug zolang de gebruiker de slug niet handmatig heeft aangeraakt.\n const slug = slugTouched.current ? draft.slug : slugify(title);\n update({ ...draft, title, slug });\n }\n function addBlock(type: string) {\n const def = registry.get(type);\n const base = def?.defaults?.() ?? {};\n setBlocks([...blocks, { _key: newKey(), _type: type, ...base } as Block]);\n }\n function addBlocks(newBlocks: Array<Record<string, unknown>>) {\n setBlocks([...blocks, ...(newBlocks as Block[])]);\n }\n function updateBlock(i: number, next: Record<string, unknown>) {\n const copy = [...blocks];\n copy[i] = { ...(next as Block), _key: blocks[i]._key, _type: blocks[i]._type };\n setBlocks(copy);\n }\n async function removeBlock(i: number) {\n const ok = await confirm({\n title: \"Blok verwijderen?\",\n description: \"Dit blok wordt uit de pagina verwijderd.\",\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n setBlocks(blocks.filter((_, idx) => idx !== i));\n }\n function move(i: number, dir: -1 | 1) {\n const j = i + dir;\n if (j < 0 || j >= blocks.length) return;\n const copy = [...blocks];\n [copy[i], copy[j]] = [copy[j], copy[i]];\n setBlocks(copy);\n }\n\n async function onPublish() {\n const ok = await confirm({\n title: \"Pagina publiceren?\",\n description: `De gepubliceerde site wordt bijgewerkt (${draft.path}).`,\n confirmLabel: \"Publiceren\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await saveDraft(siteId, draft.id, { title: draft.title, slug: draft.slug, path: draft.path, seo: draft.seo, blocks: draft.blocks });\n await publishPage(siteId, draft.id);\n toast({ title: \"Gepubliceerd\", description: draft.path, tone: \"success\" });\n } catch (e) {\n toast({ title: \"Publiceren mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function onRestore(versionId: string) {\n const ok = await confirm({\n title: \"Versie herstellen?\",\n description: \"De huidige werkversie wordt overschreven met deze versie. Publiceer daarna om het live te zetten.\",\n confirmLabel: \"Herstellen\",\n });\n if (!ok) return;\n try {\n const restored = await restoreVersion(siteId, draft.id, versionId);\n setDraft({ ...draft, ...restored });\n firstRender.current = true; // herstelde versie is al opgeslagen; geen extra autosave nodig\n toast({ title: \"Versie hersteld\", description: \"Controleer en publiceer om live te zetten.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Herstellen mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n }\n\n const addOptions = useMemo(() => buildBlockAddOptions(registry.values()), [registry]);\n\n // Blocks waarvan de module is uitgeschakeld → tonen niet op de live site.\n const disabledBlocks = useMemo(() => {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const b of blocks) {\n if (registry.get(b._type)) continue; // actief block-type → ok\n const modId = moduleIdForBlockType(defs, b._type);\n if (modId && !activeIds.has(modId) && !seen.has(b._type)) {\n seen.add(b._type);\n out.push(b._type);\n }\n }\n return out;\n }, [blocks, registry, defs, activeIds]);\n\n return (\n <div className=\"grid gap-4\">\n <Card>\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-base font-semibold\">Pagina-instellingen</h3>\n {draft.published ? <Badge tone=\"green\">gepubliceerd</Badge> : <Badge tone=\"amber\">concept</Badge>}\n </div>\n <div className=\"flex items-center gap-3\">\n <span className=\"text-xs text-slate-500\" aria-live=\"polite\">\n {saveState === \"saving\" ? \"Opslaan…\" : saveState === \"saved\" ? \"✓ Opgeslagen\" : \"\"}\n </span>\n {saveState === \"error\" && (\n <Button variant=\"secondary\" size=\"sm\" onClick={() => void persist(draft)}>\n Opnieuw opslaan\n </Button>\n )}\n <Button onClick={onPublish} disabled={busy}>\n {busy ? \"Bezig…\" : \"Publiceren\"}\n </Button>\n </div>\n </div>\n {saveState === \"error\" && (\n <div className=\"mb-3 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700\">\n Niet alle wijzigingen zijn opgeslagen. Controleer je verbinding en klik “Opnieuw opslaan”.\n </div>\n )}\n <div className=\"grid gap-3 md:grid-cols-2\">\n <Field label=\"Titel\">\n <Input value={draft.title} onChange={(e) => setTitle(e.target.value)} />\n </Field>\n <Field label=\"Pad\" hint=\"bv. / of /over-ons\">\n <Input value={draft.path} onChange={(e) => update({ ...draft, path: e.target.value })} />\n </Field>\n <Field label=\"SEO-titel\">\n <Input value={draft.seo?.title ?? \"\"} onChange={(e) => update({ ...draft, seo: { ...draft.seo, title: e.target.value } })} />\n </Field>\n <Field label=\"SEO-omschrijving\">\n <Textarea rows={2} value={draft.seo?.description ?? \"\"} onChange={(e) => update({ ...draft, seo: { ...draft.seo, description: e.target.value } })} />\n </Field>\n </div>\n </Card>\n\n {disabledBlocks.length > 0 && (\n <div className=\"rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800\">\n Deze pagina bevat blokken van uitgeschakelde modules ({disabledBlocks.join(\", \")}). Ze worden\n <strong> niet</strong> op de live site getoond. Zet de module aan onder Instellingen → Modules,\n of verwijder de blokken.\n </div>\n )}\n\n <div className=\"grid gap-3\">\n {blocks.map((b, i) => {\n const def = registry.get(b._type);\n const Editor: ComponentType<BlockEditorProps> =\n (def?.Editor as ComponentType<BlockEditorProps> | undefined) ?? builtinBlockEditors[b._type] ?? GenericEditor;\n return (\n <Card key={b._key}>\n <div className=\"mb-3 flex items-center justify-between\">\n <span className=\"text-sm font-semibold\">{def?.label ?? b._type}</span>\n <div className=\"flex items-center gap-1\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => move(i, -1)} aria-label=\"Omhoog\">↑</Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => move(i, 1)} aria-label=\"Omlaag\">↓</Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void removeBlock(i)} aria-label=\"Verwijderen\">🗑</Button>\n </div>\n </div>\n <Editor block={b} siteId={siteId} onChange={(next) => updateBlock(i, next)} />\n <div className=\"mt-3\">\n <StyleEditor\n value={(b as Block & { _style?: BlockStyle })._style}\n onChange={(style) => updateBlock(i, { ...b, _style: style })}\n />\n </div>\n </Card>\n );\n })}\n {blocks.length === 0 && <p className=\"text-sm text-slate-500\">Nog geen blokken. Voeg er hieronder een toe.</p>}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-slate-600\">Toevoegen:</span>\n <div className=\"w-56\">\n <Select value=\"\" placeholder=\"Kies een blok…\" onValueChange={(v) => v && addBlock(v)} options={addOptions} />\n </div>\n <PatternInserter onInsert={addBlocks} />\n </div>\n\n {versions.length > 0 && (\n <Card>\n <h4 className=\"mb-2 text-sm font-semibold\">Versiegeschiedenis</h4>\n <ul className=\"grid gap-1 text-sm text-slate-600\">\n {versions.slice(0, 10).map((v) => (\n <li key={v.id} className=\"flex items-center justify-between border-b border-slate-100 py-1 last:border-0\">\n <span>\n {v.label ?? v.kind ?? \"snapshot\"}\n {formatVersionDate(v.createdAt) && <span className=\"text-slate-400\"> · {formatVersionDate(v.createdAt)}</span>}\n </span>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void onRestore(v.id)}>\n Herstellen\n </Button>\n </li>\n ))}\n </ul>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":";AA4MU,SACE,KADF;AA1MV,SAAS,WAAW,SAAS,QAAQ,gBAAoC;AACzE,SAAS,mBAAmB;AAC5B,SAAS,4BAA4B;AACrC,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AACrC,SAAS,QAAQ,MAAM,OAAO,OAAO,UAAU,aAAa;AAC5D,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB,qBAA4C;AAC1E,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,SAAS,WAAW,aAAa,mBAAmB,sBAAsC;AAC1F,SAAS,eAAe;AAYxB,SAAS,SAAS;AAChB,UAAQ,WAAW,QAAQ,aAAa,KAAK,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,IAAI,MAAM,GAAG,EAAE;AAC5F;AAEA,SAAS,kBAAkB,WAA4B;AACrD,QAAM,IACJ,aAAa,OAAQ,UAAsC,WAAW,aACjE,UAAqC,OAAO,IAC7C,OAAO,cAAc,WACnB,IAAI,KAAK,SAAS,IAClB;AACR,MAAI,CAAC,KAAK,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO;AAC5C,SAAO,EAAE,eAAe,SAAS,EAAE,WAAW,UAAU,WAAW,QAAQ,CAAC;AAC9E;AAEO,SAAS,WAAW,EAAE,QAAQ,KAAK,GAAwC;AAChF,QAAM,WAAW,YAAY;AAC7B,QAAM,EAAE,WAAW,KAAK,IAAI,WAAW;AACvC,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,UAAU,WAAW;AAC3B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAoB,IAAI;AAClD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAoB,MAAM;AAC5D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAuB,CAAC,CAAC;AACzD,QAAM,cAAc,OAAO,IAAI;AAC/B,QAAM,cAAc,OAAO,QAAQ,KAAK,IAAI,CAAC;AAG7C,YAAU,MAAM;AACd,aAAS,IAAI;AACb,gBAAY,UAAU;AACtB,gBAAY,UAAU,QAAQ,KAAK,IAAI;AAAA,EACzC,GAAG,CAAC,KAAK,EAAE,CAAC;AAEZ,YAAU,MAAM,kBAAkB,QAAQ,KAAK,IAAI,CAAC,MAAM,YAAY,CAAC,CAAC,GAAG,CAAC,QAAQ,KAAK,EAAE,CAAC;AAE5F,WAAS,QAAQ,GAAc;AAC7B,iBAAa,QAAQ;AACrB,WAAO,UAAU,QAAQ,EAAE,IAAI;AAAA,MAC7B,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR,KAAK,EAAE;AAAA,MACP,QAAQ,EAAE;AAAA,MACV,OAAO,EAAE;AAAA,IACX,CAAC,EACE,KAAK,MAAM,aAAa,OAAO,CAAC,EAChC,MAAM,MAAM;AACX,mBAAa,OAAO;AACpB,YAAM,EAAE,OAAO,mBAAmB,aAAa,yCAAyC,MAAM,QAAQ,CAAC;AAAA,IACzG,CAAC;AAAA,EACL;AAGA,YAAU,MAAM;AACd,QAAI,YAAY,SAAS;AACvB,kBAAY,UAAU;AACtB;AAAA,IACF;AACA,UAAM,IAAI,WAAW,MAAM,KAAK,QAAQ,KAAK,GAAG,GAAG;AACnD,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,KAAK,CAAC;AAGV,YAAU,MAAM;AACd,UAAM,iBAAiB,CAAC,MAAyB;AAC/C,UAAI,cAAc,YAAY,cAAc,SAAS;AACnD,UAAE,eAAe;AACjB,UAAE,cAAc;AAAA,MAClB;AAAA,IACF;AACA,WAAO,iBAAiB,gBAAgB,cAAc;AACtD,WAAO,MAAM,OAAO,oBAAoB,gBAAgB,cAAc;AAAA,EACxE,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,SAAU,MAAM,UAAU,CAAC;AAEjC,WAAS,OAAO,MAAiB;AAC/B,aAAS,IAAI;AAAA,EACf;AACA,WAAS,UAAU,MAAe;AAChC,WAAO,EAAE,GAAG,OAAO,QAAQ,KAAK,CAAC;AAAA,EACnC;AACA,WAAS,SAAS,OAAe;AAE/B,UAAM,OAAO,YAAY,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC7D,WAAO,EAAE,GAAG,OAAO,OAAO,KAAK,CAAC;AAAA,EAClC;AACA,WAAS,SAAS,MAAc;AAC9B,UAAM,MAAM,SAAS,IAAI,IAAI;AAC7B,UAAM,OAAO,KAAK,WAAW,KAAK,CAAC;AACnC,cAAU,CAAC,GAAG,QAAQ,EAAE,MAAM,OAAO,GAAG,OAAO,MAAM,GAAG,KAAK,CAAU,CAAC;AAAA,EAC1E;AACA,WAAS,UAAU,WAA2C;AAC5D,cAAU,CAAC,GAAG,QAAQ,GAAI,SAAqB,CAAC;AAAA,EAClD;AACA,WAAS,YAAY,GAAW,MAA+B;AAC7D,UAAM,OAAO,CAAC,GAAG,MAAM;AACvB,SAAK,CAAC,IAAI,EAAE,GAAI,MAAgB,MAAM,OAAO,CAAC,EAAE,MAAM,OAAO,OAAO,CAAC,EAAE,MAAM;AAC7E,cAAU,IAAI;AAAA,EAChB;AACA,iBAAe,YAAY,GAAW;AACpC,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,OAAO;AAAA,MACP,aAAa;AAAA,MACb,cAAc;AAAA,MACd,MAAM;AAAA,IACR,CAAC;AACD,QAAI,CAAC,GAAI;AACT,cAAU,OAAO,OAAO,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAChD;AACA,WAAS,KAAK,GAAW,KAAa;AACpC,UAAM,IAAI,IAAI;AACd,QAAI,IAAI,KAAK,KAAK,OAAO,OAAQ;AACjC,UAAM,OAAO,CAAC,GAAG,MAAM;AACvB,KAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AACtC,cAAU,IAAI;AAAA,EAChB;AAEA,iBAAe,YAAY;AACzB,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,OAAO;AAAA,MACP,aAAa,2CAA2C,MAAM,IAAI;AAAA,MAClE,cAAc;AAAA,IAChB,CAAC;AACD,QAAI,CAAC,GAAI;AACT,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,UAAU,QAAQ,MAAM,IAAI,EAAE,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,KAAK,MAAM,KAAK,QAAQ,MAAM,OAAO,CAAC;AAClI,YAAM,YAAY,QAAQ,MAAM,EAAE;AAClC,YAAM,EAAE,OAAO,gBAAgB,aAAa,MAAM,MAAM,MAAM,UAAU,CAAC;AAAA,IAC3E,SAAS,GAAG;AACV,YAAM,EAAE,OAAO,sBAAsB,aAAc,EAAY,SAAS,MAAM,QAAQ,CAAC;AAAA,IACzF,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,iBAAe,UAAU,WAAmB;AAC1C,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,OAAO;AAAA,MACP,aAAa;AAAA,MACb,cAAc;AAAA,IAChB,CAAC;AACD,QAAI,CAAC,GAAI;AACT,QAAI;AACF,YAAM,WAAW,MAAM,eAAe,QAAQ,MAAM,IAAI,SAAS;AACjE,eAAS,EAAE,GAAG,OAAO,GAAG,SAAS,CAAC;AAClC,kBAAY,UAAU;AACtB,YAAM,EAAE,OAAO,mBAAmB,aAAa,8CAA8C,MAAM,UAAU,CAAC;AAAA,IAChH,SAAS,GAAG;AACV,YAAM,EAAE,OAAO,sBAAsB,aAAc,EAAY,SAAS,MAAM,QAAQ,CAAC;AAAA,IACzF;AAAA,EACF;AAEA,QAAM,aAAa,QAAQ,MAAM,qBAAqB,SAAS,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;AAGpF,QAAM,iBAAiB,QAAQ,MAAM;AACnC,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,MAAgB,CAAC;AACvB,eAAW,KAAK,QAAQ;AACtB,UAAI,SAAS,IAAI,EAAE,KAAK,EAAG;AAC3B,YAAM,QAAQ,qBAAqB,MAAM,EAAE,KAAK;AAChD,UAAI,SAAS,CAAC,UAAU,IAAI,KAAK,KAAK,CAAC,KAAK,IAAI,EAAE,KAAK,GAAG;AACxD,aAAK,IAAI,EAAE,KAAK;AAChB,YAAI,KAAK,EAAE,KAAK;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,UAAU,MAAM,SAAS,CAAC;AAEtC,SACE,qBAAC,SAAI,WAAU,cACb;AAAA,yBAAC,QACC;AAAA,2BAAC,SAAI,WAAU,0DACb;AAAA,6BAAC,SAAI,WAAU,2BACb;AAAA,8BAAC,QAAG,WAAU,2BAA0B,iCAAmB;AAAA,UAC1D,MAAM,YAAY,oBAAC,SAAM,MAAK,SAAQ,0BAAY,IAAW,oBAAC,SAAM,MAAK,SAAQ,qBAAO;AAAA,WAC3F;AAAA,QACA,qBAAC,SAAI,WAAU,2BACb;AAAA,8BAAC,UAAK,WAAU,0BAAyB,aAAU,UAChD,wBAAc,WAAW,kBAAa,cAAc,UAAU,sBAAiB,IAClF;AAAA,UACC,cAAc,WACb,oBAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,KAAK,QAAQ,KAAK,GAAG,6BAE1E;AAAA,UAEF,oBAAC,UAAO,SAAS,WAAW,UAAU,MACnC,iBAAO,gBAAW,cACrB;AAAA,WACF;AAAA,SACF;AAAA,MACC,cAAc,WACb,oBAAC,SAAI,WAAU,4EAA2E,kHAE1F;AAAA,MAEF,qBAAC,SAAI,WAAU,6BACb;AAAA,4BAAC,SAAM,OAAM,SACX,8BAAC,SAAM,OAAO,MAAM,OAAO,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK,GAAG,GACxE;AAAA,QACA,oBAAC,SAAM,OAAM,OAAM,MAAK,sBACtB,8BAAC,SAAM,OAAO,MAAM,MAAM,UAAU,CAAC,MAAM,OAAO,EAAE,GAAG,OAAO,MAAM,EAAE,OAAO,MAAM,CAAC,GAAG,GACzF;AAAA,QACA,oBAAC,SAAM,OAAM,aACX,8BAAC,SAAM,OAAO,MAAM,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,OAAO,EAAE,GAAG,OAAO,KAAK,EAAE,GAAG,MAAM,KAAK,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC,GAAG,GAC7H;AAAA,QACA,oBAAC,SAAM,OAAM,oBACX,8BAAC,YAAS,MAAM,GAAG,OAAO,MAAM,KAAK,eAAe,IAAI,UAAU,CAAC,MAAM,OAAO,EAAE,GAAG,OAAO,KAAK,EAAE,GAAG,MAAM,KAAK,aAAa,EAAE,OAAO,MAAM,EAAE,CAAC,GAAG,GACrJ;AAAA,SACF;AAAA,OACF;AAAA,IAEC,eAAe,SAAS,KACvB,qBAAC,SAAI,WAAU,6EAA4E;AAAA;AAAA,MAClC,eAAe,KAAK,IAAI;AAAA,MAAE;AAAA,MACjF,oBAAC,YAAO,mBAAK;AAAA,MAAS;AAAA,OAExB;AAAA,IAGF,qBAAC,SAAI,WAAU,cACZ;AAAA,aAAO,IAAI,CAAC,GAAG,MAAM;AACpB,cAAM,MAAM,SAAS,IAAI,EAAE,KAAK;AAChC,cAAM,SACH,KAAK,UAA0D,oBAAoB,EAAE,KAAK,KAAK;AAClG,eACE,qBAAC,QACC;AAAA,+BAAC,SAAI,WAAU,0CACb;AAAA,gCAAC,UAAK,WAAU,yBAAyB,eAAK,SAAS,EAAE,OAAM;AAAA,YAC/D,qBAAC,SAAI,WAAU,2BACb;AAAA,kCAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,GAAG,EAAE,GAAG,cAAW,UAAS,oBAAC;AAAA,cACnF,oBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,GAAG,CAAC,GAAG,cAAW,UAAS,oBAAC;AAAA,cAClF,oBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,YAAY,CAAC,GAAG,cAAW,eAAc,uBAAE;AAAA,eACnG;AAAA,aACF;AAAA,UACA,oBAAC,UAAO,OAAO,GAAG,QAAgB,UAAU,CAAC,SAAS,YAAY,GAAG,IAAI,GAAG;AAAA,UAC5E,oBAAC,SAAI,WAAU,QACb;AAAA,YAAC;AAAA;AAAA,cACC,OAAQ,EAAsC;AAAA,cAC9C,UAAU,CAAC,UAAU,YAAY,GAAG,EAAE,GAAG,GAAG,QAAQ,MAAM,CAAC;AAAA;AAAA,UAC7D,GACF;AAAA,aAfS,EAAE,IAgBb;AAAA,MAEJ,CAAC;AAAA,MACA,OAAO,WAAW,KAAK,oBAAC,OAAE,WAAU,0BAAyB,0DAA4C;AAAA,OAC5G;AAAA,IAEA,qBAAC,SAAI,WAAU,qCACb;AAAA,0BAAC,UAAK,WAAU,0BAAyB,wBAAU;AAAA,MACnD,oBAAC,SAAI,WAAU,QACb,8BAAC,UAAO,OAAM,IAAG,aAAY,uBAAiB,eAAe,CAAC,MAAM,KAAK,SAAS,CAAC,GAAG,SAAS,YAAY,GAC7G;AAAA,MACA,oBAAC,mBAAgB,UAAU,WAAW;AAAA,OACxC;AAAA,IAEC,SAAS,SAAS,KACjB,qBAAC,QACC;AAAA,0BAAC,QAAG,WAAU,8BAA6B,gCAAkB;AAAA,MAC7D,oBAAC,QAAG,WAAU,qCACX,mBAAS,MAAM,GAAG,EAAE,EAAE,IAAI,CAAC,MAC1B,qBAAC,QAAc,WAAU,kFACvB;AAAA,6BAAC,UACE;AAAA,YAAE,SAAS,EAAE,QAAQ;AAAA,UACrB,kBAAkB,EAAE,SAAS,KAAK,qBAAC,UAAK,WAAU,kBAAiB;AAAA;AAAA,YAAI,kBAAkB,EAAE,SAAS;AAAA,aAAE;AAAA,WACzG;AAAA,QACA,oBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,UAAU,EAAE,EAAE,GAAG,wBAEvE;AAAA,WAPO,EAAE,EAQX,CACD,GACH;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Button, Card, Field, Input, Badge } from "../ui/primitives";
|
|
5
|
+
import { Dialog, DialogTrigger, DialogContent, DialogClose } from "../ui/dialog";
|
|
6
|
+
import { useToast } from "../ui/toast";
|
|
7
|
+
import { useConfirm } from "../ui/ConfirmDialog";
|
|
8
|
+
import { subscribePages, createPage, deletePage } from "../lib/pagesClient";
|
|
9
|
+
import { PageEditor } from "./PageEditor";
|
|
10
|
+
function slugify(s) {
|
|
11
|
+
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
12
|
+
}
|
|
13
|
+
function PagesPanel({ siteId }) {
|
|
14
|
+
const { toast } = useToast();
|
|
15
|
+
const confirm = useConfirm();
|
|
16
|
+
const [pages, setPages] = useState([]);
|
|
17
|
+
const [selected, setSelected] = useState(null);
|
|
18
|
+
const [newTitle, setNewTitle] = useState("");
|
|
19
|
+
useEffect(() => subscribePages(siteId, setPages), [siteId]);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!selected && pages.length) setSelected(pages[0].id);
|
|
22
|
+
}, [pages, selected]);
|
|
23
|
+
const current = pages.find((p) => p.id === selected) ?? null;
|
|
24
|
+
async function create() {
|
|
25
|
+
const title = newTitle.trim();
|
|
26
|
+
if (!title) return;
|
|
27
|
+
const slug = slugify(title) || "pagina";
|
|
28
|
+
const path = slug === "home" ? "/" : `/${slug}`;
|
|
29
|
+
const id = await createPage(siteId, { title, slug, path });
|
|
30
|
+
setNewTitle("");
|
|
31
|
+
setSelected(id);
|
|
32
|
+
toast({ title: "Pagina aangemaakt", description: path });
|
|
33
|
+
}
|
|
34
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[260px_1fr]", children: [
|
|
35
|
+
/* @__PURE__ */ jsxs(Card, { className: "h-fit", children: [
|
|
36
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
37
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Pagina's" }),
|
|
38
|
+
/* @__PURE__ */ jsxs(Dialog, { children: [
|
|
39
|
+
/* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "sm", children: "+ Nieuw" }) }),
|
|
40
|
+
/* @__PURE__ */ jsx(DialogContent, { title: "Nieuwe pagina", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
41
|
+
/* @__PURE__ */ jsx(Field, { label: "Titel", children: /* @__PURE__ */ jsx(Input, { value: newTitle, onChange: (e) => setNewTitle(e.target.value), placeholder: "bv. Over ons" }) }),
|
|
42
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
43
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "secondary", children: "Annuleren" }) }),
|
|
44
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { onClick: create, children: "Aanmaken" }) })
|
|
45
|
+
] })
|
|
46
|
+
] }) })
|
|
47
|
+
] })
|
|
48
|
+
] }),
|
|
49
|
+
/* @__PURE__ */ jsxs("ul", { className: "grid gap-1", children: [
|
|
50
|
+
pages.map((p) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
51
|
+
"button",
|
|
52
|
+
{
|
|
53
|
+
onClick: () => setSelected(p.id),
|
|
54
|
+
className: `flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm ${p.id === selected ? "bg-brand-50 text-brand-700" : "hover:bg-slate-100"}`,
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: p.title }),
|
|
57
|
+
p.published ? /* @__PURE__ */ jsx(Badge, { tone: "green", children: "live" }) : /* @__PURE__ */ jsx(Badge, { tone: "amber", children: "concept" })
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
) }, p.id)),
|
|
61
|
+
pages.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-slate-500", children: "Nog geen pagina's." })
|
|
62
|
+
] })
|
|
63
|
+
] }),
|
|
64
|
+
/* @__PURE__ */ jsx("div", { children: current ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
65
|
+
/* @__PURE__ */ jsx("div", { className: "mb-3 flex justify-end", children: /* @__PURE__ */ jsx(
|
|
66
|
+
Button,
|
|
67
|
+
{
|
|
68
|
+
variant: "danger",
|
|
69
|
+
size: "sm",
|
|
70
|
+
onClick: async () => {
|
|
71
|
+
const ok = await confirm({
|
|
72
|
+
title: `Pagina "${current.title}" verwijderen?`,
|
|
73
|
+
description: "De pagina en de bijbehorende werkversie worden verwijderd. Als de pagina gepubliceerd is, verdwijnt hij ook van de live site. Dit kan niet ongedaan worden gemaakt.",
|
|
74
|
+
confirmLabel: "Verwijderen",
|
|
75
|
+
tone: "danger"
|
|
76
|
+
});
|
|
77
|
+
if (!ok) return;
|
|
78
|
+
await deletePage(siteId, current.id);
|
|
79
|
+
setSelected(null);
|
|
80
|
+
toast({ title: "Pagina verwijderd" });
|
|
81
|
+
},
|
|
82
|
+
children: "Verwijderen"
|
|
83
|
+
}
|
|
84
|
+
) }),
|
|
85
|
+
/* @__PURE__ */ jsx(PageEditor, { siteId, page: current })
|
|
86
|
+
] }) : /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Selecteer of maak een pagina om te bewerken." }) }) })
|
|
87
|
+
] });
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
PagesPanel
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=PagesPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/pages/PagesPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Button, Card, Field, Input, Badge } from \"../ui/primitives\";\nimport { Dialog, DialogTrigger, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { subscribePages, createPage, deletePage, type DraftPage } from \"../lib/pagesClient\";\nimport { PageEditor } from \"./PageEditor\";\n\nfunction 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\nexport function PagesPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [pages, setPages] = useState<DraftPage[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [newTitle, setNewTitle] = useState(\"\");\n\n useEffect(() => subscribePages(siteId, setPages), [siteId]);\n useEffect(() => {\n if (!selected && pages.length) setSelected(pages[0].id);\n }, [pages, selected]);\n\n const current = pages.find((p) => p.id === selected) ?? null;\n\n async function create() {\n const title = newTitle.trim();\n if (!title) return;\n const slug = slugify(title) || \"pagina\";\n const path = slug === \"home\" ? \"/\" : `/${slug}`;\n const id = await createPage(siteId, { title, slug, path });\n setNewTitle(\"\");\n setSelected(id);\n toast({ title: \"Pagina aangemaakt\", description: path });\n }\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[260px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Pagina's</h3>\n <Dialog>\n <DialogTrigger asChild>\n <Button size=\"sm\">+ Nieuw</Button>\n </DialogTrigger>\n <DialogContent title=\"Nieuwe pagina\">\n <div className=\"grid gap-3\">\n <Field label=\"Titel\">\n <Input value={newTitle} onChange={(e) => setNewTitle(e.target.value)} placeholder=\"bv. Over ons\" />\n </Field>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <DialogClose asChild>\n <Button onClick={create}>Aanmaken</Button>\n </DialogClose>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n <ul className=\"grid gap-1\">\n {pages.map((p) => (\n <li key={p.id}>\n <button\n onClick={() => setSelected(p.id)}\n className={`flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm ${\n p.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"truncate\">{p.title}</span>\n {p.published ? <Badge tone=\"green\">live</Badge> : <Badge tone=\"amber\">concept</Badge>}\n </button>\n </li>\n ))}\n {pages.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Nog geen pagina's.</li>}\n </ul>\n </Card>\n\n <div>\n {current ? (\n <>\n <div className=\"mb-3 flex justify-end\">\n <Button\n variant=\"danger\"\n size=\"sm\"\n onClick={async () => {\n const ok = await confirm({\n title: `Pagina \"${current.title}\" verwijderen?`,\n description:\n \"De pagina en de bijbehorende werkversie worden verwijderd. Als de pagina gepubliceerd is, verdwijnt hij ook van de live site. Dit kan niet ongedaan worden gemaakt.\",\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n await deletePage(siteId, current.id);\n setSelected(null);\n toast({ title: \"Pagina verwijderd\" });\n }}\n >\n Verwijderen\n </Button>\n </div>\n <PageEditor siteId={siteId} page={current} />\n </>\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer of maak een pagina om te bewerken.</p>\n </Card>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paneel-registry — koppelt een beheer-paneel aan een module-id. `AdminApp` rendert de tabs
|
|
3
|
+
* hieruit (gesorteerd op `order`, gated op rol én actieve-module-set) i.p.v. hardgecodeerd. Een
|
|
4
|
+
* nieuwe module met een eigen paneel hoeft `AdminApp` dus niet meer aan te raken.
|
|
5
|
+
*/
|
|
6
|
+
import type { ComponentType } from "react";
|
|
7
|
+
export type PanelMinRole = "editor" | "siteadmin";
|
|
8
|
+
export interface PanelDefinition {
|
|
9
|
+
/** Moet overeenkomen met de module-`id`; bepaalt of het paneel actief is. */
|
|
10
|
+
moduleId: string;
|
|
11
|
+
label: string;
|
|
12
|
+
/** Sorteervolgorde (laag = links). Instellingen staat vast achteraan (100). */
|
|
13
|
+
order: number;
|
|
14
|
+
minRole: PanelMinRole;
|
|
15
|
+
Panel: ComponentType<{
|
|
16
|
+
siteId: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/** De panelen van de ingebouwde modules. */
|
|
20
|
+
export declare const builtinPanels: PanelDefinition[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { PagesPanel } from "./pages/PagesPanel";
|
|
4
|
+
import { MediaManager } from "./media/MediaManager";
|
|
5
|
+
import { FormsPanel } from "./forms/FormsPanel";
|
|
6
|
+
import { StatsPanel } from "./stats/StatsPanel";
|
|
7
|
+
import { SettingsPanel } from "./settings/SettingsPanel";
|
|
8
|
+
import { NewsEditor } from "./NewsEditor";
|
|
9
|
+
function NewsPanel(_) {
|
|
10
|
+
return /* @__PURE__ */ jsx(NewsEditor, {});
|
|
11
|
+
}
|
|
12
|
+
const builtinPanels = [
|
|
13
|
+
{ moduleId: "pages", label: "Pagina's", order: 10, minRole: "editor", Panel: PagesPanel },
|
|
14
|
+
{ moduleId: "news", label: "Nieuws", order: 20, minRole: "editor", Panel: NewsPanel },
|
|
15
|
+
{ moduleId: "media", label: "Media", order: 30, minRole: "editor", Panel: MediaManager },
|
|
16
|
+
{ moduleId: "forms", label: "Formulieren", order: 40, minRole: "editor", Panel: FormsPanel },
|
|
17
|
+
{ moduleId: "stats", label: "Statistieken", order: 50, minRole: "editor", Panel: StatsPanel },
|
|
18
|
+
{ moduleId: "settings", label: "Instellingen", order: 100, minRole: "siteadmin", Panel: SettingsPanel }
|
|
19
|
+
];
|
|
20
|
+
export {
|
|
21
|
+
builtinPanels
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=panelRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/admin/panelRegistry.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Paneel-registry — koppelt een beheer-paneel aan een module-id. `AdminApp` rendert de tabs\n * hieruit (gesorteerd op `order`, gated op rol én actieve-module-set) i.p.v. hardgecodeerd. Een\n * nieuwe module met een eigen paneel hoeft `AdminApp` dus niet meer aan te raken.\n */\nimport type { ComponentType } from \"react\";\nimport { PagesPanel } from \"./pages/PagesPanel\";\nimport { MediaManager } from \"./media/MediaManager\";\nimport { FormsPanel } from \"./forms/FormsPanel\";\nimport { StatsPanel } from \"./stats/StatsPanel\";\nimport { SettingsPanel } from \"./settings/SettingsPanel\";\nimport { NewsEditor } from \"./NewsEditor\";\n\nexport type PanelMinRole = \"editor\" | \"siteadmin\";\n\nexport interface PanelDefinition {\n /** Moet overeenkomen met de module-`id`; bepaalt of het paneel actief is. */\n moduleId: string;\n label: string;\n /** Sorteervolgorde (laag = links). Instellingen staat vast achteraan (100). */\n order: number;\n minRole: PanelMinRole;\n Panel: ComponentType<{ siteId: string }>;\n}\n\n/** Adapter: de NewsEditor leest zelf siteId via context; wrap voor een uniforme prop-signatuur. */\nfunction NewsPanel(_: { siteId: string }) {\n return <NewsEditor />;\n}\n\n/** De panelen van de ingebouwde modules. */\nexport const builtinPanels: PanelDefinition[] = [\n { moduleId: \"pages\", label: \"Pagina's\", order: 10, minRole: \"editor\", Panel: PagesPanel },\n { moduleId: \"news\", label: \"Nieuws\", order: 20, minRole: \"editor\", Panel: NewsPanel },\n { moduleId: \"media\", label: \"Media\", order: 30, minRole: \"editor\", Panel: MediaManager },\n { moduleId: \"forms\", label: \"Formulieren\", order: 40, minRole: \"editor\", Panel: FormsPanel },\n { moduleId: \"stats\", label: \"Statistieken\", order: 50, minRole: \"editor\", Panel: StatsPanel },\n { moduleId: \"settings\", label: \"Instellingen\", order: 100, minRole: \"siteadmin\", Panel: SettingsPanel },\n];\n"],"mappings":";AA6BS;AArBT,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAe3B,SAAS,UAAU,GAAuB;AACxC,SAAO,oBAAC,cAAW;AACrB;AAGO,MAAM,gBAAmC;AAAA,EAC9C,EAAE,UAAU,SAAS,OAAO,YAAY,OAAO,IAAI,SAAS,UAAU,OAAO,WAAW;AAAA,EACxF,EAAE,UAAU,QAAQ,OAAO,UAAU,OAAO,IAAI,SAAS,UAAU,OAAO,UAAU;AAAA,EACpF,EAAE,UAAU,SAAS,OAAO,SAAS,OAAO,IAAI,SAAS,UAAU,OAAO,aAAa;AAAA,EACvF,EAAE,UAAU,SAAS,OAAO,eAAe,OAAO,IAAI,SAAS,UAAU,OAAO,WAAW;AAAA,EAC3F,EAAE,UAAU,SAAS,OAAO,gBAAgB,OAAO,IAAI,SAAS,UAAU,OAAO,WAAW;AAAA,EAC5F,EAAE,UAAU,YAAY,OAAO,gBAAgB,OAAO,KAAK,SAAS,aAAa,OAAO,cAAc;AACxG;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Pattern } from "../../patterns";
|
|
2
|
+
/**
|
|
3
|
+
* "Sectie toevoegen" — kiest een kant-en-klaar patroon en voegt de blocks (met verse keys) in.
|
|
4
|
+
* Hergebruikt in de visuele editor én het /beheer-paginapaneel.
|
|
5
|
+
*/
|
|
6
|
+
export declare function PatternInserter({ patterns, onInsert, label, }: {
|
|
7
|
+
patterns?: Pattern[];
|
|
8
|
+
onInsert: (blocks: Array<Record<string, unknown>>) => void;
|
|
9
|
+
label?: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import { Dialog, DialogContent, DialogClose } from "../ui/dialog";
|
|
5
|
+
import { Button } from "../ui/primitives";
|
|
6
|
+
import { useRegistry } from "../../blocks/context";
|
|
7
|
+
import { builtinPatterns, instantiatePattern, collectPatternTypes } from "../../patterns";
|
|
8
|
+
function PatternInserter({
|
|
9
|
+
patterns = builtinPatterns,
|
|
10
|
+
onInsert,
|
|
11
|
+
label = "+ Sectie toevoegen"
|
|
12
|
+
}) {
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const registry = useRegistry();
|
|
15
|
+
const byCategory = useMemo(() => {
|
|
16
|
+
const available = patterns.filter((p) => collectPatternTypes(p).every((t) => registry.get(t)));
|
|
17
|
+
const map = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const p of available) {
|
|
19
|
+
const arr = map.get(p.category) ?? [];
|
|
20
|
+
arr.push(p);
|
|
21
|
+
map.set(p.category, arr);
|
|
22
|
+
}
|
|
23
|
+
return [...map.entries()];
|
|
24
|
+
}, [patterns, registry]);
|
|
25
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
26
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOpen(true), children: label }),
|
|
27
|
+
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Sectie toevoegen", className: "max-w-xl", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-sm text-slate-500", children: "Kies een kant-en-klare sectie; je past de inhoud daarna aan." }),
|
|
29
|
+
/* @__PURE__ */ jsx("div", { className: "grid max-h-[60vh] gap-4 overflow-auto", children: byCategory.map(([cat, items]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
30
|
+
/* @__PURE__ */ jsx("h4", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-slate-400", children: cat }),
|
|
31
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-2", children: items.map((p) => /* @__PURE__ */ jsxs(
|
|
32
|
+
"button",
|
|
33
|
+
{
|
|
34
|
+
type: "button",
|
|
35
|
+
className: "rounded-lg border border-slate-200 p-3 text-left text-sm hover:border-brand-500 hover:bg-slate-50",
|
|
36
|
+
onClick: () => {
|
|
37
|
+
onInsert(instantiatePattern(p));
|
|
38
|
+
setOpen(false);
|
|
39
|
+
},
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-2", children: [
|
|
42
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: p.label }),
|
|
43
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-base opacity-70", "aria-hidden": true, children: p.blocks.slice(0, 6).map((b, idx) => /* @__PURE__ */ jsx("span", { children: registry.get(b._type)?.icon ?? "\u{1F9E9}" }, idx)) })
|
|
44
|
+
] }),
|
|
45
|
+
p.description && /* @__PURE__ */ jsx("span", { className: "block text-xs text-slate-500", children: p.description })
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
p.id
|
|
49
|
+
)) })
|
|
50
|
+
] }, cat)) }),
|
|
51
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-end", children: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", children: "Sluiten" }) }) })
|
|
52
|
+
] }) })
|
|
53
|
+
] });
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
PatternInserter
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=PatternInserter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/patterns/PatternInserter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { Dialog, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { Button } from \"../ui/primitives\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { builtinPatterns, instantiatePattern, collectPatternTypes, type Pattern } from \"../../patterns\";\n\n/**\n * \"Sectie toevoegen\" — kiest een kant-en-klaar patroon en voegt de blocks (met verse keys) in.\n * Hergebruikt in de visuele editor én het /beheer-paginapaneel.\n */\nexport function PatternInserter({\n patterns = builtinPatterns,\n onInsert,\n label = \"+ Sectie toevoegen\",\n}: {\n patterns?: Pattern[];\n onInsert: (blocks: Array<Record<string, unknown>>) => void;\n label?: string;\n}) {\n const [open, setOpen] = useState(false);\n const registry = useRegistry();\n const byCategory = useMemo(() => {\n // Verberg patronen waarvan een benodigd blok niet in de registry zit (module uitgeschakeld):\n // invoegen zou anders \"Onbekend blok\"-gaten geven.\n const available = patterns.filter((p) => collectPatternTypes(p).every((t) => registry.get(t)));\n const map = new Map<string, Pattern[]>();\n for (const p of available) {\n const arr = map.get(p.category) ?? [];\n arr.push(p);\n map.set(p.category, arr);\n }\n return [...map.entries()];\n }, [patterns, registry]);\n\n return (\n <>\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => setOpen(true)}>\n {label}\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent title=\"Sectie toevoegen\" className=\"max-w-xl\">\n <p className=\"mb-3 text-sm text-slate-500\">Kies een kant-en-klare sectie; je past de inhoud daarna aan.</p>\n <div className=\"grid max-h-[60vh] gap-4 overflow-auto\">\n {byCategory.map(([cat, items]) => (\n <div key={cat}>\n <h4 className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-slate-400\">{cat}</h4>\n <div className=\"grid gap-2\">\n {items.map((p) => (\n <button\n key={p.id}\n type=\"button\"\n className=\"rounded-lg border border-slate-200 p-3 text-left text-sm hover:border-brand-500 hover:bg-slate-50\"\n onClick={() => {\n onInsert(instantiatePattern(p));\n setOpen(false);\n }}\n >\n <span className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium\">{p.label}</span>\n {/* Mini-preview: de iconen van de blokken die dit patroon invoegt. */}\n <span className=\"shrink-0 text-base opacity-70\" aria-hidden>\n {p.blocks.slice(0, 6).map((b, idx) => (\n <span key={idx}>{registry.get(b._type)?.icon ?? \"🧩\"}</span>\n ))}\n </span>\n </span>\n {p.description && <span className=\"block text-xs text-slate-500\">{p.description}</span>}\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n <div className=\"mt-4 flex justify-end\">\n <DialogClose asChild>\n <Button variant=\"secondary\" size=\"sm\">Sluiten</Button>\n </DialogClose>\n </div>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n"],"mappings":";AAqCI,mBACE,KAqBgB,YAtBlB;AAnCJ,SAAS,SAAS,gBAAgB;AAClC,SAAS,QAAQ,eAAe,mBAAmB;AACnD,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB,oBAAoB,2BAAyC;AAMhF,SAAS,gBAAgB;AAAA,EAC9B,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AACV,GAIG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,WAAW,YAAY;AAC7B,QAAM,aAAa,QAAQ,MAAM;AAG/B,UAAM,YAAY,SAAS,OAAO,CAAC,MAAM,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC;AAC7F,UAAM,MAAM,oBAAI,IAAuB;AACvC,eAAW,KAAK,WAAW;AACzB,YAAM,MAAM,IAAI,IAAI,EAAE,QAAQ,KAAK,CAAC;AACpC,UAAI,KAAK,CAAC;AACV,UAAI,IAAI,EAAE,UAAU,GAAG;AAAA,IACzB;AACA,WAAO,CAAC,GAAG,IAAI,QAAQ,CAAC;AAAA,EAC1B,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,SACE,iCACE;AAAA,wBAAC,UAAO,MAAK,UAAS,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,QAAQ,IAAI,GAC5E,iBACH;AAAA,IACA,oBAAC,UAAO,MAAY,cAAc,SAChC,+BAAC,iBAAc,OAAM,oBAAmB,WAAU,YAChD;AAAA,0BAAC,OAAE,WAAU,+BAA8B,0EAA4D;AAAA,MACvG,oBAAC,SAAI,WAAU,yCACZ,qBAAW,IAAI,CAAC,CAAC,KAAK,KAAK,MAC1B,qBAAC,SACC;AAAA,4BAAC,QAAG,WAAU,qEAAqE,eAAI;AAAA,QACvF,oBAAC,SAAI,WAAU,cACZ,gBAAM,IAAI,CAAC,MACV;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,MAAM;AACb,uBAAS,mBAAmB,CAAC,CAAC;AAC9B,sBAAQ,KAAK;AAAA,YACf;AAAA,YAEA;AAAA,mCAAC,UAAK,WAAU,2CACd;AAAA,oCAAC,UAAK,WAAU,eAAe,YAAE,OAAM;AAAA,gBAEvC,oBAAC,UAAK,WAAU,iCAAgC,eAAW,MACxD,YAAE,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,QAC5B,oBAAC,UAAgB,mBAAS,IAAI,EAAE,KAAK,GAAG,QAAQ,eAArC,GAA0C,CACtD,GACH;AAAA,iBACF;AAAA,cACC,EAAE,eAAe,oBAAC,UAAK,WAAU,gCAAgC,YAAE,aAAY;AAAA;AAAA;AAAA,UAjB3E,EAAE;AAAA,QAkBT,CACD,GACH;AAAA,WAzBQ,GA0BV,CACD,GACH;AAAA,MACA,oBAAC,SAAI,WAAU,yBACb,8BAAC,eAAY,SAAO,MAClB,8BAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,qBAAO,GAC/C,GACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
|