@multiplatform.one/table 7.29.0 → 7.30.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/cjs/components/DataTableCell/index.cjs +7 -0
- package/dist/cjs/components/DataTableCell/index.native.js +9 -0
- package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
- package/dist/cjs/components/KnobDialog.cjs +8 -9
- package/dist/cjs/components/KnobDialog.native.js +8 -9
- package/dist/cjs/components/KnobDialog.native.js.map +1 -1
- package/dist/cjs/components/TableInput/RowDetailEditor.cjs +79 -81
- package/dist/cjs/components/TableInput/index.cjs +139 -110
- package/dist/esm/components/DataTableCell/index.mjs +7 -0
- package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
- package/dist/esm/components/DataTableCell/index.native.js +9 -0
- package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
- package/dist/esm/components/KnobDialog.mjs +1 -2
- package/dist/esm/components/KnobDialog.mjs.map +1 -1
- package/dist/esm/components/KnobDialog.native.js +1 -2
- package/dist/esm/components/KnobDialog.native.js.map +1 -1
- package/dist/esm/components/TableInput/RowDetailEditor.mjs +79 -81
- package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -1
- package/dist/esm/components/TableInput/index.mjs +140 -111
- package/dist/esm/components/TableInput/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.mjs +7 -0
- package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
- package/dist/jsx/components/DataTableCell/index.native.js +9 -0
- package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
- package/dist/jsx/components/KnobDialog.mjs +1 -2
- package/dist/jsx/components/KnobDialog.mjs.map +1 -1
- package/dist/jsx/components/KnobDialog.native.js +8 -9
- package/dist/jsx/components/KnobDialog.native.js.map +1 -1
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs +79 -81
- package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -1
- package/dist/jsx/components/TableInput/index.mjs +140 -111
- package/dist/jsx/components/TableInput/index.mjs.map +1 -1
- package/package.json +12 -12
- package/src/components/ChildTable/childTable.rowDetail.spec.tsx +69 -0
- package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +60 -0
- package/src/components/DataTableCell/index.tsx +10 -1
- package/src/components/KnobDialog.tsx +1 -2
- package/src/components/TableInput/RowDetailEditor.tsx +71 -70
- package/src/components/TableInput/index.tsx +136 -128
- package/src/components/TableInput/tableInput.cellIdentity.spec.tsx +196 -0
- package/types/components/KnobDialog.d.ts.map +1 -1
- package/types/components/TableInput/RowDetailEditor.d.ts +4 -0
- package/types/components/TableInput/RowDetailEditor.d.ts.map +1 -1
- package/types/components/TableInput/index.d.ts.map +1 -1
|
@@ -1107,12 +1107,19 @@ function DraftFieldEditor({
|
|
|
1107
1107
|
const onCommitRef = useRef(onCommit);
|
|
1108
1108
|
onCommitRef.current = onCommit;
|
|
1109
1109
|
const blurTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
1110
|
+
const shownRef = useRef(committed);
|
|
1110
1111
|
|
|
1111
1112
|
// External value updates (a commit landing, row edits) clear the draft so
|
|
1112
|
-
// the field tracks the new committed value.
|
|
1113
|
+
// the field tracks the new committed value. A value the field did not emit
|
|
1114
|
+
// itself also remounts it: Stepper and PhoneInput only read defaultValue on
|
|
1115
|
+
// mount, and the grid keeps its cells mounted across row commits (MPO-475).
|
|
1113
1116
|
if (!Object.is(prevCommitted, committed)) {
|
|
1114
1117
|
setPrevCommitted(committed);
|
|
1115
1118
|
if (draft) setDraft(null);
|
|
1119
|
+
if (!Object.is(shownRef.current, committed)) {
|
|
1120
|
+
shownRef.current = committed;
|
|
1121
|
+
setResetKey((key) => key + 1);
|
|
1122
|
+
}
|
|
1116
1123
|
}
|
|
1117
1124
|
|
|
1118
1125
|
const clearBlurTimer = useCallback(() => {
|
|
@@ -1144,6 +1151,7 @@ function DraftFieldEditor({
|
|
|
1144
1151
|
setSessionActive(false);
|
|
1145
1152
|
draftRef.current = null;
|
|
1146
1153
|
setDraft(null);
|
|
1154
|
+
shownRef.current = committedRef.current;
|
|
1147
1155
|
// Remount the field: uncontrolled editors and popover-internal state
|
|
1148
1156
|
// (DateRange mid-pick) hold edits that no prop change can reset.
|
|
1149
1157
|
setResetKey((key) => key + 1);
|
|
@@ -1151,6 +1159,7 @@ function DraftFieldEditor({
|
|
|
1151
1159
|
|
|
1152
1160
|
const handleDraftChange = useCallback(
|
|
1153
1161
|
(value: any) => {
|
|
1162
|
+
shownRef.current = value;
|
|
1154
1163
|
if (mode === "change") {
|
|
1155
1164
|
onCommitRef.current(value);
|
|
1156
1165
|
return;
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* borderRadius, elevation, and sizing across the table package.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { DialogShowFocus } from "@multiplatform.one/components";
|
|
9
|
+
import { Dialog, DialogShowFocus } from "@multiplatform.one/components";
|
|
10
10
|
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
11
11
|
import type React from "react";
|
|
12
|
-
import { Dialog } from "tamagui";
|
|
13
12
|
|
|
14
13
|
export interface KnobDialogProps {
|
|
15
14
|
open: boolean;
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* copy of the row seeded on open. "Done" and an outside click commit the
|
|
8
8
|
* draft once; "Cancel" and Escape discard it. A read-only row opens for
|
|
9
9
|
* viewing with a single "Close".
|
|
10
|
+
*
|
|
11
|
+
* The pencil is the dialog's `Dialog.Trigger`: tamagui's modal close returns
|
|
12
|
+
* focus to the trigger ref and nowhere else, so without it every close
|
|
13
|
+
* dropped focus on <body> (MPO-471).
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
import { DialogContent, DialogOverlay, ScrollView } from "@multiplatform.one/components";
|
|
@@ -99,75 +103,72 @@ export function RowDetailEditor<TData extends DataTableData>({
|
|
|
99
103
|
const current = draft ?? row;
|
|
100
104
|
|
|
101
105
|
return (
|
|
102
|
-
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
</Dialog.Portal>
|
|
170
|
-
</Dialog>
|
|
171
|
-
</>
|
|
106
|
+
<Dialog open={draft !== null} onOpenChange={handleOpenChange} modal>
|
|
107
|
+
<Dialog.Trigger asChild>
|
|
108
|
+
<Button
|
|
109
|
+
chromeless
|
|
110
|
+
compact={compact}
|
|
111
|
+
hitSlop={knobProps.nestedControl.hitSlop}
|
|
112
|
+
aria-label={triggerLabel}
|
|
113
|
+
>
|
|
114
|
+
<PencilSimpleIcon size={16} />
|
|
115
|
+
</Button>
|
|
116
|
+
</Dialog.Trigger>
|
|
117
|
+
<Dialog.Portal>
|
|
118
|
+
<DialogOverlay opacity={0.5} />
|
|
119
|
+
<DialogContent
|
|
120
|
+
maxWidth={720}
|
|
121
|
+
maxHeight="80vh"
|
|
122
|
+
width="90vw"
|
|
123
|
+
onEscapeKeyDown={() => {
|
|
124
|
+
discardRef.current = true;
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<Dialog.Title {...knobProps.heading}>
|
|
128
|
+
{withInterp(t("Row {{row}}", { row: rowNumber }), { row: rowNumber })}
|
|
129
|
+
</Dialog.Title>
|
|
130
|
+
<ScrollView flexShrink={1} minHeight={0}>
|
|
131
|
+
<TableCellContext.Provider value={{ inTableCell: false }}>
|
|
132
|
+
<YStack {...knobProps.gapLg}>
|
|
133
|
+
{columns.map((col) => {
|
|
134
|
+
const columnId = columnKey(col);
|
|
135
|
+
if (isCellHidden?.(current, columnId, rowIndex)) return null;
|
|
136
|
+
const label = typeof col.header === "string" ? col.header : columnId;
|
|
137
|
+
return (
|
|
138
|
+
<YStack key={columnId} {...knobProps.gap}>
|
|
139
|
+
<Text {...knobProps.label}>{label}</Text>
|
|
140
|
+
<DataTableCell
|
|
141
|
+
value={current[columnId as keyof TData]}
|
|
142
|
+
onChange={(value) => setDraftValue(columnId, value)}
|
|
143
|
+
field={(col.field || "input") as DataTableField}
|
|
144
|
+
fieldProps={col.fieldProps as DataTableFieldProps | undefined}
|
|
145
|
+
readOnly={readOnly}
|
|
146
|
+
compact={false}
|
|
147
|
+
columnId={columnId}
|
|
148
|
+
columnLabel={label}
|
|
149
|
+
rowIndex={rowIndex}
|
|
150
|
+
rowData={current}
|
|
151
|
+
/>
|
|
152
|
+
</YStack>
|
|
153
|
+
);
|
|
154
|
+
})}
|
|
155
|
+
</YStack>
|
|
156
|
+
</TableCellContext.Provider>
|
|
157
|
+
</ScrollView>
|
|
158
|
+
<XStack {...knobProps.gap} justifyContent="flex-end">
|
|
159
|
+
{readOnly ? (
|
|
160
|
+
<Button onPress={handleCancel}>{t("Close")}</Button>
|
|
161
|
+
) : (
|
|
162
|
+
<>
|
|
163
|
+
<Button onPress={handleCancel}>{t("Cancel")}</Button>
|
|
164
|
+
<Button theme="accent" onPress={() => handleOpenChange(false)}>
|
|
165
|
+
{t("Done")}
|
|
166
|
+
</Button>
|
|
167
|
+
</>
|
|
168
|
+
)}
|
|
169
|
+
</XStack>
|
|
170
|
+
</DialogContent>
|
|
171
|
+
</Dialog.Portal>
|
|
172
|
+
</Dialog>
|
|
172
173
|
);
|
|
173
174
|
}
|
|
@@ -35,7 +35,13 @@ import {
|
|
|
35
35
|
useResolvedValidators,
|
|
36
36
|
type AnyFormApi,
|
|
37
37
|
} from "@multiplatform.one/forms";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
flexRender,
|
|
40
|
+
getCoreRowModel,
|
|
41
|
+
useReactTable,
|
|
42
|
+
type CellContext,
|
|
43
|
+
type ColumnDef,
|
|
44
|
+
} from "@tanstack/react-table";
|
|
39
45
|
import { TrashIcon, PlusIcon } from "@phosphor-icons/react";
|
|
40
46
|
import { useTranslation } from "react-i18next";
|
|
41
47
|
import { Table, getTableCellPadding } from "@multiplatform.one/table-primitives";
|
|
@@ -53,6 +59,7 @@ import { RowDetailEditor } from "./RowDetailEditor";
|
|
|
53
59
|
import {
|
|
54
60
|
childRowAttrs,
|
|
55
61
|
childTableKeyAction,
|
|
62
|
+
columnKey,
|
|
56
63
|
generateRowId,
|
|
57
64
|
isEmptyRows,
|
|
58
65
|
SKELETON_ROW_COUNT,
|
|
@@ -61,6 +68,113 @@ import {
|
|
|
61
68
|
|
|
62
69
|
const chromeColumnIds = new Set(["_reorder", "_rowNumber", "_detail", "_actions"]);
|
|
63
70
|
|
|
71
|
+
/**
|
|
72
|
+
* What the cell renderers read at render time. It rides on TanStack's table
|
|
73
|
+
* meta so the column definitions depend on the schema alone.
|
|
74
|
+
*/
|
|
75
|
+
interface GridMeta<TData extends DataTableData> {
|
|
76
|
+
readOnly: boolean;
|
|
77
|
+
compact: boolean;
|
|
78
|
+
canDelete: boolean;
|
|
79
|
+
deleteColor: string;
|
|
80
|
+
knobProps: ReturnType<typeof useResolvedKnobs>["knobProps"];
|
|
81
|
+
sortable: ReturnType<typeof useSortableRows<TData>>;
|
|
82
|
+
detailColumns: DataTableColumn<TData>[];
|
|
83
|
+
isCellHidden?: ChildCellHidden<TData>;
|
|
84
|
+
t: (key: string, options?: Record<string, unknown>) => string;
|
|
85
|
+
changeCell: (rowIndex: number, columnId: string, value: unknown) => void;
|
|
86
|
+
deleteRow: (rowIndex: number) => void;
|
|
87
|
+
commitRowDetail: (rowIndex: number, next: TData, changed: string[]) => void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type GridCellProps<TData extends DataTableData> = CellContext<TData, unknown>;
|
|
91
|
+
|
|
92
|
+
const gridMeta = <TData extends DataTableData>({ table }: GridCellProps<TData>) =>
|
|
93
|
+
table.options.meta as GridMeta<TData>;
|
|
94
|
+
|
|
95
|
+
// flexRender mounts each column's `cell` as a component, so these renderers
|
|
96
|
+
// live at module scope: a renderer created per render remounts every cell on
|
|
97
|
+
// a row commit, and the focused control goes with it (MPO-471, MPO-475).
|
|
98
|
+
function ReorderCell<TData extends DataTableData>(props: GridCellProps<TData>) {
|
|
99
|
+
const { row } = props;
|
|
100
|
+
const { sortable, t, compact } = gridMeta(props);
|
|
101
|
+
return (
|
|
102
|
+
<SortableHandle
|
|
103
|
+
label={sortable.labels[row.index] ?? t("Row {{row}}", { row: row.index + 1 })}
|
|
104
|
+
itemId={String(row.original.id ?? row.id)}
|
|
105
|
+
compact={compact}
|
|
106
|
+
onPointerDown={(e) => sortable.onHandlePointerDown(row.index, e)}
|
|
107
|
+
onKeyDown={(e) => sortable.onHandleKeyDown(row.index, e)}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function RowNumberCell<TData extends DataTableData>(props: GridCellProps<TData>) {
|
|
113
|
+
const { knobProps } = gridMeta(props);
|
|
114
|
+
return (
|
|
115
|
+
<Text color="$color10" fontSize={knobProps.label.fontSize}>
|
|
116
|
+
{props.row.index + 1}
|
|
117
|
+
</Text>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function DataCell<TData extends DataTableData>(props: GridCellProps<TData>) {
|
|
122
|
+
const { row, column } = props;
|
|
123
|
+
const { isCellHidden, readOnly, compact, changeCell } = gridMeta(props);
|
|
124
|
+
const columnId = column.id;
|
|
125
|
+
if (isCellHidden?.(row.original, columnId, row.index)) return null;
|
|
126
|
+
const col = column.columnDef as DataTableColumn<TData>;
|
|
127
|
+
return (
|
|
128
|
+
<DataTableCell
|
|
129
|
+
value={row.original[columnId as keyof TData]}
|
|
130
|
+
onChange={(newValue) => changeCell(row.index, columnId, newValue)}
|
|
131
|
+
field={(col.field || "input") as DataTableField}
|
|
132
|
+
fieldProps={col.fieldProps as DataTableFieldProps | undefined}
|
|
133
|
+
readOnly={readOnly}
|
|
134
|
+
compact={compact}
|
|
135
|
+
columnId={columnId}
|
|
136
|
+
columnLabel={typeof col.header === "string" ? col.header : undefined}
|
|
137
|
+
rowIndex={row.index}
|
|
138
|
+
rowData={row.original}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function DetailCell<TData extends DataTableData>(props: GridCellProps<TData>) {
|
|
144
|
+
const { row } = props;
|
|
145
|
+
const { detailColumns, readOnly, compact, isCellHidden, commitRowDetail } = gridMeta(props);
|
|
146
|
+
return (
|
|
147
|
+
<RowDetailEditor
|
|
148
|
+
row={row.original}
|
|
149
|
+
rowIndex={row.index}
|
|
150
|
+
columns={detailColumns}
|
|
151
|
+
readOnly={readOnly}
|
|
152
|
+
compact={compact}
|
|
153
|
+
isCellHidden={isCellHidden}
|
|
154
|
+
onCommit={(next, changed) => commitRowDetail(row.index, next, changed)}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function DeleteCell<TData extends DataTableData>(props: GridCellProps<TData>) {
|
|
160
|
+
const { row } = props;
|
|
161
|
+
const { canDelete, compact, knobProps, deleteColor, deleteRow, t } = gridMeta(props);
|
|
162
|
+
return (
|
|
163
|
+
<Button
|
|
164
|
+
chromeless
|
|
165
|
+
compact={compact}
|
|
166
|
+
onPress={() => deleteRow(row.index)}
|
|
167
|
+
disabled={!canDelete}
|
|
168
|
+
hitSlop={knobProps.nestedControl.hitSlop}
|
|
169
|
+
aria-label={withInterp(t("Delete row {{row}}", { row: row.index + 1 }), {
|
|
170
|
+
row: row.index + 1,
|
|
171
|
+
})}
|
|
172
|
+
>
|
|
173
|
+
<TrashIcon size={16} color={deleteColor} />
|
|
174
|
+
</Button>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
64
178
|
export interface TableInputProps<TData extends DataTableData = DataTableData> {
|
|
65
179
|
/** Data array for the table (ignored in favor of form state in the form branch) */
|
|
66
180
|
data?: TData[];
|
|
@@ -272,7 +386,6 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
272
386
|
const pendingFocusRef = useRef<string | null>(null);
|
|
273
387
|
const gridRef = useRef<HTMLElement | null>(null);
|
|
274
388
|
const reorderEnabled = enableReorder && !readOnly && !skeleton;
|
|
275
|
-
const Detail = RowDetailEditor<TData>;
|
|
276
389
|
|
|
277
390
|
const firstDataKey = useMemo(() => {
|
|
278
391
|
const first = columns[0];
|
|
@@ -307,13 +420,6 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
307
420
|
rowSelector: `[${childRowAttrs.row}]`,
|
|
308
421
|
});
|
|
309
422
|
|
|
310
|
-
const handlePointerRef = useRef(sortable.onHandlePointerDown);
|
|
311
|
-
handlePointerRef.current = sortable.onHandlePointerDown;
|
|
312
|
-
const handleKeyRef = useRef(sortable.onHandleKeyDown);
|
|
313
|
-
handleKeyRef.current = sortable.onHandleKeyDown;
|
|
314
|
-
const labelsRef = useRef(sortable.labels);
|
|
315
|
-
labelsRef.current = sortable.labels;
|
|
316
|
-
|
|
317
423
|
const lastDataColumnId = useMemo(() => {
|
|
318
424
|
const last = columns[columns.length - 1];
|
|
319
425
|
if (!last) return null;
|
|
@@ -428,133 +534,34 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
428
534
|
|
|
429
535
|
const tableColumns = useMemo(() => {
|
|
430
536
|
const cols: ColumnDef<TData, unknown>[] = [];
|
|
537
|
+
const chrome = (id: string, header: string, size: number, cell: ColumnDef<TData>["cell"]) =>
|
|
538
|
+
cols.push({ id, header, size, cell });
|
|
431
539
|
|
|
432
|
-
if (reorderEnabled)
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
size: chromePx + 16,
|
|
437
|
-
cell: ({ row }) => (
|
|
438
|
-
<SortableHandle
|
|
439
|
-
label={labelsRef.current[row.index] ?? t("Row {{row}}", { row: row.index + 1 })}
|
|
440
|
-
itemId={String((row.original as DataTableData).id ?? row.id)}
|
|
441
|
-
compact={compact}
|
|
442
|
-
onPointerDown={(e) => handlePointerRef.current(row.index, e)}
|
|
443
|
-
onKeyDown={(e) => handleKeyRef.current(row.index, e)}
|
|
444
|
-
/>
|
|
445
|
-
),
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (showRowNumbers) {
|
|
450
|
-
cols.push({
|
|
451
|
-
id: "_rowNumber",
|
|
452
|
-
header: "#",
|
|
453
|
-
size: Math.max(36, chromePx),
|
|
454
|
-
cell: ({ row }) => (
|
|
455
|
-
<Text color="$color10" fontSize={knobProps.label.fontSize}>
|
|
456
|
-
{row.index + 1}
|
|
457
|
-
</Text>
|
|
458
|
-
),
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
columns.forEach((col) => {
|
|
463
|
-
const columnId =
|
|
464
|
-
typeof col.accessorKey === "string" ? col.accessorKey : col.id || String(col.accessorKey);
|
|
465
|
-
|
|
466
|
-
cols.push({
|
|
467
|
-
...col,
|
|
468
|
-
id: columnId,
|
|
469
|
-
cell: ({ row }) => {
|
|
470
|
-
if (isCellHidden?.(row.original, columnId, row.index)) return null;
|
|
471
|
-
const value = row.original[columnId as keyof TData];
|
|
472
|
-
const field = (col.field || "input") as DataTableField;
|
|
473
|
-
const fieldProps = col.fieldProps as DataTableFieldProps | undefined;
|
|
474
|
-
|
|
475
|
-
return (
|
|
476
|
-
<DataTableCell
|
|
477
|
-
value={value}
|
|
478
|
-
onChange={(newValue) => handleCellChange(row.index, columnId, newValue)}
|
|
479
|
-
field={field}
|
|
480
|
-
fieldProps={fieldProps}
|
|
481
|
-
readOnly={readOnly}
|
|
482
|
-
compact={compact}
|
|
483
|
-
columnId={columnId}
|
|
484
|
-
columnLabel={typeof col.header === "string" ? col.header : undefined}
|
|
485
|
-
rowIndex={row.index}
|
|
486
|
-
rowData={row.original}
|
|
487
|
-
/>
|
|
488
|
-
);
|
|
489
|
-
},
|
|
490
|
-
} as ColumnDef<TData, unknown>);
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
if (rowDetail) {
|
|
494
|
-
cols.push({
|
|
495
|
-
id: "_detail",
|
|
496
|
-
header: "",
|
|
497
|
-
size: chromePx + 16,
|
|
498
|
-
cell: ({ row }) => (
|
|
499
|
-
<Detail
|
|
500
|
-
row={row.original}
|
|
501
|
-
rowIndex={row.index}
|
|
502
|
-
columns={detailColumns}
|
|
503
|
-
readOnly={readOnly}
|
|
504
|
-
compact={compact}
|
|
505
|
-
isCellHidden={isCellHidden}
|
|
506
|
-
onCommit={(next, changed) => handleRowDetailCommit(row.index, next, changed)}
|
|
507
|
-
/>
|
|
508
|
-
),
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (showDeleteRow && !readOnly) {
|
|
513
|
-
cols.push({
|
|
514
|
-
id: "_actions",
|
|
515
|
-
header: "",
|
|
516
|
-
size: chromePx + 16,
|
|
517
|
-
cell: ({ row }) => {
|
|
518
|
-
const canDelete = !minRows || rows.length > minRows;
|
|
519
|
-
return (
|
|
520
|
-
<Button
|
|
521
|
-
chromeless
|
|
522
|
-
compact={compact}
|
|
523
|
-
onPress={() => handleDeleteRow(row.index)}
|
|
524
|
-
disabled={!canDelete}
|
|
525
|
-
hitSlop={knobProps.nestedControl.hitSlop}
|
|
526
|
-
aria-label={withInterp(t("Delete row {{row}}", { row: row.index + 1 }), {
|
|
527
|
-
row: row.index + 1,
|
|
528
|
-
})}
|
|
529
|
-
>
|
|
530
|
-
<TrashIcon size={16} color={theme.red10?.val ?? "currentColor"} />
|
|
531
|
-
</Button>
|
|
532
|
-
);
|
|
533
|
-
},
|
|
534
|
-
});
|
|
540
|
+
if (reorderEnabled) chrome("_reorder", "", chromePx + 16, ReorderCell<TData>);
|
|
541
|
+
if (showRowNumbers) chrome("_rowNumber", "#", Math.max(36, chromePx), RowNumberCell<TData>);
|
|
542
|
+
for (const col of columns) {
|
|
543
|
+
cols.push({ ...col, id: columnKey(col), cell: DataCell<TData> } as ColumnDef<TData, unknown>);
|
|
535
544
|
}
|
|
545
|
+
if (rowDetail) chrome("_detail", "", chromePx + 16, DetailCell<TData>);
|
|
546
|
+
if (showDeleteRow && !readOnly) chrome("_actions", "", chromePx + 16, DeleteCell<TData>);
|
|
536
547
|
|
|
537
548
|
return cols;
|
|
538
|
-
}, [
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
showDeleteRow,
|
|
549
|
+
}, [columns, reorderEnabled, showRowNumbers, rowDetail, showDeleteRow, readOnly, chromePx]);
|
|
550
|
+
|
|
551
|
+
const meta: GridMeta<TData> = {
|
|
542
552
|
readOnly,
|
|
543
|
-
handleCellChange,
|
|
544
|
-
handleDeleteRow,
|
|
545
|
-
rows.length,
|
|
546
|
-
minRows,
|
|
547
|
-
t,
|
|
548
553
|
compact,
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
knobProps
|
|
552
|
-
|
|
553
|
-
rowDetail,
|
|
554
|
+
canDelete: !minRows || rows.length > minRows,
|
|
555
|
+
deleteColor: theme.red10?.val ?? "currentColor",
|
|
556
|
+
knobProps,
|
|
557
|
+
sortable,
|
|
554
558
|
detailColumns,
|
|
555
559
|
isCellHidden,
|
|
556
|
-
|
|
557
|
-
|
|
560
|
+
t,
|
|
561
|
+
changeCell: handleCellChange,
|
|
562
|
+
deleteRow: handleDeleteRow,
|
|
563
|
+
commitRowDetail: handleRowDetailCommit,
|
|
564
|
+
};
|
|
558
565
|
|
|
559
566
|
const table = useReactTable({
|
|
560
567
|
data: rows,
|
|
@@ -563,6 +570,7 @@ function TableInputInner<TData extends DataTableData>({
|
|
|
563
570
|
// Stable row identity: keeps in-cell editor drafts pinned to their data
|
|
564
571
|
// row across deletes instead of following the positional index.
|
|
565
572
|
getRowId: (row, index) => String((row as DataTableData).id ?? index),
|
|
573
|
+
meta,
|
|
566
574
|
});
|
|
567
575
|
|
|
568
576
|
// Empty state flips to flex so a single auto-width cell fills the row
|