@jielga/tmdatagrid 2.2.0 → 2.4.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/index.d.ts +302 -11
- package/dist/index.js +2338 -2097
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/columns.md +9 -1
- package/docs/components.md +1 -0
- package/docs/filtering.md +5 -1
- package/docs/grouping.md +1 -1
- package/docs/localization.md +3 -3
- package/docs/persistence.md +189 -2
- package/docs/row-details.md +5 -0
- package/docs/row-interaction.md +1 -1
- package/docs/row-styling.md +17 -0
- package/docs/typed-deep-links.md +169 -0
- package/docs/use-tm-data-grid.md +6 -2
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +1 -1
- package/skills/appearance/references/labels.md +3 -3
- package/skills/cell-selection/SKILL.md +1 -1
- package/skills/columns/SKILL.md +6 -3
- package/skills/columns/references/columns-api.md +1 -0
- package/skills/data/SKILL.md +1 -1
- package/skills/editing/SKILL.md +1 -1
- package/skills/filtering/SKILL.md +26 -13
- package/skills/getting-started/SKILL.md +2 -2
- package/skills/grouping/SKILL.md +2 -2
- package/skills/migrating-to-2/SKILL.md +1 -1
- package/skills/options/SKILL.md +105 -10
- package/skills/options/references/typed-links.md +134 -0
- package/skills/rows/SKILL.md +18 -9
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +1 -1
- package/skills/testing-components/SKILL.md +1 -1
- package/skills/testing-editing/SKILL.md +1 -1
- package/src/components/TMDataGridFilterPills.module.css +9 -7
- package/src/components/TMDataGridFilterPills.tsx +15 -1
- package/src/components/TMDataGridGroupColumn.tsx +19 -4
- package/src/components/TMDataGridHeaderFilterRow.tsx +7 -1
- package/src/components/TMDataGridTable.module.css +15 -6
- package/src/components/TMDataGridTable.tsx +23 -11
- package/src/components/sticky.module.css +27 -7
- package/src/core/filterControls.ts +8 -0
- package/src/core/filterOperators.ts +54 -18
- package/src/core/grouping.ts +11 -3
- package/src/core/labels.ts +4 -3
- package/src/core/persistence.ts +330 -26
- package/src/core/urlState.ts +439 -0
- package/src/core/valueText.ts +46 -0
- package/src/index.ts +14 -0
- package/src/useTMDataGrid.tsx +145 -21
|
@@ -6,6 +6,9 @@ import type {
|
|
|
6
6
|
TableFeatures,
|
|
7
7
|
} from "@tanstack/react-table";
|
|
8
8
|
import type { TMDataGridFeatures, TMDataGridTable } from "../useTMDataGrid";
|
|
9
|
+
import type { TMDataGridOption } from "./columnOptions";
|
|
10
|
+
import type { TMDataGridLabels } from "./labels";
|
|
11
|
+
import { formatValueText } from "./valueText";
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* The value shape stored in `columnFilters` for every TMDataGrid column.
|
|
@@ -75,7 +78,7 @@ export const FILTER_OPERATOR_LABELS: Record<TMDataGridFilterOperator, string> =
|
|
|
75
78
|
isNotEmpty: "is not empty",
|
|
76
79
|
};
|
|
77
80
|
|
|
78
|
-
const STRING_OPERATORS
|
|
81
|
+
const STRING_OPERATORS = [
|
|
79
82
|
"contains",
|
|
80
83
|
"equals",
|
|
81
84
|
"notEquals",
|
|
@@ -83,9 +86,9 @@ const STRING_OPERATORS: readonly TMDataGridFilterOperator[] = [
|
|
|
83
86
|
"endsWith",
|
|
84
87
|
"isEmpty",
|
|
85
88
|
"isNotEmpty",
|
|
86
|
-
];
|
|
89
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
87
90
|
|
|
88
|
-
const NUMBER_OPERATORS
|
|
91
|
+
const NUMBER_OPERATORS = [
|
|
89
92
|
"equals",
|
|
90
93
|
"notEquals",
|
|
91
94
|
"greaterThan",
|
|
@@ -95,12 +98,12 @@ const NUMBER_OPERATORS: readonly TMDataGridFilterOperator[] = [
|
|
|
95
98
|
"between",
|
|
96
99
|
"isEmpty",
|
|
97
100
|
"isNotEmpty",
|
|
98
|
-
];
|
|
101
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
99
102
|
|
|
100
103
|
// `equals` doubles as a date's "is": it compares by calendar day whenever the
|
|
101
104
|
// cell holds a Date (see matchesFilter), so a separate `is` operator would be
|
|
102
105
|
// the same comparison under a second name.
|
|
103
|
-
const DATE_OPERATORS
|
|
106
|
+
const DATE_OPERATORS = [
|
|
104
107
|
"equals",
|
|
105
108
|
"notEquals",
|
|
106
109
|
"before",
|
|
@@ -110,23 +113,23 @@ const DATE_OPERATORS: readonly TMDataGridFilterOperator[] = [
|
|
|
110
113
|
"between",
|
|
111
114
|
"isEmpty",
|
|
112
115
|
"isNotEmpty",
|
|
113
|
-
];
|
|
116
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
114
117
|
|
|
115
|
-
const BOOLEAN_OPERATORS
|
|
118
|
+
const BOOLEAN_OPERATORS = [
|
|
116
119
|
"equals",
|
|
117
120
|
"notEquals",
|
|
118
121
|
"isEmpty",
|
|
119
122
|
"isNotEmpty",
|
|
120
|
-
];
|
|
123
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
121
124
|
|
|
122
125
|
// One set for both select types: the operators are about membership in the
|
|
123
126
|
// filter's set, which reads the same whether the cell holds one value or many.
|
|
124
|
-
const SELECT_OPERATORS
|
|
127
|
+
const SELECT_OPERATORS = [
|
|
125
128
|
"isAnyOf",
|
|
126
129
|
"isNoneOf",
|
|
127
130
|
"isEmpty",
|
|
128
131
|
"isNotEmpty",
|
|
129
|
-
];
|
|
132
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
130
133
|
|
|
131
134
|
/** Operators that ignore the value input (the input is disabled for these). */
|
|
132
135
|
const VALUELESS_OPERATORS: readonly TMDataGridFilterOperator[] = [
|
|
@@ -143,16 +146,27 @@ const ARRAY_OPERATORS: readonly TMDataGridFilterOperator[] = [
|
|
|
143
146
|
/** Operators whose value is a `[min, max]` pair. */
|
|
144
147
|
const RANGE_OPERATORS: readonly TMDataGridFilterOperator[] = ["between"];
|
|
145
148
|
|
|
146
|
-
const OPERATORS_BY_TYPE
|
|
147
|
-
TMDataGridColumnType,
|
|
148
|
-
readonly TMDataGridFilterOperator[]
|
|
149
|
-
> = {
|
|
149
|
+
const OPERATORS_BY_TYPE = {
|
|
150
150
|
string: STRING_OPERATORS,
|
|
151
151
|
number: NUMBER_OPERATORS,
|
|
152
152
|
boolean: BOOLEAN_OPERATORS,
|
|
153
153
|
date: DATE_OPERATORS,
|
|
154
154
|
select: SELECT_OPERATORS,
|
|
155
155
|
multiSelect: SELECT_OPERATORS,
|
|
156
|
+
} as const satisfies Record<
|
|
157
|
+
TMDataGridColumnType,
|
|
158
|
+
readonly TMDataGridFilterOperator[]
|
|
159
|
+
>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The operators each column type offers, as a type: the same lists the filter
|
|
163
|
+
* panel offers, so a typed link can never name an operator the panel lacks.
|
|
164
|
+
*
|
|
165
|
+
* @internal Feeds the typed links in urlState.ts; not exported from the
|
|
166
|
+
* package.
|
|
167
|
+
*/
|
|
168
|
+
export type OperatorsByColumnType = {
|
|
169
|
+
[TType in TMDataGridColumnType]: (typeof OPERATORS_BY_TYPE)[TType][number];
|
|
156
170
|
};
|
|
157
171
|
|
|
158
172
|
const DEFAULT_OPERATOR_BY_TYPE: Record<
|
|
@@ -251,6 +265,19 @@ export function isFilterActive(value: unknown): boolean {
|
|
|
251
265
|
: typeof value.value === "string" && value.value.trim() !== "";
|
|
252
266
|
}
|
|
253
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Whether one `columnFilters` entry narrows the rows, by the one rule the grid
|
|
270
|
+
* uses for "active": an envelope counts once {@link isFilterActive} says so,
|
|
271
|
+
* and a value that is not an envelope always counts, since a raw value written
|
|
272
|
+
* by consumer code narrows the rows as far as the grid can tell.
|
|
273
|
+
*
|
|
274
|
+
* @internal Shared by the empty state and the `urlKey` param; not exported
|
|
275
|
+
* from the package.
|
|
276
|
+
*/
|
|
277
|
+
export function filterNarrowsRows(entry: { value: unknown }): boolean {
|
|
278
|
+
return !isTMDataGridFilterValue(entry.value) || isFilterActive(entry.value);
|
|
279
|
+
}
|
|
280
|
+
|
|
254
281
|
/** One column's filter, typed - what `columnFilters` holds per entry. */
|
|
255
282
|
export type TMDataGridColumnFilter = {
|
|
256
283
|
id: string;
|
|
@@ -294,30 +321,39 @@ export function activeColumnFilters<TData extends RowData>(
|
|
|
294
321
|
*
|
|
295
322
|
* The type's default operator is left implicit - "First name: Sofia" reads the
|
|
296
323
|
* way a person would say it - while any other operator is spelled out, since
|
|
297
|
-
* that is the part a reader cannot guess.
|
|
324
|
+
* that is the part a reader cannot guess. A value with an option reads as the
|
|
325
|
+
* option's label, and a boolean as `labels.booleanTrue` / `booleanFalse`.
|
|
298
326
|
*/
|
|
299
327
|
export function formatFilterLabel({
|
|
300
328
|
label,
|
|
301
329
|
type,
|
|
302
330
|
filter,
|
|
303
331
|
operatorLabels = FILTER_OPERATOR_LABELS,
|
|
332
|
+
options,
|
|
333
|
+
labels,
|
|
304
334
|
}: {
|
|
305
335
|
label: string;
|
|
306
336
|
type: TMDataGridColumnType;
|
|
307
337
|
filter: TMDataGridFilterValue;
|
|
308
338
|
/** Localized operator names - `labels.operators`. Defaults to English. */
|
|
309
339
|
operatorLabels?: Record<TMDataGridFilterOperator, string>;
|
|
340
|
+
/** The column's options, so a value reads as its option's label. */
|
|
341
|
+
options?: ReadonlyArray<TMDataGridOption>;
|
|
342
|
+
/** What a boolean value reads as. Defaults to English. */
|
|
343
|
+
labels?: Pick<TMDataGridLabels, "booleanTrue" | "booleanFalse">;
|
|
310
344
|
}): string {
|
|
311
345
|
const { operator, value } = filter;
|
|
312
346
|
if (!operatorNeedsValue(operator)) {
|
|
313
347
|
return `${label} ${operatorLabels[operator]}`;
|
|
314
348
|
}
|
|
349
|
+
const valueText = (entry: string) =>
|
|
350
|
+
formatValueText(String(entry).trim(), { type, options, labels });
|
|
315
351
|
const text =
|
|
316
352
|
operator === "between" && Array.isArray(value)
|
|
317
|
-
? value.map(
|
|
353
|
+
? value.map(valueText).join("–")
|
|
318
354
|
: Array.isArray(value)
|
|
319
|
-
? value.join(", ")
|
|
320
|
-
: String(value)
|
|
355
|
+
? value.map(valueText).join(", ")
|
|
356
|
+
: valueText(String(value));
|
|
321
357
|
if (operator === getDefaultOperator(type)) return `${label}: ${text}`;
|
|
322
358
|
return `${label} ${operatorLabels[operator]} ${text}`;
|
|
323
359
|
}
|
package/src/core/grouping.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Row, RowData } from "@tanstack/react-table";
|
|
2
2
|
import type { TMDataGridFeatures } from "../useTMDataGrid";
|
|
3
|
+
import { formatValueText, type TMDataGridValueTextOptions } from "./valueText";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The data rows under a group row, at any depth.
|
|
@@ -29,14 +30,21 @@ const BLANK_GROUP_LABEL = "(Blank)";
|
|
|
29
30
|
* Deliberately not the grouped column's own `cell` renderer: that renderer is
|
|
30
31
|
* written for a data row and is free to reach into `row.original`, which on a
|
|
31
32
|
* group row is the first leaf's record rather than anything about the group.
|
|
33
|
+
* `options` carries what the chrome knows of the column instead - its type,
|
|
34
|
+
* its option labels and the boolean labels.
|
|
32
35
|
*/
|
|
33
36
|
export function formatGroupValue(
|
|
34
37
|
value: unknown,
|
|
35
38
|
blankLabel = BLANK_GROUP_LABEL,
|
|
39
|
+
options?: TMDataGridValueTextOptions,
|
|
36
40
|
): string {
|
|
37
|
-
if (
|
|
41
|
+
if (
|
|
42
|
+
value === null ||
|
|
43
|
+
value === undefined ||
|
|
44
|
+
value === "" ||
|
|
45
|
+
(Array.isArray(value) && value.length === 0)
|
|
46
|
+
) {
|
|
38
47
|
return blankLabel;
|
|
39
48
|
}
|
|
40
|
-
|
|
41
|
-
return String(value);
|
|
49
|
+
return formatValueText(value, options);
|
|
42
50
|
}
|
package/src/core/labels.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
FILTER_OPERATOR_LABELS,
|
|
3
3
|
type TMDataGridFilterOperator,
|
|
4
4
|
} from "./filterOperators";
|
|
5
|
+
import { BOOLEAN_LABELS_EN } from "./valueText";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Every user-facing string in the grid - menu items, panels, tooltips, the
|
|
@@ -55,7 +56,7 @@ export type TMDataGridLabels = {
|
|
|
55
56
|
filterTo: string;
|
|
56
57
|
/** `DgTriStateFilter`'s no-filter segment. */
|
|
57
58
|
filterAll: string;
|
|
58
|
-
/** What a boolean
|
|
59
|
+
/** What a boolean `true` reads as - its filter choice, filter pill and group row. */
|
|
59
60
|
booleanTrue: string;
|
|
60
61
|
booleanFalse: string;
|
|
61
62
|
addFilter: string;
|
|
@@ -225,8 +226,8 @@ export const TMDATAGRID_LABELS_EN: TMDataGridLabels = {
|
|
|
225
226
|
filterFrom: "From",
|
|
226
227
|
filterTo: "To",
|
|
227
228
|
filterAll: "All",
|
|
228
|
-
booleanTrue:
|
|
229
|
-
booleanFalse:
|
|
229
|
+
booleanTrue: BOOLEAN_LABELS_EN.booleanTrue,
|
|
230
|
+
booleanFalse: BOOLEAN_LABELS_EN.booleanFalse,
|
|
230
231
|
addFilter: "Add filter",
|
|
231
232
|
clearAllFilters: "Clear all",
|
|
232
233
|
closeFilters: "Close filters",
|