@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,58 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type BlockDefinition } from "./blocks/registry";
|
|
3
|
+
import { type BlockOverrides } from "./modules/overrides";
|
|
4
|
+
import type { ModuleDefinition } from "./modules/types";
|
|
5
|
+
import { type DefinedTheme } from "@astryxdesign/core/theme";
|
|
6
|
+
import { type LinkComponentType } from "@astryxdesign/core/Link";
|
|
7
|
+
import { type ImageComponentType } from "./media/ImageContext";
|
|
8
|
+
import { type SiteConfig } from "./schema/siteConfig";
|
|
9
|
+
interface ProviderProps {
|
|
10
|
+
config: SiteConfig;
|
|
11
|
+
/** Losse klant-blokken (via `defineBlock`) buiten een module om. Altijd actief. */
|
|
12
|
+
blocks?: BlockDefinition[];
|
|
13
|
+
/**
|
|
14
|
+
* Klant-modules (via `defineModule`) bovenop de ingebouwde. Worden gemerged op `id` (klant wint)
|
|
15
|
+
* en respecteren `config.modules`/`config.features` voor activatie.
|
|
16
|
+
*/
|
|
17
|
+
modules?: ModuleDefinition[];
|
|
18
|
+
/**
|
|
19
|
+
* Per-site presentatie-overrides per block-type (`{ gallery: { Render: MijnGalerij } }`). Vervangt
|
|
20
|
+
* alleen het uiterlijk; schema/editor/logica blijven van core. De krachtigste maatwerk-naad.
|
|
21
|
+
*/
|
|
22
|
+
overrides?: BlockOverrides;
|
|
23
|
+
/**
|
|
24
|
+
* Astryx-basistheme voor de hele site (radius-/spacing-/neutralen-karakter). Standaard `neutral`.
|
|
25
|
+
* Een site kan een ander pakket kiezen (`@astryxdesign/theme-butter`, `-stone`, …) of een eigen
|
|
26
|
+
* `defineTheme(...)` doorgeven voor component-overrides — zónder core te forken.
|
|
27
|
+
*/
|
|
28
|
+
astryxTheme?: DefinedTheme;
|
|
29
|
+
/**
|
|
30
|
+
* Framework-link-component (bv. Next `Link`) voor soepele client-navigatie + prefetch. Astryx-
|
|
31
|
+
* componenten met een `href` (knoppen/links/tabs) gebruiken 'm dan i.p.v. een rauwe `<a>` (= volledige
|
|
32
|
+
* page-reload). Core importeert `next/link` niet zelf (optionele peer) — geef 'm hier door vanuit de site.
|
|
33
|
+
*/
|
|
34
|
+
linkComponent?: LinkComponentType;
|
|
35
|
+
/**
|
|
36
|
+
* Framework-beeldcomponent (bv. Next `Image`) voor automatische beeldoptimalisatie (responsive
|
|
37
|
+
* srcset, AVIF/WebP, geen layout-shift). Core importeert `next/image` niet zelf — geef 'm hier door.
|
|
38
|
+
*/
|
|
39
|
+
imageComponent?: ImageComponentType;
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Wikkel de hele site in deze provider (in `app/layout.tsx`). Hij zet:
|
|
44
|
+
* - de SiteConfig-context (siteId, theme, mode) + live publieke settings (footer/nav/modules);
|
|
45
|
+
* - auth + content (Firebase, env-gedreven; werkt ook zonder Firebase);
|
|
46
|
+
* - de block-registry (ingebouwde + klant-blokken) voor de page-renderer;
|
|
47
|
+
* - de themavariabelen als CSS custom properties;
|
|
48
|
+
* - App Check (alleen als geconfigureerd) en de EditBar/DemoLock.
|
|
49
|
+
*/
|
|
50
|
+
export declare function XiCMSProvider({ config, blocks, modules, overrides, astryxTheme, linkComponent, imageComponent, children }: ProviderProps): import("react").JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* Is deze (hex-)kleur donker? Bepaalt de Astryx-thememodus voor sites met een vaste donkere
|
|
53
|
+
* achtergrond (bv. presets donker-modern/luxe-donker, `dark.auto: false`): zonder deze afleiding
|
|
54
|
+
* bleef Astryx op "light" staan en tekenden zijn componenten lichte kaarten op een donkere pagina.
|
|
55
|
+
* Puur en exporteerbaar voor tests; niet-hex waarden gelden als licht (veilige default).
|
|
56
|
+
*/
|
|
57
|
+
export declare function isDarkColor(color?: string): boolean;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo } from "react";
|
|
4
|
+
import { ConfigProvider } from "./config";
|
|
5
|
+
import { AuthProvider } from "./auth/AuthProvider";
|
|
6
|
+
import { ContentProvider } from "./content/ContentProvider";
|
|
7
|
+
import { EditBar } from "./components/EditBar";
|
|
8
|
+
import { DemoLock } from "./lock/DemoLock";
|
|
9
|
+
import { RegistryProvider } from "./blocks/context";
|
|
10
|
+
import { createRegistry, mergeRegistry } from "./blocks/registry";
|
|
11
|
+
import { resolveModules } from "./modules/registry";
|
|
12
|
+
import { ModulesProvider } from "./modules/context";
|
|
13
|
+
import { applyBlockOverrides } from "./modules/overrides";
|
|
14
|
+
import { builtinModules } from "./modules/builtins";
|
|
15
|
+
import { ConsentBanner } from "./modules/consent/ConsentBanner";
|
|
16
|
+
import { EditorProvider } from "./editor/EditorProvider";
|
|
17
|
+
import { SiteSettingsProvider, useSiteSettings } from "./settings/useSiteSettings";
|
|
18
|
+
import { mergeThemeTokens } from "./schema/settings";
|
|
19
|
+
import { initAppCheck } from "./firebase/appcheck";
|
|
20
|
+
import { DemoStudio } from "./demo/DemoStudio";
|
|
21
|
+
import { setDemoActive } from "./demo/demoStore";
|
|
22
|
+
import { Theme } from "@astryxdesign/core/theme";
|
|
23
|
+
import { neutralTheme } from "@astryxdesign/theme-neutral/built";
|
|
24
|
+
import { LinkProvider } from "@astryxdesign/core/Link";
|
|
25
|
+
import { ImageProvider } from "./media/ImageContext";
|
|
26
|
+
import { validateSiteConfig } from "./schema/siteConfig";
|
|
27
|
+
import { validateModuleContract } from "./modules/contract";
|
|
28
|
+
const NO_BLOCKS = [];
|
|
29
|
+
const NO_MODULES = [];
|
|
30
|
+
const warned = /* @__PURE__ */ new Set();
|
|
31
|
+
function warnOnce(message) {
|
|
32
|
+
if (warned.has(message)) return;
|
|
33
|
+
warned.add(message);
|
|
34
|
+
console.warn(message);
|
|
35
|
+
}
|
|
36
|
+
function XiCMSProvider({ config, blocks = NO_BLOCKS, modules = NO_MODULES, overrides, astryxTheme, linkComponent, imageComponent, children }) {
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
initAppCheck();
|
|
39
|
+
}, []);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (process.env.NODE_ENV === "production") return;
|
|
42
|
+
const configIssues = validateSiteConfig(config);
|
|
43
|
+
if (configIssues.length > 0) {
|
|
44
|
+
warnOnce(`[xicms] site.config.ts bevat problemen:
|
|
45
|
+
- ${configIssues.join("\n- ")}`);
|
|
46
|
+
}
|
|
47
|
+
if (modules.length > 0) {
|
|
48
|
+
const contractIssues = validateModuleContract(modules);
|
|
49
|
+
if (contractIssues.length > 0) {
|
|
50
|
+
warnOnce(`[xicms] klant-modules voldoen niet aan het module-contract:
|
|
51
|
+
- ${contractIssues.join("\n- ")}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}, [config, modules]);
|
|
55
|
+
return /* @__PURE__ */ jsx(ConfigProvider, { config, children: /* @__PURE__ */ jsx(SiteSettingsProvider, { siteId: config.siteId, children: /* @__PURE__ */ jsx(ModuleLayer, { config, blocks, modules, overrides, astryxTheme, linkComponent, imageComponent, children }) }) });
|
|
56
|
+
}
|
|
57
|
+
function themeCssVars(theme) {
|
|
58
|
+
const auto = Boolean(theme.dark?.auto);
|
|
59
|
+
const ld = (light, dark) => auto ? `light-dark(${light}, ${dark})` : light;
|
|
60
|
+
const cssVars = {
|
|
61
|
+
"--xicms-primary": theme.dark?.primary ? ld(theme.primary, theme.dark.primary) : theme.primary,
|
|
62
|
+
"--xicms-accent": theme.accent ?? theme.primary,
|
|
63
|
+
"--xicms-bg": ld(theme.background ?? "#ffffff", theme.dark?.background ?? "#0f172a"),
|
|
64
|
+
"--xicms-fg": ld(theme.foreground ?? "#0f172a", theme.dark?.foreground ?? "#e2e8f0")
|
|
65
|
+
};
|
|
66
|
+
if (theme.fontFamily) cssVars["--xicms-font"] = theme.fontFamily;
|
|
67
|
+
for (const [k, v] of Object.entries(theme.spacing ?? {})) if (v) cssVars[`--xicms-spacing-${k}`] = v;
|
|
68
|
+
if (theme.radius?.sm) cssVars["--xicms-radius-sm"] = theme.radius.sm;
|
|
69
|
+
if (theme.radius?.md) cssVars["--xicms-radius"] = theme.radius.md;
|
|
70
|
+
if (theme.radius?.lg) cssVars["--xicms-radius-lg"] = theme.radius.lg;
|
|
71
|
+
if (theme.container?.maxWidth) cssVars["--xicms-container-max"] = theme.container.maxWidth;
|
|
72
|
+
if (theme.container?.padding) cssVars["--xicms-container-pad"] = theme.container.padding;
|
|
73
|
+
if (theme.dark?.background) cssVars["--xicms-bg-dark"] = theme.dark.background;
|
|
74
|
+
if (theme.dark?.foreground) cssVars["--xicms-fg-dark"] = theme.dark.foreground;
|
|
75
|
+
if (theme.dark?.primary) cssVars["--xicms-primary-dark"] = theme.dark.primary;
|
|
76
|
+
cssVars["--color-accent"] = "var(--xicms-primary)";
|
|
77
|
+
cssVars["--color-on-accent"] = "#ffffff";
|
|
78
|
+
cssVars["--color-text-accent"] = "var(--xicms-primary)";
|
|
79
|
+
cssVars["--radius-element"] = "var(--xicms-radius-sm, 8px)";
|
|
80
|
+
cssVars["--radius-container"] = "var(--xicms-radius, 14px)";
|
|
81
|
+
cssVars["--radius-page"] = "var(--xicms-radius-lg, 24px)";
|
|
82
|
+
if (theme.background) {
|
|
83
|
+
cssVars["--color-background-body"] = "var(--xicms-bg)";
|
|
84
|
+
cssVars["--color-background-surface"] = "color-mix(in srgb, var(--xicms-fg) 4%, var(--xicms-bg))";
|
|
85
|
+
cssVars["--color-background-card"] = "color-mix(in srgb, var(--xicms-fg) 3%, var(--xicms-bg))";
|
|
86
|
+
cssVars["--color-background-popover"] = "var(--xicms-bg)";
|
|
87
|
+
cssVars["--color-background-muted"] = "color-mix(in srgb, var(--xicms-fg) 6%, var(--xicms-bg))";
|
|
88
|
+
cssVars["--xicms-surface"] = "color-mix(in srgb, var(--xicms-fg) 3%, var(--xicms-bg))";
|
|
89
|
+
cssVars["--xicms-muted"] = "color-mix(in srgb, var(--xicms-fg) 5%, var(--xicms-bg))";
|
|
90
|
+
}
|
|
91
|
+
if (theme.foreground) {
|
|
92
|
+
cssVars["--color-text-primary"] = "var(--xicms-fg)";
|
|
93
|
+
cssVars["--color-text-secondary"] = "color-mix(in srgb, var(--xicms-fg) 65%, transparent)";
|
|
94
|
+
cssVars["--color-neutral"] = "var(--xicms-fg)";
|
|
95
|
+
cssVars["--color-border"] = "color-mix(in srgb, var(--xicms-fg) 14%, transparent)";
|
|
96
|
+
cssVars["--xicms-border"] = "color-mix(in srgb, var(--xicms-fg) 12%, transparent)";
|
|
97
|
+
cssVars["--xicms-border-strong"] = "color-mix(in srgb, var(--xicms-fg) 22%, transparent)";
|
|
98
|
+
}
|
|
99
|
+
if (theme.headingFont) cssVars["--xicms-font-heading"] = theme.headingFont;
|
|
100
|
+
if (theme.fontFamily) {
|
|
101
|
+
cssVars["--font-family-body"] = "var(--xicms-font)";
|
|
102
|
+
cssVars["--font-family-heading"] = theme.headingFont ? "var(--xicms-font-heading)" : "var(--xicms-font)";
|
|
103
|
+
} else if (theme.headingFont) {
|
|
104
|
+
cssVars["--font-family-heading"] = "var(--xicms-font-heading)";
|
|
105
|
+
}
|
|
106
|
+
return cssVars;
|
|
107
|
+
}
|
|
108
|
+
function isDarkColor(color) {
|
|
109
|
+
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color?.trim() ?? "");
|
|
110
|
+
if (!m) return false;
|
|
111
|
+
let hex = m[1];
|
|
112
|
+
if (hex.length === 3) hex = hex.replace(/./g, (c) => c + c);
|
|
113
|
+
const [r, g, b] = [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
|
|
114
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.45;
|
|
115
|
+
}
|
|
116
|
+
function ModuleLayer({
|
|
117
|
+
config,
|
|
118
|
+
blocks = NO_BLOCKS,
|
|
119
|
+
modules = NO_MODULES,
|
|
120
|
+
overrides,
|
|
121
|
+
astryxTheme,
|
|
122
|
+
linkComponent,
|
|
123
|
+
imageComponent,
|
|
124
|
+
children
|
|
125
|
+
}) {
|
|
126
|
+
const { settings } = useSiteSettings();
|
|
127
|
+
const theme = useMemo(() => mergeThemeTokens(config.theme, settings.theme), [config.theme, settings.theme]);
|
|
128
|
+
const cssVars = useMemo(() => themeCssVars(theme), [theme]);
|
|
129
|
+
setDemoActive(Boolean(config.demoEditing));
|
|
130
|
+
const resolved = useMemo(
|
|
131
|
+
() => resolveModules([...builtinModules, ...modules], {
|
|
132
|
+
modules: config.modules,
|
|
133
|
+
features: config.features,
|
|
134
|
+
firestoreModules: settings.modules
|
|
135
|
+
}),
|
|
136
|
+
[config.modules, config.features, modules, settings.modules]
|
|
137
|
+
);
|
|
138
|
+
const registry = useMemo(
|
|
139
|
+
() => applyBlockOverrides(mergeRegistry(createRegistry(resolved.blocks), blocks), overrides),
|
|
140
|
+
[resolved.blocks, blocks, overrides]
|
|
141
|
+
);
|
|
142
|
+
return /* @__PURE__ */ jsx(ModulesProvider, { value: resolved, children: /* @__PURE__ */ jsx(RegistryProvider, { registry, children: /* @__PURE__ */ jsx(AuthProvider, { siteId: config.siteId, demo: Boolean(config.demoEditing), children: /* @__PURE__ */ jsx(ContentProvider, { siteId: config.siteId, defaults: config.defaults, children: /* @__PURE__ */ jsxs(EditorProvider, { children: [
|
|
143
|
+
theme.fontUrl && /* @__PURE__ */ jsx("link", { rel: "stylesheet", href: theme.fontUrl }),
|
|
144
|
+
/* @__PURE__ */ jsx(Theme, { theme: astryxTheme ?? neutralTheme, mode: theme.dark?.auto ? "system" : isDarkColor(theme.background) ? "dark" : "light", children: /* @__PURE__ */ jsx(LinkProvider, { component: linkComponent ?? "a", children: /* @__PURE__ */ jsx(ImageProvider, { component: imageComponent, children: /* @__PURE__ */ jsxs(
|
|
145
|
+
"div",
|
|
146
|
+
{
|
|
147
|
+
className: "xicms-root",
|
|
148
|
+
"data-dark": theme.dark?.auto ? "auto" : void 0,
|
|
149
|
+
style: cssVars,
|
|
150
|
+
children: [
|
|
151
|
+
/* @__PURE__ */ jsx(DemoStudio, {}),
|
|
152
|
+
children,
|
|
153
|
+
/* @__PURE__ */ jsx(EditBar, {}),
|
|
154
|
+
/* @__PURE__ */ jsx(DemoLock, {}),
|
|
155
|
+
resolved.activeIds.has("consent") && /* @__PURE__ */ jsx(ConsentBanner, { settings: resolved.settings.consent })
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
) }) }) })
|
|
159
|
+
] }) }) }) }) });
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
XiCMSProvider,
|
|
163
|
+
isDarkColor
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=XiCMSProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/XiCMSProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, type ReactNode } from \"react\";\nimport { ConfigProvider } from \"./config\";\nimport { AuthProvider } from \"./auth/AuthProvider\";\nimport { ContentProvider } from \"./content/ContentProvider\";\nimport { EditBar } from \"./components/EditBar\";\nimport { DemoLock } from \"./lock/DemoLock\";\nimport { RegistryProvider } from \"./blocks/context\";\nimport { createRegistry, mergeRegistry, type BlockDefinition } from \"./blocks/registry\";\nimport { resolveModules } from \"./modules/registry\";\nimport { ModulesProvider } from \"./modules/context\";\nimport { applyBlockOverrides, type BlockOverrides } from \"./modules/overrides\";\nimport { builtinModules } from \"./modules/builtins\";\nimport type { ModuleDefinition, ModulesSiteConfig } from \"./modules/types\";\nimport { ConsentBanner, type ConsentSettings } from \"./modules/consent/ConsentBanner\";\nimport { EditorProvider } from \"./editor/EditorProvider\";\nimport { SiteSettingsProvider, useSiteSettings } from \"./settings/useSiteSettings\";\nimport { mergeThemeTokens, type ThemeTokens } from \"./schema/settings\";\nimport { initAppCheck } from \"./firebase/appcheck\";\nimport { DemoStudio } from \"./demo/DemoStudio\";\nimport { setDemoActive } from \"./demo/demoStore\";\nimport { Theme, type DefinedTheme } from \"@astryxdesign/core/theme\";\nimport { neutralTheme } from \"@astryxdesign/theme-neutral/built\";\nimport { LinkProvider, type LinkComponentType } from \"@astryxdesign/core/Link\";\nimport { ImageProvider, type ImageComponentType } from \"./media/ImageContext\";\nimport { validateSiteConfig, type SiteConfig } from \"./schema/siteConfig\";\nimport { validateModuleContract } from \"./modules/contract\";\n\ninterface ProviderProps {\n config: SiteConfig;\n /** Losse klant-blokken (via `defineBlock`) buiten een module om. Altijd actief. */\n blocks?: BlockDefinition[];\n /**\n * Klant-modules (via `defineModule`) bovenop de ingebouwde. Worden gemerged op `id` (klant wint)\n * en respecteren `config.modules`/`config.features` voor activatie.\n */\n modules?: ModuleDefinition[];\n /**\n * Per-site presentatie-overrides per block-type (`{ gallery: { Render: MijnGalerij } }`). Vervangt\n * alleen het uiterlijk; schema/editor/logica blijven van core. De krachtigste maatwerk-naad.\n */\n overrides?: BlockOverrides;\n /**\n * Astryx-basistheme voor de hele site (radius-/spacing-/neutralen-karakter). Standaard `neutral`.\n * Een site kan een ander pakket kiezen (`@astryxdesign/theme-butter`, `-stone`, …) of een eigen\n * `defineTheme(...)` doorgeven voor component-overrides — zónder core te forken.\n */\n astryxTheme?: DefinedTheme;\n /**\n * Framework-link-component (bv. Next `Link`) voor soepele client-navigatie + prefetch. Astryx-\n * componenten met een `href` (knoppen/links/tabs) gebruiken 'm dan i.p.v. een rauwe `<a>` (= volledige\n * page-reload). Core importeert `next/link` niet zelf (optionele peer) — geef 'm hier door vanuit de site.\n */\n linkComponent?: LinkComponentType;\n /**\n * Framework-beeldcomponent (bv. Next `Image`) voor automatische beeldoptimalisatie (responsive\n * srcset, AVIF/WebP, geen layout-shift). Core importeert `next/image` niet zelf — geef 'm hier door.\n */\n imageComponent?: ImageComponentType;\n children: ReactNode;\n}\n\n// Stabiele lege defaults: een `= []`-defaultparameter maakt bij ELKE render een nieuwe\n// array-referentie en laat memo's/effects met `modules`/`blocks` in de deps onnodig hervuren.\nconst NO_BLOCKS: BlockDefinition[] = [];\nconst NO_MODULES: ModuleDefinition[] = [];\n\n// Dev-waarschuwingen één keer per boodschap (React strict-mode dubbelt effects; zonder dedupe\n// verschijnt dezelfde config-waarschuwing telkens opnieuw in de console).\nconst warned = new Set<string>();\nfunction warnOnce(message: string) {\n if (warned.has(message)) return;\n warned.add(message);\n console.warn(message);\n}\n\n/**\n * Wikkel de hele site in deze provider (in `app/layout.tsx`). Hij zet:\n * - de SiteConfig-context (siteId, theme, mode) + live publieke settings (footer/nav/modules);\n * - auth + content (Firebase, env-gedreven; werkt ook zonder Firebase);\n * - de block-registry (ingebouwde + klant-blokken) voor de page-renderer;\n * - de themavariabelen als CSS custom properties;\n * - App Check (alleen als geconfigureerd) en de EditBar/DemoLock.\n */\nexport function XiCMSProvider({ config, blocks = NO_BLOCKS, modules = NO_MODULES, overrides, astryxTheme, linkComponent, imageComponent, children }: ProviderProps) {\n useEffect(() => {\n initAppCheck();\n }, []);\n\n // Dev-vangnet voor (agent-)geschreven configs: meld config- en module-contractfouten meteen in\n // de console i.p.v. een subtiel kapotte site. In productie geen werk — en er wordt nooit gegooid.\n useEffect(() => {\n if (process.env.NODE_ENV === \"production\") return;\n const configIssues = validateSiteConfig(config);\n if (configIssues.length > 0) {\n warnOnce(`[xicms] site.config.ts bevat problemen:\\n- ${configIssues.join(\"\\n- \")}`);\n }\n if (modules.length > 0) {\n const contractIssues = validateModuleContract(modules);\n if (contractIssues.length > 0) {\n warnOnce(`[xicms] klant-modules voldoen niet aan het module-contract:\\n- ${contractIssues.join(\"\\n- \")}`);\n }\n }\n }, [config, modules]);\n\n return (\n <ConfigProvider config={config}>\n <SiteSettingsProvider siteId={config.siteId}>\n <ModuleLayer config={config} blocks={blocks} modules={modules} overrides={overrides} astryxTheme={astryxTheme} linkComponent={linkComponent} imageComponent={imageComponent}>\n {children}\n </ModuleLayer>\n </SiteSettingsProvider>\n </ConfigProvider>\n );\n}\n\n/**\n * Theme-tokens → CSS custom properties. Puur; wordt in `ModuleLayer` op het LIVE-gemergde thema\n * toegepast (SiteConfig + `settings/public.theme`), zodat huisstijl-wijzigingen uit /beheer direct\n * op de site verschijnen — net als module-instellingen.\n */\nfunction themeCssVars(theme: ThemeTokens): Record<string, string> {\n // Dark-auto via light-dark(): deze variabelen staan INLINE op `.xicms-root`, en inline wint\n // altijd van het `@media (prefers-color-scheme: dark)`-blok in styles.css. Alleen dat blok kan\n // een inline lichte waarde dus nooit overrulen — de tokenwaarde zelf moet meeschakelen.\n // (styles.css zet `color-scheme: light dark` op `[data-dark=\"auto\"]`, vereist voor light-dark().)\n const auto = Boolean(theme.dark?.auto);\n const ld = (light: string, dark: string) => (auto ? `light-dark(${light}, ${dark})` : light);\n const cssVars: Record<string, string> = {\n \"--xicms-primary\": theme.dark?.primary ? ld(theme.primary, theme.dark.primary) : theme.primary,\n \"--xicms-accent\": theme.accent ?? theme.primary,\n \"--xicms-bg\": ld(theme.background ?? \"#ffffff\", theme.dark?.background ?? \"#0f172a\"),\n \"--xicms-fg\": ld(theme.foreground ?? \"#0f172a\", theme.dark?.foreground ?? \"#e2e8f0\"),\n };\n if (theme.fontFamily) cssVars[\"--xicms-font\"] = theme.fontFamily;\n // Optionele schalen → CSS-variabelen (alleen gezet als opgegeven; styles.css heeft defaults).\n for (const [k, v] of Object.entries(theme.spacing ?? {})) if (v) cssVars[`--xicms-spacing-${k}`] = v;\n if (theme.radius?.sm) cssVars[\"--xicms-radius-sm\"] = theme.radius.sm;\n if (theme.radius?.md) cssVars[\"--xicms-radius\"] = theme.radius.md;\n if (theme.radius?.lg) cssVars[\"--xicms-radius-lg\"] = theme.radius.lg;\n if (theme.container?.maxWidth) cssVars[\"--xicms-container-max\"] = theme.container.maxWidth;\n if (theme.container?.padding) cssVars[\"--xicms-container-pad\"] = theme.container.padding;\n if (theme.dark?.background) cssVars[\"--xicms-bg-dark\"] = theme.dark.background;\n if (theme.dark?.foreground) cssVars[\"--xicms-fg-dark\"] = theme.dark.foreground;\n if (theme.dark?.primary) cssVars[\"--xicms-primary-dark\"] = theme.dark.primary;\n\n // Astryx-tokens op XiCMS-waarden mappen, zodat de Astryx-componenten elke site z'n eigen merk\n // (kleur/typografie/radius) volgen. Inline op `.xicms-root` → wint over de theme.css-defaults.\n cssVars[\"--color-accent\"] = \"var(--xicms-primary)\";\n cssVars[\"--color-on-accent\"] = \"#ffffff\";\n cssVars[\"--color-text-accent\"] = \"var(--xicms-primary)\";\n cssVars[\"--radius-element\"] = \"var(--xicms-radius-sm, 8px)\";\n cssVars[\"--radius-container\"] = \"var(--xicms-radius, 14px)\";\n cssVars[\"--radius-page\"] = \"var(--xicms-radius-lg, 24px)\";\n // Volledige paletmapping — ALLEEN als de site een eigen achtergrond/tekst kiest. Zo verschilt niet\n // enkel het accent maar de hele sfeer (kaarten, borders, secundaire tekst) per site; een minimale\n // config blijft op de gepolijste neutrale basis (incl. automatische dark-mode via light-dark()).\n if (theme.background) {\n cssVars[\"--color-background-body\"] = \"var(--xicms-bg)\";\n cssVars[\"--color-background-surface\"] = \"color-mix(in srgb, var(--xicms-fg) 4%, var(--xicms-bg))\";\n cssVars[\"--color-background-card\"] = \"color-mix(in srgb, var(--xicms-fg) 3%, var(--xicms-bg))\";\n cssVars[\"--color-background-popover\"] = \"var(--xicms-bg)\";\n cssVars[\"--color-background-muted\"] = \"color-mix(in srgb, var(--xicms-fg) 6%, var(--xicms-bg))\";\n // Ook de XiCMS-oppervlaktetokens meeschakelen: hun defaults in styles.css zijn licht (#fff/zwart-\n // rgba) en het dark-blok vuurt alleen bij `data-dark=\"auto\"`. Zonder deze mapping tonen presets\n // met een vaste donkere achtergrond (donker-modern, luxe-donker) witte kaarten en zwarte borders.\n cssVars[\"--xicms-surface\"] = \"color-mix(in srgb, var(--xicms-fg) 3%, var(--xicms-bg))\";\n cssVars[\"--xicms-muted\"] = \"color-mix(in srgb, var(--xicms-fg) 5%, var(--xicms-bg))\";\n }\n if (theme.foreground) {\n cssVars[\"--color-text-primary\"] = \"var(--xicms-fg)\";\n cssVars[\"--color-text-secondary\"] = \"color-mix(in srgb, var(--xicms-fg) 65%, transparent)\";\n cssVars[\"--color-neutral\"] = \"var(--xicms-fg)\";\n cssVars[\"--color-border\"] = \"color-mix(in srgb, var(--xicms-fg) 14%, transparent)\";\n cssVars[\"--xicms-border\"] = \"color-mix(in srgb, var(--xicms-fg) 12%, transparent)\";\n cssVars[\"--xicms-border-strong\"] = \"color-mix(in srgb, var(--xicms-fg) 22%, transparent)\";\n }\n if (theme.headingFont) cssVars[\"--xicms-font-heading\"] = theme.headingFont;\n if (theme.fontFamily) {\n cssVars[\"--font-family-body\"] = \"var(--xicms-font)\";\n // Koppen volgen een apart display/serif-font als dat is opgegeven, anders het body-font.\n cssVars[\"--font-family-heading\"] = theme.headingFont ? \"var(--xicms-font-heading)\" : \"var(--xicms-font)\";\n } else if (theme.headingFont) {\n cssVars[\"--font-family-heading\"] = \"var(--xicms-font-heading)\";\n }\n\n return cssVars;\n}\n\n/**\n * Is deze (hex-)kleur donker? Bepaalt de Astryx-thememodus voor sites met een vaste donkere\n * achtergrond (bv. presets donker-modern/luxe-donker, `dark.auto: false`): zonder deze afleiding\n * bleef Astryx op \"light\" staan en tekenden zijn componenten lichte kaarten op een donkere pagina.\n * Puur en exporteerbaar voor tests; niet-hex waarden gelden als licht (veilige default).\n */\nexport function isDarkColor(color?: string): boolean {\n const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color?.trim() ?? \"\");\n if (!m) return false;\n let hex = m[1];\n if (hex.length === 3) hex = hex.replace(/./g, (c) => c + c);\n const [r, g, b] = [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.45;\n}\n\n/**\n * Binnenlaag: leest de live publieke settings en resolvet daarmee de modules (settings/public.modules\n * legt zich over SiteConfig.modules) én het thema (settings/public.theme over SiteConfig.theme).\n * Zo werken module-aan/uit, -instellingen én huisstijl die de klant in /beheer zet direct door.\n */\nfunction ModuleLayer({\n config,\n blocks = NO_BLOCKS,\n modules = NO_MODULES,\n overrides,\n astryxTheme,\n linkComponent,\n imageComponent,\n children,\n}: ProviderProps) {\n const { settings } = useSiteSettings();\n\n // LIVE thema: wijzigingen uit /beheer (settings/public.theme) winnen per token van SiteConfig.\n const theme = useMemo(() => mergeThemeTokens(config.theme, settings.theme), [config.theme, settings.theme]);\n const cssVars = useMemo(() => themeCssVars(theme), [theme]);\n\n // Demo-bewerkmodus globaal markeren zodat de (Firebase-)dataclients lokaal-only werken.\n setDemoActive(Boolean(config.demoEditing));\n\n const resolved = useMemo(\n () =>\n resolveModules([...builtinModules, ...modules], {\n modules: config.modules,\n features: config.features,\n firestoreModules: settings.modules as ModulesSiteConfig | undefined,\n }),\n [config.modules, config.features, modules, settings.modules],\n );\n\n const registry = useMemo(\n () => applyBlockOverrides(mergeRegistry(createRegistry(resolved.blocks), blocks), overrides),\n [resolved.blocks, blocks, overrides],\n );\n\n return (\n <ModulesProvider value={resolved}>\n <RegistryProvider registry={registry}>\n <AuthProvider siteId={config.siteId} demo={Boolean(config.demoEditing)}>\n <ContentProvider siteId={config.siteId} defaults={config.defaults}>\n <EditorProvider>\n {/* React 19 hijst <link rel=\"stylesheet\"> automatisch naar <head> → lettertype laadt. */}\n {theme.fontUrl && <link rel=\"stylesheet\" href={theme.fontUrl} />}\n {/* Astryx-thema (Neutral basis) — de XiCMS-token-mapping op `.xicms-root` legt elke\n site z'n eigen merk eroverheen. */}\n <Theme theme={astryxTheme ?? neutralTheme} mode={theme.dark?.auto ? \"system\" : isDarkColor(theme.background) ? \"dark\" : \"light\"}>\n {/* Framework-router voor Astryx-links/knoppen (soepele client-nav + prefetch);\n zonder `linkComponent` valt het terug op een gewone `<a>` (huidig gedrag). */}\n <LinkProvider component={linkComponent ?? \"a\"}>\n <ImageProvider component={imageComponent}>\n <div\n className=\"xicms-root\"\n data-dark={theme.dark?.auto ? \"auto\" : undefined}\n style={cssVars as React.CSSProperties}\n >\n <DemoStudio />\n {children}\n <EditBar />\n <DemoLock />\n {resolved.activeIds.has(\"consent\") && (\n <ConsentBanner settings={resolved.settings.consent as ConsentSettings} />\n )}\n </div>\n </ImageProvider>\n </LinkProvider>\n </Theme>\n </EditorProvider>\n </ContentProvider>\n </AuthProvider>\n </RegistryProvider>\n </ModulesProvider>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useAuth } from "../auth/AuthProvider";
|
|
4
|
+
import { LoginForm } from "../auth/LoginForm";
|
|
5
|
+
import { useSiteConfig } from "../config";
|
|
6
|
+
import { useModules } from "../modules/context";
|
|
7
|
+
import { canEdit, canManageSite } from "../auth/roles";
|
|
8
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "./ui/tabs";
|
|
9
|
+
import { ToastProvider } from "./ui/toast";
|
|
10
|
+
import { ConfirmHost } from "./ui/ConfirmDialog";
|
|
11
|
+
import { Button } from "./ui/primitives";
|
|
12
|
+
import { builtinPanels } from "./panelRegistry";
|
|
13
|
+
import { AdminHelp } from "./AdminHelp";
|
|
14
|
+
function roleAllows(role, min) {
|
|
15
|
+
return min === "siteadmin" ? canManageSite(role) : canEdit(role);
|
|
16
|
+
}
|
|
17
|
+
function AdminApp({ extraPanels = [] }) {
|
|
18
|
+
const { user, role, loading, configured, signOut } = useAuth();
|
|
19
|
+
const { name, siteId } = useSiteConfig();
|
|
20
|
+
const { activeIds } = useModules();
|
|
21
|
+
const panels = [...builtinPanels, ...extraPanels].filter((p) => activeIds.has(p.moduleId) && roleAllows(role, p.minRole)).sort((a, b) => a.order - b.order);
|
|
22
|
+
const firstTab = panels[0]?.moduleId;
|
|
23
|
+
return /* @__PURE__ */ jsx("div", { className: "xicms-admin", children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(ConfirmHost, { children: !configured ? /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-slate-200 bg-white p-6 text-sm", children: [
|
|
24
|
+
"Firebase is nog niet geconfigureerd. Vul de ",
|
|
25
|
+
/* @__PURE__ */ jsx("code", { children: "NEXT_PUBLIC_FIREBASE_*" }),
|
|
26
|
+
"-variabelen in om het beheer aan te zetten."
|
|
27
|
+
] }) : loading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Laden\u2026" }) : !user ? /* @__PURE__ */ jsx(LoginForm, {}) : !canEdit(role) ? /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-slate-200 bg-white p-6 text-sm", children: [
|
|
28
|
+
"Je bent ingelogd als ",
|
|
29
|
+
/* @__PURE__ */ jsx("strong", { children: user.email }),
|
|
30
|
+
", maar hebt geen beheerrechten voor deze site.",
|
|
31
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: () => void signOut(), children: "Uitloggen" }) })
|
|
32
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
34
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
35
|
+
/* @__PURE__ */ jsxs("h1", { className: "text-xl font-bold", children: [
|
|
36
|
+
"Beheer \xB7 ",
|
|
37
|
+
name
|
|
38
|
+
] }),
|
|
39
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-slate-500", children: [
|
|
40
|
+
"Ingelogd als ",
|
|
41
|
+
user.email,
|
|
42
|
+
" (",
|
|
43
|
+
role,
|
|
44
|
+
")"
|
|
45
|
+
] })
|
|
46
|
+
] }),
|
|
47
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
48
|
+
/* @__PURE__ */ jsx("a", { className: "inline-flex h-9 items-center rounded-lg border border-slate-200 px-3 text-sm hover:bg-slate-50", href: "/", children: "Bekijk site" }),
|
|
49
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: () => void signOut(), children: "Uitloggen" })
|
|
50
|
+
] })
|
|
51
|
+
] }),
|
|
52
|
+
/* @__PURE__ */ jsx(AdminHelp, {}),
|
|
53
|
+
panels.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Geen actieve beheer-modules voor deze site." }) : /* @__PURE__ */ jsxs(Tabs, { defaultValue: firstTab, children: [
|
|
54
|
+
/* @__PURE__ */ jsx(TabsList, { children: panels.map((p) => /* @__PURE__ */ jsx(TabsTrigger, { value: p.moduleId, children: p.label }, p.moduleId)) }),
|
|
55
|
+
panels.map((p) => /* @__PURE__ */ jsx(TabsContent, { value: p.moduleId, children: /* @__PURE__ */ jsx(p.Panel, { siteId }) }, p.moduleId))
|
|
56
|
+
] })
|
|
57
|
+
] }) }) }) });
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
AdminApp
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=AdminApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/admin/AdminApp.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuth } from \"../auth/AuthProvider\";\nimport { LoginForm } from \"../auth/LoginForm\";\nimport { useSiteConfig } from \"../config\";\nimport { useModules } from \"../modules/context\";\nimport { canEdit, canManageSite, type Role } from \"../auth/roles\";\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from \"./ui/tabs\";\nimport { ToastProvider } from \"./ui/toast\";\nimport { ConfirmHost } from \"./ui/ConfirmDialog\";\nimport { Button } from \"./ui/primitives\";\nimport { builtinPanels, type PanelDefinition } from \"./panelRegistry\";\nimport { AdminHelp } from \"./AdminHelp\";\n\n/**\n * Rol-bewaakt beheerdashboard (XiCMS 3.0). De tabs komen uit de paneel-registry, gefilterd op de\n * actieve modules (`SiteConfig.modules`/`features`) en de rol van de gebruiker. Een module met een\n * eigen paneel (ook klant-modules via `extraPanels`) verschijnt automatisch.\n *\n * Wikkel in `.xicms-admin` zodat de Tailwind-stijlen uit `@xiwebdesign/xicms-core/admin.css` scoped\n * blijven en niet naar de site lekken. Importeer die CSS alleen op de /beheer-route.\n */\nfunction roleAllows(role: Role | null, min: PanelDefinition[\"minRole\"]): boolean {\n return min === \"siteadmin\" ? canManageSite(role) : canEdit(role);\n}\n\nexport function AdminApp({ extraPanels = [] }: { extraPanels?: PanelDefinition[] }) {\n const { user, role, loading, configured, signOut } = useAuth();\n const { name, siteId } = useSiteConfig();\n const { activeIds } = useModules();\n\n // Panelen van actieve modules waar de rol toegang toe heeft, gesorteerd op volgorde.\n const panels = [...builtinPanels, ...extraPanels]\n .filter((p) => activeIds.has(p.moduleId) && roleAllows(role, p.minRole))\n .sort((a, b) => a.order - b.order);\n const firstTab = panels[0]?.moduleId;\n\n return (\n <div className=\"xicms-admin\">\n <ToastProvider>\n <ConfirmHost>\n {!configured ? (\n <div className=\"rounded-xl border border-slate-200 bg-white p-6 text-sm\">\n Firebase is nog niet geconfigureerd. Vul de <code>NEXT_PUBLIC_FIREBASE_*</code>-variabelen in\n om het beheer aan te zetten.\n </div>\n ) : loading ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : !user ? (\n <LoginForm />\n ) : !canEdit(role) ? (\n <div className=\"rounded-xl border border-slate-200 bg-white p-6 text-sm\">\n Je bent ingelogd als <strong>{user.email}</strong>, maar hebt geen beheerrechten voor deze site.\n <div className=\"mt-3\">\n <Button variant=\"secondary\" size=\"sm\" onClick={() => void signOut()}>Uitloggen</Button>\n </div>\n </div>\n ) : (\n <div className=\"grid gap-6\">\n <header className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-xl font-bold\">Beheer · {name}</h1>\n <p className=\"text-sm text-slate-500\">Ingelogd als {user.email} ({role})</p>\n </div>\n <div className=\"flex gap-2\">\n <a className=\"inline-flex h-9 items-center rounded-lg border border-slate-200 px-3 text-sm hover:bg-slate-50\" href=\"/\">Bekijk site</a>\n <Button variant=\"secondary\" size=\"sm\" onClick={() => void signOut()}>Uitloggen</Button>\n </div>\n </header>\n\n <AdminHelp />\n\n {panels.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Geen actieve beheer-modules voor deze site.</p>\n ) : (\n <Tabs defaultValue={firstTab}>\n <TabsList>\n {panels.map((p) => (\n <TabsTrigger key={p.moduleId} value={p.moduleId}>\n {p.label}\n </TabsTrigger>\n ))}\n </TabsList>\n\n {panels.map((p) => (\n <TabsContent key={p.moduleId} value={p.moduleId}>\n <p.Panel siteId={siteId} />\n </TabsContent>\n ))}\n </Tabs>\n )}\n </div>\n )}\n </ConfirmHost>\n </ToastProvider>\n </div>\n );\n}\n"],"mappings":";AA0CU,SAC8C,KAD9C;AAxCV,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,SAAS,qBAAgC;AAClD,SAAS,MAAM,UAAU,aAAa,mBAAmB;AACzD,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AACvB,SAAS,qBAA2C;AACpD,SAAS,iBAAiB;AAU1B,SAAS,WAAW,MAAmB,KAA0C;AAC/E,SAAO,QAAQ,cAAc,cAAc,IAAI,IAAI,QAAQ,IAAI;AACjE;AAEO,SAAS,SAAS,EAAE,cAAc,CAAC,EAAE,GAAwC;AAClF,QAAM,EAAE,MAAM,MAAM,SAAS,YAAY,QAAQ,IAAI,QAAQ;AAC7D,QAAM,EAAE,MAAM,OAAO,IAAI,cAAc;AACvC,QAAM,EAAE,UAAU,IAAI,WAAW;AAGjC,QAAM,SAAS,CAAC,GAAG,eAAe,GAAG,WAAW,EAC7C,OAAO,CAAC,MAAM,UAAU,IAAI,EAAE,QAAQ,KAAK,WAAW,MAAM,EAAE,OAAO,CAAC,EACtE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACnC,QAAM,WAAW,OAAO,CAAC,GAAG;AAE5B,SACE,oBAAC,SAAI,WAAU,eACb,8BAAC,iBACA,8BAAC,eACC,WAAC,aACA,qBAAC,SAAI,WAAU,2DAA0D;AAAA;AAAA,IAC3B,oBAAC,UAAK,oCAAsB;AAAA,IAAO;AAAA,KAEjF,IACE,UACF,oBAAC,OAAE,WAAU,0BAAyB,yBAAM,IAC1C,CAAC,OACH,oBAAC,aAAU,IACT,CAAC,QAAQ,IAAI,IACf,qBAAC,SAAI,WAAU,2DAA0D;AAAA;AAAA,IAClD,oBAAC,YAAQ,eAAK,OAAM;AAAA,IAAS;AAAA,IAClD,oBAAC,SAAI,WAAU,QACb,8BAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,KAAK,QAAQ,GAAG,uBAAS,GAChF;AAAA,KACF,IAEA,qBAAC,SAAI,WAAU,cACb;AAAA,yBAAC,YAAO,WAAU,qDAChB;AAAA,2BAAC,SACC;AAAA,6BAAC,QAAG,WAAU,qBAAoB;AAAA;AAAA,UAAU;AAAA,WAAK;AAAA,QACjD,qBAAC,OAAE,WAAU,0BAAyB;AAAA;AAAA,UAAc,KAAK;AAAA,UAAM;AAAA,UAAG;AAAA,UAAK;AAAA,WAAC;AAAA,SAC1E;AAAA,MACA,qBAAC,SAAI,WAAU,cACb;AAAA,4BAAC,OAAE,WAAU,kGAAiG,MAAK,KAAI,yBAAW;AAAA,QAClI,oBAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,KAAK,QAAQ,GAAG,uBAAS;AAAA,SAChF;AAAA,OACF;AAAA,IAEA,oBAAC,aAAU;AAAA,IAEV,OAAO,WAAW,IACjB,oBAAC,OAAE,WAAU,0BAAyB,yDAA2C,IAEjF,qBAAC,QAAK,cAAc,UAClB;AAAA,0BAAC,YACE,iBAAO,IAAI,CAAC,MACX,oBAAC,eAA6B,OAAO,EAAE,UACpC,YAAE,SADa,EAAE,QAEpB,CACD,GACH;AAAA,MAEC,OAAO,IAAI,CAAC,MACX,oBAAC,eAA6B,OAAO,EAAE,UACrC,8BAAC,EAAE,OAAF,EAAQ,QAAgB,KADT,EAAE,QAEpB,CACD;AAAA,OACH;AAAA,KAEJ,GAEH,GACD,GACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
const KEY = "xicms-help-dismissed";
|
|
5
|
+
function AdminHelp() {
|
|
6
|
+
const [hidden, setHidden] = useState(true);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
try {
|
|
9
|
+
setHidden(localStorage.getItem(KEY) === "1");
|
|
10
|
+
} catch {
|
|
11
|
+
setHidden(false);
|
|
12
|
+
}
|
|
13
|
+
}, []);
|
|
14
|
+
if (hidden) return null;
|
|
15
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-brand-200 bg-brand-50 p-4 text-sm text-slate-700", children: [
|
|
16
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
17
|
+
/* @__PURE__ */ jsx("strong", { children: "Welkom in het beheer \u{1F44B}" }),
|
|
18
|
+
/* @__PURE__ */ jsx(
|
|
19
|
+
"button",
|
|
20
|
+
{
|
|
21
|
+
type: "button",
|
|
22
|
+
className: "text-xs text-slate-500 hover:underline",
|
|
23
|
+
onClick: () => {
|
|
24
|
+
try {
|
|
25
|
+
localStorage.setItem(KEY, "1");
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
setHidden(true);
|
|
29
|
+
},
|
|
30
|
+
children: "Verbergen"
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
] }),
|
|
34
|
+
/* @__PURE__ */ jsxs("ul", { className: "grid list-disc gap-1 pl-5", children: [
|
|
35
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
36
|
+
/* @__PURE__ */ jsx("strong", { children: "Pagina's" }),
|
|
37
|
+
": pas teksten en secties aan. Je wijzigingen gaan pas live als je op",
|
|
38
|
+
/* @__PURE__ */ jsx("strong", { children: " Publiceren" }),
|
|
39
|
+
" klikt \u2014 experimenteren kan dus veilig."
|
|
40
|
+
] }),
|
|
41
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
42
|
+
"Liever direct op de site typen? Klik op de website zelf op ",
|
|
43
|
+
/* @__PURE__ */ jsx("strong", { children: "\u201CPagina bewerken\u201D" }),
|
|
44
|
+
"."
|
|
45
|
+
] }),
|
|
46
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
47
|
+
/* @__PURE__ */ jsx("strong", { children: "Nieuws" }),
|
|
48
|
+
": plaats artikelen. ",
|
|
49
|
+
/* @__PURE__ */ jsx("strong", { children: "Media" }),
|
|
50
|
+
": upload foto's \u2014 die worden automatisch verkleind om je site snel te houden."
|
|
51
|
+
] }),
|
|
52
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
53
|
+
"Maak af en toe een ",
|
|
54
|
+
/* @__PURE__ */ jsx("strong", { children: "back-up" }),
|
|
55
|
+
" onder ",
|
|
56
|
+
/* @__PURE__ */ jsx("strong", { children: "Instellingen" }),
|
|
57
|
+
"; terugzetten kan altijd."
|
|
58
|
+
] })
|
|
59
|
+
] })
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
AdminHelp
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=AdminHelp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/admin/AdminHelp.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nconst KEY = \"xicms-help-dismissed\";\n\n/** Korte, vriendelijke uitleg voor (a-technische) gebruikers die voor het eerst inloggen. */\nexport function AdminHelp() {\n const [hidden, setHidden] = useState(true);\n useEffect(() => {\n try {\n setHidden(localStorage.getItem(KEY) === \"1\");\n } catch {\n setHidden(false);\n }\n }, []);\n if (hidden) return null;\n return (\n <div className=\"rounded-xl border border-brand-200 bg-brand-50 p-4 text-sm text-slate-700\">\n <div className=\"mb-2 flex items-center justify-between\">\n <strong>Welkom in het beheer 👋</strong>\n <button\n type=\"button\"\n className=\"text-xs text-slate-500 hover:underline\"\n onClick={() => {\n try {\n localStorage.setItem(KEY, \"1\");\n } catch {\n /* opslag geweigerd */\n }\n setHidden(true);\n }}\n >\n Verbergen\n </button>\n </div>\n <ul className=\"grid list-disc gap-1 pl-5\">\n <li>\n <strong>Pagina's</strong>: pas teksten en secties aan. Je wijzigingen gaan pas live als je op\n <strong> Publiceren</strong> klikt — experimenteren kan dus veilig.\n </li>\n <li>\n Liever direct op de site typen? Klik op de website zelf op <strong>“Pagina bewerken”</strong>.\n </li>\n <li>\n <strong>Nieuws</strong>: plaats artikelen. <strong>Media</strong>: upload foto's — die worden\n automatisch verkleind om je site snel te houden.\n </li>\n <li>\n Maak af en toe een <strong>back-up</strong> onder <strong>Instellingen</strong>; terugzetten kan altijd.\n </li>\n </ul>\n </div>\n );\n}\n"],"mappings":";AAmBM,SACE,KADF;AAjBN,SAAS,WAAW,gBAAgB;AAEpC,MAAM,MAAM;AAGL,SAAS,YAAY;AAC1B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,IAAI;AACzC,YAAU,MAAM;AACd,QAAI;AACF,gBAAU,aAAa,QAAQ,GAAG,MAAM,GAAG;AAAA,IAC7C,QAAQ;AACN,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,CAAC;AACL,MAAI,OAAQ,QAAO;AACnB,SACE,qBAAC,SAAI,WAAU,6EACb;AAAA,yBAAC,SAAI,WAAU,0CACb;AAAA,0BAAC,YAAO,4CAAuB;AAAA,MAC/B;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM;AACb,gBAAI;AACF,2BAAa,QAAQ,KAAK,GAAG;AAAA,YAC/B,QAAQ;AAAA,YAER;AACA,sBAAU,IAAI;AAAA,UAChB;AAAA,UACD;AAAA;AAAA,MAED;AAAA,OACF;AAAA,IACA,qBAAC,QAAG,WAAU,6BACZ;AAAA,2BAAC,QACC;AAAA,4BAAC,YAAO,sBAAa;AAAA,QAAS;AAAA,QAC9B,oBAAC,YAAO,yBAAW;AAAA,QAAS;AAAA,SAC9B;AAAA,MACA,qBAAC,QAAG;AAAA;AAAA,QACyD,oBAAC,YAAO,yCAAiB;AAAA,QAAS;AAAA,SAC/F;AAAA,MACA,qBAAC,QACC;AAAA,4BAAC,YAAO,oBAAM;AAAA,QAAS;AAAA,QAAoB,oBAAC,YAAO,mBAAK;AAAA,QAAS;AAAA,SAEnE;AAAA,MACA,qBAAC,QAAG;AAAA;AAAA,QACiB,oBAAC,YAAO,qBAAO;AAAA,QAAS;AAAA,QAAO,oBAAC,YAAO,0BAAY;AAAA,QAAS;AAAA,SACjF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { addDoc, collection, deleteDoc, doc, serverTimestamp, updateDoc } from "firebase/firestore";
|
|
5
|
+
import { getDb } from "../firebase/client";
|
|
6
|
+
import { useSiteConfig } from "../config";
|
|
7
|
+
import { useNews } from "../components/News";
|
|
8
|
+
import { uploadImage } from "./lib/upload";
|
|
9
|
+
import { slugify } from "./lib/slugify";
|
|
10
|
+
import { useConfirm } from "./ui/ConfirmDialog";
|
|
11
|
+
import { useToast } from "./ui/toast";
|
|
12
|
+
import { Button, Card, Field, Input, Badge } from "./ui/primitives";
|
|
13
|
+
import { RichTextEditor } from "./editors/RichTextEditor";
|
|
14
|
+
const empty = { title: "", excerpt: "", body: "", bodyHtml: "", coverUrl: "", date: "", published: false };
|
|
15
|
+
function NewsEditor() {
|
|
16
|
+
const { siteId } = useSiteConfig();
|
|
17
|
+
const { articles } = useNews({ onlyPublished: false });
|
|
18
|
+
const confirm = useConfirm();
|
|
19
|
+
const { toast } = useToast();
|
|
20
|
+
const [form, setForm] = useState(empty);
|
|
21
|
+
const [editingId, setEditingId] = useState(null);
|
|
22
|
+
const [busy, setBusy] = useState(false);
|
|
23
|
+
const [coverBusy, setCoverBusy] = useState(false);
|
|
24
|
+
const coverInputRef = useRef(null);
|
|
25
|
+
function reset() {
|
|
26
|
+
setForm(empty);
|
|
27
|
+
setEditingId(null);
|
|
28
|
+
}
|
|
29
|
+
function loadIntoForm(a) {
|
|
30
|
+
setEditingId(a.id);
|
|
31
|
+
setForm({
|
|
32
|
+
title: a.title,
|
|
33
|
+
excerpt: a.excerpt ?? "",
|
|
34
|
+
body: a.body,
|
|
35
|
+
bodyHtml: a.bodyHtml ?? "",
|
|
36
|
+
coverUrl: a.coverUrl ?? "",
|
|
37
|
+
date: a.date,
|
|
38
|
+
published: a.published
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function save() {
|
|
42
|
+
const db = getDb();
|
|
43
|
+
if (!db) return;
|
|
44
|
+
if (!form.title.trim()) {
|
|
45
|
+
toast({ title: "Titel ontbreekt", description: "Geef het artikel een titel.", tone: "error" });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
setBusy(true);
|
|
49
|
+
try {
|
|
50
|
+
const payload = {
|
|
51
|
+
title: form.title.trim(),
|
|
52
|
+
slug: slugify(form.title),
|
|
53
|
+
excerpt: form.excerpt.trim(),
|
|
54
|
+
body: form.body,
|
|
55
|
+
// Rich text (client-side al door DOMPurify gehaald in de editor; de weergave sanitized
|
|
56
|
+
// bovendien opnieuw bij het renderen — dubbele verdediging).
|
|
57
|
+
bodyHtml: form.bodyHtml,
|
|
58
|
+
coverUrl: form.coverUrl.trim(),
|
|
59
|
+
date: form.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
60
|
+
published: form.published,
|
|
61
|
+
updatedAt: serverTimestamp()
|
|
62
|
+
};
|
|
63
|
+
if (editingId) {
|
|
64
|
+
await updateDoc(doc(db, "sites", siteId, "news", editingId), payload);
|
|
65
|
+
} else {
|
|
66
|
+
await addDoc(collection(db, "sites", siteId, "news"), { ...payload, createdAt: serverTimestamp() });
|
|
67
|
+
}
|
|
68
|
+
toast({ title: editingId ? "Artikel bijgewerkt" : "Artikel opgeslagen", tone: "success" });
|
|
69
|
+
reset();
|
|
70
|
+
} catch (e) {
|
|
71
|
+
toast({ title: "Opslaan mislukt", description: e.message, tone: "error" });
|
|
72
|
+
} finally {
|
|
73
|
+
setBusy(false);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function remove(id, title) {
|
|
77
|
+
const db = getDb();
|
|
78
|
+
if (!db) return;
|
|
79
|
+
const ok = await confirm({
|
|
80
|
+
title: "Artikel verwijderen?",
|
|
81
|
+
description: `\u201C${title}\u201D wordt definitief verwijderd.`,
|
|
82
|
+
confirmLabel: "Verwijderen",
|
|
83
|
+
tone: "danger"
|
|
84
|
+
});
|
|
85
|
+
if (!ok) return;
|
|
86
|
+
await deleteDoc(doc(db, "sites", siteId, "news", id));
|
|
87
|
+
if (editingId === id) reset();
|
|
88
|
+
toast({ title: "Artikel verwijderd", tone: "success" });
|
|
89
|
+
}
|
|
90
|
+
async function onCover(file) {
|
|
91
|
+
setCoverBusy(true);
|
|
92
|
+
try {
|
|
93
|
+
const { url } = await uploadImage(siteId, file);
|
|
94
|
+
setForm((prev) => ({ ...prev, coverUrl: url }));
|
|
95
|
+
toast({ title: "Foto toegevoegd", description: "Automatisch verkleind voor het web.", tone: "success" });
|
|
96
|
+
} catch (e) {
|
|
97
|
+
toast({ title: "Upload mislukt", description: e.message, tone: "error" });
|
|
98
|
+
} finally {
|
|
99
|
+
setCoverBusy(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
|
|
103
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
104
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
105
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: editingId ? "Artikel bewerken" : "Nieuw artikel" }),
|
|
106
|
+
editingId && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: reset, children: "+ Nieuw artikel" })
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
109
|
+
/* @__PURE__ */ jsx(Field, { label: "Titel", children: /* @__PURE__ */ jsx(Input, { value: form.title, onChange: (e) => setForm({ ...form, title: e.target.value }), placeholder: "Kop van het artikel" }) }),
|
|
110
|
+
/* @__PURE__ */ jsx(Field, { label: "Korte samenvatting", hint: "Verschijnt in de nieuwslijst.", children: /* @__PURE__ */ jsx(Input, { value: form.excerpt, onChange: (e) => setForm({ ...form, excerpt: e.target.value }) }) }),
|
|
111
|
+
/* @__PURE__ */ jsx(Field, { label: "Coverfoto", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
112
|
+
/* @__PURE__ */ jsx(Input, { value: form.coverUrl, onChange: (e) => setForm({ ...form, coverUrl: e.target.value }), placeholder: "URL of upload \u2192" }),
|
|
113
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: coverBusy, onClick: () => coverInputRef.current?.click(), children: coverBusy ? "Uploaden\u2026" : "Upload" }),
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
"input",
|
|
116
|
+
{
|
|
117
|
+
ref: coverInputRef,
|
|
118
|
+
type: "file",
|
|
119
|
+
accept: "image/*",
|
|
120
|
+
hidden: true,
|
|
121
|
+
onChange: (e) => {
|
|
122
|
+
const f = e.target.files?.[0];
|
|
123
|
+
if (f) void onCover(f);
|
|
124
|
+
e.target.value = "";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
] }) }),
|
|
129
|
+
form.coverUrl && // eslint-disable-next-line @next/next/no-img-element
|
|
130
|
+
/* @__PURE__ */ jsx("img", { src: form.coverUrl, alt: "", className: "h-32 w-full rounded-lg object-cover" }),
|
|
131
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
132
|
+
/* @__PURE__ */ jsx(Field, { label: "Datum", children: /* @__PURE__ */ jsx(Input, { type: "date", value: form.date, onChange: (e) => setForm({ ...form, date: e.target.value }) }) }),
|
|
133
|
+
/* @__PURE__ */ jsx(Field, { label: "Zichtbaarheid", hint: "Aangevinkt = direct zichtbaar op de site; uit = concept.", children: /* @__PURE__ */ jsxs("label", { className: "flex h-9 items-center gap-2 text-sm", children: [
|
|
134
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: form.published, onChange: (e) => setForm({ ...form, published: e.target.checked }) }),
|
|
135
|
+
"Gepubliceerd"
|
|
136
|
+
] }) })
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ jsx(Field, { label: "Tekst", hint: "Met opmaak (koppen, lijsten, links, afbeeldingen). Bestaande artikelen zonder opmaak blijven gewoon werken.", children: /* @__PURE__ */ jsx(
|
|
139
|
+
RichTextEditor,
|
|
140
|
+
{
|
|
141
|
+
siteId,
|
|
142
|
+
value: form.bodyHtml || (form.body ? `<p>${form.body.split(/\n{2,}/).join("</p><p>")}</p>` : ""),
|
|
143
|
+
onChange: (html) => setForm((f) => ({ ...f, bodyHtml: html }))
|
|
144
|
+
}
|
|
145
|
+
) }),
|
|
146
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => void save(), children: busy ? "Opslaan\u2026" : editingId ? "Wijzigingen opslaan" : "Artikel opslaan" }) })
|
|
147
|
+
] })
|
|
148
|
+
] }),
|
|
149
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
150
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-base font-semibold", children: "Artikelen" }),
|
|
151
|
+
articles.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen artikelen. Maak hierboven je eerste nieuwsartikel." }) : /* @__PURE__ */ jsx("div", { className: "grid gap-2", children: articles.map((a) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg border border-slate-200 p-3", children: [
|
|
152
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm", children: [
|
|
153
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: a.title }),
|
|
154
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-400", children: [
|
|
155
|
+
"\xB7 ",
|
|
156
|
+
a.date
|
|
157
|
+
] }),
|
|
158
|
+
a.published ? /* @__PURE__ */ jsx(Badge, { tone: "green", children: "zichtbaar" }) : /* @__PURE__ */ jsx(Badge, { tone: "amber", children: "concept" })
|
|
159
|
+
] }),
|
|
160
|
+
/* @__PURE__ */ jsxs("span", { className: "flex gap-2", children: [
|
|
161
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => loadIntoForm(a), children: "Bewerk" }),
|
|
162
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => void remove(a.id, a.title), children: "Verwijder" })
|
|
163
|
+
] })
|
|
164
|
+
] }, a.id)) })
|
|
165
|
+
] })
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
NewsEditor
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=NewsEditor.js.map
|