@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,13 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { usePrimitives, useTranslation } from "../../index.js";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
export function PlanCard({ title, description, price, features, current = false, cta, ctaLink, secondaryAction, variant = "default", testId, }) {
|
|
5
|
+
const { Card, Button, Link } = usePrimitives();
|
|
6
|
+
const t = useTranslation();
|
|
7
|
+
const compact = variant === "compact";
|
|
8
|
+
const footer = cta !== undefined || ctaLink !== undefined || secondaryAction !== undefined ? (_jsxs("div", { className: "flex w-full flex-col gap-2", children: [ctaLink !== undefined && (_jsx(Link, { href: ctaLink.href, variant: "button", target: ctaLink.target, className: "w-full justify-center", testId: testId !== undefined ? `${testId}-cta` : "plan-card-cta", children: ctaLink.label })), ctaLink === undefined && cta !== undefined && (_jsx(Button, { width: "full", disabled: cta.disabled, onClick: cta.onClick, testId: testId !== undefined ? `${testId}-cta` : "plan-card-cta", children: cta.label })), secondaryAction !== undefined && (_jsx(Button, { variant: "secondary", width: "full", disabled: secondaryAction.disabled, onClick: secondaryAction.onClick, children: secondaryAction.label }))] })) : undefined;
|
|
9
|
+
return (_jsxs(Card, { testId: testId ?? "plan-card", className: cn(current && "border-primary"), slots: { footer }, children: [_jsxs("div", { className: cn("flex items-start justify-between gap-2", compact && "items-center"), children: [_jsx("h2", { className: cn("font-semibold", compact ? "text-base" : "text-lg"), children: title }), current && (_jsx("span", { "data-testid": testId !== undefined ? `${testId}-current` : "plan-card-current", className: "inline-block rounded-xl bg-primary/10 px-2.5 py-1 text-xs font-semibold text-primary", children: t("kumiko.planCard.current") }))] }), description !== undefined && !compact && (_jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })), price !== undefined && (_jsx("div", { className: cn(compact ? "mt-1" : "mt-1 mb-3"), children: price === null ? (_jsx("span", { "data-testid": testId !== undefined ? `${testId}-price-unavailable` : "plan-card-price-unavailable", className: "text-sm text-muted-foreground", children: t("kumiko.planCard.priceUnavailable") })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-2xl font-bold", children: price.amount }), price.period !== undefined && (_jsx("span", { className: "ml-1 text-sm text-muted-foreground", children: price.period }))] })) })), !compact && features !== undefined && features.length > 0 && (_jsx("ul", { className: "space-y-1 text-sm", children: features.map((feature, index) => (_jsxs("li", { className: "flex gap-2", children: [_jsx("span", { "aria-hidden": "true", children: "\u2713" }), _jsx("span", { children: feature })] }, index))) }))] }));
|
|
10
|
+
}
|
|
11
|
+
export function PlanGrid({ children, testId }) {
|
|
12
|
+
return (_jsx("div", { "data-testid": testId, className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: children }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ProgressTone } from "../../index.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Progress bar, `value` 0..1 (clamped).
|
|
5
|
+
*
|
|
6
|
+
* The track lives in its own inner element so a parent that pads or
|
|
7
|
+
* stretches its direct children (e.g. RenderEdit's wizard chrome) never
|
|
8
|
+
* touches the track's own `h-2` — a caller's `className` reaches the
|
|
9
|
+
* outer wrapper, not the height-bearing track.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ProgressBar({ value, tone, size, ariaLabel, className, testId, }: {
|
|
12
|
+
readonly value: number;
|
|
13
|
+
readonly tone?: ProgressTone;
|
|
14
|
+
/** `"thin"` is a 4px bar on the border-token track for quiet inline progress. */
|
|
15
|
+
readonly size?: "default" | "thin";
|
|
16
|
+
readonly ariaLabel?: string;
|
|
17
|
+
readonly className?: string;
|
|
18
|
+
readonly testId?: string;
|
|
19
|
+
}): ReactNode;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
// Fully spelled out (no interpolation) — Tailwind's scanner only picks up
|
|
4
|
+
// literal class strings, not `bg-status-${tone}`.
|
|
5
|
+
const FILL_TONE = {
|
|
6
|
+
default: "bg-primary",
|
|
7
|
+
success: "bg-status-ok",
|
|
8
|
+
warn: "bg-status-warn",
|
|
9
|
+
danger: "bg-status-critical",
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Progress bar, `value` 0..1 (clamped).
|
|
13
|
+
*
|
|
14
|
+
* The track lives in its own inner element so a parent that pads or
|
|
15
|
+
* stretches its direct children (e.g. RenderEdit's wizard chrome) never
|
|
16
|
+
* touches the track's own `h-2` — a caller's `className` reaches the
|
|
17
|
+
* outer wrapper, not the height-bearing track.
|
|
18
|
+
*/
|
|
19
|
+
export function ProgressBar({ value, tone = "default", size = "default", ariaLabel, className, testId, }) {
|
|
20
|
+
// `value` is typically `done / total`; total:0 produces NaN, which would
|
|
21
|
+
// otherwise leak into `width: "NaN%"` and an invalid aria-valuenow.
|
|
22
|
+
const pct = Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : 0;
|
|
23
|
+
return (_jsx("div", { className: cn("w-full", className), children: _jsx("div", { "data-testid": testId, role: "progressbar", "aria-valuenow": Math.round(pct * 100), "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": ariaLabel, className: cn("relative w-full overflow-hidden rounded-full", size === "thin" ? "h-1 bg-border" : "h-2 bg-muted"), children: _jsx("div", { className: cn("absolute inset-y-0 left-0 rounded-full", FILL_TONE[tone]), style: { width: `${pct * 100}%` } }) }) }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type ProgressListRow = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly value: string;
|
|
6
|
+
/** null renders no bar (nothing to measure against, e.g. an unlimited cap). */
|
|
7
|
+
readonly fraction: number | null;
|
|
8
|
+
/** danger colors bar and value. */
|
|
9
|
+
readonly tone?: "default" | "danger";
|
|
10
|
+
/** Small line under the bar, e.g. "42 % repaid". */
|
|
11
|
+
readonly sub?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Liste aus Label/Wert-Kopfzeile + Fortschrittsbalken pro Eintrag (z.B.
|
|
14
|
+
* Tilgungsfortschritt pro Kredit). */
|
|
15
|
+
export declare function ProgressList({ rows, emptyContent, testId, }: {
|
|
16
|
+
readonly rows: readonly ProgressListRow[];
|
|
17
|
+
readonly emptyContent?: ReactNode;
|
|
18
|
+
readonly testId?: string;
|
|
19
|
+
}): ReactNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { ProgressBar } from "./progress-bar.js";
|
|
4
|
+
/** Liste aus Label/Wert-Kopfzeile + Fortschrittsbalken pro Eintrag (z.B.
|
|
5
|
+
* Tilgungsfortschritt pro Kredit). */
|
|
6
|
+
export function ProgressList({ rows, emptyContent, testId, }) {
|
|
7
|
+
if (rows.length === 0) {
|
|
8
|
+
return _jsx("div", { "data-testid": testId, children: emptyContent });
|
|
9
|
+
}
|
|
10
|
+
return (_jsx("ul", { "data-testid": testId, className: "flex max-h-64 flex-col gap-3 overflow-y-auto pr-1", children: rows.map((row) => (_jsxs("li", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-2 text-sm", children: [_jsx("span", { className: "font-medium", children: row.label }), _jsx("span", { className: cn("tabular-nums", row.tone === "danger" ? "text-status-critical" : "text-muted-foreground"), children: row.value })] }), row.fraction !== null && _jsx(ProgressBar, { value: row.fraction, tone: row.tone }), row.sub !== undefined && _jsx("div", { className: "text-xs text-muted-foreground", children: row.sub })] }, row.id))) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type PublicShellCommonProps = {
|
|
3
|
+
readonly brand: ReactNode;
|
|
4
|
+
readonly headerActions?: ReactNode;
|
|
5
|
+
readonly footer?: ReactNode;
|
|
6
|
+
readonly children: ReactNode;
|
|
7
|
+
readonly testId?: string;
|
|
8
|
+
};
|
|
9
|
+
export type PublicShellVariant = "marketing" | "focus" | "card";
|
|
10
|
+
export type PublicShellProps = PublicShellCommonProps & ({
|
|
11
|
+
readonly variant: "marketing";
|
|
12
|
+
readonly notice?: ReactNode;
|
|
13
|
+
} | {
|
|
14
|
+
readonly variant: "focus";
|
|
15
|
+
readonly progress?: ReactNode;
|
|
16
|
+
} | {
|
|
17
|
+
readonly variant: "card";
|
|
18
|
+
});
|
|
19
|
+
/** One header and footer for anonymous pages; `focus` narrows to a task column,
|
|
20
|
+
* `card` centers a single card (sign-in). */
|
|
21
|
+
export declare function PublicShell(props: PublicShellProps): ReactNode;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const CONTENT_WIDTH_CLASS = "mx-auto w-full max-w-6xl px-4 sm:px-6";
|
|
3
|
+
const MAIN_CLASS = {
|
|
4
|
+
marketing: "w-full flex-1",
|
|
5
|
+
focus: "mx-auto w-full max-w-3xl flex-1 px-4 py-6 sm:px-6",
|
|
6
|
+
card: "flex w-full flex-1 items-center justify-center bg-muted px-4 py-10",
|
|
7
|
+
};
|
|
8
|
+
/** One header and footer for anonymous pages; `focus` narrows to a task column,
|
|
9
|
+
* `card` centers a single card (sign-in). */
|
|
10
|
+
export function PublicShell(props) {
|
|
11
|
+
const { variant, brand, headerActions, footer, children, testId = "public-shell" } = props;
|
|
12
|
+
const notice = props.variant === "marketing" ? props.notice : undefined;
|
|
13
|
+
const progress = props.variant === "focus" ? props.progress : undefined;
|
|
14
|
+
return (_jsxs("div", { "data-testid": testId, "data-variant": variant, className: "flex min-h-dvh flex-col bg-background text-foreground", children: [notice !== undefined && (_jsx("div", { "data-testid": `${testId}-notice`, className: "bg-promo px-4 py-2 text-center text-promo-foreground text-sm", children: notice })), _jsxs("header", { className: "border-border border-b", children: [_jsxs("div", { className: `${CONTENT_WIDTH_CLASS} flex min-h-16 items-center justify-between gap-4`, children: [_jsx("div", { className: "min-w-0", children: brand }), headerActions !== undefined && (_jsx("div", { className: "flex shrink-0 items-center gap-2", children: headerActions }))] }), progress !== undefined && (_jsx("div", { "data-testid": `${testId}-progress`, className: "mx-auto w-full max-w-3xl px-4 pb-3 sm:px-6", children: progress }))] }), _jsx("main", { className: MAIN_CLASS[variant], children: variant === "card" ? (_jsx("div", { "data-testid": `${testId}-card`, className: "w-full max-w-md rounded-(--card-radius) border border-border bg-card p-6 text-card-foreground shadow-sm sm:p-8", children: children })) : (children) }), footer !== undefined && (_jsx("footer", { className: "border-border border-t", children: _jsx("div", { className: `${CONTENT_WIDTH_CLASS} py-6 text-muted-foreground text-sm`, children: footer }) }))] }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type DataTableProps } from "../../index.ts";
|
|
3
|
+
export type QueryTableColumn = {
|
|
4
|
+
readonly field: string;
|
|
5
|
+
/** Translated Header-Label (i18n-Key auflösen liegt beim Caller). */
|
|
6
|
+
readonly label: string;
|
|
7
|
+
/** Field-Type für die Zell-Formatierung. Default "text". */
|
|
8
|
+
readonly type?: string;
|
|
9
|
+
};
|
|
10
|
+
export type QueryTableProps<TData = unknown> = {
|
|
11
|
+
/** Dispatcher-Query-Type (`<feature>:query:<entity>:<verb>`). */
|
|
12
|
+
readonly query: string;
|
|
13
|
+
readonly payload?: unknown;
|
|
14
|
+
/** SSE-Invalidierung — refetcht bei Entity-Events (useQuery live-mode). */
|
|
15
|
+
readonly live?: boolean;
|
|
16
|
+
readonly columns: readonly QueryTableColumn[];
|
|
17
|
+
/** Rows aus dem Query-Result ziehen. Default: Result selbst als Array. */
|
|
18
|
+
readonly rows?: (data: TData) => readonly Readonly<Record<string, unknown>>[];
|
|
19
|
+
/** Row-Id für Keys/Row-Click. Default: `row.id`, sonst der Index. */
|
|
20
|
+
readonly rowId?: (row: Readonly<Record<string, unknown>>, index: number) => string;
|
|
21
|
+
readonly onRowClick?: DataTableProps["onRowClick"];
|
|
22
|
+
readonly rowActions?: DataTableProps["rowActions"];
|
|
23
|
+
/** Empty-Inhalt — Default ist der Standard-Empty-State der Listen. */
|
|
24
|
+
readonly emptyState?: ReactNode;
|
|
25
|
+
readonly toolbarTitle?: ReactNode;
|
|
26
|
+
readonly toolbarEnd?: ReactNode;
|
|
27
|
+
readonly testId?: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function QueryTable<TData = unknown>({ query, payload, live, columns, rows, rowId, onRowClick, rowActions, emptyState, toolbarTitle, toolbarEnd, testId, }: QueryTableProps<TData>): ReactNode;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { usePrimitives, useQuery, useTranslation } from "../../index.js";
|
|
3
|
+
import { EmptyState, ErrorState, LoadingState } from "./states.js";
|
|
4
|
+
function defaultRows(data) {
|
|
5
|
+
return Array.isArray(data) ? data : [];
|
|
6
|
+
}
|
|
7
|
+
export function QueryTable({ query, payload, live = false, columns, rows, rowId, onRowClick, rowActions, emptyState, toolbarTitle, toolbarEnd, testId, }) {
|
|
8
|
+
const { DataTable } = usePrimitives();
|
|
9
|
+
const t = useTranslation();
|
|
10
|
+
const { data, error, loading, refetch } = useQuery(query, payload ?? {}, { live });
|
|
11
|
+
if (loading && data === null)
|
|
12
|
+
return _jsx(LoadingState, { rows: 4, testId: testId });
|
|
13
|
+
if (error !== null)
|
|
14
|
+
return _jsx(ErrorState, { error: error, onRetry: () => void refetch(), testId: testId });
|
|
15
|
+
const rawRows = rows !== undefined ? rows(data) : defaultRows(data);
|
|
16
|
+
const vmRows = rawRows.map((row, index) => ({
|
|
17
|
+
id: rowId !== undefined
|
|
18
|
+
? rowId(row, index)
|
|
19
|
+
: typeof row["id"] === "string"
|
|
20
|
+
? row["id"]
|
|
21
|
+
: String(index),
|
|
22
|
+
values: row,
|
|
23
|
+
}));
|
|
24
|
+
return (_jsx(DataTable, { columns: columns.map((c) => ({
|
|
25
|
+
field: c.field,
|
|
26
|
+
label: c.label,
|
|
27
|
+
type: c.type ?? "text",
|
|
28
|
+
sortable: false,
|
|
29
|
+
})), rows: vmRows, onRowClick: onRowClick, rowActions: rowActions, emptyState: emptyState ?? _jsx(EmptyState, { title: t("kumiko.list.no-entries") }), toolbarTitle: toolbarTitle, toolbarEnd: toolbarEnd, testId: testId }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** Ergebnis-Sektion eines Rechners: SectionCard mit Empty-Zustand (Banner)
|
|
3
|
+
* oder Kennzahl-Liste (DetailList) + optionalen Extras (Tabelle, Hinweise).
|
|
4
|
+
* Ersetzt die handgebauten `<dl>`/Banner-Blöcke in Custom-Screens. */
|
|
5
|
+
export declare function ResultPanel({ title, subtitle, empty, emptyText, rows, footer, children, testId, }: {
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly subtitle?: string;
|
|
8
|
+
readonly empty?: boolean;
|
|
9
|
+
readonly emptyText?: ReactNode;
|
|
10
|
+
readonly rows?: readonly {
|
|
11
|
+
readonly label: string;
|
|
12
|
+
readonly value: ReactNode;
|
|
13
|
+
readonly emphasize?: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
/** Action-Slot am Karten-Fuß (z.B. „In Finanzierung übernehmen"). */
|
|
16
|
+
readonly footer?: ReactNode;
|
|
17
|
+
readonly children?: ReactNode;
|
|
18
|
+
readonly testId?: string;
|
|
19
|
+
}): ReactNode;
|
|
20
|
+
export interface ResultColumn<Row> {
|
|
21
|
+
readonly header: string;
|
|
22
|
+
readonly align?: "left" | "right";
|
|
23
|
+
readonly cell: (row: Row) => ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export interface ResultTableFooterRow {
|
|
26
|
+
readonly label: string;
|
|
27
|
+
readonly value: ReactNode;
|
|
28
|
+
readonly emphasize?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Statische, prop-getriebene Ergebnistabelle für berechnete Zeilen (Tranchen,
|
|
31
|
+
* Szenarien). Nimmt dem Screen die `<table>`+tabular-nums-Ketten ab.
|
|
32
|
+
* ponytail: bewusst die minimale Read-only-Tabelle — für Sort/Pager/Facets
|
|
33
|
+
* stattdessen usePrimitives().DataTable bzw. QueryTable nutzen. */
|
|
34
|
+
export declare function ResultTable<Row>({ columns, rows, rowKey, testId, className, card, footer, }: {
|
|
35
|
+
readonly columns: readonly ResultColumn<Row>[];
|
|
36
|
+
readonly rows: readonly Row[];
|
|
37
|
+
readonly rowKey: (row: Row, index: number) => string;
|
|
38
|
+
readonly testId?: string;
|
|
39
|
+
readonly className?: string;
|
|
40
|
+
/** Rahmen-Look wie die CRUD-Liste (DataTable): gerundeter Border-Container +
|
|
41
|
+
* bg-muted-Header-Band. Default = bare (nur die Tabelle, ohne Container). */
|
|
42
|
+
readonly card?: boolean;
|
|
43
|
+
/** Summary rows (Subtotal/Tax/Total) rendered as real `<tr>`s in the same
|
|
44
|
+
* `<table>` instead of a separate flex-div next to it — guarantees
|
|
45
|
+
* column-alignment to the last (typically "Amount") column instead of
|
|
46
|
+
* approximating it with an independent layout. */
|
|
47
|
+
readonly footer?: readonly ResultTableFooterRow[];
|
|
48
|
+
}): ReactNode;
|
|
49
|
+
export interface ComparisonMetric<Col> {
|
|
50
|
+
readonly label: string;
|
|
51
|
+
readonly value: (col: Col, index: number) => ReactNode;
|
|
52
|
+
/** Index der besten Spalte für diese Zeile (hervorgehoben); -1 = keine. */
|
|
53
|
+
readonly bestIndex?: (cols: readonly Col[]) => number;
|
|
54
|
+
}
|
|
55
|
+
/** Transponierte Vergleichstabelle: Zeile = Kennzahl, Spalte = Variante, je
|
|
56
|
+
* Kennzahl optional die beste Spalte hervorgehoben. Für Szenario-/Angebots-
|
|
57
|
+
* Vergleiche, wo ResultTable (Zeile=Datensatz) nicht passt. */
|
|
58
|
+
export declare function ComparisonTable<Col>({ columns, columnHeader, columnKey, metrics, metricLabel, testId, className, card, }: {
|
|
59
|
+
readonly columns: readonly Col[];
|
|
60
|
+
readonly columnHeader: (col: Col, index: number) => string;
|
|
61
|
+
readonly columnKey: (col: Col, index: number) => string;
|
|
62
|
+
readonly metrics: readonly ComparisonMetric<Col>[];
|
|
63
|
+
readonly metricLabel: string;
|
|
64
|
+
readonly testId?: string;
|
|
65
|
+
readonly className?: string;
|
|
66
|
+
/** Rahmen-Look wie die CRUD-Liste (DataTable): gerundeter Border-Container +
|
|
67
|
+
* bg-muted-Header-Band. Default = bare. */
|
|
68
|
+
readonly card?: boolean;
|
|
69
|
+
}): ReactNode;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { usePrimitives } from "../../index.js";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { DetailList } from "./detail-list.js";
|
|
5
|
+
import { SectionCard } from "./section-card.js";
|
|
6
|
+
/** Ergebnis-Sektion eines Rechners: SectionCard mit Empty-Zustand (Banner)
|
|
7
|
+
* oder Kennzahl-Liste (DetailList) + optionalen Extras (Tabelle, Hinweise).
|
|
8
|
+
* Ersetzt die handgebauten `<dl>`/Banner-Blöcke in Custom-Screens. */
|
|
9
|
+
export function ResultPanel({ title, subtitle, empty, emptyText, rows, footer, children, testId, }) {
|
|
10
|
+
const { Banner } = usePrimitives();
|
|
11
|
+
return (_jsx(SectionCard, { title: title, subtitle: subtitle, footer: footer, testId: testId, children: empty === true ? (_jsx(Banner, { variant: "info", padded: true, children: emptyText })) : (_jsxs("div", { className: "flex flex-col gap-4", children: [rows !== undefined && rows.length > 0 && _jsx(DetailList, { rows: rows }), children] })) }));
|
|
12
|
+
}
|
|
13
|
+
/** Statische, prop-getriebene Ergebnistabelle für berechnete Zeilen (Tranchen,
|
|
14
|
+
* Szenarien). Nimmt dem Screen die `<table>`+tabular-nums-Ketten ab.
|
|
15
|
+
* ponytail: bewusst die minimale Read-only-Tabelle — für Sort/Pager/Facets
|
|
16
|
+
* stattdessen usePrimitives().DataTable bzw. QueryTable nutzen. */
|
|
17
|
+
export function ResultTable({ columns, rows, rowKey, testId, className, card, footer, }) {
|
|
18
|
+
const table = (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { "data-testid": testId, className: cn("w-full text-sm", className), children: [_jsx("thead", { className: cn(card === true && "bg-muted"), children: _jsx("tr", { className: "border-b text-left text-muted-foreground", children: columns.map((col) => (_jsx("th", { className: cn("py-1.5 font-medium", card === true && "px-3", col.align === "right" && "text-right"), children: col.header }, col.header))) }) }), _jsxs("tbody", { children: [rows.map((row, i) => (_jsx("tr", { className: "border-b last:border-0", children: columns.map((col) => (_jsx("td", { className: cn("py-1.5", card === true && "px-3", col.align === "right" && "text-right tabular-nums"), children: col.cell(row) }, col.header))) }, rowKey(row, i)))), footer?.map((row, i) => (_jsxs("tr", { className: cn(i === 0 && "border-t", row.emphasize === true && "font-semibold"), children: [_jsxs("td", { colSpan: columns.length > 1 ? Math.max(columns.length - 1, 1) : undefined, className: cn("py-1.5 text-muted-foreground", card === true && "px-3", row.emphasize === true && "text-foreground", columns.length === 1 && "flex items-center justify-between gap-4"), children: [row.label, columns.length === 1 && (_jsx("span", { className: cn("text-right tabular-nums", row.emphasize === true && "text-foreground"), children: row.value }))] }), columns.length > 1 && (_jsx("td", { className: cn("py-1.5 text-right tabular-nums", card === true && "px-3"), children: row.value }))] }, row.label)))] })] }) }));
|
|
19
|
+
return card === true ? (_jsx("div", { className: "overflow-hidden rounded-lg border bg-card", children: table })) : (table);
|
|
20
|
+
}
|
|
21
|
+
/** Transponierte Vergleichstabelle: Zeile = Kennzahl, Spalte = Variante, je
|
|
22
|
+
* Kennzahl optional die beste Spalte hervorgehoben. Für Szenario-/Angebots-
|
|
23
|
+
* Vergleiche, wo ResultTable (Zeile=Datensatz) nicht passt. */
|
|
24
|
+
export function ComparisonTable({ columns, columnHeader, columnKey, metrics, metricLabel, testId, className, card, }) {
|
|
25
|
+
const table = (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { "data-testid": testId, className: cn("w-full min-w-[24rem] text-sm", className), children: [_jsx("thead", { className: cn(card === true && "bg-muted"), children: _jsxs("tr", { className: "border-b text-left text-muted-foreground", children: [_jsx("th", { className: cn("py-1.5 font-medium", card === true && "px-3"), children: metricLabel }), columns.map((col, i) => (_jsx("th", { className: cn("py-1.5 text-right font-medium", card === true && "px-3"), children: columnHeader(col, i) }, columnKey(col, i))))] }) }), _jsx("tbody", { children: metrics.map((metric) => {
|
|
26
|
+
const best = metric.bestIndex !== undefined ? metric.bestIndex(columns) : -1;
|
|
27
|
+
return (_jsxs("tr", { className: "border-b last:border-0", children: [_jsx("td", { className: cn("py-1.5 text-muted-foreground", card === true && "px-3"), children: metric.label }), columns.map((col, i) => (_jsx("td", { className: cn("py-1.5 text-right tabular-nums", card === true && "px-3"), children: i === best ? (_jsx("span", { className: "inline-block rounded bg-primary/10 px-2 py-0.5 font-semibold text-primary", children: metric.value(col, i) })) : (metric.value(col, i)) }, columnKey(col, i))))] }, metric.label));
|
|
28
|
+
}) })] }) }));
|
|
29
|
+
return card === true ? (_jsx("div", { className: "overflow-hidden rounded-lg border bg-card", children: table })) : (table);
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** Sektion mit Titel + optionalem Action-Slot (Range-Umschalter, Filter) —
|
|
3
|
+
* ersetzt die wiederholten `<section className={CARD}>…<h2>`-Blöcke in
|
|
4
|
+
* Custom-Screens. Dünner Wrapper über das Card-Primitive. */
|
|
5
|
+
export declare function SectionCard({ title, subtitle, action, footer, children, testId, }: {
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly subtitle?: string;
|
|
8
|
+
readonly action?: ReactNode;
|
|
9
|
+
readonly footer?: ReactNode;
|
|
10
|
+
readonly children: ReactNode;
|
|
11
|
+
readonly testId?: string;
|
|
12
|
+
}): ReactNode;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { usePrimitives } from "../../index.js";
|
|
3
|
+
/** Sektion mit Titel + optionalem Action-Slot (Range-Umschalter, Filter) —
|
|
4
|
+
* ersetzt die wiederholten `<section className={CARD}>…<h2>`-Blöcke in
|
|
5
|
+
* Custom-Screens. Dünner Wrapper über das Card-Primitive. */
|
|
6
|
+
export function SectionCard({ title, subtitle, action, footer, children, testId, }) {
|
|
7
|
+
const { Card } = usePrimitives();
|
|
8
|
+
return (_jsx(Card, { slots: { title, subtitle, headerActions: action, footer }, testId: testId, children: _jsx("div", { className: "flex h-full flex-col gap-4", children: children }) }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
2
|
+
import type { ComponentProps, CSSProperties, ReactNode } from "react";
|
|
3
|
+
export type DrawerSheetContentProps = ComponentProps<typeof SheetPrimitive.Content> & {
|
|
4
|
+
readonly side?: "top" | "right" | "bottom" | "left";
|
|
5
|
+
readonly showCloseButton?: boolean;
|
|
6
|
+
readonly overlayStyle?: CSSProperties;
|
|
7
|
+
readonly closeLabel: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function DrawerSheetContent({ className, overlayStyle, children, side, showCloseButton, closeLabel, ...props }: DrawerSheetContentProps): ReactNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { XIcon } from "lucide-react";
|
|
3
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
export function DrawerSheetContent({ className, overlayStyle, children, side = "right", showCloseButton = true, closeLabel, ...props }) {
|
|
6
|
+
return (_jsxs(SheetPrimitive.Portal, { "data-slot": "sheet-portal", children: [_jsx(SheetPrimitive.Overlay, { "data-slot": "sheet-overlay", style: overlayStyle, className: "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0" }), _jsxs(SheetPrimitive.Content, { "data-slot": "sheet-content", className: cn("fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500", side === "right" &&
|
|
7
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", side === "left" &&
|
|
8
|
+
"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", side === "top" &&
|
|
9
|
+
"inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", side === "bottom" &&
|
|
10
|
+
"inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", className), ...props, children: [children, showCloseButton && (_jsxs(SheetPrimitive.Close, { className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary", children: [_jsx(XIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: closeLabel })] }))] })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type SideBySideTableColumn = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
};
|
|
6
|
+
export type SideBySideTableCell = {
|
|
7
|
+
readonly content: ReactNode;
|
|
8
|
+
/** Spans this cell over following columns (a value that is identical on
|
|
9
|
+
* both sides shown once). */
|
|
10
|
+
readonly colSpan?: number;
|
|
11
|
+
};
|
|
12
|
+
export type SideBySideTableRow = {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly header: ReactNode;
|
|
15
|
+
readonly cells: readonly SideBySideTableCell[];
|
|
16
|
+
/** Highlights a row that needs attention (e.g. differing values). */
|
|
17
|
+
readonly tone?: "warn";
|
|
18
|
+
};
|
|
19
|
+
/** Small read-only grid with real table semantics (`th scope`) whose cells
|
|
20
|
+
* are arbitrary React nodes — for side-by-side comparisons like "from the
|
|
21
|
+
* document" vs. "in stock". Not a `DataTable`: no sorting, paging, or
|
|
22
|
+
* view-model columns. */
|
|
23
|
+
export declare function SideBySideTable({ caption, rowHeaderLabel, columns, rows, rowHeaderWidth, testId, }: {
|
|
24
|
+
/** Accessible name of the table, not rendered visibly. */
|
|
25
|
+
readonly caption: string;
|
|
26
|
+
readonly rowHeaderLabel: string;
|
|
27
|
+
readonly columns: readonly SideBySideTableColumn[];
|
|
28
|
+
readonly rows: readonly SideBySideTableRow[];
|
|
29
|
+
readonly rowHeaderWidth?: number;
|
|
30
|
+
readonly testId?: string;
|
|
31
|
+
}): ReactNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
/** Small read-only grid with real table semantics (`th scope`) whose cells
|
|
4
|
+
* are arbitrary React nodes — for side-by-side comparisons like "from the
|
|
5
|
+
* document" vs. "in stock". Not a `DataTable`: no sorting, paging, or
|
|
6
|
+
* view-model columns. */
|
|
7
|
+
export function SideBySideTable({ caption, rowHeaderLabel, columns, rows, rowHeaderWidth = 120, testId, }) {
|
|
8
|
+
return (_jsxs("table", { "data-testid": testId, className: "w-full table-fixed border-collapse border border-border text-sm", children: [_jsx("caption", { className: "sr-only", children: caption }), _jsx("thead", { children: _jsxs("tr", { className: "bg-muted text-[13px] text-muted-foreground", children: [_jsx("th", { scope: "col", style: { width: rowHeaderWidth }, className: "border-b border-border px-3 py-2 text-left font-medium", children: rowHeaderLabel }), columns.map((column) => (_jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-medium", children: column.label }, column.id)))] }) }), _jsx("tbody", { children: rows.map((row) => (_jsxs("tr", { className: cn(row.tone === "warn" && "bg-status-warn-surface"), children: [_jsx("th", { scope: "row", className: cn("border-b border-border-row px-3 py-2 text-left align-top", row.tone === "warn"
|
|
9
|
+
? "font-semibold text-status-warn"
|
|
10
|
+
: "font-normal text-muted-foreground"), children: row.header }), row.cells.map((cell, index) => (_jsx("td", { colSpan: cell.colSpan, className: "border-b border-border-row px-3 py-2 align-top", children: cell.content }, index)))] }, row.id))) })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DashboardStatTone } from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export type StatTone = DashboardStatTone;
|
|
4
|
+
export type StatDelta = {
|
|
5
|
+
readonly value: string;
|
|
6
|
+
readonly direction: "up" | "down";
|
|
7
|
+
readonly tone?: StatTone;
|
|
8
|
+
};
|
|
9
|
+
export declare function Sparkline({ points, className, }: {
|
|
10
|
+
readonly points: readonly number[];
|
|
11
|
+
readonly className?: string;
|
|
12
|
+
}): ReactNode;
|
|
13
|
+
/** Icon-tragende Kennzahl-Kachel mit optionalem Delta-Chip, Trend-Zeile
|
|
14
|
+
* und Sparkline. `icon` ist ein fertiger Knoten (App liefert ihr SVG). */
|
|
15
|
+
export declare function StatCard({ icon, label, value, sub, tone, accentColor, delta, trend, spark, testId, children, }: {
|
|
16
|
+
readonly icon?: ReactNode;
|
|
17
|
+
readonly label: string;
|
|
18
|
+
readonly value: string;
|
|
19
|
+
readonly sub?: string;
|
|
20
|
+
readonly tone?: StatTone;
|
|
21
|
+
readonly accentColor?: string;
|
|
22
|
+
readonly delta?: StatDelta;
|
|
23
|
+
readonly trend?: string;
|
|
24
|
+
readonly spark?: readonly number[];
|
|
25
|
+
readonly testId?: string;
|
|
26
|
+
/** Extra content below the built-in fields (e.g. a usage meter) — kept
|
|
27
|
+
* inside the same card chrome instead of stacking a second bordered box. */
|
|
28
|
+
readonly children?: ReactNode;
|
|
29
|
+
}): ReactNode;
|
|
30
|
+
/** Flat KPI cell for a strip (divider instead of card): label, value with
|
|
31
|
+
* sparkline beside it, sub line. */
|
|
32
|
+
export declare function StatStripCell({ icon, label, value, sub, tone, accentColor, delta, spark, testId, }: {
|
|
33
|
+
/** Chip before the label, colored like StatCard's (`accentColor` or the tone). */
|
|
34
|
+
readonly icon?: ReactNode;
|
|
35
|
+
readonly label: string;
|
|
36
|
+
readonly value: string;
|
|
37
|
+
readonly sub?: string;
|
|
38
|
+
readonly tone?: StatTone;
|
|
39
|
+
readonly accentColor?: string;
|
|
40
|
+
readonly delta?: StatDelta;
|
|
41
|
+
readonly spark?: readonly number[];
|
|
42
|
+
readonly testId?: string;
|
|
43
|
+
}): ReactNode;
|
|
44
|
+
/** Kompakte Kennzahl-Kachel (ohne Icon) — für dichte KPI-Raster.
|
|
45
|
+
* StatCard ist die icon-tragende Variante. */
|
|
46
|
+
export declare function MiniStat({ label, value, tone, emphasize, testId, labelTestId, valueTestId, }: {
|
|
47
|
+
readonly label: string;
|
|
48
|
+
readonly value: string;
|
|
49
|
+
readonly tone?: StatTone;
|
|
50
|
+
readonly emphasize?: boolean;
|
|
51
|
+
readonly testId?: string;
|
|
52
|
+
readonly labelTestId?: string;
|
|
53
|
+
readonly valueTestId?: string;
|
|
54
|
+
}): ReactNode;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { usePrimitives } from "../../index.js";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
const TONE_CHIP = {
|
|
5
|
+
default: "bg-muted text-foreground",
|
|
6
|
+
positive: "bg-primary/10 text-primary",
|
|
7
|
+
ok: "bg-status-ok-surface text-status-ok",
|
|
8
|
+
warn: "bg-destructive/10 text-destructive",
|
|
9
|
+
negative: "bg-status-bad-surface text-status-bad",
|
|
10
|
+
};
|
|
11
|
+
const TONE_VALUE = {
|
|
12
|
+
default: "text-foreground",
|
|
13
|
+
positive: "text-primary",
|
|
14
|
+
ok: "text-status-ok",
|
|
15
|
+
warn: "text-destructive",
|
|
16
|
+
negative: "text-status-bad",
|
|
17
|
+
};
|
|
18
|
+
// Inline-SVG-Sparkline (kein Chart-Dep für eine 28px-Kurve): Linie +
|
|
19
|
+
// schwacher Flächen-Verlauf, Farbe via currentColor (Caller setzt text-tone).
|
|
20
|
+
export function Sparkline({ points, className, }) {
|
|
21
|
+
if (points.length < 2)
|
|
22
|
+
return null;
|
|
23
|
+
const w = 100;
|
|
24
|
+
const h = 28;
|
|
25
|
+
const min = Math.min(...points);
|
|
26
|
+
const max = Math.max(...points);
|
|
27
|
+
const span = max - min || 1;
|
|
28
|
+
const line = points
|
|
29
|
+
.map((p, i) => {
|
|
30
|
+
const x = (i / (points.length - 1)) * w;
|
|
31
|
+
const y = h - ((p - min) / span) * h;
|
|
32
|
+
return `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`;
|
|
33
|
+
})
|
|
34
|
+
.join(" ");
|
|
35
|
+
return (_jsxs("svg", { viewBox: `0 0 ${w} ${h}`, preserveAspectRatio: "none", className: className ?? "h-7 w-full", "aria-hidden": "true", children: [_jsx("path", { d: `${line} L${w},${h} L0,${h} Z`, fill: "currentColor", fillOpacity: 0.12 }), _jsx("path", { d: line, fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke" })] }));
|
|
36
|
+
}
|
|
37
|
+
function StatIconChip({ icon, tone, accentColor, }) {
|
|
38
|
+
return (_jsx("span", { className: cn("flex size-7 shrink-0 items-center justify-center rounded-lg", accentColor === undefined && TONE_CHIP[tone]), style: accentColor === undefined
|
|
39
|
+
? undefined
|
|
40
|
+
: {
|
|
41
|
+
color: accentColor,
|
|
42
|
+
backgroundColor: `color-mix(in srgb, ${accentColor} 12%, transparent)`,
|
|
43
|
+
}, children: icon }));
|
|
44
|
+
}
|
|
45
|
+
/** Icon-tragende Kennzahl-Kachel mit optionalem Delta-Chip, Trend-Zeile
|
|
46
|
+
* und Sparkline. `icon` ist ein fertiger Knoten (App liefert ihr SVG). */
|
|
47
|
+
export function StatCard({ icon, label, value, sub, tone = "default", accentColor, delta, trend, spark, testId, children, }) {
|
|
48
|
+
const { Card } = usePrimitives();
|
|
49
|
+
// StatCard is a reusable small-tile widget (a metrics dashboard grid, not
|
|
50
|
+
// a whole-section frame) — an extension section mounting several of
|
|
51
|
+
// these renders a grid of tiles inside its own host Card, not a
|
|
52
|
+
// duplicate outer frame around the section's full content.
|
|
53
|
+
return (_jsxs(Card, { options: { padded: false }, className: "p-4", testId: testId, children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-x-2 gap-y-1", children: [_jsxs("div", { className: "flex min-w-0 flex-1 basis-[7rem] items-center gap-2 text-muted-foreground", children: [icon !== undefined && _jsx(StatIconChip, { icon: icon, tone: tone, accentColor: accentColor }), _jsx("span", { title: label, className: "min-w-0 line-clamp-2 text-xs font-medium", children: label })] }), delta !== undefined && (_jsxs("span", { className: cn("inline-flex shrink-0 items-center gap-0.5 whitespace-nowrap rounded-full px-1.5 py-0.5 text-xs font-medium tabular-nums", TONE_CHIP[delta.tone ?? tone]), children: [delta.direction === "up" ? "↑" : "↓", delta.value] }))] }), _jsx("div", { className: "mt-2 text-xl font-semibold tabular-nums text-foreground", children: value }), sub !== undefined && _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: sub }), trend !== undefined && (_jsx("div", { className: "mt-0.5 text-xs font-medium text-foreground/80", children: trend })), spark !== undefined && (_jsx(Sparkline, { points: spark, className: cn("mt-2 h-7 w-full", TONE_VALUE[tone]) })), children !== undefined && _jsx("div", { className: "mt-3", children: children })] }));
|
|
54
|
+
}
|
|
55
|
+
/** Flat KPI cell for a strip (divider instead of card): label, value with
|
|
56
|
+
* sparkline beside it, sub line. */
|
|
57
|
+
export function StatStripCell({ icon, label, value, sub, tone = "default", accentColor, delta, spark, testId, }) {
|
|
58
|
+
return (_jsxs("div", { "data-testid": testId, className: "flex min-w-0 flex-col gap-1 px-4 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-2 gap-y-1", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2 text-xs font-medium text-muted-foreground", children: [icon !== undefined && _jsx(StatIconChip, { icon: icon, tone: tone, accentColor: accentColor }), _jsx("span", { children: label })] }), delta !== undefined && (_jsxs("span", { className: cn("inline-flex shrink-0 items-center gap-0.5 whitespace-nowrap rounded-full px-1.5 py-0.5 text-xs font-medium tabular-nums", TONE_CHIP[delta.tone ?? tone]), children: [delta.direction === "up" ? "↑" : "↓", delta.value] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: cn("text-xl font-semibold tabular-nums", TONE_VALUE[tone]), children: value }), spark !== undefined && (_jsx(Sparkline, { points: spark, className: cn("h-7 w-20 shrink-0", TONE_VALUE[tone]) }))] }), sub !== undefined && _jsx("div", { className: "text-xs text-muted-foreground", children: sub })] }));
|
|
59
|
+
}
|
|
60
|
+
/** Kompakte Kennzahl-Kachel (ohne Icon) — für dichte KPI-Raster.
|
|
61
|
+
* StatCard ist die icon-tragende Variante. */
|
|
62
|
+
export function MiniStat({ label, value, tone = "default", emphasize = false, testId, labelTestId, valueTestId, }) {
|
|
63
|
+
const { Card } = usePrimitives();
|
|
64
|
+
return (_jsxs(Card, { options: { padded: false }, className: cn("p-3", emphasize && "ring-1 ring-primary/30"), testId: testId, children: [_jsx("div", { className: "text-xs text-muted-foreground", "data-testid": labelTestId, children: label }), _jsx("div", { className: cn("mt-0.5 font-semibold tabular-nums", TONE_VALUE[tone], emphasize ? "text-lg" : "text-sm"), "data-testid": valueTestId, children: value })] }));
|
|
65
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { DispatcherError } from "../../headless/index.ts";
|
|
3
|
+
/** Leerer-Zustand mit optionalem Icon + CTA — die dashed-Box-Optik der
|
|
4
|
+
* entityList-Empty-States, als Standalone-Widget für Custom-Screens. */
|
|
5
|
+
export declare function EmptyState({ icon, title, description, action, testId, }: {
|
|
6
|
+
readonly icon?: ReactNode;
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly description?: string;
|
|
9
|
+
readonly action?: ReactNode;
|
|
10
|
+
readonly testId?: string;
|
|
11
|
+
}): ReactNode;
|
|
12
|
+
/** Lade-Zustand: Skeleton-Zeilen in Card-Optik. `rows` steuert die Höhe. */
|
|
13
|
+
export declare function LoadingState({ rows, className, testId, }: {
|
|
14
|
+
readonly rows?: number;
|
|
15
|
+
readonly className?: string;
|
|
16
|
+
readonly testId?: string;
|
|
17
|
+
}): ReactNode;
|
|
18
|
+
/** Fehler-Zustand auf Basis des Banner-Primitives, mit Retry-Button. */
|
|
19
|
+
export declare function ErrorState({ error, onRetry, testId, }: {
|
|
20
|
+
readonly error: DispatcherError;
|
|
21
|
+
readonly onRetry?: () => void;
|
|
22
|
+
readonly testId?: string;
|
|
23
|
+
}): ReactNode;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { dispatcherErrorText, usePrimitives, useTranslation } from "../../index.js";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { Skeleton } from "../ui/skeleton.js";
|
|
5
|
+
/** Leerer-Zustand mit optionalem Icon + CTA — die dashed-Box-Optik der
|
|
6
|
+
* entityList-Empty-States, als Standalone-Widget für Custom-Screens. */
|
|
7
|
+
export function EmptyState({ icon, title, description, action, testId, }) {
|
|
8
|
+
return (_jsxs("div", { "data-testid": testId, className: "flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed px-6 py-10 text-center", children: [icon !== undefined && _jsx("div", { className: "text-muted-foreground", children: icon }), _jsx("div", { className: "text-sm font-medium", children: title }), description !== undefined && (_jsx("div", { className: "text-sm text-muted-foreground", children: description })), action !== undefined && _jsx("div", { className: "mt-2", children: action })] }));
|
|
9
|
+
}
|
|
10
|
+
/** Lade-Zustand: Skeleton-Zeilen in Card-Optik. `rows` steuert die Höhe. */
|
|
11
|
+
export function LoadingState({ rows = 3, className, testId, }) {
|
|
12
|
+
const t = useTranslation();
|
|
13
|
+
return (_jsx("output", { "data-testid": testId, "aria-label": t("kumiko.widget.loading"), className: cn("flex w-full flex-col gap-2", className), children: Array.from({ length: rows }, (_, i) => (_jsx(Skeleton, { className: "h-9 w-full" }, i))) }));
|
|
14
|
+
}
|
|
15
|
+
/** Fehler-Zustand auf Basis des Banner-Primitives, mit Retry-Button. */
|
|
16
|
+
export function ErrorState({ error, onRetry, testId, }) {
|
|
17
|
+
const { Banner, Button } = usePrimitives();
|
|
18
|
+
const t = useTranslation();
|
|
19
|
+
return (_jsxs(Banner, { variant: "error", testId: testId, actions: onRetry !== undefined ? (_jsx(Button, { variant: "secondary", onClick: onRetry, children: t("kumiko.actions.reload") })) : undefined, children: [t("kumiko.widget.error.title"), " ", dispatcherErrorText(error, t)] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type StatusTone = "ok" | "warn" | "bad" | "critical" | "muted";
|
|
3
|
+
export declare const STATUS_TONE_TEXT: Record<StatusTone, string>;
|
|
4
|
+
/** `accent` is a brand-colored call-out (e.g. "3 suggestions"), not a status —
|
|
5
|
+
* hence not part of `StatusTone`, which toasts and select options share. */
|
|
6
|
+
export type StatusBadgeTone = StatusTone | "accent";
|
|
7
|
+
/** Pill badge with a status dot. Caller maps domain values to a tone
|
|
8
|
+
* (e.g. operational→ok, investigating→warn) and passes the translated
|
|
9
|
+
* label as children. */
|
|
10
|
+
export declare function StatusBadge({ tone, children, className, testId, }: {
|
|
11
|
+
readonly tone: StatusBadgeTone;
|
|
12
|
+
readonly children: ReactNode;
|
|
13
|
+
readonly className?: string;
|
|
14
|
+
readonly testId?: string;
|
|
15
|
+
}): ReactNode;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
export const STATUS_TONE_TEXT = {
|
|
4
|
+
ok: "text-status-ok",
|
|
5
|
+
warn: "text-status-warn",
|
|
6
|
+
bad: "text-status-bad",
|
|
7
|
+
critical: "text-status-critical",
|
|
8
|
+
muted: "text-muted-foreground",
|
|
9
|
+
};
|
|
10
|
+
// `muted` maps to the neutral tokens, not a status-* palette.
|
|
11
|
+
const TONE_PILL = {
|
|
12
|
+
accent: "bg-primary/10 text-primary",
|
|
13
|
+
ok: "bg-status-ok-surface text-status-ok",
|
|
14
|
+
warn: "bg-status-warn-surface text-status-warn",
|
|
15
|
+
bad: "bg-status-bad-surface text-status-bad",
|
|
16
|
+
critical: "bg-status-critical-surface text-status-critical",
|
|
17
|
+
muted: "bg-status-neutral-surface text-status-neutral",
|
|
18
|
+
};
|
|
19
|
+
/** Pill badge with a status dot. Caller maps domain values to a tone
|
|
20
|
+
* (e.g. operational→ok, investigating→warn) and passes the translated
|
|
21
|
+
* label as children. */
|
|
22
|
+
export function StatusBadge({ tone, children, className, testId, }) {
|
|
23
|
+
return (_jsxs("span", { "data-testid": testId, className: cn("inline-flex h-[22px] items-center gap-1.5 whitespace-nowrap rounded-full px-2 text-xs font-medium", TONE_PILL[tone], className), children: [_jsx("span", { "aria-hidden": "true", "data-status-dot": true, className: "size-1.5 rounded-full bg-current" }), children] }));
|
|
24
|
+
}
|