@vamos-ui/react 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +27 -2
- package/dist/blocks/block-heading.d.ts +29 -0
- package/dist/blocks/block-heading.js +39 -0
- package/dist/blocks/block-image.d.ts +52 -0
- package/dist/blocks/block-image.js +48 -0
- package/dist/blocks/block-link.d.ts +30 -0
- package/dist/blocks/block-link.js +36 -0
- package/dist/blocks/create-blocks.d.ts +54 -0
- package/dist/blocks/create-blocks.js +52 -0
- package/dist/blocks/index.d.ts +55 -0
- package/dist/blocks/index.js +59 -0
- package/dist/blocks/json-ld.d.ts +18 -0
- package/dist/blocks/json-ld.js +44 -0
- package/dist/blocks/labels.d.ts +20 -0
- package/dist/blocks/labels.js +43 -0
- package/dist/blocks/rich-text.d.ts +36 -0
- package/dist/blocks/rich-text.js +106 -0
- package/dist/blocks/schema/define-block.d.ts +151 -0
- package/dist/blocks/schema/define-block.js +120 -0
- package/dist/blocks/schema/emphasis.d.ts +17 -0
- package/dist/blocks/schema/emphasis.js +69 -0
- package/dist/blocks/schema/fields.d.ts +497 -0
- package/dist/blocks/schema/fields.js +240 -0
- package/dist/blocks/schema/icons.d.ts +2 -0
- package/dist/blocks/schema/icons.js +26 -0
- package/dist/blocks/schema/index.d.ts +18 -0
- package/dist/blocks/schema/index.js +21 -0
- package/dist/blocks/schema/lint-types.d.ts +16 -0
- package/dist/blocks/schema/lint-types.js +2 -0
- package/dist/blocks/schema/lint.d.ts +32 -0
- package/dist/blocks/schema/lint.js +164 -0
- package/dist/blocks/schema/localize.d.ts +20 -0
- package/dist/blocks/schema/localize.js +29 -0
- package/dist/blocks/schema/localized.d.ts +25 -0
- package/dist/blocks/schema/localized.js +53 -0
- package/dist/blocks/schema/migrate.d.ts +24 -0
- package/dist/blocks/schema/migrate.js +75 -0
- package/dist/blocks/schema/nodes.d.ts +196 -0
- package/dist/blocks/schema/nodes.js +78 -0
- package/dist/blocks/schema/rich-doc-schema.d.ts +32 -0
- package/dist/blocks/schema/rich-doc-schema.js +66 -0
- package/dist/blocks/schema/rich-doc.d.ts +90 -0
- package/dist/blocks/schema/rich-doc.js +62 -0
- package/dist/blocks/schema/safety.d.ts +20 -0
- package/dist/blocks/schema/safety.js +49 -0
- package/dist/blocks/schema/types.d.ts +172 -0
- package/dist/blocks/schema/types.js +3 -0
- package/dist/blocks/schema/validate.d.ts +17 -0
- package/dist/blocks/schema/validate.js +32 -0
- package/dist/blocks/schema/walk.d.ts +22 -0
- package/dist/blocks/schema/walk.js +60 -0
- package/dist/blocks/section-header.d.ts +38 -0
- package/dist/blocks/section-header.js +22 -0
- package/dist/blocks/section.d.ts +59 -0
- package/dist/blocks/section.js +66 -0
- package/dist/blocks/site/_kit-1.d.ts +100 -0
- package/dist/blocks/site/_kit-1.js +135 -0
- package/dist/blocks/site/_site-icon-1.d.ts +6 -0
- package/dist/blocks/site/_site-icon-1.js +15 -0
- package/dist/blocks/site/announcement-bar.d.ts +42 -0
- package/dist/blocks/site/announcement-bar.js +38 -0
- package/dist/blocks/site/announcement-dismiss.d.ts +18 -0
- package/dist/blocks/site/announcement-dismiss.js +28 -0
- package/dist/blocks/site/article-layout.d.ts +65 -0
- package/dist/blocks/site/article-layout.js +88 -0
- package/dist/blocks/site/blog-list.d.ts +79 -0
- package/dist/blocks/site/blog-list.js +78 -0
- package/dist/blocks/site/comparison-table.d.ts +54 -0
- package/dist/blocks/site/comparison-table.js +38 -0
- package/dist/blocks/site/consent-banner.d.ts +52 -0
- package/dist/blocks/site/consent-banner.js +33 -0
- package/dist/blocks/site/contact-dock.d.ts +52 -0
- package/dist/blocks/site/contact-dock.js +55 -0
- package/dist/blocks/site/contact-form.d.ts +60 -0
- package/dist/blocks/site/contact-form.js +45 -0
- package/dist/blocks/site/cta-booking.d.ts +43 -0
- package/dist/blocks/site/cta-booking.js +69 -0
- package/dist/blocks/site/cta.d.ts +74 -0
- package/dist/blocks/site/cta.js +112 -0
- package/dist/blocks/site/embed-facade.d.ts +20 -0
- package/dist/blocks/site/embed-facade.js +21 -0
- package/dist/blocks/site/faq.d.ts +48 -0
- package/dist/blocks/site/faq.js +51 -0
- package/dist/blocks/site/feature-grid.d.ts +46 -0
- package/dist/blocks/site/feature-grid.js +49 -0
- package/dist/blocks/site/feature-tabs.d.ts +22 -0
- package/dist/blocks/site/feature-tabs.js +11 -0
- package/dist/blocks/site/footer.d.ts +68 -0
- package/dist/blocks/site/footer.js +61 -0
- package/dist/blocks/site/gallery-section.d.ts +42 -0
- package/dist/blocks/site/gallery-section.js +47 -0
- package/dist/blocks/site/hero-video.d.ts +15 -0
- package/dist/blocks/site/hero-video.js +31 -0
- package/dist/blocks/site/hero.d.ts +64 -0
- package/dist/blocks/site/hero.js +67 -0
- package/dist/blocks/site/logo-strip.d.ts +42 -0
- package/dist/blocks/site/logo-strip.js +26 -0
- package/dist/blocks/site/map-hours.d.ts +83 -0
- package/dist/blocks/site/map-hours.js +125 -0
- package/dist/blocks/site/media-text.d.ts +45 -0
- package/dist/blocks/site/media-text.js +55 -0
- package/dist/blocks/site/menu-list.d.ts +74 -0
- package/dist/blocks/site/menu-list.js +84 -0
- package/dist/blocks/site/navbar-submenu.d.ts +18 -0
- package/dist/blocks/site/navbar-submenu.js +51 -0
- package/dist/blocks/site/navbar.d.ts +67 -0
- package/dist/blocks/site/navbar.js +64 -0
- package/dist/blocks/site/not-found.d.ts +40 -0
- package/dist/blocks/site/not-found.js +38 -0
- package/dist/blocks/site/page-intro.d.ts +46 -0
- package/dist/blocks/site/page-intro.js +44 -0
- package/dist/blocks/site/pricing-table.d.ts +67 -0
- package/dist/blocks/site/pricing-table.js +82 -0
- package/dist/blocks/site/process.d.ts +36 -0
- package/dist/blocks/site/process.js +41 -0
- package/dist/blocks/site/registry-1.d.ts +720 -0
- package/dist/blocks/site/registry-1.js +783 -0
- package/dist/blocks/site/registry-2.d.ts +914 -0
- package/dist/blocks/site/registry-2.js +563 -0
- package/dist/blocks/site/rich-text-section.d.ts +34 -0
- package/dist/blocks/site/rich-text-section.js +32 -0
- package/dist/blocks/site/shared/brand-icons.d.ts +21 -0
- package/dist/blocks/site/shared/brand-icons.js +56 -0
- package/dist/blocks/site/shared/consent-gate.d.ts +41 -0
- package/dist/blocks/site/shared/consent-gate.js +43 -0
- package/dist/blocks/site/shared/consent-island.d.ts +38 -0
- package/dist/blocks/site/shared/consent-island.js +63 -0
- package/dist/blocks/site/shared/consent.d.ts +49 -0
- package/dist/blocks/site/shared/consent.js +130 -0
- package/dist/blocks/site/shared/contact-form-island.d.ts +41 -0
- package/dist/blocks/site/shared/contact-form-island.js +100 -0
- package/dist/blocks/site/shared/copy-link.d.ts +10 -0
- package/dist/blocks/site/shared/copy-link.js +25 -0
- package/dist/blocks/site/shared/dock-avoid.d.ts +15 -0
- package/dist/blocks/site/shared/dock-avoid.js +107 -0
- package/dist/blocks/site/shared/dock-hours.d.ts +13 -0
- package/dist/blocks/site/shared/dock-hours.js +25 -0
- package/dist/blocks/site/shared/gallery-lightbox.d.ts +12 -0
- package/dist/blocks/site/shared/gallery-lightbox.js +10 -0
- package/dist/blocks/site/shared/island-keys.d.ts +10 -0
- package/dist/blocks/site/shared/island-keys.js +44 -0
- package/dist/blocks/site/shared/kit-2.d.ts +55 -0
- package/dist/blocks/site/shared/kit-2.js +84 -0
- package/dist/blocks/site/shared/labels-2.d.ts +209 -0
- package/dist/blocks/site/shared/labels-2.js +427 -0
- package/dist/blocks/site/shared/open-now.d.ts +22 -0
- package/dist/blocks/site/shared/open-now.js +27 -0
- package/dist/blocks/site/shared/opening-hours.d.ts +117 -0
- package/dist/blocks/site/shared/opening-hours.js +245 -0
- package/dist/blocks/site/shared/site-accent.d.ts +48 -0
- package/dist/blocks/site/shared/site-accent.js +249 -0
- package/dist/blocks/site/shared/site-form.d.ts +79 -0
- package/dist/blocks/site/shared/site-form.js +111 -0
- package/dist/blocks/site/shared/site-routing.d.ts +54 -0
- package/dist/blocks/site/shared/site-routing.js +86 -0
- package/dist/blocks/site/shared/testimonials-carousel.d.ts +17 -0
- package/dist/blocks/site/shared/testimonials-carousel.js +64 -0
- package/dist/blocks/site/site-shell.d.ts +97 -0
- package/dist/blocks/site/site-shell.js +227 -0
- package/dist/blocks/site/stats-band.d.ts +40 -0
- package/dist/blocks/site/stats-band.js +41 -0
- package/dist/blocks/site/team.d.ts +44 -0
- package/dist/blocks/site/team.js +48 -0
- package/dist/blocks/site/testimonials.d.ts +68 -0
- package/dist/blocks/site/testimonials.js +65 -0
- package/dist/blocks/types.d.ts +63 -0
- package/dist/blocks/types.js +2 -0
- package/dist/business/approval-flow.d.ts +152 -0
- package/dist/business/approval-flow.js +221 -0
- package/dist/business/export-core.d.ts +55 -0
- package/dist/business/export-core.js +101 -0
- package/dist/business/export.d.ts +148 -0
- package/dist/business/export.js +264 -0
- package/dist/business/import-core.d.ts +104 -0
- package/dist/business/import-core.js +281 -0
- package/dist/business/import-wizard.d.ts +186 -0
- package/dist/business/import-wizard.js +401 -0
- package/dist/business/index.d.ts +11 -0
- package/dist/business/index.js +20 -0
- package/dist/business/line-items-core.d.ts +129 -0
- package/dist/business/line-items-core.js +183 -0
- package/dist/business/line-items.d.ts +119 -0
- package/dist/business/line-items.js +299 -0
- package/dist/business/multi-step-form.d.ts +131 -0
- package/dist/business/multi-step-form.js +337 -0
- package/dist/business/permission-core.d.ts +59 -0
- package/dist/business/permission-core.js +99 -0
- package/dist/business/permission-matrix.d.ts +158 -0
- package/dist/business/permission-matrix.js +341 -0
- package/dist/business/setup-checklist.d.ts +80 -0
- package/dist/business/setup-checklist.js +92 -0
- package/dist/charts/area-chart.d.ts +35 -0
- package/dist/charts/area-chart.js +33 -0
- package/dist/charts/bar-chart.d.ts +27 -0
- package/dist/charts/bar-chart.js +42 -0
- package/dist/charts/bar-list.d.ts +47 -0
- package/dist/charts/bar-list.js +60 -0
- package/dist/charts/chart-shell.d.ts +173 -0
- package/dist/charts/chart-shell.js +205 -0
- package/dist/charts/chart.d.ts +73 -0
- package/dist/charts/chart.js +106 -0
- package/dist/charts/combo-chart.d.ts +29 -0
- package/dist/charts/combo-chart.js +47 -0
- package/dist/charts/donut-chart.d.ts +33 -0
- package/dist/charts/donut-chart.js +113 -0
- package/dist/charts/funnel.d.ts +51 -0
- package/dist/charts/funnel.js +69 -0
- package/dist/charts/gauge.d.ts +62 -0
- package/dist/charts/gauge.js +104 -0
- package/dist/charts/heatmap.d.ts +81 -0
- package/dist/charts/heatmap.js +338 -0
- package/dist/charts/index.d.ts +12 -0
- package/dist/charts/index.js +14 -0
- package/dist/charts/line-chart.d.ts +21 -0
- package/dist/charts/line-chart.js +36 -0
- package/dist/charts/test-utils.d.ts +2 -0
- package/dist/charts/test-utils.js +38 -0
- package/dist/charts/utils.d.ts +130 -0
- package/dist/charts/utils.js +262 -0
- package/dist/components/accordion.d.ts +47 -0
- package/dist/components/accordion.js +70 -0
- package/dist/components/activity-timeline.d.ts +149 -0
- package/dist/components/activity-timeline.js +122 -0
- package/dist/components/address-field.d.ts +116 -0
- package/dist/components/address-field.js +219 -0
- package/dist/components/alert-dialog.d.ts +99 -0
- package/dist/components/alert-dialog.js +192 -0
- package/dist/components/alert.d.ts +42 -0
- package/dist/components/alert.js +82 -0
- package/dist/components/app-shell.d.ts +159 -0
- package/dist/components/app-shell.js +289 -0
- package/dist/components/attachment-list.d.ts +96 -0
- package/dist/components/attachment-list.js +196 -0
- package/dist/components/audit-log.d.ts +150 -0
- package/dist/components/audit-log.js +205 -0
- package/dist/components/avatar.d.ts +87 -0
- package/dist/components/avatar.js +126 -0
- package/dist/components/badge.d.ts +41 -0
- package/dist/components/badge.js +68 -0
- package/dist/components/breadcrumb.d.ts +78 -0
- package/dist/components/breadcrumb.js +106 -0
- package/dist/components/button-group.d.ts +52 -0
- package/dist/components/button-group.js +77 -0
- package/dist/components/button.d.ts +58 -0
- package/dist/components/button.js +107 -0
- package/dist/components/calendar.d.ts +240 -0
- package/dist/components/calendar.js +483 -0
- package/dist/components/card.d.ts +66 -0
- package/dist/components/card.js +76 -0
- package/dist/components/carousel.d.ts +170 -0
- package/dist/components/carousel.js +348 -0
- package/dist/components/checkbox.d.ts +78 -0
- package/dist/components/checkbox.js +138 -0
- package/dist/components/color-picker.d.ts +112 -0
- package/dist/components/color-picker.js +235 -0
- package/dist/components/combobox.d.ts +136 -0
- package/dist/components/combobox.js +294 -0
- package/dist/components/command.d.ts +275 -0
- package/dist/components/command.js +493 -0
- package/dist/components/comment-thread.d.ts +252 -0
- package/dist/components/comment-thread.js +449 -0
- package/dist/components/context-menu.d.ts +77 -0
- package/dist/components/context-menu.js +82 -0
- package/dist/components/currency-input.d.ts +43 -0
- package/dist/components/currency-input.js +85 -0
- package/dist/components/date-picker.d.ts +141 -0
- package/dist/components/date-picker.js +241 -0
- package/dist/components/date-range-picker.d.ts +91 -0
- package/dist/components/date-range-picker.js +251 -0
- package/dist/components/description-list.d.ts +92 -0
- package/dist/components/description-list.js +42 -0
- package/dist/components/dialog.d.ts +139 -0
- package/dist/components/dialog.js +185 -0
- package/dist/components/dropdown-menu.d.ts +100 -0
- package/dist/components/dropdown-menu.js +102 -0
- package/dist/components/editor-layout.d.ts +166 -0
- package/dist/components/editor-layout.js +237 -0
- package/dist/components/empty-state.d.ts +49 -0
- package/dist/components/empty-state.js +68 -0
- package/dist/components/entity-card.d.ts +83 -0
- package/dist/components/entity-card.js +92 -0
- package/dist/components/file-upload.d.ts +256 -0
- package/dist/components/file-upload.js +490 -0
- package/dist/components/formatted-input.d.ts +177 -0
- package/dist/components/formatted-input.js +399 -0
- package/dist/components/hierarchy-chart.d.ts +190 -0
- package/dist/components/hierarchy-chart.js +890 -0
- package/dist/components/hover-card.d.ts +21 -0
- package/dist/components/hover-card.js +32 -0
- package/dist/components/iban-input.d.ts +38 -0
- package/dist/components/iban-input.js +112 -0
- package/dist/components/id-input.d.ts +65 -0
- package/dist/components/id-input.js +130 -0
- package/dist/components/image.d.ts +130 -0
- package/dist/components/image.js +168 -0
- package/dist/components/input-otp.d.ts +62 -0
- package/dist/components/input-otp.js +73 -0
- package/dist/components/input.d.ts +141 -0
- package/dist/components/input.js +187 -0
- package/dist/components/item.d.ts +96 -0
- package/dist/components/item.js +113 -0
- package/dist/components/kbd.d.ts +64 -0
- package/dist/components/kbd.js +119 -0
- package/dist/components/list-layout.d.ts +144 -0
- package/dist/components/list-layout.js +210 -0
- package/dist/components/native-select.d.ts +33 -0
- package/dist/components/native-select.js +48 -0
- package/dist/components/navigation-menu.d.ts +127 -0
- package/dist/components/navigation-menu.js +129 -0
- package/dist/components/notification-center.d.ts +164 -0
- package/dist/components/notification-center.js +399 -0
- package/dist/components/page-header.d.ts +60 -0
- package/dist/components/page-header.js +60 -0
- package/dist/components/pagination.d.ts +121 -0
- package/dist/components/pagination.js +174 -0
- package/dist/components/phone-input.d.ts +49 -0
- package/dist/components/phone-input.js +266 -0
- package/dist/components/popover.d.ts +38 -0
- package/dist/components/popover.js +69 -0
- package/dist/components/progress.d.ts +79 -0
- package/dist/components/progress.js +97 -0
- package/dist/components/radio-group.d.ts +43 -0
- package/dist/components/radio-group.js +81 -0
- package/dist/components/record-detail-layout.d.ts +167 -0
- package/dist/components/record-detail-layout.js +233 -0
- package/dist/components/record-picker.d.ts +199 -0
- package/dist/components/record-picker.js +650 -0
- package/dist/components/resizable.d.ts +50 -0
- package/dist/components/resizable.js +88 -0
- package/dist/components/scroll-area.d.ts +40 -0
- package/dist/components/scroll-area.js +56 -0
- package/dist/components/select.d.ts +126 -0
- package/dist/components/select.js +137 -0
- package/dist/components/separator.d.ts +31 -0
- package/dist/components/separator.js +41 -0
- package/dist/components/settings-layout.d.ts +134 -0
- package/dist/components/settings-layout.js +180 -0
- package/dist/components/sheet.d.ts +103 -0
- package/dist/components/sheet.js +165 -0
- package/dist/components/sidebar.d.ts +171 -0
- package/dist/components/sidebar.js +421 -0
- package/dist/components/signature-pad.d.ts +85 -0
- package/dist/components/signature-pad.js +283 -0
- package/dist/components/skeleton.d.ts +64 -0
- package/dist/components/skeleton.js +53 -0
- package/dist/components/slider.d.ts +57 -0
- package/dist/components/slider.js +133 -0
- package/dist/components/slug-input.d.ts +69 -0
- package/dist/components/slug-input.js +201 -0
- package/dist/components/sparkline.d.ts +54 -0
- package/dist/components/sparkline.js +122 -0
- package/dist/components/spinner.d.ts +34 -0
- package/dist/components/spinner.js +34 -0
- package/dist/components/stat.d.ts +135 -0
- package/dist/components/stat.js +130 -0
- package/dist/components/status-picker.d.ts +107 -0
- package/dist/components/status-picker.js +175 -0
- package/dist/components/stepper.d.ts +86 -0
- package/dist/components/stepper.js +103 -0
- package/dist/components/switch.d.ts +34 -0
- package/dist/components/switch.js +70 -0
- package/dist/components/table.d.ts +101 -0
- package/dist/components/table.js +72 -0
- package/dist/components/tabs.d.ts +103 -0
- package/dist/components/tabs.js +173 -0
- package/dist/components/tag-input.d.ts +77 -0
- package/dist/components/tag-input.js +305 -0
- package/dist/components/task-list.d.ts +89 -0
- package/dist/components/task-list.js +238 -0
- package/dist/components/time-picker.d.ts +119 -0
- package/dist/components/time-picker.js +399 -0
- package/dist/components/toast.d.ts +87 -0
- package/dist/components/toast.js +142 -0
- package/dist/components/toggle.d.ts +77 -0
- package/dist/components/toggle.js +109 -0
- package/dist/components/tooltip.d.ts +69 -0
- package/dist/components/tooltip.js +105 -0
- package/dist/components/top-bar.d.ts +87 -0
- package/dist/components/top-bar.js +99 -0
- package/dist/components/tree-select.d.ts +89 -0
- package/dist/components/tree-select.js +243 -0
- package/dist/components/tree-view.d.ts +109 -0
- package/dist/components/tree-view.js +170 -0
- package/dist/components/typography.d.ts +107 -0
- package/dist/components/typography.js +128 -0
- package/dist/components/user-picker.d.ts +89 -0
- package/dist/components/user-picker.js +162 -0
- package/dist/components/version-history.d.ts +191 -0
- package/dist/components/version-history.js +431 -0
- package/dist/data-table/csv.d.ts +27 -0
- package/dist/data-table/csv.js +68 -0
- package/dist/data-table/data-table.d.ts +423 -0
- package/dist/data-table/data-table.js +1259 -0
- package/dist/data-table/filter-bar.d.ts +146 -0
- package/dist/data-table/filter-bar.js +569 -0
- package/dist/data-table/filter-fns.d.ts +65 -0
- package/dist/data-table/filter-fns.js +382 -0
- package/dist/data-table/index.d.ts +8 -0
- package/dist/data-table/index.js +9 -0
- package/dist/data-table/saved-views.d.ts +100 -0
- package/dist/data-table/saved-views.js +251 -0
- package/dist/data-table/types.d.ts +168 -0
- package/dist/data-table/types.js +87 -0
- package/dist/data-table/url-state.d.ts +94 -0
- package/dist/data-table/url-state.js +303 -0
- package/dist/data-table/view-state.d.ts +16 -0
- package/dist/data-table/view-state.js +81 -0
- package/dist/editor/_test-setup.d.ts +1 -0
- package/dist/editor/_test-setup.js +33 -0
- package/dist/editor/block-editor.d.ts +137 -0
- package/dist/editor/block-editor.js +280 -0
- package/dist/editor/commands.d.ts +31 -0
- package/dist/editor/commands.js +150 -0
- package/dist/editor/context.d.ts +108 -0
- package/dist/editor/context.js +30 -0
- package/dist/editor/convert.d.ts +66 -0
- package/dist/editor/convert.js +567 -0
- package/dist/editor/drag-handle.d.ts +10 -0
- package/dist/editor/drag-handle.js +69 -0
- package/dist/editor/extensions.d.ts +34 -0
- package/dist/editor/extensions.js +239 -0
- package/dist/editor/index.d.ts +14 -0
- package/dist/editor/index.js +17 -0
- package/dist/editor/inserter.d.ts +12 -0
- package/dist/editor/inserter.js +88 -0
- package/dist/editor/inspector.d.ts +17 -0
- package/dist/editor/inspector.js +65 -0
- package/dist/editor/items.d.ts +62 -0
- package/dist/editor/items.js +195 -0
- package/dist/editor/labels.d.ts +119 -0
- package/dist/editor/labels.js +130 -0
- package/dist/editor/markdown.d.ts +14 -0
- package/dist/editor/markdown.js +209 -0
- package/dist/editor/menus.d.ts +20 -0
- package/dist/editor/menus.js +236 -0
- package/dist/editor/nodes.d.ts +14 -0
- package/dist/editor/nodes.js +201 -0
- package/dist/editor/outline.d.ts +11 -0
- package/dist/editor/outline.js +52 -0
- package/dist/editor/schema-form.d.ts +64 -0
- package/dist/editor/schema-form.js +290 -0
- package/dist/editor/slash-menu.d.ts +15 -0
- package/dist/editor/slash-menu.js +119 -0
- package/dist/fonts/catalog.d.ts +53 -0
- package/dist/fonts/catalog.js +121 -0
- package/dist/fonts/font-picker.d.ts +70 -0
- package/dist/fonts/font-picker.js +225 -0
- package/dist/fonts/index.d.ts +6 -0
- package/dist/fonts/index.js +7 -0
- package/dist/fonts/loader.d.ts +66 -0
- package/dist/fonts/loader.js +161 -0
- package/dist/fonts/pairing-picker.d.ts +31 -0
- package/dist/fonts/pairing-picker.js +81 -0
- package/dist/fonts/pairings.d.ts +84 -0
- package/dist/fonts/pairings.js +149 -0
- package/dist/fonts/provider.d.ts +34 -0
- package/dist/fonts/provider.js +51 -0
- package/dist/form/index.d.ts +1 -0
- package/dist/form/index.js +1 -0
- package/dist/format/core.d.ts +149 -0
- package/dist/format/core.js +357 -0
- package/dist/format/index.d.ts +4 -0
- package/dist/format/index.js +5 -0
- package/dist/format/use-format.d.ts +1 -0
- package/dist/format/use-format.js +3 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +87 -0
- package/dist/lib/cn.d.ts +3 -0
- package/dist/lib/cn.js +16 -0
- package/dist/lib/color.d.ts +64 -0
- package/dist/lib/color.js +177 -0
- package/dist/lib/compose-refs.d.ts +24 -0
- package/dist/lib/compose-refs.js +45 -0
- package/dist/lib/data-islands.d.ts +194 -0
- package/dist/lib/data-islands.js +368 -0
- package/dist/lib/date.d.ts +129 -0
- package/dist/lib/date.js +386 -0
- package/dist/lib/field-context.d.ts +64 -0
- package/dist/lib/field-context.js +62 -0
- package/dist/lib/floating-arrow.d.ts +18 -0
- package/dist/lib/floating-arrow.js +15 -0
- package/dist/lib/islands.d.ts +38 -0
- package/dist/lib/islands.js +40 -0
- package/dist/lib/labels.d.ts +51 -0
- package/dist/lib/labels.js +56 -0
- package/dist/lib/layer.d.ts +36 -0
- package/dist/lib/layer.js +42 -0
- package/dist/lib/locale.d.ts +85 -0
- package/dist/lib/locale.js +105 -0
- package/dist/lib/menu.d.ts +58 -0
- package/dist/lib/menu.js +99 -0
- package/dist/lib/message-format.d.ts +20 -0
- package/dist/lib/message-format.js +116 -0
- package/dist/lib/nav.d.ts +132 -0
- package/dist/lib/nav.js +178 -0
- package/dist/lib/provider.d.ts +100 -0
- package/dist/lib/provider.js +105 -0
- package/dist/lib/public.d.ts +22 -0
- package/dist/lib/public.js +33 -0
- package/dist/lib/sr-name.d.ts +18 -0
- package/dist/lib/sr-name.js +17 -0
- package/dist/lib/text.d.ts +100 -0
- package/dist/lib/text.js +233 -0
- package/dist/lib/theme-scope.d.ts +58 -0
- package/dist/lib/theme-scope.js +59 -0
- package/dist/lib/use-async-list.d.ts +60 -0
- package/dist/lib/use-async-list.js +119 -0
- package/dist/lib/use-container-width.d.ts +19 -0
- package/dist/lib/use-container-width.js +39 -0
- package/dist/lib/use-controllable-state.d.ts +33 -0
- package/dist/lib/use-controllable-state.js +56 -0
- package/dist/lib/use-edge-fade.d.ts +13 -0
- package/dist/lib/use-edge-fade.js +43 -0
- package/dist/lib/use-formatted-input.d.ts +69 -0
- package/dist/lib/use-formatted-input.js +152 -0
- package/dist/lib/use-hotkeys.d.ts +80 -0
- package/dist/lib/use-hotkeys.js +221 -0
- package/dist/lib/use-hover-intent.d.ts +44 -0
- package/dist/lib/use-hover-intent.js +119 -0
- package/dist/lib/use-media-query.d.ts +21 -0
- package/dist/lib/use-media-query.js +28 -0
- package/dist/lib/use-reduced-motion.d.ts +8 -0
- package/dist/lib/use-reduced-motion.js +13 -0
- package/dist/lib/use-tree.d.ts +153 -0
- package/dist/lib/use-tree.js +534 -0
- package/dist/lib/value-format.d.ts +25 -0
- package/dist/lib/value-format.js +56 -0
- package/dist/lib/variants.d.ts +131 -0
- package/dist/lib/variants.js +109 -0
- package/dist/locale-data/core/date.d.ts +24 -0
- package/dist/locale-data/core/date.js +68 -0
- package/dist/locale-data/core/decimal.d.ts +19 -0
- package/dist/locale-data/core/decimal.js +79 -0
- package/dist/locale-data/core/holidays.d.ts +30 -0
- package/dist/locale-data/core/holidays.js +79 -0
- package/dist/locale-data/core/iban.d.ts +29 -0
- package/dist/locale-data/core/iban.js +74 -0
- package/dist/locale-data/core/index.d.ts +7 -0
- package/dist/locale-data/core/index.js +6 -0
- package/dist/locale-data/core/types.d.ts +173 -0
- package/dist/locale-data/core/types.js +6 -0
- package/dist/locale-data/core/week-info.d.ts +23 -0
- package/dist/locale-data/core/week-info.js +85 -0
- package/dist/locale-data/core/working-days.d.ts +27 -0
- package/dist/locale-data/core/working-days.js +133 -0
- package/dist/locale-data/index.d.ts +2 -0
- package/dist/locale-data/index.js +4 -0
- package/dist/locale-data/tr/address-tr.json +1 -0
- package/dist/locale-data/tr/address.d.ts +23 -0
- package/dist/locale-data/tr/address.js +52 -0
- package/dist/locale-data/tr/holidays.d.ts +31 -0
- package/dist/locale-data/tr/holidays.js +81 -0
- package/dist/locale-data/tr/iban.d.ts +21 -0
- package/dist/locale-data/tr/iban.js +45 -0
- package/dist/locale-data/tr/id.d.ts +36 -0
- package/dist/locale-data/tr/id.js +120 -0
- package/dist/locale-data/tr/index.d.ts +12 -0
- package/dist/locale-data/tr/index.js +33 -0
- package/dist/locale-data/tr/kdv.d.ts +91 -0
- package/dist/locale-data/tr/kdv.js +222 -0
- package/dist/locales/en.d.ts +2102 -0
- package/dist/locales/en.js +2088 -0
- package/dist/locales/tr.d.ts +2086 -0
- package/dist/locales/tr.js +2087 -0
- package/dist/media/focal-point.d.ts +42 -0
- package/dist/media/focal-point.js +96 -0
- package/dist/media/gallery-layout.d.ts +19 -0
- package/dist/media/gallery-layout.js +61 -0
- package/dist/media/gallery.d.ts +64 -0
- package/dist/media/gallery.js +138 -0
- package/dist/media/image-cropper.d.ts +86 -0
- package/dist/media/image-cropper.js +170 -0
- package/dist/media/image-field.d.ts +115 -0
- package/dist/media/image-field.js +212 -0
- package/dist/media/index.d.ts +19 -0
- package/dist/media/index.js +23 -0
- package/dist/media/lightbox.d.ts +82 -0
- package/dist/media/lightbox.js +171 -0
- package/dist/media/link-preview.d.ts +97 -0
- package/dist/media/link-preview.js +177 -0
- package/dist/media/media-library.d.ts +244 -0
- package/dist/media/media-library.js +776 -0
- package/dist/media/media-picker.d.ts +96 -0
- package/dist/media/media-picker.js +209 -0
- package/dist/media/media-provider.d.ts +23 -0
- package/dist/media/media-provider.js +44 -0
- package/dist/media/media-utils.d.ts +39 -0
- package/dist/media/media-utils.js +145 -0
- package/dist/media/memory-adapter.d.ts +22 -0
- package/dist/media/memory-adapter.js +162 -0
- package/dist/media/rest-adapter.d.ts +90 -0
- package/dist/media/rest-adapter.js +208 -0
- package/dist/media/serp.d.ts +62 -0
- package/dist/media/serp.js +108 -0
- package/dist/media/test-fixtures.d.ts +3 -0
- package/dist/media/test-fixtures.js +37 -0
- package/dist/media/types.d.ts +183 -0
- package/dist/media/types.js +3 -0
- package/dist/media/use-pan-zoom.d.ts +51 -0
- package/dist/media/use-pan-zoom.js +156 -0
- package/dist/media/use-upload-queue.d.ts +64 -0
- package/dist/media/use-upload-queue.js +115 -0
- package/dist/media/video-player.d.ts +135 -0
- package/dist/media/video-player.js +319 -0
- package/dist/next/index.d.ts +42 -0
- package/dist/next/index.js +36 -0
- package/dist/render/block-renderer.d.ts +56 -0
- package/dist/render/block-renderer.js +137 -0
- package/dist/render/index.d.ts +6 -0
- package/dist/render/index.js +7 -0
- package/dist/render/prepare.d.ts +61 -0
- package/dist/render/prepare.js +119 -0
- package/dist/scheduler/core/duration.d.ts +83 -0
- package/dist/scheduler/core/duration.js +174 -0
- package/dist/scheduler/core/gantt.d.ts +63 -0
- package/dist/scheduler/core/gantt.js +226 -0
- package/dist/scheduler/core/index.d.ts +6 -0
- package/dist/scheduler/core/index.js +7 -0
- package/dist/scheduler/core/layout.d.ts +97 -0
- package/dist/scheduler/core/layout.js +249 -0
- package/dist/scheduler/core/time-scale.d.ts +89 -0
- package/dist/scheduler/core/time-scale.js +173 -0
- package/dist/scheduler/gantt.d.ts +125 -0
- package/dist/scheduler/gantt.js +532 -0
- package/dist/scheduler/index.d.ts +6 -0
- package/dist/scheduler/index.js +7 -0
- package/dist/scheduler/resource-calendar.d.ts +218 -0
- package/dist/scheduler/resource-calendar.js +661 -0
- package/dist/scheduler/scheduler.d.ts +168 -0
- package/dist/scheduler/scheduler.js +768 -0
- package/dist/scheduler/shared.d.ts +63 -0
- package/dist/scheduler/shared.js +145 -0
- package/dist/scheduler/time-tracker.d.ts +138 -0
- package/dist/scheduler/time-tracker.js +403 -0
- package/dist/sortable/core.d.ts +67 -0
- package/dist/sortable/core.js +167 -0
- package/dist/sortable/index.d.ts +8 -0
- package/dist/sortable/index.js +10 -0
- package/dist/sortable/kanban-core.d.ts +70 -0
- package/dist/sortable/kanban-core.js +67 -0
- package/dist/sortable/kanban.d.ts +147 -0
- package/dist/sortable/kanban.js +472 -0
- package/dist/sortable/sortable-list.d.ts +71 -0
- package/dist/sortable/sortable-list.js +76 -0
- package/dist/sortable/sortable-tree.d.ts +101 -0
- package/dist/sortable/sortable-tree.js +450 -0
- package/dist/sortable/sortable.d.ts +188 -0
- package/dist/sortable/sortable.js +485 -0
- package/dist/sortable/use-pointer-drag.d.ts +75 -0
- package/dist/sortable/use-pointer-drag.js +173 -0
- package/dist/sortable/use-time-scale.d.ts +11 -0
- package/dist/sortable/use-time-scale.js +19 -0
- package/dist/styles/base.css +51 -0
- package/dist/styles/blocks.css +949 -0
- package/dist/styles/calendar.css +642 -0
- package/dist/styles/charts.css +264 -0
- package/dist/styles/data.css +474 -0
- package/dist/styles/editors.css +1022 -0
- package/dist/styles/forms.css +406 -0
- package/dist/styles/media.css +515 -0
- package/dist/styles/navigation.css +293 -0
- package/dist/styles/overlays.css +244 -0
- package/dist/styles.css +742 -0
- package/package.json +156 -4
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// OpeningHours — pure, React-free, works in RSC, edge and the browser.
|
|
2
|
+
// Every computation happens in the business's IANA time zone, so "open now" is right for a
|
|
3
|
+
// visitor in another zone and on a server running in UTC. Ranges with close <= open run past
|
|
4
|
+
// midnight ("18:00"–"02:00"); "00:00"–"24:00" (or open == close) is open all day.
|
|
5
|
+
import { formatMessage } from "../../../lib/message-format.js";
|
|
6
|
+
/** Monday-first, the ISO order. */
|
|
7
|
+
export const WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
8
|
+
const DAY_MS = 86_400_000;
|
|
9
|
+
const SHORT_TO_WEEKDAY = { Mon: "mon", Tue: "tue", Wed: "wed", Thu: "thu", Fri: "fri", Sat: "sat", Sun: "sun" };
|
|
10
|
+
const partsCache = new Map();
|
|
11
|
+
function zoneFormat(timeZone) {
|
|
12
|
+
let f = partsCache.get(timeZone);
|
|
13
|
+
if (!f) {
|
|
14
|
+
f = new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit", weekday: "short", hour: "2-digit", minute: "2-digit", hourCycle: "h23" });
|
|
15
|
+
partsCache.set(timeZone, f);
|
|
16
|
+
}
|
|
17
|
+
return f;
|
|
18
|
+
}
|
|
19
|
+
/** The wall-clock date, weekday and minute of `at` in `timeZone`. */
|
|
20
|
+
export function zonedMoment(at, timeZone) {
|
|
21
|
+
const d = at instanceof Date ? at : new Date(at);
|
|
22
|
+
const p = {};
|
|
23
|
+
for (const part of zoneFormat(timeZone).formatToParts(d))
|
|
24
|
+
p[part.type] = part.value;
|
|
25
|
+
const hour = Number(p.hour) % 24;
|
|
26
|
+
return { date: `${p.year}-${p.month}-${p.day}`, weekday: SHORT_TO_WEEKDAY[p.weekday] ?? "mon", minutes: hour * 60 + Number(p.minute) };
|
|
27
|
+
}
|
|
28
|
+
/** "09:30" → 570; "24:00" → 1440. */
|
|
29
|
+
export const toMinutes = (hhmm) => {
|
|
30
|
+
const [h, m] = hhmm.split(":").map(Number);
|
|
31
|
+
return (h ?? 0) * 60 + (m ?? 0);
|
|
32
|
+
};
|
|
33
|
+
const fromMinutes = (min) => {
|
|
34
|
+
const m = ((min % 1440) + 1440) % 1440;
|
|
35
|
+
return `${String(Math.floor(m / 60)).padStart(2, "0")}:${String(m % 60).padStart(2, "0")}`;
|
|
36
|
+
};
|
|
37
|
+
/** Calendar arithmetic on `YYYY-MM-DD` strings (UTC-anchored, never shifts a day). */
|
|
38
|
+
export function addDays(date, n) {
|
|
39
|
+
const [y, m, d] = date.split("-").map(Number);
|
|
40
|
+
const t = Date.UTC(y, m - 1, d) + n * DAY_MS;
|
|
41
|
+
return new Date(t).toISOString().slice(0, 10);
|
|
42
|
+
}
|
|
43
|
+
/** The weekday of a `YYYY-MM-DD` date. */
|
|
44
|
+
export function weekdayOf(date) {
|
|
45
|
+
const [y, m, d] = date.split("-").map(Number);
|
|
46
|
+
const js = new Date(Date.UTC(y, m - 1, d)).getUTCDay(); // 0 = Sunday
|
|
47
|
+
return WEEKDAYS[(js + 6) % 7];
|
|
48
|
+
}
|
|
49
|
+
/** Hours for one date (special days win over the weekly schedule). */
|
|
50
|
+
export function hoursOn(hours, date) {
|
|
51
|
+
const weekday = weekdayOf(date);
|
|
52
|
+
const special = hours.special?.find((s) => s.date === date);
|
|
53
|
+
if (special) {
|
|
54
|
+
const ranges = special.closed ? [] : (special.ranges ?? hours.weekly[weekday] ?? []);
|
|
55
|
+
const note = typeof special.note === "string" ? special.note : undefined;
|
|
56
|
+
return { date, weekday, ranges, closed: ranges.length === 0, special: { note } };
|
|
57
|
+
}
|
|
58
|
+
const ranges = hours.weekly[weekday] ?? [];
|
|
59
|
+
return { date, weekday, ranges, closed: ranges.length === 0 };
|
|
60
|
+
}
|
|
61
|
+
/** A range as [start, end) in minutes from `date`'s midnight; past-midnight ends exceed 1440. */
|
|
62
|
+
function span(r) {
|
|
63
|
+
const a = toMinutes(r.open);
|
|
64
|
+
let b = toMinutes(r.close);
|
|
65
|
+
if (b <= a)
|
|
66
|
+
b += 1440; // past midnight, or open == close → 24 h
|
|
67
|
+
return [a, b];
|
|
68
|
+
}
|
|
69
|
+
/** Open intervals around `date`, in minutes relative to its midnight, merged and sorted. */
|
|
70
|
+
function intervals(hours, date, fromDay, toDay) {
|
|
71
|
+
const out = [];
|
|
72
|
+
for (let d = fromDay; d <= toDay; d++) {
|
|
73
|
+
const day = hoursOn(hours, addDays(date, d));
|
|
74
|
+
for (const r of day.ranges) {
|
|
75
|
+
const [a, b] = span(r);
|
|
76
|
+
out.push([a + d * 1440, b + d * 1440]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
out.sort((x, y) => x[0] - y[0]);
|
|
80
|
+
const merged = [];
|
|
81
|
+
for (const iv of out) {
|
|
82
|
+
const last = merged[merged.length - 1];
|
|
83
|
+
if (last && iv[0] <= last[1])
|
|
84
|
+
last[1] = Math.max(last[1], iv[1]);
|
|
85
|
+
else
|
|
86
|
+
merged.push([...iv]);
|
|
87
|
+
}
|
|
88
|
+
return merged;
|
|
89
|
+
}
|
|
90
|
+
/** True when the business is open at `at` (default now). Handles past-midnight and special days. */
|
|
91
|
+
export function isOpenAt(hours, at = Date.now()) {
|
|
92
|
+
const z = zonedMoment(at, hours.timeZone);
|
|
93
|
+
return intervals(hours, z.date, -1, 0).some(([a, b]) => z.minutes >= a && z.minutes < b);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Open / closing soon / closed at `at`, with the closing or next opening time.
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* hoursStatus(hours, new Date()) // { state: "open", closesAt: "22:00", minutesLeft: 95, … }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function hoursStatus(hours, at = Date.now(), opts = {}) {
|
|
103
|
+
const soon = opts.soonMinutes ?? 60;
|
|
104
|
+
const z = zonedMoment(at, hours.timeZone);
|
|
105
|
+
const day = hoursOn(hours, z.date);
|
|
106
|
+
const base = { today: { date: z.date, weekday: z.weekday }, special: day.special ? { closed: day.closed, note: day.special.note } : undefined };
|
|
107
|
+
const ivs = intervals(hours, z.date, -1, 8);
|
|
108
|
+
const current = ivs.find(([a, b]) => z.minutes >= a && z.minutes < b);
|
|
109
|
+
if (current) {
|
|
110
|
+
const left = current[1] - z.minutes;
|
|
111
|
+
const allDay = left >= 1440;
|
|
112
|
+
return {
|
|
113
|
+
...base,
|
|
114
|
+
state: !allDay && left <= soon ? "closing-soon" : "open",
|
|
115
|
+
closesAt: allDay ? undefined : fromMinutes(current[1]),
|
|
116
|
+
minutesLeft: allDay ? undefined : left,
|
|
117
|
+
allDay: allDay || undefined,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const next = ivs.find(([a]) => a > z.minutes);
|
|
121
|
+
if (!next)
|
|
122
|
+
return { ...base, state: "closed" };
|
|
123
|
+
const dayOffset = Math.floor(next[0] / 1440);
|
|
124
|
+
const date = addDays(z.date, dayOffset);
|
|
125
|
+
return { ...base, state: "closed", minutesLeft: next[0] - z.minutes, opensAt: { time: fromMinutes(next[0]), weekday: weekdayOf(date), dayOffset, date } };
|
|
126
|
+
}
|
|
127
|
+
/* ------------------------------------------------------------------ */
|
|
128
|
+
/* Locale formatting */
|
|
129
|
+
/* ------------------------------------------------------------------ */
|
|
130
|
+
const REF_MONDAY = Date.UTC(2024, 0, 1); // 2024-01-01 was a Monday
|
|
131
|
+
/** Weekday name from the locale ("Monday" / "Pazartesi"). */
|
|
132
|
+
export function weekdayName(day, locale = "en-US", style = "long") {
|
|
133
|
+
const t = REF_MONDAY + WEEKDAYS.indexOf(day) * DAY_MS;
|
|
134
|
+
return new Intl.DateTimeFormat(locale, { weekday: style, timeZone: "UTC" }).format(t);
|
|
135
|
+
}
|
|
136
|
+
/** Local `HH:mm` → "9:00 AM" (en-US) / "09:00" (tr). Whole hours drop ":00" in 12-hour locales. */
|
|
137
|
+
export function formatTime(hhmm, locale = "en-US") {
|
|
138
|
+
const min = toMinutes(hhmm) % 1440;
|
|
139
|
+
const d = Date.UTC(2000, 0, 1, Math.floor(min / 60), min % 60);
|
|
140
|
+
const h12 = new Intl.DateTimeFormat(locale, { hour: "numeric" }).resolvedOptions().hourCycle?.startsWith("h1");
|
|
141
|
+
const opts = h12 && min % 60 === 0 ? { hour: "numeric", timeZone: "UTC" } : { hour: h12 ? "numeric" : "2-digit", minute: "2-digit", timeZone: "UTC" };
|
|
142
|
+
return new Intl.DateTimeFormat(locale, opts).format(d).replace(/[ ]/g, " ");
|
|
143
|
+
}
|
|
144
|
+
/** "09:00–22:00" / "9 AM–10 PM". */
|
|
145
|
+
export const formatRange = (r, locale = "en-US") => `${formatTime(r.open, locale)}–${formatTime(r.close, locale)}`;
|
|
146
|
+
/** First day of the week for a locale: 1 = Monday … 7 = Sunday. */
|
|
147
|
+
export function weekStartOf(locale = "en-US") {
|
|
148
|
+
try {
|
|
149
|
+
const loc = new Intl.Locale(locale);
|
|
150
|
+
const info = loc.getWeekInfo?.() ?? loc.weekInfo;
|
|
151
|
+
if (info?.firstDay)
|
|
152
|
+
return info.firstDay;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
/* fall through */
|
|
156
|
+
}
|
|
157
|
+
const region = locale.split("-")[1]?.toUpperCase();
|
|
158
|
+
return region && ["US", "CA", "JP", "BR", "MX", "IL", "PH", "SA", "IN"].includes(region) ? 7 : 1;
|
|
159
|
+
}
|
|
160
|
+
/** The week in the locale's order (Sunday first for en-US, Monday first for tr). */
|
|
161
|
+
export function orderedWeek(locale = "en-US") {
|
|
162
|
+
const start = weekStartOf(locale) - 1;
|
|
163
|
+
return [...WEEKDAYS.slice(start), ...WEEKDAYS.slice(0, start)];
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The 7 rows of an hours table in locale order. With `at`, rows carry this week's dates, so
|
|
167
|
+
* special days (holidays) replace the weekly hours, and `today` is marked.
|
|
168
|
+
*/
|
|
169
|
+
export function weekRows(hours, opts = {}) {
|
|
170
|
+
const locale = opts.locale ?? "en-US";
|
|
171
|
+
const z = opts.at !== undefined ? zonedMoment(opts.at, hours.timeZone) : undefined;
|
|
172
|
+
const todayIdx = z ? WEEKDAYS.indexOf(z.weekday) : -1;
|
|
173
|
+
return orderedWeek(locale).map((weekday) => {
|
|
174
|
+
let ranges = hours.weekly[weekday] ?? [];
|
|
175
|
+
let date;
|
|
176
|
+
let note;
|
|
177
|
+
if (z) {
|
|
178
|
+
// Upcoming occurrence of this weekday (today counts), so the table shows the coming week.
|
|
179
|
+
const offset = (WEEKDAYS.indexOf(weekday) - todayIdx + 7) % 7;
|
|
180
|
+
date = addDays(z.date, offset);
|
|
181
|
+
const day = hoursOn(hours, date);
|
|
182
|
+
ranges = day.ranges;
|
|
183
|
+
note = day.special?.note;
|
|
184
|
+
}
|
|
185
|
+
const allDay = ranges.some((r) => span(r)[1] - span(r)[0] >= 1440);
|
|
186
|
+
return {
|
|
187
|
+
weekday,
|
|
188
|
+
label: weekdayName(weekday, locale, opts.style ?? "long"),
|
|
189
|
+
ranges,
|
|
190
|
+
text: ranges.map((r) => formatRange(r, locale)).join(", "),
|
|
191
|
+
closed: ranges.length === 0,
|
|
192
|
+
allDay,
|
|
193
|
+
today: z ? z.weekday === weekday : false,
|
|
194
|
+
date,
|
|
195
|
+
note,
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** Consecutive days with identical hours collapsed: [{ days: "Mon–Fri", text: "09:00–18:00" }]. */
|
|
200
|
+
export function summarizeHours(hours, locale = "en-US") {
|
|
201
|
+
const rows = weekRows(hours, { locale, style: "short" });
|
|
202
|
+
const groups = [];
|
|
203
|
+
for (const r of rows) {
|
|
204
|
+
const key = r.text || "closed";
|
|
205
|
+
const g = groups[groups.length - 1];
|
|
206
|
+
if (g && g.key === key)
|
|
207
|
+
g.to = r;
|
|
208
|
+
else
|
|
209
|
+
groups.push({ from: r, to: r, key });
|
|
210
|
+
}
|
|
211
|
+
return groups.map((g) => ({ days: g.from === g.to ? g.from.label : `${g.from.label}–${g.to.label}`, text: g.from.text, closed: g.from.closed }));
|
|
212
|
+
}
|
|
213
|
+
const SCHEMA_DAY = { mon: "Monday", tue: "Tuesday", wed: "Wednesday", thu: "Thursday", fri: "Friday", sat: "Saturday", sun: "Sunday" };
|
|
214
|
+
/** schema.org `OpeningHoursSpecification[]` for JSON-LD (weekly + special days). */
|
|
215
|
+
export function openingHoursJsonLd(hours) {
|
|
216
|
+
const out = [];
|
|
217
|
+
for (const day of WEEKDAYS) {
|
|
218
|
+
for (const r of hours.weekly[day] ?? [])
|
|
219
|
+
out.push({ "@type": "OpeningHoursSpecification", dayOfWeek: `https://schema.org/${SCHEMA_DAY[day]}`, opens: r.open, closes: r.close === "24:00" ? "23:59" : r.close });
|
|
220
|
+
}
|
|
221
|
+
for (const s of hours.special ?? []) {
|
|
222
|
+
if (s.closed || !s.ranges?.length)
|
|
223
|
+
out.push({ "@type": "OpeningHoursSpecification", validFrom: s.date, validThrough: s.date, opens: "00:00", closes: "00:00" });
|
|
224
|
+
else
|
|
225
|
+
for (const r of s.ranges)
|
|
226
|
+
out.push({ "@type": "OpeningHoursSpecification", validFrom: s.date, validThrough: s.date, opens: r.open, closes: r.close });
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
/** The status sentence: "Open now · closes at 22:00", "Closed · opens Thu at 12:00". */
|
|
231
|
+
export function describeStatus(s, labels, locale = "en-US") {
|
|
232
|
+
const f = (tpl, vars) => formatMessage(tpl, vars, locale);
|
|
233
|
+
if (s.state !== "closed") {
|
|
234
|
+
if (s.allDay)
|
|
235
|
+
return { head: f(labels.openNow), tail: f(labels.open24h) };
|
|
236
|
+
const tail = f(labels.closesAt, { time: formatTime(s.closesAt, locale) });
|
|
237
|
+
return { head: f(s.state === "closing-soon" ? labels.closingSoon : labels.openNow), tail };
|
|
238
|
+
}
|
|
239
|
+
const head = s.special?.closed ? (s.special.note ? `${f(labels.closedToday)} · ${s.special.note}` : f(labels.closedToday)) : f(labels.closed);
|
|
240
|
+
if (!s.opensAt)
|
|
241
|
+
return { head };
|
|
242
|
+
const time = formatTime(s.opensAt.time, locale);
|
|
243
|
+
const tail = s.opensAt.dayOffset === 0 ? f(labels.opensTodayAt, { time }) : f(labels.opensAt, { day: weekdayName(s.opensAt.weekday, locale, "short"), time });
|
|
244
|
+
return { head, tail };
|
|
245
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Text contrast target (WCAG 1.4.3 AA). */
|
|
2
|
+
export declare const SITE_TEXT_CONTRAST = 4.5;
|
|
3
|
+
/** Built-in accent presets of styles.css; these are used by name, not recomputed. */
|
|
4
|
+
export declare const BUILT_IN_SITE_ACCENTS: readonly ["lime", "ocean", "ember", "rose", "graphite"];
|
|
5
|
+
export declare const isBuiltInAccent: (v: string) => boolean;
|
|
6
|
+
export interface SiteAccentPalette {
|
|
7
|
+
/** The colour as given. */
|
|
8
|
+
source: string;
|
|
9
|
+
base: string;
|
|
10
|
+
fg: string;
|
|
11
|
+
inkLight: string;
|
|
12
|
+
inkDark: string;
|
|
13
|
+
/** Dark-mode fill, when the base would vanish on dark surfaces (or `dark` was given). */
|
|
14
|
+
dark?: {
|
|
15
|
+
base: string;
|
|
16
|
+
fg: string;
|
|
17
|
+
};
|
|
18
|
+
/** Measured contrast ratios (rounded down). */
|
|
19
|
+
contrast: {
|
|
20
|
+
fgOnBase: number;
|
|
21
|
+
inkLightOnLight: number;
|
|
22
|
+
inkDarkOnDark: number;
|
|
23
|
+
darkFgOnBase?: number;
|
|
24
|
+
};
|
|
25
|
+
/** What was adjusted, in plain words. Empty = used as given. */
|
|
26
|
+
warnings: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface SiteAccentOptions {
|
|
29
|
+
/** Explicit dark-mode fill. */
|
|
30
|
+
dark?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The four accent variables for one brand colour (hex, rgb() or oklch()).
|
|
34
|
+
* Throws a TypeError when the colour cannot be read.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* siteAccent("#B4532A"); // { base: "oklch(…)", fg: "#fff", inkLight: "oklch(…)", inkDark: "oklch(…)", … }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function siteAccent(colour: string, options?: SiteAccentOptions): SiteAccentPalette;
|
|
41
|
+
/** A stable attribute value for a palette (`b4532a`-like), safe inside a quoted CSS selector. */
|
|
42
|
+
export declare function siteAccentKey(colour: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* The scoped stylesheet for a palette under `[data-site-accent="<key>"]`: the four variables,
|
|
45
|
+
* plus the dark-mode fill for dark pages, dark sections inside the site and light sections
|
|
46
|
+
* inside those. Pair it with `data-accent` on the same element so the derived values recompute.
|
|
47
|
+
*/
|
|
48
|
+
export declare function siteAccentCss(palette: SiteAccentPalette, key: string): string;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site accent: one brand colour → the kit's four accent variables, with WCAG 2.x text contrast
|
|
3
|
+
* guaranteed (the same rules as `vamos-ui brand`, so a site and its panel derive identical
|
|
4
|
+
* values from one colour). Pure, React-free, server-safe.
|
|
5
|
+
*
|
|
6
|
+
* --accent-base fill (primary button, active state)
|
|
7
|
+
* --accent-fg text on the fill ≥ 4.5:1 on --accent-base
|
|
8
|
+
* --accent-ink-light accent text on light surfaces ≥ 4.5:1 on the light scene and on accent-soft over it
|
|
9
|
+
* --accent-ink-dark accent text on dark surfaces ≥ 4.5:1 on dark surface-2 and on accent-soft over it
|
|
10
|
+
*
|
|
11
|
+
* Very dark colours get a lighter dark-mode fill so buttons stay visible on dark surfaces.
|
|
12
|
+
*/
|
|
13
|
+
import { parseColor as parseRgba } from "../../../lib/color.js";
|
|
14
|
+
/** Text contrast target (WCAG 1.4.3 AA). */
|
|
15
|
+
export const SITE_TEXT_CONTRAST = 4.5;
|
|
16
|
+
const UI_CONTRAST = 3;
|
|
17
|
+
/** Hardest surfaces of each theme in styles.css (light scene, dark surface-2) and the plain surfaces. */
|
|
18
|
+
const SURFACES = {
|
|
19
|
+
light: { surface: "#ffffff", hardest: "#eef0ea" },
|
|
20
|
+
dark: { surface: "#15180f", hardest: "#1b1e15" },
|
|
21
|
+
};
|
|
22
|
+
/** Built-in accent presets of styles.css; these are used by name, not recomputed. */
|
|
23
|
+
export const BUILT_IN_SITE_ACCENTS = ["lime", "ocean", "ember", "rose", "graphite"];
|
|
24
|
+
export const isBuiltInAccent = (v) => BUILT_IN_SITE_ACCENTS.includes(v);
|
|
25
|
+
// ---------------------------------------------------------------- colour maths (sRGB 0–1)
|
|
26
|
+
const toLinear = (v) => (v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
|
|
27
|
+
const toGamma = (v) => (v <= 0.0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055);
|
|
28
|
+
function rgbToOklch(r, g, b) {
|
|
29
|
+
const lr = toLinear(r);
|
|
30
|
+
const lg = toLinear(g);
|
|
31
|
+
const lb = toLinear(b);
|
|
32
|
+
const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
|
|
33
|
+
const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
|
|
34
|
+
const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
|
|
35
|
+
const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
|
|
36
|
+
const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
|
|
37
|
+
const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
|
|
38
|
+
const c = Math.sqrt(A * A + B * B);
|
|
39
|
+
let h = c < 1e-4 ? 0 : (Math.atan2(B, A) * 180) / Math.PI;
|
|
40
|
+
if (h < 0)
|
|
41
|
+
h += 360;
|
|
42
|
+
return { l: L, c, h };
|
|
43
|
+
}
|
|
44
|
+
/** OKLCH → sRGB, unclamped (may leave 0–1 when out of gamut). */
|
|
45
|
+
function oklchToRgbRaw({ l: L, c, h }) {
|
|
46
|
+
const rad = (h * Math.PI) / 180;
|
|
47
|
+
const a = c * Math.cos(rad);
|
|
48
|
+
const b = c * Math.sin(rad);
|
|
49
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
50
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
51
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
52
|
+
const g = (v) => (v < 0 ? -toGamma(-v) : toGamma(v));
|
|
53
|
+
return {
|
|
54
|
+
r: g(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
|
55
|
+
g: g(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
|
56
|
+
b: g(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const EPS = 1e-6;
|
|
60
|
+
const inGamut = (c) => {
|
|
61
|
+
const { r, g, b } = oklchToRgbRaw(c);
|
|
62
|
+
return [r, g, b].every((v) => v >= -EPS && v <= 1 + EPS);
|
|
63
|
+
};
|
|
64
|
+
/** Into sRGB by lowering chroma (hue and lightness kept). */
|
|
65
|
+
function gamutMap(color) {
|
|
66
|
+
const base = { ...color, l: Math.min(1, Math.max(0, color.l)) };
|
|
67
|
+
if (inGamut(base))
|
|
68
|
+
return base;
|
|
69
|
+
let lo = 0;
|
|
70
|
+
let hi = base.c;
|
|
71
|
+
for (let i = 0; i < 24; i++) {
|
|
72
|
+
const mid = (lo + hi) / 2;
|
|
73
|
+
if (inGamut({ ...base, c: mid }))
|
|
74
|
+
lo = mid;
|
|
75
|
+
else
|
|
76
|
+
hi = mid;
|
|
77
|
+
}
|
|
78
|
+
return { ...base, c: lo };
|
|
79
|
+
}
|
|
80
|
+
const trim = (n, digits) => {
|
|
81
|
+
const s = n.toFixed(digits);
|
|
82
|
+
return s.includes(".") ? s.replace(/0+$/, "").replace(/\.$/, "") : s;
|
|
83
|
+
};
|
|
84
|
+
/** `oklch(0.64 0.17 245)`: the house format for accent values. */
|
|
85
|
+
function formatOklch(color) {
|
|
86
|
+
const c = gamutMap(color);
|
|
87
|
+
const chroma = c.c < 0.0005 ? 0 : c.c;
|
|
88
|
+
return `oklch(${trim(c.l, 3)} ${trim(chroma, 3)} ${trim(chroma === 0 ? 0 : c.h, 1)})`;
|
|
89
|
+
}
|
|
90
|
+
const parse = (input) => {
|
|
91
|
+
const rgba = parseRgba(input);
|
|
92
|
+
return rgba ? rgbToOklch(rgba.r / 255, rgba.g / 255, rgba.b / 255) : null;
|
|
93
|
+
};
|
|
94
|
+
function luminance(color) {
|
|
95
|
+
const raw = oklchToRgbRaw(gamutMap(color));
|
|
96
|
+
const ch = (v) => toLinear(Math.min(1, Math.max(0, v)));
|
|
97
|
+
return 0.2126 * ch(raw.r) + 0.7152 * ch(raw.g) + 0.0722 * ch(raw.b);
|
|
98
|
+
}
|
|
99
|
+
function contrast(a, b) {
|
|
100
|
+
const pa = typeof a === "string" ? parse(a) : a;
|
|
101
|
+
const pb = typeof b === "string" ? parse(b) : b;
|
|
102
|
+
if (!pa || !pb)
|
|
103
|
+
return NaN;
|
|
104
|
+
const la = luminance(pa);
|
|
105
|
+
const lb = luminance(pb);
|
|
106
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
107
|
+
}
|
|
108
|
+
/** Re-reads a formatted value: what the stylesheet gets is what is judged. */
|
|
109
|
+
const asWritten = (c) => parseOklchString(formatOklch(c));
|
|
110
|
+
function parseOklchString(s) {
|
|
111
|
+
const m = /oklch\(([\d.]+) ([\d.]+) ([\d.]+)\)/.exec(s);
|
|
112
|
+
return { l: Number(m[1]), c: Number(m[2]), h: Number(m[3]) };
|
|
113
|
+
}
|
|
114
|
+
const roundDown = (n) => Math.floor(n * 100) / 100;
|
|
115
|
+
/**
|
|
116
|
+
* `color-mix(in oklch, base p%, transparent)` painted on `bg`: the browser blends the
|
|
117
|
+
* translucent colour in gamma-encoded sRGB. That is the `--accent-soft` chip behind accent text.
|
|
118
|
+
*/
|
|
119
|
+
function softOn(base, bg, amount) {
|
|
120
|
+
const b = parse(bg);
|
|
121
|
+
const fg = oklchToRgbRaw(gamutMap(base));
|
|
122
|
+
const bgRgb = oklchToRgbRaw(b);
|
|
123
|
+
const mix = (f, k) => Math.min(1, Math.max(0, f)) * amount + Math.min(1, Math.max(0, k)) * (1 - amount);
|
|
124
|
+
return rgbToOklch(mix(fg.r, bgRgb.r), mix(fg.g, bgRgb.g), mix(fg.b, bgRgb.b));
|
|
125
|
+
}
|
|
126
|
+
/** Accent-soft amounts of styles.css (light 22 %, dark 16 %). */
|
|
127
|
+
const SOFT = { light: 0.22, dark: 0.16 };
|
|
128
|
+
const WHITE = { l: 1, c: 0, h: 0 };
|
|
129
|
+
const darkInk = (h) => ({ l: 0.18, c: 0.02, h });
|
|
130
|
+
const fgString = (fg) => (fg.l === 1 && fg.c === 0 ? "#fff" : formatOklch(fg));
|
|
131
|
+
function seekLightness(from, dir, ok) {
|
|
132
|
+
for (let i = 0; i <= 200; i++) {
|
|
133
|
+
const l = from.l + dir * i * 0.005;
|
|
134
|
+
if (l < 0 || l > 1)
|
|
135
|
+
break;
|
|
136
|
+
const c = gamutMap({ ...from, l });
|
|
137
|
+
if (ok(asWritten(c)))
|
|
138
|
+
return c;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function fillWithText(base, label, warnings) {
|
|
143
|
+
const cw = contrast(WHITE, base);
|
|
144
|
+
const cd = contrast(darkInk(base.h), base);
|
|
145
|
+
if (Math.max(cw, cd) >= SITE_TEXT_CONTRAST)
|
|
146
|
+
return { base, fg: cw >= cd ? WHITE : darkInk(base.h) };
|
|
147
|
+
const target = SITE_TEXT_CONTRAST + 0.05;
|
|
148
|
+
const forWhite = seekLightness(base, -1, (c) => contrast(WHITE, c) >= target);
|
|
149
|
+
const forDark = seekLightness(base, 1, (c) => contrast(darkInk(c.h), c) >= target);
|
|
150
|
+
const dW = forWhite ? base.l - forWhite.l : Infinity;
|
|
151
|
+
const dD = forDark ? forDark.l - base.l : Infinity;
|
|
152
|
+
const useWhite = dW <= dD;
|
|
153
|
+
const next = (useWhite ? forWhite : forDark) ?? base;
|
|
154
|
+
warnings.push(`${label} lightness ${base.l.toFixed(3)} → ${next.l.toFixed(3)} so ${useWhite ? "white" : "dark"} text on it reaches 4.5:1.`);
|
|
155
|
+
return { base: next, fg: useWhite ? WHITE : darkInk(next.h) };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The four accent variables for one brand colour (hex, rgb() or oklch()).
|
|
159
|
+
* Throws a TypeError when the colour cannot be read.
|
|
160
|
+
*
|
|
161
|
+
* ```ts
|
|
162
|
+
* siteAccent("#B4532A"); // { base: "oklch(…)", fg: "#fff", inkLight: "oklch(…)", inkDark: "oklch(…)", … }
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export function siteAccent(colour, options = {}) {
|
|
166
|
+
const parsed = parse(colour);
|
|
167
|
+
if (!parsed)
|
|
168
|
+
throw new TypeError(`"${colour}" is not a colour. Use hex (#1d4ed8), rgb() or oklch().`);
|
|
169
|
+
const warnings = [];
|
|
170
|
+
let base = gamutMap(parsed);
|
|
171
|
+
// 1. A near-white fill would lose its edge on white surfaces.
|
|
172
|
+
if (contrast(base, SURFACES.light.surface) < 1.25) {
|
|
173
|
+
const darker = seekLightness(base, -1, (c) => contrast(c, SURFACES.light.surface) >= 1.35);
|
|
174
|
+
if (darker) {
|
|
175
|
+
warnings.push(`Near-white colour: fill darkened so buttons stay visible on white.`);
|
|
176
|
+
base = darker;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// 2. Text on the fill.
|
|
180
|
+
const fill = fillWithText(base, "Fill", warnings);
|
|
181
|
+
base = fill.base;
|
|
182
|
+
const fg = fill.fg;
|
|
183
|
+
// 3. Ink on light surfaces: the scene and an accent-soft chip on it (accent badges, selected rows).
|
|
184
|
+
const softLight = softOn(base, SURFACES.light.hardest, SOFT.light);
|
|
185
|
+
const inkLightOk = (c) => Math.min(contrast(c, SURFACES.light.hardest), contrast(c, softLight)) >= SITE_TEXT_CONTRAST + 0.05;
|
|
186
|
+
let inkLight = base;
|
|
187
|
+
if (!inkLightOk(asWritten(base))) {
|
|
188
|
+
inkLight = seekLightness(base, -1, inkLightOk) ?? { ...base, l: 0.3 };
|
|
189
|
+
}
|
|
190
|
+
// 4. Dark-mode fill: explicit, or derived when the fill disappears on dark surfaces.
|
|
191
|
+
let dark;
|
|
192
|
+
if (options.dark) {
|
|
193
|
+
const pd = parse(options.dark);
|
|
194
|
+
if (!pd)
|
|
195
|
+
throw new TypeError(`"${options.dark}" (dark) is not a colour.`);
|
|
196
|
+
dark = fillWithText(gamutMap(pd), "Dark-mode fill", warnings);
|
|
197
|
+
}
|
|
198
|
+
else if (contrast(base, SURFACES.dark.surface) < 1.6) {
|
|
199
|
+
const lifted = base.c < 0.04
|
|
200
|
+
? gamutMap({ ...base, l: Math.min(0.95, Math.max(0.88, 1 - base.l)) })
|
|
201
|
+
: seekLightness(base, 1, (c) => contrast(c, SURFACES.dark.surface) >= UI_CONTRAST) ?? { ...base, l: 0.7 };
|
|
202
|
+
dark = fillWithText(lifted, "Dark-mode fill", warnings);
|
|
203
|
+
warnings.push(`Very dark colour: dark mode uses a lighter fill so buttons stay visible.`);
|
|
204
|
+
}
|
|
205
|
+
// 5. Ink on dark surfaces: a light shade of the (dark-mode) fill.
|
|
206
|
+
const from = dark?.base ?? base;
|
|
207
|
+
const softDark = softOn(from, SURFACES.dark.hardest, SOFT.dark);
|
|
208
|
+
const inkDarkOk = (c) => Math.min(contrast(c, SURFACES.dark.hardest), contrast(c, softDark)) >= SITE_TEXT_CONTRAST + 0.05;
|
|
209
|
+
let inkDark = from;
|
|
210
|
+
if (from.l < 0.7 || !inkDarkOk(asWritten(from))) {
|
|
211
|
+
const start = gamutMap({ ...from, l: Math.max(from.l, 0.7), c: from.l < 0.7 ? from.c * 0.75 : from.c });
|
|
212
|
+
inkDark = inkDarkOk(asWritten(start)) ? start : seekLightness(start, 1, inkDarkOk) ?? { ...start, l: 0.9 };
|
|
213
|
+
}
|
|
214
|
+
const out = { base: formatOklch(base), fg: fgString(fg), inkLight: formatOklch(inkLight), inkDark: formatOklch(inkDark) };
|
|
215
|
+
const palette = {
|
|
216
|
+
source: colour,
|
|
217
|
+
...out,
|
|
218
|
+
contrast: {
|
|
219
|
+
fgOnBase: roundDown(contrast(out.fg, out.base)),
|
|
220
|
+
inkLightOnLight: roundDown(contrast(out.inkLight, SURFACES.light.hardest)),
|
|
221
|
+
inkDarkOnDark: roundDown(contrast(out.inkDark, SURFACES.dark.hardest)),
|
|
222
|
+
},
|
|
223
|
+
warnings,
|
|
224
|
+
};
|
|
225
|
+
if (dark) {
|
|
226
|
+
palette.dark = { base: formatOklch(dark.base), fg: fgString(dark.fg) };
|
|
227
|
+
palette.contrast.darkFgOnBase = roundDown(contrast(palette.dark.fg, palette.dark.base));
|
|
228
|
+
}
|
|
229
|
+
return palette;
|
|
230
|
+
}
|
|
231
|
+
/** A stable attribute value for a palette (`b4532a`-like), safe inside a quoted CSS selector. */
|
|
232
|
+
export function siteAccentKey(colour) {
|
|
233
|
+
return colour.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 48) || "accent";
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* The scoped stylesheet for a palette under `[data-site-accent="<key>"]`: the four variables,
|
|
237
|
+
* plus the dark-mode fill for dark pages, dark sections inside the site and light sections
|
|
238
|
+
* inside those. Pair it with `data-accent` on the same element so the derived values recompute.
|
|
239
|
+
*/
|
|
240
|
+
export function siteAccentCss(palette, key) {
|
|
241
|
+
const s = `[data-site-accent="${key.replace(/["\\]/g, "")}"]`;
|
|
242
|
+
const light = `--accent-base:${palette.base};--accent-fg:${palette.fg};`;
|
|
243
|
+
let css = `${s}{${light}--accent-ink-light:${palette.inkLight};--accent-ink-dark:${palette.inkDark};}`;
|
|
244
|
+
if (palette.dark) {
|
|
245
|
+
css += `.dark${s},.dark ${s},${s} .dark{--accent-base:${palette.dark.base};--accent-fg:${palette.dark.fg};}`;
|
|
246
|
+
css += `.dark ${s} .light,.dark${s} .light,${s} .dark .light{${light}}`;
|
|
247
|
+
}
|
|
248
|
+
return css;
|
|
249
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Site2BlockLabels } from "./labels-2.js";
|
|
2
|
+
export type ContactFieldName = "name" | "email" | "phone" | "company" | "subject" | "budget" | "message" | "date";
|
|
3
|
+
export interface ContactFieldConfig {
|
|
4
|
+
name: ContactFieldName;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
/** Overrides the built-in label ("Full name"). */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** subject / budget: a select with these options instead of free text. */
|
|
9
|
+
options?: string[];
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
hint?: string;
|
|
12
|
+
/** Max characters. Defaults: message 2000, others 160. */
|
|
13
|
+
max?: number;
|
|
14
|
+
}
|
|
15
|
+
/** What the action returns; the form renders from it (also without JS, after a full round trip). */
|
|
16
|
+
export interface SiteFormState {
|
|
17
|
+
status: "idle" | "success" | "error" | "invalid";
|
|
18
|
+
/** Field name → error message. */
|
|
19
|
+
errors?: Partial<Record<string, string>>;
|
|
20
|
+
/** Submitted values, so a failed submit keeps what the visitor typed. */
|
|
21
|
+
values?: Record<string, string>;
|
|
22
|
+
/** Server error detail (shown with the fallback phone number). */
|
|
23
|
+
message?: string;
|
|
24
|
+
/** Changes on every submit (remounts the fields with `values`). */
|
|
25
|
+
ts?: number;
|
|
26
|
+
}
|
|
27
|
+
export type SiteFormAction = (prev: SiteFormState, data: FormData) => Promise<SiteFormState>;
|
|
28
|
+
/** Name of the honeypot input. Real visitors never see or fill it. */
|
|
29
|
+
export declare const HONEYPOT_FIELD = "website";
|
|
30
|
+
/** Hidden render timestamp for the time trap. */
|
|
31
|
+
export declare const TIMESTAMP_FIELD = "_ts";
|
|
32
|
+
export declare const PRIVACY_FIELD = "privacy";
|
|
33
|
+
export declare const MARKETING_FIELD = "marketing";
|
|
34
|
+
/** The label shown for a field: config label → `labels` → English. */
|
|
35
|
+
export declare function contactFieldLabel(field: ContactFieldConfig, labels?: Site2BlockLabels | Record<string, string>): string;
|
|
36
|
+
/** True for honeypot hits and submits faster than `minMs` after render (bots). */
|
|
37
|
+
export declare function isSpam(data: FormData, opts?: {
|
|
38
|
+
minMs?: number;
|
|
39
|
+
now?: number;
|
|
40
|
+
}): boolean;
|
|
41
|
+
export interface ContactValidation {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
/** Honeypot / time trap hit: answer with a fake success, store nothing. */
|
|
44
|
+
spam: boolean;
|
|
45
|
+
values: Record<string, string>;
|
|
46
|
+
errors: Partial<Record<string, string>>;
|
|
47
|
+
privacy: boolean;
|
|
48
|
+
marketing: boolean;
|
|
49
|
+
/** A ready state for `useActionState` (invalid with errors, or success for spam). */
|
|
50
|
+
state: SiteFormState;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validates a submission against the field config the form was rendered with. Call it first in
|
|
54
|
+
* the app's server action — the browser runs it too, but only the server's verdict counts.
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* "use server";
|
|
58
|
+
* export async function contact(prev: SiteFormState, data: FormData) {
|
|
59
|
+
* const v = validateContactForm(fields, data, { privacyCheckbox: true });
|
|
60
|
+
* if (v.spam) return { status: "success" };
|
|
61
|
+
* if (!v.ok) return v.state;
|
|
62
|
+
* await crm.createLead(v.values);
|
|
63
|
+
* return { status: "success" };
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function validateContactForm(fields: readonly ContactFieldConfig[], data: FormData, opts?: {
|
|
68
|
+
privacyCheckbox?: boolean;
|
|
69
|
+
labels?: Site2BlockLabels | Record<string, string>;
|
|
70
|
+
locale?: string;
|
|
71
|
+
spamMinMs?: number;
|
|
72
|
+
}): ContactValidation;
|
|
73
|
+
/** HTML constraints for a field (the no-JS path relies on these). */
|
|
74
|
+
export declare function fieldConstraints(f: ContactFieldConfig): {
|
|
75
|
+
type: string;
|
|
76
|
+
autoComplete?: string;
|
|
77
|
+
inputMode?: "tel" | "email" | "text";
|
|
78
|
+
maxLength: number;
|
|
79
|
+
};
|