@cosmicdrift/kumiko-renderer 0.389.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/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 +69 -189
- 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/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/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 -670
- 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-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 +4 -113
- package/dist/components/render-list.js +91 -307
- 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/primitives.d.ts +1 -1
- 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/use-dashboard-screen-state.d.ts +35 -0
- package/dist/web/app/use-dashboard-screen-state.js +69 -0
- package/dist/web/primitives/data-table/actions-column.js +6 -5
- 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-view.d.ts +3 -0
- package/dist/web/primitives/data-table/card-view.js +13 -0
- package/dist/web/primitives/data-table/column-resize-handle.js +3 -1
- 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/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/table-body.d.ts +3 -0
- package/dist/web/primitives/data-table/table-body.js +32 -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-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.d.ts +6 -0
- package/dist/web/primitives/data-table/toolbar.js +53 -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.d.ts +3 -4
- package/dist/web/primitives/data-table.js +32 -252
- package/dist/web/primitives/file-value-list.js +1 -1
- 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/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/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 +54 -0
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { extensionSectionName, useExtensionSectionComponent } from "../app/extension-sections.js";
|
|
5
|
-
import { useReferenceLinkResolver } from "../app/reference-link.js";
|
|
6
|
-
import { rendererWarn } from "../dev-console.js";
|
|
7
|
-
import { computeListViewModel, EMPTY_VALUE_PLACEHOLDER } from "../headless/index.js";
|
|
8
|
-
import { useReferenceLookup } from "../hooks/use-reference-lookup.js";
|
|
2
|
+
import { useState } from "react";
|
|
9
3
|
import { useTranslation } from "../i18n.js";
|
|
10
4
|
import { usePageHeaderCompact, usePageHeaderSlotAvailable } from "../page-header-slot.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function isHeaderPromotableToolbarAction(action) {
|
|
19
|
-
return action.style === "primary" && !needsActionConfirm(action);
|
|
20
|
-
}
|
|
5
|
+
import { usePrimitives } from "../primitives.js";
|
|
6
|
+
import { renderReferenceLookupBridges } from "./render-list-reference-bridge.js";
|
|
7
|
+
import { tableChromeProps, tableFilterProps, tableLayoutProps, tableRowProps, tableSortPagingProps, } from "./render-list-table-props.js";
|
|
8
|
+
import { composeEmptyState, renderCreateButton, renderHeaderActions, renderMenuActionError, renderPageHeader, renderSearchInput, renderToolbarEnd, } from "./render-list-toolbar.js";
|
|
9
|
+
import { buildPromotedMenuItems, resolveItemNoun, resolveToolbarLabels, resolveToolbarTitle, splitToolbarActions, } from "./render-list-toolbar-model.js";
|
|
10
|
+
import { useRenderListColumns } from "./use-render-list-columns.js";
|
|
11
|
+
import { useRenderListSearch } from "./use-render-list-search.js";
|
|
21
12
|
export function RenderList(props) {
|
|
22
13
|
const { screen, entity, rows, featureName, translate: translateProp, onRowClick, emptyState, onCreate, createLabel, searchable = false, searchPlaceholder, description, emptyContent, narrowed = false, defaultFilterActive: defaultFilterActiveProp = false, itemNounKey, searchValue, onSearchChange, sort, onSortChange, pager, onReachEnd, loadingMore, hasMore, rowActions, rowActionMode, bulk, rowTone, toolbarActions, filterFacets, filterValues, onFilterChange, onFilterReset, dateRangeFacets, onDateRangeChange, chromeless, scrollBody, screenPadding, expandedRowIds, onToggleRowExpanded, renderExpandedRow, ...tableBlockProps } = props;
|
|
23
14
|
// As in RenderEdit, fall back to the i18next context for translation;
|
|
@@ -32,303 +23,96 @@ export function RenderList(props) {
|
|
|
32
23
|
// their own, so their busy and error state lives here.
|
|
33
24
|
const [menuActionBusyId, setMenuActionBusyId] = useState(null);
|
|
34
25
|
const [menuActionError, setMenuActionError] = useState(null);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// mount and can flush after a keystroke landing in the same commit,
|
|
41
|
-
// resetting localQ and swallowing the input.
|
|
42
|
-
const [syncedSearchValue, setSyncedSearchValue] = useState(searchValue);
|
|
43
|
-
// The parent may echo our own debounced emits back late (router/URL round
|
|
44
|
-
// trip), possibly after a newer emit already went out; adopting such a stale
|
|
45
|
-
// echo would erase keys typed in between. All unechoed emits are kept, and a
|
|
46
|
-
// matching echo drops itself and every older one. The render phase only reads
|
|
47
|
-
// the list (a discarded or repeated render must find the echo again); the
|
|
48
|
-
// matched entries are dropped in an effect.
|
|
49
|
-
const pendingEmitsRef = useRef([]);
|
|
50
|
-
if (searchValue !== syncedSearchValue) {
|
|
51
|
-
setSyncedSearchValue(searchValue);
|
|
52
|
-
if (!pendingEmitsRef.current.includes(searchValue ?? "")) {
|
|
53
|
-
setLocalQ(searchValue ?? "");
|
|
54
|
-
pendingEmitsRef.current = [];
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
const echoIndex = pendingEmitsRef.current.indexOf(syncedSearchValue ?? "");
|
|
59
|
-
if (echoIndex >= 0)
|
|
60
|
-
pendingEmitsRef.current.splice(0, echoIndex + 1);
|
|
61
|
-
}, [syncedSearchValue]);
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
if (onSearchChange === undefined)
|
|
64
|
-
return;
|
|
65
|
-
if (localQ === (searchValue ?? ""))
|
|
66
|
-
return;
|
|
67
|
-
// A late echo of an older emit changes searchValue but must not re-send
|
|
68
|
-
// the value that is already in flight.
|
|
69
|
-
if (pendingEmitsRef.current.at(-1) === localQ)
|
|
70
|
-
return;
|
|
71
|
-
const timer = setTimeout(() => {
|
|
72
|
-
pendingEmitsRef.current.push(localQ);
|
|
73
|
-
onSearchChange(localQ);
|
|
74
|
-
}, SEARCH_DEBOUNCE_MS);
|
|
75
|
-
return () => clearTimeout(timer);
|
|
76
|
-
}, [localQ, searchValue, onSearchChange]);
|
|
77
|
-
const vm = useMemo(() => computeListViewModel({ screen, entity, rows, translate, featureName }), [screen, entity, rows, translate, featureName]);
|
|
78
|
-
// Reference-field eager loading through bridge components.
|
|
79
|
-
// The Hooks rule requires useReferenceLookup for each reference column,
|
|
80
|
-
// but the number of columns varies with the schema. Each column therefore
|
|
81
|
-
// mounts a ReferenceLookupBridge that publishes its map to local state
|
|
82
|
-
// through onMap. Each bridge makes a constant number of hook calls (one),
|
|
83
|
-
// and React manages its lifecycle when the schema changes via key.
|
|
84
|
-
const referenceColumns = useMemo(() => vm.columns
|
|
85
|
-
.filter((c) => c.type === "reference" && c.refEntity !== undefined)
|
|
86
|
-
.map((c) => ({
|
|
87
|
-
field: c.field,
|
|
88
|
-
refEntity: c.refEntity ?? "",
|
|
89
|
-
// refFeature comes from parseRefTarget in the ViewModel
|
|
90
|
-
// (default = the current featureName).
|
|
91
|
-
refFeature: c.refFeature ?? featureName,
|
|
92
|
-
labelField: c.refLabelField ?? "id",
|
|
93
|
-
})), [vm.columns, featureName]);
|
|
94
|
-
const [referenceLookups, setReferenceLookups] = useState({});
|
|
95
|
-
const handleLookupMap = useCallback((field, lookup) => {
|
|
96
|
-
setReferenceLookups((prev) => {
|
|
97
|
-
// Skip the update when the lookup is identical. Otherwise the bridge
|
|
98
|
-
// would re-render and send the same map back until React entered
|
|
99
|
-
// an infinite loop.
|
|
100
|
-
const current = prev[field];
|
|
101
|
-
if (current?.map === lookup.map && current.complete === lookup.complete)
|
|
102
|
-
return prev;
|
|
103
|
-
return { ...prev, [field]: lookup };
|
|
104
|
-
});
|
|
105
|
-
}, []);
|
|
106
|
-
const resolveReferenceLink = useReferenceLinkResolver();
|
|
107
|
-
const enrichedColumns = useMemo(() => {
|
|
108
|
-
if (referenceColumns.length === 0)
|
|
109
|
-
return vm.columns;
|
|
110
|
-
return vm.columns.map((col) => {
|
|
111
|
-
if (col.type !== "reference")
|
|
112
|
-
return col;
|
|
113
|
-
// An author-declared renderer always takes precedence;
|
|
114
|
-
// the default is used only when none is set.
|
|
115
|
-
if (col.renderer !== undefined)
|
|
116
|
-
return col;
|
|
117
|
-
const published = referenceLookups[col.field];
|
|
118
|
-
const map = published?.map;
|
|
119
|
-
const unknownKey = UNRESOLVED_REFERENCE_LABEL_KEYS[`${col.refFeature ?? featureName}:${col.refEntity ?? ""}`];
|
|
120
|
-
// No raw id for entities that opt in: a placeholder while the lookup
|
|
121
|
-
// loads, the "unknown" label once the complete lookup found nothing. A
|
|
122
|
-
// capped lookup page cannot prove absence, so it keeps the raw id.
|
|
123
|
-
const unresolvedLabel = (id) => unknownKey === undefined
|
|
124
|
-
? id
|
|
125
|
-
: published === undefined
|
|
126
|
-
? EMPTY_VALUE_PLACEHOLDER
|
|
127
|
-
: published.complete
|
|
128
|
-
? translate(unknownKey)
|
|
129
|
-
: id;
|
|
130
|
-
const labelField = col.refLabelField ?? "id";
|
|
131
|
-
const refEntity = col.refEntity ?? "";
|
|
132
|
-
// Only a row the user can see is linkable: resolved by the server
|
|
133
|
-
// eagerload or by the lookup query. A multi-reference links per entry via linkItemsFor.
|
|
134
|
-
const linkFor = (value, row) => {
|
|
135
|
-
if (value === null || value === undefined || value === "" || Array.isArray(value)) {
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
const id = String(value);
|
|
139
|
-
const eagerloaded = eagerloadedRef(row, col.field);
|
|
140
|
-
// The server leaves a missing or unreadable target out; null is treated the same.
|
|
141
|
-
const resolved = (typeof eagerloaded === "object" &&
|
|
142
|
-
eagerloaded !== null &&
|
|
143
|
-
!Array.isArray(eagerloaded)) ||
|
|
144
|
-
map?.has(id);
|
|
145
|
-
return resolved === true ? resolveReferenceLink(refEntity, id) : undefined;
|
|
146
|
-
};
|
|
147
|
-
const linkItemsFor = (value, row) => {
|
|
148
|
-
if (!Array.isArray(value) || value.length === 0)
|
|
149
|
-
return undefined;
|
|
150
|
-
const eagerloaded = eagerloadedRef(row, col.field);
|
|
151
|
-
const eagerById = new Map(Array.isArray(eagerloaded) ? eagerloaded.map((r) => [String(r["id"]), r]) : []);
|
|
152
|
-
return value.map((v) => {
|
|
153
|
-
const id = String(v);
|
|
154
|
-
const target = eagerById.get(id);
|
|
155
|
-
const label = String(target?.[labelField] ?? map?.get(id) ?? unresolvedLabel(id));
|
|
156
|
-
// Only a row the user can see is linkable: server eagerload or lookup query.
|
|
157
|
-
const link = target !== undefined || map?.has(id) === true
|
|
158
|
-
? resolveReferenceLink(refEntity, id)
|
|
159
|
-
: undefined;
|
|
160
|
-
return { id, label, ...(link !== undefined && { link }) };
|
|
161
|
-
});
|
|
162
|
-
};
|
|
163
|
-
const renderer = (value, row) => {
|
|
164
|
-
// Server eager loading: when the server includes _refs, read the display
|
|
165
|
-
// value directly from the resolved row. This avoids a round trip
|
|
166
|
-
// through the bridge map and the limit:200 constraint. The
|
|
167
|
-
// EagerloadedRow type from @cosmicdrift/kumiko-framework/db defines
|
|
168
|
-
// the shape of _refs.
|
|
169
|
-
const resolved = eagerloadedRef(row, col.field);
|
|
170
|
-
if (Array.isArray(resolved) && resolved.length > 0) {
|
|
171
|
-
return resolved.map((r) => String(r[labelField] ?? r["id"] ?? "")).join(", ");
|
|
172
|
-
}
|
|
173
|
-
if (isPlainRecord(resolved)) {
|
|
174
|
-
return String(resolved[labelField] ?? resolved["id"] ?? "");
|
|
175
|
-
}
|
|
176
|
-
// Renderer-side fallback when server eager loading is not active.
|
|
177
|
-
if (Array.isArray(value)) {
|
|
178
|
-
if (value.length === 0)
|
|
179
|
-
return EMPTY_VALUE_PLACEHOLDER;
|
|
180
|
-
return value.map((v) => map?.get(String(v)) ?? unresolvedLabel(String(v))).join(", ");
|
|
181
|
-
}
|
|
182
|
-
if (value === null || value === undefined || value === "")
|
|
183
|
-
return EMPTY_VALUE_PLACEHOLDER;
|
|
184
|
-
const idStr = String(value);
|
|
185
|
-
return map?.get(idStr) ?? unresolvedLabel(idStr);
|
|
186
|
-
};
|
|
187
|
-
return { ...col, renderer, linkFor, linkItemsFor };
|
|
188
|
-
});
|
|
189
|
-
}, [
|
|
190
|
-
vm.columns,
|
|
191
|
-
referenceColumns,
|
|
192
|
-
referenceLookups,
|
|
193
|
-
resolveReferenceLink,
|
|
26
|
+
const { localQ, setLocalQ } = useRenderListSearch(searchValue, onSearchChange);
|
|
27
|
+
const { referenceColumns, handleLookupMap, enrichedVm } = useRenderListColumns({
|
|
28
|
+
screen,
|
|
29
|
+
entity,
|
|
30
|
+
rows,
|
|
194
31
|
featureName,
|
|
195
32
|
translate,
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const effectiveSearchPlaceholder =
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
? (count) => translate(nounKey, { count })
|
|
33
|
+
});
|
|
34
|
+
const labelInput = {
|
|
35
|
+
screen,
|
|
36
|
+
translate,
|
|
37
|
+
createLabel,
|
|
38
|
+
searchPlaceholder,
|
|
39
|
+
itemNounKey,
|
|
40
|
+
featureName,
|
|
41
|
+
};
|
|
42
|
+
const { effectiveCreateLabel, effectiveSearchPlaceholder } = resolveToolbarLabels(labelInput);
|
|
43
|
+
const itemNoun = resolveItemNoun(labelInput);
|
|
44
|
+
const toolbarStart = searchable
|
|
45
|
+
? renderSearchInput(Input, localQ, setLocalQ, effectiveSearchPlaceholder)
|
|
210
46
|
: undefined;
|
|
211
|
-
const toolbarStart = searchable ? (_jsx(Input, { kind: "text", id: "render-list-search", name: "search", value: localQ, onChange: setLocalQ, placeholder: effectiveSearchPlaceholder })) : undefined;
|
|
212
47
|
// Toolbar-end slot: an optional header slot (for example, a cap counter,
|
|
213
48
|
// at the left of the right-hand cluster), toolbar actions from the schema,
|
|
214
49
|
// and an optional "+ New" button at the right edge. The header slot lives
|
|
215
50
|
// inside the toolbar row, with the same card frame, rather than as loose
|
|
216
51
|
// text above the screen title. Items retain array order, and "+ New" comes
|
|
217
52
|
// last because it is the most common and prominent CTA.
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// useExtensionSectionComponent must not run conditionally in RenderList.
|
|
284
|
-
function ListHeaderSlotMount({ screen, }) {
|
|
285
|
-
const header = screen.slots?.header;
|
|
286
|
-
const name = header !== undefined ? extensionSectionName(header) : undefined;
|
|
287
|
-
const Component = useExtensionSectionComponent(name);
|
|
288
|
-
// A declared-but-unregistered header slot renders nothing; warn so the author
|
|
289
|
-
// finds the typo / missing clientFeatures.extensionSectionComponents entry,
|
|
290
|
-
// mirroring the diagnostic banner in render-edit's ExtensionSectionMount
|
|
291
|
-
// (the toolbar has no room for a banner, so a dev-warn it is).
|
|
292
|
-
useEffect(() => {
|
|
293
|
-
if (name !== undefined && Component === undefined) {
|
|
294
|
-
rendererWarn(`[kumiko] List header slot component "${name}" on screen "${screen.id}" is not registered in clientFeatures.extensionSectionComponents — the header slot renders nothing.`);
|
|
295
|
-
}
|
|
296
|
-
}, [name, Component, screen.id]);
|
|
297
|
-
if (Component === undefined)
|
|
298
|
-
return null;
|
|
299
|
-
// screenId lets a header control drive this list's URL-filter state
|
|
300
|
-
// (useListUrlState) — e.g. a tag filter narrowing the list to an id-set.
|
|
301
|
-
return _jsx(Component, { entityName: screen.entity, entityId: null, screenId: screen.id });
|
|
302
|
-
}
|
|
303
|
-
// Bridge component for each reference column. It is mounted once per
|
|
304
|
-
// column, calls useReferenceLookup unconditionally, and publishes the map
|
|
305
|
-
// to the parent through onMap. React manages mounting and unmounting when
|
|
306
|
-
// the schema changes through the key prop in the parent's map loop.
|
|
307
|
-
function ReferenceLookupBridge({ field, refEntity, labelField, featureName, onMap, }) {
|
|
308
|
-
const lookup = useReferenceLookup(featureName, refEntity, labelField);
|
|
309
|
-
// Use an effect rather than calling directly so setState runs outside the
|
|
310
|
-
// render path; otherwise React warns about updating one component while
|
|
311
|
-
// rendering another.
|
|
312
|
-
// Do not publish while loading or after a failed lookup: a missing map means
|
|
313
|
-
// "not resolved", whereas an empty published map means "nothing found".
|
|
314
|
-
// Only the latter may display "Unknown".
|
|
315
|
-
const resolved = !lookup.loading && !lookup.failed;
|
|
316
|
-
useEffect(() => {
|
|
317
|
-
if (resolved)
|
|
318
|
-
onMap(field, { map: lookup.map, complete: lookup.complete });
|
|
319
|
-
}, [field, lookup.map, lookup.complete, resolved, onMap]);
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
function isPlainRecord(value) {
|
|
323
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
324
|
-
}
|
|
325
|
-
// The server leaves a missing or unreadable target out of `_refs`, and a null
|
|
326
|
-
// entry means the same: both read as unresolved.
|
|
327
|
-
function eagerloadedRef(row, field) {
|
|
328
|
-
if (!isPlainRecord(row) || !isPlainRecord(row["_refs"]))
|
|
329
|
-
return undefined;
|
|
330
|
-
const ref = row["_refs"][field];
|
|
331
|
-
if (Array.isArray(ref))
|
|
332
|
-
return ref.filter(isPlainRecord);
|
|
333
|
-
return isPlainRecord(ref) ? ref : undefined;
|
|
53
|
+
const split = splitToolbarActions({
|
|
54
|
+
toolbarActions,
|
|
55
|
+
pageHeaderAvailable: PageHeader !== undefined,
|
|
56
|
+
pageHeaderSlotAvailable,
|
|
57
|
+
pageHeaderCompact,
|
|
58
|
+
createInPageHeader,
|
|
59
|
+
});
|
|
60
|
+
const toolbarModel = {
|
|
61
|
+
screen,
|
|
62
|
+
Button,
|
|
63
|
+
Dialog,
|
|
64
|
+
Banner,
|
|
65
|
+
onCreate,
|
|
66
|
+
createInPageHeader,
|
|
67
|
+
pageHeaderCompact,
|
|
68
|
+
effectiveCreateLabel,
|
|
69
|
+
split,
|
|
70
|
+
};
|
|
71
|
+
const createButton = renderCreateButton(toolbarModel);
|
|
72
|
+
const promotedMenuItems = buildPromotedMenuItems({
|
|
73
|
+
promotedToolbarActions: split.promotedToolbarActions,
|
|
74
|
+
promotedInOverflowMenu: split.promotedInOverflowMenu,
|
|
75
|
+
menuActionBusyId,
|
|
76
|
+
setMenuActionBusyId,
|
|
77
|
+
setMenuActionError,
|
|
78
|
+
});
|
|
79
|
+
const headerActions = renderHeaderActions(toolbarModel, createButton);
|
|
80
|
+
const toolbarEnd = renderToolbarEnd(toolbarModel, createButton);
|
|
81
|
+
const composedEmptyState = composeEmptyState({
|
|
82
|
+
Button,
|
|
83
|
+
emptyState,
|
|
84
|
+
onCreate,
|
|
85
|
+
emptyContent,
|
|
86
|
+
narrowed,
|
|
87
|
+
defaultFilterActive: defaultFilterActiveProp && onFilterReset !== undefined,
|
|
88
|
+
onFilterReset,
|
|
89
|
+
translate,
|
|
90
|
+
createInPageHeader,
|
|
91
|
+
effectiveCreateLabel,
|
|
92
|
+
});
|
|
93
|
+
const toolbarTitle = resolveToolbarTitle(screen, translate);
|
|
94
|
+
return (_jsxs(_Fragment, { children: [headerActions !== undefined &&
|
|
95
|
+
PageHeader !== undefined &&
|
|
96
|
+
renderPageHeader(PageHeader, headerActions, split.promotedInOverflowMenu, promotedMenuItems), menuActionError !== null && renderMenuActionError(Banner, menuActionError), renderReferenceLookupBridges(referenceColumns, handleLookupMap), _jsx(DataTable, { columns: enrichedVm.columns, rows: enrichedVm.rows, toolbarTitle: toolbarTitle, ...tableChromeProps({
|
|
97
|
+
onRowClick,
|
|
98
|
+
composedEmptyState,
|
|
99
|
+
toolbarStart,
|
|
100
|
+
description,
|
|
101
|
+
toolbarEnd,
|
|
102
|
+
itemNoun,
|
|
103
|
+
}), ...tableSortPagingProps({ sort, onSortChange, pager, onReachEnd, loadingMore, hasMore }), ...tableRowProps({ rowActions, rowActionMode, bulk, rowTone }), ...tableFilterProps({
|
|
104
|
+
filterFacets,
|
|
105
|
+
filterValues,
|
|
106
|
+
onFilterChange,
|
|
107
|
+
onFilterReset,
|
|
108
|
+
dateRangeFacets,
|
|
109
|
+
onDateRangeChange,
|
|
110
|
+
}), ...tableLayoutProps({
|
|
111
|
+
chromeless,
|
|
112
|
+
scrollBody,
|
|
113
|
+
screenPadding,
|
|
114
|
+
expandedRowIds,
|
|
115
|
+
onToggleRowExpanded,
|
|
116
|
+
renderExpandedRow,
|
|
117
|
+
}), ...tableBlockProps, testId: "render-list-table" })] }));
|
|
334
118
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EntityEditScreenDefinition } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
import type { EditFieldViewModel } from "../headless/index.ts";
|
|
3
|
+
export declare function useReferenceInputState(field: EditFieldViewModel, featureName: string, onChange: (value: unknown) => void): {
|
|
4
|
+
refFeature: string;
|
|
5
|
+
isMultiple: boolean;
|
|
6
|
+
options: {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}[];
|
|
10
|
+
loading: boolean;
|
|
11
|
+
createTarget: {
|
|
12
|
+
screen: EntityEditScreenDefinition;
|
|
13
|
+
entity: import("@cosmicdrift/kumiko-types/fields").EntityDefinition;
|
|
14
|
+
} | undefined;
|
|
15
|
+
createOpen: boolean;
|
|
16
|
+
setCreateOpen: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
17
|
+
createdWithoutIdWarning: boolean;
|
|
18
|
+
handleCreated: (newId: string | undefined) => void;
|
|
19
|
+
handleSearchChange: (q: string) => void;
|
|
20
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
import { useAppFeatures } from "../app/app-features-context.js";
|
|
3
|
+
import { screenAccessAllows } from "../app/screen-access.js";
|
|
4
|
+
import { useUserRoles } from "../context/user-roles-context.js";
|
|
5
|
+
import { rendererError } from "../dev-console.js";
|
|
6
|
+
import { REFERENCE_COMBOBOX_LIMIT } from "../hooks/reference-limits.js";
|
|
7
|
+
import { useQuery } from "../hooks/use-query.js";
|
|
8
|
+
import { referenceOptionSource } from "../hooks/use-reference-lookup.js";
|
|
9
|
+
import { stringArrayOf } from "./render-field-values.js";
|
|
10
|
+
export function useReferenceInputState(field, featureName, onChange) {
|
|
11
|
+
const refEntity = field.refEntity ?? "";
|
|
12
|
+
const refFeature = field.refFeature ?? featureName;
|
|
13
|
+
const isMultiple = field.refMultiple === true;
|
|
14
|
+
// refFeature may differ from featureName (for example,
|
|
15
|
+
// items.assignee → users:query:user:list). The default is the same feature,
|
|
16
|
+
// as provided by the ViewModel (parseRefTarget).
|
|
17
|
+
// refOptionsQuery replaces that source with an author-supplied handler whose
|
|
18
|
+
// rows already carry a composed `label`.
|
|
19
|
+
const { queryQn, labelKey } = referenceOptionSource(field, refFeature, refEntity);
|
|
20
|
+
// "+ Neu" in the combobox opens the referenced entity's create screen in a
|
|
21
|
+
// dialog instead of leaving the current form.
|
|
22
|
+
// refFeature may differ from the currently rendered feature; appFeatures
|
|
23
|
+
// (createKumikoApp) knows all feature schemas, not only the active screen.
|
|
24
|
+
// If the feature, screen, or entity is not registered, or allowCreate:false,
|
|
25
|
+
// onCreate remains undefined and the combobox renders without the footer.
|
|
26
|
+
const appFeatures = useAppFeatures();
|
|
27
|
+
const userRoles = useUserRoles();
|
|
28
|
+
const [createOpen, setCreateOpen] = useState(false);
|
|
29
|
+
const refTargetSchema = appFeatures.find((f) => f.featureName === refFeature);
|
|
30
|
+
const refCreateScreen = refTargetSchema?.screens.find((s) => s.type === "entityEdit" &&
|
|
31
|
+
s.entity === refEntity &&
|
|
32
|
+
s.allowCreate !== false &&
|
|
33
|
+
screenAccessAllows(s.access, userRoles));
|
|
34
|
+
const refEntityDef = refTargetSchema?.entities[refEntity];
|
|
35
|
+
// When the user types in the combobox, the server filters through the
|
|
36
|
+
// existing list-payload `search` parameter. The combobox debounces the
|
|
37
|
+
// keystroke itself (300ms) and calls onSearchChange.
|
|
38
|
+
// An empty initial state requests the first 50 items in default order.
|
|
39
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
40
|
+
const queryPayload = useMemo(() => searchTerm === ""
|
|
41
|
+
? { limit: REFERENCE_COMBOBOX_LIMIT }
|
|
42
|
+
: { limit: REFERENCE_COMBOBOX_LIMIT, search: searchTerm }, [searchTerm]);
|
|
43
|
+
const queryResult = useQuery(queryQn, queryPayload);
|
|
44
|
+
const handleSearchChange = useCallback((q) => setSearchTerm(q), []);
|
|
45
|
+
const createTarget = !field.readOnly && refCreateScreen !== undefined && refEntityDef !== undefined
|
|
46
|
+
? { screen: refCreateScreen, entity: refEntityDef }
|
|
47
|
+
: undefined;
|
|
48
|
+
const [createdWithoutIdWarning, setCreatedWithoutIdWarning] = useState(false);
|
|
49
|
+
const handleCreated = useCallback((newId) => {
|
|
50
|
+
setCreateOpen(false);
|
|
51
|
+
// Clear the search filter before refetching — the just-created record
|
|
52
|
+
// won't match whatever the user had typed pre-create, and would then
|
|
53
|
+
// be missing from `options` (shown as a raw id instead of a label).
|
|
54
|
+
setSearchTerm("");
|
|
55
|
+
queryResult.refetch().catch((err) => {
|
|
56
|
+
rendererError("render-field: refetch after create failed", err);
|
|
57
|
+
});
|
|
58
|
+
// Record was created server-side but the payload carried no id —
|
|
59
|
+
// can't auto-select it, surface that instead of failing silently.
|
|
60
|
+
setCreatedWithoutIdWarning(newId === undefined);
|
|
61
|
+
if (newId !== undefined) {
|
|
62
|
+
onChange(isMultiple ? [...stringArrayOf(field.value), newId] : newId);
|
|
63
|
+
}
|
|
64
|
+
}, [isMultiple, field.value, onChange, queryResult.refetch]);
|
|
65
|
+
const options = useMemo(() => {
|
|
66
|
+
const rows = queryResult.data?.rows ?? [];
|
|
67
|
+
return rows.map((row) => {
|
|
68
|
+
const idVal = String(row["id"] ?? "");
|
|
69
|
+
const label = String(row[labelKey] ?? idVal);
|
|
70
|
+
return { value: idVal, label };
|
|
71
|
+
});
|
|
72
|
+
}, [queryResult.data, labelKey]);
|
|
73
|
+
return {
|
|
74
|
+
refFeature,
|
|
75
|
+
isMultiple,
|
|
76
|
+
options,
|
|
77
|
+
loading: queryResult.loading,
|
|
78
|
+
createTarget,
|
|
79
|
+
createOpen,
|
|
80
|
+
setCreateOpen,
|
|
81
|
+
createdWithoutIdWarning,
|
|
82
|
+
handleCreated,
|
|
83
|
+
handleSearchChange,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FormValues } from "../headless/index.ts";
|
|
2
|
+
import type { RenderEditCore } from "./render-edit-render-model.ts";
|
|
3
|
+
export declare function useRenderEditControlledSync<TValues extends FormValues, TCtx>({ props, state, form, draft, }: RenderEditCore<TValues, TCtx>): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { useUnsavedInputSource } from "../leave-guard.js";
|
|
3
|
+
import { useWizardFormDraftRestore } from "./use-wizard-form-draft.js";
|
|
4
|
+
export function useRenderEditControlledSync({ props, state, form, draft, }) {
|
|
5
|
+
const { controller, snapshot } = form;
|
|
6
|
+
const { extensionDirty } = state;
|
|
7
|
+
const onDirtyChangeRef = useRef(props.onDirtyChange);
|
|
8
|
+
onDirtyChangeRef.current = props.onDirtyChange;
|
|
9
|
+
const hasUnsavedInput = snapshot.isDirty || extensionDirty;
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
onDirtyChangeRef.current?.(hasUnsavedInput);
|
|
12
|
+
}, [hasUnsavedInput]);
|
|
13
|
+
// Draft wizards keep their input server-side, so leaving loses nothing.
|
|
14
|
+
const persistsDraft = state.isWizard && props.screen.layout.draft === true;
|
|
15
|
+
useUnsavedInputSource(() => !persistsDraft && (controller.getSnapshot().isDirty || state.extensionDirtyRef.current));
|
|
16
|
+
useWizardFormDraftRestore({
|
|
17
|
+
screenId: props.screen.id,
|
|
18
|
+
isCreateMode: state.isCreateMode,
|
|
19
|
+
draftEnabled: state.draftEnabled,
|
|
20
|
+
dispatcher: state.dispatcher,
|
|
21
|
+
controller,
|
|
22
|
+
hasNavigatedRef: draft.refs.hasNavigatedRef,
|
|
23
|
+
setRawStep: state.setRawStep,
|
|
24
|
+
draftState: state.draftState,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type MutableRefObject } from "react";
|
|
2
|
+
import type { FormValues } from "../headless/index.ts";
|
|
3
|
+
import type { ResolvedRenderEditProps } from "./render-edit-render-model.ts";
|
|
4
|
+
import type { RenderEditForm } from "./use-render-edit-form.ts";
|
|
5
|
+
import type { RenderEditState } from "./use-render-edit-state.ts";
|
|
6
|
+
export type RenderEditRefs<TValues extends FormValues> = {
|
|
7
|
+
readonly onChangeRef: MutableRefObject<ResolvedRenderEditProps<TValues>["onChange"]>;
|
|
8
|
+
readonly deliveredControlsToRef: MutableRefObject<ResolvedRenderEditProps<TValues>["onControlsReady"]>;
|
|
9
|
+
readonly onControlsReadyRef: MutableRefObject<ResolvedRenderEditProps<TValues>["onControlsReady"]>;
|
|
10
|
+
readonly hasNavigatedRef: MutableRefObject<boolean>;
|
|
11
|
+
readonly submittedRef: MutableRefObject<boolean>;
|
|
12
|
+
readonly isSubmittingRef: MutableRefObject<boolean>;
|
|
13
|
+
readonly scopeFieldNamesRef: MutableRefObject<readonly string[] | undefined>;
|
|
14
|
+
readonly saveDraftRef: MutableRefObject<(stepIndex: number) => void>;
|
|
15
|
+
readonly handleSubmitRef: MutableRefObject<(options?: {
|
|
16
|
+
readonly saveNow?: boolean;
|
|
17
|
+
}) => Promise<void>>;
|
|
18
|
+
readonly wizardNextRef: MutableRefObject<() => void>;
|
|
19
|
+
readonly wizardBackRef: MutableRefObject<() => void>;
|
|
20
|
+
readonly currentStepRef: MutableRefObject<number>;
|
|
21
|
+
readonly draftSaveTimerRef: MutableRefObject<ReturnType<typeof setTimeout> | null>;
|
|
22
|
+
};
|
|
23
|
+
export type RenderEditDraft<TValues extends FormValues> = {
|
|
24
|
+
readonly refs: RenderEditRefs<TValues>;
|
|
25
|
+
readonly scopedValidate: () => boolean;
|
|
26
|
+
readonly markSaved: (fields?: readonly string[]) => void;
|
|
27
|
+
readonly patchAndScheduleDraftSave: (partial: Partial<TValues>) => void;
|
|
28
|
+
};
|
|
29
|
+
export declare function useRenderEditDraftSave<TValues extends FormValues, TCtx>(props: ResolvedRenderEditProps<TValues, TCtx>, state: RenderEditState, form: RenderEditForm<TValues>): RenderEditDraft<TValues>;
|