@jielga/tmdatagrid 2.0.0-beta.0 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -34
- package/dist/index.d.ts +164 -102
- package/dist/index.js +2060 -1705
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +37 -34
- package/skills/cell-selection/SKILL.md +19 -19
- package/skills/columns/SKILL.md +9 -9
- package/skills/data/SKILL.md +53 -53
- package/skills/editing/SKILL.md +152 -311
- package/skills/editing/references/common-mistakes.md +177 -0
- package/skills/editing/references/editing-api.md +56 -28
- package/skills/editing/references/editors-and-validation.md +26 -19
- package/skills/filtering/SKILL.md +51 -50
- package/skills/getting-started/SKILL.md +10 -10
- package/skills/grouping/SKILL.md +41 -43
- package/skills/options/SKILL.md +4 -4
- package/skills/rows/SKILL.md +59 -59
- package/skills/rows/references/rows-api.md +7 -7
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +11 -10
- package/src/tmdatagrid/components/TMDataGrid.module.css +13 -1
- package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +20 -8
- package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +29 -18
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +5 -8
- package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
- package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +246 -86
- package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +176 -77
- package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +11 -6
- package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +9 -5
- package/src/tmdatagrid/components/TMDataGridTable.module.css +47 -3
- package/src/tmdatagrid/components/TMDataGridTable.tsx +101 -40
- package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
- package/src/tmdatagrid/components/icons.ts +1 -0
- package/src/tmdatagrid/core/autosize.ts +30 -6
- package/src/tmdatagrid/core/capabilities.ts +15 -5
- package/src/tmdatagrid/core/cellExport.ts +6 -7
- package/src/tmdatagrid/core/cellNavigation.ts +2 -2
- package/src/tmdatagrid/core/cellRange.ts +6 -6
- package/src/tmdatagrid/core/columnOrdering.ts +30 -1
- package/src/tmdatagrid/core/columnUtils.ts +14 -0
- package/src/tmdatagrid/core/draftCellContext.ts +68 -0
- package/src/tmdatagrid/core/editEngine.ts +111 -37
- package/src/tmdatagrid/core/filterOperators.ts +6 -6
- package/src/tmdatagrid/core/labels.ts +13 -1
- package/src/tmdatagrid/core/labelsSv.ts +4 -0
- package/src/tmdatagrid/core/matchHighlight.ts +3 -3
- package/src/tmdatagrid/core/persistence.ts +3 -3
- package/src/tmdatagrid/core/rowSelection.ts +3 -3
- package/src/tmdatagrid/index.ts +3 -1
- package/src/tmdatagrid/useTMDataGrid.tsx +141 -99
|
@@ -53,7 +53,7 @@ function isEntryCellEditable(
|
|
|
53
53
|
* every other draft, keyed by their temporary ids.
|
|
54
54
|
*
|
|
55
55
|
* Enter commits the entry (`onRowAdd`) under the immediate modes and parks
|
|
56
|
-
* it under
|
|
56
|
+
* it under draft, where `submitAll` reports it in `added`; Escape discards
|
|
57
57
|
* it. The edit lane, when present, offers the same pair as buttons.
|
|
58
58
|
*/
|
|
59
59
|
export function TMDataGridEntryRows({
|
|
@@ -65,11 +65,23 @@ export function TMDataGridEntryRows({
|
|
|
65
65
|
layoutFor: (columnId: string) => TMDataGridColumnLayout;
|
|
66
66
|
rowHeight: number;
|
|
67
67
|
}) {
|
|
68
|
-
const { table, edit } = useTMDataGridContext();
|
|
68
|
+
const { table, edit, features } = useTMDataGridContext();
|
|
69
69
|
const newRows = useSelector(edit.store, (state) => state.newRows);
|
|
70
|
+
// The reopen gesture: `begin` on a confirmed entry row flips it back to
|
|
71
|
+
// editors and names the cell double-clicked - where the caret goes.
|
|
72
|
+
const activeEntry = useSelector(edit.store, (state) =>
|
|
73
|
+
state.active !== null &&
|
|
74
|
+
state.newRows.some(
|
|
75
|
+
(newRow) => newRow.tempId === state.active?.rowId && !newRow.confirmed,
|
|
76
|
+
)
|
|
77
|
+
? state.active
|
|
78
|
+
: null,
|
|
79
|
+
);
|
|
70
80
|
const blockRef = useRef<HTMLDivElement>(null);
|
|
71
81
|
/** The entry row the caret has already been placed in. */
|
|
72
82
|
const focusedTempIdRef = useRef<string | null>(null);
|
|
83
|
+
/** The reopen target (`rowId:columnId`) the caret has already landed in. */
|
|
84
|
+
const focusedActiveRef = useRef<string | null>(null);
|
|
73
85
|
|
|
74
86
|
/**
|
|
75
87
|
* The caret goes into a row the moment `edit.addRow()` opens it, landing in
|
|
@@ -96,14 +108,44 @@ export function TMDataGridEntryRows({
|
|
|
96
108
|
focusEditorContent(editor);
|
|
97
109
|
});
|
|
98
110
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
/**
|
|
112
|
+
* The caret for a reopen: `begin` on a confirmed row re-arms its editors
|
|
113
|
+
* and the double-clicked cell's editor should hold the caret, not the
|
|
114
|
+
* row's first. Keyed separately from the add effect above - the row was
|
|
115
|
+
* already focused once when it was added.
|
|
116
|
+
*/
|
|
117
|
+
useLayoutEffect(() => {
|
|
118
|
+
if (activeEntry === null) {
|
|
119
|
+
focusedActiveRef.current = null;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const key = `${activeEntry.rowId}:${activeEntry.columnId ?? ""}`;
|
|
123
|
+
if (focusedActiveRef.current === key) return;
|
|
124
|
+
const block = blockRef.current;
|
|
125
|
+
if (block === null) return;
|
|
126
|
+
const editor = block.querySelector<HTMLElement>(
|
|
127
|
+
activeEntry.columnId !== null
|
|
128
|
+
? `[data-dg-part="editor"][data-row-id="${CSS.escape(activeEntry.rowId)}"][data-column-id="${CSS.escape(activeEntry.columnId)}"]`
|
|
129
|
+
: `[data-dg-part="editor"][data-row-id="${CSS.escape(activeEntry.rowId)}"]`,
|
|
130
|
+
);
|
|
131
|
+
// Not mounted yet; the next render tries again, as the add effect does.
|
|
132
|
+
if (editor === null) return;
|
|
133
|
+
focusedActiveRef.current = key;
|
|
134
|
+
focusEditorContent(editor);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Unconfirmed rows keep the seed values frozen at addRow - the live values
|
|
138
|
+
// belong to the forms, which the editors read directly, and this table only
|
|
139
|
+
// provides row and cell identity. A confirmed row's cells render values, so
|
|
140
|
+
// there the draft itself is the row; the memo recomputes on every
|
|
141
|
+
// confirm/reopen because either flips `newRows`' identity.
|
|
102
142
|
const data = useMemo(
|
|
103
143
|
() =>
|
|
104
144
|
newRows.map(
|
|
105
|
-
({ tempId }) =>
|
|
106
|
-
(
|
|
145
|
+
({ tempId, confirmed }) =>
|
|
146
|
+
((confirmed
|
|
147
|
+
? edit.getForm(tempId)?.state.values
|
|
148
|
+
: edit.getForm(tempId)?.options.defaultValues) ??
|
|
107
149
|
{}) as TMDataGridRowData,
|
|
108
150
|
),
|
|
109
151
|
[newRows, edit],
|
|
@@ -119,77 +161,134 @@ export function TMDataGridEntryRows({
|
|
|
119
161
|
if (newRows.length === 0) return null;
|
|
120
162
|
|
|
121
163
|
const entryRows = entryTable.getCoreRowModel().rows;
|
|
164
|
+
const confirmedById = new Map(
|
|
165
|
+
newRows.map((newRow) => [newRow.tempId, newRow.confirmed]),
|
|
166
|
+
);
|
|
167
|
+
// A row being *typed* into is always sticky - it exists nowhere else to
|
|
168
|
+
// scroll back to. A confirmed row joins the scrolling flow unless
|
|
169
|
+
// `newRowsSticky` keeps it pinned, so entering many rows cannot fill the
|
|
170
|
+
// viewport with sticky chrome.
|
|
171
|
+
const stickyRows = entryRows.filter(
|
|
172
|
+
(entryRow) =>
|
|
173
|
+
features.editNewRowsSticky || confirmedById.get(entryRow.id) !== true,
|
|
174
|
+
);
|
|
175
|
+
const flowRows = features.editNewRowsSticky
|
|
176
|
+
? []
|
|
177
|
+
: entryRows.filter((entryRow) => confirmedById.get(entryRow.id) === true);
|
|
178
|
+
|
|
179
|
+
const renderEntryRow = (
|
|
180
|
+
entryRow: (typeof entryRows)[number],
|
|
181
|
+
pinnedZ: string,
|
|
182
|
+
) => {
|
|
183
|
+
const cellsById = new Map(
|
|
184
|
+
entryRow.getAllCells().map((cell) => [cell.column.id, cell]),
|
|
185
|
+
);
|
|
186
|
+
const confirmed = confirmedById.get(entryRow.id) === true;
|
|
187
|
+
return (
|
|
188
|
+
<div
|
|
189
|
+
key={entryRow.id}
|
|
190
|
+
role="row"
|
|
191
|
+
data-dg-part="entry-row"
|
|
192
|
+
data-row-id={entryRow.id}
|
|
193
|
+
data-new
|
|
194
|
+
data-confirmed={confirmed}
|
|
195
|
+
className={classes.entryRow}
|
|
196
|
+
>
|
|
197
|
+
{orderedColumns.map((column) => {
|
|
198
|
+
const cell = cellsById.get(column.id);
|
|
199
|
+
const layout = layoutFor(column.id);
|
|
200
|
+
const editable =
|
|
201
|
+
cell !== undefined && isEntryCellEditable(entryRow, column);
|
|
202
|
+
return (
|
|
203
|
+
<div
|
|
204
|
+
key={column.id}
|
|
205
|
+
role="cell"
|
|
206
|
+
data-column-id={column.id}
|
|
207
|
+
data-align={getColumnAlign(column)}
|
|
208
|
+
data-control-column={isControlColumn(column.id)}
|
|
209
|
+
// A confirmed row re-opens where it is double-clicked, the
|
|
210
|
+
// same gesture a body cell answers.
|
|
211
|
+
onDoubleClick={
|
|
212
|
+
confirmed && !isControlColumn(column.id)
|
|
213
|
+
? () =>
|
|
214
|
+
edit.begin({
|
|
215
|
+
rowId: entryRow.id,
|
|
216
|
+
columnId: editable ? column.id : null,
|
|
217
|
+
})
|
|
218
|
+
: undefined
|
|
219
|
+
}
|
|
220
|
+
className={[
|
|
221
|
+
classes.entryCell,
|
|
222
|
+
layout.isBoundary && layout.pinnedAt === "left"
|
|
223
|
+
? sticky.stickyLeft
|
|
224
|
+
: "",
|
|
225
|
+
layout.isBoundary && layout.pinnedAt === "right"
|
|
226
|
+
? sticky.stickyRight
|
|
227
|
+
: "",
|
|
228
|
+
]
|
|
229
|
+
.filter(Boolean)
|
|
230
|
+
.join(" ")}
|
|
231
|
+
style={{
|
|
232
|
+
minHeight: rowHeight,
|
|
233
|
+
left: layout.pinnedAt === "left" ? layout.offset : undefined,
|
|
234
|
+
right: layout.pinnedAt === "right" ? layout.offset : undefined,
|
|
235
|
+
position: layout.pinnedAt ? "sticky" : undefined,
|
|
236
|
+
zIndex: layout.pinnedAt ? pinnedZ : undefined,
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
{cell !== undefined && column.id === EDIT_COLUMN_ID ? (
|
|
240
|
+
// The lane's cell - the entry row's controls.
|
|
241
|
+
flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
242
|
+
) : confirmed && cell !== undefined ? (
|
|
243
|
+
// Entered, awaiting Save all: a value row through the
|
|
244
|
+
// columns' own renderers, over the draft the memo above
|
|
245
|
+
// fed this table.
|
|
246
|
+
<span className={classes.cellContent}>
|
|
247
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
248
|
+
</span>
|
|
249
|
+
) : editable && cell !== undefined ? (
|
|
250
|
+
<TMDataGridCellEditor
|
|
251
|
+
cell={cell}
|
|
252
|
+
row={entryRow}
|
|
253
|
+
takeSeedText={() => undefined}
|
|
254
|
+
onClose={() => {}}
|
|
255
|
+
inEntryBlock
|
|
256
|
+
/>
|
|
257
|
+
) : null}
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
})}
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
};
|
|
122
264
|
|
|
123
265
|
return (
|
|
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
|
-
data-column-id={column.id}
|
|
152
|
-
data-align={getColumnAlign(column)}
|
|
153
|
-
data-control-column={isControlColumn(column.id)}
|
|
154
|
-
className={[
|
|
155
|
-
classes.entryCell,
|
|
156
|
-
layout.isBoundary && layout.pinnedAt === "left"
|
|
157
|
-
? sticky.stickyLeft
|
|
158
|
-
: "",
|
|
159
|
-
layout.isBoundary && layout.pinnedAt === "right"
|
|
160
|
-
? sticky.stickyRight
|
|
161
|
-
: "",
|
|
162
|
-
]
|
|
163
|
-
.filter(Boolean)
|
|
164
|
-
.join(" ")}
|
|
165
|
-
style={{
|
|
166
|
-
minHeight: rowHeight,
|
|
167
|
-
left: layout.pinnedAt === "left" ? layout.offset : undefined,
|
|
168
|
-
right:
|
|
169
|
-
layout.pinnedAt === "right" ? layout.offset : undefined,
|
|
170
|
-
position: layout.pinnedAt ? "sticky" : undefined,
|
|
171
|
-
zIndex: layout.pinnedAt
|
|
172
|
-
? "var(--dg-z-pinned-row-pinned-cell, 5)"
|
|
173
|
-
: undefined,
|
|
174
|
-
}}
|
|
175
|
-
>
|
|
176
|
-
{editable && cell !== undefined ? (
|
|
177
|
-
<TMDataGridCellEditor
|
|
178
|
-
cell={cell}
|
|
179
|
-
row={entryRow}
|
|
180
|
-
takeSeedText={() => undefined}
|
|
181
|
-
onClose={() => {}}
|
|
182
|
-
/>
|
|
183
|
-
) : cell !== undefined && column.id === EDIT_COLUMN_ID ? (
|
|
184
|
-
// The lane's cell - the entry row's ✓/✕ pair.
|
|
185
|
-
flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
186
|
-
) : null}
|
|
187
|
-
</div>
|
|
188
|
-
);
|
|
189
|
-
})}
|
|
190
|
-
</div>
|
|
191
|
-
);
|
|
192
|
-
})}
|
|
193
|
-
</div>
|
|
266
|
+
<>
|
|
267
|
+
{stickyRows.length > 0 && (
|
|
268
|
+
<div
|
|
269
|
+
ref={blockRef}
|
|
270
|
+
role="rowgroup"
|
|
271
|
+
data-dg-entry-block
|
|
272
|
+
className={classes.entryBlock}
|
|
273
|
+
>
|
|
274
|
+
{stickyRows.map((entryRow) =>
|
|
275
|
+
renderEntryRow(entryRow, "var(--dg-z-pinned-row-pinned-cell, 5)"),
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
{flowRows.length > 0 && (
|
|
280
|
+
<div
|
|
281
|
+
role="rowgroup"
|
|
282
|
+
data-dg-entry-flow-block
|
|
283
|
+
className={classes.entryFlowBlock}
|
|
284
|
+
>
|
|
285
|
+
{/* In flow, so a pinned cell stacks like a body row's, under the
|
|
286
|
+
sticky blocks it scrolls past. */}
|
|
287
|
+
{flowRows.map((entryRow) =>
|
|
288
|
+
renderEntryRow(entryRow, "var(--dg-z-pinned-cell, 2)"),
|
|
289
|
+
)}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
</>
|
|
194
293
|
);
|
|
195
294
|
}
|
|
@@ -15,6 +15,7 @@ import { autosizeColumn } from "../core/autosize";
|
|
|
15
15
|
import {
|
|
16
16
|
getColumnRegion,
|
|
17
17
|
getStepTargetColumn,
|
|
18
|
+
keepGeneratedColumnsOutermost,
|
|
18
19
|
moveColumn,
|
|
19
20
|
moveColumnByStep,
|
|
20
21
|
type TMDataGridDropSide,
|
|
@@ -238,6 +239,10 @@ export function TMDataGridHeaderCell({
|
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
column.pin(position);
|
|
242
|
+
// `pin("right")` appends, which would leave the column outside the edit
|
|
243
|
+
// lane. Normalising after the pin rather than writing the lane by hand
|
|
244
|
+
// keeps TanStack's own handling of a header group's leaves.
|
|
245
|
+
table.setColumnPinning(keepGeneratedColumnsOutermost);
|
|
241
246
|
}
|
|
242
247
|
|
|
243
248
|
const menuItems: ReactNode[] = [];
|
|
@@ -497,8 +502,8 @@ export function TMDataGridHeaderCell({
|
|
|
497
502
|
// A control lane is a fixed track, so it cannot take the cell padding the
|
|
498
503
|
// scale grows for text. See isControlColumn.
|
|
499
504
|
data-control-column={isControlColumn(column.id)}
|
|
500
|
-
// Only meaningful on a sortable column: "none"
|
|
501
|
-
// header sorts, which is
|
|
505
|
+
// Only meaningful on a sortable column: "none" announces that this
|
|
506
|
+
// header sorts, which is wrong on one that does not.
|
|
502
507
|
aria-sort={
|
|
503
508
|
canSort
|
|
504
509
|
? isSorted
|
|
@@ -583,10 +588,10 @@ export function TMDataGridHeaderCell({
|
|
|
583
588
|
<ActionIcon
|
|
584
589
|
className={`${classes.headerAction} ${classes.sortAction}`}
|
|
585
590
|
data-pinned-visible={isSorted}
|
|
586
|
-
// The arrow is the
|
|
587
|
-
// tints, so it takes the colour while the sort holds. On
|
|
588
|
-
// unsorted column it is only the hover affordance, and stays a
|
|
589
|
-
// faded grey so the two
|
|
591
|
+
// The arrow is the only sort indicator now that the title no
|
|
592
|
+
// longer tints, so it takes the colour while the sort holds. On
|
|
593
|
+
// an unsorted column it is only the hover affordance, and stays a
|
|
594
|
+
// faded grey so the two are not confused.
|
|
590
595
|
data-sorted={isSorted}
|
|
591
596
|
variant="subtle"
|
|
592
597
|
color={isSorted ? undefined : "gray"}
|
|
@@ -87,11 +87,10 @@ function SelectRowCheckbox<TData extends RowData>({
|
|
|
87
87
|
const tabIndex = useCellControlTabIndex();
|
|
88
88
|
const isGroupRow = row.subRows.length > 0;
|
|
89
89
|
|
|
90
|
-
// A group row is never selected by id: `rowSelection` only
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// as that stops being true.
|
|
90
|
+
// A group row is never selected by id: `rowSelection` only holds the leaves,
|
|
91
|
+
// and TanStack's `getIsSelected()` is a plain lookup in that map. A group
|
|
92
|
+
// therefore asks about its descendants instead, so the box means "all of
|
|
93
|
+
// these" and goes indeterminate as soon as that stops being true.
|
|
95
94
|
const selected = useSelector(row.table.store, () =>
|
|
96
95
|
isGroupRow ? row.getIsAllSubRowsSelected() : row.getIsSelected(),
|
|
97
96
|
);
|
|
@@ -177,6 +176,11 @@ export function createSelectColumn<TData extends RowData>(
|
|
|
177
176
|
enableSorting: false,
|
|
178
177
|
enableColumnFilter: false,
|
|
179
178
|
enableGlobalFilter: false,
|
|
179
|
+
// Not a column the user chose, so not one they can switch off: hiding the
|
|
180
|
+
// lane would take the grid's only way to select a row with it, with the
|
|
181
|
+
// row-selection state left behind and no way back to it. Keeping it out of
|
|
182
|
+
// "Manage columns" follows from this - the panel lists what can be hidden.
|
|
183
|
+
enableHiding: false,
|
|
180
184
|
// Structurally pinned to the left; users shouldn't be able to move it.
|
|
181
185
|
enablePinning: false,
|
|
182
186
|
header: ({ table }) => <SelectAllHeader table={table} />,
|
|
@@ -329,12 +329,18 @@
|
|
|
329
329
|
padding: var(--mantine-spacing-xl);
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
/* Marked deleted under
|
|
333
|
-
reports it
|
|
334
|
-
|
|
332
|
+
/* Marked deleted under draft: struck through and inert until submitAll
|
|
333
|
+
reports it - a row about to go away must not keep taking edits. Interaction
|
|
334
|
+
comes off the data cells, not the row: `pointer-events: none` on the row
|
|
335
|
+
would also kill the edit lane's Restore, which is the one control the row
|
|
336
|
+
still needs (jsdom does not enforce pointer-events, so only a real browser
|
|
337
|
+
ever saw that bug). */
|
|
335
338
|
.bodyRow[data-deleted="true"] {
|
|
336
339
|
text-decoration: line-through;
|
|
337
340
|
opacity: 0.55;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.bodyRow[data-deleted="true"] .bodyCell:not([data-control-column="true"]) {
|
|
338
344
|
pointer-events: none;
|
|
339
345
|
}
|
|
340
346
|
|
|
@@ -359,6 +365,15 @@
|
|
|
359
365
|
grid-template-columns: subgrid;
|
|
360
366
|
}
|
|
361
367
|
|
|
368
|
+
/* Confirmed entry rows in the scrolling flow (the default; `newRowsSticky`
|
|
369
|
+
keeps them in the sticky block instead). Same tracks, no stickiness - they
|
|
370
|
+
sit above the body rows and scroll with them. */
|
|
371
|
+
.entryFlowBlock {
|
|
372
|
+
display: grid;
|
|
373
|
+
grid-column: 1 / -1;
|
|
374
|
+
grid-template-columns: subgrid;
|
|
375
|
+
}
|
|
376
|
+
|
|
362
377
|
/* The pinned edge blocks - user-pinned rows held at the top or bottom of the
|
|
363
378
|
body, on the entry block's sticky mechanics: the block is the sticky
|
|
364
379
|
element, its rows flow normally inside it. The top block stacks under the
|
|
@@ -407,6 +422,35 @@
|
|
|
407
422
|
justify-content: center;
|
|
408
423
|
}
|
|
409
424
|
|
|
425
|
+
/* A confirmed entry row - entered, awaiting Save all. It reads as a value
|
|
426
|
+
row: text padding, the body's border weight, and the "new" tint instead of
|
|
427
|
+
the editing underline. */
|
|
428
|
+
.entryRow[data-confirmed="true"] .entryCell {
|
|
429
|
+
padding: 0 var(--dg-padding);
|
|
430
|
+
border-bottom: 1px solid var(--mantine-color-default-border);
|
|
431
|
+
background-color: var(--dg-row-new-bg, var(--mantine-color-body));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.entryRow[data-confirmed="true"] .entryCell[data-control-column="true"] {
|
|
435
|
+
padding: 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* Draft mode's change marker in the edit lane - dimmed beside the buttons
|
|
439
|
+
that act, red when validation blocks the row. */
|
|
440
|
+
.rowStateIndicator {
|
|
441
|
+
display: inline-flex;
|
|
442
|
+
align-items: center;
|
|
443
|
+
justify-content: center;
|
|
444
|
+
width: 22px;
|
|
445
|
+
height: 22px;
|
|
446
|
+
flex: 0 0 auto;
|
|
447
|
+
color: var(--mantine-color-dimmed);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.rowStateIndicator[data-error] {
|
|
451
|
+
color: var(--mantine-color-error);
|
|
452
|
+
}
|
|
453
|
+
|
|
410
454
|
.entryCell[data-align="right"] {
|
|
411
455
|
justify-content: flex-end;
|
|
412
456
|
}
|