@cosmicdrift/kumiko-renderer 0.374.0 → 0.378.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 +10 -7
- 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/form-derivation-patch.d.ts +8 -0
- package/dist/components/form-derivation-patch.js +24 -0
- package/dist/components/form-screen-layout.d.ts +13 -0
- package/dist/components/form-screen-layout.js +13 -0
- 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 +6 -3
- package/dist/components/render-edit.d.ts +1 -1
- package/dist/components/render-edit.js +18 -3
- package/dist/components/render-field.d.ts +1 -1
- package/dist/components/render-field.js +12 -2
- 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/file-ids-of.d.ts +1 -0
- package/dist/file-ids-of.js +7 -0
- package/dist/flow-diagram/icons.d.ts +3 -0
- package/dist/flow-diagram/icons.js +14 -0
- package/dist/flow-diagram/index.d.ts +2 -0
- package/dist/flow-diagram/index.js +1 -0
- package/dist/flow-diagram/render.d.ts +3 -0
- package/dist/flow-diagram/render.js +234 -0
- package/dist/flow-diagram/types.d.ts +18 -0
- package/dist/flow-diagram/types.js +1 -0
- 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 +21 -0
- package/dist/headless/view-model/edit.js +512 -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 +6 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -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 +45 -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 +31 -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/file-value-list.d.ts +3 -0
- package/dist/web/primitives/file-value-list.js +22 -0
- package/dist/web/primitives/index.d.ts +34 -0
- package/dist/web/primitives/index.js +2023 -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/bounded-load-cache.d.ts +1 -0
- package/dist/web/widgets/bounded-load-cache.js +25 -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 +177 -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 +28 -0
- package/dist/web/widgets/index.js +31 -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/pdf-preview.d.ts +10 -0
- package/dist/web/widgets/pdf-preview.js +140 -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 +114 -7
- package/src/changes.json +92 -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 +405 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Translates a ZodError into the same FieldIssue shape the server emits via
|
|
2
|
+
// its own zod-bridge (packages/framework/src/errors/zod-bridge.ts). The two
|
|
3
|
+
// bridges stay in sync on purpose: a field failing zod on the client reads
|
|
4
|
+
// identically to the same field failing zod on the server, so the UI's
|
|
5
|
+
// error-display path doesn't branch on provenance.
|
|
6
|
+
//
|
|
7
|
+
// Duplicated here (not imported) so ui-core remains free of
|
|
8
|
+
// @cosmicdrift/kumiko-framework — this package has to bundle for browsers and Expo,
|
|
9
|
+
// where a server-oriented framework dep would balloon bundle size and drag
|
|
10
|
+
// in Node-only modules (postgres, drizzle, bullmq) even if only types are
|
|
11
|
+
// consumed.
|
|
12
|
+
//
|
|
13
|
+
// Keep this list in sync with the server-side mirror — Zod version bumps
|
|
14
|
+
// tend to introduce new param keys, and the server's classes.test.ts is
|
|
15
|
+
// what catches them. A value added there should be added here too.
|
|
16
|
+
const ISSUE_PARAM_KEYS = [
|
|
17
|
+
"minimum",
|
|
18
|
+
"maximum",
|
|
19
|
+
"expected",
|
|
20
|
+
"received",
|
|
21
|
+
"type",
|
|
22
|
+
"inclusive",
|
|
23
|
+
"exact",
|
|
24
|
+
"keys",
|
|
25
|
+
// Zod 4 additions
|
|
26
|
+
"format",
|
|
27
|
+
"divisor",
|
|
28
|
+
"values",
|
|
29
|
+
"pattern",
|
|
30
|
+
];
|
|
31
|
+
// Flat FieldIssue list — same shape as server-side ValidationError.fields.
|
|
32
|
+
// The form-controller groups this by `path` for its snapshot's errors map.
|
|
33
|
+
export function zodErrorToFieldIssues(error) {
|
|
34
|
+
return error.issues.map((issue) => {
|
|
35
|
+
const params = extractIssueParams(issue);
|
|
36
|
+
return {
|
|
37
|
+
// Empty-path issues (top-level object/array failures) use "(root)" so
|
|
38
|
+
// the form-controller has a stable key to show "form itself is
|
|
39
|
+
// malformed" errors under — matches what the server does.
|
|
40
|
+
path: issue.path.map(String).join(".") || "(root)",
|
|
41
|
+
code: issue.code,
|
|
42
|
+
i18nKey: resolveI18nKey(issue),
|
|
43
|
+
...(params && { params }),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// The issues that count for validity: hidden fields never block, and a
|
|
48
|
+
// scope narrows to the listed paths (an entry matches itself and everything
|
|
49
|
+
// below it, not siblings — #1898). `includeRoot` keeps "(root)" refine issues
|
|
50
|
+
// inside a scope (submit passes it, #1907). Shared by the controller's
|
|
51
|
+
// validate() and RenderEdit's `valid` dry-run so the two cannot drift.
|
|
52
|
+
export function relevantFieldIssues(issues, opts) {
|
|
53
|
+
const { hiddenFields, scope, includeRoot } = opts;
|
|
54
|
+
return issues.filter((issue) => {
|
|
55
|
+
const rootField = issue.path.split(".")[0] ?? "";
|
|
56
|
+
if (hiddenFields.has(rootField))
|
|
57
|
+
return false;
|
|
58
|
+
if (scope === undefined)
|
|
59
|
+
return true;
|
|
60
|
+
if (includeRoot === true && issue.path === "(root)")
|
|
61
|
+
return true;
|
|
62
|
+
return scope.some((entry) => issue.path === entry || issue.path.startsWith(`${entry}.`));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Same magic key both sides of a superRefine can set to opt out of the
|
|
66
|
+
// generic `errors.validation.custom` message — see form-schema.ts.
|
|
67
|
+
export const I18N_KEY_PARAM = "i18nKey";
|
|
68
|
+
// `code: "custom"` is zod's catch-all for every superRefine/refine check;
|
|
69
|
+
// left mechanical it'd collapse onto one generic key, so a superRefine can
|
|
70
|
+
// set `params[I18N_KEY_PARAM]` to override it. Keep in sync with the server
|
|
71
|
+
// mirror (packages/framework/src/errors/zod-bridge.ts).
|
|
72
|
+
function resolveI18nKey(issue) {
|
|
73
|
+
if (issue.code === "custom") {
|
|
74
|
+
const override = issue.params?.[I18N_KEY_PARAM];
|
|
75
|
+
if (typeof override === "string")
|
|
76
|
+
return override;
|
|
77
|
+
}
|
|
78
|
+
return `errors.validation.${issue.code}`;
|
|
79
|
+
}
|
|
80
|
+
function extractIssueParams(issue) {
|
|
81
|
+
const out = {};
|
|
82
|
+
const bag = issue; // @cast-boundary zod-issue
|
|
83
|
+
for (const key of ISSUE_PARAM_KEYS) {
|
|
84
|
+
if (bag[key] !== undefined)
|
|
85
|
+
out[key] = bag[key];
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
88
|
+
}
|
|
89
|
+
// Groups a flat FieldIssue list into the `errors` map carried by a
|
|
90
|
+
// FormSnapshot. A field with multiple issues (e.g. both "required" and
|
|
91
|
+
// "min") keeps both — UIs that only render the first can pick the first
|
|
92
|
+
// element, those that want a full list have it.
|
|
93
|
+
export function groupIssuesByPath(issues) {
|
|
94
|
+
const out = {};
|
|
95
|
+
for (const issue of issues) {
|
|
96
|
+
const bucket = out[issue.path];
|
|
97
|
+
if (bucket)
|
|
98
|
+
bucket.push(issue);
|
|
99
|
+
else
|
|
100
|
+
out[issue.path] = [issue];
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const DOCUMENT_FALLBACK_CURRENCY = "EUR";
|
|
2
|
+
export declare function formatDocumentMoney(value: unknown, tenantCurrency: string, locale: string): string;
|
|
3
|
+
export type DocumentDateOptions = {
|
|
4
|
+
/** Append the time of day. Only instants and offset date-times carry one; a
|
|
5
|
+
* date-only value prints just the date. */
|
|
6
|
+
readonly time?: boolean;
|
|
7
|
+
/** IANA zone an instant is shown in (default UTC). Offset date-times keep
|
|
8
|
+
* their own wall clock. */
|
|
9
|
+
readonly timeZone?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function formatDocumentDate(value: unknown, locale: string, options?: DocumentDateOptions): string;
|
|
12
|
+
export declare function formatDocumentPercent(value: unknown, locale: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
import { formatMoney, resolveSafeMoneyLocale } from "./money.js";
|
|
3
|
+
// Used when the tenant has no currency setting.
|
|
4
|
+
export const DOCUMENT_FALLBACK_CURRENCY = "EUR";
|
|
5
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
6
|
+
const DATE_TIME_WITH_OFFSET = /^\d{4}-\d{2}-\d{2}T.*[+-]\d{2}(:?\d{2})?$/;
|
|
7
|
+
function isPlainRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
// A bare number is minor units in the tenant currency; a `{ amount, currency }`
|
|
11
|
+
// record brings its own currency.
|
|
12
|
+
export function formatDocumentMoney(value, tenantCurrency, locale) {
|
|
13
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
14
|
+
return formatMoney(value, tenantCurrency, locale);
|
|
15
|
+
}
|
|
16
|
+
if (isPlainRecord(value)) {
|
|
17
|
+
const { amount, currency } = value;
|
|
18
|
+
if (typeof amount === "number" && Number.isFinite(amount) && typeof currency === "string") {
|
|
19
|
+
return formatMoney(amount, currency, locale);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return value === undefined || value === null ? "" : String(value);
|
|
23
|
+
}
|
|
24
|
+
function formatOffsetDateTime(value, locale, withTime) {
|
|
25
|
+
const wallClock = Temporal.PlainDateTime.from(value);
|
|
26
|
+
return withTime
|
|
27
|
+
? wallClock.toLocaleString(locale, { dateStyle: "long", timeStyle: "short" })
|
|
28
|
+
: wallClock.toPlainDate().toLocaleString(locale, { dateStyle: "long" });
|
|
29
|
+
}
|
|
30
|
+
function instantFormatOptions(options) {
|
|
31
|
+
if (options.time !== true)
|
|
32
|
+
return { dateStyle: "long", timeZone: "UTC" };
|
|
33
|
+
return { dateStyle: "long", timeStyle: "short", timeZone: options.timeZone ?? "UTC" };
|
|
34
|
+
}
|
|
35
|
+
// The long date a rendered mail or PDF prints for a `date` placeholder; the editor's
|
|
36
|
+
// sample values use it too, so both show the same text.
|
|
37
|
+
export function formatDocumentDate(value, locale, options = {}) {
|
|
38
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
39
|
+
return value === undefined || value === null ? "" : String(value);
|
|
40
|
+
}
|
|
41
|
+
const safeLocale = resolveSafeMoneyLocale(locale);
|
|
42
|
+
try {
|
|
43
|
+
// A date-only string has no zone; going through an instant would show the
|
|
44
|
+
// previous day west of UTC.
|
|
45
|
+
if (typeof value === "string" && DATE_ONLY.test(value)) {
|
|
46
|
+
return Temporal.PlainDate.from(value).toLocaleString(safeLocale, { dateStyle: "long" });
|
|
47
|
+
}
|
|
48
|
+
// An explicit offset names the sender's zone: the calendar date is the wall-clock
|
|
49
|
+
// date of the string, not the date at UTC.
|
|
50
|
+
if (typeof value === "string" && DATE_TIME_WITH_OFFSET.test(value)) {
|
|
51
|
+
return formatOffsetDateTime(value, safeLocale, options.time === true);
|
|
52
|
+
}
|
|
53
|
+
const instant = typeof value === "number"
|
|
54
|
+
? Temporal.Instant.fromEpochMilliseconds(value)
|
|
55
|
+
: Temporal.Instant.from(value);
|
|
56
|
+
return instant.toLocaleString(safeLocale, instantFormatOptions(options));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function formatDocumentPercent(value, locale) {
|
|
63
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
64
|
+
return new Intl.NumberFormat(resolveSafeMoneyLocale(locale), {
|
|
65
|
+
style: "percent",
|
|
66
|
+
maximumFractionDigits: 2,
|
|
67
|
+
}).format(value / 100);
|
|
68
|
+
}
|
|
69
|
+
return value === undefined || value === null ? "" : String(value);
|
|
70
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function escapeHtml(s: string): string;
|
|
2
|
+
export declare function escapeHtmlAttr(s: string): string;
|
|
3
|
+
export declare function escapeXml(s: string): string;
|
|
4
|
+
export declare function stripControlChars(value: string): string;
|
|
5
|
+
export declare function isSafeHref(href: string): boolean;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export function escapeHtml(s) {
|
|
2
|
+
return s
|
|
3
|
+
.replace(/&/g, "&")
|
|
4
|
+
.replace(/</g, "<")
|
|
5
|
+
.replace(/>/g, ">")
|
|
6
|
+
.replace(/"/g, """);
|
|
7
|
+
}
|
|
8
|
+
// Superset of escapeHtml for attribute contexts: additionally escapes ' so
|
|
9
|
+
// single-quoted attributes cannot be broken out of.
|
|
10
|
+
export function escapeHtmlAttr(s) {
|
|
11
|
+
return escapeHtml(s).replace(/'/g, "'");
|
|
12
|
+
}
|
|
13
|
+
// Shared API for app repos (publicstatus badge-SVG renders XML by hand) —
|
|
14
|
+
// no in-repo consumer yet, deliberately exported for cross-repo dedup.
|
|
15
|
+
export function escapeXml(s) {
|
|
16
|
+
return s
|
|
17
|
+
.replace(/&/g, "&")
|
|
18
|
+
.replace(/</g, "<")
|
|
19
|
+
.replace(/>/g, ">")
|
|
20
|
+
.replace(/"/g, """)
|
|
21
|
+
.replace(/'/g, "'");
|
|
22
|
+
}
|
|
23
|
+
// Strips ASCII control chars + space (codepoints 0x00-0x20 and 0x7f) — the
|
|
24
|
+
// same characters browsers strip before scheme detection. `.trim()` alone
|
|
25
|
+
// only removes leading/trailing whitespace, so a scheme like
|
|
26
|
+
// "java\tscript:" (tab embedded mid-scheme) survives to isSafeHref's regex
|
|
27
|
+
// check below, breaks its character class before the ":", falls through to
|
|
28
|
+
// `return true`, and the browser then normalizes it back to "javascript:"
|
|
29
|
+
// and executes it. Written as a char-code filter rather than a regex range
|
|
30
|
+
// to avoid embedding literal control characters in source.
|
|
31
|
+
export function stripControlChars(value) {
|
|
32
|
+
let result = "";
|
|
33
|
+
for (const ch of value) {
|
|
34
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
35
|
+
if (code > 0x20 && code !== 0x7f)
|
|
36
|
+
result += ch;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
// http(s)/mailto or scheme-less (relative/anchor) allowed; javascript:,
|
|
41
|
+
// data:, vbscript:, etc. rejected. Shared between renderer/web's Link
|
|
42
|
+
// primitive and page-render's server-side markdown renderer (both take
|
|
43
|
+
// untrusted tenant-authored hrefs) — no sanitizer dependency needed for a
|
|
44
|
+
// four-line regex check.
|
|
45
|
+
// Browsers decode HTML character references (:, :, :, 	,
|
|
46
|
+
// 	, ...) while parsing the href attribute, before the URL parser ever
|
|
47
|
+
// sees the value — so "javascript:alert(1)" and "java	script:"
|
|
48
|
+
// both normalize to an executable javascript: URL at click time even though
|
|
49
|
+
// neither contains a literal ":" for the regex below to catch. Decode
|
|
50
|
+
// (not delete) so the scheme check sees what the browser will execute —
|
|
51
|
+
// deleting ":" would leave no colon at all, which reads as a safe
|
|
52
|
+
// scheme-less href, exactly backwards.
|
|
53
|
+
const NAMED_HTML_ENTITY_CHARS = {
|
|
54
|
+
colon: ":",
|
|
55
|
+
tab: "\t",
|
|
56
|
+
newline: "\n",
|
|
57
|
+
semi: ";",
|
|
58
|
+
amp: "&",
|
|
59
|
+
};
|
|
60
|
+
function decodeHtmlEntities(value) {
|
|
61
|
+
return value.replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z][a-z0-9]*);/gi, (match, entity) => {
|
|
62
|
+
if (entity[0] === "#") {
|
|
63
|
+
const isHex = entity[1]?.toLowerCase() === "x";
|
|
64
|
+
const code = Number.parseInt(isHex ? entity.slice(2) : entity.slice(1), isHex ? 16 : 10);
|
|
65
|
+
return Number.isFinite(code) ? String.fromCodePoint(code) : match;
|
|
66
|
+
}
|
|
67
|
+
return NAMED_HTML_ENTITY_CHARS[entity.toLowerCase()] ?? match;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function isSafeHref(href) {
|
|
71
|
+
const trimmed = stripControlChars(decodeHtmlEntities(href)).toLowerCase();
|
|
72
|
+
if (!/^[a-z][a-z0-9+.-]*:/.test(trimmed))
|
|
73
|
+
return true;
|
|
74
|
+
return /^(?:https?|mailto):/.test(trimmed);
|
|
75
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class RawHtml {
|
|
2
|
+
readonly html: string;
|
|
3
|
+
constructor(html: string);
|
|
4
|
+
toString(): string;
|
|
5
|
+
}
|
|
6
|
+
/** Marks already escaped or trusted markup for html`...`. */
|
|
7
|
+
export declare function raw(html: string): RawHtml;
|
|
8
|
+
export type HtmlValue = string | number | boolean | null | undefined | RawHtml | ReadonlyArray<HtmlValue>;
|
|
9
|
+
export declare function html(strings: TemplateStringsArray, ...values: ReadonlyArray<HtmlValue>): RawHtml;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// html`...` — tagged template that HTML-escapes every interpolation automatically.
|
|
2
|
+
// Makes escaping structural instead of a per-call-site convention: forgetting
|
|
3
|
+
// it is impossible, deliberately raw HTML needs an explicit raw(). The
|
|
4
|
+
// HTML-escape guard (kumiko-guards) accepts html`...` as safe.
|
|
5
|
+
import { escapeHtml } from "./escape.js";
|
|
6
|
+
export class RawHtml {
|
|
7
|
+
html;
|
|
8
|
+
constructor(html) {
|
|
9
|
+
this.html = html;
|
|
10
|
+
}
|
|
11
|
+
toString() {
|
|
12
|
+
return this.html;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Marks already escaped or trusted markup for html`...`. */
|
|
16
|
+
export function raw(html) {
|
|
17
|
+
return new RawHtml(html);
|
|
18
|
+
}
|
|
19
|
+
function renderValue(value) {
|
|
20
|
+
if (value === null || value === undefined)
|
|
21
|
+
return "";
|
|
22
|
+
if (value instanceof RawHtml)
|
|
23
|
+
return value.html;
|
|
24
|
+
if (Array.isArray(value))
|
|
25
|
+
return value.map(renderValue).join("");
|
|
26
|
+
if (typeof value === "string")
|
|
27
|
+
return escapeHtml(value);
|
|
28
|
+
return String(value);
|
|
29
|
+
}
|
|
30
|
+
// Returns RawHtml so fragments can be nested without double escaping:
|
|
31
|
+
// `html`<div>${item}</div>`` inside an outer html`...` passes through
|
|
32
|
+
// unchanged. toString() yields the finished markup.
|
|
33
|
+
export function html(strings, ...values) {
|
|
34
|
+
let out = strings[0] ?? "";
|
|
35
|
+
values.forEach((value, i) => {
|
|
36
|
+
out += renderValue(value) + (strings[i + 1] ?? "");
|
|
37
|
+
});
|
|
38
|
+
return new RawHtml(out);
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
export declare const EMPTY_VALUE_PLACEHOLDER = "\u2014";
|
|
3
|
+
export declare function toInstant(raw: string): Temporal.Instant;
|
|
4
|
+
/** Exported for UnitKey parity tests against @cosmicdrift/kumiko-types/screen. */
|
|
5
|
+
export declare const UNIT_FORMAT_KEYS: readonly string[];
|
|
6
|
+
export { DOCUMENT_FALLBACK_CURRENCY, type DocumentDateOptions, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, } from "./document-values.ts";
|
|
7
|
+
export { escapeHtml, escapeHtmlAttr, escapeXml, isSafeHref, stripControlChars } from "./escape.ts";
|
|
8
|
+
export { type HtmlValue, html, RawHtml, raw } from "./html-template.ts";
|
|
9
|
+
export { currencyDecimals, formatMoney, resolveSafeMoneyLocale } from "./money.ts";
|
|
10
|
+
export declare function applyFormatSpec(spec: {
|
|
11
|
+
format: string;
|
|
12
|
+
} & Record<string, unknown>, value: unknown, translate?: (key: string) => string): string;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Pure format utilities — no web or platform dependencies.
|
|
2
|
+
// Shared between renderer/web, renderer-native, and server-side tests.
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
5
|
+
export const EMPTY_VALUE_PLACEHOLDER = "—";
|
|
6
|
+
function toPlainDate(raw) {
|
|
7
|
+
try {
|
|
8
|
+
return Temporal.PlainDate.from(raw);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// "date"-typed field stored as a full instant (day-boundary timestamp) —
|
|
12
|
+
// take the calendar date in the local zone rather than fail.
|
|
13
|
+
return toInstant(raw).toZonedDateTimeISO(Temporal.Now.timeZoneId()).toPlainDate();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Temporal.Instant.from is stricter than the `new Date(raw)` this replaced:
|
|
17
|
+
// it requires a UTC designator/offset (Z/+hh:mm). Timestamps without one
|
|
18
|
+
// (e.g. "2026-07-18T12:00:00", still valid input to `new Date`) throw here
|
|
19
|
+
// instead of parsing — fall back to reading them as a local wall-clock time,
|
|
20
|
+
// same posture as toPlainDate's own fallback above.
|
|
21
|
+
export function toInstant(raw) {
|
|
22
|
+
try {
|
|
23
|
+
return Temporal.Instant.from(raw);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return Temporal.PlainDateTime.from(raw).toZonedDateTime(Temporal.Now.timeZoneId()).toInstant();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function formatDateCell(value, type, opts) {
|
|
30
|
+
try {
|
|
31
|
+
const raw = typeof value === "string" ? value : String(value);
|
|
32
|
+
const locale = opts?.locale;
|
|
33
|
+
if (opts?.dateStyle || opts?.timeStyle) {
|
|
34
|
+
if (type === "date") {
|
|
35
|
+
// ponytail: timeStyle is ignored here on purpose — a PlainDate has no
|
|
36
|
+
// time component to format, so a timeStyle-only "date" field falls
|
|
37
|
+
// back to PlainDate's default dateStyle instead of rendering a time.
|
|
38
|
+
return toPlainDate(raw).toLocaleString(locale, {
|
|
39
|
+
dateStyle: opts.dateStyle,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return toInstant(raw).toLocaleString(locale, {
|
|
43
|
+
dateStyle: opts.dateStyle,
|
|
44
|
+
timeStyle: opts.timeStyle,
|
|
45
|
+
timeZone: opts.timeZone,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (type === "date") {
|
|
49
|
+
return toPlainDate(raw).toLocaleString(locale, {
|
|
50
|
+
year: "numeric",
|
|
51
|
+
month: "2-digit",
|
|
52
|
+
day: "2-digit",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return toInstant(raw).toLocaleString(locale, {
|
|
56
|
+
year: "numeric",
|
|
57
|
+
month: "short",
|
|
58
|
+
day: "numeric",
|
|
59
|
+
hour: "2-digit",
|
|
60
|
+
minute: "2-digit",
|
|
61
|
+
timeZone: opts?.timeZone,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return String(value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// No `locale` fallback to a browser API here (headless has none) — passing
|
|
69
|
+
// `undefined` to Intl.NumberFormat resolves the runtime's default locale,
|
|
70
|
+
// the same value a browser's navigator.language-based guess would produce.
|
|
71
|
+
function formatNumberCell(value, locale, grouping = true) {
|
|
72
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
73
|
+
return String(value);
|
|
74
|
+
try {
|
|
75
|
+
return new Intl.NumberFormat(locale, { useGrouping: grouping }).format(value);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Malformed BCP-47 locale tag — mirrors formatDateCell's fallback above.
|
|
79
|
+
return String(value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Two-tier unit table for `{ format: "unit" }`. Keys mirror
|
|
83
|
+
// @cosmicdrift/kumiko-types' UnitKey (kept as a local literal union instead
|
|
84
|
+
// of an import — headless has no build-time dependency on kumiko-types
|
|
85
|
+
// today, and applyFormatSpec's spec param is already an untyped
|
|
86
|
+
// Record<string, unknown>, cast per-case like every other format below).
|
|
87
|
+
const UNIT_INTL_IDS = {
|
|
88
|
+
km: "kilometer",
|
|
89
|
+
m: "meter",
|
|
90
|
+
kg: "kilogram",
|
|
91
|
+
percent: "percent",
|
|
92
|
+
mi: "mile",
|
|
93
|
+
};
|
|
94
|
+
// "m2" (square meter) has no ECMA-402 sanctioned unit — Intl.NumberFormat
|
|
95
|
+
// throws RangeError for "square-meter". Locale-format the number and append
|
|
96
|
+
// the literal suffix instead of going through `style: "unit"`.
|
|
97
|
+
const UNIT_SUFFIXES = { m2: "m²" };
|
|
98
|
+
function isIntlUnit(unit) {
|
|
99
|
+
return Object.hasOwn(UNIT_INTL_IDS, unit);
|
|
100
|
+
}
|
|
101
|
+
function isSuffixUnit(unit) {
|
|
102
|
+
return Object.hasOwn(UNIT_SUFFIXES, unit);
|
|
103
|
+
}
|
|
104
|
+
function formatUnitCell(value, unit, locale, unitDisplay) {
|
|
105
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
106
|
+
if (unit === undefined || !Number.isFinite(n))
|
|
107
|
+
return String(value);
|
|
108
|
+
if (isIntlUnit(unit)) {
|
|
109
|
+
try {
|
|
110
|
+
return new Intl.NumberFormat(locale, {
|
|
111
|
+
style: "unit",
|
|
112
|
+
unit: UNIT_INTL_IDS[unit],
|
|
113
|
+
unitDisplay,
|
|
114
|
+
}).format(n);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Malformed BCP-47 locale tag — mirrors formatNumberCell's fallback below.
|
|
118
|
+
return formatNumberCell(n, locale);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (isSuffixUnit(unit)) {
|
|
122
|
+
return `${formatNumberCell(n, locale)} ${UNIT_SUFFIXES[unit]}`;
|
|
123
|
+
}
|
|
124
|
+
return String(value);
|
|
125
|
+
}
|
|
126
|
+
/** Exported for UnitKey parity tests against @cosmicdrift/kumiko-types/screen. */
|
|
127
|
+
export const UNIT_FORMAT_KEYS = [
|
|
128
|
+
...Object.keys(UNIT_INTL_IDS),
|
|
129
|
+
...Object.keys(UNIT_SUFFIXES),
|
|
130
|
+
];
|
|
131
|
+
export { DOCUMENT_FALLBACK_CURRENCY, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, } from "./document-values.js";
|
|
132
|
+
export { escapeHtml, escapeHtmlAttr, escapeXml, isSafeHref, stripControlChars } from "./escape.js";
|
|
133
|
+
export { html, RawHtml, raw } from "./html-template.js";
|
|
134
|
+
export { currencyDecimals, formatMoney, resolveSafeMoneyLocale } from "./money.js";
|
|
135
|
+
// JSON.stringify throws on BigInt and circular values; a cell must degrade to text instead of crashing the screen.
|
|
136
|
+
function stringifyJsonOrString(value, indent) {
|
|
137
|
+
try {
|
|
138
|
+
return JSON.stringify(value, null, indent) ?? String(value);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
try {
|
|
142
|
+
return String(value);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return "[unserializable value]";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const INTL_DATE_STYLES = ["full", "long", "medium", "short"];
|
|
150
|
+
const INTL_UNIT_DISPLAYS = ["short", "long", "narrow"];
|
|
151
|
+
function stringSpec(spec, key) {
|
|
152
|
+
const v = spec[key];
|
|
153
|
+
return typeof v === "string" ? v : undefined;
|
|
154
|
+
}
|
|
155
|
+
function booleanSpec(spec, key) {
|
|
156
|
+
const v = spec[key];
|
|
157
|
+
return typeof v === "boolean" ? v : undefined;
|
|
158
|
+
}
|
|
159
|
+
function dateStyleSpec(spec, key) {
|
|
160
|
+
const v = spec[key];
|
|
161
|
+
return isOneOf(INTL_DATE_STYLES, v) ? v : undefined;
|
|
162
|
+
}
|
|
163
|
+
function unitDisplaySpec(spec) {
|
|
164
|
+
const v = spec["unitDisplay"];
|
|
165
|
+
return isOneOf(INTL_UNIT_DISPLAYS, v) ? v : "short";
|
|
166
|
+
}
|
|
167
|
+
export function applyFormatSpec(spec, value, translate) {
|
|
168
|
+
const isEmpty = value === null || value === undefined || value === "";
|
|
169
|
+
// priority renders its emptyLabel for empty values — every other format
|
|
170
|
+
// collapses empty to "".
|
|
171
|
+
if (isEmpty && spec.format !== "priority")
|
|
172
|
+
return "";
|
|
173
|
+
switch (spec.format) {
|
|
174
|
+
case "timestamp":
|
|
175
|
+
case "date":
|
|
176
|
+
return formatDateCell(value, spec.format, {
|
|
177
|
+
locale: stringSpec(spec, "locale"),
|
|
178
|
+
dateStyle: dateStyleSpec(spec, "dateStyle"),
|
|
179
|
+
timeStyle: spec.format === "timestamp" ? dateStyleSpec(spec, "timeStyle") : undefined,
|
|
180
|
+
timeZone: spec.format === "timestamp" ? stringSpec(spec, "timeZone") : undefined,
|
|
181
|
+
});
|
|
182
|
+
case "boolean": {
|
|
183
|
+
if (value === true) {
|
|
184
|
+
const trueLabel = stringSpec(spec, "trueLabel");
|
|
185
|
+
return trueLabel === undefined ? "✓" : (translate?.(trueLabel) ?? trueLabel);
|
|
186
|
+
}
|
|
187
|
+
if (value === false) {
|
|
188
|
+
const falseLabel = stringSpec(spec, "falseLabel");
|
|
189
|
+
return falseLabel === undefined ? "" : (translate?.(falseLabel) ?? falseLabel);
|
|
190
|
+
}
|
|
191
|
+
return "";
|
|
192
|
+
}
|
|
193
|
+
case "currency": {
|
|
194
|
+
const sym = stringSpec(spec, "symbol") ?? "";
|
|
195
|
+
return sym.length > 0 ? `${value} ${sym}` : String(value);
|
|
196
|
+
}
|
|
197
|
+
case "number":
|
|
198
|
+
case "decimal":
|
|
199
|
+
case "bigInt":
|
|
200
|
+
return formatNumberCell(value, stringSpec(spec, "locale"), booleanSpec(spec, "grouping") ?? true);
|
|
201
|
+
case "unit":
|
|
202
|
+
return formatUnitCell(value, stringSpec(spec, "unit"), stringSpec(spec, "locale"), unitDisplaySpec(spec));
|
|
203
|
+
case "priority": {
|
|
204
|
+
const emptyLabel = stringSpec(spec, "emptyLabel") ?? EMPTY_VALUE_PLACEHOLDER;
|
|
205
|
+
const prefix = stringSpec(spec, "prefix") ?? "";
|
|
206
|
+
if (isEmpty || value === 0)
|
|
207
|
+
return emptyLabel;
|
|
208
|
+
return `${prefix}${value}`;
|
|
209
|
+
}
|
|
210
|
+
case "enumOption": {
|
|
211
|
+
const keyPrefix = stringSpec(spec, "keyPrefix");
|
|
212
|
+
const labelFor = (item) => {
|
|
213
|
+
const raw = typeof item === "string" ? item : String(item);
|
|
214
|
+
if (keyPrefix === undefined || translate === undefined)
|
|
215
|
+
return raw;
|
|
216
|
+
const key = `${keyPrefix}${raw}`;
|
|
217
|
+
const translated = translate(key);
|
|
218
|
+
return translated === key ? raw : translated;
|
|
219
|
+
};
|
|
220
|
+
// multiSelect-style columns hold an array of option values
|
|
221
|
+
return Array.isArray(value) ? value.map(labelFor).join(", ") : labelFor(value);
|
|
222
|
+
}
|
|
223
|
+
// Thumbnails and file links are drawn by the web list cell; this is the
|
|
224
|
+
// text fallback for every other surface.
|
|
225
|
+
case "image":
|
|
226
|
+
case "file":
|
|
227
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
228
|
+
case "json":
|
|
229
|
+
return stringifyJsonOrString(value, typeof spec["indent"] === "number" ? spec["indent"] : 2);
|
|
230
|
+
default:
|
|
231
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
232
|
+
// biome-ignore lint/suspicious/noConsole: dev-only warning
|
|
233
|
+
console.warn(`[kumiko] applyFormatSpec: unknown format key "${spec.format}" — registered via FieldFormatRegistry module augmentation?`);
|
|
234
|
+
}
|
|
235
|
+
return typeof value === "string" ? value : String(value);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function currencyDecimals(code: string): number;
|
|
2
|
+
export declare function resolveSafeMoneyLocale(locale: string | undefined): string;
|
|
3
|
+
export declare function formatMoney(amountMinor: number, currency: string, locale?: string, fractionDigits?: number): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Currency decimal places — covers the world's zero- and three-decimal
|
|
2
|
+
// outliers, defaults to 2 for everything else. Shared between renderer/web's
|
|
3
|
+
// MoneyInput (minor-unit display factor) and RenderField's minor/major-unit
|
|
4
|
+
// conversion (kumiko-framework#1923) — a single source keeps both sides
|
|
5
|
+
// scaling the same currency the same way.
|
|
6
|
+
export function currencyDecimals(code) {
|
|
7
|
+
if (code === "JPY" || code === "KRW" || code === "VND" || code === "ISK")
|
|
8
|
+
return 0;
|
|
9
|
+
if (code === "BHD" || code === "JOD" || code === "KWD" || code === "OMR" || code === "TND")
|
|
10
|
+
return 3;
|
|
11
|
+
return 2;
|
|
12
|
+
}
|
|
13
|
+
const FALLBACK_MONEY_LOCALE = "en-US";
|
|
14
|
+
// An invalid tag makes Intl constructors throw a RangeError; canonicalizing up
|
|
15
|
+
// front keeps server renders and client cells safe. No navigator lookup, so it
|
|
16
|
+
// behaves the same on the server.
|
|
17
|
+
export function resolveSafeMoneyLocale(locale) {
|
|
18
|
+
try {
|
|
19
|
+
return Intl.getCanonicalLocales(locale ?? FALLBACK_MONEY_LOCALE)[0] ?? FALLBACK_MONEY_LOCALE;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return FALLBACK_MONEY_LOCALE;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// currency is validated against the 3-letter-alpha shape Intl.NumberFormat
|
|
26
|
+
// requires: a non-conforming value (e.g. from a JSONB custom field) would throw
|
|
27
|
+
// a RangeError and take down the whole render instead of just this value.
|
|
28
|
+
export function formatMoney(amountMinor, currency, locale, fractionDigits) {
|
|
29
|
+
if (!/^[A-Za-z]{3}$/.test(currency))
|
|
30
|
+
return String(amountMinor);
|
|
31
|
+
const decimals = currencyDecimals(currency);
|
|
32
|
+
const displayDigits = fractionDigits ?? decimals;
|
|
33
|
+
return new Intl.NumberFormat(resolveSafeMoneyLocale(locale), {
|
|
34
|
+
style: "currency",
|
|
35
|
+
currency,
|
|
36
|
+
minimumFractionDigits: displayDigits,
|
|
37
|
+
maximumFractionDigits: displayDigits,
|
|
38
|
+
}).format(amountMinor / 10 ** decimals);
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type { ParsedRefTarget } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
export { parseRefTarget } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
3
|
+
export type { AssetResolution, AssetResolveContext, AssetResolver, BadgeProps, ButtonProps, CardProps, DatePickerProps, IconProps, LocaleResolver, ModalProps, NumberInputProps, PrimitiveCommonProps, PrimitivesContract, SelectOption, SelectProps, TextInputProps, ToastIntent, ToastProps, ToggleProps, } from "./contracts/index.ts";
|
|
4
|
+
export type { BatchResult, Command, Dispatcher, DispatcherError, DispatcherStatus, FieldIssue, PendingFile, PendingWrite, QueryOpts, QueryResult, StreamOpts, WriteOpts, WriteResult, } from "./dispatcher/index.ts";
|
|
5
|
+
export { StreamFrame, type StreamFrameEvent } from "./dispatcher/index.ts";
|
|
6
|
+
export type { FieldConditionPredicate, FieldConditions, FieldConditionValue, FieldState, FormController, FormControllerOptions, FormSnapshot, FormValues, SubmitConfig, SubmitPayloadMode, SubmitResult, } from "./form/index.ts";
|
|
7
|
+
export { createFormController, groupIssuesByPath, I18N_KEY_PARAM, relevantFieldIssues, zodErrorToFieldIssues, } from "./form/index.ts";
|
|
8
|
+
export { applyFormatSpec, currencyDecimals, DOCUMENT_FALLBACK_CURRENCY, EMPTY_VALUE_PLACEHOLDER, escapeHtml, escapeHtmlAttr, escapeXml, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, formatMoney, type HtmlValue, html, isSafeHref, RawHtml, raw, resolveSafeMoneyLocale, stripControlChars, toInstant, } from "./format/index.ts";
|
|
9
|
+
export type { NavDefinition, NavNode, NavRegistrySlice, NavTree, ResolveNavigationOptions, } from "./nav/index.ts";
|
|
10
|
+
export { resolveNavigation } from "./nav/index.ts";
|
|
11
|
+
export type { Store, WritableStore } from "./store/index.ts";
|
|
12
|
+
export { createStore, shallowEqual } from "./store/index.ts";
|
|
13
|
+
export type { CellLink, CellLinkItem, ComputeEditViewModelInput, ComputeListViewModelInput, DerivedCellRoundingTarget, EditExtensionSectionViewModel, EditFieldSpec, EditFieldsSectionViewModel, EditFieldViewModel, EditFormMode, EditRelatedListSectionViewModel, EditSectionSpec, EditSectionViewModel, EditViewModel, EditWriteFormSectionViewModel, EmbeddedDerivedOp, EmbeddedListCellViewModel, EmbeddedListIssueGroups, FieldConditionCtx, FieldRenderer, ListColumnFileDisplay, ListColumnSpec, ListColumnViewModel, ListRowViewModel, ListViewModel, RuntimeCellLink, RuntimeCellLinkItems, RuntimeRenderer, ScreenSlots, Translate, } from "./view-model/index.ts";
|
|
14
|
+
export { buildOptionLabels, computeDerivedCellValue, computeEditViewModel, computeListViewModel, computeRelatedListSectionViewModel, embeddedCellLabelKey, embeddedCellOptionLabelKey, fieldLabelKey, fieldOptionLabelKey, fieldOptionLabelKeyPrefix, groupEmbeddedListIssues, roundDerivedCellValue, sumEmbeddedListColumn, } from "./view-model/index.ts";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Re-Export aus framework/ui-types damit Renderer-Code denselben
|
|
2
|
+
// Parser nutzt wie der Server-Boot-Validator (Cross-Feature-Refs).
|
|
3
|
+
export { parseRefTarget } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
4
|
+
export { StreamFrame } from "./dispatcher/index.js";
|
|
5
|
+
export { createFormController, groupIssuesByPath, I18N_KEY_PARAM, relevantFieldIssues, zodErrorToFieldIssues, } from "./form/index.js";
|
|
6
|
+
export { applyFormatSpec, currencyDecimals, DOCUMENT_FALLBACK_CURRENCY, EMPTY_VALUE_PLACEHOLDER, escapeHtml, escapeHtmlAttr, escapeXml, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, formatMoney, html, isSafeHref, RawHtml, raw, resolveSafeMoneyLocale, stripControlChars, toInstant, } from "./format/index.js";
|
|
7
|
+
export { resolveNavigation } from "./nav/index.js";
|
|
8
|
+
export { createStore, shallowEqual } from "./store/index.js";
|
|
9
|
+
export { buildOptionLabels, computeDerivedCellValue, computeEditViewModel, computeListViewModel, computeRelatedListSectionViewModel, embeddedCellLabelKey, embeddedCellOptionLabelKey, fieldLabelKey, fieldOptionLabelKey, fieldOptionLabelKeyPrefix, groupEmbeddedListIssues, roundDerivedCellValue, sumEmbeddedListColumn, } from "./view-model/index.js";
|