@xiwebdesign/xicms-core 7.31.0 → 8.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/XiCMSProvider.js +23 -7
- package/dist/XiCMSProvider.js.map +1 -1
- package/dist/admin/AdminApp.js +175 -93
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/AdminHelp.d.ts +1 -1
- package/dist/admin/AdminHelp.js +44 -36
- package/dist/admin/AdminHelp.js.map +1 -1
- package/dist/admin/CoreVersion.d.ts +6 -3
- package/dist/admin/CoreVersion.js +60 -24
- package/dist/admin/CoreVersion.js.map +1 -1
- package/dist/admin/NewsEditor.d.ts +7 -0
- package/dist/admin/NewsEditor.js +117 -75
- package/dist/admin/NewsEditor.js.map +1 -1
- package/dist/admin/SuperadminPanel.js +1 -1
- package/dist/admin/SuperadminPanel.js.map +1 -1
- package/dist/admin/ZoekPalet.d.ts +15 -0
- package/dist/admin/ZoekPalet.js +123 -0
- package/dist/admin/ZoekPalet.js.map +1 -0
- package/dist/admin/blockEditors/GenericEditor.js +36 -18
- package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
- package/dist/admin/blockEditors/StyleEditor.js +37 -3
- package/dist/admin/blockEditors/StyleEditor.js.map +1 -1
- package/dist/admin/blockEditors/SymbolRefEditor.js +44 -2
- package/dist/admin/blockEditors/SymbolRefEditor.js.map +1 -1
- package/dist/admin/blockEditors/index.js +154 -73
- package/dist/admin/blockEditors/index.js.map +1 -1
- package/dist/admin/customBlocks/CustomBlocksPanel.js +18 -7
- package/dist/admin/customBlocks/CustomBlocksPanel.js.map +1 -1
- package/dist/admin/dashboard/DashboardPanel.js +26 -16
- package/dist/admin/dashboard/DashboardPanel.js.map +1 -1
- package/dist/admin/editors/RichTextEditor.d.ts +4 -3
- package/dist/admin/editors/RichTextEditor.js +19 -60
- package/dist/admin/editors/RichTextEditor.js.map +1 -1
- package/dist/admin/editors/fontSizeExtension.js +7 -1
- package/dist/admin/editors/fontSizeExtension.js.map +1 -1
- package/dist/admin/editors/slashMenu.d.ts +18 -0
- package/dist/admin/editors/slashMenu.js +34 -0
- package/dist/admin/editors/slashMenu.js.map +1 -0
- package/dist/admin/editors/useRijkeTekst.d.ts +48 -0
- package/dist/admin/editors/useRijkeTekst.js +310 -0
- package/dist/admin/editors/useRijkeTekst.js.map +1 -0
- package/dist/admin/forms/FormsPanel.d.ts +11 -2
- package/dist/admin/forms/FormsPanel.js +218 -65
- package/dist/admin/forms/FormsPanel.js.map +1 -1
- package/dist/admin/lib/blokInfo.d.ts +17 -0
- package/dist/admin/lib/blokInfo.js +80 -0
- package/dist/admin/lib/blokInfo.js.map +1 -0
- package/dist/admin/lib/foutNaarMens.d.ts +1 -0
- package/dist/admin/lib/foutNaarMens.js +22 -0
- package/dist/admin/lib/foutNaarMens.js.map +1 -0
- package/dist/admin/lib/gewisteVelden.d.ts +13 -0
- package/dist/admin/lib/gewisteVelden.js +31 -0
- package/dist/admin/lib/gewisteVelden.js.map +1 -0
- package/dist/admin/lib/heroHulp.d.ts +11 -0
- package/dist/admin/lib/heroHulp.js +18 -0
- package/dist/admin/lib/heroHulp.js.map +1 -0
- package/dist/admin/lib/kleur.js +2 -3
- package/dist/admin/lib/kleur.js.map +1 -1
- package/dist/admin/lib/leesbaarheid.d.ts +34 -0
- package/dist/admin/lib/leesbaarheid.js +82 -0
- package/dist/admin/lib/leesbaarheid.js.map +1 -0
- package/dist/admin/lib/lengteAdvies.d.ts +1 -0
- package/dist/admin/lib/lengteAdvies.js +16 -0
- package/dist/admin/lib/lengteAdvies.js.map +1 -0
- package/dist/admin/lib/mediaLijst.d.ts +26 -0
- package/dist/admin/lib/mediaLijst.js +42 -0
- package/dist/admin/lib/mediaLijst.js.map +1 -0
- package/dist/admin/lib/mediaUsage.d.ts +12 -1
- package/dist/admin/lib/mediaUsage.js +43 -33
- package/dist/admin/lib/mediaUsage.js.map +1 -1
- package/dist/admin/lib/menuHulp.d.ts +16 -0
- package/dist/admin/lib/menuHulp.js +41 -0
- package/dist/admin/lib/menuHulp.js.map +1 -0
- package/dist/admin/lib/onopgeslagen.d.ts +10 -0
- package/dist/admin/lib/onopgeslagen.js +35 -0
- package/dist/admin/lib/onopgeslagen.js.map +1 -0
- package/dist/admin/lib/pagesClient.d.ts +55 -2
- package/dist/admin/lib/pagesClient.js +126 -28
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/lib/paginaGebruik.d.ts +56 -0
- package/dist/admin/lib/paginaGebruik.js +109 -0
- package/dist/admin/lib/paginaGebruik.js.map +1 -0
- package/dist/admin/lib/publicatieCheck.d.ts +25 -0
- package/dist/admin/lib/publicatieCheck.js +114 -0
- package/dist/admin/lib/publicatieCheck.js.map +1 -0
- package/dist/admin/lib/scrollNaarDetail.d.ts +6 -0
- package/dist/admin/lib/scrollNaarDetail.js +20 -0
- package/dist/admin/lib/scrollNaarDetail.js.map +1 -0
- package/dist/admin/lib/symbolsClient.d.ts +10 -0
- package/dist/admin/lib/symbolsClient.js +14 -1
- package/dist/admin/lib/symbolsClient.js.map +1 -1
- package/dist/admin/lib/tekstVeld.d.ts +8 -0
- package/dist/admin/lib/tekstVeld.js +10 -0
- package/dist/admin/lib/tekstVeld.js.map +1 -0
- package/dist/admin/lib/useIsSuperadmin.d.ts +2 -0
- package/dist/admin/lib/useIsSuperadmin.js +13 -0
- package/dist/admin/lib/useIsSuperadmin.js.map +1 -0
- package/dist/admin/lib/veldTaal.d.ts +14 -0
- package/dist/admin/lib/veldTaal.js +248 -0
- package/dist/admin/lib/veldTaal.js.map +1 -0
- package/dist/admin/lib/versieVergelijk.d.ts +17 -0
- package/dist/admin/lib/versieVergelijk.js +24 -0
- package/dist/admin/lib/versieVergelijk.js.map +1 -0
- package/dist/admin/lib/zodFields.js +22 -4
- package/dist/admin/lib/zodFields.js.map +1 -1
- package/dist/admin/lib/zodFout.d.ts +9 -0
- package/dist/admin/lib/zodFout.js +63 -0
- package/dist/admin/lib/zodFout.js.map +1 -0
- package/dist/admin/media/MediaManager.js +162 -32
- package/dist/admin/media/MediaManager.js.map +1 -1
- package/dist/admin/media/MediaPicker.d.ts +3 -0
- package/dist/admin/media/MediaPicker.js +84 -31
- package/dist/admin/media/MediaPicker.js.map +1 -1
- package/dist/admin/menu/MenuPanel.js +144 -83
- package/dist/admin/menu/MenuPanel.js.map +1 -1
- package/dist/admin/navContext.d.ts +3 -0
- package/dist/admin/navContext.js +13 -1
- package/dist/admin/navContext.js.map +1 -1
- package/dist/admin/pages/PageEditor.js +146 -30
- package/dist/admin/pages/PageEditor.js.map +1 -1
- package/dist/admin/pages/PagesPanel.js +215 -61
- package/dist/admin/pages/PagesPanel.js.map +1 -1
- package/dist/admin/pages/StarterKiezer.d.ts +12 -0
- package/dist/admin/pages/StarterKiezer.js +113 -0
- package/dist/admin/pages/StarterKiezer.js.map +1 -0
- package/dist/admin/panelRegistry.d.ts +1 -1
- package/dist/admin/panelRegistry.js +2 -1
- package/dist/admin/panelRegistry.js.map +1 -1
- package/dist/admin/patterns/PatternInserter.js +1 -1
- package/dist/admin/patterns/PatternInserter.js.map +1 -1
- package/dist/admin/settings/BedrijfInstellingen.d.ts +20 -0
- package/dist/admin/settings/BedrijfInstellingen.js +159 -0
- package/dist/admin/settings/BedrijfInstellingen.js.map +1 -0
- package/dist/admin/settings/MailTemplates.js +5 -4
- package/dist/admin/settings/MailTemplates.js.map +1 -1
- package/dist/admin/settings/SettingsPanel.js +120 -78
- package/dist/admin/settings/SettingsPanel.js.map +1 -1
- package/dist/admin/settings/SnapshotsCard.js +4 -4
- package/dist/admin/settings/SnapshotsCard.js.map +1 -1
- package/dist/admin/settings/VorigeTerugzetten.d.ts +15 -0
- package/dist/admin/settings/VorigeTerugzetten.js +89 -0
- package/dist/admin/settings/VorigeTerugzetten.js.map +1 -0
- package/dist/admin/shop/CategoriesTab.js +6 -4
- package/dist/admin/shop/CategoriesTab.js.map +1 -1
- package/dist/admin/shop/DiscountsTab.js +4 -2
- package/dist/admin/shop/DiscountsTab.js.map +1 -1
- package/dist/admin/shop/OrdersTab.js +57 -19
- package/dist/admin/shop/OrdersTab.js.map +1 -1
- package/dist/admin/shop/ProductPaneel.js +2 -1
- package/dist/admin/shop/ProductPaneel.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +40 -19
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/ShopPanel.js +13 -1
- package/dist/admin/shop/ShopPanel.js.map +1 -1
- package/dist/admin/shop/ShopSettingsTab.js +56 -17
- package/dist/admin/shop/ShopSettingsTab.js.map +1 -1
- package/dist/admin/shop/productClient.js +7 -3
- package/dist/admin/shop/productClient.js.map +1 -1
- package/dist/admin/stats/NietGevonden.d.ts +10 -0
- package/dist/admin/stats/NietGevonden.js +78 -0
- package/dist/admin/stats/NietGevonden.js.map +1 -0
- package/dist/admin/stats/StatsPanel.js +6 -1
- package/dist/admin/stats/StatsPanel.js.map +1 -1
- package/dist/admin/stats/lib.d.ts +2 -0
- package/dist/admin/stats/lib.js.map +1 -1
- package/dist/admin/ui/ConfirmDialog.js +10 -1
- package/dist/admin/ui/ConfirmDialog.js.map +1 -1
- package/dist/admin/ui/Icon.d.ts +34 -0
- package/dist/admin/ui/Icon.js +44 -0
- package/dist/admin/ui/Icon.js.map +1 -0
- package/dist/admin/ui/ImageField.d.ts +15 -0
- package/dist/admin/ui/ImageField.js +67 -0
- package/dist/admin/ui/ImageField.js.map +1 -0
- package/dist/admin/ui/KleurVeld.d.ts +25 -0
- package/dist/admin/ui/KleurVeld.js +76 -0
- package/dist/admin/ui/KleurVeld.js.map +1 -0
- package/dist/admin/ui/LaadStaat.d.ts +21 -0
- package/dist/admin/ui/LaadStaat.js +43 -0
- package/dist/admin/ui/LaadStaat.js.map +1 -0
- package/dist/admin/ui/LeesbaarheidPaneel.d.ts +10 -0
- package/dist/admin/ui/LeesbaarheidPaneel.js +68 -0
- package/dist/admin/ui/LeesbaarheidPaneel.js.map +1 -0
- package/dist/admin/ui/LinkDialog.d.ts +22 -0
- package/dist/admin/ui/LinkDialog.js +131 -0
- package/dist/admin/ui/LinkDialog.js.map +1 -0
- package/dist/admin/ui/LinkVeld.d.ts +14 -0
- package/dist/admin/ui/LinkVeld.js +49 -0
- package/dist/admin/ui/LinkVeld.js.map +1 -0
- package/dist/admin/ui/PreviewLinkResultaat.d.ts +14 -0
- package/dist/admin/ui/PreviewLinkResultaat.js +52 -0
- package/dist/admin/ui/PreviewLinkResultaat.js.map +1 -0
- package/dist/admin/ui/charts.js +5 -5
- package/dist/admin/ui/charts.js.map +1 -1
- package/dist/admin/ui/primitives.d.ts +16 -6
- package/dist/admin/ui/primitives.js +98 -26
- package/dist/admin/ui/primitives.js.map +1 -1
- package/dist/admin/ui/select.js +5 -2
- package/dist/admin/ui/select.js.map +1 -1
- package/dist/admin/ui/switch.d.ts +3 -1
- package/dist/admin/ui/switch.js +6 -3
- package/dist/admin/ui/switch.js.map +1 -1
- package/dist/admin/ui/toast.d.ts +2 -1
- package/dist/admin/ui/toast.js +17 -11
- package/dist/admin/ui/toast.js.map +1 -1
- package/dist/admin/visual/BlokKiezer.d.ts +34 -0
- package/dist/admin/visual/BlokKiezer.js +225 -0
- package/dist/admin/visual/BlokKiezer.js.map +1 -0
- package/dist/admin/visual/InlineGereedschap.d.ts +52 -0
- package/dist/admin/visual/InlineGereedschap.js +356 -0
- package/dist/admin/visual/InlineGereedschap.js.map +1 -0
- package/dist/admin/visual/InlineRijkeTekst.d.ts +23 -0
- package/dist/admin/visual/InlineRijkeTekst.js +152 -0
- package/dist/admin/visual/InlineRijkeTekst.js.map +1 -0
- package/dist/admin/visual/VisualEditor.js +273 -125
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin.css +1 -1
- package/dist/auth/AuthProvider.js +1 -0
- package/dist/auth/AuthProvider.js.map +1 -1
- package/dist/bedrijf/bedrijf.d.ts +44 -0
- package/dist/bedrijf/bedrijf.js +125 -0
- package/dist/bedrijf/bedrijf.js.map +1 -0
- package/dist/blocks/BlockRenderer.js +11 -1
- package/dist/blocks/BlockRenderer.js.map +1 -1
- package/dist/blocks/blockStyle.d.ts +7 -0
- package/dist/blocks/blockStyle.js +16 -3
- package/dist/blocks/blockStyle.js.map +1 -1
- package/dist/blocks/builtinSchemas.js +6 -0
- package/dist/blocks/builtinSchemas.js.map +1 -1
- package/dist/blocks/builtins/AgendaRender.d.ts +22 -0
- package/dist/blocks/builtins/AgendaRender.js +69 -9
- package/dist/blocks/builtins/AgendaRender.js.map +1 -1
- package/dist/blocks/builtins/Availability.d.ts +1 -0
- package/dist/blocks/builtins/Availability.js +23 -0
- package/dist/blocks/builtins/Availability.js.map +1 -0
- package/dist/blocks/builtins/AvailabilityRender.d.ts +12 -0
- package/dist/blocks/builtins/AvailabilityRender.js +105 -0
- package/dist/blocks/builtins/AvailabilityRender.js.map +1 -0
- package/dist/blocks/builtins/BeforeAfterRender.js +4 -3
- package/dist/blocks/builtins/BeforeAfterRender.js.map +1 -1
- package/dist/blocks/builtins/Booking.js +1 -1
- package/dist/blocks/builtins/Booking.js.map +1 -1
- package/dist/blocks/builtins/BreadcrumbsRender.d.ts +4 -3
- package/dist/blocks/builtins/BreadcrumbsRender.js +14 -8
- package/dist/blocks/builtins/BreadcrumbsRender.js.map +1 -1
- package/dist/blocks/builtins/ColumnsRender.d.ts +1 -1
- package/dist/blocks/builtins/ColumnsRender.js +3 -1
- package/dist/blocks/builtins/ColumnsRender.js.map +1 -1
- package/dist/blocks/builtins/Contact.js +1 -1
- package/dist/blocks/builtins/Contact.js.map +1 -1
- package/dist/blocks/builtins/ContactRender.d.ts +1 -1
- package/dist/blocks/builtins/ContactRender.js +37 -8
- package/dist/blocks/builtins/ContactRender.js.map +1 -1
- package/dist/blocks/builtins/Cta.js +1 -1
- package/dist/blocks/builtins/Cta.js.map +1 -1
- package/dist/blocks/builtins/CtaRender.d.ts +1 -1
- package/dist/blocks/builtins/CtaRender.js +5 -4
- package/dist/blocks/builtins/CtaRender.js.map +1 -1
- package/dist/blocks/builtins/Embed.js +1 -1
- package/dist/blocks/builtins/Embed.js.map +1 -1
- package/dist/blocks/builtins/FormBlock.js +1 -0
- package/dist/blocks/builtins/FormBlock.js.map +1 -1
- package/dist/blocks/builtins/FormBlockRender.d.ts +6 -0
- package/dist/blocks/builtins/FormBlockRender.js +88 -16
- package/dist/blocks/builtins/FormBlockRender.js.map +1 -1
- package/dist/blocks/builtins/GalleryRender.js +3 -2
- package/dist/blocks/builtins/GalleryRender.js.map +1 -1
- package/dist/blocks/builtins/Hero.js +1 -1
- package/dist/blocks/builtins/Hero.js.map +1 -1
- package/dist/blocks/builtins/HeroRender.js +7 -3
- package/dist/blocks/builtins/HeroRender.js.map +1 -1
- package/dist/blocks/builtins/OpenNowBadge.d.ts +4 -2
- package/dist/blocks/builtins/OpenNowBadge.js +6 -6
- package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
- package/dist/blocks/builtins/OpeningHoursRender.d.ts +8 -2
- package/dist/blocks/builtins/OpeningHoursRender.js +27 -6
- package/dist/blocks/builtins/OpeningHoursRender.js.map +1 -1
- package/dist/blocks/builtins/PriceList.d.ts +1 -0
- package/dist/blocks/builtins/PriceList.js +25 -0
- package/dist/blocks/builtins/PriceList.js.map +1 -0
- package/dist/blocks/builtins/PriceListRender.d.ts +17 -0
- package/dist/blocks/builtins/PriceListRender.js +87 -0
- package/dist/blocks/builtins/PriceListRender.js.map +1 -0
- package/dist/blocks/builtins/RichTextBlockRender.js +6 -1
- package/dist/blocks/builtins/RichTextBlockRender.js.map +1 -1
- package/dist/blocks/builtins/SectionHeading.d.ts +1 -0
- package/dist/blocks/builtins/SectionHeading.js +14 -0
- package/dist/blocks/builtins/SectionHeading.js.map +1 -0
- package/dist/blocks/builtins/SectionHeadingRender.d.ts +7 -0
- package/dist/blocks/builtins/SectionHeadingRender.js +14 -0
- package/dist/blocks/builtins/SectionHeadingRender.js.map +1 -0
- package/dist/blocks/builtins/SliderRender.js +3 -2
- package/dist/blocks/builtins/SliderRender.js.map +1 -1
- package/dist/blocks/builtins/SymbolRef.js +1 -1
- package/dist/blocks/builtins/SymbolRef.js.map +1 -1
- package/dist/blocks/builtins/TeamRender.js +6 -1
- package/dist/blocks/builtins/TeamRender.js.map +1 -1
- package/dist/blocks/builtins/Testimonials.js +3 -1
- package/dist/blocks/builtins/Testimonials.js.map +1 -1
- package/dist/blocks/builtins/TestimonialsRender.js +4 -0
- package/dist/blocks/builtins/TestimonialsRender.js.map +1 -1
- package/dist/blocks/builtins/WhatsappChatRender.d.ts +1 -1
- package/dist/blocks/builtins/WhatsappChatRender.js +3 -1
- package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -1
- package/dist/blocks/builtins/Widgets.js +2 -2
- package/dist/blocks/builtins/Widgets.js.map +1 -1
- package/dist/blocks/builtins/WidgetsRender.js +3 -1
- package/dist/blocks/builtins/WidgetsRender.js.map +1 -1
- package/dist/blocks/builtins/consentEmbed.js +26 -9
- package/dist/blocks/builtins/consentEmbed.js.map +1 -1
- package/dist/blocks/builtins/index.d.ts +3 -0
- package/dist/blocks/builtins/index.js +6 -0
- package/dist/blocks/builtins/index.js.map +1 -1
- package/dist/blocks/builtins/lib/autoComplete.d.ts +7 -0
- package/dist/blocks/builtins/lib/autoComplete.js +20 -0
- package/dist/blocks/builtins/lib/autoComplete.js.map +1 -0
- package/dist/blocks/builtins/lib/bookingUrl.d.ts +2 -2
- package/dist/blocks/builtins/lib/bookingUrl.js +1 -1
- package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -1
- package/dist/blocks/builtins/lib/ical.d.ts +24 -0
- package/dist/blocks/builtins/lib/ical.js +82 -0
- package/dist/blocks/builtins/lib/ical.js.map +1 -0
- package/dist/blocks/builtins/lib/vandaag.d.ts +8 -0
- package/dist/blocks/builtins/lib/vandaag.js +22 -0
- package/dist/blocks/builtins/lib/vandaag.js.map +1 -0
- package/dist/blocks/registry.d.ts +6 -0
- package/dist/blocks/registry.js.map +1 -1
- package/dist/blocks/ui/index.js +1 -0
- package/dist/blocks/ui/index.js.map +1 -1
- package/dist/components/EditBar.js +2 -4
- package/dist/components/EditBar.js.map +1 -1
- package/dist/components/News.js +26 -18
- package/dist/components/News.js.map +1 -1
- package/dist/components/PageView.d.ts +7 -1
- package/dist/components/PageView.js +17 -0
- package/dist/components/PageView.js.map +1 -1
- package/dist/components/ResponsiveImage.js +18 -20
- package/dist/components/ResponsiveImage.js.map +1 -1
- package/dist/components/onderOffset.d.ts +13 -0
- package/dist/components/onderOffset.js +74 -0
- package/dist/components/onderOffset.js.map +1 -0
- package/dist/components/site/Bedrijfsnummers.d.ts +9 -0
- package/dist/components/site/Bedrijfsnummers.js +12 -0
- package/dist/components/site/Bedrijfsnummers.js.map +1 -0
- package/dist/components/site/SiteFooter.d.ts +6 -1
- package/dist/components/site/SiteFooter.js +62 -9
- package/dist/components/site/SiteFooter.js.map +1 -1
- package/dist/components/site/SiteHeader.d.ts +15 -0
- package/dist/components/site/SiteHeader.js +70 -0
- package/dist/components/site/SiteHeader.js.map +1 -0
- package/dist/components/site/SiteNav.d.ts +2 -0
- package/dist/components/site/SiteNav.js +29 -5
- package/dist/components/site/SiteNav.js.map +1 -1
- package/dist/demo/DemoStudio.js +2 -2
- package/dist/demo/DemoStudio.js.map +1 -1
- package/dist/demo/demoStore.d.ts +13 -0
- package/dist/demo/demoStore.js +36 -0
- package/dist/demo/demoStore.js.map +1 -1
- package/dist/editor/EditorProvider.js +15 -4
- package/dist/editor/EditorProvider.js.map +1 -1
- package/dist/firebase/functionsClient.js +5 -1
- package/dist/firebase/functionsClient.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/media/ImageContext.d.ts +17 -0
- package/dist/media/ImageContext.js +65 -20
- package/dist/media/ImageContext.js.map +1 -1
- package/dist/media/altInHtml.d.ts +9 -0
- package/dist/media/altInHtml.js +17 -0
- package/dist/media/altInHtml.js.map +1 -0
- package/dist/media/srcset.d.ts +12 -0
- package/dist/media/srcset.js +13 -0
- package/dist/media/srcset.js.map +1 -0
- package/dist/modules/builtins.d.ts +1 -0
- package/dist/modules/builtins.js +22 -9
- package/dist/modules/builtins.js.map +1 -1
- package/dist/modules/consent/ConsentBanner.d.ts +5 -0
- package/dist/modules/consent/ConsentBanner.js +8 -7
- package/dist/modules/consent/ConsentBanner.js.map +1 -1
- package/dist/modules/consent/standaard.d.ts +12 -0
- package/dist/modules/consent/standaard.js +10 -0
- package/dist/modules/consent/standaard.js.map +1 -0
- package/dist/modules/consent/useConsent.d.ts +4 -0
- package/dist/modules/consent/useConsent.js +30 -1
- package/dist/modules/consent/useConsent.js.map +1 -1
- package/dist/modules/shop/blocks/Cart.js +5 -3
- package/dist/modules/shop/blocks/Cart.js.map +1 -1
- package/dist/modules/shop/blocks/Checkout.js +56 -35
- package/dist/modules/shop/blocks/Checkout.js.map +1 -1
- package/dist/modules/shop/blocks/Donation.js +4 -4
- package/dist/modules/shop/blocks/Donation.js.map +1 -1
- package/dist/modules/shop/blocks/ProductDetail.js +19 -3
- package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
- package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -1
- package/dist/modules/shop/blocks/ProductGrid.js +8 -4
- package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
- package/dist/modules/shop/blocks/ShopCategories.js +4 -3
- package/dist/modules/shop/blocks/ShopCategories.js.map +1 -1
- package/dist/modules/shop/lib.d.ts +4 -0
- package/dist/modules/shop/lib.js +14 -0
- package/dist/modules/shop/lib.js.map +1 -1
- package/dist/modules/shop/settings.d.ts +3 -0
- package/dist/modules/shop/settings.js +14 -0
- package/dist/modules/shop/settings.js.map +1 -1
- package/dist/modules/shop/strings.d.ts +4 -0
- package/dist/modules/shop/strings.js +8 -0
- package/dist/modules/shop/strings.js.map +1 -1
- package/dist/next/envCheck.d.ts +42 -0
- package/dist/next/envCheck.js +106 -0
- package/dist/next/envCheck.js.map +1 -0
- package/dist/next/index.cjs +185 -2
- package/dist/next/index.d.ts +2 -0
- package/dist/next/index.js +8 -1
- package/dist/next/index.js.map +1 -1
- package/dist/next/securityHeaders.d.ts +36 -0
- package/dist/next/securityHeaders.js +74 -0
- package/dist/next/securityHeaders.js.map +1 -0
- package/dist/patterns/builtins.d.ts +7 -0
- package/dist/patterns/builtins.js +138 -3
- package/dist/patterns/builtins.js.map +1 -1
- package/dist/releaseNotes.js +148 -0
- package/dist/releaseNotes.js.map +1 -1
- package/dist/render/PublishedPage.d.ts +2 -1
- package/dist/render/PublishedPage.js +10 -4
- package/dist/render/PublishedPage.js.map +1 -1
- package/dist/schema/blocks.d.ts +473 -0
- package/dist/schema/blocks.js +105 -5
- package/dist/schema/blocks.js.map +1 -1
- package/dist/schema/business.d.ts +118 -0
- package/dist/schema/business.js +75 -0
- package/dist/schema/business.js.map +1 -0
- package/dist/schema/fields.d.ts +2 -1
- package/dist/schema/fields.js.map +1 -1
- package/dist/schema/forms.d.ts +18 -0
- package/dist/schema/forms.js +18 -3
- package/dist/schema/forms.js.map +1 -1
- package/dist/schema/mail.d.ts +10 -0
- package/dist/schema/mail.js +7 -0
- package/dist/schema/mail.js.map +1 -1
- package/dist/schema/settings.d.ts +103 -0
- package/dist/schema/settings.js +24 -3
- package/dist/schema/settings.js.map +1 -1
- package/dist/schema/shop.d.ts +2 -0
- package/dist/schema/shop.js +4 -0
- package/dist/schema/shop.js.map +1 -1
- package/dist/schema/shopGeld.d.ts +1 -0
- package/dist/schema/shopGeld.js +1 -0
- package/dist/schema/shopGeld.js.map +1 -1
- package/dist/schema/siteConfig.d.ts +69 -1
- package/dist/schema/siteConfig.js +7 -2
- package/dist/schema/siteConfig.js.map +1 -1
- package/dist/server/blockData.d.ts +1 -1
- package/dist/server/blockData.js +74 -18
- package/dist/server/blockData.js.map +1 -1
- package/dist/server/cspRapport.d.ts +20 -0
- package/dist/server/cspRapport.js +56 -0
- package/dist/server/cspRapport.js.map +1 -0
- package/dist/server/fonts.d.ts +3 -0
- package/dist/server/fonts.js +39 -0
- package/dist/server/fonts.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +8 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/sanitize.js +2 -0
- package/dist/server/sanitize.js.map +1 -1
- package/dist/settings/useSiteSettings.js +4 -1
- package/dist/settings/useSiteSettings.js.map +1 -1
- package/dist/starters/starters.d.ts +69 -0
- package/dist/starters/starters.js +171 -0
- package/dist/starters/starters.js.map +1 -0
- package/dist/styles.css +1181 -212
- package/dist/theme/color.d.ts +10 -0
- package/dist/theme/color.js +17 -1
- package/dist/theme/color.js.map +1 -1
- package/dist/theme/fontFallback.d.ts +23 -0
- package/dist/theme/fontFallback.js +58 -0
- package/dist/theme/fontFallback.js.map +1 -0
- package/dist/theme/fonts.d.ts +11 -0
- package/dist/theme/fonts.js +16 -0
- package/dist/theme/fonts.js.map +1 -0
- package/dist/version.js +1 -1
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/editors/RichTextEditor.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { TableKit } from \"@tiptap/extension-table\";\nimport Link from \"@tiptap/extension-link\";\nimport Image from \"@tiptap/extension-image\";\nimport DOMPurify from \"dompurify\";\nimport { useCallback, useEffect, type ReactNode } from \"react\";\nimport { cn } from \"../ui/cn\";\nimport { MediaPickerButton } from \"../media/MediaPicker\";\nimport { FontSize, FONT_SIZES } from \"./fontSizeExtension\";\n\n/**\n * WYSIWYG-editor (TipTap) met opmaak én afbeeldingen. Output wordt client-side met DOMPurify\n * gesanitized voor preview/opslag; de server sanitized nogmaals bij publiceren (server/sanitize.ts)\n * — nooit op de client vertrouwen. Geef `siteId` mee om afbeeldingen te kunnen uploaden/invoegen.\n */\nexport function RichTextEditor({\n value,\n onChange,\n siteId,\n}: {\n value: string;\n onChange: (html: string) => void;\n siteId?: string;\n}) {\n\n const editor = useEditor({\n immediatelyRender: false,\n extensions: [\n // Tiptap 3 bundelt Link en Underline IN StarterKit. Los toevoegen geeft dubbele\n // extensienamen (en stille gedragsfouten), dus: Underline komt uit StarterKit, en zijn Link\n // zetten we uit omdat we die hieronder zelf configureren (geen open-on-click, eigen rel).\n StarterKit.configure({ heading: { levels: [2, 3, 4] }, link: false }),\n FontSize,\n // Zonder deze extensie gooit TipTap een <table> bij het inladen weg (onbekende node) — een\n // tabel uit de seed of een import overleefde dan geen enkele bewerking van dat blok.\n TableKit.configure({ table: { resizable: false } }),\n Link.configure({ openOnClick: false, autolink: true, HTMLAttributes: { rel: \"noopener noreferrer nofollow\" } }),\n Image.configure({ inline: false, allowBase64: false, HTMLAttributes: { loading: \"lazy\", decoding: \"async\" } }),\n ],\n content: value || \"<p></p>\",\n onUpdate: ({ editor }) => onChange(DOMPurify.sanitize(editor.getHTML())),\n editorProps: {\n attributes: { class: \"min-h-[8rem] rounded-b-lg border border-t-0 border-slate-300 bg-white p-3 text-sm\" },\n },\n });\n\n // Externe waarde-wijzigingen (undo/redo in de VisualEditor, taal-wissel in het vertaalpaneel)\n // in de editor doorvoeren: TipTap leest `content` alleen bij init. Zonder deze sync blijft de\n // zichtbare HTML stale terwijl de opgeslagen waarde al veranderd is — data-integriteitsval.\n // Niet syncen terwijl de gebruiker zelf typt (editor.isFocused) — dan is de editor de bron.\n useEffect(() => {\n if (!editor || editor.isFocused) return;\n const current = editor.getHTML();\n const next = value || \"<p></p>\";\n // Tiptap 3: de tweede parameter is een options-object i.p.v. een boolean.\n if (current !== next) editor.commands.setContent(next, { emitUpdate: false });\n }, [value, editor]);\n\n const setLink = useCallback(() => {\n if (!editor) return;\n const prev = editor.getAttributes(\"link\").href as string | undefined;\n const url = window.prompt(\"Link-URL (leeg = verwijderen)\", prev ?? \"https://\");\n if (url === null) return;\n if (url === \"\") editor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n else editor.chain().focus().extendMarkRange(\"link\").setLink({ href: url }).run();\n }, [editor]);\n\n const insertImageByUrl = useCallback(() => {\n if (!editor) return;\n const url = window.prompt(\"Afbeeldings-URL (https)\");\n if (!url) return;\n const alt = window.prompt(\"Beschrijving (alt-tekst, voor toegankelijkheid)\") ?? \"\";\n editor.chain().focus().setImage({ src: url, alt }).run();\n }, [editor]);\n\n const setFontSize = useCallback(\n (value: string) => {\n if (!editor) return;\n editor.chain().focus().setMark(\"textStyle\", { fontSize: value || null }).run();\n },\n [editor],\n );\n\n if (!editor) return null;\n\n const Btn = ({ on, active, label, title }: { on: () => void; active?: boolean; label: ReactNode; title?: string }) => (\n <button\n type=\"button\"\n title={title}\n onMouseDown={(e) => e.preventDefault()}\n onClick={on}\n className={cn(\n \"h-8 min-w-8 rounded px-2 text-sm hover:bg-slate-100\",\n active && \"bg-slate-200 font-semibold\",\n )}\n >\n {label}\n </button>\n );\n\n return (\n <div>\n <div className=\"flex flex-wrap gap-1 rounded-t-lg border border-slate-300 bg-slate-50 p-1\">\n <Btn on={() => editor.chain().focus().toggleBold().run()} active={editor.isActive(\"bold\")} label=\"B\" title=\"Vet\" />\n <Btn on={() => editor.chain().focus().toggleItalic().run()} active={editor.isActive(\"italic\")} label=\"I\" title=\"Cursief\" />\n <Btn on={() => editor.chain().focus().toggleUnderline().run()} active={editor.isActive(\"underline\")} label={<span style={{ textDecoration: \"underline\" }}>U</span>} title=\"Onderstrepen\" />\n <Btn on={() => editor.chain().focus().toggleStrike().run()} active={editor.isActive(\"strike\")} label={<span style={{ textDecoration: \"line-through\" }}>S</span>} title=\"Doorstrepen\" />\n <select\n className=\"h-8 rounded border border-slate-300 bg-white px-1 text-sm\"\n value={(editor.getAttributes(\"textStyle\").fontSize as string | undefined) ?? \"\"}\n onChange={(e) => setFontSize(e.target.value)}\n title=\"Lettergrootte\"\n aria-label=\"Lettergrootte\"\n >\n {FONT_SIZES.map((s) => (\n <option key={s.label} value={s.value}>\n {s.label}\n </option>\n ))}\n </select>\n <Btn on={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} active={editor.isActive(\"heading\", { level: 2 })} label=\"H2\" title=\"Kop\" />\n <Btn on={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} active={editor.isActive(\"heading\", { level: 3 })} label=\"H3\" title=\"Subkop\" />\n <Btn on={() => editor.chain().focus().toggleBulletList().run()} active={editor.isActive(\"bulletList\")} label=\"• Lijst\" title=\"Opsomming\" />\n <Btn on={() => editor.chain().focus().toggleOrderedList().run()} active={editor.isActive(\"orderedList\")} label=\"1. Lijst\" title=\"Genummerde lijst\" />\n <Btn on={() => editor.chain().focus().toggleBlockquote().run()} active={editor.isActive(\"blockquote\")} label=\"❝\" title=\"Citaat\" />\n <Btn on={setLink} active={editor.isActive(\"link\")} label=\"🔗\" title=\"Link\" />\n {siteId && (\n <MediaPickerButton\n siteId={siteId}\n label=\"🖼 Afbeelding\"\n variant=\"ghost\"\n onSelect={(m) => editor.chain().focus().setImage({ src: m.url, alt: m.alt ?? \"\" }).run()}\n />\n )}\n <Btn on={insertImageByUrl} label=\"🖼 URL\" title=\"Externe afbeelding via URL invoegen\" />\n </div>\n <EditorContent editor={editor} />\n </div>\n );\n}\n"],"mappings":"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","names":["editor","value"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/editors/RichTextEditor.tsx"],"sourcesContent":["\"use client\";\n\nimport { EditorContent } from \"@tiptap/react\";\nimport { useCallback, useEffect } from \"react\";\nimport { MediaPickerButton } from \"../media/MediaPicker\";\nimport { FONT_SIZES } from \"./fontSizeExtension\";\nimport { RteKnop as Btn, useRijkeTekst, zonderLegeStaart } from \"./useRijkeTekst\";\n\n/**\n * WYSIWYG-editor (TipTap) met opmaak én afbeeldingen — de jas voor het beheer, met vaste werkbalk.\n * De kern (extensies, slash-menu, selectie-werkbalk, foto's plakken/slepen, dialogen) zit in\n * `useRijkeTekst` en is dezelfde als bij bewerken op de site (`InlineRijkeTekst`).\n * Geef `siteId` mee om afbeeldingen te kunnen uploaden/invoegen.\n */\nexport function RichTextEditor({\n value,\n onChange,\n siteId,\n}: {\n value: string;\n onChange: (html: string) => void;\n siteId?: string;\n}) {\n const { editor, overlays, openLink: setLink } = useRijkeTekst({\n value,\n onChange,\n siteId,\n editorClass: \"xicms-richtext-editor min-h-[8rem] rounded-b-lg border border-t-0 border-slate-300 bg-white p-3 text-sm\",\n });\n\n // Externe waarde-wijzigingen (undo/redo in de VisualEditor, taal-wissel in het vertaalpaneel)\n // in de editor doorvoeren: TipTap leest `content` alleen bij init. Zonder deze sync blijft de\n // zichtbare HTML stale terwijl de opgeslagen waarde al veranderd is — data-integriteitsval.\n // Niet syncen terwijl de gebruiker zelf typt (editor.isFocused) — dan is de editor de bron.\n useEffect(() => {\n if (!editor || editor.isFocused) return;\n // Vergelijk zonder de lege staart-alinea van TipTap, anders lijkt alles altijd anders.\n const current = zonderLegeStaart(editor.getHTML()) || \"<p></p>\";\n const next = value || \"<p></p>\";\n // Tiptap 3: de tweede parameter is een options-object i.p.v. een boolean.\n if (current !== next) editor.commands.setContent(next, { emitUpdate: false });\n }, [value, editor]);\n\n const setFontSize = useCallback(\n (value: string) => {\n if (!editor) return;\n editor.chain().focus().setMark(\"textStyle\", { fontSize: value || null }).run();\n },\n [editor],\n );\n\n if (!editor) return null;\n\n return (\n <div>\n <div className=\"flex flex-wrap gap-1 rounded-t-lg border border-slate-300 bg-slate-50 p-1\">\n <Btn on={() => editor.chain().focus().toggleBold().run()} active={editor.isActive(\"bold\")} label=\"B\" title=\"Vet\" />\n <Btn on={() => editor.chain().focus().toggleItalic().run()} active={editor.isActive(\"italic\")} label=\"I\" title=\"Cursief\" />\n <Btn on={() => editor.chain().focus().toggleUnderline().run()} active={editor.isActive(\"underline\")} label={<span style={{ textDecoration: \"underline\" }}>U</span>} title=\"Onderstrepen\" />\n <Btn on={() => editor.chain().focus().toggleStrike().run()} active={editor.isActive(\"strike\")} label={<span style={{ textDecoration: \"line-through\" }}>S</span>} title=\"Doorstrepen\" />\n <select\n className=\"h-8 rounded border border-slate-300 bg-white px-1 text-sm\"\n value={(editor.getAttributes(\"textStyle\").fontSize as string | undefined) ?? \"\"}\n onChange={(e) => setFontSize(e.target.value)}\n title=\"Lettergrootte\"\n aria-label=\"Lettergrootte\"\n >\n {FONT_SIZES.map((s) => (\n <option key={s.label} value={s.value}>\n {s.label}\n </option>\n ))}\n </select>\n <Btn on={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} active={editor.isActive(\"heading\", { level: 2 })} label=\"H2\" title=\"Kop\" />\n <Btn on={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} active={editor.isActive(\"heading\", { level: 3 })} label=\"H3\" title=\"Subkop\" />\n <Btn on={() => editor.chain().focus().toggleBulletList().run()} active={editor.isActive(\"bulletList\")} label=\"• Lijst\" title=\"Opsomming\" />\n <Btn on={() => editor.chain().focus().toggleOrderedList().run()} active={editor.isActive(\"orderedList\")} label=\"1. Lijst\" title=\"Genummerde lijst\" />\n <Btn on={() => editor.chain().focus().toggleBlockquote().run()} active={editor.isActive(\"blockquote\")} label=\"❝\" title=\"Citaat\" />\n <Btn on={setLink} active={editor.isActive(\"link\")} label=\"🔗\" title=\"Link\" />\n {siteId && (\n <MediaPickerButton\n siteId={siteId}\n label=\"🖼 Afbeelding\"\n variant=\"ghost\"\n onSelect={(m) => editor.chain().focus().setImage({ src: m.url, alt: m.alt ?? \"\" }).run()}\n />\n )}\n </div>\n <EditorContent editor={editor} />\n <p className=\"mt-1 text-xs text-slate-500\">Tip: typ <kbd className=\"rounded border border-slate-300 px-1\">/</kbd> voor koppen, lijsten, tabellen{siteId ? \" en foto's — of sleep een foto erin\" : \"\"}.</p>\n {overlays}\n </div>\n );\n}\n"],"mappings":";AAuDM,SACE,KADF;AArDN,SAAS,qBAAqB;AAC9B,SAAS,aAAa,iBAAiB;AACvC,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;AAC3B,SAAS,WAAW,KAAK,eAAe,wBAAwB;AAQzD,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,EAAE,QAAQ,UAAU,UAAU,QAAQ,IAAI,cAAc;AAAA,IAC5D;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf,CAAC;AAMD,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,UAAW;AAEjC,UAAM,UAAU,iBAAiB,OAAO,QAAQ,CAAC,KAAK;AACtD,UAAM,OAAO,SAAS;AAEtB,QAAI,YAAY,KAAM,QAAO,SAAS,WAAW,MAAM,EAAE,YAAY,MAAM,CAAC;AAAA,EAC9E,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,QAAM,cAAc;AAAA,IAClB,CAACA,WAAkB;AACjB,UAAI,CAAC,OAAQ;AACb,aAAO,MAAM,EAAE,MAAM,EAAE,QAAQ,aAAa,EAAE,UAAUA,UAAS,KAAK,CAAC,EAAE,IAAI;AAAA,IAC/E;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,MAAI,CAAC,OAAQ,QAAO;AAEpB,SACE,qBAAC,SACC;AAAA,yBAAC,SAAI,WAAU,6EACb;AAAA,0BAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,MAAM,GAAG,OAAM,KAAI,OAAM,OAAM;AAAA,MACjH,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,QAAQ,GAAG,OAAM,KAAI,OAAM,WAAU;AAAA,MACzH,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,WAAW,GAAG,OAAO,oBAAC,UAAK,OAAO,EAAE,gBAAgB,YAAY,GAAG,eAAC,GAAS,OAAM,gBAAe;AAAA,MACzL,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,QAAQ,GAAG,OAAO,oBAAC,UAAK,OAAO,EAAE,gBAAgB,eAAe,GAAG,eAAC,GAAS,OAAM,eAAc;AAAA,MACrL;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAQ,OAAO,cAAc,WAAW,EAAE,YAAmC;AAAA,UAC7E,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,UAC3C,OAAM;AAAA,UACN,cAAW;AAAA,UAEV,qBAAW,IAAI,CAAC,MACf,oBAAC,YAAqB,OAAO,EAAE,OAC5B,YAAE,SADQ,EAAE,KAEf,CACD;AAAA;AAAA,MACH;AAAA,MACA,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,OAAM,MAAK,OAAM,OAAM;AAAA,MAClJ,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,OAAM,MAAK,OAAM,UAAS;AAAA,MACrJ,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,YAAY,GAAG,OAAM,gBAAU,OAAM,aAAY;AAAA,MACzI,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,aAAa,GAAG,OAAM,YAAW,OAAM,oBAAmB;AAAA,MACnJ,oBAAC,OAAI,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,GAAG,QAAQ,OAAO,SAAS,YAAY,GAAG,OAAM,UAAI,OAAM,UAAS;AAAA,MAChI,oBAAC,OAAI,IAAI,SAAS,QAAQ,OAAO,SAAS,MAAM,GAAG,OAAM,aAAK,OAAM,QAAO;AAAA,MAC1E,UACC;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAM;AAAA,UACN,SAAQ;AAAA,UACR,UAAU,CAAC,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,IAAI;AAAA;AAAA,MACzF;AAAA,OAEJ;AAAA,IACA,oBAAC,iBAAc,QAAgB;AAAA,IAC/B,qBAAC,OAAE,WAAU,+BAA8B;AAAA;AAAA,MAAS,oBAAC,SAAI,WAAU,wCAAuC,eAAC;AAAA,MAAM;AAAA,MAAgC,SAAS,6CAAwC;AAAA,MAAG;AAAA,OAAC;AAAA,IACrM;AAAA,KACH;AAEJ;","names":["value"]}
|
|
@@ -11,7 +11,13 @@ const FontSize = TextStyle.extend({
|
|
|
11
11
|
...this.parent?.(),
|
|
12
12
|
fontSize: {
|
|
13
13
|
default: null,
|
|
14
|
-
|
|
14
|
+
// 8.10: alleen de presets. Geplakt uit Word/Docs ("11pt", "36px") bleef anders in de
|
|
15
|
+
// werkversie staan, terwijl de server het bij publiceren weghaalt — de editor toonde dan
|
|
16
|
+
// iets anders dan wat live kwam, en de klant dacht dat publiceren de opmaak "sloopte".
|
|
17
|
+
parseHTML: (element) => {
|
|
18
|
+
const v = element.style.fontSize;
|
|
19
|
+
return FONT_SIZES.some((f) => f.value && f.value === v) ? v : null;
|
|
20
|
+
},
|
|
15
21
|
renderHTML: (attributes) => {
|
|
16
22
|
if (!attributes.fontSize) return {};
|
|
17
23
|
return { style: `font-size: ${attributes.fontSize}` };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/editors/fontSizeExtension.ts"],"sourcesContent":["// Tiptap 3: extension-text-style exporteert geen default meer.\nimport { TextStyle } from \"@tiptap/extension-text-style\";\n\n/**\n * Vaste, veilige preset-set — geen vrij invoerveld. `sanitizeRichText` (server/sanitize.ts en de\n * spiegel-kopie in functions/src/lib/sanitize.ts) staat via `allowedStyles` UITSLUITEND exact deze\n * `font-size`-waarden toe; een los tekstveld voor lettergrootte zou een CSS-allowlist met een brede\n * regex vergen (groter injectie-oppervlak) voor weinig extra waarde t.o.v. een paar consistente maten.\n */\nexport const FONT_SIZES = [\n { label: \"Klein\", value: \"0.85rem\" },\n { label: \"Normaal\", value: \"\" },\n { label: \"Groot\", value: \"1.25rem\" },\n { label: \"Extra groot\", value: \"1.5rem\" },\n] as const;\n\n/** `textStyle`-mark (Tiptap) uitgebreid met een `fontSize`-attribuut → `<span style=\"font-size:...\">`. */\nexport const FontSize = TextStyle.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n fontSize: {\n default: null,\n parseHTML: (element: HTMLElement) => element.style.fontSize
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/editors/fontSizeExtension.ts"],"sourcesContent":["// Tiptap 3: extension-text-style exporteert geen default meer.\nimport { TextStyle } from \"@tiptap/extension-text-style\";\n\n/**\n * Vaste, veilige preset-set — geen vrij invoerveld. `sanitizeRichText` (server/sanitize.ts en de\n * spiegel-kopie in functions/src/lib/sanitize.ts) staat via `allowedStyles` UITSLUITEND exact deze\n * `font-size`-waarden toe; een los tekstveld voor lettergrootte zou een CSS-allowlist met een brede\n * regex vergen (groter injectie-oppervlak) voor weinig extra waarde t.o.v. een paar consistente maten.\n */\nexport const FONT_SIZES = [\n { label: \"Klein\", value: \"0.85rem\" },\n { label: \"Normaal\", value: \"\" },\n { label: \"Groot\", value: \"1.25rem\" },\n { label: \"Extra groot\", value: \"1.5rem\" },\n] as const;\n\n/** `textStyle`-mark (Tiptap) uitgebreid met een `fontSize`-attribuut → `<span style=\"font-size:...\">`. */\nexport const FontSize = TextStyle.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n fontSize: {\n default: null,\n // 8.10: alleen de presets. Geplakt uit Word/Docs (\"11pt\", \"36px\") bleef anders in de\n // werkversie staan, terwijl de server het bij publiceren weghaalt — de editor toonde dan\n // iets anders dan wat live kwam, en de klant dacht dat publiceren de opmaak \"sloopte\".\n parseHTML: (element: HTMLElement) => {\n const v = element.style.fontSize;\n return FONT_SIZES.some((f) => f.value && f.value === v) ? v : null;\n },\n renderHTML: (attributes: Record<string, unknown>) => {\n if (!attributes.fontSize) return {};\n return { style: `font-size: ${attributes.fontSize}` };\n },\n },\n };\n },\n});\n"],"mappings":"AACA,SAAS,iBAAiB;AAQnB,MAAM,aAAa;AAAA,EACxB,EAAE,OAAO,SAAS,OAAO,UAAU;AAAA,EACnC,EAAE,OAAO,WAAW,OAAO,GAAG;AAAA,EAC9B,EAAE,OAAO,SAAS,OAAO,UAAU;AAAA,EACnC,EAAE,OAAO,eAAe,OAAO,SAAS;AAC1C;AAGO,MAAM,WAAW,UAAU,OAAO;AAAA,EACvC,gBAAgB;AACd,WAAO;AAAA,MACL,GAAG,KAAK,SAAS;AAAA,MACjB,UAAU;AAAA,QACR,SAAS;AAAA;AAAA;AAAA;AAAA,QAIT,WAAW,CAAC,YAAyB;AACnC,gBAAM,IAAI,QAAQ,MAAM;AACxB,iBAAO,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,IAAI,IAAI;AAAA,QAChE;AAAA,QACA,YAAY,CAAC,eAAwC;AACnD,cAAI,CAAC,WAAW,SAAU,QAAO,CAAC;AAClC,iBAAO,EAAE,OAAO,cAAc,WAAW,QAAQ,GAAG;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Het "/"-menu van de teksteditor (8.5) — het idee komt uit AstraCMS (Novel): typ "/" en kies een
|
|
3
|
+
* kop, lijst, citaat, foto of tabel, met zoekwoorden als ingang. Eigen, kleine implementatie op
|
|
4
|
+
* TipTap (zonder @tiptap/suggestion): detectie en filtering zijn puur en getest; het menu zelf
|
|
5
|
+
* rendert de editor.
|
|
6
|
+
*/
|
|
7
|
+
export interface SlashItem {
|
|
8
|
+
id: "tekst" | "kop" | "subkop" | "kleineKop" | "lijst" | "nummers" | "citaat" | "lijn" | "tabel" | "foto" | "link";
|
|
9
|
+
titel: string;
|
|
10
|
+
uitleg: string;
|
|
11
|
+
icoon: string;
|
|
12
|
+
zoekwoorden: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare const SLASH_ITEMS: SlashItem[];
|
|
15
|
+
/** Staat er "/zoekterm" vlak vóór de cursor (aan het begin of na een spatie)? Geeft de zoekterm. */
|
|
16
|
+
export declare function slashZoekterm(tekstVoorCursor: string): string | null;
|
|
17
|
+
/** Items die bij de zoekterm passen; begint-met eerst. */
|
|
18
|
+
export declare function filterSlash(items: SlashItem[], term: string): SlashItem[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const SLASH_ITEMS = [
|
|
2
|
+
{ id: "tekst", titel: "Tekst", uitleg: "Gewone alinea", icoon: "\xB6", zoekwoorden: ["alinea", "paragraaf", "normaal", "p"] },
|
|
3
|
+
{ id: "kop", titel: "Kop", uitleg: "Grote tussenkop", icoon: "H2", zoekwoorden: ["titel", "heading", "h2", "kop"] },
|
|
4
|
+
{ id: "subkop", titel: "Subkop", uitleg: "Kleinere tussenkop", icoon: "H3", zoekwoorden: ["h3", "subtitel", "kop"] },
|
|
5
|
+
{ id: "kleineKop", titel: "Kleine kop", uitleg: "Kopje boven een alinea", icoon: "H4", zoekwoorden: ["h4", "kopje"] },
|
|
6
|
+
{ id: "lijst", titel: "Opsomming", uitleg: "Lijst met bolletjes", icoon: "\u2022", zoekwoorden: ["lijst", "bullets", "punten", "ul"] },
|
|
7
|
+
{ id: "nummers", titel: "Genummerde lijst", uitleg: "1, 2, 3 \u2026", icoon: "1.", zoekwoorden: ["lijst", "nummers", "stappen", "ol"] },
|
|
8
|
+
{ id: "citaat", titel: "Citaat", uitleg: "Uitgelichte tekst of quote", icoon: "\u275D", zoekwoorden: ["quote", "aanhaling"] },
|
|
9
|
+
{ id: "lijn", titel: "Scheidingslijn", uitleg: "Horizontale lijn", icoon: "\u2014", zoekwoorden: ["lijn", "streep", "hr", "scheiding"] },
|
|
10
|
+
{ id: "tabel", titel: "Tabel", uitleg: "Tabel van 3 \xD7 3", icoon: "\u25A6", zoekwoorden: ["tabel", "table", "prijzen", "schema"] },
|
|
11
|
+
{ id: "foto", titel: "Foto", uitleg: "Uit de mediabibliotheek of uploaden", icoon: "\u{1F5BC}", zoekwoorden: ["afbeelding", "beeld", "plaatje", "image", "foto"] },
|
|
12
|
+
{ id: "link", titel: "Link", uitleg: "Naar een pagina of website", icoon: "\u{1F517}", zoekwoorden: ["url", "koppeling", "adres"] }
|
|
13
|
+
];
|
|
14
|
+
function slashZoekterm(tekstVoorCursor) {
|
|
15
|
+
const m = /(?:^|\s)\/([\p{L}\d]{0,24})$/u.exec(tekstVoorCursor);
|
|
16
|
+
return m ? m[1] : null;
|
|
17
|
+
}
|
|
18
|
+
function filterSlash(items, term) {
|
|
19
|
+
const q = term.trim().toLowerCase();
|
|
20
|
+
if (!q) return items;
|
|
21
|
+
const score = (it) => {
|
|
22
|
+
const velden = [it.titel.toLowerCase(), ...it.zoekwoorden];
|
|
23
|
+
if (velden.some((v) => v.startsWith(q))) return 2;
|
|
24
|
+
if (velden.some((v) => v.includes(q))) return 1;
|
|
25
|
+
return 0;
|
|
26
|
+
};
|
|
27
|
+
return items.map((it) => ({ it, s: score(it) })).filter((x) => x.s > 0).sort((a, b) => b.s - a.s).map((x) => x.it);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
SLASH_ITEMS,
|
|
31
|
+
filterSlash,
|
|
32
|
+
slashZoekterm
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=slashMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/editors/slashMenu.ts"],"sourcesContent":["/**\n * Het \"/\"-menu van de teksteditor (8.5) — het idee komt uit AstraCMS (Novel): typ \"/\" en kies een\n * kop, lijst, citaat, foto of tabel, met zoekwoorden als ingang. Eigen, kleine implementatie op\n * TipTap (zonder @tiptap/suggestion): detectie en filtering zijn puur en getest; het menu zelf\n * rendert de editor.\n */\nexport interface SlashItem {\n id: \"tekst\" | \"kop\" | \"subkop\" | \"kleineKop\" | \"lijst\" | \"nummers\" | \"citaat\" | \"lijn\" | \"tabel\" | \"foto\" | \"link\";\n titel: string;\n uitleg: string;\n icoon: string;\n zoekwoorden: string[];\n}\n\nexport const SLASH_ITEMS: SlashItem[] = [\n { id: \"tekst\", titel: \"Tekst\", uitleg: \"Gewone alinea\", icoon: \"¶\", zoekwoorden: [\"alinea\", \"paragraaf\", \"normaal\", \"p\"] },\n { id: \"kop\", titel: \"Kop\", uitleg: \"Grote tussenkop\", icoon: \"H2\", zoekwoorden: [\"titel\", \"heading\", \"h2\", \"kop\"] },\n { id: \"subkop\", titel: \"Subkop\", uitleg: \"Kleinere tussenkop\", icoon: \"H3\", zoekwoorden: [\"h3\", \"subtitel\", \"kop\"] },\n { id: \"kleineKop\", titel: \"Kleine kop\", uitleg: \"Kopje boven een alinea\", icoon: \"H4\", zoekwoorden: [\"h4\", \"kopje\"] },\n { id: \"lijst\", titel: \"Opsomming\", uitleg: \"Lijst met bolletjes\", icoon: \"•\", zoekwoorden: [\"lijst\", \"bullets\", \"punten\", \"ul\"] },\n { id: \"nummers\", titel: \"Genummerde lijst\", uitleg: \"1, 2, 3 …\", icoon: \"1.\", zoekwoorden: [\"lijst\", \"nummers\", \"stappen\", \"ol\"] },\n { id: \"citaat\", titel: \"Citaat\", uitleg: \"Uitgelichte tekst of quote\", icoon: \"❝\", zoekwoorden: [\"quote\", \"aanhaling\"] },\n { id: \"lijn\", titel: \"Scheidingslijn\", uitleg: \"Horizontale lijn\", icoon: \"—\", zoekwoorden: [\"lijn\", \"streep\", \"hr\", \"scheiding\"] },\n { id: \"tabel\", titel: \"Tabel\", uitleg: \"Tabel van 3 × 3\", icoon: \"▦\", zoekwoorden: [\"tabel\", \"table\", \"prijzen\", \"schema\"] },\n { id: \"foto\", titel: \"Foto\", uitleg: \"Uit de mediabibliotheek of uploaden\", icoon: \"🖼\", zoekwoorden: [\"afbeelding\", \"beeld\", \"plaatje\", \"image\", \"foto\"] },\n { id: \"link\", titel: \"Link\", uitleg: \"Naar een pagina of website\", icoon: \"🔗\", zoekwoorden: [\"url\", \"koppeling\", \"adres\"] },\n];\n\n/** Staat er \"/zoekterm\" vlak vóór de cursor (aan het begin of na een spatie)? Geeft de zoekterm. */\nexport function slashZoekterm(tekstVoorCursor: string): string | null {\n const m = /(?:^|\\s)\\/([\\p{L}\\d]{0,24})$/u.exec(tekstVoorCursor);\n return m ? m[1] : null;\n}\n\n/** Items die bij de zoekterm passen; begint-met eerst. */\nexport function filterSlash(items: SlashItem[], term: string): SlashItem[] {\n const q = term.trim().toLowerCase();\n if (!q) return items;\n const score = (it: SlashItem) => {\n const velden = [it.titel.toLowerCase(), ...it.zoekwoorden];\n if (velden.some((v) => v.startsWith(q))) return 2;\n if (velden.some((v) => v.includes(q))) return 1;\n return 0;\n };\n return items\n .map((it) => ({ it, s: score(it) }))\n .filter((x) => x.s > 0)\n .sort((a, b) => b.s - a.s)\n .map((x) => x.it);\n}\n"],"mappings":"AAcO,MAAM,cAA2B;AAAA,EACtC,EAAE,IAAI,SAAS,OAAO,SAAS,QAAQ,iBAAiB,OAAO,QAAK,aAAa,CAAC,UAAU,aAAa,WAAW,GAAG,EAAE;AAAA,EACzH,EAAE,IAAI,OAAO,OAAO,OAAO,QAAQ,mBAAmB,OAAO,MAAM,aAAa,CAAC,SAAS,WAAW,MAAM,KAAK,EAAE;AAAA,EAClH,EAAE,IAAI,UAAU,OAAO,UAAU,QAAQ,sBAAsB,OAAO,MAAM,aAAa,CAAC,MAAM,YAAY,KAAK,EAAE;AAAA,EACnH,EAAE,IAAI,aAAa,OAAO,cAAc,QAAQ,0BAA0B,OAAO,MAAM,aAAa,CAAC,MAAM,OAAO,EAAE;AAAA,EACpH,EAAE,IAAI,SAAS,OAAO,aAAa,QAAQ,uBAAuB,OAAO,UAAK,aAAa,CAAC,SAAS,WAAW,UAAU,IAAI,EAAE;AAAA,EAChI,EAAE,IAAI,WAAW,OAAO,oBAAoB,QAAQ,kBAAa,OAAO,MAAM,aAAa,CAAC,SAAS,WAAW,WAAW,IAAI,EAAE;AAAA,EACjI,EAAE,IAAI,UAAU,OAAO,UAAU,QAAQ,8BAA8B,OAAO,UAAK,aAAa,CAAC,SAAS,WAAW,EAAE;AAAA,EACvH,EAAE,IAAI,QAAQ,OAAO,kBAAkB,QAAQ,oBAAoB,OAAO,UAAK,aAAa,CAAC,QAAQ,UAAU,MAAM,WAAW,EAAE;AAAA,EAClI,EAAE,IAAI,SAAS,OAAO,SAAS,QAAQ,sBAAmB,OAAO,UAAK,aAAa,CAAC,SAAS,SAAS,WAAW,QAAQ,EAAE;AAAA,EAC3H,EAAE,IAAI,QAAQ,OAAO,QAAQ,QAAQ,uCAAuC,OAAO,aAAM,aAAa,CAAC,cAAc,SAAS,WAAW,SAAS,MAAM,EAAE;AAAA,EAC1J,EAAE,IAAI,QAAQ,OAAO,QAAQ,QAAQ,8BAA8B,OAAO,aAAM,aAAa,CAAC,OAAO,aAAa,OAAO,EAAE;AAC7H;AAGO,SAAS,cAAc,iBAAwC;AACpE,QAAM,IAAI,gCAAgC,KAAK,eAAe;AAC9D,SAAO,IAAI,EAAE,CAAC,IAAI;AACpB;AAGO,SAAS,YAAY,OAAoB,MAA2B;AACzE,QAAM,IAAI,KAAK,KAAK,EAAE,YAAY;AAClC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,QAAQ,CAAC,OAAkB;AAC/B,UAAM,SAAS,CAAC,GAAG,MAAM,YAAY,GAAG,GAAG,GAAG,WAAW;AACzD,QAAI,OAAO,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,EAAG,QAAO;AAChD,QAAI,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,EAAG,QAAO;AAC9C,WAAO;AAAA,EACT;AACA,SAAO,MACJ,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,MAAM,EAAE,EAAE,EAAE,EAClC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EACrB,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,EACxB,IAAI,CAAC,MAAM,EAAE,EAAE;AACpB;","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type Editor } from "@tiptap/react";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* De kern van de rich-text-editor (8.6), gedeeld door het beheer (`RichTextEditor`, vaste
|
|
5
|
+
* werkbalk) en het bewerken op de site zelf (`InlineRijkeTekst`, zwevende werkbalk). Tot 8.5 had
|
|
6
|
+
* de site een eigen, kalere editor (contentEditable + execCommand): geen slash-menu, geen foto's,
|
|
7
|
+
* geen tabellen. Nu is het één editor met twee jassen.
|
|
8
|
+
*
|
|
9
|
+
* Output wordt client-side met DOMPurify gesanitized; de server sanitized nogmaals bij publiceren
|
|
10
|
+
* (server/sanitize.ts) — nooit op de client vertrouwen.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useRijkeTekst({ value, onChange, siteId, editorClass, autofocus, onEscape, alleenVensterScroll, }: {
|
|
13
|
+
value: string;
|
|
14
|
+
onChange: (html: string) => void;
|
|
15
|
+
/** Nodig voor foto's (uploaden, plakken, kiezen). */
|
|
16
|
+
siteId?: string;
|
|
17
|
+
/** Klassen op het bewerkbare oppervlak (de ProseMirror-root). */
|
|
18
|
+
editorClass?: string;
|
|
19
|
+
autofocus?: boolean;
|
|
20
|
+
/** Escape buiten een open slash-menu (inline: klaar met bewerken). */
|
|
21
|
+
onEscape?: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Alleen het venster scrollen om de cursor in beeld te houden (inline op de site). ProseMirror
|
|
24
|
+
* loopt anders alle ouders langs en zet `scrollTop` op containers van de site; met
|
|
25
|
+
* `scroll-behavior: smooth` gleed de pagina dan bij elke Enter naar beneden.
|
|
26
|
+
*/
|
|
27
|
+
alleenVensterScroll?: boolean;
|
|
28
|
+
}): {
|
|
29
|
+
editor: Editor | null;
|
|
30
|
+
overlays: import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | null;
|
|
31
|
+
openLink: () => void;
|
|
32
|
+
openFoto: () => void;
|
|
33
|
+
ietsOpen: boolean;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* TipTap 3 (StarterKit `trailingNode`) houdt altijd een lege alinea achteraan, zodat je na een
|
|
37
|
+
* lijst of tabel verder kunt typen. Prima in de editor, maar niet in de opgeslagen tekst: daar werd
|
|
38
|
+
* het extra witruimte onder elk blok.
|
|
39
|
+
*/
|
|
40
|
+
export declare function zonderLegeStaart(html: string): string;
|
|
41
|
+
/** Werkbalkknop die de selectie in de editor laat staan (mousedown neemt geen focus). */
|
|
42
|
+
export declare function RteKnop({ on, active, label, title, className }: {
|
|
43
|
+
on: () => void;
|
|
44
|
+
active?: boolean;
|
|
45
|
+
label: ReactNode;
|
|
46
|
+
title?: string;
|
|
47
|
+
className?: string;
|
|
48
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEditor } from "@tiptap/react";
|
|
4
|
+
import { BubbleMenu } from "@tiptap/react/menus";
|
|
5
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
6
|
+
import { TableKit } from "@tiptap/extension-table";
|
|
7
|
+
import Link from "@tiptap/extension-link";
|
|
8
|
+
import Image from "@tiptap/extension-image";
|
|
9
|
+
import DOMPurify from "dompurify";
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
+
import { cn } from "../ui/cn.js";
|
|
12
|
+
import { MediaPickerDialog } from "../media/MediaPicker.js";
|
|
13
|
+
import { useToast } from "../ui/toast.js";
|
|
14
|
+
import { uploadImage } from "../lib/upload.js";
|
|
15
|
+
import { filterSlash, SLASH_ITEMS, slashZoekterm } from "./slashMenu.js";
|
|
16
|
+
import { LinkDialog } from "../ui/LinkDialog.js";
|
|
17
|
+
import { FontSize } from "./fontSizeExtension.js";
|
|
18
|
+
function useRijkeTekst({
|
|
19
|
+
value,
|
|
20
|
+
onChange,
|
|
21
|
+
siteId,
|
|
22
|
+
editorClass,
|
|
23
|
+
autofocus = false,
|
|
24
|
+
onEscape,
|
|
25
|
+
alleenVensterScroll = false
|
|
26
|
+
}) {
|
|
27
|
+
const { toast } = useToast();
|
|
28
|
+
const [slash, setSlash] = useState(null);
|
|
29
|
+
const [slashIndex, setSlashIndex] = useState(0);
|
|
30
|
+
const slashRef = useRef({
|
|
31
|
+
open: false,
|
|
32
|
+
items: [],
|
|
33
|
+
index: 0,
|
|
34
|
+
kies: () => {
|
|
35
|
+
},
|
|
36
|
+
sluit: () => {
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const afgewezenRef = useRef(null);
|
|
40
|
+
const [fotoKiezer, setFotoKiezer] = useState(false);
|
|
41
|
+
const plaatsFotosRef = useRef(() => {
|
|
42
|
+
});
|
|
43
|
+
const onChangeRef = useRef(onChange);
|
|
44
|
+
onChangeRef.current = onChange;
|
|
45
|
+
const onEscapeRef = useRef(onEscape);
|
|
46
|
+
onEscapeRef.current = onEscape;
|
|
47
|
+
const bepaalSlash = useCallback((ed) => {
|
|
48
|
+
const { selection } = ed.state;
|
|
49
|
+
const { $from, empty } = selection;
|
|
50
|
+
if (!empty || !$from.parent.isTextblock || $from.parent.type.name === "codeBlock") return setSlash(null);
|
|
51
|
+
const term = slashZoekterm($from.parent.textBetween(0, $from.parentOffset, void 0, "\uFFFC"));
|
|
52
|
+
if (term === null) {
|
|
53
|
+
afgewezenRef.current = null;
|
|
54
|
+
return setSlash(null);
|
|
55
|
+
}
|
|
56
|
+
const van = $from.pos - term.length - 1;
|
|
57
|
+
if (afgewezenRef.current === van) return setSlash(null);
|
|
58
|
+
const c = ed.view.coordsAtPos($from.pos);
|
|
59
|
+
setSlash({ van, tot: $from.pos, term, x: c.left, y: c.bottom, boven: c.top });
|
|
60
|
+
}, []);
|
|
61
|
+
const editor = useEditor({
|
|
62
|
+
immediatelyRender: false,
|
|
63
|
+
autofocus: autofocus ? "end" : false,
|
|
64
|
+
extensions: [
|
|
65
|
+
// Tiptap 3 bundelt Link en Underline IN StarterKit. Los toevoegen geeft dubbele
|
|
66
|
+
// extensienamen (en stille gedragsfouten), dus: Underline komt uit StarterKit, en zijn Link
|
|
67
|
+
// zetten we uit omdat we die hieronder zelf configureren (geen open-on-click, eigen rel).
|
|
68
|
+
StarterKit.configure({ heading: { levels: [2, 3, 4] }, link: false }),
|
|
69
|
+
FontSize,
|
|
70
|
+
// Zonder deze extensie gooit TipTap een <table> bij het inladen weg (onbekende node) — een
|
|
71
|
+
// tabel uit de seed of een import overleefde dan geen enkele bewerking van dat blok.
|
|
72
|
+
TableKit.configure({ table: { resizable: false } }),
|
|
73
|
+
Link.configure({ openOnClick: false, autolink: true, HTMLAttributes: { rel: "noopener noreferrer nofollow" } }),
|
|
74
|
+
Image.configure({ inline: false, allowBase64: false, HTMLAttributes: { loading: "lazy", decoding: "async" } })
|
|
75
|
+
],
|
|
76
|
+
content: value || "<p></p>",
|
|
77
|
+
onUpdate: ({ editor: editor2 }) => {
|
|
78
|
+
onChangeRef.current(zonderLegeStaart(DOMPurify.sanitize(editor2.getHTML())));
|
|
79
|
+
bepaalSlash(editor2);
|
|
80
|
+
},
|
|
81
|
+
onSelectionUpdate: ({ editor: editor2 }) => bepaalSlash(editor2),
|
|
82
|
+
onBlur: () => setSlash(null),
|
|
83
|
+
editorProps: {
|
|
84
|
+
handleKeyDown: (view, e) => {
|
|
85
|
+
if (alleenVensterScroll && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
86
|
+
if (e.key === "End" && view.endOfTextblock("forward")) return true;
|
|
87
|
+
if (e.key === "Home" && view.endOfTextblock("backward")) return true;
|
|
88
|
+
}
|
|
89
|
+
const s = slashRef.current;
|
|
90
|
+
if (!s.open) {
|
|
91
|
+
if (e.key === "Escape" && onEscapeRef.current) {
|
|
92
|
+
onEscapeRef.current();
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (e.key === "Escape") {
|
|
98
|
+
s.sluit();
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (s.items.length === 0) return false;
|
|
102
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
103
|
+
const n = s.items.length;
|
|
104
|
+
setSlashIndex((i) => e.key === "ArrowDown" ? (i + 1) % n : (i - 1 + n) % n);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (e.key === "Enter" || e.key === "Tab") {
|
|
108
|
+
s.kies(s.items[Math.min(s.index, s.items.length - 1)]);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
},
|
|
113
|
+
// 8.5: foto's plakken of erin slepen — upload naar de mediabibliotheek en plaats ze meteen.
|
|
114
|
+
handlePaste: (_view, e) => {
|
|
115
|
+
const files = [...e.clipboardData?.files ?? []].filter((f) => f.type.startsWith("image/"));
|
|
116
|
+
if (!siteId || files.length === 0) return false;
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
plaatsFotosRef.current(files);
|
|
119
|
+
return true;
|
|
120
|
+
},
|
|
121
|
+
handleDrop: (view, e, _slice, verplaatst) => {
|
|
122
|
+
if (verplaatst) return false;
|
|
123
|
+
const files = [...e.dataTransfer?.files ?? []].filter((f) => f.type.startsWith("image/"));
|
|
124
|
+
if (!siteId || files.length === 0) return false;
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
plaatsFotosRef.current(files, view.posAtCoords({ left: e.clientX, top: e.clientY })?.pos);
|
|
127
|
+
return true;
|
|
128
|
+
},
|
|
129
|
+
handleScrollToSelection: alleenVensterScroll ? (view) => {
|
|
130
|
+
const c = view.coordsAtPos(view.state.selection.head);
|
|
131
|
+
const onder = 170;
|
|
132
|
+
const boven = 110;
|
|
133
|
+
if (c.bottom > window.innerHeight - onder) window.scrollBy({ top: c.bottom - window.innerHeight + onder, behavior: "instant" });
|
|
134
|
+
else if (c.top < boven) window.scrollBy({ top: c.top - boven, behavior: "instant" });
|
|
135
|
+
return true;
|
|
136
|
+
} : void 0,
|
|
137
|
+
attributes: { class: editorClass ?? "", spellcheck: "true" }
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
const [linkOpen, setLinkOpen] = useState(false);
|
|
141
|
+
const openLink = useCallback(() => setLinkOpen(true), []);
|
|
142
|
+
const openFoto = useCallback(() => setFotoKiezer(true), []);
|
|
143
|
+
const zetLink = useCallback(
|
|
144
|
+
(href, opties) => {
|
|
145
|
+
if (!editor) return;
|
|
146
|
+
if (href === null) editor.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
147
|
+
else editor.chain().focus().extendMarkRange("link").setLink({ href, target: opties?.nieuwTabblad ? "_blank" : null }).run();
|
|
148
|
+
},
|
|
149
|
+
[editor]
|
|
150
|
+
);
|
|
151
|
+
const zichtbareItems = slash ? filterSlash(SLASH_ITEMS.filter((it) => it.id !== "foto" || siteId), slash.term) : [];
|
|
152
|
+
useEffect(() => setSlashIndex(0), [slash?.term, slash?.van]);
|
|
153
|
+
const kiesSlash = useCallback(
|
|
154
|
+
(it) => {
|
|
155
|
+
if (!editor || !slash) return;
|
|
156
|
+
const c = editor.chain().focus().deleteRange({ from: slash.van, to: slash.tot });
|
|
157
|
+
setSlash(null);
|
|
158
|
+
switch (it.id) {
|
|
159
|
+
case "tekst":
|
|
160
|
+
return c.setParagraph().run();
|
|
161
|
+
case "kop":
|
|
162
|
+
return c.setHeading({ level: 2 }).run();
|
|
163
|
+
case "subkop":
|
|
164
|
+
return c.setHeading({ level: 3 }).run();
|
|
165
|
+
case "kleineKop":
|
|
166
|
+
return c.setHeading({ level: 4 }).run();
|
|
167
|
+
case "lijst":
|
|
168
|
+
return c.toggleBulletList().run();
|
|
169
|
+
case "nummers":
|
|
170
|
+
return c.toggleOrderedList().run();
|
|
171
|
+
case "citaat":
|
|
172
|
+
return c.toggleBlockquote().run();
|
|
173
|
+
case "lijn":
|
|
174
|
+
return c.setHorizontalRule().run();
|
|
175
|
+
case "tabel":
|
|
176
|
+
return c.insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
|
|
177
|
+
case "foto":
|
|
178
|
+
c.run();
|
|
179
|
+
return setFotoKiezer(true);
|
|
180
|
+
case "link":
|
|
181
|
+
c.run();
|
|
182
|
+
return setLinkOpen(true);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
[editor, slash]
|
|
186
|
+
);
|
|
187
|
+
slashRef.current = {
|
|
188
|
+
open: Boolean(slash),
|
|
189
|
+
items: zichtbareItems,
|
|
190
|
+
index: slashIndex,
|
|
191
|
+
kies: kiesSlash,
|
|
192
|
+
sluit: () => {
|
|
193
|
+
afgewezenRef.current = slash?.van ?? null;
|
|
194
|
+
setSlash(null);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
plaatsFotosRef.current = (files, pos) => {
|
|
198
|
+
if (!editor || !siteId) return;
|
|
199
|
+
for (const file of files) {
|
|
200
|
+
if (file.type === "image/svg+xml") {
|
|
201
|
+
toast({ title: "SVG kan hier niet", description: "Gebruik een foto (jpg, png, webp).", tone: "error" });
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
toast({ title: `Foto uploaden\u2026`, description: file.name });
|
|
205
|
+
void uploadImage(siteId, file).then(({ url }) => {
|
|
206
|
+
const img = new window.Image();
|
|
207
|
+
img.src = url;
|
|
208
|
+
const plaats = () => {
|
|
209
|
+
const chain = editor.chain().focus();
|
|
210
|
+
(pos !== void 0 ? chain.insertContentAt(pos, { type: "image", attrs: { src: url, alt: "" } }) : chain.setImage({ src: url, alt: "" })).run();
|
|
211
|
+
toast({ title: "Foto geplaatst", description: "Geef hem in Media een korte beschrijving (alt-tekst); die verschijnt dan vanzelf bij deze foto, voor slechtzienden en Google.", tone: "success" });
|
|
212
|
+
};
|
|
213
|
+
img.onload = plaats;
|
|
214
|
+
img.onerror = plaats;
|
|
215
|
+
}).catch((e) => toast({ title: "Uploaden mislukt", description: e.message, tone: "error" }));
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const overlays = editor ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
219
|
+
/* @__PURE__ */ jsxs(BubbleMenu, { editor, className: "xicms-bubbel", options: { placement: "top" }, children: [
|
|
220
|
+
/* @__PURE__ */ jsx(RteKnop, { on: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), label: "B", title: "Vet" }),
|
|
221
|
+
/* @__PURE__ */ jsx(RteKnop, { on: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), label: "I", title: "Cursief" }),
|
|
222
|
+
/* @__PURE__ */ jsx(RteKnop, { on: () => editor.chain().focus().toggleUnderline().run(), active: editor.isActive("underline"), label: /* @__PURE__ */ jsx("span", { style: { textDecoration: "underline" }, children: "U" }), title: "Onderstrepen" }),
|
|
223
|
+
/* @__PURE__ */ jsx(RteKnop, { on: openLink, active: editor.isActive("link"), label: "\u{1F517}", title: "Link" }),
|
|
224
|
+
/* @__PURE__ */ jsx(RteKnop, { on: () => editor.chain().focus().unsetAllMarks().run(), label: "\u2A2F", title: "Opmaak weghalen" })
|
|
225
|
+
] }),
|
|
226
|
+
slash && /* @__PURE__ */ jsx(
|
|
227
|
+
"div",
|
|
228
|
+
{
|
|
229
|
+
role: "listbox",
|
|
230
|
+
"aria-label": "Invoegen",
|
|
231
|
+
className: "xicms-slash",
|
|
232
|
+
style: {
|
|
233
|
+
left: Math.min(slash.x, (typeof window !== "undefined" ? window.innerWidth : 1200) - 280),
|
|
234
|
+
// Te weinig ruimte onder de cursor (onderaan het scherm): klap omhoog open.
|
|
235
|
+
...typeof window !== "undefined" && slash.y + 330 > window.innerHeight ? { bottom: window.innerHeight - slash.boven + 6 } : { top: slash.y + 6 }
|
|
236
|
+
},
|
|
237
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
238
|
+
children: zichtbareItems.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "xicms-slash__leeg", children: [
|
|
239
|
+
"Niets gevonden voor \u201C",
|
|
240
|
+
slash.term,
|
|
241
|
+
"\u201D"
|
|
242
|
+
] }) : zichtbareItems.map((it, i) => /* @__PURE__ */ jsxs(
|
|
243
|
+
"button",
|
|
244
|
+
{
|
|
245
|
+
type: "button",
|
|
246
|
+
role: "option",
|
|
247
|
+
"aria-selected": i === slashIndex,
|
|
248
|
+
className: "xicms-slash__item",
|
|
249
|
+
onMouseEnter: () => setSlashIndex(i),
|
|
250
|
+
onClick: () => kiesSlash(it),
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-slash__icoon", "aria-hidden": true, children: it.icoon }),
|
|
253
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
254
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-slash__titel", children: it.titel }),
|
|
255
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-slash__uitleg", children: it.uitleg })
|
|
256
|
+
] })
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
it.id
|
|
260
|
+
))
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
siteId && /* @__PURE__ */ jsx(
|
|
264
|
+
MediaPickerDialog,
|
|
265
|
+
{
|
|
266
|
+
siteId,
|
|
267
|
+
open: fotoKiezer,
|
|
268
|
+
onOpenChange: setFotoKiezer,
|
|
269
|
+
onSelect: (m) => editor.chain().focus().setImage({ src: m.url, alt: m.alt ?? "" }).run()
|
|
270
|
+
}
|
|
271
|
+
),
|
|
272
|
+
/* @__PURE__ */ jsx(
|
|
273
|
+
LinkDialog,
|
|
274
|
+
{
|
|
275
|
+
open: linkOpen,
|
|
276
|
+
onOpenChange: setLinkOpen,
|
|
277
|
+
siteId,
|
|
278
|
+
huidig: editor.getAttributes("link").href ?? void 0,
|
|
279
|
+
huidigNieuwTabblad: editor.isActive("link") ? editor.getAttributes("link").target === "_blank" : void 0,
|
|
280
|
+
onKlaar: zetLink
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
] }) : null;
|
|
284
|
+
const ietsOpen = Boolean(slash) || linkOpen || fotoKiezer;
|
|
285
|
+
return { editor, overlays, openLink, openFoto, ietsOpen };
|
|
286
|
+
}
|
|
287
|
+
function zonderLegeStaart(html) {
|
|
288
|
+
return html.replace(/(?:<p><\/p>|<p><br\s*\/?><\/p>)+$/i, "");
|
|
289
|
+
}
|
|
290
|
+
function RteKnop({ on, active, label, title, className }) {
|
|
291
|
+
return /* @__PURE__ */ jsx(
|
|
292
|
+
"button",
|
|
293
|
+
{
|
|
294
|
+
type: "button",
|
|
295
|
+
title,
|
|
296
|
+
"aria-label": title,
|
|
297
|
+
"aria-pressed": active ? true : void 0,
|
|
298
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
299
|
+
onClick: on,
|
|
300
|
+
className: cn(className ?? "xicms-admin-btn h-8 min-w-8 rounded px-2 text-sm hover:bg-slate-100", active && (className ? "is-actief" : "bg-slate-200 font-semibold")),
|
|
301
|
+
children: label
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
export {
|
|
306
|
+
RteKnop,
|
|
307
|
+
useRijkeTekst,
|
|
308
|
+
zonderLegeStaart
|
|
309
|
+
};
|
|
310
|
+
//# sourceMappingURL=useRijkeTekst.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/editors/useRijkeTekst.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEditor, type Editor } from \"@tiptap/react\";\nimport { BubbleMenu } from \"@tiptap/react/menus\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { TableKit } from \"@tiptap/extension-table\";\nimport Link from \"@tiptap/extension-link\";\nimport Image from \"@tiptap/extension-image\";\nimport DOMPurify from \"dompurify\";\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../ui/cn\";\nimport { MediaPickerDialog } from \"../media/MediaPicker\";\nimport { useToast } from \"../ui/toast\";\nimport { uploadImage } from \"../lib/upload\";\nimport { filterSlash, SLASH_ITEMS, slashZoekterm, type SlashItem } from \"./slashMenu\";\nimport { LinkDialog } from \"../ui/LinkDialog\";\nimport { FontSize } from \"./fontSizeExtension\";\n\n/**\n * De kern van de rich-text-editor (8.6), gedeeld door het beheer (`RichTextEditor`, vaste\n * werkbalk) en het bewerken op de site zelf (`InlineRijkeTekst`, zwevende werkbalk). Tot 8.5 had\n * de site een eigen, kalere editor (contentEditable + execCommand): geen slash-menu, geen foto's,\n * geen tabellen. Nu is het één editor met twee jassen.\n *\n * Output wordt client-side met DOMPurify gesanitized; de server sanitized nogmaals bij publiceren\n * (server/sanitize.ts) — nooit op de client vertrouwen.\n */\nexport function useRijkeTekst({\n value,\n onChange,\n siteId,\n editorClass,\n autofocus = false,\n onEscape,\n alleenVensterScroll = false,\n}: {\n value: string;\n onChange: (html: string) => void;\n /** Nodig voor foto's (uploaden, plakken, kiezen). */\n siteId?: string;\n /** Klassen op het bewerkbare oppervlak (de ProseMirror-root). */\n editorClass?: string;\n autofocus?: boolean;\n /** Escape buiten een open slash-menu (inline: klaar met bewerken). */\n onEscape?: () => void;\n /**\n * Alleen het venster scrollen om de cursor in beeld te houden (inline op de site). ProseMirror\n * loopt anders alle ouders langs en zet `scrollTop` op containers van de site; met\n * `scroll-behavior: smooth` gleed de pagina dan bij elke Enter naar beneden.\n */\n alleenVensterScroll?: boolean;\n}) {\n const { toast } = useToast();\n /**\n * \"/\"-menu (8.5). Staat in een ref zodat de TipTap-keyhandler (één keer gemaakt) altijd de\n * actuele toestand leest.\n */\n const [slash, setSlash] = useState<{ van: number; tot: number; term: string; x: number; y: number; boven: number } | null>(null);\n const [slashIndex, setSlashIndex] = useState(0);\n const slashRef = useRef<{ open: boolean; items: SlashItem[]; index: number; kies: (it: SlashItem) => void; sluit: () => void }>({\n open: false,\n items: [],\n index: 0,\n kies: () => {},\n sluit: () => {},\n });\n const afgewezenRef = useRef<number | null>(null);\n const [fotoKiezer, setFotoKiezer] = useState(false);\n const plaatsFotosRef = useRef<(files: File[], pos?: number) => void>(() => {});\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const onEscapeRef = useRef(onEscape);\n onEscapeRef.current = onEscape;\n\n const bepaalSlash = useCallback((ed: Editor) => {\n const { selection } = ed.state;\n const { $from, empty } = selection;\n if (!empty || !$from.parent.isTextblock || $from.parent.type.name === \"codeBlock\") return setSlash(null);\n const term = slashZoekterm($from.parent.textBetween(0, $from.parentOffset, undefined, \"\"));\n if (term === null) {\n afgewezenRef.current = null;\n return setSlash(null);\n }\n const van = $from.pos - term.length - 1;\n if (afgewezenRef.current === van) return setSlash(null);\n const c = ed.view.coordsAtPos($from.pos);\n setSlash({ van, tot: $from.pos, term, x: c.left, y: c.bottom, boven: c.top });\n }, []);\n\n const editor = useEditor({\n immediatelyRender: false,\n autofocus: autofocus ? \"end\" : false,\n extensions: [\n // Tiptap 3 bundelt Link en Underline IN StarterKit. Los toevoegen geeft dubbele\n // extensienamen (en stille gedragsfouten), dus: Underline komt uit StarterKit, en zijn Link\n // zetten we uit omdat we die hieronder zelf configureren (geen open-on-click, eigen rel).\n StarterKit.configure({ heading: { levels: [2, 3, 4] }, link: false }),\n FontSize,\n // Zonder deze extensie gooit TipTap een <table> bij het inladen weg (onbekende node) — een\n // tabel uit de seed of een import overleefde dan geen enkele bewerking van dat blok.\n TableKit.configure({ table: { resizable: false } }),\n Link.configure({ openOnClick: false, autolink: true, HTMLAttributes: { rel: \"noopener noreferrer nofollow\" } }),\n Image.configure({ inline: false, allowBase64: false, HTMLAttributes: { loading: \"lazy\", decoding: \"async\" } }),\n ],\n content: value || \"<p></p>\",\n onUpdate: ({ editor }) => {\n onChangeRef.current(zonderLegeStaart(DOMPurify.sanitize(editor.getHTML())));\n bepaalSlash(editor);\n },\n onSelectionUpdate: ({ editor }) => bepaalSlash(editor),\n onBlur: () => setSlash(null),\n editorProps: {\n handleKeyDown: (view, e) => {\n // Inline op de site: End/Home met de cursor al aan het eind/begin liet Chrome de hele\n // pagina naar onder/boven scrollen (de toets \"viel door\" naar het document).\n if (alleenVensterScroll && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey) {\n if (e.key === \"End\" && view.endOfTextblock(\"forward\")) return true;\n if (e.key === \"Home\" && view.endOfTextblock(\"backward\")) return true;\n }\n const s = slashRef.current;\n if (!s.open) {\n if (e.key === \"Escape\" && onEscapeRef.current) {\n onEscapeRef.current();\n return true;\n }\n return false;\n }\n if (e.key === \"Escape\") {\n s.sluit();\n return true;\n }\n if (s.items.length === 0) return false;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n const n = s.items.length;\n setSlashIndex((i) => (e.key === \"ArrowDown\" ? (i + 1) % n : (i - 1 + n) % n));\n return true;\n }\n if (e.key === \"Enter\" || e.key === \"Tab\") {\n s.kies(s.items[Math.min(s.index, s.items.length - 1)]);\n return true;\n }\n return false;\n },\n // 8.5: foto's plakken of erin slepen — upload naar de mediabibliotheek en plaats ze meteen.\n handlePaste: (_view, e) => {\n const files = [...(e.clipboardData?.files ?? [])].filter((f) => f.type.startsWith(\"image/\"));\n if (!siteId || files.length === 0) return false;\n e.preventDefault();\n plaatsFotosRef.current(files);\n return true;\n },\n handleDrop: (view, e, _slice, verplaatst) => {\n if (verplaatst) return false;\n const files = [...(e.dataTransfer?.files ?? [])].filter((f) => f.type.startsWith(\"image/\"));\n if (!siteId || files.length === 0) return false;\n e.preventDefault();\n plaatsFotosRef.current(files, view.posAtCoords({ left: e.clientX, top: e.clientY })?.pos);\n return true;\n },\n handleScrollToSelection: alleenVensterScroll\n ? (view) => {\n const c = view.coordsAtPos(view.state.selection.head);\n // Onder: ruimte voor de editorbalk en het slash-menu; boven: de werkbalk boven het veld.\n const onder = 170;\n const boven = 110;\n if (c.bottom > window.innerHeight - onder) window.scrollBy({ top: c.bottom - window.innerHeight + onder, behavior: \"instant\" });\n else if (c.top < boven) window.scrollBy({ top: c.top - boven, behavior: \"instant\" });\n return true;\n }\n : undefined,\n attributes: { class: editorClass ?? \"\", spellcheck: \"true\" },\n },\n });\n\n // 8.2: een eigen dialoog (pagina kiezen of adres typen) i.p.v. window.prompt.\n const [linkOpen, setLinkOpen] = useState(false);\n const openLink = useCallback(() => setLinkOpen(true), []);\n const openFoto = useCallback(() => setFotoKiezer(true), []);\n const zetLink = useCallback(\n (href: string | null, opties?: { nieuwTabblad: boolean }) => {\n if (!editor) return;\n if (href === null) editor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n else editor.chain().focus().extendMarkRange(\"link\").setLink({ href, target: opties?.nieuwTabblad ? \"_blank\" : null }).run();\n },\n [editor],\n );\n\n const zichtbareItems = slash ? filterSlash(SLASH_ITEMS.filter((it) => it.id !== \"foto\" || siteId), slash.term) : [];\n useEffect(() => setSlashIndex(0), [slash?.term, slash?.van]);\n\n const kiesSlash = useCallback(\n (it: SlashItem) => {\n if (!editor || !slash) return;\n const c = editor.chain().focus().deleteRange({ from: slash.van, to: slash.tot });\n setSlash(null);\n switch (it.id) {\n case \"tekst\": return c.setParagraph().run();\n case \"kop\": return c.setHeading({ level: 2 }).run();\n case \"subkop\": return c.setHeading({ level: 3 }).run();\n case \"kleineKop\": return c.setHeading({ level: 4 }).run();\n case \"lijst\": return c.toggleBulletList().run();\n case \"nummers\": return c.toggleOrderedList().run();\n case \"citaat\": return c.toggleBlockquote().run();\n case \"lijn\": return c.setHorizontalRule().run();\n case \"tabel\": return c.insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();\n case \"foto\":\n c.run();\n return setFotoKiezer(true);\n case \"link\":\n c.run();\n return setLinkOpen(true);\n }\n },\n [editor, slash],\n );\n slashRef.current = {\n open: Boolean(slash),\n items: zichtbareItems,\n index: slashIndex,\n kies: kiesSlash,\n sluit: () => {\n afgewezenRef.current = slash?.van ?? null;\n setSlash(null);\n },\n };\n\n plaatsFotosRef.current = (files: File[], pos?: number) => {\n if (!editor || !siteId) return;\n for (const file of files) {\n if (file.type === \"image/svg+xml\") {\n toast({ title: \"SVG kan hier niet\", description: \"Gebruik een foto (jpg, png, webp).\", tone: \"error\" });\n continue;\n }\n toast({ title: `Foto uploaden…`, description: file.name });\n void uploadImage(siteId, file)\n .then(({ url }) => {\n // Vooraf laden (idee uit AstraCMS): de foto verschijnt in één keer i.p.v. regel voor regel.\n const img = new window.Image();\n img.src = url;\n const plaats = () => {\n const chain = editor.chain().focus();\n (pos !== undefined ? chain.insertContentAt(pos, { type: \"image\", attrs: { src: url, alt: \"\" } }) : chain.setImage({ src: url, alt: \"\" })).run();\n toast({ title: \"Foto geplaatst\", description: \"Geef hem in Media een korte beschrijving (alt-tekst); die verschijnt dan vanzelf bij deze foto, voor slechtzienden en Google.\", tone: \"success\" });\n };\n img.onload = plaats;\n img.onerror = plaats;\n })\n .catch((e: Error) => toast({ title: \"Uploaden mislukt\", description: e.message, tone: \"error\" }));\n }\n };\n\n /** Selectie-werkbalk, slash-menu en de dialogen — hetzelfde in beide jassen. */\n const overlays: ReactNode = editor ? (\n <>\n {/* Selectie-werkbalk (8.5): de meest gebruikte opmaak zweeft boven geselecteerde tekst. */}\n <BubbleMenu editor={editor} className=\"xicms-bubbel\" options={{ placement: \"top\" }}>\n <RteKnop on={() => editor.chain().focus().toggleBold().run()} active={editor.isActive(\"bold\")} label=\"B\" title=\"Vet\" />\n <RteKnop on={() => editor.chain().focus().toggleItalic().run()} active={editor.isActive(\"italic\")} label=\"I\" title=\"Cursief\" />\n <RteKnop on={() => editor.chain().focus().toggleUnderline().run()} active={editor.isActive(\"underline\")} label={<span style={{ textDecoration: \"underline\" }}>U</span>} title=\"Onderstrepen\" />\n <RteKnop on={openLink} active={editor.isActive(\"link\")} label=\"🔗\" title=\"Link\" />\n <RteKnop on={() => editor.chain().focus().unsetAllMarks().run()} label=\"⨯\" title=\"Opmaak weghalen\" />\n </BubbleMenu>\n {slash && (\n <div\n role=\"listbox\"\n aria-label=\"Invoegen\"\n className=\"xicms-slash\"\n style={{\n left: Math.min(slash.x, (typeof window !== \"undefined\" ? window.innerWidth : 1200) - 280),\n // Te weinig ruimte onder de cursor (onderaan het scherm): klap omhoog open.\n ...(typeof window !== \"undefined\" && slash.y + 330 > window.innerHeight\n ? { bottom: window.innerHeight - slash.boven + 6 }\n : { top: slash.y + 6 }),\n }}\n onMouseDown={(e) => e.preventDefault()}\n >\n {zichtbareItems.length === 0 ? (\n <p className=\"xicms-slash__leeg\">Niets gevonden voor “{slash.term}”</p>\n ) : (\n zichtbareItems.map((it, i) => (\n <button\n key={it.id}\n type=\"button\"\n role=\"option\"\n aria-selected={i === slashIndex}\n className=\"xicms-slash__item\"\n onMouseEnter={() => setSlashIndex(i)}\n onClick={() => kiesSlash(it)}\n >\n <span className=\"xicms-slash__icoon\" aria-hidden>{it.icoon}</span>\n <span>\n <span className=\"xicms-slash__titel\">{it.titel}</span>\n <span className=\"xicms-slash__uitleg\">{it.uitleg}</span>\n </span>\n </button>\n ))\n )}\n </div>\n )}\n {siteId && (\n <MediaPickerDialog\n siteId={siteId}\n open={fotoKiezer}\n onOpenChange={setFotoKiezer}\n onSelect={(m) => editor.chain().focus().setImage({ src: m.url, alt: m.alt ?? \"\" }).run()}\n />\n )}\n <LinkDialog\n open={linkOpen}\n onOpenChange={setLinkOpen}\n siteId={siteId}\n huidig={(editor.getAttributes(\"link\").href as string | undefined) ?? undefined}\n huidigNieuwTabblad={editor.isActive(\"link\") ? editor.getAttributes(\"link\").target === \"_blank\" : undefined}\n onKlaar={zetLink}\n />\n </>\n ) : null;\n\n /** Staat er een menu of dialoog van deze editor open? (Inline: dan niet \"klaar\" bij een klik.) */\n const ietsOpen = Boolean(slash) || linkOpen || fotoKiezer;\n\n return { editor, overlays, openLink, openFoto, ietsOpen };\n}\n\n/**\n * TipTap 3 (StarterKit `trailingNode`) houdt altijd een lege alinea achteraan, zodat je na een\n * lijst of tabel verder kunt typen. Prima in de editor, maar niet in de opgeslagen tekst: daar werd\n * het extra witruimte onder elk blok.\n */\nexport function zonderLegeStaart(html: string): string {\n return html.replace(/(?:<p><\\/p>|<p><br\\s*\\/?><\\/p>)+$/i, \"\");\n}\n\n/** Werkbalkknop die de selectie in de editor laat staan (mousedown neemt geen focus). */\nexport function RteKnop({ on, active, label, title, className }: { on: () => void; active?: boolean; label: ReactNode; title?: string; className?: string }) {\n return (\n <button\n type=\"button\"\n title={title}\n aria-label={title}\n aria-pressed={active ? true : undefined}\n onMouseDown={(e) => e.preventDefault()}\n onClick={on}\n // Met eigen className (de donkere werkbalk op de site) alleen `is-actief`; die jas kleurt zelf.\n className={cn(className ?? \"xicms-admin-btn h-8 min-w-8 rounded px-2 text-sm hover:bg-slate-100\", active && (className ? \"is-actief\" : \"bg-slate-200 font-semibold\"))}\n >\n {label}\n </button>\n );\n}\n"],"mappings":"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","names":["editor"]}
|
|
@@ -2,11 +2,20 @@ import type { Timestamp } from "firebase/firestore";
|
|
|
2
2
|
export declare function FormsPanel({ siteId }: {
|
|
3
3
|
siteId: string;
|
|
4
4
|
}): import("react").JSX.Element;
|
|
5
|
+
/** "E-mailadres" → "emailadres"; uniek gemaakt met een volgnummer. Voldoet aan /^[a-zA-Z][a-zA-Z0-9_]*$/. */
|
|
6
|
+
export declare function sleutelUitLabel(label: string, bezet: Set<string>): string;
|
|
5
7
|
type Inzending = {
|
|
6
8
|
id: string;
|
|
7
9
|
values: Record<string, unknown>;
|
|
8
10
|
createdAt?: Timestamp;
|
|
9
11
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
type Kolom = string | {
|
|
13
|
+
name: string;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* CSV met Excel-vriendelijke opmaak: BOM, puntkomma, alles gequoot. Kolommen = de velden van het
|
|
18
|
+
* formulier, met hun LABEL als kop (8.2; was de technische sleutel, bv. "naam_1").
|
|
19
|
+
*/
|
|
20
|
+
export declare function inzendingenCsv(velden: Kolom[], subs: Inzending[]): string;
|
|
12
21
|
export {};
|