@cosmicdrift/kumiko-renderer 0.388.0 → 0.389.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-list-body.js +51 -46
- 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-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-field.js +6 -5
- 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.d.ts +13 -29
- package/dist/components/render-list.js +42 -50
- 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/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/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 +34 -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-sort-select.d.ts +14 -0
- package/dist/web/primitives/data-table/card-sort-select.js +40 -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 +46 -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/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-chrome.d.ts +13 -0
- package/dist/web/primitives/data-table/table-chrome.js +30 -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/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/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-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 +1 -1
- package/dist/web/primitives/data-table.js +122 -144
- 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/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/pdf-preview.js +9 -4
- package/package.json +5 -5
- package/src/changes.json +109 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ListColumnPin, ListColumnSpec, ListTableOptions } from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
+
import type { ListStateStore } from "../components/list-state-store.tsx";
|
|
3
|
+
import type { DataTableColumnPins, DataTableColumnWidths, DataTableDensity, DataTableViewColumn } from "../data-table-view-types.ts";
|
|
4
|
+
export type ListViewSchemaColumn = {
|
|
5
|
+
readonly field: string;
|
|
6
|
+
readonly hidden?: boolean;
|
|
7
|
+
readonly pinned?: ListColumnPin;
|
|
8
|
+
readonly width?: number;
|
|
9
|
+
readonly minWidth?: number;
|
|
10
|
+
};
|
|
11
|
+
export type ListViewState = {
|
|
12
|
+
/** Undefined unless `table.columnPicker` is on. */
|
|
13
|
+
readonly columns: readonly DataTableViewColumn[] | undefined;
|
|
14
|
+
readonly density: DataTableDensity;
|
|
15
|
+
readonly userChoosesDensity: boolean;
|
|
16
|
+
readonly setColumns: (next: readonly DataTableViewColumn[]) => void;
|
|
17
|
+
readonly setDensity: (next: DataTableDensity) => void;
|
|
18
|
+
/** Undefined unless `table.pinning` is on. */
|
|
19
|
+
readonly pins: DataTableColumnPins | undefined;
|
|
20
|
+
readonly setPins: (next: DataTableColumnPins) => void;
|
|
21
|
+
/** Undefined unless `table.resizing` is on. */
|
|
22
|
+
readonly widths: DataTableColumnWidths | undefined;
|
|
23
|
+
readonly minWidths: DataTableColumnWidths | undefined;
|
|
24
|
+
readonly setWidth: (field: string, width: number) => void;
|
|
25
|
+
/** Active group field, "" = not grouped. */
|
|
26
|
+
readonly groupBy: string;
|
|
27
|
+
/** Undefined unless `table.grouping.userChoice` is on. */
|
|
28
|
+
readonly groupByFields: readonly string[] | undefined;
|
|
29
|
+
readonly setGroupBy: (next: string) => void;
|
|
30
|
+
readonly resetView: () => void;
|
|
31
|
+
readonly viewIsDefault: boolean;
|
|
32
|
+
};
|
|
33
|
+
export declare function listViewSchemaColumns(columns: readonly ListColumnSpec[]): ListViewSchemaColumn[];
|
|
34
|
+
type ViewStateArgs = {
|
|
35
|
+
readonly storeKey: string;
|
|
36
|
+
readonly store: ListStateStore;
|
|
37
|
+
readonly table: ListTableOptions;
|
|
38
|
+
readonly schemaColumns: readonly ListViewSchemaColumn[];
|
|
39
|
+
};
|
|
40
|
+
/** Column choice, order and density of one list. Lives in the list-state store
|
|
41
|
+
* (never in the URL) and is independent of `rememberState`. */
|
|
42
|
+
export declare function useListViewState(args: ViewStateArgs): ListViewState;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { normalizeListColumn } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import { clampWidth, DEFAULT_MIN_COLUMN_WIDTH_PX, defaultPins, defaultWidths, mergeSavedWidths, minWidthsOf, sameRecord, } from "./list-column-layout.js";
|
|
4
|
+
import { sanitizeStoredListState } from "./list-state-schema.js";
|
|
5
|
+
export function listViewSchemaColumns(columns) {
|
|
6
|
+
return columns.map((column) => {
|
|
7
|
+
const { field, hidden, pinned, width, minWidth } = normalizeListColumn(column);
|
|
8
|
+
return {
|
|
9
|
+
field,
|
|
10
|
+
...(hidden === true && { hidden }),
|
|
11
|
+
...(pinned !== undefined && { pinned }),
|
|
12
|
+
...(width !== undefined && { width }),
|
|
13
|
+
...(minWidth !== undefined && { minWidth }),
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function schemaDefaultColumns(schema) {
|
|
18
|
+
return schema.map((column) => ({ field: column.field, visible: column.hidden !== true }));
|
|
19
|
+
}
|
|
20
|
+
/** Saved order wins; a column the saved view has never seen takes its schema
|
|
21
|
+
* position (after its schema predecessor) with its schema default. */
|
|
22
|
+
function mergeSavedColumns(schema, saved) {
|
|
23
|
+
const merged = [...saved];
|
|
24
|
+
const present = new Set(saved.map((column) => column.field));
|
|
25
|
+
schema.forEach((column, index) => {
|
|
26
|
+
// skip: column already saved
|
|
27
|
+
if (present.has(column.field))
|
|
28
|
+
return;
|
|
29
|
+
const predecessor = schema[index - 1];
|
|
30
|
+
const predecessorAt = predecessor === undefined ? -1 : merged.findIndex((m) => m.field === predecessor.field);
|
|
31
|
+
merged.splice(predecessorAt + 1, 0, {
|
|
32
|
+
field: column.field,
|
|
33
|
+
visible: column.hidden !== true,
|
|
34
|
+
});
|
|
35
|
+
present.add(column.field);
|
|
36
|
+
});
|
|
37
|
+
return merged;
|
|
38
|
+
}
|
|
39
|
+
function sameColumns(a, b) {
|
|
40
|
+
return (a.length === b.length &&
|
|
41
|
+
a.every((c, i) => c.field === b[i]?.field && c.visible === b[i]?.visible));
|
|
42
|
+
}
|
|
43
|
+
function hasVisibleColumn(columns) {
|
|
44
|
+
return columns.some((column) => column.visible);
|
|
45
|
+
}
|
|
46
|
+
function groupByChoices(grouping) {
|
|
47
|
+
if (grouping?.userChoice !== true)
|
|
48
|
+
return undefined;
|
|
49
|
+
const offered = [
|
|
50
|
+
...(grouping.field !== undefined ? [grouping.field] : []),
|
|
51
|
+
...(grouping.fields ?? []),
|
|
52
|
+
];
|
|
53
|
+
return [...new Set(offered)];
|
|
54
|
+
}
|
|
55
|
+
function isOfferedGroupBy(grouping, saved) {
|
|
56
|
+
if (saved === undefined)
|
|
57
|
+
return false;
|
|
58
|
+
const choices = groupByChoices(grouping);
|
|
59
|
+
return choices !== undefined && (saved === "" || choices.includes(saved));
|
|
60
|
+
}
|
|
61
|
+
function readInitialView(args, defaults) {
|
|
62
|
+
const saved = sanitizeStoredListState(args.store.read(args.storeKey), {
|
|
63
|
+
columnFields: args.schemaColumns.map((column) => column.field),
|
|
64
|
+
filterFields: [],
|
|
65
|
+
})?.view;
|
|
66
|
+
const savedGroupBy = saved?.groupBy;
|
|
67
|
+
const merged = args.table.columnPicker === true && saved?.columns !== undefined
|
|
68
|
+
? mergeSavedColumns(args.schemaColumns, saved.columns)
|
|
69
|
+
: defaults.columns;
|
|
70
|
+
return {
|
|
71
|
+
columns: hasVisibleColumn(merged) ? merged : defaults.columns,
|
|
72
|
+
density: args.table.density?.userChoice === true && saved?.density ? saved.density : defaults.density,
|
|
73
|
+
// Saved pins replace the schema pins entirely, so the user can release a schema pin.
|
|
74
|
+
pins: args.table.pinning === true && saved?.pins !== undefined ? saved.pins : defaults.pins,
|
|
75
|
+
widths: args.table.resizing === true && saved?.widths !== undefined
|
|
76
|
+
? mergeSavedWidths(defaults.widths, saved.widths, minWidthsOf(args.schemaColumns))
|
|
77
|
+
: defaults.widths,
|
|
78
|
+
groupBy: isOfferedGroupBy(args.table.grouping, savedGroupBy) ? savedGroupBy : defaults.groupBy,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Column choice, order and density of one list. Lives in the list-state store
|
|
82
|
+
* (never in the URL) and is independent of `rememberState`. */
|
|
83
|
+
export function useListViewState(args) {
|
|
84
|
+
const { storeKey, store, table, schemaColumns } = args;
|
|
85
|
+
const columnPicker = table.columnPicker === true;
|
|
86
|
+
const userChoosesDensity = table.density?.userChoice === true;
|
|
87
|
+
const pinning = table.pinning === true;
|
|
88
|
+
const resizing = table.resizing === true;
|
|
89
|
+
const groupByFields = useMemo(() => groupByChoices(table.grouping), [table.grouping]);
|
|
90
|
+
const userChoosesGroupBy = groupByFields !== undefined;
|
|
91
|
+
const minWidths = useMemo(() => minWidthsOf(schemaColumns), [schemaColumns]);
|
|
92
|
+
const defaults = useMemo(() => ({
|
|
93
|
+
columns: schemaDefaultColumns(schemaColumns),
|
|
94
|
+
density: table.density?.default ?? "comfortable",
|
|
95
|
+
pins: pinning ? defaultPins(schemaColumns) : {},
|
|
96
|
+
widths: resizing ? defaultWidths(schemaColumns, minWidths) : {},
|
|
97
|
+
groupBy: table.grouping?.field ?? "",
|
|
98
|
+
}), [schemaColumns, table.density?.default, pinning, resizing, minWidths, table.grouping?.field]);
|
|
99
|
+
const [seeded, setSeeded] = useState(() => ({
|
|
100
|
+
storeKey,
|
|
101
|
+
view: readInitialView(args, defaults),
|
|
102
|
+
}));
|
|
103
|
+
const view = seeded.storeKey === storeKey ? seeded.view : readInitialView(args, defaults);
|
|
104
|
+
if (seeded.storeKey !== storeKey)
|
|
105
|
+
setSeeded({ storeKey, view });
|
|
106
|
+
const commit = useCallback((next) => {
|
|
107
|
+
setSeeded({ storeKey, view: next });
|
|
108
|
+
store.write(storeKey, {
|
|
109
|
+
...store.read(storeKey),
|
|
110
|
+
view: {
|
|
111
|
+
...(columnPicker && { columns: next.columns }),
|
|
112
|
+
...(userChoosesDensity && { density: next.density }),
|
|
113
|
+
...(pinning && { pins: next.pins }),
|
|
114
|
+
...(resizing && { widths: next.widths }),
|
|
115
|
+
...(userChoosesGroupBy && { groupBy: next.groupBy }),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}, [store, storeKey, columnPicker, userChoosesDensity, pinning, resizing, userChoosesGroupBy]);
|
|
119
|
+
const setColumns = useCallback((next) => {
|
|
120
|
+
// skip: a view must keep at least one visible column
|
|
121
|
+
if (!hasVisibleColumn(next))
|
|
122
|
+
return;
|
|
123
|
+
commit({ ...view, columns: next });
|
|
124
|
+
}, [commit, view]);
|
|
125
|
+
const setDensity = useCallback((next) => commit({ ...view, density: next }), [commit, view]);
|
|
126
|
+
const setPins = useCallback((next) => commit({ ...view, pins: next }), [commit, view]);
|
|
127
|
+
const setWidth = useCallback((field, width) => commit({
|
|
128
|
+
...view,
|
|
129
|
+
widths: {
|
|
130
|
+
...view.widths,
|
|
131
|
+
[field]: clampWidth(width, minWidths[field] ?? DEFAULT_MIN_COLUMN_WIDTH_PX),
|
|
132
|
+
},
|
|
133
|
+
}), [commit, view, minWidths]);
|
|
134
|
+
const setGroupBy = useCallback((next) => commit({ ...view, groupBy: next }), [commit, view]);
|
|
135
|
+
const resetView = useCallback(() => {
|
|
136
|
+
setSeeded({ storeKey, view: defaults });
|
|
137
|
+
const { view: _dropped, ...rest } = store.read(storeKey) ?? {};
|
|
138
|
+
store.write(storeKey, rest);
|
|
139
|
+
}, [store, storeKey, defaults]);
|
|
140
|
+
return {
|
|
141
|
+
columns: columnPicker ? view.columns : undefined,
|
|
142
|
+
density: view.density,
|
|
143
|
+
userChoosesDensity,
|
|
144
|
+
setColumns,
|
|
145
|
+
setDensity,
|
|
146
|
+
pins: pinning ? view.pins : undefined,
|
|
147
|
+
setPins,
|
|
148
|
+
widths: resizing ? view.widths : undefined,
|
|
149
|
+
minWidths: resizing ? minWidths : undefined,
|
|
150
|
+
setWidth,
|
|
151
|
+
groupBy: view.groupBy,
|
|
152
|
+
groupByFields,
|
|
153
|
+
setGroupBy,
|
|
154
|
+
resetView,
|
|
155
|
+
viewIsDefault: view.density === defaults.density &&
|
|
156
|
+
(!columnPicker || sameColumns(view.columns, defaults.columns)) &&
|
|
157
|
+
(!pinning || sameRecord(view.pins, defaults.pins)) &&
|
|
158
|
+
(!resizing || sameRecord(view.widths, defaults.widths)) &&
|
|
159
|
+
view.groupBy === defaults.groupBy,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EditRelatedListSectionViewModel, Translate } from "../headless/index.ts";
|
|
2
|
+
import type { DataTableSort } from "../primitives.tsx";
|
|
3
|
+
import { type ListAggregates } from "./use-list-aggregates.ts";
|
|
4
|
+
type Row = Readonly<Record<string, unknown>>;
|
|
5
|
+
export type RelatedListRows = {
|
|
6
|
+
readonly rows: readonly Row[];
|
|
7
|
+
readonly firstLoad: boolean;
|
|
8
|
+
readonly error: ListAggregates["error"];
|
|
9
|
+
readonly hasData: boolean;
|
|
10
|
+
/** The one-shot fetch stopped early, so the client-side sort covers part of the set. */
|
|
11
|
+
readonly truncated: boolean;
|
|
12
|
+
readonly aggregates: ListAggregates;
|
|
13
|
+
readonly refetchSelfAndHost: () => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
/** Rows of a relatedList section: one fetch sorted on the client, or the loaded
|
|
16
|
+
* rows of its server groups in server order when `table.grouping` is active. */
|
|
17
|
+
export declare function useRelatedListRows(args: {
|
|
18
|
+
readonly section: EditRelatedListSectionViewModel;
|
|
19
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
20
|
+
readonly groupField: string | undefined;
|
|
21
|
+
readonly sort: DataTableSort | null;
|
|
22
|
+
readonly locale: string | undefined;
|
|
23
|
+
readonly refreshToken: unknown;
|
|
24
|
+
readonly onAfterWrite: (() => Promise<void> | void) | undefined;
|
|
25
|
+
readonly translate: Translate;
|
|
26
|
+
}): RelatedListRows;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { normalizeListColumn } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
|
+
import { sortByAccessor } from "../sort-by-accessor.js";
|
|
4
|
+
import { useListAggregates } from "./use-list-aggregates.js";
|
|
5
|
+
import { useQuery } from "./use-query.js";
|
|
6
|
+
const DEFAULT_GROUP_PAGE_SIZE = 50;
|
|
7
|
+
// A handler that omits `nextCursor` (or reports a `total` the fetched rows
|
|
8
|
+
// already cover) has no further rows; only a real cursor marks truncation.
|
|
9
|
+
function isTruncatedFetch(data) {
|
|
10
|
+
return (data !== null &&
|
|
11
|
+
typeof data.nextCursor === "string" &&
|
|
12
|
+
(data.total === undefined || data.total > data.rows.length));
|
|
13
|
+
}
|
|
14
|
+
/** Rows of a relatedList section: one fetch sorted on the client, or the loaded
|
|
15
|
+
* rows of its server groups in server order when `table.grouping` is active. */
|
|
16
|
+
export function useRelatedListRows(args) {
|
|
17
|
+
const { section, payload, groupField, sort, locale, refreshToken, onAfterWrite } = args;
|
|
18
|
+
const aggregates = useListAggregates({
|
|
19
|
+
queryType: section.query,
|
|
20
|
+
payload,
|
|
21
|
+
table: section.table,
|
|
22
|
+
groupField,
|
|
23
|
+
pageSize: section.pageSize ?? DEFAULT_GROUP_PAGE_SIZE,
|
|
24
|
+
paging: "cursor",
|
|
25
|
+
translate: args.translate,
|
|
26
|
+
});
|
|
27
|
+
const { grouped, refresh: refreshAggregates } = aggregates;
|
|
28
|
+
const rowsQuery = useQuery(section.query, payload, { enabled: !grouped });
|
|
29
|
+
const ownRefetch = rowsQuery.refetch;
|
|
30
|
+
const refetchOwn = useCallback(async () => {
|
|
31
|
+
refreshAggregates();
|
|
32
|
+
if (!grouped)
|
|
33
|
+
await ownRefetch();
|
|
34
|
+
}, [refreshAggregates, grouped, ownRefetch]);
|
|
35
|
+
const lastRefreshTokenRef = useRef(refreshToken);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
// skip: the token has not changed since the last refetch
|
|
38
|
+
if (lastRefreshTokenRef.current === refreshToken)
|
|
39
|
+
return;
|
|
40
|
+
lastRefreshTokenRef.current = refreshToken;
|
|
41
|
+
void refetchOwn();
|
|
42
|
+
}, [refreshToken, refetchOwn]);
|
|
43
|
+
const refetchSelfAndHost = useCallback(async () => {
|
|
44
|
+
await refetchOwn();
|
|
45
|
+
await onAfterWrite?.();
|
|
46
|
+
}, [refetchOwn, onAfterWrite]);
|
|
47
|
+
const sortAccessors = useMemo(() => {
|
|
48
|
+
const accessors = {};
|
|
49
|
+
for (const col of section.columns) {
|
|
50
|
+
const field = normalizeListColumn(col).field;
|
|
51
|
+
accessors[field] = (row) => {
|
|
52
|
+
const value = row[field];
|
|
53
|
+
if (value === null || value === undefined)
|
|
54
|
+
return null;
|
|
55
|
+
return typeof value === "number" ? value : String(value);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return accessors;
|
|
59
|
+
}, [section.columns]);
|
|
60
|
+
const sortedRows = useMemo(() => sortByAccessor(rowsQuery.data?.rows ?? [], sort, sortAccessors, locale), [rowsQuery.data, sort, sortAccessors, locale]);
|
|
61
|
+
if (grouped) {
|
|
62
|
+
return {
|
|
63
|
+
rows: aggregates.rows,
|
|
64
|
+
firstLoad: aggregates.loading,
|
|
65
|
+
error: aggregates.error,
|
|
66
|
+
hasData: aggregates.serverGroups !== undefined,
|
|
67
|
+
truncated: false,
|
|
68
|
+
aggregates,
|
|
69
|
+
refetchSelfAndHost,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
rows: sortedRows,
|
|
74
|
+
firstLoad: rowsQuery.loading && rowsQuery.data === null,
|
|
75
|
+
error: rowsQuery.error,
|
|
76
|
+
hasData: rowsQuery.data !== null,
|
|
77
|
+
truncated: isTruncatedFetch(rowsQuery.data),
|
|
78
|
+
aggregates,
|
|
79
|
+
refetchSelfAndHost,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EditRelatedListSectionViewModel } from "../headless/index.ts";
|
|
2
|
+
/** Search, filters and sort of a relatedList section (component-local: it has
|
|
3
|
+
* no URL namespace) plus the DataTable props of its `table` block. */
|
|
4
|
+
export declare function useRelatedListState(section: EditRelatedListSectionViewModel, parentScreenId: string | undefined): {
|
|
5
|
+
search: string;
|
|
6
|
+
filters: Readonly<Record<string, readonly string[]>>;
|
|
7
|
+
sort: import("./use-list-url-state.ts").ListSort | null;
|
|
8
|
+
setSearch: (search: string) => void;
|
|
9
|
+
setFilters: import("react").Dispatch<import("react").SetStateAction<Readonly<Record<string, readonly string[]>>>>;
|
|
10
|
+
setSort: (sort: import("./use-list-url-state.ts").ListSort | null) => void;
|
|
11
|
+
reset: () => void;
|
|
12
|
+
atDefault: boolean;
|
|
13
|
+
listTable: import("./use-list-table.ts").ListTableProps;
|
|
14
|
+
groupField: string | undefined;
|
|
15
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useListStateStore } from "../components/list-state-store.js";
|
|
3
|
+
import { useListTable } from "./use-list-table.js";
|
|
4
|
+
import { listViewSchemaColumns } from "./use-list-view-state.js";
|
|
5
|
+
import { useRememberedLocalListState } from "./use-remembered-local-list-state.js";
|
|
6
|
+
/** Search, filters and sort of a relatedList section (component-local: it has
|
|
7
|
+
* no URL namespace) plus the DataTable props of its `table` block. */
|
|
8
|
+
export function useRelatedListState(section, parentScreenId) {
|
|
9
|
+
const store = useListStateStore();
|
|
10
|
+
const storeKey = `${parentScreenId ?? ""}#${section.id ?? section.query}`;
|
|
11
|
+
const defaultSort = useMemo(() => section.defaultSort ?? null, [section.defaultSort]);
|
|
12
|
+
const schemaColumns = useMemo(() => listViewSchemaColumns(section.columns), [section.columns]);
|
|
13
|
+
const known = useMemo(() => ({
|
|
14
|
+
columnFields: schemaColumns.map((column) => column.field),
|
|
15
|
+
filterFields: (section.facets ?? []).map((facet) => facet.field),
|
|
16
|
+
}), [schemaColumns, section.facets]);
|
|
17
|
+
const local = useRememberedLocalListState({
|
|
18
|
+
storeKey,
|
|
19
|
+
store,
|
|
20
|
+
remember: section.table?.rememberState === true,
|
|
21
|
+
defaultSort,
|
|
22
|
+
known,
|
|
23
|
+
});
|
|
24
|
+
const { props: listTable, groupField } = useListTable({
|
|
25
|
+
storeKey,
|
|
26
|
+
table: section.table,
|
|
27
|
+
schemaColumns,
|
|
28
|
+
local: { atDefault: local.atDefault, reset: local.reset },
|
|
29
|
+
});
|
|
30
|
+
return { ...local, listTable, groupField };
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ListStateStore } from "../components/list-state-store.tsx";
|
|
2
|
+
import { type KnownListFields } from "./list-state-schema.ts";
|
|
3
|
+
type RememberedListParamsArgs = {
|
|
4
|
+
readonly screenId: string;
|
|
5
|
+
readonly enabled: boolean;
|
|
6
|
+
readonly store: ListStateStore;
|
|
7
|
+
readonly known: KnownListFields;
|
|
8
|
+
};
|
|
9
|
+
/** URL stays the source of truth: the stored list params are copied into the
|
|
10
|
+
* URL once when the screen opens without any, and every later URL change is
|
|
11
|
+
* written back. A link that already carries list params is never overridden. */
|
|
12
|
+
export declare function useRememberedListParams({ screenId, enabled, store, known, }: RememberedListParamsArgs): void;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { useNav } from "../app/nav.js";
|
|
3
|
+
import { listParamKeys } from "./list-reset.js";
|
|
4
|
+
import { sanitizeStoredListState } from "./list-state-schema.js";
|
|
5
|
+
function stripScreenPrefix(params, screenId) {
|
|
6
|
+
const prefix = `${screenId}.`;
|
|
7
|
+
return Object.fromEntries(listParamKeys(params, screenId).flatMap((key) => {
|
|
8
|
+
const value = params[key];
|
|
9
|
+
return value === undefined ? [] : [[key.slice(prefix.length), value]];
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
function sameParams(a, b) {
|
|
13
|
+
const keys = Object.keys(a);
|
|
14
|
+
return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);
|
|
15
|
+
}
|
|
16
|
+
/** URL stays the source of truth: the stored list params are copied into the
|
|
17
|
+
* URL once when the screen opens without any, and every later URL change is
|
|
18
|
+
* written back. A link that already carries list params is never overridden. */
|
|
19
|
+
export function useRememberedListParams({ screenId, enabled, store, known, }) {
|
|
20
|
+
const nav = useNav();
|
|
21
|
+
const current = useMemo(() => stripScreenPrefix(nav.searchParams, screenId), [nav.searchParams, screenId]);
|
|
22
|
+
const phaseRef = useRef({ screenId, phase: "pending" });
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
// skip: remembering is not enabled
|
|
25
|
+
if (!enabled)
|
|
26
|
+
return;
|
|
27
|
+
if (phaseRef.current.screenId !== screenId)
|
|
28
|
+
phaseRef.current = { screenId, phase: "pending" };
|
|
29
|
+
const state = phaseRef.current;
|
|
30
|
+
const currentIsEmpty = Object.keys(current).length === 0;
|
|
31
|
+
if (state.phase === "pending" && currentIsEmpty) {
|
|
32
|
+
const stored = sanitizeStoredListState(store.read(screenId), known)?.params;
|
|
33
|
+
if (stored !== undefined && Object.keys(stored).length > 0) {
|
|
34
|
+
state.phase = "restoring";
|
|
35
|
+
nav.setSearchParams(Object.fromEntries(Object.entries(stored).map(([k, v]) => [`${screenId}.${k}`, v])));
|
|
36
|
+
// skip: restore started, effect reruns with the restored URL
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// skip: waiting for the restored URL to arrive
|
|
41
|
+
if (state.phase === "restoring" && currentIsEmpty)
|
|
42
|
+
return;
|
|
43
|
+
state.phase = "live";
|
|
44
|
+
const existing = store.read(screenId);
|
|
45
|
+
// skip: nothing stored and nothing to store
|
|
46
|
+
if (existing?.params === undefined && currentIsEmpty)
|
|
47
|
+
return;
|
|
48
|
+
// skip: stored state already matches the URL
|
|
49
|
+
if (existing?.params !== undefined && sameParams(existing.params, current))
|
|
50
|
+
return;
|
|
51
|
+
store.write(screenId, { ...existing, params: current });
|
|
52
|
+
}, [enabled, screenId, current, known, store, nav]);
|
|
53
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Dispatch, type SetStateAction } from "react";
|
|
2
|
+
import type { ListStateStore } from "../components/list-state-store.tsx";
|
|
3
|
+
import { type KnownListFields } from "./list-state-schema.ts";
|
|
4
|
+
import type { ListSort } from "./use-list-url-state.ts";
|
|
5
|
+
type LocalFilters = Readonly<Record<string, readonly string[]>>;
|
|
6
|
+
type LocalListStateArgs = {
|
|
7
|
+
readonly storeKey: string;
|
|
8
|
+
readonly store: ListStateStore;
|
|
9
|
+
/** `table.rememberState`: seeds from the store and writes every change back. */
|
|
10
|
+
readonly remember: boolean;
|
|
11
|
+
readonly defaultSort: ListSort | null;
|
|
12
|
+
readonly known: KnownListFields;
|
|
13
|
+
};
|
|
14
|
+
/** Search, facet filters and sort of a list whose state is component-local
|
|
15
|
+
* (relatedList has no URL namespace). Same stored `params` format as the URL lists. */
|
|
16
|
+
export declare function useRememberedLocalListState({ storeKey, store, remember, defaultSort, known, }: LocalListStateArgs): {
|
|
17
|
+
search: string;
|
|
18
|
+
filters: LocalFilters;
|
|
19
|
+
sort: ListSort | null;
|
|
20
|
+
setSearch: (search: string) => void;
|
|
21
|
+
setFilters: Dispatch<SetStateAction<Readonly<Record<string, readonly string[]>>>>;
|
|
22
|
+
setSort: (sort: ListSort | null) => void;
|
|
23
|
+
reset: () => void;
|
|
24
|
+
atDefault: boolean;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { sanitizeStoredListState } from "./list-state-schema.js";
|
|
3
|
+
const FILTER_PREFIX = "f.";
|
|
4
|
+
function sameSort(a, b) {
|
|
5
|
+
return a === b || (a !== null && b !== null && a.field === b.field && a.dir === b.dir);
|
|
6
|
+
}
|
|
7
|
+
function toParams(state, defaultSort) {
|
|
8
|
+
const params = {};
|
|
9
|
+
if (state.search !== "")
|
|
10
|
+
params["q"] = state.search;
|
|
11
|
+
if (!sameSort(state.sort, defaultSort) && state.sort !== null) {
|
|
12
|
+
params["sort"] = state.sort.field;
|
|
13
|
+
params["dir"] = state.sort.dir;
|
|
14
|
+
}
|
|
15
|
+
for (const [field, values] of Object.entries(state.filters)) {
|
|
16
|
+
if (values.length > 0)
|
|
17
|
+
params[`${FILTER_PREFIX}${field}`] = values.join(",");
|
|
18
|
+
}
|
|
19
|
+
return params;
|
|
20
|
+
}
|
|
21
|
+
function fromParams(params, defaultSort) {
|
|
22
|
+
const sortField = params?.["sort"];
|
|
23
|
+
const dir = params?.["dir"];
|
|
24
|
+
const sort = sortField !== undefined && (dir === "asc" || dir === "desc")
|
|
25
|
+
? { field: sortField, dir }
|
|
26
|
+
: defaultSort;
|
|
27
|
+
const filters = {};
|
|
28
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
29
|
+
if (key.startsWith(FILTER_PREFIX)) {
|
|
30
|
+
filters[key.slice(FILTER_PREFIX.length)] = value.split(",").filter((v) => v !== "");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { search: params?.["q"] ?? "", filters, sort };
|
|
34
|
+
}
|
|
35
|
+
/** Search, facet filters and sort of a list whose state is component-local
|
|
36
|
+
* (relatedList has no URL namespace). Same stored `params` format as the URL lists. */
|
|
37
|
+
export function useRememberedLocalListState({ storeKey, store, remember, defaultSort, known, }) {
|
|
38
|
+
const [state, setState] = useState(() => remember
|
|
39
|
+
? fromParams(sanitizeStoredListState(store.read(storeKey), known)?.params, defaultSort)
|
|
40
|
+
: { search: "", filters: {}, sort: defaultSort });
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
// skip: remembering is not enabled
|
|
43
|
+
if (!remember)
|
|
44
|
+
return;
|
|
45
|
+
const params = toParams(state, defaultSort);
|
|
46
|
+
const existing = store.read(storeKey);
|
|
47
|
+
// skip: nothing stored and nothing to store
|
|
48
|
+
if (existing?.params === undefined && Object.keys(params).length === 0)
|
|
49
|
+
return;
|
|
50
|
+
store.write(storeKey, { ...existing, params });
|
|
51
|
+
}, [remember, state, defaultSort, store, storeKey]);
|
|
52
|
+
const setSearch = useCallback((search) => setState((prev) => ({ ...prev, search })), []);
|
|
53
|
+
const setFilters = useCallback((next) => setState((prev) => ({
|
|
54
|
+
...prev,
|
|
55
|
+
filters: typeof next === "function" ? next(prev.filters) : next,
|
|
56
|
+
})), []);
|
|
57
|
+
const setSort = useCallback((sort) => setState((prev) => ({ ...prev, sort })), []);
|
|
58
|
+
const reset = useCallback(() => setState({ search: "", filters: {}, sort: defaultSort }), [defaultSort]);
|
|
59
|
+
const atDefault = state.search === "" &&
|
|
60
|
+
Object.values(state.filters).every((values) => values.length === 0) &&
|
|
61
|
+
sameSort(state.sort, defaultSort);
|
|
62
|
+
return { ...state, setSearch, setFilters, setSort, reset, atDefault };
|
|
63
|
+
}
|
package/dist/i18n-defaults.js
CHANGED
|
@@ -86,6 +86,44 @@ export const kumikoDefaultTranslations = {
|
|
|
86
86
|
"kumiko.list.sort.unsorted": "Unsorted",
|
|
87
87
|
"kumiko.list.filter.reset": "Reset",
|
|
88
88
|
"kumiko.list.filter.toggle": "Filters",
|
|
89
|
+
"kumiko.list.reset-all": "Reset",
|
|
90
|
+
"kumiko.list.export": "Export",
|
|
91
|
+
"kumiko.list.export-busy": "Exporting…",
|
|
92
|
+
"kumiko.list.export-too-many": "More than {max} rows. Narrow the list with a filter and export again.",
|
|
93
|
+
"kumiko.list.export-failed": "The export failed. Please try again.",
|
|
94
|
+
"kumiko.list.view.menu": "View",
|
|
95
|
+
"kumiko.list.view.columns": "Columns",
|
|
96
|
+
"kumiko.list.view.density": "Density",
|
|
97
|
+
"kumiko.list.view.density.comfortable": "Comfortable",
|
|
98
|
+
"kumiko.list.view.density.compact": "Compact",
|
|
99
|
+
"kumiko.list.view.reset": "Reset view",
|
|
100
|
+
"kumiko.list.group.by": "Group by",
|
|
101
|
+
"kumiko.list.group.none": "None",
|
|
102
|
+
"kumiko.list.group.empty": "(empty)",
|
|
103
|
+
"kumiko.list.group.load-more": "Load more",
|
|
104
|
+
"kumiko.list.group.loading": "Loading…",
|
|
105
|
+
"kumiko.list.group.loaded": "{loaded} of {count}",
|
|
106
|
+
"kumiko.list.measure.sum": "Sum of {label}",
|
|
107
|
+
"kumiko.list.measure.avg": "Average {label}",
|
|
108
|
+
"kumiko.list.measure.count": "Count",
|
|
109
|
+
"kumiko.list.measure.min": "Minimum {label}",
|
|
110
|
+
"kumiko.list.measure.max": "Maximum {label}",
|
|
111
|
+
"kumiko.list.measure.kind.sum": "Sum",
|
|
112
|
+
"kumiko.list.measure.kind.avg": "Average",
|
|
113
|
+
"kumiko.list.measure.kind.count": "Count",
|
|
114
|
+
"kumiko.list.measure.kind.min": "Minimum",
|
|
115
|
+
"kumiko.list.measure.kind.max": "Maximum",
|
|
116
|
+
"kumiko.list.totals.label": "Total",
|
|
117
|
+
"kumiko.list.range.min": "From",
|
|
118
|
+
"kumiko.list.range.max": "To",
|
|
119
|
+
"kumiko.list.notice.truncated": "Not every group is shown. Please narrow the list with a filter.",
|
|
120
|
+
"kumiko.list.notice.searchTruncated": "The search matched too many entries; groups and totals cover only part of them. Please filter further.",
|
|
121
|
+
"kumiko.list.view.column.move-up": "Move {column} up",
|
|
122
|
+
"kumiko.list.view.pinning": "Pinned columns",
|
|
123
|
+
"kumiko.list.view.pin-left": "Pin {column} left",
|
|
124
|
+
"kumiko.list.view.pin-right": "Pin {column} right",
|
|
125
|
+
"kumiko.list.column.resize": "Resize {column}",
|
|
126
|
+
"kumiko.list.view.column.move-down": "Move {column} down",
|
|
89
127
|
"kumiko.list.related-list-truncated": "Showing the first {count} entries. More are available but not loaded — this list does not paginate.",
|
|
90
128
|
"kumiko.reference.system-tenant": "System",
|
|
91
129
|
"kumiko.reference.system-user": "System",
|
|
@@ -111,6 +149,21 @@ export const kumikoDefaultTranslations = {
|
|
|
111
149
|
"kumiko.theme.light": "Light theme",
|
|
112
150
|
"kumiko.list.row.collapse": "Collapse {title}",
|
|
113
151
|
"kumiko.list.row.expand": "Expand {title}",
|
|
152
|
+
"kumiko.list.bulk.selected": "{count} selected",
|
|
153
|
+
"kumiko.list.bulk.clear": "Clear selection",
|
|
154
|
+
"kumiko.list.bulk.limit": "At most {max} rows per selection",
|
|
155
|
+
"kumiko.list.bulk.select-all": "Select all rows on this page",
|
|
156
|
+
"kumiko.list.bulk.select-row": "Select {title}",
|
|
157
|
+
"kumiko.list.bulk.progress": "{settled} of {total} done",
|
|
158
|
+
"kumiko.list.bulk.cancel": "Cancel",
|
|
159
|
+
"kumiko.list.bulk.cancelling": "Cancelling…",
|
|
160
|
+
"kumiko.list.bulk.confirm": "Run “{action}” on the selection ({count})?",
|
|
161
|
+
"kumiko.list.bulk.result.title": "“{action}” finished",
|
|
162
|
+
"kumiko.list.bulk.result.succeeded": "Succeeded: {count}",
|
|
163
|
+
"kumiko.list.bulk.result.failed": "Failed: {count}",
|
|
164
|
+
"kumiko.list.bulk.result.skipped": "Skipped: {count}",
|
|
165
|
+
"kumiko.list.bulk.result.not-started": "Not run: {count}",
|
|
166
|
+
"kumiko.list.bulk.result.dismiss": "Dismiss",
|
|
114
167
|
"kumiko.list.sort.by": "Sorted by {column}",
|
|
115
168
|
"kumiko.form.changed": "changed",
|
|
116
169
|
"kumiko.form.on-this-page": "On this page",
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { type Formality, formalLocaleTag } from "@cosmicdrift/kumiko-framework/u
|
|
|
2
2
|
export { synthesizeActionFormEntity, synthesizeActionFormScreen } from "./app/action-form-shim.ts";
|
|
3
3
|
export type { AppFeaturesProviderProps } from "./app/app-features-context.tsx";
|
|
4
4
|
export { AppFeaturesProvider, useAppFeatures } from "./app/app-features-context.tsx";
|
|
5
|
+
export { normalizeBasePath, prependBasePath, stripBasePath } from "./app/base-path.ts";
|
|
6
|
+
export { BasePathProvider, useBasePath } from "./app/base-path-context.tsx";
|
|
5
7
|
export type { ActionNotice, ActionNotifier, ClientActionHandler, ClientActionOutcome, ClientActionRuntime, ClientActionsMap, } from "./app/client-actions.tsx";
|
|
6
8
|
export { ActionNoticeProvider, ClientActionsProvider, runClientAction, useClientActionHandler, useClientActionRuntime, } from "./app/client-actions.tsx";
|
|
7
9
|
export type { ColumnRendererComponent, ColumnRendererProps, ColumnRenderersMap, ColumnRenderersProviderProps, } from "./app/column-renderers.tsx";
|
|
@@ -86,7 +88,7 @@ export { InsideDrawerProvider, useInsideDrawer } from "./inside-drawer.ts";
|
|
|
86
88
|
export { hasUnsavedInput, registerUnsavedInputSource, requestLeave, setLeaveConfirmer, useUnsavedInputSource, } from "./leave-guard.ts";
|
|
87
89
|
export { LeaveGuardDialog } from "./leave-guard-dialog.tsx";
|
|
88
90
|
export { PageHeaderCompactProvider, PageHeaderSlotAvailableProvider, usePageHeaderCompact, usePageHeaderSlotAvailable, } from "./page-header-slot.ts";
|
|
89
|
-
export type { ActionMenuItemSpec, ActionOverflowMenuProps, AppPrimitives, BannerProps, ButtonProps, CardOptions, CardProps, CardSlots, CopyButtonProps, CorePrimitives, DataTableDateRangeFacet, DataTableFacet, DataTableFacetChip, DataTableProps, DataTableRowAction, DataTableRowActionMode,
|
|
91
|
+
export type { ActionMenuItemSpec, ActionOverflowMenuProps, AppPrimitives, BannerProps, BulkRowOutcome, ButtonProps, CardOptions, CardProps, CardSlots, CopyButtonProps, CorePrimitives, DataTableBulk, DataTableBulkAction, DataTableDateRangeFacet, DataTableFacet, DataTableFacetChip, DataTableProps, DataTableRowAction, DataTableRowActionMode, DataTableSort, DataTableSortDir, DialogProps, DrawerProps, EmbeddedListCellType, EmbeddedListColumn, EmbeddedListInputProps, EmbeddedListTotal, FieldCellWidth, FieldProps, FileValueListProps, FillContainerProps, FooterActionMarker, FooterActionRole, FormProps, FormSectionNavItem, FormWidth, GridCellProps, GridProps, HeadingProps, InputFillAction, InputProps, JsonViewProps, LightboxImage, LightboxProps, LinkProps, MetricBandProps, MetricProps, ModalProps, PageHeaderProps, PickedFile, PrimitivesProviderProps, PrimitivesRegistry, ProgressProps, ProgressTone, PromoPanelAction, PromoPanelProps, RuntimeRenderer, SecretRevealProps, SecretRevealValue, SectionProps, ShareButtonProps, StatusBadgeProps, StatusTone, StepBarProps, StickyActionBarProps, StickyPrimaryActionMarker, TabsProps, TextProps, TrailingInputAction, WizardStepGroupProps, } from "./primitives.tsx";
|
|
90
92
|
export { FOOTER_ACTION_ROLE_PROP, NARROW_LABEL_PROP, PrimitivesProvider, STICKY_PRIMARY_ACTION_PROP, shouldRenderActionsIconOnly, statusToneForOptionTone, statusToneForValue, useOptionalPrimitives, usePrimitives, } from "./primitives.tsx";
|
|
91
93
|
export type { SessionEndedSignal, SessionEndedSignalProviderProps, } from "./session/session-ended.tsx";
|
|
92
94
|
export { createSessionEndedSignal, isSessionEndedError, SESSION_ENDED_ERROR_CODES, SessionEndedSignalProvider, useSessionEndedSignal, withSessionEndedDetection, } from "./session/session-ended.tsx";
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
export { formalLocaleTag } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
12
12
|
export { synthesizeActionFormEntity, synthesizeActionFormScreen } from "./app/action-form-shim.js";
|
|
13
13
|
export { AppFeaturesProvider, useAppFeatures } from "./app/app-features-context.js";
|
|
14
|
+
export { normalizeBasePath, prependBasePath, stripBasePath } from "./app/base-path.js";
|
|
15
|
+
export { BasePathProvider, useBasePath } from "./app/base-path-context.js";
|
|
14
16
|
export { ActionNoticeProvider, ClientActionsProvider, runClientAction, useClientActionHandler, useClientActionRuntime, } from "./app/client-actions.js";
|
|
15
17
|
export { ColumnRenderersProvider, useColumnRenderer } from "./app/column-renderers.js";
|
|
16
18
|
export { CONTENT_EDITOR_ELEMENT_ID, ContentEditorsProvider, TextareaContentEditor, useContentEditor, } from "./app/content-editors.js";
|