@cosmicdrift/kumiko-renderer 0.388.0 → 0.390.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/dist/app/base-path-context.d.ts +9 -0
- package/dist/app/base-path-context.js +13 -0
- package/dist/app/base-path.d.ts +3 -0
- package/dist/app/base-path.js +27 -0
- package/dist/app/config-edit-body.d.ts +1 -1
- package/dist/app/config-edit-body.js +15 -6
- package/dist/app/config-edit-without-secret-fields.d.ts +2 -0
- package/dist/app/config-edit-without-secret-fields.js +28 -0
- package/dist/app/entity-list-facet-specs.d.ts +3 -0
- package/dist/app/entity-list-facet-specs.js +65 -0
- package/dist/app/entity-list-screen.js +74 -206
- package/dist/app/kumiko-screen.js +6 -14
- package/dist/app/list-bulk-actions.d.ts +18 -0
- package/dist/app/list-bulk-actions.js +54 -0
- package/dist/app/locked-screen-header.d.ts +9 -0
- package/dist/app/locked-screen-header.js +33 -0
- package/dist/app/projection-detail-body.js +59 -394
- package/dist/app/projection-detail-header-model.d.ts +11 -0
- package/dist/app/projection-detail-header-model.js +33 -0
- package/dist/app/projection-detail-header-region.d.ts +28 -0
- package/dist/app/projection-detail-header-region.js +81 -0
- package/dist/app/projection-detail-header.d.ts +29 -0
- package/dist/app/projection-detail-header.js +84 -0
- package/dist/app/projection-detail-navigation.d.ts +13 -0
- package/dist/app/projection-detail-navigation.js +91 -0
- package/dist/app/projection-detail-states.d.ts +22 -0
- package/dist/app/projection-detail-states.js +26 -0
- package/dist/app/projection-drawer-host.d.ts +12 -0
- package/dist/app/projection-drawer-host.js +10 -0
- package/dist/app/projection-list-body.d.ts +1 -1
- package/dist/app/projection-list-body.js +82 -197
- package/dist/app/projection-list-render-props.d.ts +56 -0
- package/dist/app/projection-list-render-props.js +47 -0
- package/dist/app/projection-list-render.d.ts +26 -0
- package/dist/app/projection-list-render.js +20 -0
- package/dist/app/projection-list-row-click.d.ts +12 -0
- package/dist/app/projection-list-row-click.js +14 -0
- package/dist/app/row-actions.d.ts +3 -0
- package/dist/app/row-actions.js +10 -5
- package/dist/app/screen-shared.js +9 -2
- package/dist/app/use-entity-range-facets.d.ts +24 -0
- package/dist/app/use-entity-range-facets.js +64 -0
- package/dist/app/use-projection-date-range.d.ts +12 -0
- package/dist/app/use-projection-date-range.js +21 -0
- package/dist/app/use-projection-detail-actions.d.ts +35 -0
- package/dist/app/use-projection-detail-actions.js +156 -0
- package/dist/app/use-projection-detail-data.d.ts +19 -0
- package/dist/app/use-projection-detail-data.js +47 -0
- package/dist/app/use-projection-detail-layout.d.ts +17 -0
- package/dist/app/use-projection-detail-layout.js +23 -0
- package/dist/app/use-projection-list-actions.d.ts +31 -0
- package/dist/app/use-projection-list-actions.js +73 -0
- package/dist/app/use-projection-list-data.d.ts +20 -0
- package/dist/app/use-projection-list-data.js +26 -0
- package/dist/app/use-projection-list-definitions.d.ts +11 -0
- package/dist/app/use-projection-list-definitions.js +20 -0
- package/dist/app/use-projection-list-query.d.ts +28 -0
- package/dist/app/use-projection-list-query.js +83 -0
- package/dist/app/use-screen-list-rows.d.ts +37 -0
- package/dist/app/use-screen-list-rows.js +104 -0
- package/dist/app/use-screen-list-table.d.ts +24 -0
- package/dist/app/use-screen-list-table.js +46 -0
- package/dist/components/list-group-page-bridges.d.ts +3 -0
- package/dist/components/list-group-page-bridges.js +31 -0
- package/dist/components/list-state-store.d.ts +16 -0
- package/dist/components/list-state-store.js +31 -0
- package/dist/components/related-list-section.js +37 -89
- package/dist/components/render-edit-banners.d.ts +12 -0
- package/dist/components/render-edit-banners.js +90 -0
- package/dist/components/render-edit-draft-save-helpers.d.ts +19 -0
- package/dist/components/render-edit-draft-save-helpers.js +42 -0
- package/dist/components/render-edit-field-grid.d.ts +4 -0
- package/dist/components/render-edit-field-grid.js +40 -0
- package/dist/components/render-edit-fields-section.d.ts +4 -0
- package/dist/components/render-edit-fields-section.js +76 -0
- package/dist/components/render-edit-form-actions.d.ts +4 -0
- package/dist/components/render-edit-form-actions.js +66 -0
- package/dist/components/render-edit-header-actions.d.ts +11 -0
- package/dist/components/render-edit-header-actions.js +120 -0
- package/dist/components/render-edit-layout-flags.d.ts +29 -0
- package/dist/components/render-edit-layout-flags.js +106 -0
- package/dist/components/render-edit-layout.d.ts +4 -0
- package/dist/components/render-edit-layout.js +79 -0
- package/dist/components/render-edit-render-model.d.ts +63 -0
- package/dist/components/render-edit-render-model.js +14 -0
- package/dist/components/render-edit-section-kinds.d.ts +4 -0
- package/dist/components/render-edit-section-kinds.js +115 -0
- package/dist/components/render-edit-submit-result.d.ts +8 -0
- package/dist/components/render-edit-submit-result.js +77 -0
- package/dist/components/render-edit-submit.d.ts +7 -0
- package/dist/components/render-edit-submit.js +113 -0
- package/dist/components/render-edit-titles.d.ts +17 -0
- package/dist/components/render-edit-titles.js +99 -0
- package/dist/components/render-edit-wizard-navigation.d.ts +11 -0
- package/dist/components/render-edit-wizard-navigation.js +115 -0
- package/dist/components/render-edit.d.ts +2 -2
- package/dist/components/render-edit.js +63 -1128
- package/dist/components/render-field-choice-inputs.d.ts +7 -0
- package/dist/components/render-field-choice-inputs.js +52 -0
- package/dist/components/render-field-control.d.ts +12 -0
- package/dist/components/render-field-control.js +93 -0
- package/dist/components/render-field-display-text.d.ts +4 -0
- package/dist/components/render-field-display-text.js +48 -0
- package/dist/components/render-field-fill-action.d.ts +13 -0
- package/dist/components/render-field-fill-action.js +63 -0
- package/dist/components/render-field-input-shared.d.ts +39 -0
- package/dist/components/render-field-input-shared.js +20 -0
- package/dist/components/render-field-input.d.ts +3 -0
- package/dist/components/render-field-input.js +34 -0
- package/dist/components/render-field-meta.d.ts +6 -0
- package/dist/components/render-field-meta.js +51 -0
- package/dist/components/render-field-read-only.d.ts +11 -0
- package/dist/components/render-field-read-only.js +75 -0
- package/dist/components/render-field-reference.d.ts +15 -0
- package/dist/components/render-field-reference.js +103 -0
- package/dist/components/render-field-scalar-inputs.d.ts +10 -0
- package/dist/components/render-field-scalar-inputs.js +35 -0
- package/dist/components/render-field-text-inputs.d.ts +3 -0
- package/dist/components/render-field-text-inputs.js +34 -0
- package/dist/components/render-field-values.d.ts +18 -0
- package/dist/components/render-field-values.js +111 -0
- package/dist/components/render-field-write-only.d.ts +9 -0
- package/dist/components/render-field-write-only.js +29 -0
- package/dist/components/render-field.d.ts +2 -2
- package/dist/components/render-field.js +39 -669
- package/dist/components/render-list-header-slot.d.ts +5 -0
- package/dist/components/render-list-header-slot.js +30 -0
- package/dist/components/render-list-reference-bridge.d.ts +3 -0
- package/dist/components/render-list-reference-bridge.js +25 -0
- package/dist/components/render-list-reference-columns.d.ts +25 -0
- package/dist/components/render-list-reference-columns.js +137 -0
- package/dist/components/render-list-table-props.d.ts +52 -0
- package/dist/components/render-list-table-props.js +53 -0
- package/dist/components/render-list-toolbar-action.d.ts +27 -0
- package/dist/components/render-list-toolbar-action.js +40 -0
- package/dist/components/render-list-toolbar-model.d.ts +42 -0
- package/dist/components/render-list-toolbar-model.js +82 -0
- package/dist/components/render-list-toolbar.d.ts +38 -0
- package/dist/components/render-list-toolbar.js +40 -0
- package/dist/components/render-list-types.d.ts +113 -0
- package/dist/components/render-list-types.js +1 -0
- package/dist/components/render-list.d.ts +5 -130
- package/dist/components/render-list.js +95 -319
- package/dist/components/use-reference-input-state.d.ts +20 -0
- package/dist/components/use-reference-input-state.js +85 -0
- package/dist/components/use-render-edit-controlled-sync.d.ts +3 -0
- package/dist/components/use-render-edit-controlled-sync.js +26 -0
- package/dist/components/use-render-edit-draft-save.d.ts +29 -0
- package/dist/components/use-render-edit-draft-save.js +141 -0
- package/dist/components/use-render-edit-form.d.ts +10 -0
- package/dist/components/use-render-edit-form.js +101 -0
- package/dist/components/use-render-edit-sections.d.ts +15 -0
- package/dist/components/use-render-edit-sections.js +100 -0
- package/dist/components/use-render-edit-state.d.ts +65 -0
- package/dist/components/use-render-edit-state.js +158 -0
- package/dist/components/use-render-edit-steps.d.ts +13 -0
- package/dist/components/use-render-edit-steps.js +52 -0
- package/dist/components/use-render-list-columns.d.ts +18 -0
- package/dist/components/use-render-list-columns.js +41 -0
- package/dist/components/use-render-list-search.d.ts +4 -0
- package/dist/components/use-render-list-search.js +50 -0
- package/dist/current-user.d.ts +2 -0
- package/dist/data-table-aggregate-types.d.ts +47 -0
- package/dist/data-table-aggregate-types.js +1 -0
- package/dist/data-table-bulk-types.d.ts +27 -0
- package/dist/data-table-bulk-types.js +1 -0
- package/dist/data-table-facet-types.d.ts +36 -0
- package/dist/data-table-facet-types.js +1 -0
- package/dist/data-table-view-types.d.ts +73 -0
- package/dist/data-table-view-types.js +1 -0
- package/dist/headless/view-model/edit.js +3 -1
- package/dist/headless/view-model/types.d.ts +4 -2
- package/dist/hooks/list-column-layout.d.ts +19 -0
- package/dist/hooks/list-column-layout.js +36 -0
- package/dist/hooks/list-group-pages.d.ts +26 -0
- package/dist/hooks/list-group-pages.js +37 -0
- package/dist/hooks/list-reset.d.ts +8 -0
- package/dist/hooks/list-reset.js +15 -0
- package/dist/hooks/list-server-groups.d.ts +39 -0
- package/dist/hooks/list-server-groups.js +84 -0
- package/dist/hooks/list-state-schema.d.ts +28 -0
- package/dist/hooks/list-state-schema.js +76 -0
- package/dist/hooks/use-bulk-run.d.ts +41 -0
- package/dist/hooks/use-bulk-run.js +65 -0
- package/dist/hooks/use-group-load-state.d.ts +11 -0
- package/dist/hooks/use-group-load-state.js +31 -0
- package/dist/hooks/use-list-aggregates.d.ts +38 -0
- package/dist/hooks/use-list-aggregates.js +107 -0
- package/dist/hooks/use-list-table.d.ts +38 -0
- package/dist/hooks/use-list-table.js +91 -0
- package/dist/hooks/use-list-view-state.d.ts +43 -0
- package/dist/hooks/use-list-view-state.js +161 -0
- package/dist/hooks/use-related-list-rows.d.ts +27 -0
- package/dist/hooks/use-related-list-rows.js +81 -0
- package/dist/hooks/use-related-list-state.d.ts +15 -0
- package/dist/hooks/use-related-list-state.js +31 -0
- package/dist/hooks/use-remembered-list-params.d.ts +13 -0
- package/dist/hooks/use-remembered-list-params.js +53 -0
- package/dist/hooks/use-remembered-local-list-state.d.ts +26 -0
- package/dist/hooks/use-remembered-local-list-state.js +63 -0
- package/dist/i18n-defaults.js +53 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/primitives.d.ts +11 -47
- package/dist/web/app/create-app.d.ts +5 -0
- package/dist/web/app/create-app.js +7 -4
- package/dist/web/app/dashboard-body.d.ts +1 -1
- package/dist/web/app/dashboard-body.js +42 -246
- package/dist/web/app/dashboard-header.d.ts +34 -0
- package/dist/web/app/dashboard-header.js +58 -0
- package/dist/web/app/dashboard-screen-controls.d.ts +11 -0
- package/dist/web/app/dashboard-screen-controls.js +42 -0
- package/dist/web/app/dashboard-screen-tiles.d.ts +8 -0
- package/dist/web/app/dashboard-screen-tiles.js +42 -0
- package/dist/web/app/dashboard-visibility.d.ts +42 -0
- package/dist/web/app/dashboard-visibility.js +102 -0
- package/dist/web/app/history-leave-guard.d.ts +6 -0
- package/dist/web/app/history-leave-guard.js +115 -0
- package/dist/web/app/list-state-store-boot.d.ts +6 -0
- package/dist/web/app/list-state-store-boot.js +15 -0
- package/dist/web/app/nav.js +7 -54
- package/dist/web/app/session-list-state-store.d.ts +15 -0
- package/dist/web/app/session-list-state-store.js +117 -0
- package/dist/web/app/use-dashboard-screen-state.d.ts +35 -0
- package/dist/web/app/use-dashboard-screen-state.js +69 -0
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +1 -0
- package/dist/web/lib/export-xlsx-request.d.ts +11 -0
- package/dist/web/lib/export-xlsx-request.js +46 -0
- package/dist/web/primitives/data-table/actions-column.d.ts +16 -0
- package/dist/web/primitives/data-table/actions-column.js +35 -0
- package/dist/web/primitives/data-table/bulk-selection-bar.d.ts +20 -0
- package/dist/web/primitives/data-table/bulk-selection-bar.js +37 -0
- package/dist/web/primitives/data-table/card-body.d.ts +6 -0
- package/dist/web/primitives/data-table/card-body.js +32 -0
- package/dist/web/primitives/data-table/card-row.d.ts +5 -0
- package/dist/web/primitives/data-table/card-row.js +31 -0
- package/dist/web/primitives/data-table/card-sort-select.d.ts +14 -0
- package/dist/web/primitives/data-table/card-sort-select.js +40 -0
- package/dist/web/primitives/data-table/card-view.d.ts +3 -0
- package/dist/web/primitives/data-table/card-view.js +13 -0
- package/dist/web/primitives/data-table/column-layout-mode.d.ts +5 -0
- package/dist/web/primitives/data-table/column-layout-mode.js +10 -0
- package/dist/web/primitives/data-table/column-picker-section.d.ts +7 -0
- package/dist/web/primitives/data-table/column-picker-section.js +35 -0
- package/dist/web/primitives/data-table/column-resize-handle.d.ts +14 -0
- package/dist/web/primitives/data-table/column-resize-handle.js +48 -0
- package/dist/web/primitives/data-table/data-cell-props.d.ts +7 -0
- package/dist/web/primitives/data-table/data-cell-props.js +8 -0
- package/dist/web/primitives/data-table/density.d.ts +14 -0
- package/dist/web/primitives/data-table/density.js +20 -0
- package/dist/web/primitives/data-table/export-button.d.ts +5 -0
- package/dist/web/primitives/data-table/export-button.js +37 -0
- package/dist/web/primitives/data-table/facet-cluster.d.ts +8 -0
- package/dist/web/primitives/data-table/facet-cluster.js +37 -0
- package/dist/web/primitives/data-table/group-band.d.ts +28 -0
- package/dist/web/primitives/data-table/group-band.js +53 -0
- package/dist/web/primitives/data-table/measure-value.d.ts +10 -0
- package/dist/web/primitives/data-table/measure-value.js +28 -0
- package/dist/web/primitives/data-table/pin-section.d.ts +8 -0
- package/dist/web/primitives/data-table/pin-section.js +20 -0
- package/dist/web/primitives/data-table/reset-all-button.d.ts +4 -0
- package/dist/web/primitives/data-table/reset-all-button.js +8 -0
- package/dist/web/primitives/data-table/row-render-parts.d.ts +8 -0
- package/dist/web/primitives/data-table/row-render-parts.js +35 -0
- package/dist/web/primitives/data-table/server-group-header.d.ts +11 -0
- package/dist/web/primitives/data-table/server-group-header.js +51 -0
- package/dist/web/primitives/data-table/sortable-header.d.ts +17 -0
- package/dist/web/primitives/data-table/sortable-header.js +41 -0
- package/dist/web/primitives/data-table/table-body.d.ts +3 -0
- package/dist/web/primitives/data-table/table-body.js +32 -0
- package/dist/web/primitives/data-table/table-chrome.d.ts +13 -0
- package/dist/web/primitives/data-table/table-chrome.js +30 -0
- package/dist/web/primitives/data-table/table-content.d.ts +3 -0
- package/dist/web/primitives/data-table/table-content.js +41 -0
- package/dist/web/primitives/data-table/table-data-cell.d.ts +4 -0
- package/dist/web/primitives/data-table/table-data-cell.js +25 -0
- package/dist/web/primitives/data-table/table-footer.d.ts +7 -0
- package/dist/web/primitives/data-table/table-footer.js +20 -0
- package/dist/web/primitives/data-table/table-header-row.d.ts +3 -0
- package/dist/web/primitives/data-table/table-header-row.js +21 -0
- package/dist/web/primitives/data-table/table-view.d.ts +3 -0
- package/dist/web/primitives/data-table/table-view.js +25 -0
- package/dist/web/primitives/data-table/toolbar-end-cluster.d.ts +10 -0
- package/dist/web/primitives/data-table/toolbar-end-cluster.js +13 -0
- package/dist/web/primitives/data-table/toolbar.d.ts +6 -0
- package/dist/web/primitives/data-table/toolbar.js +53 -0
- package/dist/web/primitives/data-table/totals-footer.d.ts +24 -0
- package/dist/web/primitives/data-table/totals-footer.js +48 -0
- package/dist/web/primitives/data-table/use-bulk-selection.d.ts +21 -0
- package/dist/web/primitives/data-table/use-bulk-selection.js +66 -0
- package/dist/web/primitives/data-table/use-column-pins.d.ts +32 -0
- package/dist/web/primitives/data-table/use-column-pins.js +117 -0
- package/dist/web/primitives/data-table/use-column-sizing.d.ts +22 -0
- package/dist/web/primitives/data-table/use-column-sizing.js +44 -0
- package/dist/web/primitives/data-table/use-data-table-state.d.ts +44 -0
- package/dist/web/primitives/data-table/use-data-table-state.js +123 -0
- package/dist/web/primitives/data-table/use-measured-column-widths.d.ts +15 -0
- package/dist/web/primitives/data-table/use-measured-column-widths.js +83 -0
- package/dist/web/primitives/data-table/use-row-keyboard-navigation.d.ts +22 -0
- package/dist/web/primitives/data-table/use-row-keyboard-navigation.js +65 -0
- package/dist/web/primitives/data-table/use-row-selection.d.ts +14 -0
- package/dist/web/primitives/data-table/use-row-selection.js +48 -0
- package/dist/web/primitives/data-table/use-view-columns.d.ts +6 -0
- package/dist/web/primitives/data-table/use-view-columns.js +17 -0
- package/dist/web/primitives/data-table/view-menu.d.ts +8 -0
- package/dist/web/primitives/data-table/view-menu.js +45 -0
- package/dist/web/primitives/data-table-cell.d.ts +0 -22
- package/dist/web/primitives/data-table-cell.js +1 -58
- package/dist/web/primitives/data-table.d.ts +3 -4
- package/dist/web/primitives/data-table.js +30 -272
- package/dist/web/primitives/file-value-list.d.ts +1 -1
- package/dist/web/primitives/file-value-list.js +5 -5
- package/dist/web/primitives/form-footer-parts.d.ts +25 -0
- package/dist/web/primitives/form-footer-parts.js +55 -0
- package/dist/web/primitives/form.js +33 -43
- package/dist/web/primitives/input-choice-kinds.d.ts +4 -0
- package/dist/web/primitives/input-choice-kinds.js +67 -0
- package/dist/web/primitives/input-file-kinds.d.ts +3 -0
- package/dist/web/primitives/input-file-kinds.js +6 -0
- package/dist/web/primitives/input-number-kinds.d.ts +4 -0
- package/dist/web/primitives/input-number-kinds.js +16 -0
- package/dist/web/primitives/input-optional-props.d.ts +20 -0
- package/dist/web/primitives/input-optional-props.js +24 -0
- package/dist/web/primitives/input-select-presentation.d.ts +13 -0
- package/dist/web/primitives/input-select-presentation.js +19 -0
- package/dist/web/primitives/input-shared.d.ts +22 -0
- package/dist/web/primitives/input-shared.js +10 -0
- package/dist/web/primitives/input-temporal-kinds.d.ts +5 -0
- package/dist/web/primitives/input-temporal-kinds.js +10 -0
- package/dist/web/primitives/input-text-kinds.d.ts +7 -0
- package/dist/web/primitives/input-text-kinds.js +33 -0
- package/dist/web/primitives/input.js +35 -161
- package/dist/web/primitives/number-range-facet.d.ts +7 -0
- package/dist/web/primitives/number-range-facet.js +33 -0
- package/dist/web/widgets/bounded-load-cache.d.ts +5 -1
- package/dist/web/widgets/bounded-load-cache.js +48 -26
- package/dist/web/widgets/chart-data.d.ts +35 -0
- package/dist/web/widgets/chart-data.js +37 -0
- package/dist/web/widgets/chart-markers.d.ts +26 -0
- package/dist/web/widgets/chart-markers.js +64 -0
- package/dist/web/widgets/chart-parts.d.ts +20 -0
- package/dist/web/widgets/chart-parts.js +15 -0
- package/dist/web/widgets/charts.d.ts +5 -78
- package/dist/web/widgets/charts.js +7 -485
- package/dist/web/widgets/drawer-panel-class.d.ts +3 -0
- package/dist/web/widgets/drawer-panel-class.js +40 -0
- package/dist/web/widgets/drawer-resize-controls.d.ts +11 -0
- package/dist/web/widgets/drawer-resize-controls.js +12 -0
- package/dist/web/widgets/drawer-sizing.d.ts +19 -0
- package/dist/web/widgets/drawer-sizing.js +60 -0
- package/dist/web/widgets/drawer.js +17 -116
- package/dist/web/widgets/pdf-preview.js +9 -4
- package/dist/web/widgets/stacked-area-brush.d.ts +13 -0
- package/dist/web/widgets/stacked-area-brush.js +91 -0
- package/dist/web/widgets/stacked-area-chart.d.ts +6 -0
- package/dist/web/widgets/stacked-area-chart.js +45 -0
- package/dist/web/widgets/stacked-area-chrome.d.ts +7 -0
- package/dist/web/widgets/stacked-area-chrome.js +77 -0
- package/dist/web/widgets/stacked-area-geometry.d.ts +54 -0
- package/dist/web/widgets/stacked-area-geometry.js +83 -0
- package/dist/web/widgets/stacked-area-model.d.ts +11 -0
- package/dist/web/widgets/stacked-area-model.js +1 -0
- package/dist/web/widgets/stacked-area-plot.d.ts +3 -0
- package/dist/web/widgets/stacked-area-plot.js +51 -0
- package/dist/web/widgets/stacked-area-props.d.ts +83 -0
- package/dist/web/widgets/stacked-area-props.js +16 -0
- package/dist/web/widgets/stacked-area-svg.d.ts +3 -0
- package/dist/web/widgets/stacked-area-svg.js +38 -0
- package/dist/web/widgets/stacked-area-tooltip.d.ts +3 -0
- package/dist/web/widgets/stacked-area-tooltip.js +86 -0
- package/dist/web/widgets/use-drawer-resize.d.ts +22 -0
- package/dist/web/widgets/use-drawer-resize.js +52 -0
- package/dist/web/widgets/use-stacked-area-tooltip.d.ts +9 -0
- package/dist/web/widgets/use-stacked-area-tooltip.js +34 -0
- package/dist/web/widgets/use-stacked-area-window.d.ts +20 -0
- package/dist/web/widgets/use-stacked-area-window.js +76 -0
- package/package.json +9 -9
- package/src/changes.json +163 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
import { useListUrlState } from "../hooks/use-list-url-state.js";
|
|
3
|
+
import { buildFilterPayload, mergeReferenceFacetOptions, resolveProjectionFacetSpecs, } from "./list-facets.js";
|
|
4
|
+
import { buildListQueryPayload } from "./screen-shared.js";
|
|
5
|
+
import { useProjectionDateRange } from "./use-projection-date-range.js";
|
|
6
|
+
// Same predicate that makes a header clickable: a stale link onto a
|
|
7
|
+
// sortable:false/unknown column must not reach the server (it answers 400).
|
|
8
|
+
function resolveActiveSort(urlSort, input) {
|
|
9
|
+
const { screen, entity, sortable } = input;
|
|
10
|
+
const urlSortField = urlSort === null ? undefined : entity.fields[urlSort.field];
|
|
11
|
+
const urlSortAllowed = urlSortField !== undefined && "sortable" in urlSortField && urlSortField.sortable === true;
|
|
12
|
+
return sortable ? ((urlSortAllowed ? urlSort : null) ?? screen.defaultSort ?? null) : null;
|
|
13
|
+
}
|
|
14
|
+
export function useProjectionListQuery(input) {
|
|
15
|
+
const { screen, featureName, effectiveTranslate, searchable, paginated } = input;
|
|
16
|
+
const urlState = useListUrlState(screen.id);
|
|
17
|
+
// Gated on the derived capability, not just on whether URL-state happens to
|
|
18
|
+
// carry a value — a stale/hand-crafted URL (?…q=x on a non-searchable
|
|
19
|
+
// screen) must not smuggle a param the query's Zod schema doesn't accept.
|
|
20
|
+
const activeSearch = searchable ? urlState.q : "";
|
|
21
|
+
const activeSort = resolveActiveSort(urlState.sort, input);
|
|
22
|
+
const limit = urlState.pageSize ?? screen.pageSize ?? 50;
|
|
23
|
+
// Pages-mode only (see header comment) — an author-set pagination:
|
|
24
|
+
// "infinite" on a paginated projectionList is silently a no-op today.
|
|
25
|
+
const usePager = paginated && (screen.pagination ?? "pages") === "pages";
|
|
26
|
+
// Facets — a projectionList has no entity, so screen.facets is
|
|
27
|
+
// the only field inventory; resolveProjectionFacetSpecs reshapes +
|
|
28
|
+
// translates its explicit labels into the same ResolvedFacetSpec the
|
|
29
|
+
// entityList adapter produces, so both feed the same
|
|
30
|
+
// buildFilterFacets/buildFilterPayload.
|
|
31
|
+
const facetSpecs = useMemo(() => resolveProjectionFacetSpecs(screen.facets, effectiveTranslate, featureName), [screen.facets, effectiveTranslate, featureName]);
|
|
32
|
+
const [projectionReferenceFacetOptions, setProjectionReferenceFacetOptions] = useState({});
|
|
33
|
+
const handleProjectionFacetOptions = useCallback((field, options) => setProjectionReferenceFacetOptions((prev) => prev[field] === options ? prev : { ...prev, [field]: options }), []);
|
|
34
|
+
const resolvedFacetSpecs = useMemo(() => mergeReferenceFacetOptions(facetSpecs, projectionReferenceFacetOptions), [facetSpecs, projectionReferenceFacetOptions]);
|
|
35
|
+
const filterPayload = useMemo(() => buildFilterPayload(urlState.filters, (field) => facetSpecs.find((spec) => spec.field === field)?.type), [urlState.filters, facetSpecs]);
|
|
36
|
+
const { dateRangePayload, dateRangeFacets, handleDateRangeChange } = useProjectionDateRange(screen.facets, effectiveTranslate, urlState);
|
|
37
|
+
const queryPayload = useMemo(() => {
|
|
38
|
+
const payload = buildListQueryPayload({
|
|
39
|
+
limit,
|
|
40
|
+
search: activeSearch,
|
|
41
|
+
sort: activeSort,
|
|
42
|
+
usePager,
|
|
43
|
+
page: urlState.page,
|
|
44
|
+
useInfinite: false,
|
|
45
|
+
cursor: undefined,
|
|
46
|
+
});
|
|
47
|
+
// Gated on screen.filter/screen.facets being declared (not just on
|
|
48
|
+
// urlState/filterPayload happening to carry a value) — same rule as
|
|
49
|
+
// activeSearch/activeSort above: a stale URL must not smuggle a param
|
|
50
|
+
// the bound query's Zod schema doesn't accept.
|
|
51
|
+
if (screen.filter !== undefined) {
|
|
52
|
+
payload["filter"] = screen.filter;
|
|
53
|
+
}
|
|
54
|
+
if (screen.facets !== undefined && filterPayload.length > 0) {
|
|
55
|
+
payload["filters"] = filterPayload;
|
|
56
|
+
}
|
|
57
|
+
Object.assign(payload, dateRangePayload);
|
|
58
|
+
return payload;
|
|
59
|
+
}, [
|
|
60
|
+
limit,
|
|
61
|
+
activeSearch,
|
|
62
|
+
activeSort,
|
|
63
|
+
usePager,
|
|
64
|
+
urlState.page,
|
|
65
|
+
screen.filter,
|
|
66
|
+
screen.facets,
|
|
67
|
+
filterPayload,
|
|
68
|
+
dateRangePayload,
|
|
69
|
+
]);
|
|
70
|
+
return {
|
|
71
|
+
urlState,
|
|
72
|
+
activeSearch,
|
|
73
|
+
activeSort,
|
|
74
|
+
limit,
|
|
75
|
+
usePager,
|
|
76
|
+
facetSpecs,
|
|
77
|
+
handleProjectionFacetOptions,
|
|
78
|
+
resolvedFacetSpecs,
|
|
79
|
+
dateRangeFacets,
|
|
80
|
+
handleDateRangeChange,
|
|
81
|
+
queryPayload,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { DispatcherError } from "../headless/index.ts";
|
|
2
|
+
import type { ListAggregates } from "../hooks/use-list-aggregates.ts";
|
|
3
|
+
import type { ListUrlStateApi } from "../hooks/use-list-url-state.ts";
|
|
4
|
+
import type { DataTableProps } from "../primitives.tsx";
|
|
5
|
+
import { type PagedRows } from "./screen-shared.tsx";
|
|
6
|
+
type Row = Readonly<Record<string, unknown>>;
|
|
7
|
+
export type ScreenListRows = {
|
|
8
|
+
readonly rows: readonly Row[];
|
|
9
|
+
/** The ungrouped page answer; null while grouped or before the first answer. */
|
|
10
|
+
readonly page: PagedRows | null;
|
|
11
|
+
readonly firstLoad: boolean;
|
|
12
|
+
readonly error: DispatcherError | null;
|
|
13
|
+
readonly hasData: boolean;
|
|
14
|
+
readonly retry: () => Promise<void>;
|
|
15
|
+
readonly infinite?: {
|
|
16
|
+
readonly onReachEnd: () => void;
|
|
17
|
+
readonly loadingMore: boolean;
|
|
18
|
+
readonly hasMore: boolean;
|
|
19
|
+
};
|
|
20
|
+
readonly refreshAfterWrite: () => Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
/** Rows of a URL-backed list screen: one page, accumulated infinite pages, or the
|
|
23
|
+
* loaded rows of the open server groups. `payload` carries no cursor. */
|
|
24
|
+
export declare function useScreenListRows({ queryType, payload, infinite, aggregates, }: {
|
|
25
|
+
readonly queryType: string;
|
|
26
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
27
|
+
readonly infinite: boolean;
|
|
28
|
+
readonly aggregates: ListAggregates;
|
|
29
|
+
}): ScreenListRows;
|
|
30
|
+
/** Pager props for `pagination: "pages"`; none until the server total arrives. */
|
|
31
|
+
export declare function listPager(args: {
|
|
32
|
+
readonly usePager: boolean;
|
|
33
|
+
readonly page: PagedRows | null;
|
|
34
|
+
readonly limit: number;
|
|
35
|
+
readonly urlState: Pick<ListUrlStateApi, "page" | "setPage" | "setPageSize">;
|
|
36
|
+
}): DataTableProps["pager"];
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { useQuery } from "../hooks/use-query.js";
|
|
3
|
+
import { refetchAfterWrite } from "./row-actions.js";
|
|
4
|
+
import { pageSizeOptionsFor } from "./screen-shared.js";
|
|
5
|
+
function freshInfiniteState(resetKey) {
|
|
6
|
+
return { resetKey, rows: [], cursor: undefined, hasMore: true };
|
|
7
|
+
}
|
|
8
|
+
// Live updates can bring an entry onto the next page that was already appended.
|
|
9
|
+
function appendUnseen(previous, next) {
|
|
10
|
+
const seen = new Set(previous.map((row) => row["id"]));
|
|
11
|
+
return [...previous, ...next.filter((row) => !seen.has(row["id"]))];
|
|
12
|
+
}
|
|
13
|
+
/** Rows of a URL-backed list screen: one page, accumulated infinite pages, or the
|
|
14
|
+
* loaded rows of the open server groups. `payload` carries no cursor. */
|
|
15
|
+
export function useScreenListRows({ queryType, payload, infinite, aggregates, }) {
|
|
16
|
+
const { grouped, refresh: refreshAggregates } = aggregates;
|
|
17
|
+
const resetKey = JSON.stringify(payload);
|
|
18
|
+
const [infiniteState, setInfiniteState] = useState(() => freshInfiniteState(resetKey));
|
|
19
|
+
const state = infiniteState.resetKey === resetKey ? infiniteState : freshInfiniteState(resetKey);
|
|
20
|
+
if (infiniteState.resetKey !== resetKey)
|
|
21
|
+
setInfiniteState(state);
|
|
22
|
+
const cursor = infinite ? state.cursor : undefined;
|
|
23
|
+
const rowsPayload = useMemo(() => (cursor === undefined ? payload : { ...payload, cursor }), [payload, cursor]);
|
|
24
|
+
const rowsQuery = useQuery(queryType, rowsPayload, { live: true, enabled: !grouped });
|
|
25
|
+
const { data, loading, refetch } = rowsQuery;
|
|
26
|
+
const lastDataRef = useRef(null);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
// skip: only a new answer of an infinite list is appended
|
|
29
|
+
if (!infinite || data === null || data === lastDataRef.current)
|
|
30
|
+
return;
|
|
31
|
+
lastDataRef.current = data;
|
|
32
|
+
setInfiniteState((current) => current.resetKey !== resetKey
|
|
33
|
+
? current
|
|
34
|
+
: {
|
|
35
|
+
...current,
|
|
36
|
+
rows: current.cursor === undefined ? data.rows : appendUnseen(current.rows, data.rows),
|
|
37
|
+
hasMore: data.nextCursor !== null,
|
|
38
|
+
});
|
|
39
|
+
}, [data, infinite, resetKey]);
|
|
40
|
+
const onReachEnd = useCallback(() => {
|
|
41
|
+
// skip: the next page waits for the current answer
|
|
42
|
+
if (loading)
|
|
43
|
+
return;
|
|
44
|
+
const nextCursor = data?.nextCursor;
|
|
45
|
+
// skip: no cursor means the last page is loaded
|
|
46
|
+
if (nextCursor === undefined || nextCursor === null)
|
|
47
|
+
return;
|
|
48
|
+
setInfiniteState((current) => current.resetKey === resetKey ? { ...current, cursor: nextCursor } : current);
|
|
49
|
+
}, [loading, data, resetKey]);
|
|
50
|
+
const refreshAfterWrite = useCallback(async () => {
|
|
51
|
+
refreshAggregates();
|
|
52
|
+
// skip: grouped rows reload through the aggregates refresh
|
|
53
|
+
if (grouped)
|
|
54
|
+
return;
|
|
55
|
+
if (infinite && cursor !== undefined) {
|
|
56
|
+
setInfiniteState(freshInfiniteState(resetKey));
|
|
57
|
+
// skip: starting over refetches the first page
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
await refetchAfterWrite(refetch);
|
|
61
|
+
}, [refreshAggregates, grouped, infinite, cursor, resetKey, refetch]);
|
|
62
|
+
const retry = useCallback(async () => {
|
|
63
|
+
if (grouped)
|
|
64
|
+
refreshAggregates();
|
|
65
|
+
else
|
|
66
|
+
await refetch();
|
|
67
|
+
}, [grouped, refreshAggregates, refetch]);
|
|
68
|
+
if (grouped) {
|
|
69
|
+
return {
|
|
70
|
+
rows: aggregates.rows,
|
|
71
|
+
page: null,
|
|
72
|
+
firstLoad: aggregates.loading,
|
|
73
|
+
error: aggregates.error,
|
|
74
|
+
hasData: aggregates.serverGroups !== undefined,
|
|
75
|
+
retry,
|
|
76
|
+
refreshAfterWrite,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
rows: infinite ? state.rows : (data?.rows ?? []),
|
|
81
|
+
page: data,
|
|
82
|
+
firstLoad: loading && data === null,
|
|
83
|
+
error: rowsQuery.error,
|
|
84
|
+
hasData: data !== null,
|
|
85
|
+
retry,
|
|
86
|
+
...(infinite && { infinite: { onReachEnd, loadingMore: loading, hasMore: state.hasMore } }),
|
|
87
|
+
refreshAfterWrite,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Pager props for `pagination: "pages"`; none until the server total arrives. */
|
|
91
|
+
export function listPager(args) {
|
|
92
|
+
const { usePager, page, limit, urlState } = args;
|
|
93
|
+
const total = page?.total;
|
|
94
|
+
if (!usePager || total === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
return {
|
|
97
|
+
page: urlState.page,
|
|
98
|
+
limit,
|
|
99
|
+
total,
|
|
100
|
+
onPageChange: urlState.setPage,
|
|
101
|
+
pageSizeOptions: pageSizeOptionsFor(limit),
|
|
102
|
+
onPageSizeChange: urlState.setPageSize,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { EntityListScreenDefinition, ProjectionListScreenDefinition } from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
+
import type { Translate } from "../headless/index.ts";
|
|
3
|
+
import type { ListGroupPaging } from "../hooks/list-group-pages.ts";
|
|
4
|
+
import { type ListAggregates } from "../hooks/use-list-aggregates.ts";
|
|
5
|
+
import { type ListTableProps } from "../hooks/use-list-table.ts";
|
|
6
|
+
import type { DataTableTableBlockProps } from "../primitives.tsx";
|
|
7
|
+
type ScreenListTableArgs = {
|
|
8
|
+
readonly screen: EntityListScreenDefinition | ProjectionListScreenDefinition;
|
|
9
|
+
readonly featureName: string;
|
|
10
|
+
/** The list query payload as sent; paging keys are stripped for the export. */
|
|
11
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
12
|
+
readonly filterFields: readonly string[];
|
|
13
|
+
readonly queryType: string;
|
|
14
|
+
readonly pageSize: number;
|
|
15
|
+
readonly paging: ListGroupPaging;
|
|
16
|
+
readonly translate: Translate;
|
|
17
|
+
};
|
|
18
|
+
export type ScreenListTable = {
|
|
19
|
+
readonly props: ListTableProps & Pick<DataTableTableBlockProps, "serverGroups" | "totals" | "notice">;
|
|
20
|
+
readonly aggregates: ListAggregates;
|
|
21
|
+
};
|
|
22
|
+
/** Table-block wiring of a URL-backed list screen (entityList, projectionList). */
|
|
23
|
+
export declare function useScreenListTable({ screen, featureName, payload, filterFields, queryType, pageSize, paging, translate, }: ScreenListTableArgs): ScreenListTable;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useListStateStore } from "../components/list-state-store.js";
|
|
3
|
+
import { useListAggregates } from "../hooks/use-list-aggregates.js";
|
|
4
|
+
import { useListTable } from "../hooks/use-list-table.js";
|
|
5
|
+
import { listViewSchemaColumns } from "../hooks/use-list-view-state.js";
|
|
6
|
+
import { useRememberedListParams } from "../hooks/use-remembered-list-params.js";
|
|
7
|
+
/** Table-block wiring of a URL-backed list screen (entityList, projectionList). */
|
|
8
|
+
export function useScreenListTable({ screen, featureName, payload, filterFields, queryType, pageSize, paging, translate, }) {
|
|
9
|
+
const { id: screenId, table, columns } = screen;
|
|
10
|
+
const store = useListStateStore();
|
|
11
|
+
const schemaColumns = useMemo(() => listViewSchemaColumns(columns), [columns]);
|
|
12
|
+
const known = useMemo(() => ({ columnFields: schemaColumns.map((column) => column.field), filterFields }), [schemaColumns, filterFields]);
|
|
13
|
+
useRememberedListParams({
|
|
14
|
+
screenId,
|
|
15
|
+
enabled: table?.rememberState === true,
|
|
16
|
+
store,
|
|
17
|
+
known,
|
|
18
|
+
});
|
|
19
|
+
const exportSource = useMemo(() => ({ screen: `${featureName}:screen:${screenId}`, screenShortId: screenId, payload }), [featureName, screenId, payload]);
|
|
20
|
+
const { props, groupField } = useListTable({
|
|
21
|
+
storeKey: screenId,
|
|
22
|
+
table,
|
|
23
|
+
schemaColumns,
|
|
24
|
+
url: { screenId },
|
|
25
|
+
exportSource,
|
|
26
|
+
});
|
|
27
|
+
const aggregates = useListAggregates({
|
|
28
|
+
queryType,
|
|
29
|
+
payload,
|
|
30
|
+
table,
|
|
31
|
+
groupField,
|
|
32
|
+
pageSize,
|
|
33
|
+
paging,
|
|
34
|
+
translate,
|
|
35
|
+
});
|
|
36
|
+
const { serverGroups, totals, notice } = aggregates;
|
|
37
|
+
return {
|
|
38
|
+
props: {
|
|
39
|
+
...props,
|
|
40
|
+
...(serverGroups !== undefined && { serverGroups }),
|
|
41
|
+
...(totals !== undefined && { totals }),
|
|
42
|
+
...(notice !== undefined && { notice }),
|
|
43
|
+
},
|
|
44
|
+
aggregates,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// One invisible bridge per requested group page: the number of open groups and
|
|
3
|
+
// loaded pages varies, so useQuery can't be called in a loop. The pool caps
|
|
4
|
+
// group page requests at four in flight.
|
|
5
|
+
import { useEffect, useRef } from "react";
|
|
6
|
+
import { useQuery } from "../hooks/use-query.js";
|
|
7
|
+
const GROUP_PAGE_CONCURRENCY = 4;
|
|
8
|
+
function GroupPageBridge({ pageKey, queryType, payload, onResult, refreshNonce, }) {
|
|
9
|
+
const result = useQuery(queryType, payload, {
|
|
10
|
+
live: true,
|
|
11
|
+
concurrency: GROUP_PAGE_CONCURRENCY,
|
|
12
|
+
});
|
|
13
|
+
const { data, loading, refetch } = result;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (data === null)
|
|
16
|
+
return;
|
|
17
|
+
onResult(pageKey, { rows: data.rows, nextCursor: data.nextCursor ?? null, loading });
|
|
18
|
+
}, [data, loading, pageKey, onResult]);
|
|
19
|
+
const seenNonce = useRef(refreshNonce);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
// skip: the mount fetch already covers the nonce the bridge started with
|
|
22
|
+
if (seenNonce.current === refreshNonce)
|
|
23
|
+
return;
|
|
24
|
+
seenNonce.current = refreshNonce;
|
|
25
|
+
void refetch();
|
|
26
|
+
}, [refreshNonce, refetch]);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function ListGroupPageBridges({ queryType, requests, onResult, refreshNonce, }) {
|
|
30
|
+
return (_jsx(_Fragment, { children: requests.map((request) => (_jsx(GroupPageBridge, { pageKey: request.pageKey, queryType: queryType, payload: request.payload, onResult: onResult, refreshNonce: refreshNonce }, request.pageKey))) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { StoredListState } from "../hooks/list-state-schema.ts";
|
|
3
|
+
export type ListStateStore = {
|
|
4
|
+
read(key: string): StoredListState | undefined;
|
|
5
|
+
write(key: string, state: StoredListState): void;
|
|
6
|
+
remove(key: string): void;
|
|
7
|
+
clear(): void;
|
|
8
|
+
};
|
|
9
|
+
export declare function createMemoryListStateStore(): ListStateStore;
|
|
10
|
+
export declare function ListStateStoreProvider({ store, children, }: {
|
|
11
|
+
readonly store: ListStateStore;
|
|
12
|
+
readonly children: ReactNode;
|
|
13
|
+
}): ReactNode;
|
|
14
|
+
export declare function useListStateStore(): ListStateStore;
|
|
15
|
+
/** The shared in-memory default; the web boot clears it when the identity changes. */
|
|
16
|
+
export declare function getDefaultMemoryListStateStore(): ListStateStore;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
export function createMemoryListStateStore() {
|
|
4
|
+
const entries = new Map();
|
|
5
|
+
return {
|
|
6
|
+
read: (key) => entries.get(key),
|
|
7
|
+
write: (key, state) => {
|
|
8
|
+
entries.set(key, state);
|
|
9
|
+
},
|
|
10
|
+
remove: (key) => {
|
|
11
|
+
entries.delete(key);
|
|
12
|
+
},
|
|
13
|
+
clear: () => {
|
|
14
|
+
entries.clear();
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Module-wide so remembered list state survives route changes in the SPA
|
|
19
|
+
// without any provider mounted.
|
|
20
|
+
const defaultMemoryStore = createMemoryListStateStore();
|
|
21
|
+
const ListStateStoreContext = createContext(defaultMemoryStore);
|
|
22
|
+
export function ListStateStoreProvider({ store, children, }) {
|
|
23
|
+
return _jsx(ListStateStoreContext.Provider, { value: store, children: children });
|
|
24
|
+
}
|
|
25
|
+
export function useListStateStore() {
|
|
26
|
+
return useContext(ListStateStoreContext);
|
|
27
|
+
}
|
|
28
|
+
/** The shared in-memory default; the web boot clears it when the identity changes. */
|
|
29
|
+
export function getDefaultMemoryListStateStore() {
|
|
30
|
+
return defaultMemoryStore;
|
|
31
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { evalFieldCondition, normalizeListColumn, parseRefTarget,
|
|
3
|
-
import { useCallback,
|
|
2
|
+
import { evalFieldCondition, normalizeListColumn, parseRefTarget, } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
4
|
import { useAppFeatures } from "../app/app-features-context.js";
|
|
5
5
|
import { useClientActionRuntime } from "../app/client-actions.js";
|
|
6
6
|
import { ErrorReference } from "../app/error-reference.js";
|
|
7
|
+
import { useListBulk } from "../app/list-bulk-actions.js";
|
|
7
8
|
import { buildFilterFacets, buildFilterPayload, mergeReferenceFacetOptions, resolveProjectionFacetSpecs, } from "../app/list-facets.js";
|
|
8
9
|
import { useNav } from "../app/nav.js";
|
|
9
10
|
import { SYNTHESIZED_FIELD_PII_REASON } from "../app/projection-list-shim.js";
|
|
@@ -15,11 +16,12 @@ import { dispatcherErrorText } from "../app/write-failed-error.js";
|
|
|
15
16
|
import { useOptionalDispatcher } from "../context/dispatcher-context.js";
|
|
16
17
|
import { useUserRoles } from "../context/user-roles-context.js";
|
|
17
18
|
import { rendererWarn } from "../dev-console.js";
|
|
18
|
-
import {
|
|
19
|
+
import { useRelatedListRows } from "../hooks/use-related-list-rows.js";
|
|
20
|
+
import { useRelatedListState } from "../hooks/use-related-list-state.js";
|
|
19
21
|
import { useOptionalLocale, useTranslation } from "../i18n.js";
|
|
20
22
|
import { PageHeaderSlotAvailableProvider } from "../page-header-slot.js";
|
|
21
|
-
import { usePrimitives
|
|
22
|
-
import {
|
|
23
|
+
import { usePrimitives } from "../primitives.js";
|
|
24
|
+
import { ListGroupPageBridges } from "./list-group-page-bridges.js";
|
|
23
25
|
import { RenderEditActionButton } from "./render-edit-action-button.js";
|
|
24
26
|
import { RenderList } from "./render-list.js";
|
|
25
27
|
// Keyed on the type, not on key presence: most factories omit `sortable`
|
|
@@ -85,36 +87,6 @@ function valueTypeField(valueType, sortable) {
|
|
|
85
87
|
function warnRelatedListEmptyStateDrawerDropped(actionId) {
|
|
86
88
|
rendererWarn(`[kumiko] relatedList emptyState.action "${actionId}" is kind: "drawer" but no onOpenDrawer was supplied — dropped.`);
|
|
87
89
|
}
|
|
88
|
-
function buildRowGrouping(groupBy, translate, locale) {
|
|
89
|
-
return {
|
|
90
|
-
keyOf: (row) => relatedListGroupKey(row.values[groupBy.field]),
|
|
91
|
-
startsCollapsed: (key) => groupBy.collapsedWhen !== undefined && key === relatedListGroupKey(groupBy.collapsedWhen),
|
|
92
|
-
headerLabel: (key, rows) => {
|
|
93
|
-
const labelKey = relatedListGroupHeaderLabel(groupBy, key);
|
|
94
|
-
if (labelKey === undefined)
|
|
95
|
-
return undefined;
|
|
96
|
-
const dates = groupBy.dateField === undefined
|
|
97
|
-
? []
|
|
98
|
-
: rows
|
|
99
|
-
.map((row) => String(row.values[groupBy.dateField ?? ""] ?? ""))
|
|
100
|
-
.filter((value) => value !== "")
|
|
101
|
-
.sort();
|
|
102
|
-
const latest = dates[dates.length - 1];
|
|
103
|
-
return translate(labelKey, {
|
|
104
|
-
count: rows.length,
|
|
105
|
-
value: key,
|
|
106
|
-
lastDate: latest === undefined
|
|
107
|
-
? ""
|
|
108
|
-
: new Date(latest).toLocaleDateString(locale, {
|
|
109
|
-
year: "numeric",
|
|
110
|
-
month: "2-digit",
|
|
111
|
-
day: "2-digit",
|
|
112
|
-
timeZone: "UTC",
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
90
|
function buildRowTone(rule) {
|
|
119
91
|
if (rule === undefined)
|
|
120
92
|
return undefined;
|
|
@@ -126,9 +98,6 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
126
98
|
const t = useTranslation();
|
|
127
99
|
const effectiveTranslate = translate ?? t;
|
|
128
100
|
const locale = useOptionalLocale();
|
|
129
|
-
const rowGrouping = useMemo(() => section.groupBy === undefined
|
|
130
|
-
? undefined
|
|
131
|
-
: buildRowGrouping(section.groupBy, effectiveTranslate, locale ?? "en"), [section.groupBy, effectiveTranslate, locale]);
|
|
132
101
|
const rowTone = useMemo(() => buildRowTone(section.rowTone), [section.rowTone]);
|
|
133
102
|
const nav = useNav();
|
|
134
103
|
const host = useReturnHost();
|
|
@@ -165,8 +134,7 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
165
134
|
// Local state, not URL state: a section `id` is optional, so there is no
|
|
166
135
|
// stable URL key to namespace against — the section's sort is local for the
|
|
167
136
|
// same reason. Consequence: search/filters reset on reload.
|
|
168
|
-
const
|
|
169
|
-
const [filters, setFilters] = useState({});
|
|
137
|
+
const { search, filters, sort, setSearch, setFilters, setSort, listTable, groupField } = useRelatedListState(section, nav.route?.screenId);
|
|
170
138
|
const facetSpecs = useMemo(() => resolveProjectionFacetSpecs(section.facets, effectiveTranslate, featureName), [section.facets, effectiveTranslate, featureName]);
|
|
171
139
|
const [referenceFacetOptions, setReferenceFacetOptions] = useState({});
|
|
172
140
|
const handleFacetOptions = useCallback((field, options) => setReferenceFacetOptions((prev) => prev[field] === options ? prev : { ...prev, [field]: options }), []);
|
|
@@ -198,39 +166,19 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
198
166
|
search,
|
|
199
167
|
filterPayload,
|
|
200
168
|
]);
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}, [
|
|
214
|
-
const onFilterChange = useCallback((field, values) => setFilters((prev) => ({ ...prev, [field]: values })), []);
|
|
215
|
-
const onFilterReset = useCallback(() => setFilters({}), []);
|
|
216
|
-
// Sorted client-side over the already-loaded rows only. The section has no
|
|
217
|
-
// pager (one-shot fetch); when that fetch is truncated (`nextCursor` set) the
|
|
218
|
-
// truncation banner below tells the user the sorted set is partial.
|
|
219
|
-
const [sort, setSort] = useState(section.defaultSort ?? null);
|
|
220
|
-
const sortAccessors = useMemo(() => {
|
|
221
|
-
const accessors = {};
|
|
222
|
-
for (const col of section.columns) {
|
|
223
|
-
const field = normalizeListColumn(col).field;
|
|
224
|
-
accessors[field] = (row) => {
|
|
225
|
-
const value = row[field];
|
|
226
|
-
if (value === null || value === undefined)
|
|
227
|
-
return null;
|
|
228
|
-
return typeof value === "number" ? value : String(value);
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
return accessors;
|
|
232
|
-
}, [section.columns]);
|
|
233
|
-
const sortedRows = useMemo(() => sortByAccessor(rowsQuery.data?.rows ?? [], sort, sortAccessors, locale), [rowsQuery.data, sort, sortAccessors, locale]);
|
|
169
|
+
const listRows = useRelatedListRows({
|
|
170
|
+
section,
|
|
171
|
+
payload,
|
|
172
|
+
groupField,
|
|
173
|
+
sort,
|
|
174
|
+
locale,
|
|
175
|
+
refreshToken,
|
|
176
|
+
onAfterWrite,
|
|
177
|
+
translate: effectiveTranslate,
|
|
178
|
+
});
|
|
179
|
+
const { refetchSelfAndHost, aggregates } = listRows;
|
|
180
|
+
const onFilterChange = useCallback((field, values) => setFilters((prev) => ({ ...prev, [field]: values })), [setFilters]);
|
|
181
|
+
const onFilterReset = useCallback(() => setFilters({}), [setFilters]);
|
|
234
182
|
const rowClick = section.rowClick;
|
|
235
183
|
// A row-body click target comes from EITHER the legacy `rowClick` field OR
|
|
236
184
|
// a `rowActions` entry marked rowClick:true — the boot-validator rejects
|
|
@@ -278,6 +226,13 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
278
226
|
defaultEditRowAction,
|
|
279
227
|
host,
|
|
280
228
|
]);
|
|
229
|
+
const bulk = useListBulk({
|
|
230
|
+
bulkActions: section.bulkActions,
|
|
231
|
+
rowActions: section.rowActions,
|
|
232
|
+
translate: effectiveTranslate,
|
|
233
|
+
dispatcher,
|
|
234
|
+
reload: refetchSelfAndHost,
|
|
235
|
+
});
|
|
281
236
|
// Toolbar actions (e.g. "+ Create" above the tab table) — same schema
|
|
282
237
|
// and dispatch semantics as entityList/projectionList.
|
|
283
238
|
const toolbarActionButtons = useMemo(() => buildProjectionToolbarActions({
|
|
@@ -348,20 +303,11 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
348
303
|
refetchSelfAndHost,
|
|
349
304
|
]);
|
|
350
305
|
const emptyStateContent = section.emptyState !== undefined ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: section.emptyState.title }), section.emptyState.description !== undefined && (_jsx(Text, { variant: "small", children: section.emptyState.description })), emptyStateActionButton !== undefined && (_jsx(RenderEditActionButton, { action: emptyStateActionButton, Button: Button, Dialog: Dialog, onError: setEmptyStateActionError }))] })) : undefined;
|
|
351
|
-
//
|
|
352
|
-
// the
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
// more rows exist server-side beyond what was fetched.
|
|
357
|
-
// A handler that omits `nextCursor` (or reports a `total` the fetched rows
|
|
358
|
-
// already cover) has no further rows — only a real cursor marks truncation.
|
|
359
|
-
const truncated = rowsQuery.data !== null &&
|
|
360
|
-
typeof rowsQuery.data.nextCursor === "string" &&
|
|
361
|
-
(rowsQuery.data.total === undefined || rowsQuery.data.total > rowsQuery.data.rows.length);
|
|
362
|
-
const content = rowsQuery.loading && rowsQuery.data === null ? (_jsx(Banner, { padded: true, variant: "loading", testId: "related-list-loading", children: t("kumiko.widget.loading") })) : rowsQuery.error ? (_jsx(Banner, { padded: true, variant: "error", testId: "related-list-error", children: dispatcherErrorText(rowsQuery.error, effectiveTranslate) })) : (_jsxs(_Fragment, { children: [truncated && (_jsx(Banner, { variant: "info", testId: "related-list-truncated", children: effectiveTranslate("kumiko.list.related-list-truncated", {
|
|
363
|
-
count: sortedRows.length,
|
|
364
|
-
}) })), emptyStateActionError !== null && (_jsxs(Banner, { variant: "error", testId: "related-list-empty-state-action-error", children: [emptyStateActionError.message, _jsx(ErrorReference, { requestId: emptyStateActionError.requestId })] })), _jsx(PageHeaderSlotAvailableProvider, { value: false, children: _jsx(RenderList, { screen: listScreen, entity: entity, rows: sortedRows, featureName: listFeatureName ?? featureName, translate: effectiveTranslate, ...(section.description !== undefined && { description: section.description }), ...(section.itemNoun !== undefined && { itemNounKey: section.itemNoun }), sort: sort, onSortChange: setSort, ...(section.searchable === true && {
|
|
306
|
+
// Without a cursor-driven pager the client-side sort covers only the fetched
|
|
307
|
+
// rows; the banner keeps it from claiming "these are the top N".
|
|
308
|
+
const content = listRows.firstLoad ? (_jsx(Banner, { padded: true, variant: "loading", testId: "related-list-loading", children: t("kumiko.widget.loading") })) : listRows.error ? (_jsx(Banner, { padded: true, variant: "error", testId: "related-list-error", children: dispatcherErrorText(listRows.error, effectiveTranslate) })) : (_jsxs(_Fragment, { children: [listRows.truncated && (_jsx(Banner, { variant: "info", testId: "related-list-truncated", children: effectiveTranslate("kumiko.list.related-list-truncated", {
|
|
309
|
+
count: listRows.rows.length,
|
|
310
|
+
}) })), emptyStateActionError !== null && (_jsxs(Banner, { variant: "error", testId: "related-list-empty-state-action-error", children: [emptyStateActionError.message, _jsx(ErrorReference, { requestId: emptyStateActionError.requestId })] })), _jsx(ListGroupPageBridges, { ...aggregates.groupPages }), _jsx(PageHeaderSlotAvailableProvider, { value: false, children: _jsx(RenderList, { screen: listScreen, entity: entity, rows: listRows.rows, featureName: listFeatureName ?? featureName, translate: effectiveTranslate, ...(section.description !== undefined && { description: section.description }), ...(section.itemNoun !== undefined && { itemNounKey: section.itemNoun }), sort: sort, onSortChange: setSort, ...(section.searchable === true && {
|
|
365
311
|
searchable: true,
|
|
366
312
|
searchValue: search,
|
|
367
313
|
onSearchChange: setSearch,
|
|
@@ -370,7 +316,7 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
370
316
|
filterValues: filters,
|
|
371
317
|
onFilterChange,
|
|
372
318
|
onFilterReset,
|
|
373
|
-
}), ...(onRowClick !== undefined && { onRowClick }), ...(rowActions !== undefined && { rowActions }), ...(section.rowActionMode !== undefined && { rowActionMode: section.rowActionMode }), ...(
|
|
319
|
+
}), ...(onRowClick !== undefined && { onRowClick }), ...(rowActions !== undefined && { rowActions }), ...(section.rowActionMode !== undefined && { rowActionMode: section.rowActionMode }), ...(bulk !== undefined && { bulk }), ...(rowTone !== undefined && { rowTone }), ...(toolbarActionButtons !== undefined && { toolbarActions: toolbarActionButtons }), ...(emptyStateContent !== undefined && { emptyState: emptyStateContent }), ...listTable, ...(aggregates.serverGroups !== undefined && { serverGroups: aggregates.serverGroups }), ...(aggregates.totals !== undefined && { totals: aggregates.totals }), ...(aggregates.notice !== undefined && { notice: aggregates.notice }), screenPadding: false, ...(hideTitle === true && { scrollBody: true, chromeless: true }), ...(embedded === true && { chromeless: true }) }) })] }));
|
|
374
320
|
// hideTitle (tabs mode) → the caller (render-edit.tsx) already wraps this
|
|
375
321
|
// component's own return value in the same Card frame every other tab
|
|
376
322
|
// kind gets — no card here, or the two would nest. `scrollBody` caps the
|
|
@@ -382,7 +328,9 @@ export function RelatedListSection({ section, parentId, record, featureName, tra
|
|
|
382
328
|
// and aren't confined to a tab panel.
|
|
383
329
|
const bridges = _jsx(ReferenceFacetBridges, { specs: facetSpecs, onOptions: handleFacetOptions });
|
|
384
330
|
if (embedded === true) {
|
|
385
|
-
const countSuffix =
|
|
331
|
+
const countSuffix = listRows.hasData
|
|
332
|
+
? ` · ${aggregates.serverGroups?.totalCount ?? listRows.rows.length}`
|
|
333
|
+
: "";
|
|
386
334
|
return (_jsxs(_Fragment, { children: [bridges, _jsx(Card, { options: { framed: false, padded: false },
|
|
387
335
|
// Trim the default header inset: the host row already pads the area.
|
|
388
336
|
className: "[&>div:first-child]:px-0 [&>div:first-child]:pt-1 [&>div:first-child]:pb-2", slots: {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { FormValues } from "../headless/index.ts";
|
|
3
|
+
import type { RenderEditModel } from "./render-edit-render-model.ts";
|
|
4
|
+
export declare function renderAllRequiredHint<TValues extends FormValues, TCtx>({ engine: { state }, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
5
|
+
export declare function renderDraftPicker<TValues extends FormValues, TCtx>({ engine: { state, draftActions }, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
6
|
+
export declare function renderWizardProgress<TValues extends FormValues, TCtx>({ engine: { state, steps, sections, nav }, titles, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
7
|
+
export declare function renderTabStrip<TValues extends FormValues, TCtx>({ engine: { state, steps, sections, nav }, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
8
|
+
export declare function renderFormErrorBanner<TValues extends FormValues, TCtx>({ engine: { props, state }, layout, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
9
|
+
export declare function renderRootIssueBanners<TValues extends FormValues, TCtx>({ engine: { state }, layout, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
10
|
+
export declare function renderExtensionErrorBanner<TValues extends FormValues, TCtx>({ engine: { state }, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
11
|
+
export declare function renderActionErrorBanner<TValues extends FormValues, TCtx>({ engine: { state }, }: RenderEditModel<TValues, TCtx>): ReactNode;
|
|
12
|
+
export declare function renderDeleteDialog<TValues extends FormValues, TCtx>({ engine: { props, state }, }: RenderEditModel<TValues, TCtx>): ReactNode;
|