@cosmicdrift/kumiko-renderer 0.373.0 → 0.377.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/app/action-form-support.d.ts +58 -0
- package/dist/app/action-form-support.js +96 -0
- package/dist/app/client-actions.d.ts +46 -0
- package/dist/app/client-actions.js +60 -0
- package/dist/app/column-renderers.js +1 -1
- package/dist/app/config-edit-shim.js +1 -1
- package/dist/app/content-preview.js +1 -6
- package/dist/app/custom-screens.js +1 -1
- package/dist/app/dashboard-body.d.ts +1 -1
- package/dist/app/entity-list-expanded-row.d.ts +1 -1
- package/dist/app/entity-list-expanded-row.js +4 -0
- package/dist/app/form-schema.d.ts +1 -1
- package/dist/app/form-schema.js +2 -2
- package/dist/app/header-actions-bar.d.ts +19 -0
- package/dist/app/header-actions-bar.js +30 -0
- package/dist/app/kumiko-screen.d.ts +1 -1
- package/dist/app/kumiko-screen.js +134 -66
- package/dist/app/list-empty-content.d.ts +2 -2
- package/dist/app/list-empty-content.js +3 -2
- package/dist/app/list-facets.d.ts +1 -1
- package/dist/app/nav.js +2 -2
- package/dist/app/query-state-banners.d.ts +1 -1
- package/dist/app/reference-link.d.ts +1 -1
- package/dist/app/row-actions.d.ts +10 -2
- package/dist/app/row-actions.js +85 -6
- package/dist/app/secret-mint-body.d.ts +1 -1
- package/dist/app/secrets-edit-body.d.ts +1 -1
- package/dist/app/use-record-actions.d.ts +12 -0
- package/dist/app/use-record-actions.js +30 -0
- package/dist/app/write-failed-error.d.ts +1 -1
- package/dist/components/availability-options-select.d.ts +1 -1
- package/dist/components/embedded-list-field.d.ts +1 -1
- package/dist/components/embedded-list-field.js +1 -1
- package/dist/components/file-list-field.d.ts +1 -1
- package/dist/components/grid-cell-for-field.d.ts +4 -2
- package/dist/components/grid-cell-for-field.js +2 -2
- package/dist/components/multi-select-checkboxes.d.ts +1 -1
- package/dist/components/multi-select-checkboxes.js +1 -1
- package/dist/components/query-options-select.d.ts +1 -1
- package/dist/components/related-list-section.d.ts +1 -1
- package/dist/components/related-list-section.js +16 -3
- package/dist/components/render-edit-logic.d.ts +1 -1
- package/dist/components/render-edit-types.d.ts +2 -2
- package/dist/components/render-edit.d.ts +1 -1
- package/dist/components/render-edit.js +5 -3
- package/dist/components/render-field.d.ts +5 -2
- package/dist/components/render-field.js +103 -9
- package/dist/components/render-list.d.ts +1 -1
- package/dist/components/render-list.js +20 -4
- package/dist/components/write-form-section.d.ts +1 -1
- package/dist/components/write-form-section.js +1 -1
- package/dist/context/dispatcher-context.d.ts +1 -1
- package/dist/format-when.js +1 -1
- package/dist/headless/apex/css.d.ts +3 -0
- package/dist/headless/apex/css.js +298 -0
- package/dist/headless/apex/index.d.ts +236 -0
- package/dist/headless/apex/index.js +377 -0
- package/dist/headless/apex/lightbox.d.ts +4 -0
- package/dist/headless/apex/lightbox.js +81 -0
- package/dist/headless/contracts/asset.d.ts +15 -0
- package/dist/headless/contracts/asset.js +17 -0
- package/dist/headless/contracts/index.d.ts +3 -0
- package/dist/headless/contracts/index.js +1 -0
- package/dist/headless/contracts/locale.d.ts +7 -0
- package/dist/headless/contracts/locale.js +11 -0
- package/dist/headless/contracts/primitives.d.ts +93 -0
- package/dist/headless/contracts/primitives.js +21 -0
- package/dist/headless/dispatcher/index.d.ts +2 -0
- package/dist/headless/dispatcher/index.js +1 -0
- package/dist/headless/dispatcher/types.d.ts +86 -0
- package/dist/headless/dispatcher/types.js +34 -0
- package/dist/headless/form/form-controller.d.ts +2 -0
- package/dist/headless/form/form-controller.js +409 -0
- package/dist/headless/form/index.d.ts +3 -0
- package/dist/headless/form/index.js +2 -0
- package/dist/headless/form/types.d.ts +76 -0
- package/dist/headless/form/types.js +1 -0
- package/dist/headless/form/zod-bridge.d.ts +10 -0
- package/dist/headless/form/zod-bridge.js +103 -0
- package/dist/headless/format/document-values.d.ts +12 -0
- package/dist/headless/format/document-values.js +70 -0
- package/dist/headless/format/escape.d.ts +5 -0
- package/dist/headless/format/escape.js +75 -0
- package/dist/headless/format/html-template.d.ts +9 -0
- package/dist/headless/format/html-template.js +39 -0
- package/dist/headless/format/index.d.ts +12 -0
- package/dist/headless/format/index.js +237 -0
- package/dist/headless/format/money.d.ts +3 -0
- package/dist/headless/format/money.js +39 -0
- package/dist/headless/index.d.ts +14 -0
- package/dist/headless/index.js +9 -0
- package/dist/headless/locale-routing/index.d.ts +41 -0
- package/dist/headless/locale-routing/index.js +145 -0
- package/dist/headless/nav/index.d.ts +2 -0
- package/dist/headless/nav/index.js +1 -0
- package/dist/headless/nav/resolve.d.ts +2 -0
- package/dist/headless/nav/resolve.js +71 -0
- package/dist/headless/nav/types.d.ts +26 -0
- package/dist/headless/nav/types.js +1 -0
- package/dist/headless/store/create-store.d.ts +2 -0
- package/dist/headless/store/create-store.js +43 -0
- package/dist/headless/store/equality.d.ts +1 -0
- package/dist/headless/store/equality.js +34 -0
- package/dist/headless/store/index.d.ts +3 -0
- package/dist/headless/store/index.js +2 -0
- package/dist/headless/store/types.d.ts +7 -0
- package/dist/headless/store/types.js +10 -0
- package/dist/headless/view-model/edit.d.ts +18 -0
- package/dist/headless/view-model/edit.js +501 -0
- package/dist/headless/view-model/embedded-list.d.ts +27 -0
- package/dist/headless/view-model/embedded-list.js +63 -0
- package/dist/headless/view-model/index.d.ts +7 -0
- package/dist/headless/view-model/index.js +3 -0
- package/dist/headless/view-model/list-file-display.d.ts +3 -0
- package/dist/headless/view-model/list-file-display.js +27 -0
- package/dist/headless/view-model/list.d.ts +16 -0
- package/dist/headless/view-model/list.js +229 -0
- package/dist/headless/view-model/types.d.ts +382 -0
- package/dist/headless/view-model/types.js +1 -0
- package/dist/hooks/use-ai-text.d.ts +1 -1
- package/dist/hooks/use-form.d.ts +1 -1
- package/dist/hooks/use-form.js +1 -1
- package/dist/hooks/use-mutation.d.ts +1 -1
- package/dist/hooks/use-query.d.ts +1 -1
- package/dist/hooks/use-store.d.ts +1 -1
- package/dist/hooks/use-store.js +7 -7
- package/dist/hooks/use-stream-handler.d.ts +1 -1
- package/dist/i18n-defaults.js +7 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -4
- package/dist/live/csrf.d.ts +2 -0
- package/dist/live/csrf.js +60 -0
- package/dist/live/dispatcher-live.d.ts +7 -0
- package/dist/live/dispatcher-live.js +286 -0
- package/dist/live/envelopes.d.ts +101 -0
- package/dist/live/envelopes.js +35 -0
- package/dist/live/error-mapping.d.ts +16 -0
- package/dist/live/error-mapping.js +82 -0
- package/dist/live/index.d.ts +7 -0
- package/dist/live/index.js +5 -0
- package/dist/live/locale.d.ts +2 -0
- package/dist/live/locale.js +25 -0
- package/dist/live/sse-stream.d.ts +14 -0
- package/dist/live/sse-stream.js +128 -0
- package/dist/primitives.d.ts +53 -36
- package/dist/primitives.js +1 -1
- package/dist/session/session-ended.d.ts +1 -1
- package/dist/tokens.d.ts +3 -4
- package/dist/tokens.js +4 -5
- package/dist/web/app/app-schema-boot.d.ts +19 -0
- package/dist/web/app/app-schema-boot.js +124 -0
- package/dist/web/app/browser-locale.d.ts +22 -0
- package/dist/web/app/browser-locale.js +78 -0
- package/dist/web/app/client-plugin.d.ts +106 -0
- package/dist/web/app/client-plugin.js +12 -0
- package/dist/web/app/create-app.d.ts +104 -0
- package/dist/web/app/create-app.js +498 -0
- package/dist/web/app/create-public-surface.d.ts +36 -0
- package/dist/web/app/create-public-surface.js +40 -0
- package/dist/web/app/dashboard-body.d.ts +3 -0
- package/dist/web/app/dashboard-body.js +865 -0
- package/dist/web/app/dashboard-refresh.d.ts +6 -0
- package/dist/web/app/dashboard-refresh.js +15 -0
- package/dist/web/app/document-lang-sync.d.ts +4 -0
- package/dist/web/app/document-lang-sync.js +27 -0
- package/dist/web/app/draft-storage.d.ts +11 -0
- package/dist/web/app/draft-storage.js +53 -0
- package/dist/web/app/nav-providers-context.d.ts +13 -0
- package/dist/web/app/nav-providers-context.js +18 -0
- package/dist/web/app/nav.d.ts +40 -0
- package/dist/web/app/nav.js +231 -0
- package/dist/web/app/plain-content-editor.d.ts +3 -0
- package/dist/web/app/plain-content-editor.js +30 -0
- package/dist/web/app/relative-time.d.ts +10 -0
- package/dist/web/app/relative-time.js +56 -0
- package/dist/web/app/resolvers-context.d.ts +12 -0
- package/dist/web/app/resolvers-context.js +10 -0
- package/dist/web/app/rich-content-editor.d.ts +3 -0
- package/dist/web/app/rich-content-editor.js +15 -0
- package/dist/web/app/root-error-fallback.d.ts +2 -0
- package/dist/web/app/root-error-fallback.js +21 -0
- package/dist/web/app/screen-error-fallback.d.ts +6 -0
- package/dist/web/app/screen-error-fallback.js +17 -0
- package/dist/web/app/tiptap-editor.d.ts +3 -0
- package/dist/web/app/tiptap-editor.js +120 -0
- package/dist/web/app/toast-action-notifier.d.ts +5 -0
- package/dist/web/app/toast-action-notifier.js +17 -0
- package/dist/web/components/config-cascade.d.ts +13 -0
- package/dist/web/components/config-cascade.js +155 -0
- package/dist/web/components/config-source-badge.d.ts +6 -0
- package/dist/web/components/config-source-badge.js +55 -0
- package/dist/web/default-color-scheme.d.ts +2 -0
- package/dist/web/default-color-scheme.js +3 -0
- package/dist/web/icons.d.ts +94 -0
- package/dist/web/icons.js +100 -0
- package/dist/web/index.d.ts +78 -0
- package/dist/web/index.js +53 -0
- package/dist/web/layout/app-layout.d.ts +21 -0
- package/dist/web/layout/app-layout.js +8 -0
- package/dist/web/layout/avatar.d.ts +12 -0
- package/dist/web/layout/avatar.js +63 -0
- package/dist/web/layout/default-app-shell.d.ts +41 -0
- package/dist/web/layout/default-app-shell.js +16 -0
- package/dist/web/layout/editor-panel.d.ts +10 -0
- package/dist/web/layout/editor-panel.js +29 -0
- package/dist/web/layout/fill-classes.d.ts +9 -0
- package/dist/web/layout/fill-classes.js +15 -0
- package/dist/web/layout/filter-app-schema-navs.d.ts +7 -0
- package/dist/web/layout/filter-app-schema-navs.js +68 -0
- package/dist/web/layout/header-action-group.d.ts +6 -0
- package/dist/web/layout/header-action-group.js +8 -0
- package/dist/web/layout/header-overflow-menu.d.ts +4 -0
- package/dist/web/layout/header-overflow-menu.js +8 -0
- package/dist/web/layout/language-menu-items.d.ts +8 -0
- package/dist/web/layout/language-menu-items.js +16 -0
- package/dist/web/layout/language-switcher.d.ts +25 -0
- package/dist/web/layout/language-switcher.js +46 -0
- package/dist/web/layout/nav-tree.d.ts +19 -0
- package/dist/web/layout/nav-tree.js +536 -0
- package/dist/web/layout/page-header-slot.d.ts +25 -0
- package/dist/web/layout/page-header-slot.js +50 -0
- package/dist/web/layout/profile-menu.d.ts +13 -0
- package/dist/web/layout/profile-menu.js +6 -0
- package/dist/web/layout/shell-breadcrumb.d.ts +11 -0
- package/dist/web/layout/shell-breadcrumb.js +34 -0
- package/dist/web/layout/shell-header.d.ts +12 -0
- package/dist/web/layout/shell-header.js +159 -0
- package/dist/web/layout/sidebar-brand.d.ts +15 -0
- package/dist/web/layout/sidebar-brand.js +9 -0
- package/dist/web/layout/sidebar-panel.d.ts +41 -0
- package/dist/web/layout/sidebar-panel.js +107 -0
- package/dist/web/layout/sidebar-user.d.ts +8 -0
- package/dist/web/layout/sidebar-user.js +17 -0
- package/dist/web/layout/sidebar.d.ts +17 -0
- package/dist/web/layout/sidebar.js +11 -0
- package/dist/web/layout/target-resolver-stub.d.ts +13 -0
- package/dist/web/layout/target-resolver-stub.js +61 -0
- package/dist/web/layout/target-url.d.ts +12 -0
- package/dist/web/layout/target-url.js +77 -0
- package/dist/web/layout/theme-label.d.ts +15 -0
- package/dist/web/layout/theme-label.js +35 -0
- package/dist/web/layout/theme-menu-item.d.ts +12 -0
- package/dist/web/layout/theme-menu-item.js +16 -0
- package/dist/web/layout/theme-toggle.d.ts +17 -0
- package/dist/web/layout/theme-toggle.js +22 -0
- package/dist/web/layout/topbar.d.ts +8 -0
- package/dist/web/layout/topbar.js +4 -0
- package/dist/web/layout/workspace-shell.d.ts +63 -0
- package/dist/web/layout/workspace-shell.js +220 -0
- package/dist/web/layout/workspace-switcher.d.ts +15 -0
- package/dist/web/layout/workspace-switcher.js +36 -0
- package/dist/web/lib/accept-attr.d.ts +1 -0
- package/dist/web/lib/accept-attr.js +6 -0
- package/dist/web/lib/clamp.d.ts +1 -0
- package/dist/web/lib/clamp.js +3 -0
- package/dist/web/lib/cn.d.ts +6 -0
- package/dist/web/lib/cn.js +9 -0
- package/dist/web/lib/download.d.ts +2 -0
- package/dist/web/lib/download.js +24 -0
- package/dist/web/lib/file-ref-download.d.ts +15 -0
- package/dist/web/lib/file-ref-download.js +62 -0
- package/dist/web/lib/file-upload-form-data.d.ts +7 -0
- package/dist/web/lib/file-upload-form-data.js +11 -0
- package/dist/web/lib/resize-image.d.ts +1 -0
- package/dist/web/lib/resize-image.js +66 -0
- package/dist/web/lib/upload-file-ref.d.ts +9 -0
- package/dist/web/lib/upload-file-ref.js +29 -0
- package/dist/web/primitives/action-menu.d.ts +34 -0
- package/dist/web/primitives/action-menu.js +30 -0
- package/dist/web/primitives/calendar-popover.d.ts +13 -0
- package/dist/web/primitives/calendar-popover.js +76 -0
- package/dist/web/primitives/combobox.d.ts +56 -0
- package/dist/web/primitives/combobox.js +127 -0
- package/dist/web/primitives/date-field.d.ts +18 -0
- package/dist/web/primitives/date-field.js +74 -0
- package/dist/web/primitives/date-input.d.ts +1 -0
- package/dist/web/primitives/date-input.js +5 -0
- package/dist/web/primitives/date-parse.d.ts +11 -0
- package/dist/web/primitives/date-parse.js +159 -0
- package/dist/web/primitives/dialog.d.ts +3 -0
- package/dist/web/primitives/dialog.js +42 -0
- package/dist/web/primitives/drawer.d.ts +3 -0
- package/dist/web/primitives/drawer.js +17 -0
- package/dist/web/primitives/dropdown-menu.d.ts +15 -0
- package/dist/web/primitives/dropdown-menu.js +56 -0
- package/dist/web/primitives/embedded-list-input.d.ts +9 -0
- package/dist/web/primitives/embedded-list-input.js +382 -0
- package/dist/web/primitives/file-thumbnail.d.ts +25 -0
- package/dist/web/primitives/file-thumbnail.js +90 -0
- package/dist/web/primitives/file-upload.d.ts +21 -0
- package/dist/web/primitives/file-upload.js +56 -0
- package/dist/web/primitives/index.d.ts +34 -0
- package/dist/web/primitives/index.js +2021 -0
- package/dist/web/primitives/json-view.d.ts +7 -0
- package/dist/web/primitives/json-view.js +84 -0
- package/dist/web/primitives/layout.d.ts +27 -0
- package/dist/web/primitives/layout.js +30 -0
- package/dist/web/primitives/lightbox.d.ts +3 -0
- package/dist/web/primitives/lightbox.js +36 -0
- package/dist/web/primitives/locale-number.d.ts +2 -0
- package/dist/web/primitives/locale-number.js +54 -0
- package/dist/web/primitives/located-timestamp-input.d.ts +22 -0
- package/dist/web/primitives/located-timestamp-input.js +22 -0
- package/dist/web/primitives/metric.d.ts +5 -0
- package/dist/web/primitives/metric.js +18 -0
- package/dist/web/primitives/modal-shell.d.ts +17 -0
- package/dist/web/primitives/modal-shell.js +9 -0
- package/dist/web/primitives/modal.d.ts +3 -0
- package/dist/web/primitives/modal.js +13 -0
- package/dist/web/primitives/money-input.d.ts +18 -0
- package/dist/web/primitives/money-input.js +122 -0
- package/dist/web/primitives/narrow-pinned-footer.d.ts +12 -0
- package/dist/web/primitives/narrow-pinned-footer.js +153 -0
- package/dist/web/primitives/number-input.d.ts +18 -0
- package/dist/web/primitives/number-input.js +53 -0
- package/dist/web/primitives/option-groups.d.ts +11 -0
- package/dist/web/primitives/option-groups.js +27 -0
- package/dist/web/primitives/page-header.d.ts +3 -0
- package/dist/web/primitives/page-header.js +52 -0
- package/dist/web/primitives/promo-panel.d.ts +3 -0
- package/dist/web/primitives/promo-panel.js +11 -0
- package/dist/web/primitives/segmented-select.d.ts +21 -0
- package/dist/web/primitives/segmented-select.js +62 -0
- package/dist/web/primitives/share-actions.d.ts +4 -0
- package/dist/web/primitives/share-actions.js +56 -0
- package/dist/web/primitives/status-badge.d.ts +3 -0
- package/dist/web/primitives/status-badge.js +5 -0
- package/dist/web/primitives/sticky-action-bar.d.ts +5 -0
- package/dist/web/primitives/sticky-action-bar.js +15 -0
- package/dist/web/primitives/tabs.d.ts +3 -0
- package/dist/web/primitives/tabs.js +107 -0
- package/dist/web/primitives/timestamp-input.d.ts +27 -0
- package/dist/web/primitives/timestamp-input.js +87 -0
- package/dist/web/primitives/toast.d.ts +19 -0
- package/dist/web/primitives/toast.js +63 -0
- package/dist/web/primitives/tz-input.d.ts +11 -0
- package/dist/web/primitives/tz-input.js +8 -0
- package/dist/web/primitives/tz-options.d.ts +4 -0
- package/dist/web/primitives/tz-options.js +17 -0
- package/dist/web/primitives/use-keyboard-inset.d.ts +1 -0
- package/dist/web/primitives/use-keyboard-inset.js +28 -0
- package/dist/web/primitives/use-narrow-viewport.d.ts +4 -0
- package/dist/web/primitives/use-narrow-viewport.js +46 -0
- package/dist/web/react-css-custom-properties.d.ts +6 -0
- package/dist/web/react-css-custom-properties.js +1 -0
- package/dist/web/sse/live-events.d.ts +15 -0
- package/dist/web/sse/live-events.js +148 -0
- package/dist/web/theme-plugin.d.ts +41 -0
- package/dist/web/theme-plugin.js +123 -0
- package/dist/web/tokens.d.ts +24 -0
- package/dist/web/tokens.js +203 -0
- package/dist/web/ui/avatar.d.ts +11 -0
- package/dist/web/ui/avatar.js +25 -0
- package/dist/web/ui/badge.d.ts +9 -0
- package/dist/web/ui/badge.js +26 -0
- package/dist/web/ui/breadcrumb.d.ts +11 -0
- package/dist/web/ui/breadcrumb.js +29 -0
- package/dist/web/ui/button.d.ts +10 -0
- package/dist/web/ui/button.js +37 -0
- package/dist/web/ui/checkbox.d.ts +4 -0
- package/dist/web/ui/checkbox.js +11 -0
- package/dist/web/ui/input.d.ts +3 -0
- package/dist/web/ui/input.js +8 -0
- package/dist/web/ui/label.d.ts +4 -0
- package/dist/web/ui/label.js +10 -0
- package/dist/web/ui/resizable.d.ts +7 -0
- package/dist/web/ui/resizable.js +16 -0
- package/dist/web/ui/separator.d.ts +4 -0
- package/dist/web/ui/separator.js +10 -0
- package/dist/web/ui/sheet.d.ts +14 -0
- package/dist/web/ui/sheet.js +42 -0
- package/dist/web/ui/sidebar.d.ts +73 -0
- package/dist/web/ui/sidebar.js +211 -0
- package/dist/web/ui/skeleton.d.ts +2 -0
- package/dist/web/ui/skeleton.js +7 -0
- package/dist/web/ui/switch.d.ts +6 -0
- package/dist/web/ui/switch.js +10 -0
- package/dist/web/ui/table.d.ts +10 -0
- package/dist/web/ui/table.js +30 -0
- package/dist/web/ui/tabs.d.ts +11 -0
- package/dist/web/ui/tabs.js +31 -0
- package/dist/web/ui/textarea.d.ts +3 -0
- package/dist/web/ui/textarea.js +8 -0
- package/dist/web/ui/tooltip.d.ts +7 -0
- package/dist/web/ui/tooltip.js +19 -0
- package/dist/web/ui/use-mobile.d.ts +1 -0
- package/dist/web/ui/use-mobile.js +16 -0
- package/dist/web/version/update-checker.d.ts +9 -0
- package/dist/web/version/update-checker.js +88 -0
- package/dist/web/widgets/ai-text-field.d.ts +36 -0
- package/dist/web/widgets/ai-text-field.js +137 -0
- package/dist/web/widgets/chart-window.d.ts +58 -0
- package/dist/web/widgets/chart-window.js +190 -0
- package/dist/web/widgets/charts.d.ts +208 -0
- package/dist/web/widgets/charts.js +685 -0
- package/dist/web/widgets/collapsible-section.d.ts +10 -0
- package/dist/web/widgets/collapsible-section.js +17 -0
- package/dist/web/widgets/dashboard-list.d.ts +21 -0
- package/dist/web/widgets/dashboard-list.js +45 -0
- package/dist/web/widgets/detail-list.d.ts +15 -0
- package/dist/web/widgets/detail-list.js +8 -0
- package/dist/web/widgets/drawer.d.ts +65 -0
- package/dist/web/widgets/drawer.js +142 -0
- package/dist/web/widgets/feed-list.d.ts +13 -0
- package/dist/web/widgets/feed-list.js +9 -0
- package/dist/web/widgets/floating-panel.d.ts +30 -0
- package/dist/web/widgets/floating-panel.js +242 -0
- package/dist/web/widgets/form-fields.d.ts +93 -0
- package/dist/web/widgets/form-fields.js +50 -0
- package/dist/web/widgets/index.d.ts +27 -0
- package/dist/web/widgets/index.js +30 -0
- package/dist/web/widgets/infinity-list.d.ts +46 -0
- package/dist/web/widgets/infinity-list.js +211 -0
- package/dist/web/widgets/mode-switch.d.ts +21 -0
- package/dist/web/widgets/mode-switch.js +25 -0
- package/dist/web/widgets/photo-slots.d.ts +29 -0
- package/dist/web/widgets/photo-slots.js +52 -0
- package/dist/web/widgets/plan-card.d.ts +36 -0
- package/dist/web/widgets/plan-card.js +13 -0
- package/dist/web/widgets/progress-bar.d.ts +19 -0
- package/dist/web/widgets/progress-bar.js +24 -0
- package/dist/web/widgets/progress-list.d.ts +19 -0
- package/dist/web/widgets/progress-list.js +11 -0
- package/dist/web/widgets/public-shell.d.ts +22 -0
- package/dist/web/widgets/public-shell.js +15 -0
- package/dist/web/widgets/query-table.d.ts +29 -0
- package/dist/web/widgets/query-table.js +30 -0
- package/dist/web/widgets/result-panel.d.ts +69 -0
- package/dist/web/widgets/result-panel.js +30 -0
- package/dist/web/widgets/section-card.d.ts +12 -0
- package/dist/web/widgets/section-card.js +9 -0
- package/dist/web/widgets/sheet-parts.d.ts +9 -0
- package/dist/web/widgets/sheet-parts.js +11 -0
- package/dist/web/widgets/side-by-side-table.d.ts +31 -0
- package/dist/web/widgets/side-by-side-table.js +11 -0
- package/dist/web/widgets/stat.d.ts +54 -0
- package/dist/web/widgets/stat.js +65 -0
- package/dist/web/widgets/states.d.ts +23 -0
- package/dist/web/widgets/states.js +20 -0
- package/dist/web/widgets/status-badge.d.ts +15 -0
- package/dist/web/widgets/status-badge.js +24 -0
- package/dist/web/widgets/step-bar.d.ts +35 -0
- package/dist/web/widgets/step-bar.js +60 -0
- package/dist/web/widgets/upload-zone.d.ts +24 -0
- package/dist/web/widgets/upload-zone.js +108 -0
- package/dist/web/widgets/use-draft.d.ts +20 -0
- package/dist/web/widgets/use-draft.js +21 -0
- package/dist/web/widgets/use-pointer-drag.d.ts +21 -0
- package/dist/web/widgets/use-pointer-drag.js +53 -0
- package/package.json +105 -7
- package/src/changes.json +77 -0
- package/src/web/fonts/OFL.txt +93 -0
- package/src/web/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-600.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-600.woff2 +0 -0
- package/src/web/styles.css +401 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { isFormatSpec, SYSTEM_REFERENCE_LABELS, } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
3
|
-
import { applyFormatSpec, currencyDecimals, EMPTY_VALUE_PLACEHOLDER, } from "@cosmicdrift/kumiko-headless";
|
|
4
3
|
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
5
|
-
import { createContext, Fragment, useCallback, useContext, useMemo, useState, } from "react";
|
|
4
|
+
import { createContext, Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
6
5
|
import { useAppFeatures } from "../app/app-features-context.js";
|
|
7
6
|
import { useColumnRenderer } from "../app/column-renderers.js";
|
|
8
7
|
import { extensionSectionName } from "../app/extension-sections.js";
|
|
9
8
|
import { toKebab } from "../app/qn.js";
|
|
10
9
|
import { useReferenceLinkResolver } from "../app/reference-link.js";
|
|
10
|
+
import { evalRowExtractor } from "../app/row-actions.js";
|
|
11
11
|
import { screenAccessAllows } from "../app/screen-access.js";
|
|
12
|
+
import { useOptionalDispatcher } from "../context/dispatcher-context.js";
|
|
12
13
|
import { useUserRoles } from "../context/user-roles-context.js";
|
|
14
|
+
import { applyFormatSpec, currencyDecimals, EMPTY_VALUE_PLACEHOLDER, } from "../headless/index.js";
|
|
13
15
|
import { REFERENCE_COMBOBOX_LIMIT } from "../hooks/reference-limits.js";
|
|
14
16
|
import { useQuery } from "../hooks/use-query.js";
|
|
15
17
|
import { referenceOptionSource } from "../hooks/use-reference-lookup.js";
|
|
@@ -23,7 +25,7 @@ import { QueryOptionsSelect } from "./query-options-select.js";
|
|
|
23
25
|
import { ReferenceCreateDialog } from "./reference-create-dialog.js";
|
|
24
26
|
const AllFieldsRequiredContext = createContext(false);
|
|
25
27
|
export const AllFieldsRequiredProvider = AllFieldsRequiredContext.Provider;
|
|
26
|
-
export function RenderField({ field, issues, onChange, featureName, labelAppendix, fieldAppendix, appendixPlacement, description, status, allIssues, valueDisplay = "form", row, changed, layout, accent, }) {
|
|
28
|
+
export function RenderField({ field, issues, onChange, featureName, labelAppendix, fieldAppendix, appendixPlacement, description, status, allIssues, valueDisplay = "form", row, changed, layout, accent, fillDisabled, }) {
|
|
27
29
|
const { Field, Input, Banner, Text, JsonView } = usePrimitives();
|
|
28
30
|
// App-Locale (i18n) für money/date-Inputs — sonst fielen sie auf
|
|
29
31
|
// navigator.language (Browser-Sprache) zurück statt der gewählten
|
|
@@ -48,7 +50,20 @@ export function RenderField({ field, issues, onChange, featureName, labelAppendi
|
|
|
48
50
|
// Komponente gemountet werden (nicht als pure render-Call).
|
|
49
51
|
const control = field.readOnly && field.renderer !== undefined ? (_jsx(FieldRendererOutput, { field: field, renderer: field.renderer, appLocale: appLocale, ...(row !== undefined && { row }) })) : field.type === "embedded" && field.embeddedListCells !== undefined ? (_jsx(EmbeddedListField, { field: field, id: id, onChange: onChange, allIssues: allIssues ?? {}, featureName: featureName ?? "" })) : field.type === "reference" ? (readOnlyText ? (_jsx(ReadOnlyReferenceValue, { field: field, featureName: featureName ?? "" })) : (_jsx(ReferenceInput, { field: field, id: id, hasError: hasError, onChange: onChange, Input: Input, featureName: featureName ?? "" }))) : field.type === "select" && field.selectOptionsQuery !== undefined && !readOnlyText ? (_jsx(QueryOptionsSelect, { field: field, query: field.selectOptionsQuery.query, payload: field.selectOptionsQuery.payload, row: row ?? { [field.field]: field.value }, id: id, hasError: hasError, onChange: onChange })) : field.type === "select" &&
|
|
50
52
|
field.selectOptionsAvailabilityQuery !== undefined &&
|
|
51
|
-
!readOnlyText ? (_jsx(AvailabilityOptionsSelect, { field: field, query: field.selectOptionsAvailabilityQuery.query, payload: field.selectOptionsAvailabilityQuery.payload, row: row ?? { [field.field]: field.value }, id: id, hasError: hasError, onChange: onChange })) : readOnlyText && !isComplexFieldType(field.type) && !isFileFieldType(field.type) ? (_jsx(Text, { testId: `field-value-${field.field}`, children: readOnlyDisplayText(field, appLocale, t) })) : field.type === "text" && field.writeOnly === true && !readOnlyText ? (_jsx(WriteOnlyTextInput, { field: field, id: id, hasError: hasError, onChange: onChange, changed: changed === true })) : (renderInput({
|
|
53
|
+
!readOnlyText ? (_jsx(AvailabilityOptionsSelect, { field: field, query: field.selectOptionsAvailabilityQuery.query, payload: field.selectOptionsAvailabilityQuery.payload, row: row ?? { [field.field]: field.value }, id: id, hasError: hasError, onChange: onChange })) : readOnlyText && !isComplexFieldType(field.type) && !isFileFieldType(field.type) ? (_jsx(Text, { testId: `field-value-${field.field}`, children: readOnlyDisplayText(field, appLocale, t) })) : field.type === "text" && field.writeOnly === true && !readOnlyText ? (_jsx(WriteOnlyTextInput, { field: field, id: id, hasError: hasError, onChange: onChange, changed: changed === true })) : field.fillAction !== undefined ? (_jsx(FillActionControl, { field: field, fillAction: field.fillAction, row: row ?? { [field.field]: field.value }, onChange: onChange, disabled: fillDisabled === true, Banner: Banner, renderControl: (fill) => renderInput({
|
|
54
|
+
field,
|
|
55
|
+
id,
|
|
56
|
+
hasError,
|
|
57
|
+
onChange,
|
|
58
|
+
Input,
|
|
59
|
+
appLocale,
|
|
60
|
+
Banner,
|
|
61
|
+
Text,
|
|
62
|
+
JsonView,
|
|
63
|
+
t,
|
|
64
|
+
row,
|
|
65
|
+
fill,
|
|
66
|
+
}) })) : (renderInput({
|
|
52
67
|
field,
|
|
53
68
|
id,
|
|
54
69
|
hasError,
|
|
@@ -63,6 +78,63 @@ export function RenderField({ field, issues, onChange, featureName, labelAppendi
|
|
|
63
78
|
}));
|
|
64
79
|
return (_jsx(Field, { id: id, label: field.label, required: field.required && field.type !== "boolean" && !allFieldsRequired, ...(issues !== undefined && { issues }), ...(labelAppendix !== undefined && { labelAppendix }), ...(fieldAppendix !== undefined && { fieldAppendix }), ...(appendixPlacement !== undefined && { appendixPlacement }), ...(description !== undefined && { description }), ...(status !== undefined && { status }), ...(changed === true && { changed }), ...(layout !== undefined && { layout }), ...(accent === true && { accent }), testId: `field-${field.field}`, children: control }));
|
|
65
80
|
}
|
|
81
|
+
const NUMERIC_FIELD_TYPES = new Set(["number", "bigInt", "decimal"]);
|
|
82
|
+
// A text field takes a number as its string form; a numeric field takes only a
|
|
83
|
+
// number — a string would be sent as a wrong-typed value.
|
|
84
|
+
function fillValueFor(fieldType, result) {
|
|
85
|
+
if (NUMERIC_FIELD_TYPES.has(fieldType))
|
|
86
|
+
return typeof result === "number" ? result : undefined;
|
|
87
|
+
if (typeof result === "string")
|
|
88
|
+
return result;
|
|
89
|
+
return typeof result === "number" ? String(result) : undefined;
|
|
90
|
+
}
|
|
91
|
+
// Owns the query state of a fill button. A separate component because RenderField
|
|
92
|
+
// returns early for hidden fields and the dispatcher hook must not be conditional.
|
|
93
|
+
function FillActionControl({ field, fillAction, row, onChange, disabled, Banner, renderControl, }) {
|
|
94
|
+
const dispatcher = useOptionalDispatcher();
|
|
95
|
+
const t = useTranslation();
|
|
96
|
+
const [busy, setBusy] = useState(false);
|
|
97
|
+
const [failed, setFailed] = useState(false);
|
|
98
|
+
// A newer click or an unmount cancels the in-flight query so a late answer
|
|
99
|
+
// cannot overwrite a value the user has since typed.
|
|
100
|
+
const activeRun = useRef(null);
|
|
101
|
+
useEffect(() => () => activeRun.current?.abort(), []);
|
|
102
|
+
const run = async () => {
|
|
103
|
+
if (dispatcher === undefined) {
|
|
104
|
+
setFailed(true);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
activeRun.current?.abort();
|
|
108
|
+
const ctrl = new AbortController();
|
|
109
|
+
activeRun.current = ctrl;
|
|
110
|
+
setBusy(true);
|
|
111
|
+
setFailed(false);
|
|
112
|
+
const payload = fillAction.params === undefined ? {} : evalRowExtractor(fillAction.params, row);
|
|
113
|
+
const result = await dispatcher.query(fillAction.query, payload, {
|
|
114
|
+
signal: ctrl.signal,
|
|
115
|
+
});
|
|
116
|
+
if (ctrl.signal.aborted)
|
|
117
|
+
return;
|
|
118
|
+
setBusy(false);
|
|
119
|
+
if (!result.isSuccess) {
|
|
120
|
+
setFailed(true);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const value = fillValueFor(field.type, propertyOf(result.data, fillAction.field));
|
|
124
|
+
if (value === undefined) {
|
|
125
|
+
setFailed(true);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
onChange(value);
|
|
129
|
+
};
|
|
130
|
+
return (_jsxs(_Fragment, { children: [renderControl({
|
|
131
|
+
label: fillAction.label,
|
|
132
|
+
onPress: () => void run(),
|
|
133
|
+
busy,
|
|
134
|
+
disabled,
|
|
135
|
+
testId: `field-fill-${field.field}`,
|
|
136
|
+
}), failed && (_jsx(Banner, { variant: "error", testId: `field-fill-error-${field.field}`, children: t("kumiko.field.fillAction.failed") }))] }));
|
|
137
|
+
}
|
|
66
138
|
// Wire value of a writeOnly field is `true` (set) / `null` (empty), never the
|
|
67
139
|
// secret. Typing sets a new value; an untouched or emptied input submits
|
|
68
140
|
// nothing / "" which the server reads as "keep". `null` after a loaded `true`
|
|
@@ -330,7 +402,7 @@ function resolveFieldIcon(field) {
|
|
|
330
402
|
// SelectFieldDef.options). Structural types without a widget (embedded,
|
|
331
403
|
// jsonb, multiSelect) render read-only instead of a data-destroying text
|
|
332
404
|
// fallback (#1834); truly unknown scalar types still fall back to text.
|
|
333
|
-
function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner, Text, JsonView, t, row, }) {
|
|
405
|
+
function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner, Text, JsonView, t, row, fill, }) {
|
|
334
406
|
const common = {
|
|
335
407
|
id,
|
|
336
408
|
name: field.field,
|
|
@@ -338,6 +410,14 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
338
410
|
required: field.required,
|
|
339
411
|
hasError,
|
|
340
412
|
};
|
|
413
|
+
const placeholderProp = field.placeholder !== undefined ? { placeholder: field.placeholder } : {};
|
|
414
|
+
const prefix = resolveAffix(field.prefix, row);
|
|
415
|
+
const suffix = resolveAffix(field.suffix, row);
|
|
416
|
+
const affixProps = {
|
|
417
|
+
...(prefix !== undefined && { prefix }),
|
|
418
|
+
...(suffix !== undefined && { suffix }),
|
|
419
|
+
...(fill !== undefined && { fill }),
|
|
420
|
+
};
|
|
341
421
|
switch (field.type) {
|
|
342
422
|
// decimal/bigInt are both plain numbers on the wire (fieldToZod:
|
|
343
423
|
// z.number() / z.number().int().safe()) — the number input's onChange
|
|
@@ -347,11 +427,11 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
347
427
|
case "bigInt": {
|
|
348
428
|
const icon = resolveFieldIcon(field);
|
|
349
429
|
const unit = resolveNumberUnit(field.unit, row);
|
|
350
|
-
return (_jsx(Input, { kind: "number", ...common, value: numberValue(field.value), onChange: (v) => onChange(v), locale: appLocale, ...(field.grouping !== undefined && { grouping: field.grouping }), ...(field.type === "bigInt" && { integer: true }), ...(icon !== undefined && { icon }), ...(unit !== undefined && { unit }) }));
|
|
430
|
+
return (_jsx(Input, { kind: "number", ...common, value: numberValue(field.value), onChange: (v) => onChange(v), locale: appLocale, ...(field.grouping !== undefined && { grouping: field.grouping }), ...(field.type === "bigInt" && { integer: true }), ...placeholderProp, ...affixProps, ...(icon !== undefined && { icon }), ...(unit !== undefined && { unit }) }));
|
|
351
431
|
}
|
|
352
432
|
case "decimal": {
|
|
353
433
|
const icon = resolveFieldIcon(field);
|
|
354
|
-
return (_jsx(Input, { kind: "number", ...common, value: numberValue(field.value), onChange: (v) => onChange(v), locale: appLocale, ...(field.grouping !== undefined && { grouping: field.grouping }), ...(icon !== undefined && { icon }) }));
|
|
434
|
+
return (_jsx(Input, { kind: "number", ...common, value: numberValue(field.value), onChange: (v) => onChange(v), locale: appLocale, ...(field.grouping !== undefined && { grouping: field.grouping }), ...placeholderProp, ...affixProps, ...(icon !== undefined && { icon }) }));
|
|
355
435
|
}
|
|
356
436
|
case "tz":
|
|
357
437
|
return (_jsx(Input, { kind: "tz", ...common, value: stringValue(field.value), onChange: (v) => onChange(v) }));
|
|
@@ -414,6 +494,9 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
414
494
|
if (field.type === "text" && field.format === "password") {
|
|
415
495
|
return (_jsx(Input, { kind: "password", ...common, value: stringValue(field.value), onChange: (v) => onChange(v) }));
|
|
416
496
|
}
|
|
497
|
+
if (field.type === "text" && field.format === "month") {
|
|
498
|
+
return (_jsx(Input, { kind: "month", ...common, value: stringValue(field.value), onChange: (v) => onChange(v) }));
|
|
499
|
+
}
|
|
417
500
|
// text + unknown scalar type → text input. If TextFieldDef.multiline
|
|
418
501
|
// is set (the view-model carries it), the renderer switches to
|
|
419
502
|
// textarea. longText always renders a textarea — that's the point of
|
|
@@ -421,10 +504,10 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
421
504
|
// only supplies an optional `{ rows }` override for it (#1925).
|
|
422
505
|
if (field.type === "longText" || (field.type === "text" && field.multiline)) {
|
|
423
506
|
const rows = typeof field.multiline === "object" ? field.multiline.rows : undefined;
|
|
424
|
-
return (_jsx(Input, { kind: "textarea", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...(rows !== undefined && { rows }) }));
|
|
507
|
+
return (_jsx(Input, { kind: "textarea", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...(rows !== undefined && { rows }), ...placeholderProp }));
|
|
425
508
|
}
|
|
426
509
|
const icon = resolveFieldIcon(field);
|
|
427
|
-
return (_jsx(Input, { kind: "text", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...(icon !== undefined && { icon }), ...(field.autoComplete !== undefined && { autoComplete: field.autoComplete }) }));
|
|
510
|
+
return (_jsx(Input, { kind: "text", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...placeholderProp, ...affixProps, ...(icon !== undefined && { icon }), ...(field.autoComplete !== undefined && { autoComplete: field.autoComplete }) }));
|
|
428
511
|
}
|
|
429
512
|
}
|
|
430
513
|
}
|
|
@@ -479,6 +562,17 @@ function resolveNumberUnit(unit, row) {
|
|
|
479
562
|
const trimmed = sibling.trim();
|
|
480
563
|
return trimmed.length > 0 && trimmed.length <= MAX_SIBLING_UNIT_LENGTH ? trimmed : undefined;
|
|
481
564
|
}
|
|
565
|
+
// String affix → used as-is (the view model already translated it). Field
|
|
566
|
+
// affix → the live form value; missing, empty or non-scalar means no affix.
|
|
567
|
+
function resolveAffix(affix, row) {
|
|
568
|
+
if (affix === undefined)
|
|
569
|
+
return undefined;
|
|
570
|
+
if (typeof affix === "string")
|
|
571
|
+
return affix;
|
|
572
|
+
const value = row?.[affix.field];
|
|
573
|
+
const text = typeof value === "number" ? String(value) : value;
|
|
574
|
+
return typeof text === "string" && text.trim().length > 0 ? text.trim() : undefined;
|
|
575
|
+
}
|
|
482
576
|
function stringArrayOf(value) {
|
|
483
577
|
return Array.isArray(value)
|
|
484
578
|
? value.filter((item) => typeof item === "string")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EntityDefinition, EntityListScreenDefinition, IconKey } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
-
import type { ListRowViewModel, Translate } from "@cosmicdrift/kumiko-headless";
|
|
3
2
|
import { type ReactNode } from "react";
|
|
3
|
+
import type { ListRowViewModel, Translate } from "../headless/index.ts";
|
|
4
4
|
import type { ListSort } from "../hooks/use-list-url-state.ts";
|
|
5
5
|
import { type DataTableDateRangeFacet, type DataTableFacet, type DataTableProps, type DataTableRowAction, type DataTableRowActionMode, type DataTableRowGrouping } from "../primitives.tsx";
|
|
6
6
|
import { type ListEmptyStateContent } from "./list-empty-state.tsx";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { computeListViewModel, EMPTY_VALUE_PLACEHOLDER } from "@cosmicdrift/kumiko-headless";
|
|
3
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
3
|
import { extensionSectionName, useExtensionSectionComponent } from "../app/extension-sections.js";
|
|
5
4
|
import { useReferenceLinkResolver } from "../app/reference-link.js";
|
|
5
|
+
import { computeListViewModel, EMPTY_VALUE_PLACEHOLDER } from "../headless/index.js";
|
|
6
6
|
import { useReferenceLookup } from "../hooks/use-reference-lookup.js";
|
|
7
7
|
import { useTranslation } from "../i18n.js";
|
|
8
8
|
import { usePageHeaderCompact, usePageHeaderSlotAvailable } from "../page-header-slot.js";
|
|
@@ -104,7 +104,7 @@ export function RenderList(props) {
|
|
|
104
104
|
const labelField = col.refLabelField ?? "id";
|
|
105
105
|
const refEntity = col.refEntity ?? "";
|
|
106
106
|
// Only a row the user can see is linkable: resolved by the server
|
|
107
|
-
// eagerload or by the lookup query. A multi-reference
|
|
107
|
+
// eagerload or by the lookup query. A multi-reference links per entry via linkItemsFor.
|
|
108
108
|
const linkFor = (value, row) => {
|
|
109
109
|
if (value === null || value === undefined || value === "" || Array.isArray(value)) {
|
|
110
110
|
return undefined;
|
|
@@ -118,6 +118,22 @@ export function RenderList(props) {
|
|
|
118
118
|
map?.has(id);
|
|
119
119
|
return resolved === true ? resolveReferenceLink(refEntity, id) : undefined;
|
|
120
120
|
};
|
|
121
|
+
const linkItemsFor = (value, row) => {
|
|
122
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
123
|
+
return undefined;
|
|
124
|
+
const eagerloaded = eagerloadedRef(row, col.field);
|
|
125
|
+
const eagerById = new Map(Array.isArray(eagerloaded) ? eagerloaded.map((r) => [String(r["id"]), r]) : []);
|
|
126
|
+
return value.map((v) => {
|
|
127
|
+
const id = String(v);
|
|
128
|
+
const target = eagerById.get(id);
|
|
129
|
+
const label = String(target?.[labelField] ?? map?.get(id) ?? id);
|
|
130
|
+
// Only a row the user can see is linkable: server eagerload or lookup query.
|
|
131
|
+
const link = target !== undefined || map?.has(id) === true
|
|
132
|
+
? resolveReferenceLink(refEntity, id)
|
|
133
|
+
: undefined;
|
|
134
|
+
return { id, label, ...(link !== undefined && { link }) };
|
|
135
|
+
});
|
|
136
|
+
};
|
|
121
137
|
const renderer = (value, row) => {
|
|
122
138
|
// Tier 2.7e Server-Eagerload: wenn der Server _refs mit-
|
|
123
139
|
// schickt, lesen wir den Display-Wert direkt aus der
|
|
@@ -142,7 +158,7 @@ export function RenderList(props) {
|
|
|
142
158
|
const idStr = String(value);
|
|
143
159
|
return map?.get(idStr) ?? idStr;
|
|
144
160
|
};
|
|
145
|
-
return { ...col, renderer, linkFor };
|
|
161
|
+
return { ...col, renderer, linkFor, linkItemsFor };
|
|
146
162
|
});
|
|
147
163
|
}, [vm.columns, referenceColumns, referenceLookups, resolveReferenceLink]);
|
|
148
164
|
const enrichedVm = useMemo(() => ({
|
|
@@ -190,7 +206,7 @@ export function RenderList(props) {
|
|
|
190
206
|
// Wenn weder onCreate noch ein Override gegeben ist, fällt die
|
|
191
207
|
// DataTable auf den DataTable-Default zurück (`kumiko.list.no-entries`).
|
|
192
208
|
const composedEmptyState = emptyState ??
|
|
193
|
-
(onCreate !== undefined || emptyContent !== undefined || narrowed ? (_jsx(ListEmptyStateBody, { content: emptyContent, narrowed: narrowed, translate: translate, createButton: onCreate !== undefined && !narrowed ? (_jsx(Button, { variant: "primary", icon: "plus", onClick: onCreate, testId: "render-list-empty-create", children: effectiveCreateLabel })) : undefined })) : undefined);
|
|
209
|
+
(onCreate !== undefined || emptyContent !== undefined || narrowed ? (_jsx(ListEmptyStateBody, { content: emptyContent, narrowed: narrowed, translate: translate, createButton: onCreate !== undefined && !narrowed && !createInPageHeader ? (_jsx(Button, { variant: "primary", icon: "plus", onClick: onCreate, testId: "render-list-empty-create", children: effectiveCreateLabel })) : undefined })) : undefined);
|
|
194
210
|
// Title-Resolution: ein konventioneller i18n-Key `screen:<id>.title`.
|
|
195
211
|
// Wenn das Bundle den Key kennt, schöner Titel; sonst kommt der
|
|
196
212
|
// screenId selber raus (kein "Untitled"-Fallback — der App-Dev sieht
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EditWriteFormSectionViewModel, Translate } from "@cosmicdrift/kumiko-headless";
|
|
2
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import type { EditWriteFormSectionViewModel, Translate } from "../headless/index.ts";
|
|
3
3
|
export type WriteFormSectionProps = {
|
|
4
4
|
readonly section: EditWriteFormSectionViewModel;
|
|
5
5
|
readonly featureName: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { I18N_KEY_PARAM } from "@cosmicdrift/kumiko-headless";
|
|
3
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
3
|
import * as z from "zod";
|
|
5
4
|
import { isPresent, REQUIRED_FIELD_I18N_KEY } from "../app/form-schema.js";
|
|
6
5
|
import { dispatcherErrorText } from "../app/write-failed-error.js";
|
|
6
|
+
import { I18N_KEY_PARAM } from "../headless/index.js";
|
|
7
7
|
import { useForm } from "../hooks/use-form.js";
|
|
8
8
|
import { useTranslation } from "../i18n.js";
|
|
9
9
|
import { STICKY_PRIMARY_ACTION_PROP, usePrimitives } from "../primitives.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Dispatcher, DispatcherStatus } from "@cosmicdrift/kumiko-headless";
|
|
2
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import type { Dispatcher, DispatcherStatus } from "../headless/index.ts";
|
|
3
3
|
import { type QueryPools } from "./query-pool.ts";
|
|
4
4
|
export type DispatcherProviderProps = {
|
|
5
5
|
readonly dispatcher: Dispatcher;
|
package/dist/format-when.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toInstant } from "
|
|
1
|
+
import { toInstant } from "./headless/index.js";
|
|
2
2
|
// Shared timestamp formatter for operator screens (audit log, job runs) —
|
|
3
3
|
// falls back to the raw ISO string on an unparseable value instead of "Invalid Date".
|
|
4
4
|
export function formatWhen(value) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const APEX_NAV_MENU_CSS = "\n .nav-toggle { display: none; }\n .nav-toggle__trigger { display: none; align-items: center; justify-content: center;\n width: 2.25rem; height: 2.25rem; border-radius: 0.5rem; cursor: pointer;\n color: var(--fg-muted); list-style: none; }\n .nav-toggle__trigger::-webkit-details-marker { display: none; }\n .nav-toggle__trigger:hover { color: var(--fg); background: var(--bg-muted); }\n .nav-toggle__trigger svg { width: 1.35rem; height: 1.35rem; }\n .apex-dark .nav-toggle__trigger { color: var(--on-dark-muted); }\n .apex-dark .nav-toggle__trigger:hover { color: var(--on-dark); background: rgba(255,255,255,0.08); }\n\n .nav-menu { position: relative; display: inline-flex; }\n .nav-menu__trigger { display: inline-flex; align-items: center; gap: 0.3rem;\n font: inherit; font-size: 0.9375rem; color: var(--fg-muted);\n cursor: pointer; list-style: none; }\n .nav-menu__trigger::-webkit-details-marker { display: none; }\n .nav-menu__trigger:hover { color: var(--fg); }\n .nav-menu__chev { display: inline-flex; transition: transform 0.15s; opacity: 0.7; }\n .nav-menu__chev svg { width: 0.95em; height: 0.95em; }\n .nav-menu[open] .nav-menu__chev { transform: rotate(180deg); }\n .nav-menu__panel { position: absolute; top: calc(100% + 0.5rem); left: 0; z-index: 20;\n min-width: 21rem; padding: 0.5rem; background: var(--bg-card); color: var(--fg);\n border: 1px solid var(--border); border-radius: 0.75rem; box-shadow: var(--shadow);\n flex-direction: column; gap: 0.125rem; }\n .nav-menu[open] .nav-menu__panel { display: flex; }\n .nav-menu__item { display: flex; gap: 0.75rem; align-items: flex-start;\n padding: 0.6rem 0.7rem; border-radius: 0.5rem; color: var(--fg); }\n .nav-menu__item:hover { background: var(--bg-muted); color: var(--fg); }\n .nav-menu__icon { flex: none; display: inline-flex; align-items: center; justify-content: center;\n width: 2rem; height: 2rem; border-radius: 0.5rem;\n background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary); }\n .nav-menu__icon svg { width: 1.1rem; height: 1.1rem; }\n .nav-menu__text { display: flex; flex-direction: column; gap: 0.1rem; }\n .nav-menu__title { font-weight: 600; font-size: 0.9rem; line-height: 1.2; }\n .nav-menu__desc { font-size: 0.8rem; color: var(--fg-muted); line-height: 1.35; }\n .nav-menu__sep { height: 1px; background: var(--border); margin: 0.375rem 0.3rem; }\n .nav-menu__more { display: inline-flex; padding: 0.4rem 0.7rem; font-size: 0.85rem; font-weight: 600; color: var(--primary); }\n .nav-menu__more:hover { color: var(--primary-hover); }\n .apex-dark .nav-menu__trigger { color: var(--on-dark-muted); }\n .apex-dark .nav-menu__trigger:hover { color: var(--on-dark); }\n /* The panel is a light popover; its links must NOT inherit the dark-chrome\n nav-link color (.apex-dark .nav-links a, specificity 0,2,1) or a consumer's\n own .nav-links a. Pin both link types with matching specificity (0,2,1) so\n titles + the footer link stay readable in the dark theme. */\n .nav-menu__panel a.nav-menu__item { color: var(--fg); }\n .nav-menu__panel a.nav-menu__item:hover { color: var(--fg); }\n .nav-menu__panel a.nav-menu__more { color: var(--primary); }\n .nav-menu__panel a.nav-menu__more:hover { color: var(--primary-hover); }\n";
|
|
2
|
+
export declare const APEX_NAV_TOGGLE_RESPONSIVE_CSS = "\n @media (max-width: 640px) {\n .nav { position: relative; }\n .nav .nav-toggle { display: inline-flex; }\n .nav .nav-toggle__trigger { display: inline-flex; }\n .nav-toggle[open] > .nav-links { display: flex; flex-direction: column; align-items: stretch;\n gap: 0.25rem; position: absolute; top: calc(100% + 0.5rem); left: 0; right: 0; z-index: 30;\n background: var(--bg-card); border: 1px solid var(--border); border-radius: 0.75rem;\n box-shadow: var(--shadow); padding: 0.75rem; }\n .nav-toggle[open] > .nav-links > a { color: var(--fg); padding: 0.4rem 0.25rem; }\n .nav-toggle[open] > .nav-links > a:hover { color: var(--fg); }\n .nav-toggle .nav-menu { display: block; }\n .nav-toggle .nav-menu__panel { position: static; min-width: 0;\n margin-top: 0.375rem; box-shadow: none; }\n }\n";
|
|
3
|
+
export declare const APEX_STRUCTURAL_CSS: string;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Structural CSS for the Apex marketing surface — shared by every app's static
|
|
2
|
+
// landing. Brand TOKEN VALUES (:root custom properties) and @font-face stay
|
|
3
|
+
// app-side (passed as brand.tokensCss / brand.fontFaceCss); this file owns only
|
|
4
|
+
// the structure: layout, grids, cards, buttons, header/footer, responsive.
|
|
5
|
+
//
|
|
6
|
+
// Two themes ship together; the <body class> selects one (see renderApexPage):
|
|
7
|
+
// - light (default): chrome on light backgrounds — translucent blurred header.
|
|
8
|
+
// - .apex-dark: header/hero/final-cta/footer sit on --primary with --on-dark
|
|
9
|
+
// text + inverted buttons (the "dark sandwich"). Requires the app's tokens
|
|
10
|
+
// to define --on-dark / --on-dark-muted / --on-dark-border.
|
|
11
|
+
//
|
|
12
|
+
// Token divergence is absorbed by CSS fallbacks, not parameters:
|
|
13
|
+
// --font-mono unset → .price-amount stays in body font (var(--font-mono, inherit))
|
|
14
|
+
// --accent-hover unset → eyebrow falls back to --primary
|
|
15
|
+
// --hero-tagline-max unset → hero tagline runs full column (var(--hero-tagline-max, none))
|
|
16
|
+
// icon tint → color-mix derives a faint --primary wash, no hardcoded rgba
|
|
17
|
+
const BASE_LAYOUT = `
|
|
18
|
+
* { box-sizing: border-box; }
|
|
19
|
+
html, body { margin: 0; padding: 0; }
|
|
20
|
+
body {
|
|
21
|
+
font-family: var(--font-body, "Inter Variable", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
|
|
22
|
+
background: var(--bg); color: var(--fg); line-height: 1.6;
|
|
23
|
+
-webkit-font-smoothing: antialiased;
|
|
24
|
+
min-height: 100vh; display: flex; flex-direction: column;
|
|
25
|
+
}
|
|
26
|
+
main { flex: 1; }
|
|
27
|
+
a { color: var(--primary); text-decoration: none; }
|
|
28
|
+
a:hover { color: var(--primary-hover); }
|
|
29
|
+
h1, h2, h3 { letter-spacing: -0.02em; }
|
|
30
|
+
.container { max-width: 1120px; margin: 0 auto; padding: 0 1.5rem; width: 100%; }
|
|
31
|
+
.container-narrow { max-width: 760px; margin: 0 auto; padding: 0 1.5rem; width: 100%; }
|
|
32
|
+
.btn {
|
|
33
|
+
display: inline-block; padding: 0.6rem 1.15rem; border-radius: 0.5rem;
|
|
34
|
+
font-weight: 600; font-size: 0.9375rem; border: 1px solid transparent;
|
|
35
|
+
cursor: pointer; transition: background 0.15s, border-color 0.15s, transform 0.05s;
|
|
36
|
+
}
|
|
37
|
+
.btn:active { transform: translateY(1px); }
|
|
38
|
+
.btn-primary { background: var(--primary); color: var(--primary-fg); }
|
|
39
|
+
.btn-primary:hover { background: var(--primary-hover); color: var(--primary-fg); }
|
|
40
|
+
.btn-secondary { background: var(--bg-card); color: var(--fg); border-color: var(--border); }
|
|
41
|
+
.btn-secondary:hover { border-color: var(--fg-muted); color: var(--fg); }
|
|
42
|
+
|
|
43
|
+
section { padding: 5rem 0; }
|
|
44
|
+
.section-head { text-align: center; max-width: 640px; margin: 0 auto 3rem; }
|
|
45
|
+
.section-head h2 { font-size: clamp(1.6rem, 3.5vw, 2.25rem); margin: 0 0 0.75rem; }
|
|
46
|
+
.section-head p { color: var(--fg-muted); margin: 0; font-size: 1.0625rem; }
|
|
47
|
+
.eyebrow { display: inline-block; font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.04em;
|
|
48
|
+
text-transform: uppercase; color: var(--accent-hover, var(--primary)); margin-bottom: 0.75rem; }
|
|
49
|
+
`;
|
|
50
|
+
const HERO = `
|
|
51
|
+
.hero { padding: 4.5rem 0 4rem; }
|
|
52
|
+
.hero-grid { display: grid; grid-template-columns: 1.05fr 1fr; gap: 3.5rem; align-items: center; }
|
|
53
|
+
.hero-pony { width: 88px; height: 88px; margin: 0 0 1.1rem; display: block; }
|
|
54
|
+
.hero h1 { font-size: clamp(2.1rem, 4.5vw, 3.4rem); line-height: 1.08; margin: 0 0 1.1rem; font-weight: 700; }
|
|
55
|
+
.hero .tagline { font-size: clamp(1.05rem, 1.6vw, 1.25rem); color: var(--fg-muted); margin: 0 0 1.75rem; max-width: var(--hero-tagline-max, none); }
|
|
56
|
+
.hero-cta { display: flex; gap: 0.75rem; flex-wrap: wrap; }
|
|
57
|
+
.hero-meta { margin-top: 1.5rem; font-size: 0.875rem; color: var(--fg-subtle); }
|
|
58
|
+
.hero-meta strong { color: var(--fg-muted); font-weight: 600; }
|
|
59
|
+
.shot-frame { border-radius: 0.75rem; border: 1px solid var(--border); background: var(--bg-card);
|
|
60
|
+
box-shadow: var(--shadow); overflow: hidden; cursor: zoom-in; }
|
|
61
|
+
.shot-bar { display: flex; gap: 0.4rem; padding: 0.6rem 0.85rem; border-bottom: 1px solid var(--border); background: var(--bg-muted); }
|
|
62
|
+
.shot-bar span { width: 0.65rem; height: 0.65rem; border-radius: 50%; background: var(--border); }
|
|
63
|
+
.shot-frame img { display: block; width: 100%; height: auto; }
|
|
64
|
+
.apex-lightbox { border: none; padding: 0; margin: auto; max-width: 95vw; max-height: 90vh;
|
|
65
|
+
background: transparent; overflow: visible; }
|
|
66
|
+
.apex-lightbox::backdrop { background: rgba(15, 23, 42, 0.72); }
|
|
67
|
+
.apex-lightbox__img { display: block; max-width: 90vw; max-height: 85vh; width: auto; height: auto;
|
|
68
|
+
border-radius: 0.75rem; border: 1px solid var(--border); box-shadow: var(--shadow); }
|
|
69
|
+
.apex-lightbox__close { position: fixed; top: 1rem; right: 1rem; z-index: 1; width: 2.5rem; height: 2.5rem;
|
|
70
|
+
border: 1px solid var(--border); border-radius: 0.5rem; background: var(--bg-card); color: var(--fg);
|
|
71
|
+
font-size: 1.5rem; line-height: 1; cursor: pointer; }
|
|
72
|
+
.apex-lightbox__close:hover { background: var(--bg-muted); }
|
|
73
|
+
.apex-lightbox__prev, .apex-lightbox__next { position: fixed; top: 50%; transform: translateY(-50%); z-index: 1;
|
|
74
|
+
width: 2.5rem; height: 2.5rem; border: 1px solid var(--border); border-radius: 0.5rem;
|
|
75
|
+
background: var(--bg-card); color: var(--fg); font-size: 1.75rem; line-height: 1; cursor: pointer; }
|
|
76
|
+
.apex-lightbox__prev { left: 1rem; }
|
|
77
|
+
.apex-lightbox__next { right: 1rem; }
|
|
78
|
+
.apex-lightbox__prev:hover, .apex-lightbox__next:hover { background: var(--bg-muted); }
|
|
79
|
+
.apex-lightbox__prev[hidden], .apex-lightbox__next[hidden] { display: none; }
|
|
80
|
+
.apex-dark .apex-lightbox__prev, .apex-dark .apex-lightbox__next { background: var(--on-dark); color: var(--primary); border-color: var(--on-dark-border); }
|
|
81
|
+
.apex-dark .apex-lightbox__close { background: var(--on-dark); color: var(--primary); border-color: var(--on-dark-border); }
|
|
82
|
+
`;
|
|
83
|
+
const FEATURES = `
|
|
84
|
+
.features { background: var(--bg-muted); }
|
|
85
|
+
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
|
|
86
|
+
.feature { background: var(--bg-card); border: 1px solid var(--border); border-radius: 0.75rem; padding: 1.75rem; }
|
|
87
|
+
.feature-icon { width: 2.5rem; height: 2.5rem; border-radius: 0.6rem; display: flex; align-items: center;
|
|
88
|
+
justify-content: center; background: color-mix(in srgb, var(--primary) 9%, transparent); color: var(--primary); margin-bottom: 1rem; }
|
|
89
|
+
.feature-icon svg { width: 1.35rem; height: 1.35rem; }
|
|
90
|
+
.feature h3 { font-size: 1.0625rem; margin: 0 0 0.4rem; }
|
|
91
|
+
.feature p { color: var(--fg-muted); margin: 0; font-size: 0.9375rem; }
|
|
92
|
+
`;
|
|
93
|
+
const PRICING = `
|
|
94
|
+
.price-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; align-items: stretch; }
|
|
95
|
+
.price-card { position: relative; background: var(--bg-card); border: 1px solid var(--border);
|
|
96
|
+
border-radius: 0.75rem; padding: 1.75rem 1.5rem; display: flex; flex-direction: column; gap: 0.85rem; }
|
|
97
|
+
.price-card--featured { border-color: var(--primary); box-shadow: var(--shadow); }
|
|
98
|
+
.price-badge { position: absolute; top: -0.7rem; left: 50%; transform: translateX(-50%);
|
|
99
|
+
background: var(--accent, var(--primary)); color: var(--accent-fg, var(--primary-fg)); font-size: 0.75rem; font-weight: 700;
|
|
100
|
+
padding: 0.2rem 0.7rem; border-radius: 999px; white-space: nowrap; }
|
|
101
|
+
.price-card h3 { margin: 0; font-size: 1.25rem; }
|
|
102
|
+
.price-tagline { margin: 0; color: var(--fg-subtle); font-size: 0.875rem; }
|
|
103
|
+
.price-amount { font-size: 2rem; font-weight: 700; font-family: var(--font-mono, inherit); }
|
|
104
|
+
.price-amount span { font-size: 0.9375rem; font-weight: 500; color: var(--fg-subtle); font-family: inherit; }
|
|
105
|
+
.price-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.55rem; flex: 1; }
|
|
106
|
+
.price-list li { position: relative; padding-left: 1.5rem; font-size: 0.9rem; color: var(--fg-muted); }
|
|
107
|
+
.price-list li::before { content: ""; position: absolute; left: 0; top: 0.45rem; width: 0.7rem; height: 0.4rem;
|
|
108
|
+
border-left: 2px solid var(--status-ok); border-bottom: 2px solid var(--status-ok); transform: rotate(-45deg); }
|
|
109
|
+
.price-list li.price-cap { font-weight: 600; color: var(--fg); }
|
|
110
|
+
.price-card .btn { text-align: center; margin-top: 0.5rem; }
|
|
111
|
+
`;
|
|
112
|
+
const TRUST = `
|
|
113
|
+
.trust-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; }
|
|
114
|
+
.trust-item h3 { font-size: 1.0625rem; margin: 0 0 0.4rem; }
|
|
115
|
+
.trust-item p { color: var(--fg-muted); margin: 0; font-size: 0.9375rem; }
|
|
116
|
+
`;
|
|
117
|
+
const FINAL_CTA = `
|
|
118
|
+
.final-cta { text-align: center; }
|
|
119
|
+
.final-cta-pony { width: 96px; height: auto; display: block; margin: 0 auto 1rem; }
|
|
120
|
+
.final-cta h2 { font-size: clamp(1.6rem, 3.5vw, 2.25rem); margin: 0 0 0.75rem; }
|
|
121
|
+
.final-cta p { color: var(--fg-muted); margin: 0 0 1.75rem; font-size: 1.0625rem; }
|
|
122
|
+
`;
|
|
123
|
+
// Light chrome (default). Header floats translucent over the page; footer is a
|
|
124
|
+
// muted band. Dark theme overrides both below.
|
|
125
|
+
const CHROME_LIGHT = `
|
|
126
|
+
header { position: sticky; top: 0; z-index: 10;
|
|
127
|
+
background: color-mix(in srgb, var(--bg) 85%, transparent);
|
|
128
|
+
backdrop-filter: saturate(140%) blur(8px);
|
|
129
|
+
border-bottom: 1px solid var(--border); }
|
|
130
|
+
.nav { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-block: 0.85rem; }
|
|
131
|
+
.brand { display: flex; align-items: center; gap: 0.55rem; font-weight: 700; font-size: 1.125rem; color: var(--fg); }
|
|
132
|
+
.brand a { color: var(--fg); display: inline-flex; align-items: center; gap: 0.55rem; }
|
|
133
|
+
.brand a:hover { color: var(--fg); }
|
|
134
|
+
.brand img { width: 1.7rem; height: 1.7rem; border-radius: 0.4rem; }
|
|
135
|
+
.nav-links { display: flex; gap: 1.5rem; align-items: center; font-size: 0.9375rem; }
|
|
136
|
+
.nav-links a { color: var(--fg-muted); }
|
|
137
|
+
.nav-links a:hover { color: var(--fg); }
|
|
138
|
+
.nav-actions { display: flex; gap: 0.75rem; align-items: center; font-size: 0.9375rem; }
|
|
139
|
+
.nav-actions > a:not(.btn) { color: var(--fg-muted); }
|
|
140
|
+
.nav-actions > a:not(.btn):hover { color: var(--fg); }
|
|
141
|
+
@media (max-width: 640px) { .nav-links { display: none; } }
|
|
142
|
+
|
|
143
|
+
footer { border-top: 1px solid var(--border); color: var(--fg-subtle); padding: 3.5rem 0 2.5rem; font-size: 0.9rem; }
|
|
144
|
+
.footer-grid { display: grid; gap: 2rem; grid-template-columns: 1.6fr repeat(var(--footer-cols, 2), 1fr); }
|
|
145
|
+
.footer-brand { display: flex; align-items: center; gap: 0.55rem; font-weight: 700; color: var(--fg); margin-bottom: 0.75rem; }
|
|
146
|
+
.footer-brand img { width: 1.6rem; height: 1.6rem; border-radius: 0.4rem; }
|
|
147
|
+
.footer-tagline { color: var(--fg-subtle); max-width: 34ch; margin: 0; }
|
|
148
|
+
.footer-col h4 { font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--fg-subtle); margin: 0 0 0.85rem; }
|
|
149
|
+
.footer-col a { display: block; color: var(--fg-muted); margin-bottom: 0.5rem; }
|
|
150
|
+
.footer-col a:hover { color: var(--fg); }
|
|
151
|
+
.footer-bottom { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border);
|
|
152
|
+
display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.75rem; }
|
|
153
|
+
`;
|
|
154
|
+
// Dark sandwich: header/hero/final-cta/footer ride on --primary. Buttons invert
|
|
155
|
+
// (a --primary button on --primary would vanish) → light button, --primary text.
|
|
156
|
+
const CHROME_DARK = `
|
|
157
|
+
.apex-dark header { background: var(--primary); backdrop-filter: none; border-bottom: 1px solid var(--on-dark-border); }
|
|
158
|
+
.apex-dark .brand, .apex-dark .brand a, .apex-dark .brand a:hover { color: var(--on-dark); }
|
|
159
|
+
.apex-dark .nav-links a { color: var(--on-dark-muted); }
|
|
160
|
+
.apex-dark .nav-links a:hover { color: var(--on-dark); }
|
|
161
|
+
.apex-dark .nav-actions > a:not(.btn) { color: var(--on-dark-muted); }
|
|
162
|
+
.apex-dark .nav-actions > a:not(.btn):hover { color: var(--on-dark); }
|
|
163
|
+
.apex-dark header .btn-primary { background: var(--on-dark); color: var(--primary); }
|
|
164
|
+
.apex-dark header .btn-primary:hover { background: rgba(255,255,255,0.85); color: var(--primary-hover); }
|
|
165
|
+
|
|
166
|
+
.apex-dark .hero { background: var(--primary); color: var(--on-dark); }
|
|
167
|
+
.apex-dark .hero h1 { color: var(--on-dark); }
|
|
168
|
+
.apex-dark .hero .tagline { color: var(--on-dark-muted); }
|
|
169
|
+
.apex-dark .hero .btn-primary, .apex-dark .final-cta .btn-primary { background: var(--on-dark); color: var(--primary); }
|
|
170
|
+
.apex-dark .hero .btn-primary:hover, .apex-dark .final-cta .btn-primary:hover { background: rgba(255,255,255,0.85); color: var(--primary-hover); }
|
|
171
|
+
.apex-dark .hero .btn-secondary { background: rgba(255,255,255,0.08); color: var(--on-dark); border-color: rgba(255,255,255,0.30); }
|
|
172
|
+
.apex-dark .hero .btn-secondary:hover { background: rgba(255,255,255,0.16); color: var(--on-dark); border-color: rgba(255,255,255,0.5); }
|
|
173
|
+
.apex-dark .hero-meta { color: rgba(255,255,255,0.55); }
|
|
174
|
+
.apex-dark .hero-meta strong { color: var(--on-dark-muted); }
|
|
175
|
+
|
|
176
|
+
.apex-dark .final-cta { background: var(--primary); color: var(--on-dark); }
|
|
177
|
+
.apex-dark .final-cta h2 { color: var(--on-dark); }
|
|
178
|
+
.apex-dark .final-cta p { color: var(--on-dark-muted); }
|
|
179
|
+
|
|
180
|
+
.apex-dark footer { background: var(--primary); color: var(--on-dark-muted); border-top: none; }
|
|
181
|
+
.apex-dark .footer-brand { color: var(--on-dark); }
|
|
182
|
+
.apex-dark .footer-tagline { color: var(--on-dark-muted); }
|
|
183
|
+
.apex-dark .footer-col h4 { color: var(--on-dark-muted); }
|
|
184
|
+
.apex-dark .footer-col a { color: var(--on-dark-muted); }
|
|
185
|
+
.apex-dark .footer-col a:hover { color: var(--on-dark); }
|
|
186
|
+
.apex-dark .footer-bottom { border-top: 1px solid var(--on-dark-border); }
|
|
187
|
+
`;
|
|
188
|
+
const RESPONSIVE = `
|
|
189
|
+
@media (max-width: 900px) {
|
|
190
|
+
.hero-grid { grid-template-columns: 1fr; gap: 2.5rem; }
|
|
191
|
+
.feature-grid { grid-template-columns: repeat(2, 1fr); }
|
|
192
|
+
.price-grid { grid-template-columns: repeat(2, 1fr); }
|
|
193
|
+
.footer-grid { grid-template-columns: 1fr 1fr; }
|
|
194
|
+
}
|
|
195
|
+
@media (max-width: 640px) {
|
|
196
|
+
section { padding: 3.5rem 0; }
|
|
197
|
+
.feature-grid, .price-grid, .trust-grid, .footer-grid { grid-template-columns: 1fr; }
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
// header renders navLinks TWICE (renderApexHeader): a plain always-visible
|
|
201
|
+
// .nav-links row, plus a copy nested in .nav-toggle for < 640px — a closed
|
|
202
|
+
// <details> can't be forced "always open" on desktop via CSS (Chrome keeps
|
|
203
|
+
// its content unpainted regardless of a child's own `display`), so two
|
|
204
|
+
// copies sidestep that instead of fighting it. Exported standalone so a
|
|
205
|
+
// consumer rendering its own header chrome can include just this. A
|
|
206
|
+
// consumer using renderApexHeader() outside renderApexPage also needs
|
|
207
|
+
// APEX_NAV_TOGGLE_RESPONSIVE_CSS below for the mobile toggle to open.
|
|
208
|
+
export const APEX_NAV_MENU_CSS = `
|
|
209
|
+
.nav-toggle { display: none; }
|
|
210
|
+
.nav-toggle__trigger { display: none; align-items: center; justify-content: center;
|
|
211
|
+
width: 2.25rem; height: 2.25rem; border-radius: 0.5rem; cursor: pointer;
|
|
212
|
+
color: var(--fg-muted); list-style: none; }
|
|
213
|
+
.nav-toggle__trigger::-webkit-details-marker { display: none; }
|
|
214
|
+
.nav-toggle__trigger:hover { color: var(--fg); background: var(--bg-muted); }
|
|
215
|
+
.nav-toggle__trigger svg { width: 1.35rem; height: 1.35rem; }
|
|
216
|
+
.apex-dark .nav-toggle__trigger { color: var(--on-dark-muted); }
|
|
217
|
+
.apex-dark .nav-toggle__trigger:hover { color: var(--on-dark); background: rgba(255,255,255,0.08); }
|
|
218
|
+
|
|
219
|
+
.nav-menu { position: relative; display: inline-flex; }
|
|
220
|
+
.nav-menu__trigger { display: inline-flex; align-items: center; gap: 0.3rem;
|
|
221
|
+
font: inherit; font-size: 0.9375rem; color: var(--fg-muted);
|
|
222
|
+
cursor: pointer; list-style: none; }
|
|
223
|
+
.nav-menu__trigger::-webkit-details-marker { display: none; }
|
|
224
|
+
.nav-menu__trigger:hover { color: var(--fg); }
|
|
225
|
+
.nav-menu__chev { display: inline-flex; transition: transform 0.15s; opacity: 0.7; }
|
|
226
|
+
.nav-menu__chev svg { width: 0.95em; height: 0.95em; }
|
|
227
|
+
.nav-menu[open] .nav-menu__chev { transform: rotate(180deg); }
|
|
228
|
+
.nav-menu__panel { position: absolute; top: calc(100% + 0.5rem); left: 0; z-index: 20;
|
|
229
|
+
min-width: 21rem; padding: 0.5rem; background: var(--bg-card); color: var(--fg);
|
|
230
|
+
border: 1px solid var(--border); border-radius: 0.75rem; box-shadow: var(--shadow);
|
|
231
|
+
flex-direction: column; gap: 0.125rem; }
|
|
232
|
+
.nav-menu[open] .nav-menu__panel { display: flex; }
|
|
233
|
+
.nav-menu__item { display: flex; gap: 0.75rem; align-items: flex-start;
|
|
234
|
+
padding: 0.6rem 0.7rem; border-radius: 0.5rem; color: var(--fg); }
|
|
235
|
+
.nav-menu__item:hover { background: var(--bg-muted); color: var(--fg); }
|
|
236
|
+
.nav-menu__icon { flex: none; display: inline-flex; align-items: center; justify-content: center;
|
|
237
|
+
width: 2rem; height: 2rem; border-radius: 0.5rem;
|
|
238
|
+
background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary); }
|
|
239
|
+
.nav-menu__icon svg { width: 1.1rem; height: 1.1rem; }
|
|
240
|
+
.nav-menu__text { display: flex; flex-direction: column; gap: 0.1rem; }
|
|
241
|
+
.nav-menu__title { font-weight: 600; font-size: 0.9rem; line-height: 1.2; }
|
|
242
|
+
.nav-menu__desc { font-size: 0.8rem; color: var(--fg-muted); line-height: 1.35; }
|
|
243
|
+
.nav-menu__sep { height: 1px; background: var(--border); margin: 0.375rem 0.3rem; }
|
|
244
|
+
.nav-menu__more { display: inline-flex; padding: 0.4rem 0.7rem; font-size: 0.85rem; font-weight: 600; color: var(--primary); }
|
|
245
|
+
.nav-menu__more:hover { color: var(--primary-hover); }
|
|
246
|
+
.apex-dark .nav-menu__trigger { color: var(--on-dark-muted); }
|
|
247
|
+
.apex-dark .nav-menu__trigger:hover { color: var(--on-dark); }
|
|
248
|
+
/* The panel is a light popover; its links must NOT inherit the dark-chrome
|
|
249
|
+
nav-link color (.apex-dark .nav-links a, specificity 0,2,1) or a consumer's
|
|
250
|
+
own .nav-links a. Pin both link types with matching specificity (0,2,1) so
|
|
251
|
+
titles + the footer link stay readable in the dark theme. */
|
|
252
|
+
.nav-menu__panel a.nav-menu__item { color: var(--fg); }
|
|
253
|
+
.nav-menu__panel a.nav-menu__item:hover { color: var(--fg); }
|
|
254
|
+
.nav-menu__panel a.nav-menu__more { color: var(--primary); }
|
|
255
|
+
.nav-menu__panel a.nav-menu__more:hover { color: var(--primary-hover); }
|
|
256
|
+
`;
|
|
257
|
+
// Standalone mobile-open rules for .nav-toggle, split out of RESPONSIVE so a
|
|
258
|
+
// consumer calling renderApexHeader() outside renderApexPage (money-horse,
|
|
259
|
+
// show-pony, the docs sample) can include just this alongside
|
|
260
|
+
// APEX_NAV_MENU_CSS, without pulling in the rest of RESPONSIVE's page-level
|
|
261
|
+
// grid breakpoints. The two "show" rules use the `.nav ` prefix (matching
|
|
262
|
+
// renderApexHeader's `<div class="container nav">` wrapper) to raise their
|
|
263
|
+
// specificity above APEX_NAV_MENU_CSS's bare `.nav-toggle`/`.nav-toggle__trigger`
|
|
264
|
+
// "display: none" defaults — that makes them win regardless of concatenation
|
|
265
|
+
// order, since a standalone consumer controls the order, not this file.
|
|
266
|
+
// Also declares `.nav { position: relative; }`: the open dropdown positions
|
|
267
|
+
// itself against .nav (`top/left/right` below), and renderApexPage's own
|
|
268
|
+
// CHROME_LIGHT already sets that, but a standalone consumer's own header CSS
|
|
269
|
+
// generally doesn't — without it the dropdown would anchor to the viewport
|
|
270
|
+
// instead of the header and open off-screen. Redeclaring it here is a no-op
|
|
271
|
+
// inside APEX_STRUCTURAL_CSS (same value, harmless if repeated).
|
|
272
|
+
export const APEX_NAV_TOGGLE_RESPONSIVE_CSS = `
|
|
273
|
+
@media (max-width: 640px) {
|
|
274
|
+
.nav { position: relative; }
|
|
275
|
+
.nav .nav-toggle { display: inline-flex; }
|
|
276
|
+
.nav .nav-toggle__trigger { display: inline-flex; }
|
|
277
|
+
.nav-toggle[open] > .nav-links { display: flex; flex-direction: column; align-items: stretch;
|
|
278
|
+
gap: 0.25rem; position: absolute; top: calc(100% + 0.5rem); left: 0; right: 0; z-index: 30;
|
|
279
|
+
background: var(--bg-card); border: 1px solid var(--border); border-radius: 0.75rem;
|
|
280
|
+
box-shadow: var(--shadow); padding: 0.75rem; }
|
|
281
|
+
.nav-toggle[open] > .nav-links > a { color: var(--fg); padding: 0.4rem 0.25rem; }
|
|
282
|
+
.nav-toggle[open] > .nav-links > a:hover { color: var(--fg); }
|
|
283
|
+
.nav-toggle .nav-menu { display: block; }
|
|
284
|
+
.nav-toggle .nav-menu__panel { position: static; min-width: 0;
|
|
285
|
+
margin-top: 0.375rem; box-shadow: none; }
|
|
286
|
+
}
|
|
287
|
+
`;
|
|
288
|
+
export const APEX_STRUCTURAL_CSS = BASE_LAYOUT +
|
|
289
|
+
HERO +
|
|
290
|
+
FEATURES +
|
|
291
|
+
PRICING +
|
|
292
|
+
TRUST +
|
|
293
|
+
FINAL_CTA +
|
|
294
|
+
CHROME_LIGHT +
|
|
295
|
+
CHROME_DARK +
|
|
296
|
+
APEX_NAV_MENU_CSS +
|
|
297
|
+
APEX_NAV_TOGGLE_RESPONSIVE_CSS +
|
|
298
|
+
RESPONSIVE;
|