@cosmicdrift/kumiko-renderer 0.373.0 → 0.377.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/README.md +2 -2
- package/dist/app/action-form-support.d.ts +58 -0
- package/dist/app/action-form-support.js +96 -0
- package/dist/app/client-actions.d.ts +46 -0
- package/dist/app/client-actions.js +60 -0
- package/dist/app/column-renderers.js +1 -1
- package/dist/app/config-edit-shim.js +1 -1
- package/dist/app/content-preview.js +1 -6
- package/dist/app/custom-screens.js +1 -1
- package/dist/app/dashboard-body.d.ts +1 -1
- package/dist/app/entity-list-expanded-row.d.ts +1 -1
- package/dist/app/entity-list-expanded-row.js +4 -0
- package/dist/app/form-schema.d.ts +1 -1
- package/dist/app/form-schema.js +2 -2
- package/dist/app/header-actions-bar.d.ts +19 -0
- package/dist/app/header-actions-bar.js +30 -0
- package/dist/app/kumiko-screen.d.ts +1 -1
- package/dist/app/kumiko-screen.js +134 -66
- package/dist/app/list-empty-content.d.ts +2 -2
- package/dist/app/list-empty-content.js +3 -2
- package/dist/app/list-facets.d.ts +1 -1
- package/dist/app/nav.js +2 -2
- package/dist/app/query-state-banners.d.ts +1 -1
- package/dist/app/reference-link.d.ts +1 -1
- package/dist/app/row-actions.d.ts +10 -2
- package/dist/app/row-actions.js +85 -6
- package/dist/app/secret-mint-body.d.ts +1 -1
- package/dist/app/secrets-edit-body.d.ts +1 -1
- package/dist/app/use-record-actions.d.ts +12 -0
- package/dist/app/use-record-actions.js +30 -0
- package/dist/app/write-failed-error.d.ts +1 -1
- package/dist/components/availability-options-select.d.ts +1 -1
- package/dist/components/embedded-list-field.d.ts +1 -1
- package/dist/components/embedded-list-field.js +1 -1
- package/dist/components/file-list-field.d.ts +1 -1
- package/dist/components/grid-cell-for-field.d.ts +4 -2
- package/dist/components/grid-cell-for-field.js +2 -2
- package/dist/components/multi-select-checkboxes.d.ts +1 -1
- package/dist/components/multi-select-checkboxes.js +1 -1
- package/dist/components/query-options-select.d.ts +1 -1
- package/dist/components/related-list-section.d.ts +1 -1
- package/dist/components/related-list-section.js +16 -3
- package/dist/components/render-edit-logic.d.ts +1 -1
- package/dist/components/render-edit-types.d.ts +2 -2
- package/dist/components/render-edit.d.ts +1 -1
- package/dist/components/render-edit.js +5 -3
- package/dist/components/render-field.d.ts +5 -2
- package/dist/components/render-field.js +103 -9
- package/dist/components/render-list.d.ts +1 -1
- package/dist/components/render-list.js +20 -4
- package/dist/components/write-form-section.d.ts +1 -1
- package/dist/components/write-form-section.js +1 -1
- package/dist/context/dispatcher-context.d.ts +1 -1
- package/dist/format-when.js +1 -1
- package/dist/headless/apex/css.d.ts +3 -0
- package/dist/headless/apex/css.js +298 -0
- package/dist/headless/apex/index.d.ts +236 -0
- package/dist/headless/apex/index.js +377 -0
- package/dist/headless/apex/lightbox.d.ts +4 -0
- package/dist/headless/apex/lightbox.js +81 -0
- package/dist/headless/contracts/asset.d.ts +15 -0
- package/dist/headless/contracts/asset.js +17 -0
- package/dist/headless/contracts/index.d.ts +3 -0
- package/dist/headless/contracts/index.js +1 -0
- package/dist/headless/contracts/locale.d.ts +7 -0
- package/dist/headless/contracts/locale.js +11 -0
- package/dist/headless/contracts/primitives.d.ts +93 -0
- package/dist/headless/contracts/primitives.js +21 -0
- package/dist/headless/dispatcher/index.d.ts +2 -0
- package/dist/headless/dispatcher/index.js +1 -0
- package/dist/headless/dispatcher/types.d.ts +86 -0
- package/dist/headless/dispatcher/types.js +34 -0
- package/dist/headless/form/form-controller.d.ts +2 -0
- package/dist/headless/form/form-controller.js +409 -0
- package/dist/headless/form/index.d.ts +3 -0
- package/dist/headless/form/index.js +2 -0
- package/dist/headless/form/types.d.ts +76 -0
- package/dist/headless/form/types.js +1 -0
- package/dist/headless/form/zod-bridge.d.ts +10 -0
- package/dist/headless/form/zod-bridge.js +103 -0
- package/dist/headless/format/document-values.d.ts +12 -0
- package/dist/headless/format/document-values.js +70 -0
- package/dist/headless/format/escape.d.ts +5 -0
- package/dist/headless/format/escape.js +75 -0
- package/dist/headless/format/html-template.d.ts +9 -0
- package/dist/headless/format/html-template.js +39 -0
- package/dist/headless/format/index.d.ts +12 -0
- package/dist/headless/format/index.js +237 -0
- package/dist/headless/format/money.d.ts +3 -0
- package/dist/headless/format/money.js +39 -0
- package/dist/headless/index.d.ts +14 -0
- package/dist/headless/index.js +9 -0
- package/dist/headless/locale-routing/index.d.ts +41 -0
- package/dist/headless/locale-routing/index.js +145 -0
- package/dist/headless/nav/index.d.ts +2 -0
- package/dist/headless/nav/index.js +1 -0
- package/dist/headless/nav/resolve.d.ts +2 -0
- package/dist/headless/nav/resolve.js +71 -0
- package/dist/headless/nav/types.d.ts +26 -0
- package/dist/headless/nav/types.js +1 -0
- package/dist/headless/store/create-store.d.ts +2 -0
- package/dist/headless/store/create-store.js +43 -0
- package/dist/headless/store/equality.d.ts +1 -0
- package/dist/headless/store/equality.js +34 -0
- package/dist/headless/store/index.d.ts +3 -0
- package/dist/headless/store/index.js +2 -0
- package/dist/headless/store/types.d.ts +7 -0
- package/dist/headless/store/types.js +10 -0
- package/dist/headless/view-model/edit.d.ts +18 -0
- package/dist/headless/view-model/edit.js +501 -0
- package/dist/headless/view-model/embedded-list.d.ts +27 -0
- package/dist/headless/view-model/embedded-list.js +63 -0
- package/dist/headless/view-model/index.d.ts +7 -0
- package/dist/headless/view-model/index.js +3 -0
- package/dist/headless/view-model/list-file-display.d.ts +3 -0
- package/dist/headless/view-model/list-file-display.js +27 -0
- package/dist/headless/view-model/list.d.ts +16 -0
- package/dist/headless/view-model/list.js +229 -0
- package/dist/headless/view-model/types.d.ts +382 -0
- package/dist/headless/view-model/types.js +1 -0
- package/dist/hooks/use-ai-text.d.ts +1 -1
- package/dist/hooks/use-form.d.ts +1 -1
- package/dist/hooks/use-form.js +1 -1
- package/dist/hooks/use-mutation.d.ts +1 -1
- package/dist/hooks/use-query.d.ts +1 -1
- package/dist/hooks/use-store.d.ts +1 -1
- package/dist/hooks/use-store.js +7 -7
- package/dist/hooks/use-stream-handler.d.ts +1 -1
- package/dist/i18n-defaults.js +7 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -4
- package/dist/live/csrf.d.ts +2 -0
- package/dist/live/csrf.js +60 -0
- package/dist/live/dispatcher-live.d.ts +7 -0
- package/dist/live/dispatcher-live.js +286 -0
- package/dist/live/envelopes.d.ts +101 -0
- package/dist/live/envelopes.js +35 -0
- package/dist/live/error-mapping.d.ts +16 -0
- package/dist/live/error-mapping.js +82 -0
- package/dist/live/index.d.ts +7 -0
- package/dist/live/index.js +5 -0
- package/dist/live/locale.d.ts +2 -0
- package/dist/live/locale.js +25 -0
- package/dist/live/sse-stream.d.ts +14 -0
- package/dist/live/sse-stream.js +128 -0
- package/dist/primitives.d.ts +53 -36
- package/dist/primitives.js +1 -1
- package/dist/session/session-ended.d.ts +1 -1
- package/dist/tokens.d.ts +3 -4
- package/dist/tokens.js +4 -5
- package/dist/web/app/app-schema-boot.d.ts +19 -0
- package/dist/web/app/app-schema-boot.js +124 -0
- package/dist/web/app/browser-locale.d.ts +22 -0
- package/dist/web/app/browser-locale.js +78 -0
- package/dist/web/app/client-plugin.d.ts +106 -0
- package/dist/web/app/client-plugin.js +12 -0
- package/dist/web/app/create-app.d.ts +104 -0
- package/dist/web/app/create-app.js +498 -0
- package/dist/web/app/create-public-surface.d.ts +36 -0
- package/dist/web/app/create-public-surface.js +40 -0
- package/dist/web/app/dashboard-body.d.ts +3 -0
- package/dist/web/app/dashboard-body.js +865 -0
- package/dist/web/app/dashboard-refresh.d.ts +6 -0
- package/dist/web/app/dashboard-refresh.js +15 -0
- package/dist/web/app/document-lang-sync.d.ts +4 -0
- package/dist/web/app/document-lang-sync.js +27 -0
- package/dist/web/app/draft-storage.d.ts +11 -0
- package/dist/web/app/draft-storage.js +53 -0
- package/dist/web/app/nav-providers-context.d.ts +13 -0
- package/dist/web/app/nav-providers-context.js +18 -0
- package/dist/web/app/nav.d.ts +40 -0
- package/dist/web/app/nav.js +231 -0
- package/dist/web/app/plain-content-editor.d.ts +3 -0
- package/dist/web/app/plain-content-editor.js +30 -0
- package/dist/web/app/relative-time.d.ts +10 -0
- package/dist/web/app/relative-time.js +56 -0
- package/dist/web/app/resolvers-context.d.ts +12 -0
- package/dist/web/app/resolvers-context.js +10 -0
- package/dist/web/app/rich-content-editor.d.ts +3 -0
- package/dist/web/app/rich-content-editor.js +15 -0
- package/dist/web/app/root-error-fallback.d.ts +2 -0
- package/dist/web/app/root-error-fallback.js +21 -0
- package/dist/web/app/screen-error-fallback.d.ts +6 -0
- package/dist/web/app/screen-error-fallback.js +17 -0
- package/dist/web/app/tiptap-editor.d.ts +3 -0
- package/dist/web/app/tiptap-editor.js +120 -0
- package/dist/web/app/toast-action-notifier.d.ts +5 -0
- package/dist/web/app/toast-action-notifier.js +17 -0
- package/dist/web/components/config-cascade.d.ts +13 -0
- package/dist/web/components/config-cascade.js +155 -0
- package/dist/web/components/config-source-badge.d.ts +6 -0
- package/dist/web/components/config-source-badge.js +55 -0
- package/dist/web/default-color-scheme.d.ts +2 -0
- package/dist/web/default-color-scheme.js +3 -0
- package/dist/web/icons.d.ts +94 -0
- package/dist/web/icons.js +100 -0
- package/dist/web/index.d.ts +78 -0
- package/dist/web/index.js +53 -0
- package/dist/web/layout/app-layout.d.ts +21 -0
- package/dist/web/layout/app-layout.js +8 -0
- package/dist/web/layout/avatar.d.ts +12 -0
- package/dist/web/layout/avatar.js +63 -0
- package/dist/web/layout/default-app-shell.d.ts +41 -0
- package/dist/web/layout/default-app-shell.js +16 -0
- package/dist/web/layout/editor-panel.d.ts +10 -0
- package/dist/web/layout/editor-panel.js +29 -0
- package/dist/web/layout/fill-classes.d.ts +9 -0
- package/dist/web/layout/fill-classes.js +15 -0
- package/dist/web/layout/filter-app-schema-navs.d.ts +7 -0
- package/dist/web/layout/filter-app-schema-navs.js +68 -0
- package/dist/web/layout/header-action-group.d.ts +6 -0
- package/dist/web/layout/header-action-group.js +8 -0
- package/dist/web/layout/header-overflow-menu.d.ts +4 -0
- package/dist/web/layout/header-overflow-menu.js +8 -0
- package/dist/web/layout/language-menu-items.d.ts +8 -0
- package/dist/web/layout/language-menu-items.js +16 -0
- package/dist/web/layout/language-switcher.d.ts +25 -0
- package/dist/web/layout/language-switcher.js +46 -0
- package/dist/web/layout/nav-tree.d.ts +19 -0
- package/dist/web/layout/nav-tree.js +536 -0
- package/dist/web/layout/page-header-slot.d.ts +25 -0
- package/dist/web/layout/page-header-slot.js +50 -0
- package/dist/web/layout/profile-menu.d.ts +13 -0
- package/dist/web/layout/profile-menu.js +6 -0
- package/dist/web/layout/shell-breadcrumb.d.ts +11 -0
- package/dist/web/layout/shell-breadcrumb.js +34 -0
- package/dist/web/layout/shell-header.d.ts +12 -0
- package/dist/web/layout/shell-header.js +159 -0
- package/dist/web/layout/sidebar-brand.d.ts +15 -0
- package/dist/web/layout/sidebar-brand.js +9 -0
- package/dist/web/layout/sidebar-panel.d.ts +41 -0
- package/dist/web/layout/sidebar-panel.js +107 -0
- package/dist/web/layout/sidebar-user.d.ts +8 -0
- package/dist/web/layout/sidebar-user.js +17 -0
- package/dist/web/layout/sidebar.d.ts +17 -0
- package/dist/web/layout/sidebar.js +11 -0
- package/dist/web/layout/target-resolver-stub.d.ts +13 -0
- package/dist/web/layout/target-resolver-stub.js +61 -0
- package/dist/web/layout/target-url.d.ts +12 -0
- package/dist/web/layout/target-url.js +77 -0
- package/dist/web/layout/theme-label.d.ts +15 -0
- package/dist/web/layout/theme-label.js +35 -0
- package/dist/web/layout/theme-menu-item.d.ts +12 -0
- package/dist/web/layout/theme-menu-item.js +16 -0
- package/dist/web/layout/theme-toggle.d.ts +17 -0
- package/dist/web/layout/theme-toggle.js +22 -0
- package/dist/web/layout/topbar.d.ts +8 -0
- package/dist/web/layout/topbar.js +4 -0
- package/dist/web/layout/workspace-shell.d.ts +63 -0
- package/dist/web/layout/workspace-shell.js +220 -0
- package/dist/web/layout/workspace-switcher.d.ts +15 -0
- package/dist/web/layout/workspace-switcher.js +36 -0
- package/dist/web/lib/accept-attr.d.ts +1 -0
- package/dist/web/lib/accept-attr.js +6 -0
- package/dist/web/lib/clamp.d.ts +1 -0
- package/dist/web/lib/clamp.js +3 -0
- package/dist/web/lib/cn.d.ts +6 -0
- package/dist/web/lib/cn.js +9 -0
- package/dist/web/lib/download.d.ts +2 -0
- package/dist/web/lib/download.js +24 -0
- package/dist/web/lib/file-ref-download.d.ts +15 -0
- package/dist/web/lib/file-ref-download.js +62 -0
- package/dist/web/lib/file-upload-form-data.d.ts +7 -0
- package/dist/web/lib/file-upload-form-data.js +11 -0
- package/dist/web/lib/resize-image.d.ts +1 -0
- package/dist/web/lib/resize-image.js +66 -0
- package/dist/web/lib/upload-file-ref.d.ts +9 -0
- package/dist/web/lib/upload-file-ref.js +29 -0
- package/dist/web/primitives/action-menu.d.ts +34 -0
- package/dist/web/primitives/action-menu.js +30 -0
- package/dist/web/primitives/calendar-popover.d.ts +13 -0
- package/dist/web/primitives/calendar-popover.js +76 -0
- package/dist/web/primitives/combobox.d.ts +56 -0
- package/dist/web/primitives/combobox.js +127 -0
- package/dist/web/primitives/date-field.d.ts +18 -0
- package/dist/web/primitives/date-field.js +74 -0
- package/dist/web/primitives/date-input.d.ts +1 -0
- package/dist/web/primitives/date-input.js +5 -0
- package/dist/web/primitives/date-parse.d.ts +11 -0
- package/dist/web/primitives/date-parse.js +159 -0
- package/dist/web/primitives/dialog.d.ts +3 -0
- package/dist/web/primitives/dialog.js +42 -0
- package/dist/web/primitives/drawer.d.ts +3 -0
- package/dist/web/primitives/drawer.js +17 -0
- package/dist/web/primitives/dropdown-menu.d.ts +15 -0
- package/dist/web/primitives/dropdown-menu.js +56 -0
- package/dist/web/primitives/embedded-list-input.d.ts +9 -0
- package/dist/web/primitives/embedded-list-input.js +382 -0
- package/dist/web/primitives/file-thumbnail.d.ts +25 -0
- package/dist/web/primitives/file-thumbnail.js +90 -0
- package/dist/web/primitives/file-upload.d.ts +21 -0
- package/dist/web/primitives/file-upload.js +56 -0
- package/dist/web/primitives/index.d.ts +34 -0
- package/dist/web/primitives/index.js +2021 -0
- package/dist/web/primitives/json-view.d.ts +7 -0
- package/dist/web/primitives/json-view.js +84 -0
- package/dist/web/primitives/layout.d.ts +27 -0
- package/dist/web/primitives/layout.js +30 -0
- package/dist/web/primitives/lightbox.d.ts +3 -0
- package/dist/web/primitives/lightbox.js +36 -0
- package/dist/web/primitives/locale-number.d.ts +2 -0
- package/dist/web/primitives/locale-number.js +54 -0
- package/dist/web/primitives/located-timestamp-input.d.ts +22 -0
- package/dist/web/primitives/located-timestamp-input.js +22 -0
- package/dist/web/primitives/metric.d.ts +5 -0
- package/dist/web/primitives/metric.js +18 -0
- package/dist/web/primitives/modal-shell.d.ts +17 -0
- package/dist/web/primitives/modal-shell.js +9 -0
- package/dist/web/primitives/modal.d.ts +3 -0
- package/dist/web/primitives/modal.js +13 -0
- package/dist/web/primitives/money-input.d.ts +18 -0
- package/dist/web/primitives/money-input.js +122 -0
- package/dist/web/primitives/narrow-pinned-footer.d.ts +12 -0
- package/dist/web/primitives/narrow-pinned-footer.js +153 -0
- package/dist/web/primitives/number-input.d.ts +18 -0
- package/dist/web/primitives/number-input.js +53 -0
- package/dist/web/primitives/option-groups.d.ts +11 -0
- package/dist/web/primitives/option-groups.js +27 -0
- package/dist/web/primitives/page-header.d.ts +3 -0
- package/dist/web/primitives/page-header.js +52 -0
- package/dist/web/primitives/promo-panel.d.ts +3 -0
- package/dist/web/primitives/promo-panel.js +11 -0
- package/dist/web/primitives/segmented-select.d.ts +21 -0
- package/dist/web/primitives/segmented-select.js +62 -0
- package/dist/web/primitives/share-actions.d.ts +4 -0
- package/dist/web/primitives/share-actions.js +56 -0
- package/dist/web/primitives/status-badge.d.ts +3 -0
- package/dist/web/primitives/status-badge.js +5 -0
- package/dist/web/primitives/sticky-action-bar.d.ts +5 -0
- package/dist/web/primitives/sticky-action-bar.js +15 -0
- package/dist/web/primitives/tabs.d.ts +3 -0
- package/dist/web/primitives/tabs.js +107 -0
- package/dist/web/primitives/timestamp-input.d.ts +27 -0
- package/dist/web/primitives/timestamp-input.js +87 -0
- package/dist/web/primitives/toast.d.ts +19 -0
- package/dist/web/primitives/toast.js +63 -0
- package/dist/web/primitives/tz-input.d.ts +11 -0
- package/dist/web/primitives/tz-input.js +8 -0
- package/dist/web/primitives/tz-options.d.ts +4 -0
- package/dist/web/primitives/tz-options.js +17 -0
- package/dist/web/primitives/use-keyboard-inset.d.ts +1 -0
- package/dist/web/primitives/use-keyboard-inset.js +28 -0
- package/dist/web/primitives/use-narrow-viewport.d.ts +4 -0
- package/dist/web/primitives/use-narrow-viewport.js +46 -0
- package/dist/web/react-css-custom-properties.d.ts +6 -0
- package/dist/web/react-css-custom-properties.js +1 -0
- package/dist/web/sse/live-events.d.ts +15 -0
- package/dist/web/sse/live-events.js +148 -0
- package/dist/web/theme-plugin.d.ts +41 -0
- package/dist/web/theme-plugin.js +123 -0
- package/dist/web/tokens.d.ts +24 -0
- package/dist/web/tokens.js +203 -0
- package/dist/web/ui/avatar.d.ts +11 -0
- package/dist/web/ui/avatar.js +25 -0
- package/dist/web/ui/badge.d.ts +9 -0
- package/dist/web/ui/badge.js +26 -0
- package/dist/web/ui/breadcrumb.d.ts +11 -0
- package/dist/web/ui/breadcrumb.js +29 -0
- package/dist/web/ui/button.d.ts +10 -0
- package/dist/web/ui/button.js +37 -0
- package/dist/web/ui/checkbox.d.ts +4 -0
- package/dist/web/ui/checkbox.js +11 -0
- package/dist/web/ui/input.d.ts +3 -0
- package/dist/web/ui/input.js +8 -0
- package/dist/web/ui/label.d.ts +4 -0
- package/dist/web/ui/label.js +10 -0
- package/dist/web/ui/resizable.d.ts +7 -0
- package/dist/web/ui/resizable.js +16 -0
- package/dist/web/ui/separator.d.ts +4 -0
- package/dist/web/ui/separator.js +10 -0
- package/dist/web/ui/sheet.d.ts +14 -0
- package/dist/web/ui/sheet.js +42 -0
- package/dist/web/ui/sidebar.d.ts +73 -0
- package/dist/web/ui/sidebar.js +211 -0
- package/dist/web/ui/skeleton.d.ts +2 -0
- package/dist/web/ui/skeleton.js +7 -0
- package/dist/web/ui/switch.d.ts +6 -0
- package/dist/web/ui/switch.js +10 -0
- package/dist/web/ui/table.d.ts +10 -0
- package/dist/web/ui/table.js +30 -0
- package/dist/web/ui/tabs.d.ts +11 -0
- package/dist/web/ui/tabs.js +31 -0
- package/dist/web/ui/textarea.d.ts +3 -0
- package/dist/web/ui/textarea.js +8 -0
- package/dist/web/ui/tooltip.d.ts +7 -0
- package/dist/web/ui/tooltip.js +19 -0
- package/dist/web/ui/use-mobile.d.ts +1 -0
- package/dist/web/ui/use-mobile.js +16 -0
- package/dist/web/version/update-checker.d.ts +9 -0
- package/dist/web/version/update-checker.js +88 -0
- package/dist/web/widgets/ai-text-field.d.ts +36 -0
- package/dist/web/widgets/ai-text-field.js +137 -0
- package/dist/web/widgets/chart-window.d.ts +58 -0
- package/dist/web/widgets/chart-window.js +190 -0
- package/dist/web/widgets/charts.d.ts +208 -0
- package/dist/web/widgets/charts.js +685 -0
- package/dist/web/widgets/collapsible-section.d.ts +10 -0
- package/dist/web/widgets/collapsible-section.js +17 -0
- package/dist/web/widgets/dashboard-list.d.ts +21 -0
- package/dist/web/widgets/dashboard-list.js +45 -0
- package/dist/web/widgets/detail-list.d.ts +15 -0
- package/dist/web/widgets/detail-list.js +8 -0
- package/dist/web/widgets/drawer.d.ts +65 -0
- package/dist/web/widgets/drawer.js +142 -0
- package/dist/web/widgets/feed-list.d.ts +13 -0
- package/dist/web/widgets/feed-list.js +9 -0
- package/dist/web/widgets/floating-panel.d.ts +30 -0
- package/dist/web/widgets/floating-panel.js +242 -0
- package/dist/web/widgets/form-fields.d.ts +93 -0
- package/dist/web/widgets/form-fields.js +50 -0
- package/dist/web/widgets/index.d.ts +27 -0
- package/dist/web/widgets/index.js +30 -0
- package/dist/web/widgets/infinity-list.d.ts +46 -0
- package/dist/web/widgets/infinity-list.js +211 -0
- package/dist/web/widgets/mode-switch.d.ts +21 -0
- package/dist/web/widgets/mode-switch.js +25 -0
- package/dist/web/widgets/photo-slots.d.ts +29 -0
- package/dist/web/widgets/photo-slots.js +52 -0
- package/dist/web/widgets/plan-card.d.ts +36 -0
- package/dist/web/widgets/plan-card.js +13 -0
- package/dist/web/widgets/progress-bar.d.ts +19 -0
- package/dist/web/widgets/progress-bar.js +24 -0
- package/dist/web/widgets/progress-list.d.ts +19 -0
- package/dist/web/widgets/progress-list.js +11 -0
- package/dist/web/widgets/public-shell.d.ts +22 -0
- package/dist/web/widgets/public-shell.js +15 -0
- package/dist/web/widgets/query-table.d.ts +29 -0
- package/dist/web/widgets/query-table.js +30 -0
- package/dist/web/widgets/result-panel.d.ts +69 -0
- package/dist/web/widgets/result-panel.js +30 -0
- package/dist/web/widgets/section-card.d.ts +12 -0
- package/dist/web/widgets/section-card.js +9 -0
- package/dist/web/widgets/sheet-parts.d.ts +9 -0
- package/dist/web/widgets/sheet-parts.js +11 -0
- package/dist/web/widgets/side-by-side-table.d.ts +31 -0
- package/dist/web/widgets/side-by-side-table.js +11 -0
- package/dist/web/widgets/stat.d.ts +54 -0
- package/dist/web/widgets/stat.js +65 -0
- package/dist/web/widgets/states.d.ts +23 -0
- package/dist/web/widgets/states.js +20 -0
- package/dist/web/widgets/status-badge.d.ts +15 -0
- package/dist/web/widgets/status-badge.js +24 -0
- package/dist/web/widgets/step-bar.d.ts +35 -0
- package/dist/web/widgets/step-bar.js +60 -0
- package/dist/web/widgets/upload-zone.d.ts +24 -0
- package/dist/web/widgets/upload-zone.js +108 -0
- package/dist/web/widgets/use-draft.d.ts +20 -0
- package/dist/web/widgets/use-draft.js +21 -0
- package/dist/web/widgets/use-pointer-drag.d.ts +21 -0
- package/dist/web/widgets/use-pointer-drag.js +53 -0
- package/package.json +105 -7
- package/src/changes.json +77 -0
- package/src/web/fonts/OFL.txt +93 -0
- package/src/web/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-600.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-600.woff2 +0 -0
- package/src/web/styles.css +401 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const DEFAULT_MAX_EDGE = 2560;
|
|
2
|
+
// Raster formats a canvas can losslessly re-encode as themselves; anything
|
|
3
|
+
// else (heic, bmp, tiff, ...) falls back to jpeg. svg/gif are excluded
|
|
4
|
+
// upstream (vector/animation would be destroyed by a canvas re-encode).
|
|
5
|
+
const PRESERVED_TYPES = new Set(["image/png", "image/webp"]);
|
|
6
|
+
const OUTPUT_EXTENSIONS = {
|
|
7
|
+
"image/jpeg": "jpg",
|
|
8
|
+
"image/png": "png",
|
|
9
|
+
"image/webp": "webp",
|
|
10
|
+
};
|
|
11
|
+
function pickOutputType(inputType) {
|
|
12
|
+
return PRESERVED_TYPES.has(inputType) ? inputType : "image/jpeg";
|
|
13
|
+
}
|
|
14
|
+
// The server validates the upload's extension against its mimeType
|
|
15
|
+
// (mime_mismatch), so a re-encode that changes type must rename the file too.
|
|
16
|
+
function withMatchingExtension(fileName, outputType) {
|
|
17
|
+
const ext = OUTPUT_EXTENSIONS[outputType] ?? "jpg";
|
|
18
|
+
const dot = fileName.lastIndexOf(".");
|
|
19
|
+
const base = dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
20
|
+
return `${base}.${ext}`;
|
|
21
|
+
}
|
|
22
|
+
// Downscales images before upload to a reasonable edge length (bandwidth)
|
|
23
|
+
// and drops EXIF/GPS as a side effect (canvas re-encode carries no
|
|
24
|
+
// metadata). Missing OffscreenCanvas/createImageBitmap (older browser), or a
|
|
25
|
+
// decode failure, leaves the file unchanged — no hard error before upload.
|
|
26
|
+
export async function resizeImageBeforeUpload(file, maxEdge = DEFAULT_MAX_EDGE) {
|
|
27
|
+
if (!file.type.startsWith("image/"))
|
|
28
|
+
return file;
|
|
29
|
+
if (file.type === "image/svg+xml" || file.type === "image/gif")
|
|
30
|
+
return file;
|
|
31
|
+
if (typeof OffscreenCanvas === "undefined" || typeof createImageBitmap !== "function") {
|
|
32
|
+
return file;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const bitmap = await createImageBitmap(file, { imageOrientation: "from-image" });
|
|
36
|
+
const scale = Math.min(1, maxEdge / Math.max(bitmap.width, bitmap.height));
|
|
37
|
+
const width = Math.round(bitmap.width * scale);
|
|
38
|
+
const height = Math.round(bitmap.height * scale);
|
|
39
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
40
|
+
try {
|
|
41
|
+
const ctx = canvas.getContext("2d");
|
|
42
|
+
if (ctx === null)
|
|
43
|
+
return file;
|
|
44
|
+
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
bitmap.close();
|
|
48
|
+
}
|
|
49
|
+
const blob = await canvas.convertToBlob({ type: pickOutputType(file.type), quality: 0.85 });
|
|
50
|
+
// Name off the blob's actual type, not the requested one — a browser
|
|
51
|
+
// without webp encoding support silently falls back to png, and naming
|
|
52
|
+
// by the request would then store a lie (extension/mimeType both "webp"
|
|
53
|
+
// over png bytes).
|
|
54
|
+
const actualType = blob.type || file.type;
|
|
55
|
+
const fileName = actualType === file.type ? file.name : withMatchingExtension(file.name, actualType);
|
|
56
|
+
const resized = new File([blob], fileName, { type: actualType });
|
|
57
|
+
// Only takes the re-encode if it actually shrank the file — a
|
|
58
|
+
// palette-optimized PNG or a small in-spec photo can come back larger
|
|
59
|
+
// after a full RGBA canvas round-trip. EXIF/GPS stripping is then
|
|
60
|
+
// best-effort: an original that's kept as-is keeps its metadata too.
|
|
61
|
+
return resized.size < file.size ? resized : file;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return file;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type FileUploadFormDataInput } from "./file-upload-form-data.ts";
|
|
2
|
+
export type FileRefTarget = Pick<FileUploadFormDataInput, "entityType" | "entityId" | "fieldName">;
|
|
3
|
+
/** Carries the server's own `error` message when the upload endpoint sent one. */
|
|
4
|
+
export declare class FileUploadFailedError extends Error {
|
|
5
|
+
readonly fromServer: boolean;
|
|
6
|
+
constructor(message: string, fromServer: boolean);
|
|
7
|
+
}
|
|
8
|
+
/** Posts one file to the stack's file endpoint (double-submit CSRF) and returns the new FileRef id. */
|
|
9
|
+
export declare function uploadFileRef(file: Blob, target?: FileRefTarget): Promise<string>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { CSRF_HEADER_NAME, readCsrfToken } from "../../live/index.js";
|
|
3
|
+
import { buildFileUploadFormData } from "./file-upload-form-data.js";
|
|
4
|
+
/** Carries the server's own `error` message when the upload endpoint sent one. */
|
|
5
|
+
export class FileUploadFailedError extends Error {
|
|
6
|
+
fromServer;
|
|
7
|
+
constructor(message, fromServer) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.fromServer = fromServer;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Posts one file to the stack's file endpoint (double-submit CSRF) and returns the new FileRef id. */
|
|
13
|
+
export async function uploadFileRef(file, target) {
|
|
14
|
+
const csrf = readCsrfToken();
|
|
15
|
+
const res = await fetch("/api/files", {
|
|
16
|
+
method: "POST",
|
|
17
|
+
body: buildFileUploadFormData({ file, ...target }),
|
|
18
|
+
...(csrf !== undefined && { headers: { [CSRF_HEADER_NAME]: csrf } }),
|
|
19
|
+
});
|
|
20
|
+
const json = await res.json().catch(() => ({}));
|
|
21
|
+
const id = propertyOf(json, "id");
|
|
22
|
+
if (!res.ok || typeof id !== "string") {
|
|
23
|
+
const message = propertyOf(json, "error");
|
|
24
|
+
throw typeof message === "string"
|
|
25
|
+
? new FileUploadFailedError(message, true)
|
|
26
|
+
: new FileUploadFailedError(`upload failed (${res.status})`, false);
|
|
27
|
+
}
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type MenuItemDef = {
|
|
3
|
+
readonly kind: "item";
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
/** Optional Icon links vor dem Label (16px). */
|
|
7
|
+
readonly icon?: ReactNode;
|
|
8
|
+
/** Pure-display Shortcut-Hint rechts (monospace, gedimmt).
|
|
9
|
+
* Caller registriert den Shortcut separat — Linear-Style
|
|
10
|
+
* Strings wie "O then M", "Alt + Q", "⌘K". */
|
|
11
|
+
readonly shortcut?: string;
|
|
12
|
+
readonly onSelect: () => void;
|
|
13
|
+
readonly disabled?: boolean;
|
|
14
|
+
/** "danger" rendert Label rot — typisch Sign-out, Delete. */
|
|
15
|
+
readonly variant?: "default" | "danger";
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: "separator";
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: "label";
|
|
20
|
+
readonly label: string;
|
|
21
|
+
};
|
|
22
|
+
export type ActionMenuProps = {
|
|
23
|
+
/** Free-form Trigger-Component — Avatar, IconButton, Pill, Custom. */
|
|
24
|
+
readonly trigger: ReactNode;
|
|
25
|
+
/** aria-label für den Trigger-Wrapper. */
|
|
26
|
+
readonly triggerLabel?: string;
|
|
27
|
+
readonly items: readonly MenuItemDef[];
|
|
28
|
+
/** Wo das Popup relativ zum Trigger anchorn soll. Default: end. */
|
|
29
|
+
readonly align?: "start" | "center" | "end";
|
|
30
|
+
/** Min-width für den Content. Default: 14rem. */
|
|
31
|
+
readonly minWidth?: string;
|
|
32
|
+
readonly testId?: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function ActionMenu({ trigger, triggerLabel, items, align, minWidth, testId, }: ActionMenuProps): ReactNode;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Generic Action-Menu — Trigger + Items, auf @radix-ui/react-dropdown-
|
|
3
|
+
// menu basiert. Wird benutzt für ProfileMenu (Topbar-Avatar), Edit-
|
|
4
|
+
// Header-Menu (Three-Dots), List-Row-Kebab — die Mechanik ist überall
|
|
5
|
+
// dieselbe, nur der Trigger unterscheidet sich.
|
|
6
|
+
//
|
|
7
|
+
// Nicht zu verwechseln mit RowActionsCell (siehe primitives/index.tsx),
|
|
8
|
+
// die hat die rowActions-Schema-API + per-row Visibility-Logik. Hier
|
|
9
|
+
// ist der Layer drunter: stable, schema-frei, callable von App-Code
|
|
10
|
+
// und vom Schema-Renderer (KumikoApp).
|
|
11
|
+
//
|
|
12
|
+
// Items sind Discriminated Union (item / separator / label), Item kann
|
|
13
|
+
// optional einen Keyboard-Shortcut-Hint rechts tragen (Linear-Pattern
|
|
14
|
+
// "O then M") + Icon links + variant für danger-styling.
|
|
15
|
+
import { useState } from "react";
|
|
16
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./dropdown-menu.js";
|
|
17
|
+
export function ActionMenu({ trigger, triggerLabel, items, align = "end", minWidth = "14rem", testId, }) {
|
|
18
|
+
const [open, setOpen] = useState(false);
|
|
19
|
+
return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-testid": testId ?? "action-menu-trigger", ...(triggerLabel !== undefined && { "aria-label": triggerLabel }), className: "rounded outline-none focus-visible:ring-1 focus-visible:ring-ring", children: trigger }) }), _jsx(DropdownMenuContent, { align: align, style: { minWidth }, children: items.map((item, idx) => {
|
|
20
|
+
if (item.kind === "separator") {
|
|
21
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: Separators haben keine ID — Reihenfolge ist Identität
|
|
22
|
+
return _jsx(DropdownMenuSeparator, {}, `sep-${idx}`);
|
|
23
|
+
}
|
|
24
|
+
if (item.kind === "label") {
|
|
25
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: gleicher Grund wie separator
|
|
26
|
+
return _jsx(DropdownMenuLabel, { children: item.label }, `label-${idx}`);
|
|
27
|
+
}
|
|
28
|
+
return (_jsxs(DropdownMenuItem, { onSelect: item.onSelect, disabled: item.disabled === true, "data-testid": `action-menu-item-${item.id}`, className: item.variant === "danger" ? "text-destructive focus:text-destructive" : undefined, children: [item.icon !== undefined && _jsx("span", { className: "size-4 shrink-0", children: item.icon }), _jsx("span", { className: "flex-1", children: item.label }), item.shortcut !== undefined && (_jsx("span", { className: "ml-auto text-xs text-muted-foreground tracking-wider font-mono", children: item.shortcut }))] }, item.id));
|
|
29
|
+
}) })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type CalendarPopoverProps = {
|
|
3
|
+
readonly selected: Date | undefined;
|
|
4
|
+
readonly onSelect: (d: Date | undefined) => void;
|
|
5
|
+
readonly min?: Date;
|
|
6
|
+
readonly max?: Date;
|
|
7
|
+
readonly disabled?: boolean;
|
|
8
|
+
readonly hasError?: boolean;
|
|
9
|
+
/** a11y-Label für den Icon-Button (kein sichtbarer Text). */
|
|
10
|
+
readonly triggerLabel: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const inFieldIconButtonClass: string;
|
|
13
|
+
export declare function CalendarPopover({ selected, onSelect, min, max, disabled, hasError, triggerLabel, }: CalendarPopoverProps): ReactNode;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// CalendarPopover — gemeinsamer Kalender-Trigger für DateInput und
|
|
3
|
+
// TimestampInput. Ein Icon-Button öffnet ein Radix-Popover mit
|
|
4
|
+
// react-day-picker (v9). captionLayout="dropdown" + startMonth/endMonth
|
|
5
|
+
// geben Monats- UND Jahres-Dropdown statt nur Monats-Vor/Zurück — der
|
|
6
|
+
// Grund für #369 (10 Jahre zurück war ~120 Klicks). min/max grauen
|
|
7
|
+
// Tage außerhalb des erlaubten Bereichs aus.
|
|
8
|
+
//
|
|
9
|
+
// Der Text-Input (Tippen) lebt bewusst NICHT hier, sondern im jeweiligen
|
|
10
|
+
// Primitive — date hat ein Feld, timestamp zwei (Datum + Uhrzeit). So
|
|
11
|
+
// teilen beide dieselbe Kalender-Mechanik ohne ihre Eingabe-Form zu teilen.
|
|
12
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
13
|
+
import { Calendar as CalendarIcon } from "lucide-react";
|
|
14
|
+
import { DayPicker } from "react-day-picker";
|
|
15
|
+
import { cn } from "../lib/cn.js";
|
|
16
|
+
export const inFieldIconButtonClass = "absolute inset-y-0 right-0 inline-flex w-9 items-center justify-center rounded-md " +
|
|
17
|
+
"text-muted-foreground transition-colors hover:text-foreground " +
|
|
18
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring " +
|
|
19
|
+
"disabled:cursor-not-allowed disabled:opacity-50";
|
|
20
|
+
const popoverClass = "z-50 rounded-md border bg-popover p-3 text-popover-foreground shadow-md " +
|
|
21
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out " +
|
|
22
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 " +
|
|
23
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
24
|
+
// Default-Range wenn das Feld kein min/max setzt — sonst wäre das
|
|
25
|
+
// Jahres-Dropdown leer/unbrauchbar. Zur Render-Zeit berechnet.
|
|
26
|
+
function defaultRange() {
|
|
27
|
+
const thisYear = new Date().getFullYear();
|
|
28
|
+
return { start: new Date(1900, 0, 1), end: new Date(thisYear + 10, 11, 31) };
|
|
29
|
+
}
|
|
30
|
+
export function CalendarPopover({ selected, onSelect, min, max, disabled, hasError, triggerLabel, }) {
|
|
31
|
+
const range = defaultRange();
|
|
32
|
+
const startMonth = min ?? range.start;
|
|
33
|
+
const endMonth = max ?? range.end;
|
|
34
|
+
const disabledMatchers = [
|
|
35
|
+
...(min !== undefined ? [{ before: min }] : []),
|
|
36
|
+
...(max !== undefined ? [{ after: max }] : []),
|
|
37
|
+
];
|
|
38
|
+
return (_jsxs(PopoverPrimitive.Root, { children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsx("button", { type: "button", disabled: disabled, "aria-label": triggerLabel, "aria-invalid": hasError === true ? true : undefined, className: cn(inFieldIconButtonClass, hasError === true && "text-destructive"), children: _jsx(CalendarIcon, { className: "size-4", "aria-hidden": "true" }) }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { className: popoverClass, align: "end", sideOffset: 4, children: _jsx(DayPicker, { mode: "single", captionLayout: "dropdown", startMonth: startMonth, endMonth: endMonth, selected: selected,
|
|
39
|
+
// Ohne defaultMonth zeigt DayPicker today statt selected —
|
|
40
|
+
// unintuitiv wenn der User schon ein Datum gewählt hat.
|
|
41
|
+
defaultMonth: selected, ...(disabledMatchers.length > 0 && { disabled: disabledMatchers }), onSelect: (d) => onSelect(d), classNames: dayPickerClasses, components: { Dropdown: SelectDropdown } }) }) })] }));
|
|
42
|
+
}
|
|
43
|
+
const dropdownSelectClass = "rounded-sm border border-input bg-transparent px-1.5 py-0.5 text-sm font-medium " +
|
|
44
|
+
"cursor-pointer focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
|
|
45
|
+
// rdp v9s Default-Dropdown rendert NEBEN dem <select> ein aria-hidden
|
|
46
|
+
// <span> mit demselben Label — sichtbar nur, wenn rdps eigene style.css
|
|
47
|
+
// das <select> transparent darüberlegt. Da wir die rdp-Klassen mit eigenen
|
|
48
|
+
// Tokens überschreiben, greift diese Positionierung nicht → Label doppelt
|
|
49
|
+
// (#369-Folgebug). Ein nacktes <select> ohne Begleit-Span vermeidet das
|
|
50
|
+
// CSS-unabhängig — eine Quelle für den sichtbaren Wert.
|
|
51
|
+
function SelectDropdown({ options, className, ...selectProps }) {
|
|
52
|
+
return (_jsx("select", { className: cn(dropdownSelectClass, className), ...selectProps, children: options?.map((opt) => (_jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))) }));
|
|
53
|
+
}
|
|
54
|
+
// classNames-Map für react-day-picker v9 — überschreibt die default-
|
|
55
|
+
// Klassen mit Tailwind/shadcn-Tokens. Die Monat-/Jahr-Selects rendert
|
|
56
|
+
// SelectDropdown (components-Prop), daher hier nur der dropdowns-Container.
|
|
57
|
+
const dayPickerClasses = {
|
|
58
|
+
root: "rdp-root",
|
|
59
|
+
months: "flex flex-col gap-2",
|
|
60
|
+
month: "flex flex-col gap-2",
|
|
61
|
+
month_caption: "flex justify-center items-center h-7 text-sm font-medium",
|
|
62
|
+
dropdowns: "flex items-center gap-1 text-sm font-medium",
|
|
63
|
+
nav: "flex items-center gap-1 absolute right-1 top-2",
|
|
64
|
+
button_previous: "inline-flex h-7 w-7 items-center justify-center rounded-sm hover:bg-accent",
|
|
65
|
+
button_next: "inline-flex h-7 w-7 items-center justify-center rounded-sm hover:bg-accent",
|
|
66
|
+
weekdays: "flex",
|
|
67
|
+
weekday: "w-8 text-xs font-normal text-muted-foreground",
|
|
68
|
+
week: "flex mt-1",
|
|
69
|
+
day: "w-8 h-8 p-0 text-sm",
|
|
70
|
+
day_button: "inline-flex h-8 w-8 items-center justify-center rounded-sm hover:bg-accent " +
|
|
71
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
72
|
+
selected: "[&_button]:bg-primary [&_button]:text-primary-foreground [&_button]:hover:bg-primary/90",
|
|
73
|
+
today: "[&_button]:underline",
|
|
74
|
+
outside: "text-muted-foreground/50",
|
|
75
|
+
disabled: "text-muted-foreground/30 pointer-events-none",
|
|
76
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type ComboboxOption = {
|
|
3
|
+
readonly value: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
/** Muted second line in the list item; also matched by the local search. */
|
|
6
|
+
readonly description?: string;
|
|
7
|
+
/** Group heading; see `groupOptions` for the ordering rule. */
|
|
8
|
+
readonly group?: string;
|
|
9
|
+
/** Visible but not choosable. */
|
|
10
|
+
readonly disabled?: boolean;
|
|
11
|
+
};
|
|
12
|
+
type ComboboxBaseProps = {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly options: readonly ComboboxOption[];
|
|
16
|
+
readonly disabled?: boolean;
|
|
17
|
+
readonly required?: boolean;
|
|
18
|
+
readonly hasError?: boolean;
|
|
19
|
+
readonly ariaLabel?: string;
|
|
20
|
+
readonly placeholder?: string;
|
|
21
|
+
readonly searchPlaceholder?: string;
|
|
22
|
+
readonly emptyText?: string;
|
|
23
|
+
/** Tier 2.7e Remote-Search: wenn gesetzt, wechselt der Combobox in
|
|
24
|
+
* Remote-Mode. cmdk's client-side Filter wird deaktiviert (Server
|
|
25
|
+
* hat schon gefiltert), und der Search-Input ruft onSearchChange
|
|
26
|
+
* debounced (300ms). Ohne diesen Callback bleibt die Local-Filter-
|
|
27
|
+
* Variante (cmdk fuzzy-match auf den geladenen options). */
|
|
28
|
+
readonly onSearchChange?: (q: string) => void;
|
|
29
|
+
/** Spinner im Trigger + Popover-Footer wenn remote search läuft. */
|
|
30
|
+
readonly loading?: boolean;
|
|
31
|
+
/** Fixed footer row below the option list ("+ createLabel"). Rendered
|
|
32
|
+
* unfiltered — stays visible even when the search matches nothing,
|
|
33
|
+
* which is the most common case for reference-lookups on a freshly
|
|
34
|
+
* seeded system (kumiko-framework#1681). Omit for the plain
|
|
35
|
+
* select-existing combobox. */
|
|
36
|
+
readonly onCreate?: () => void;
|
|
37
|
+
/** Label for the create-footer row. Default: i18n `kumiko.actions.create`. */
|
|
38
|
+
readonly createLabel?: string;
|
|
39
|
+
/** Test-Hook: forciert den initial open-state des Popovers. In
|
|
40
|
+
* jsdom + Radix-Popover triggert userEvent.click auf den Trigger
|
|
41
|
+
* nicht zuverlässig PointerEvents — Tests setzen defaultOpen=true,
|
|
42
|
+
* damit der Popover sofort gerendert ist. Production-Code lässt
|
|
43
|
+
* den Default (false). */
|
|
44
|
+
readonly defaultOpen?: boolean;
|
|
45
|
+
};
|
|
46
|
+
export type ComboboxInputProps = ComboboxBaseProps & ({
|
|
47
|
+
readonly multiple?: false;
|
|
48
|
+
readonly value: string;
|
|
49
|
+
readonly onChange: (v: string) => void;
|
|
50
|
+
} | {
|
|
51
|
+
readonly multiple: true;
|
|
52
|
+
readonly value: readonly string[];
|
|
53
|
+
readonly onChange: (v: readonly string[]) => void;
|
|
54
|
+
});
|
|
55
|
+
export declare function ComboboxInput(props: ComboboxInputProps): ReactNode;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Tier 2.1c: Searchable Select / Combobox.
|
|
3
|
+
//
|
|
4
|
+
// Pattern ist das shadcn-Standard-Combobox: Popover-Trigger als Button
|
|
5
|
+
// (zeigt aktuellen Wert oder Placeholder), Popover-Content enthält
|
|
6
|
+
// cmdk-Command mit Search-Input + filterable Item-List.
|
|
7
|
+
//
|
|
8
|
+
// cmdk ist Radix's Standard-Combobox (Library für Command-K + Searchable
|
|
9
|
+
// Selects), client-side Fuzzy-Match via `Command.Filter` mit Default-
|
|
10
|
+
// Behavior. Server-side Remote-Search (debounced query) ist nicht im
|
|
11
|
+
// MVP — kommt später als zweiter Mode-Schalter.
|
|
12
|
+
//
|
|
13
|
+
// Multi-Mode: `multiple: true` schaltet auf Tag-Anzeige + Mehrfach-
|
|
14
|
+
// Auswahl. Selected-Tags rendern als kleine entfernbare Chips, das
|
|
15
|
+
// Search-Input bleibt offen für weitere Auswahl.
|
|
16
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
17
|
+
import { Command } from "cmdk";
|
|
18
|
+
import { Check, ChevronDown, Loader2, Plus } from "lucide-react";
|
|
19
|
+
import { useEffect, useState } from "react";
|
|
20
|
+
import { REFERENCE_SEARCH_DEBOUNCE_MS, useTranslation } from "../../index.js";
|
|
21
|
+
import { cn } from "../lib/cn.js";
|
|
22
|
+
import { groupOptions } from "./option-groups.js";
|
|
23
|
+
const triggerClass = "flex h-9 w-full min-w-0 items-center justify-between gap-2 " +
|
|
24
|
+
"rounded-md border border-input text-left max-md:min-h-11 " +
|
|
25
|
+
"bg-transparent px-3 py-1 text-sm shadow-sm transition-colors " +
|
|
26
|
+
"placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 " +
|
|
27
|
+
"focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
|
|
28
|
+
const popoverContentClass = "z-50 w-[var(--radix-popover-trigger-width)] rounded-md border bg-popover " +
|
|
29
|
+
"p-0 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in " +
|
|
30
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 " +
|
|
31
|
+
"data-[state=open]:fade-in-0";
|
|
32
|
+
const tagClass = "inline-flex items-center gap-1 rounded-sm bg-muted px-2 py-0.5 text-xs " +
|
|
33
|
+
"font-medium text-muted-foreground";
|
|
34
|
+
// Debounce-Default lebt zentral (hooks/reference-limits.ts) damit
|
|
35
|
+
// app-weit ein consistentes Tipp-Window gilt.
|
|
36
|
+
// Substring-Filter für cmdk's Local-Mode. cmdk's Default ist
|
|
37
|
+
// `command-score` (fuzzy), das matcht "Ad" auch gegen "Backend" weil 'a'
|
|
38
|
+
// und 'd' irgendwo enthalten sind — für Reference-Lookups irreführend
|
|
39
|
+
// (User erwartet Prefix/Substring-Verhalten, nicht Subsequence-Match).
|
|
40
|
+
// Returns 1 wenn search im value enthalten, 0 sonst — cmdk hidet Items
|
|
41
|
+
// mit Score 0.
|
|
42
|
+
function substringFilter(value, search, keywords) {
|
|
43
|
+
const needle = search.toLowerCase();
|
|
44
|
+
return [value, ...(keywords ?? [])].some((text) => text.toLowerCase().includes(needle)) ? 1 : 0;
|
|
45
|
+
}
|
|
46
|
+
export function ComboboxInput(props) {
|
|
47
|
+
const { id, name, options, disabled, required, hasError, ariaLabel, placeholder, searchPlaceholder, emptyText, onSearchChange, loading, defaultOpen, onCreate, createLabel, } = props;
|
|
48
|
+
// i18n-Defaults aus dem Framework-Bundle (kumikoDefaultTranslations).
|
|
49
|
+
// Caller-Override gewinnt; Bundle-Override greift wenn der Caller
|
|
50
|
+
// den Prop nicht setzt; raw-Key-Fallback wenn weder noch.
|
|
51
|
+
const t = useTranslation();
|
|
52
|
+
const effectivePlaceholder = placeholder ?? t("kumiko.combobox.placeholder");
|
|
53
|
+
const effectiveSearchPlaceholder = searchPlaceholder ?? t("kumiko.combobox.search-placeholder");
|
|
54
|
+
const effectiveEmptyText = emptyText ?? t("kumiko.combobox.empty");
|
|
55
|
+
const loadingText = t("kumiko.combobox.loading");
|
|
56
|
+
const effectiveCreateLabel = createLabel ?? t("kumiko.actions.create");
|
|
57
|
+
const multiple = props.multiple === true;
|
|
58
|
+
const [open, setOpen] = useState(defaultOpen === true);
|
|
59
|
+
// Local Search-Buffer für Remote-Mode. Tipps werden mit 300ms
|
|
60
|
+
// Debounce an onSearchChange weitergereicht, damit pro Tastendruck
|
|
61
|
+
// nicht ein Server-Roundtrip fliegt. Im Local-Mode ist das State
|
|
62
|
+
// ungenutzt (cmdk steuert sein Search-Input intern).
|
|
63
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
64
|
+
const isRemote = onSearchChange !== undefined;
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!isRemote)
|
|
67
|
+
return;
|
|
68
|
+
const timer = setTimeout(() => onSearchChange(searchTerm), REFERENCE_SEARCH_DEBOUNCE_MS);
|
|
69
|
+
return () => clearTimeout(timer);
|
|
70
|
+
}, [isRemote, searchTerm, onSearchChange]);
|
|
71
|
+
// Beim Schließen des Popovers den Suchbegriff zurücksetzen damit
|
|
72
|
+
// beim nächsten Öffnen nicht der vorherige term hängt. Würde sonst
|
|
73
|
+
// den Server-State stale halten zwischen Aufrufen.
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!open && searchTerm !== "")
|
|
76
|
+
setSearchTerm("");
|
|
77
|
+
}, [open, searchTerm]);
|
|
78
|
+
// Discriminated-Union per `multiple`: TS narrowt props.value/onChange
|
|
79
|
+
// automatisch — Runtime-Cast entfällt.
|
|
80
|
+
const selectedValues = multiple ? new Set(props.value) : new Set();
|
|
81
|
+
const singleValue = !multiple ? props.value : "";
|
|
82
|
+
const singleLabel = options.find((o) => o.value === singleValue)?.label ?? "";
|
|
83
|
+
const toggleMulti = (v) => {
|
|
84
|
+
if (!multiple)
|
|
85
|
+
return;
|
|
86
|
+
const current = [...props.value];
|
|
87
|
+
const idx = current.indexOf(v);
|
|
88
|
+
if (idx >= 0)
|
|
89
|
+
current.splice(idx, 1);
|
|
90
|
+
else
|
|
91
|
+
current.push(v);
|
|
92
|
+
props.onChange(current);
|
|
93
|
+
};
|
|
94
|
+
const triggerLabel = multiple ? null : singleLabel === "" ? placeholder : singleLabel;
|
|
95
|
+
return (_jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: setOpen, children: [_jsx("input", { type: "hidden", name: name, value: multiple ? props.value.join(",") : props.value }), _jsxs(PopoverPrimitive.Trigger, { id: id, "data-testid": `combobox-${id}`, type: "button", disabled: disabled, "aria-required": required, "aria-invalid": hasError === true ? true : undefined, "aria-label": ariaLabel, "aria-haspopup": "listbox", "aria-expanded": open, title: !multiple && singleLabel !== "" ? singleLabel : undefined, className: cn(triggerClass, hasError === true && "border-destructive focus-visible:ring-destructive"), children: [multiple ? (_jsx("span", { className: "flex flex-wrap items-center gap-1", children: selectedValues.size === 0 ? (_jsx("span", { className: "text-muted-foreground", children: effectivePlaceholder })) : (
|
|
96
|
+
// Tags sind read-only Anzeige (kein nested <button>
|
|
97
|
+
// möglich im Trigger-<button>). Entfernen via Re-Click
|
|
98
|
+
// im Combobox-Popup — der Item-Toggle deselektiert die
|
|
99
|
+
// entsprechende Option (analog shadcn-Standard-Combobox).
|
|
100
|
+
[...selectedValues].map((v) => {
|
|
101
|
+
const opt = options.find((o) => o.value === v);
|
|
102
|
+
return (_jsx("span", { className: tagClass, children: opt?.label ?? v }, v));
|
|
103
|
+
})) })) : (_jsx("span", { className: cn("min-w-0 flex-1 truncate text-left", singleLabel === "" && "text-muted-foreground"), children: triggerLabel })), _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })] }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { className: popoverContentClass, align: "start", sideOffset: 4, children: _jsxs(Command, { shouldFilter: !isRemote, filter: substringFilter, children: [_jsxs("div", { className: "relative", children: [_jsx(Command.Input, { placeholder: effectiveSearchPlaceholder, value: isRemote ? searchTerm : undefined, onValueChange: isRemote ? setSearchTerm : undefined, className: "flex h-9 w-full border-0 border-b border-border bg-transparent px-3 py-1 text-sm outline-none placeholder:text-muted-foreground" }), loading === true && (_jsx(Loader2, { className: "absolute right-2 top-2.5 h-4 w-4 animate-spin text-muted-foreground" }))] }), _jsxs(Command.List, { className: "max-h-64 overflow-y-auto p-1", children: [_jsx(Command.Empty, { className: "py-3 text-center text-sm text-muted-foreground", children: loading === true ? loadingText : effectiveEmptyText }), groupOptions(options).map((section) => {
|
|
104
|
+
const items = section.options.map((opt) => {
|
|
105
|
+
const isSelected = multiple
|
|
106
|
+
? selectedValues.has(opt.value)
|
|
107
|
+
: opt.value === singleValue;
|
|
108
|
+
return (_jsxs(Command.Item, { "data-testid": `combobox-${id}-option-${opt.value}`, value: opt.label, keywords: opt.description !== undefined ? [opt.description] : undefined, disabled: opt.disabled, onSelect: () => {
|
|
109
|
+
if (props.multiple === true) {
|
|
110
|
+
toggleMulti(opt.value);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
props.onChange(opt.value);
|
|
114
|
+
setOpen(false);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
// No pointer-events toggles on the item: with Tailwind active,
|
|
118
|
+
// `data-[disabled]:pointer-events-none` silently swallowed mouse
|
|
119
|
+
// clicks (keyboard select still worked, jsdom tests stayed green).
|
|
120
|
+
className: cn("relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground", opt.disabled === true && "cursor-not-allowed opacity-50"), children: [isSelected && (_jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(Check, { className: "h-4 w-4" }) })), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { children: opt.label }), opt.description !== undefined && (_jsx("span", { className: "text-xs text-muted-foreground", children: opt.description }))] })] }, opt.value));
|
|
121
|
+
});
|
|
122
|
+
return section.heading === undefined ? (items) : (_jsx(Command.Group, { heading: section.heading, className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", children: items }, section.heading));
|
|
123
|
+
})] }), onCreate !== undefined && (_jsxs("button", { type: "button", "data-testid": `combobox-${id}-create`, onClick: () => {
|
|
124
|
+
setOpen(false);
|
|
125
|
+
onCreate();
|
|
126
|
+
}, className: "flex w-full cursor-pointer select-none items-center gap-2 border-t border-border px-3 py-1.5 text-left text-sm text-primary outline-none hover:bg-accent hover:text-accent-foreground", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: effectiveCreateLabel })] }))] }) }) })] }));
|
|
127
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type DateFieldProps = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly value: string;
|
|
6
|
+
readonly onChange: (v: string | undefined) => void;
|
|
7
|
+
readonly disabled?: boolean;
|
|
8
|
+
readonly required?: boolean;
|
|
9
|
+
readonly hasError?: boolean;
|
|
10
|
+
readonly ariaLabel?: string;
|
|
11
|
+
readonly locale?: string;
|
|
12
|
+
/** Lower/upper bound as ISO `yyyy-mm-dd`. Limits the calendar
|
|
13
|
+
* (year-dropdown range + greyed-out days). Server-side validation runs
|
|
14
|
+
* separately via the Zod schemas. */
|
|
15
|
+
readonly min?: string;
|
|
16
|
+
readonly max?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function DateField({ id, name, value, onChange, disabled, required, hasError, ariaLabel, locale, min, max, }: DateFieldProps): ReactNode;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// DateField — the shared typable date input: a text input (locale-aware
|
|
3
|
+
// parse, tolerant of partial input) plus a CalendarPopover with year/
|
|
4
|
+
// decade dropdown. Underlying value is ISO `yyyy-mm-dd`.
|
|
5
|
+
//
|
|
6
|
+
// One source for both date primitives: DateInput (kind:"date") is a thin
|
|
7
|
+
// re-export of this, TimestampInput (kind:"timestamp") uses it as the
|
|
8
|
+
// date part next to the time input. So `date` and `timestamp` share the
|
|
9
|
+
// same typing/navigation UX instead of two diverging primitives (#369).
|
|
10
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
11
|
+
import { useState } from "react";
|
|
12
|
+
import { useTranslation } from "../../index.js";
|
|
13
|
+
import { cn } from "../lib/cn.js";
|
|
14
|
+
import { CalendarPopover } from "./calendar-popover.js";
|
|
15
|
+
import { formatDateForInput, formatDatePlaceholder, guessLocale, parseIso, parseTypedDate, toIso, } from "./date-parse.js";
|
|
16
|
+
// CalendarPopover wraps react-day-picker, which only accepts native Date
|
|
17
|
+
// objects — the PlainDate↔Date boundary conversion stays confined to
|
|
18
|
+
// these two functions instead of spreading through the rest of the field.
|
|
19
|
+
function toNativeDate(pd) {
|
|
20
|
+
return new Date(pd.year, pd.month - 1, pd.day);
|
|
21
|
+
}
|
|
22
|
+
function fromNativeDate(d) {
|
|
23
|
+
return Temporal.PlainDate.from({
|
|
24
|
+
year: d.getFullYear(),
|
|
25
|
+
month: d.getMonth() + 1,
|
|
26
|
+
day: d.getDate(),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const inputClass = "flex h-9 w-full items-center rounded-md border border-input bg-transparent " +
|
|
30
|
+
"px-3 py-1 text-sm shadow-sm transition-colors " +
|
|
31
|
+
"placeholder:text-muted-foreground " +
|
|
32
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring " +
|
|
33
|
+
"disabled:cursor-not-allowed disabled:opacity-50";
|
|
34
|
+
export function DateField({ id, name, value, onChange, disabled, required, hasError, ariaLabel, locale, min, max, }) {
|
|
35
|
+
const t = useTranslation();
|
|
36
|
+
const resolvedLocale = locale ?? guessLocale();
|
|
37
|
+
const selected = parseIso(value);
|
|
38
|
+
// draft === null → show the canonically formatted value. Once the user
|
|
39
|
+
// types, draft holds the raw text so input isn't reformatted on every
|
|
40
|
+
// keystroke. onBlur resets it back to null.
|
|
41
|
+
const [draft, setDraft] = useState(null);
|
|
42
|
+
const display = draft ?? (selected !== undefined ? formatDateForInput(selected, resolvedLocale) : "");
|
|
43
|
+
function commitTyped(raw) {
|
|
44
|
+
if (raw.trim() === "") {
|
|
45
|
+
onChange(undefined);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const parsed = parseTypedDate(raw, resolvedLocale);
|
|
49
|
+
if (parsed !== undefined)
|
|
50
|
+
onChange(toIso(parsed));
|
|
51
|
+
}
|
|
52
|
+
const minDate = min !== undefined ? parseIso(min) : undefined;
|
|
53
|
+
const maxDate = max !== undefined ? parseIso(max) : undefined;
|
|
54
|
+
return (_jsxs("div", { className: "relative w-full", children: [_jsx("input", { type: "text", inputMode: "numeric", id: id, name: name, value: display, disabled: disabled, required: required, "aria-invalid": hasError === true ? true : undefined, "aria-label": ariaLabel,
|
|
55
|
+
// Deliberate: slot order/separator follow resolvedLocale (the
|
|
56
|
+
// `locale` prop, or the browser locale), but the letters follow
|
|
57
|
+
// the app's UI locale via t() — a caller rendering a date in a
|
|
58
|
+
// locale that differs from the UI locale gets a mixed-locale
|
|
59
|
+
// placeholder (e.g. German UI + locale="en-US" → "MM/TT/JJJJ").
|
|
60
|
+
// Accepted for now (fw#1879): a static per-locale letter lookup
|
|
61
|
+
// would need its own locale-root resolution and tests for a
|
|
62
|
+
// placeholder-only, Low-severity mismatch.
|
|
63
|
+
placeholder: formatDatePlaceholder(resolvedLocale, {
|
|
64
|
+
year: t("kumiko.field.dateField.placeholderYear"),
|
|
65
|
+
month: t("kumiko.field.dateField.placeholderMonth"),
|
|
66
|
+
day: t("kumiko.field.dateField.placeholderDay"),
|
|
67
|
+
}), onChange: (e) => {
|
|
68
|
+
setDraft(e.target.value);
|
|
69
|
+
commitTyped(e.target.value);
|
|
70
|
+
}, onBlur: () => setDraft(null), className: cn(inputClass, "pr-9", hasError === true && "border-destructive focus-visible:ring-destructive") }), _jsx(CalendarPopover, { selected: selected !== undefined ? toNativeDate(selected) : undefined, onSelect: (d) => {
|
|
71
|
+
onChange(d !== undefined ? toIso(fromNativeDate(d)) : undefined);
|
|
72
|
+
setDraft(null);
|
|
73
|
+
}, ...(minDate !== undefined && { min: toNativeDate(minDate) }), ...(maxDate !== undefined && { max: toNativeDate(maxDate) }), ...(disabled !== undefined && { disabled }), ...(hasError !== undefined && { hasError }), triggerLabel: t("kumiko.field.open-calendar") })] }));
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DateField as DateInput, type DateFieldProps as DateInputProps } from "./date-field.tsx";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// DateInput (kind:"date") — die tippbare Datums-Eingabe lebt in DateField,
|
|
2
|
+
// damit `date` und `timestamp` dieselbe Tipp-/Kalender-UX teilen (#369).
|
|
3
|
+
// Diese Datei hält nur das öffentliche Mapping; die Logik ist in
|
|
4
|
+
// date-field.tsx, die Date-Parse-Utils in date-parse.ts.
|
|
5
|
+
export { DateField as DateInput } from "./date-field.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
export declare function guessLocale(): string;
|
|
3
|
+
export declare function parseIso(v: string): Temporal.PlainDate | undefined;
|
|
4
|
+
export declare function toIso(d: Temporal.PlainDate): string;
|
|
5
|
+
export declare function formatDateForInput(d: Temporal.PlainDate, locale: string): string;
|
|
6
|
+
export declare function formatDatePlaceholder(locale: string, letters: {
|
|
7
|
+
readonly year: string;
|
|
8
|
+
readonly month: string;
|
|
9
|
+
readonly day: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare function parseTypedDate(input: string, locale: string): Temporal.PlainDate | undefined;
|