@cosmicdrift/kumiko-renderer 0.374.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 +9 -2
- package/dist/app/action-form-support.js +24 -2
- package/dist/app/client-actions.d.ts +2 -2
- 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/form-schema.d.ts +1 -1
- package/dist/app/form-schema.js +2 -2
- package/dist/app/kumiko-screen.d.ts +1 -1
- package/dist/app/kumiko-screen.js +7 -6
- package/dist/app/list-empty-content.d.ts +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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/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 +4 -2
- package/dist/components/render-field.d.ts +1 -1
- package/dist/components/render-field.js +4 -1
- 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.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/index.js +4 -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 +35 -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 +53 -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,7 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { JsonViewProps } from "../../index.ts";
|
|
3
|
+
/** Syntax-highlighted, whitespace-preserving JSON display. Bounded height
|
|
4
|
+
* with its own vertical scroll (job logs get long) instead of growing the
|
|
5
|
+
* page; wraps rather than overflowing horizontally. Colors come from the
|
|
6
|
+
* dedicated syntax-* theme tokens (styles.css), readable in light + dark. */
|
|
7
|
+
export declare function DefaultJsonView({ value, indent, testId }: JsonViewProps): ReactNode;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Recursive pre-pass (not JSX) that turns `value` into a JSON.stringify-safe
|
|
3
|
+
// plain structure: BigInt → string, and a real ancestor-path cycle check
|
|
4
|
+
// (not a global "seen" set — a value referenced twice via two different,
|
|
5
|
+
// non-circular paths must still render twice, not collapse to "[Circular]").
|
|
6
|
+
function toSafeJson(value, ancestors = []) {
|
|
7
|
+
if (typeof value === "bigint")
|
|
8
|
+
return `${value.toString()}n`;
|
|
9
|
+
if (typeof value !== "object" || value === null)
|
|
10
|
+
return value;
|
|
11
|
+
if (ancestors.includes(value))
|
|
12
|
+
return "[Circular]";
|
|
13
|
+
const nextAncestors = [...ancestors, value];
|
|
14
|
+
if (Array.isArray(value))
|
|
15
|
+
return value.map((item) => toSafeJson(item, nextAncestors));
|
|
16
|
+
const out = {};
|
|
17
|
+
for (const [k, v] of Object.entries(value))
|
|
18
|
+
out[k] = toSafeJson(v, nextAncestors);
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
// A broken audit/log payload must not take the whole screen down with it —
|
|
22
|
+
// double try/catch so even a throwing getter on the input falls back to a
|
|
23
|
+
// plain string instead of propagating.
|
|
24
|
+
function safeStringify(value, indent) {
|
|
25
|
+
try {
|
|
26
|
+
const json = JSON.stringify(toSafeJson(value), null, indent);
|
|
27
|
+
return json ?? String(value);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
try {
|
|
31
|
+
return String(value);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return "[unserializable value]";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const TOKEN_CLASS_NAME = {
|
|
39
|
+
key: "text-syntax-key font-medium",
|
|
40
|
+
string: "text-syntax-string",
|
|
41
|
+
number: "text-syntax-number",
|
|
42
|
+
boolean: "text-syntax-literal",
|
|
43
|
+
null: "text-syntax-literal italic",
|
|
44
|
+
};
|
|
45
|
+
function classifyToken(token) {
|
|
46
|
+
if (token.startsWith('"'))
|
|
47
|
+
return token.endsWith(":") ? "key" : "string";
|
|
48
|
+
if (token === "true" || token === "false")
|
|
49
|
+
return "boolean";
|
|
50
|
+
if (token === "null")
|
|
51
|
+
return "null";
|
|
52
|
+
return "number";
|
|
53
|
+
}
|
|
54
|
+
// The stringify output is canonical JSON — a regex tokenizer over that
|
|
55
|
+
// string is a fraction of the code of a recursive value-tree renderer, and
|
|
56
|
+
// tokens become plain React elements (never dangerouslySetInnerHTML): audit
|
|
57
|
+
// payloads and job logs carry untrusted third-party data, so this must not
|
|
58
|
+
// become an HTML-injection path.
|
|
59
|
+
function tokenizeJson(json) {
|
|
60
|
+
const tokenRe = /("(?:\\u[a-fA-F0-9]{4}|\\.|[^\\"])*"(?:\s*:)?|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|\btrue\b|\bfalse\b|\bnull\b)/g;
|
|
61
|
+
const nodes = [];
|
|
62
|
+
let lastIndex = 0;
|
|
63
|
+
let match = tokenRe.exec(json);
|
|
64
|
+
let key = 0;
|
|
65
|
+
while (match !== null) {
|
|
66
|
+
if (match.index > lastIndex)
|
|
67
|
+
nodes.push(json.slice(lastIndex, match.index));
|
|
68
|
+
const token = match[0];
|
|
69
|
+
nodes.push(_jsx("span", { className: TOKEN_CLASS_NAME[classifyToken(token)], children: token }, key++));
|
|
70
|
+
lastIndex = tokenRe.lastIndex;
|
|
71
|
+
match = tokenRe.exec(json);
|
|
72
|
+
}
|
|
73
|
+
if (lastIndex < json.length)
|
|
74
|
+
nodes.push(json.slice(lastIndex));
|
|
75
|
+
return nodes;
|
|
76
|
+
}
|
|
77
|
+
/** Syntax-highlighted, whitespace-preserving JSON display. Bounded height
|
|
78
|
+
* with its own vertical scroll (job logs get long) instead of growing the
|
|
79
|
+
* page; wraps rather than overflowing horizontally. Colors come from the
|
|
80
|
+
* dedicated syntax-* theme tokens (styles.css), readable in light + dark. */
|
|
81
|
+
export function DefaultJsonView({ value, indent = 2, testId }) {
|
|
82
|
+
const json = safeStringify(value, indent);
|
|
83
|
+
return (_jsx("pre", { "data-testid": testId, className: "max-h-80 overflow-y-auto whitespace-pre-wrap break-words rounded bg-muted p-2 font-mono text-xs", children: tokenizeJson(json) }));
|
|
84
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { FormWidth } from "../../index.ts";
|
|
3
|
+
export type ScreenWidth = FormWidth;
|
|
4
|
+
export declare const screenWidthClassName: Record<ScreenWidth, string>;
|
|
5
|
+
export declare const screenFormColumnWidthClassName: Record<ScreenWidth, string>;
|
|
6
|
+
export declare const screenPaddingClassName = "px-4 pb-6 pt-5 md:px-10 md:pb-10 md:pt-7";
|
|
7
|
+
declare const STACK_GAP: {
|
|
8
|
+
readonly sm: "gap-2";
|
|
9
|
+
readonly md: "gap-4";
|
|
10
|
+
readonly lg: "gap-6";
|
|
11
|
+
};
|
|
12
|
+
type StackGap = keyof typeof STACK_GAP;
|
|
13
|
+
type StackProps = {
|
|
14
|
+
readonly gap?: StackGap;
|
|
15
|
+
readonly className?: string;
|
|
16
|
+
readonly children?: ReactNode;
|
|
17
|
+
readonly testId?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function Stack({ gap, className, children, testId }: StackProps): ReactNode;
|
|
20
|
+
type PageSectionProps = {
|
|
21
|
+
readonly className?: string;
|
|
22
|
+
readonly children?: ReactNode;
|
|
23
|
+
readonly testId?: string;
|
|
24
|
+
readonly maxWidth?: ScreenWidth;
|
|
25
|
+
};
|
|
26
|
+
export declare function PageSection({ className, children, testId, maxWidth, }: PageSectionProps): ReactNode;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
// Shared 3xl/4xl/full → max-w-*/mx-auto map. FormScreenShell
|
|
4
|
+
// (primitives/index.tsx) reuses this instead of keeping its own copy —
|
|
5
|
+
// same width tokens must render the same class everywhere (fw#2640).
|
|
6
|
+
export const screenWidthClassName = {
|
|
7
|
+
"3xl": "max-w-3xl mx-auto",
|
|
8
|
+
"4xl": "max-w-4xl mx-auto",
|
|
9
|
+
full: "max-w-full",
|
|
10
|
+
};
|
|
11
|
+
// Screen-form column variant: left-aligned (no mx-auto) so the column lines up
|
|
12
|
+
// with the screen's other left-aligned content, e.g. the header region.
|
|
13
|
+
export const screenFormColumnWidthClassName = {
|
|
14
|
+
"3xl": "max-w-3xl",
|
|
15
|
+
"4xl": "max-w-4xl",
|
|
16
|
+
full: "max-w-full",
|
|
17
|
+
};
|
|
18
|
+
// Shared screen padding for every screen container (PageSection,
|
|
19
|
+
// FormScreenShell, list screens and the pinned-footer screen form). The inset
|
|
20
|
+
// is the one the settings screens use, so a list, a dashboard and a form start
|
|
21
|
+
// at the same edge. The bottom inset keeps the last field or table row off the
|
|
22
|
+
// viewport edge (fw#2640).
|
|
23
|
+
export const screenPaddingClassName = "px-4 pb-6 pt-5 md:px-10 md:pb-10 md:pt-7";
|
|
24
|
+
const STACK_GAP = { sm: "gap-2", md: "gap-4", lg: "gap-6" };
|
|
25
|
+
export function Stack({ gap = "md", className, children, testId }) {
|
|
26
|
+
return (_jsx("div", { "data-testid": testId, className: cn("flex flex-col", STACK_GAP[gap], className), children: children }));
|
|
27
|
+
}
|
|
28
|
+
export function PageSection({ className, children, testId, maxWidth = "full", }) {
|
|
29
|
+
return (_jsx("div", { "data-testid": testId, className: cn(screenPaddingClassName, screenWidthClassName[maxWidth], className), children: children }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Full-size image overlay — ModalShell without confirm/cancel actions.
|
|
3
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
4
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
|
+
import { useTranslation } from "../../index.js";
|
|
6
|
+
import { cn } from "../lib/cn.js";
|
|
7
|
+
import { ModalShell } from "./modal-shell.js";
|
|
8
|
+
const NAV_BUTTON_CLASS = "absolute top-1/2 -translate-y-1/2 flex size-9 items-center justify-center rounded-full border border-border bg-card text-foreground opacity-80 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring";
|
|
9
|
+
export function DefaultLightbox(props) {
|
|
10
|
+
const { open, onOpenChange, testId, actions, showPosition = true } = props;
|
|
11
|
+
const t = useTranslation();
|
|
12
|
+
const images = "images" in props ? props.images : [{ src: props.src, alt: props.alt }];
|
|
13
|
+
const onIndexChange = "images" in props ? props.onIndexChange : undefined;
|
|
14
|
+
const total = images.length;
|
|
15
|
+
// Clamped here so a stale index after the image set shrinks never renders blank.
|
|
16
|
+
const requestedIndex = "images" in props ? props.index : 0;
|
|
17
|
+
const index = Math.min(Math.max(requestedIndex, 0), total - 1);
|
|
18
|
+
const current = images[index];
|
|
19
|
+
if (current === undefined)
|
|
20
|
+
return null;
|
|
21
|
+
const canNavigate = onIndexChange !== undefined && total > 1;
|
|
22
|
+
const goTo = (target) => {
|
|
23
|
+
onIndexChange?.((target + total) % total);
|
|
24
|
+
};
|
|
25
|
+
const handleKeyDown = (event) => {
|
|
26
|
+
if (event.key === "ArrowRight") {
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
goTo(index + 1);
|
|
29
|
+
}
|
|
30
|
+
else if (event.key === "ArrowLeft") {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
goTo(index - 1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return (_jsxs(ModalShell, { open: open, onOpenChange: onOpenChange, testId: testId, closeLabel: t("kumiko.dialog.close"), noAriaDescription: true, ...(canNavigate && { onContentKeyDown: handleKeyDown }), contentClassName: cn("border-0 bg-transparent p-0 shadow-none", "max-w-[95vw] max-h-[90vh] w-auto"), children: [_jsx(DialogPrimitive.Title, { className: "sr-only", children: current.alt }), _jsx("img", { src: current.src, alt: current.alt, className: "block max-h-[85vh] max-w-[90vw] w-auto rounded-lg border border-border bg-card shadow-lg" }), canNavigate && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": t("kumiko.lightbox.previous"), onClick: () => goTo(index - 1), className: cn(NAV_BUTTON_CLASS, "left-2"), children: _jsx(ChevronLeft, { className: "size-5" }) }), _jsx("button", { type: "button", "aria-label": t("kumiko.lightbox.next"), onClick: () => goTo(index + 1), className: cn(NAV_BUTTON_CLASS, "right-2"), children: _jsx(ChevronRight, { className: "size-5" }) }), showPosition && (_jsx("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-card px-3 py-1 text-xs text-foreground shadow-sm", children: t("kumiko.lightbox.position", { current: index + 1, total }) }))] })), actions !== undefined && (_jsx("div", { "data-testid": testId !== undefined ? `${testId}-actions` : undefined, className: "absolute left-2 top-2 flex items-center gap-2", children: actions }))] }));
|
|
36
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Shared locale handling for the text-based numeric inputs (MoneyInput,
|
|
2
|
+
// NumberInput).
|
|
3
|
+
const FALLBACK_LOCALE = "en-US";
|
|
4
|
+
// An invalid tag makes every Intl constructor and toLocaleString throw a
|
|
5
|
+
// RangeError; without an ErrorBoundary that would take down the page on
|
|
6
|
+
// focus/blur. Canonicalizing once up front keeps all call sites safe.
|
|
7
|
+
export function resolveSafeLocale(locale) {
|
|
8
|
+
const candidate = locale ?? (typeof navigator !== "undefined" ? navigator.language : undefined) ?? "";
|
|
9
|
+
try {
|
|
10
|
+
return Intl.getCanonicalLocales(candidate)[0] ?? FALLBACK_LOCALE;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return FALLBACK_LOCALE;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Separators come from Intl for the locale. A minus is only accepted at the
|
|
17
|
+
// very start: "1-23" is invalid rather than -123, so a typo never turns into
|
|
18
|
+
// a wrong amount. Anything besides digits and separators yields NaN so callers
|
|
19
|
+
// drop the input instead of storing a corrupted number.
|
|
20
|
+
export function parseLocaleNumber(raw, locale) {
|
|
21
|
+
const { groupSep, decimalSep } = localeSeparators(locale);
|
|
22
|
+
const trimmed = raw.trim();
|
|
23
|
+
const negative = trimmed.startsWith("-");
|
|
24
|
+
const body = negative ? trimmed.slice(1) : trimmed;
|
|
25
|
+
const [integerPart = "", fractionPart, ...extra] = body.split(decimalSep);
|
|
26
|
+
if (extra.length > 0)
|
|
27
|
+
return Number.NaN;
|
|
28
|
+
if (!isValidIntegerPart(integerPart, fractionPart, groupSep))
|
|
29
|
+
return Number.NaN;
|
|
30
|
+
if (fractionPart !== undefined && !/^\d*$/.test(fractionPart))
|
|
31
|
+
return Number.NaN;
|
|
32
|
+
const digits = integerPart.replace(/\D/g, "");
|
|
33
|
+
const n = Number(`${digits === "" ? "0" : digits}${fractionPart ? `.${fractionPart}` : ""}`);
|
|
34
|
+
return negative ? -n : n;
|
|
35
|
+
}
|
|
36
|
+
function localeSeparators(locale) {
|
|
37
|
+
const parts = new Intl.NumberFormat(resolveSafeLocale(locale)).formatToParts(1234.5);
|
|
38
|
+
return {
|
|
39
|
+
groupSep: parts.find((p) => p.type === "group")?.value ?? ",",
|
|
40
|
+
decimalSep: parts.find((p) => p.type === "decimal")?.value ?? ".",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Group separators only count in valid positions: "1.5" in de is rejected
|
|
44
|
+
// instead of silently becoming 15. An empty integer part (",5") stays valid
|
|
45
|
+
// when a fraction follows.
|
|
46
|
+
function isValidIntegerPart(integerPart, fractionPart, groupSep) {
|
|
47
|
+
if (integerPart === "")
|
|
48
|
+
return fractionPart !== undefined && fractionPart !== "";
|
|
49
|
+
const groupClass = /\s/.test(groupSep) ? "[\\s\\u00a0\\u202f]" : escapeRegExp(groupSep);
|
|
50
|
+
return new RegExp(`^(?:\\d+|\\d{1,3}(?:${groupClass}\\d{3})+)$`).test(integerPart);
|
|
51
|
+
}
|
|
52
|
+
function escapeRegExp(value) {
|
|
53
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
54
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type LocatedTimestampValue = {
|
|
3
|
+
readonly at: string;
|
|
4
|
+
readonly tz: string;
|
|
5
|
+
readonly utc?: string;
|
|
6
|
+
};
|
|
7
|
+
export type LocatedTimestampInputProps = {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly value: LocatedTimestampValue | "";
|
|
11
|
+
readonly onChange: (v: {
|
|
12
|
+
readonly at: string;
|
|
13
|
+
readonly tz: string;
|
|
14
|
+
} | undefined) => void;
|
|
15
|
+
readonly locale?: string;
|
|
16
|
+
readonly min?: string;
|
|
17
|
+
readonly max?: string;
|
|
18
|
+
readonly disabled?: boolean;
|
|
19
|
+
readonly required?: boolean;
|
|
20
|
+
readonly hasError?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare function LocatedTimestampInput({ id, name, value, onChange, locale, min, max, disabled, required, hasError, }: LocatedTimestampInputProps): ReactNode;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "../../index.js";
|
|
3
|
+
import { ComboboxInput } from "./combobox.js";
|
|
4
|
+
import { TimestampInput } from "./timestamp-input.js";
|
|
5
|
+
import { TZ_OPTIONS } from "./tz-options.js";
|
|
6
|
+
export function LocatedTimestampInput({ id, name, value, onChange, locale, min, max, disabled, required, hasError, }) {
|
|
7
|
+
const t = useTranslation();
|
|
8
|
+
const at = value === "" ? "" : value.at;
|
|
9
|
+
const tz = value === "" ? "" : value.tz;
|
|
10
|
+
// `at` ist Minuten-Wallclock (z.iso.datetime({local:true}) akzeptiert das);
|
|
11
|
+
// tz ein IANA-Name. Solange beides leer ist gilt das Feld als leer; sobald
|
|
12
|
+
// eines gesetzt ist, fließt das Paar — die Vollständigkeit erzwingt die
|
|
13
|
+
// Required-/Zod-Validierung beim Submit (so bleibt eine halbe Eingabe sichtbar).
|
|
14
|
+
function emit(nextAt, nextTz) {
|
|
15
|
+
if (nextAt === "" && nextTz === "") {
|
|
16
|
+
onChange(undefined);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
onChange({ at: nextAt, tz: nextTz });
|
|
20
|
+
}
|
|
21
|
+
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(TimestampInput, { id: id, name: name, value: at, onChange: (v) => emit(v ?? "", tz), wallClock: true, ...(locale !== undefined && { locale }), ...(min !== undefined && { min }), ...(max !== undefined && { max }), ...(disabled !== undefined && { disabled }), ...(required !== undefined && { required }), ...(hasError !== undefined && { hasError }) }), _jsx("div", { className: "min-w-[12rem]", children: _jsx(ComboboxInput, { id: `${id}-tz`, name: `${name}-tz`, options: TZ_OPTIONS, value: tz, onChange: (v) => emit(at, v), placeholder: t("kumiko.field.timezone"), ...(disabled !== undefined && { disabled }), ...(required !== undefined && { required }), ...(hasError !== undefined && { hasError }) }) })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("kumiko.field.locatedTzHint") })] }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { MetricBandProps, MetricProps } from "../../index.ts";
|
|
3
|
+
export declare const InsideScreenFormColumnContext: import("react").Context<boolean>;
|
|
4
|
+
export declare function DefaultMetricBand({ lead, subtitle, children, testId, }: MetricBandProps): ReactNode;
|
|
5
|
+
export declare function DefaultMetric({ label, value, testId, onPress }: MetricProps): ReactNode;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
const InsideMetricBandContext = createContext(false);
|
|
5
|
+
// The screen-form column already insets and spaces its children; a band in its
|
|
6
|
+
// header region must not add its own padding on top. DefaultCard resets this
|
|
7
|
+
// to false because a card body is a different inset surface.
|
|
8
|
+
export const InsideScreenFormColumnContext = createContext(false);
|
|
9
|
+
// Column counts follow the board: 2 below md, 3 below lg, 5 from lg.
|
|
10
|
+
export function DefaultMetricBand({ lead, subtitle, children, testId, }) {
|
|
11
|
+
const insideScreenFormColumn = useContext(InsideScreenFormColumnContext);
|
|
12
|
+
return (_jsxs("div", { className: cn("flex shrink-0 flex-col gap-3", !insideScreenFormColumn && "px-6 pb-4 pt-3.5"), "data-testid": testId, children: [lead !== undefined && _jsx("div", { "data-kumiko-layout": "metric-band-lead", children: lead }), subtitle !== undefined && (_jsx("div", { className: "flex flex-wrap items-center gap-x-2 text-[13px] text-foreground-secondary [&_a]:text-primary", children: subtitle })), _jsx(InsideMetricBandContext.Provider, { value: true, children: _jsx("dl", { className: "m-0 grid grid-cols-2 gap-6 md:grid-cols-3 lg:grid-cols-5", children: children }) })] }));
|
|
13
|
+
}
|
|
14
|
+
export function DefaultMetric({ label, value, testId, onPress }) {
|
|
15
|
+
const insideBand = useContext(InsideMetricBandContext);
|
|
16
|
+
const item = (_jsxs("div", { "data-testid": testId, className: cn("flex min-w-0 flex-col gap-0.5", onPress !== undefined && "cursor-pointer"), ...(onPress !== undefined && { onClick: onPress }), children: [_jsx("dt", { className: "text-xs text-muted-foreground", "data-testid": testId !== undefined ? `${testId}-label` : undefined, children: label }), _jsx("dd", { className: "m-0 text-base font-medium tabular-nums text-foreground", "data-testid": testId !== undefined ? `${testId}-value` : undefined, children: onPress !== undefined ? (_jsx("button", { type: "button", className: "text-left hover:underline", children: value })) : (value) })] }));
|
|
17
|
+
return insideBand ? item : _jsx("dl", { className: "m-0", children: item });
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { KeyboardEvent, ReactNode } from "react";
|
|
2
|
+
export type ModalShellProps = {
|
|
3
|
+
readonly open: boolean;
|
|
4
|
+
readonly onOpenChange: (open: boolean) => void;
|
|
5
|
+
readonly children: ReactNode;
|
|
6
|
+
readonly testId?: string;
|
|
7
|
+
readonly contentClassName?: string;
|
|
8
|
+
/** Radix wants aria-describedby={undefined} when there is no Description. */
|
|
9
|
+
readonly noAriaDescription?: boolean;
|
|
10
|
+
readonly closeLabel?: string;
|
|
11
|
+
readonly showCloseButton?: boolean;
|
|
12
|
+
/** Lets the caller take over Radix's initial-focus target — call
|
|
13
|
+
* `event.preventDefault()` and focus the desired element yourself. */
|
|
14
|
+
readonly onOpenAutoFocus?: (event: Event) => void;
|
|
15
|
+
readonly onContentKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function ModalShell({ open, onOpenChange, children, testId, contentClassName, noAriaDescription, closeLabel, showCloseButton, onOpenAutoFocus, onContentKeyDown, }: ModalShellProps): ReactNode;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Shared Radix dialog chrome for confirm Dialog + image Lightbox.
|
|
3
|
+
// Not exported from the package — only an internal building block.
|
|
4
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
|
+
import { X } from "lucide-react";
|
|
6
|
+
import { cn } from "../lib/cn.js";
|
|
7
|
+
export function ModalShell({ open, onOpenChange, children, testId, contentClassName, noAriaDescription, closeLabel, showCloseButton = true, onOpenAutoFocus, onContentKeyDown, }) {
|
|
8
|
+
return (_jsx(DialogPrimitive.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" }), _jsxs(DialogPrimitive.Content, { "data-testid": testId, ...(noAriaDescription && { "aria-describedby": undefined }), ...(onOpenAutoFocus !== undefined && { onOpenAutoFocus }), ...(onContentKeyDown !== undefined && { onKeyDown: onContentKeyDown }), className: cn("fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", contentClassName), children: [children, showCloseButton && closeLabel !== undefined && (_jsx(DialogPrimitive.Close, { asChild: true, children: _jsx("button", { type: "button", "aria-label": closeLabel, className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: _jsx(X, { className: "size-4" }) }) }))] })] }) }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Bare content shell for hosting self-contained widgets (own submit/cancel
|
|
3
|
+
// buttons) in a modal overlay — same Radix chrome as DefaultDialog, no
|
|
4
|
+
// footer buttons of its own.
|
|
5
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
6
|
+
import { useTranslation } from "../../index.js";
|
|
7
|
+
import { cn } from "../lib/cn.js";
|
|
8
|
+
import { BareFormProvider } from "./index.js";
|
|
9
|
+
import { ModalShell } from "./modal-shell.js";
|
|
10
|
+
export function DefaultModal({ open, onOpenChange, title, children, testId, }) {
|
|
11
|
+
const t = useTranslation();
|
|
12
|
+
return (_jsxs(ModalShell, { open: open, onOpenChange: onOpenChange, testId: testId, closeLabel: t("kumiko.dialog.close"), noAriaDescription: true, contentClassName: cn("grid w-full max-w-lg gap-4 border bg-card p-6 shadow-lg rounded-lg"), children: [_jsx(DialogPrimitive.Title, { className: "text-lg font-semibold leading-none tracking-tight", children: title }), _jsx("div", { onSubmit: (e) => e.stopPropagation(), children: _jsx(BareFormProvider, { children: children }) })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { currencyDecimals } from "../../headless/index.ts";
|
|
3
|
+
import { parseLocaleNumber } from "./locale-number.ts";
|
|
4
|
+
export { currencyDecimals, parseLocaleNumber };
|
|
5
|
+
export type MoneyInputProps = {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly value: number | "";
|
|
9
|
+
readonly onChange: (v: number | undefined) => void;
|
|
10
|
+
readonly currency: string;
|
|
11
|
+
readonly locale?: string;
|
|
12
|
+
readonly disabled?: boolean;
|
|
13
|
+
readonly required?: boolean;
|
|
14
|
+
readonly hasError?: boolean;
|
|
15
|
+
readonly ariaLabel?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function MoneyInput({ id, name, value, onChange, currency, locale, disabled, required, hasError, ariaLabel, }: MoneyInputProps): ReactNode;
|
|
18
|
+
export declare function formatMoney(amountMinor: number, currency: string, locale?: string, fractionDigits?: number): string;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// MoneyInput — type=text mit focus-aware Locale-Format. Canonical-Wert
|
|
3
|
+
// bleiben Minor-Units (Cents) wie auf der Wire; während Focus zeigt das
|
|
4
|
+
// Input einen rohen, editierbaren Decimal-String. Bei Blur formatiert
|
|
5
|
+
// Intl.NumberFormat mit `style: "currency"` — das liefert Currency-
|
|
6
|
+
// Symbol (€/$/¥) UND Tausender-Trenner UND korrekte Decimals in einem
|
|
7
|
+
// Aufruf.
|
|
8
|
+
//
|
|
9
|
+
// Warum nicht type=number: number-Inputs lehnen formatierte Strings
|
|
10
|
+
// ("1.234,56 €") ab — kein Browser akzeptiert Locale-Decimals (Komma)
|
|
11
|
+
// in number-Inputs. inputMode="decimal" gibt mobiles Numpad-Keyboard
|
|
12
|
+
// trotzdem.
|
|
13
|
+
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
14
|
+
import { currencyDecimals, formatMoney as formatMoneyForLocale } from "../../headless/index.js";
|
|
15
|
+
import { cn } from "../lib/cn.js";
|
|
16
|
+
import { parseLocaleNumber, resolveSafeLocale } from "./locale-number.js";
|
|
17
|
+
// Re-exported for backward compat — callers used to import this from here
|
|
18
|
+
// before it moved to headless (shared with RenderField, kumiko-framework#1923).
|
|
19
|
+
export { currencyDecimals, parseLocaleNumber };
|
|
20
|
+
// The border and focus ring live on the wrapper so the currency symbol can be
|
|
21
|
+
// a flex sibling of the input: a symbol of any width ("€", "CHF", "R$") takes
|
|
22
|
+
// its own space instead of overlapping the digits.
|
|
23
|
+
const wrapperClass = "flex h-9 w-full items-center rounded-md border border-input bg-transparent text-sm shadow-sm " +
|
|
24
|
+
"transition-colors focus-within:ring-1 focus-within:ring-ring";
|
|
25
|
+
const inputClass = "h-full min-w-0 flex-1 bg-transparent px-3 placeholder:text-muted-foreground " +
|
|
26
|
+
"focus-visible:outline-none disabled:cursor-not-allowed " +
|
|
27
|
+
// Numerische Inputs rechtsbündig — wie native type=number — damit
|
|
28
|
+
// Beträge unter Listen-Spalten an den Tausender-Stellen alignen.
|
|
29
|
+
"text-right tabular-nums";
|
|
30
|
+
export function MoneyInput({ id, name, value, onChange, currency, locale, disabled, required, hasError, ariaLabel, }) {
|
|
31
|
+
const decimals = currencyDecimals(currency);
|
|
32
|
+
const factor = 10 ** decimals;
|
|
33
|
+
const resolvedLocale = resolveSafeLocale(locale);
|
|
34
|
+
const [focused, setFocused] = useState(false);
|
|
35
|
+
// Raw-Edit-Buffer während Focus. Sonst würde jeder Tipp-Step durch
|
|
36
|
+
// Math.round → format-Roundtrip jagen und der Cursor würde springen.
|
|
37
|
+
const [draft, setDraft] = useState("");
|
|
38
|
+
const inputRef = useRef(null);
|
|
39
|
+
const major = value === "" ? null : value / factor;
|
|
40
|
+
const { symbol, symbolPosition, formatNumber } = useMemo(() => moneyFormatParts(currency, resolvedLocale), [currency, resolvedLocale]);
|
|
41
|
+
const formatted = value === "" ? "" : formatNumber(value / factor);
|
|
42
|
+
// Edit-Mode: Decimal-String ohne Tausender-Trenner.
|
|
43
|
+
const toEditable = (m) => m === null
|
|
44
|
+
? ""
|
|
45
|
+
: m.toLocaleString(resolvedLocale, {
|
|
46
|
+
minimumFractionDigits: decimals,
|
|
47
|
+
maximumFractionDigits: decimals,
|
|
48
|
+
useGrouping: false,
|
|
49
|
+
});
|
|
50
|
+
const handleFocus = () => {
|
|
51
|
+
setDraft(toEditable(major));
|
|
52
|
+
setFocused(true);
|
|
53
|
+
};
|
|
54
|
+
// Select-all-on-focus: deliberate, not just a Playwright accommodation.
|
|
55
|
+
// Focus always swaps the displayed value from the formatted string
|
|
56
|
+
// ("1.234,56 €") to the raw editable one ("1234,56"). Measured in a real
|
|
57
|
+
// browser: on this kind of value swap, the browser collapses the cursor
|
|
58
|
+
// to the *end* of the new value rather than preserving position — so
|
|
59
|
+
// without an explicit re-select, typing right after focus appends
|
|
60
|
+
// instead of replacing. That's what silently corrupted values set via
|
|
61
|
+
// Playwright's `.fill()` (framework#1856). It also matches standard
|
|
62
|
+
// money-input UX (immediate overtype on click), and sidesteps mapping a
|
|
63
|
+
// click position from the formatted view to the editable one, which has
|
|
64
|
+
// no well-defined equivalent once separators and the currency symbol
|
|
65
|
+
// are stripped.
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
|
+
if (focused)
|
|
68
|
+
inputRef.current?.select();
|
|
69
|
+
}, [focused]);
|
|
70
|
+
const handleBlur = () => {
|
|
71
|
+
setFocused(false);
|
|
72
|
+
if (draft.trim() === "") {
|
|
73
|
+
onChange(undefined);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const parsed = parseLocaleNumber(draft, resolvedLocale);
|
|
77
|
+
if (Number.isNaN(parsed))
|
|
78
|
+
return;
|
|
79
|
+
onChange(Math.round(parsed * factor));
|
|
80
|
+
};
|
|
81
|
+
const symbolEl = symbol !== "" ? (_jsx("span", { id: `${id}-currency`, "data-testid": `${id}-currency`, className: cn("shrink-0 select-none text-muted-foreground", symbolPosition === "prefix" ? "pl-3" : "pr-3"), children: symbol })) : null;
|
|
82
|
+
return (_jsxs("div", { className: cn(wrapperClass, hasError === true && "border-destructive focus-within:ring-destructive", disabled === true && "cursor-not-allowed opacity-50"), children: [symbolPosition === "prefix" && symbolEl, _jsx("input", { ref: inputRef, id: id, name: name, type: "text", inputMode: "decimal", autoComplete: "off", disabled: disabled, "aria-required": required, "aria-invalid": hasError === true ? true : undefined, "aria-label": ariaLabel, "aria-describedby": symbol !== "" ? `${id}-currency` : undefined, value: focused ? draft : formatted, onFocus: handleFocus, onBlur: handleBlur, onChange: (e) => setDraft(e.target.value), className: cn(inputClass, symbolPosition === "prefix" ? "pl-2" : "pr-2") }), symbolPosition === "suffix" && symbolEl] }));
|
|
83
|
+
}
|
|
84
|
+
// Shared with defaultCellRender (index.tsx) so both formatting paths stay
|
|
85
|
+
// identical. Resolves the browser locale here; headless stays navigator-free.
|
|
86
|
+
export function formatMoney(amountMinor, currency, locale, fractionDigits) {
|
|
87
|
+
return formatMoneyForLocale(amountMinor, currency, resolveSafeLocale(locale), fractionDigits);
|
|
88
|
+
}
|
|
89
|
+
// The currency symbol is rendered as a separate adornment, so the field
|
|
90
|
+
// value shows only the grouped number. Position follows the locale's pattern.
|
|
91
|
+
function moneyFormatParts(currency, locale) {
|
|
92
|
+
if (!/^[A-Za-z]{3}$/.test(currency)) {
|
|
93
|
+
return { symbol: "", symbolPosition: "suffix", formatNumber: String };
|
|
94
|
+
}
|
|
95
|
+
const decimals = currencyDecimals(currency);
|
|
96
|
+
try {
|
|
97
|
+
const formatter = new Intl.NumberFormat(locale, {
|
|
98
|
+
style: "currency",
|
|
99
|
+
currency,
|
|
100
|
+
minimumFractionDigits: decimals,
|
|
101
|
+
maximumFractionDigits: decimals,
|
|
102
|
+
});
|
|
103
|
+
const sample = formatter.formatToParts(1);
|
|
104
|
+
const symbolIndex = sample.findIndex((p) => p.type === "currency");
|
|
105
|
+
const integerIndex = sample.findIndex((p) => p.type === "integer");
|
|
106
|
+
return {
|
|
107
|
+
symbol: sample[symbolIndex]?.value ?? currency,
|
|
108
|
+
symbolPosition: symbolIndex !== -1 && symbolIndex < integerIndex ? "prefix" : "suffix",
|
|
109
|
+
formatNumber: (major) => formatter
|
|
110
|
+
.formatToParts(major)
|
|
111
|
+
.filter((p) => p.type !== "currency")
|
|
112
|
+
.map((p) => p.value)
|
|
113
|
+
.join("")
|
|
114
|
+
.trim(),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// An invalid locale tag makes Intl throw a RangeError; without an
|
|
119
|
+
// ErrorBoundary that would take down the page over a display adornment.
|
|
120
|
+
return { symbol: "", symbolPosition: "suffix", formatNumber: String };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** Pinned footer below `sm`: always one fixed-height row, Back as icon on the left,
|
|
3
|
+
* everything else in a "…" popover (hidden while none of it is enabled), the primary
|
|
4
|
+
* action filling the rest. */
|
|
5
|
+
export declare function NarrowPinnedFooter({ nodes, overflowLabel, unsavedCount, unsavedStatus, testId, keyboardInset, }: {
|
|
6
|
+
readonly nodes: readonly ReactNode[];
|
|
7
|
+
readonly overflowLabel: string;
|
|
8
|
+
readonly unsavedCount: number | undefined;
|
|
9
|
+
readonly unsavedStatus: string | undefined;
|
|
10
|
+
readonly testId: string | undefined;
|
|
11
|
+
readonly keyboardInset: number;
|
|
12
|
+
}): ReactNode;
|