@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.
Files changed (50) hide show
  1. package/dist/index.d.ts +302 -11
  2. package/dist/index.js +2338 -2097
  3. package/dist/index.js.map +1 -1
  4. package/dist/styles.css +1 -1
  5. package/docs/columns.md +9 -1
  6. package/docs/components.md +1 -0
  7. package/docs/filtering.md +5 -1
  8. package/docs/grouping.md +1 -1
  9. package/docs/localization.md +3 -3
  10. package/docs/persistence.md +189 -2
  11. package/docs/row-details.md +5 -0
  12. package/docs/row-interaction.md +1 -1
  13. package/docs/row-styling.md +17 -0
  14. package/docs/typed-deep-links.md +169 -0
  15. package/docs/use-tm-data-grid.md +6 -2
  16. package/package.json +1 -1
  17. package/skills/appearance/SKILL.md +1 -1
  18. package/skills/appearance/references/labels.md +3 -3
  19. package/skills/cell-selection/SKILL.md +1 -1
  20. package/skills/columns/SKILL.md +6 -3
  21. package/skills/columns/references/columns-api.md +1 -0
  22. package/skills/data/SKILL.md +1 -1
  23. package/skills/editing/SKILL.md +1 -1
  24. package/skills/filtering/SKILL.md +26 -13
  25. package/skills/getting-started/SKILL.md +2 -2
  26. package/skills/grouping/SKILL.md +2 -2
  27. package/skills/migrating-to-2/SKILL.md +1 -1
  28. package/skills/options/SKILL.md +105 -10
  29. package/skills/options/references/typed-links.md +134 -0
  30. package/skills/rows/SKILL.md +18 -9
  31. package/skills/server-side/SKILL.md +1 -1
  32. package/skills/testing/SKILL.md +1 -1
  33. package/skills/testing-components/SKILL.md +1 -1
  34. package/skills/testing-editing/SKILL.md +1 -1
  35. package/src/components/TMDataGridFilterPills.module.css +9 -7
  36. package/src/components/TMDataGridFilterPills.tsx +15 -1
  37. package/src/components/TMDataGridGroupColumn.tsx +19 -4
  38. package/src/components/TMDataGridHeaderFilterRow.tsx +7 -1
  39. package/src/components/TMDataGridTable.module.css +15 -6
  40. package/src/components/TMDataGridTable.tsx +23 -11
  41. package/src/components/sticky.module.css +27 -7
  42. package/src/core/filterControls.ts +8 -0
  43. package/src/core/filterOperators.ts +54 -18
  44. package/src/core/grouping.ts +11 -3
  45. package/src/core/labels.ts +4 -3
  46. package/src/core/persistence.ts +330 -26
  47. package/src/core/urlState.ts +439 -0
  48. package/src/core/valueText.ts +46 -0
  49. package/src/index.ts +14 -0
  50. package/src/useTMDataGrid.tsx +145 -21
@@ -0,0 +1,439 @@
1
+ import type { TMDataGridColumnType } from "./filterOperators";
2
+ import type {
3
+ OperatorsByColumnType,
4
+ TMDataGridColumnFilter,
5
+ TMDataGridFilterOperator,
6
+ TMDataGridFilterValue,
7
+ } from "./filterOperators";
8
+ import {
9
+ DATA_STATE_SLICES,
10
+ forEachLeafColumnDef,
11
+ type TMDataGridDataSlice,
12
+ type TMDataGridPersistence,
13
+ type TMDataGridPersistKey,
14
+ type TMDataGridUrlStorage,
15
+ } from "./persistence";
16
+
17
+ /**
18
+ * Typed deep links.
19
+ *
20
+ * `createTMDataGridColumnHelper` returns columns that carry, in their type
21
+ * only, what a link needs to know about them: the id, the `meta.type`, the
22
+ * value type, the offered operators, and whether the column can be filtered,
23
+ * sorted and put in the URL. `createTMDataGridUrlState` reads that from the
24
+ * grid's own columns, so a link can name only what the grid can show: a
25
+ * filter on a column the grid lacks, an operator its type does not offer or a
26
+ * value of the wrong type is a compile error, not a link that silently opens
27
+ * unfiltered.
28
+ *
29
+ * Nothing here is visible at runtime except the definition object. The
30
+ * column brand is a type, never a property.
31
+ */
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // The column brand
35
+ // ---------------------------------------------------------------------------
36
+
37
+ /**
38
+ * What a column built with `createTMDataGridColumnHelper` tells a typed link
39
+ * about itself. Read from literal values in the column definition: a value
40
+ * that is not a literal (`enableSorting: isAdmin`) reads as "not allowed".
41
+ */
42
+ export type TMDataGridColumnInfo = {
43
+ /** The column id, by TanStack's rule: `id`, else the accessor key with `.` replaced by `_`. */
44
+ id: string;
45
+ /** `meta.type`, `"string"` when unset. */
46
+ type: TMDataGridColumnType;
47
+ /** The value type the accessor returns. */
48
+ value: unknown;
49
+ /** `meta.filter.operators`, every operator when unset. */
50
+ operators: TMDataGridFilterOperator;
51
+ /** `enableColumnFilter` is not `false`. */
52
+ filterable: boolean;
53
+ /** `enableSorting` is not `false`. */
54
+ sortable: boolean;
55
+ /** `meta.filter.url` is not `false`. */
56
+ url: boolean;
57
+ };
58
+
59
+ declare const columnInfo: unique symbol;
60
+
61
+ /**
62
+ * The type-only brand a column helper column carries. Never present at
63
+ * runtime, so spreading, cloning or serializing a column is unaffected.
64
+ */
65
+ export type TMDataGridTypedColumn<TInfo extends TMDataGridColumnInfo> = {
66
+ readonly [columnInfo]: TInfo;
67
+ };
68
+
69
+ /** The info of every leaf column in `TColumn`, a group's children included. */
70
+ export type TMDataGridColumnInfoOf<TColumn> =
71
+ TColumn extends TMDataGridTypedColumn<infer TInfo> ? TInfo : never;
72
+
73
+ /** `"address.city"` becomes `"address_city"`, as TanStack derives the id. */
74
+ type DotsToUnderscores<S extends string> = S extends `${infer Head}.${infer Tail}`
75
+ ? `${Head}_${DotsToUnderscores<Tail>}`
76
+ : S;
77
+
78
+ /**
79
+ * The column id the helper's `accessor` call produces: the explicit `id`,
80
+ * else the accessor key. An accessor function without an `id` gets `never`,
81
+ * which no link can name.
82
+ */
83
+ export type TMDataGridAccessorColumnId<TAccessor, TId extends string> = [TId] extends [never]
84
+ ? TAccessor extends string
85
+ ? DotsToUnderscores<TAccessor>
86
+ : never
87
+ : TId;
88
+
89
+ /**
90
+ * The column options whose literal values a typed link reads. Intersected into
91
+ * the column helper's parameter so each one is inferred as a literal.
92
+ *
93
+ * @internal
94
+ */
95
+ export type TMDataGridColumnInfoHints<
96
+ TId extends string,
97
+ TType extends TMDataGridColumnType,
98
+ TOperator extends TMDataGridFilterOperator,
99
+ TFilterable extends boolean,
100
+ TSortable extends boolean,
101
+ TUrl extends boolean,
102
+ > = {
103
+ id?: TId;
104
+ enableColumnFilter?: TFilterable;
105
+ enableSorting?: TSortable;
106
+ meta?: {
107
+ type?: TType;
108
+ filter?: { operators?: ReadonlyArray<TOperator>; url?: TUrl };
109
+ };
110
+ };
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Link input
114
+ // ---------------------------------------------------------------------------
115
+
116
+ /** A `Date`, or an ISO `YYYY-MM-DD` string. */
117
+ export type TMDataGridDateInput = Date | string;
118
+
119
+ /** One choice of a select column: the value, or an element of an array value. */
120
+ type OptionOf<TValue> =
121
+ NonNullable<TValue> extends ReadonlyArray<infer TElement> ? TElement : NonNullable<TValue>;
122
+
123
+ type ScalarInput = {
124
+ string: string;
125
+ number: number;
126
+ date: TMDataGridDateInput;
127
+ boolean: boolean;
128
+ select: string;
129
+ multiSelect: string;
130
+ };
131
+
132
+ type RangeEnd<TType extends TMDataGridColumnType> = TType extends "date"
133
+ ? TMDataGridDateInput
134
+ : TType extends "number"
135
+ ? number
136
+ : string;
137
+
138
+ /** One filter on one column: an operator, and the value that operator takes. */
139
+ type FilterInputFor<
140
+ TType extends TMDataGridColumnType,
141
+ TValue,
142
+ TOperator extends TMDataGridFilterOperator,
143
+ > = TOperator extends "isEmpty" | "isNotEmpty"
144
+ ? { operator: TOperator }
145
+ : TOperator extends "isAnyOf" | "isNoneOf"
146
+ ? { operator: TOperator; value: ReadonlyArray<OptionOf<TValue>> }
147
+ : TOperator extends "between"
148
+ ? {
149
+ operator: TOperator;
150
+ /** `null` leaves that end open. */
151
+ value: readonly [from: RangeEnd<TType> | null, to: RangeEnd<TType> | null];
152
+ }
153
+ : { operator: TOperator; value: ScalarInput[TType] };
154
+
155
+ /** Columns a link may filter: filterable, and not kept out of the URL. */
156
+ type LinkFilterable<TInfo extends TMDataGridColumnInfo> = Extract<
157
+ TInfo,
158
+ { filterable: true; url: true }
159
+ >;
160
+
161
+ /**
162
+ * The `filters` a link accepts: one optional entry per filterable column.
163
+ * With no filterable column it accepts no entry at all - an empty object type
164
+ * would let any key through, since TypeScript skips the unknown-key check
165
+ * against `{}`.
166
+ */
167
+ export type TMDataGridUrlFiltersInput<TInfo extends TMDataGridColumnInfo> = [
168
+ LinkFilterable<TInfo>,
169
+ ] extends [never]
170
+ ? Record<string, never>
171
+ : {
172
+ [TColumn in LinkFilterable<TInfo> as TColumn["id"]]?: FilterInputFor<
173
+ TColumn["type"],
174
+ TColumn["value"],
175
+ Extract<OperatorsByColumnType[TColumn["type"]], TColumn["operators"]>
176
+ >;
177
+ };
178
+
179
+ /** The ids a link may sort by. */
180
+ export type TMDataGridUrlSortableId<TInfo extends TMDataGridColumnInfo> = Extract<
181
+ TInfo,
182
+ { sortable: true }
183
+ >["id"];
184
+
185
+ type Carries<
186
+ TSlices extends ReadonlyArray<TMDataGridDataSlice>,
187
+ TSlice extends TMDataGridDataSlice,
188
+ > = TSlice extends TSlices[number] ? true : false;
189
+
190
+ /** What a link may set: only the slices the definition carries, only the grid's columns. */
191
+ export type TMDataGridUrlInput<
192
+ TInfo extends TMDataGridColumnInfo,
193
+ TSlices extends ReadonlyArray<TMDataGridDataSlice>,
194
+ > = (Carries<TSlices, "columnFilters"> extends true
195
+ ? { filters?: TMDataGridUrlFiltersInput<TInfo> }
196
+ : unknown) &
197
+ (Carries<TSlices, "globalFilter"> extends true ? { globalFilter?: string } : unknown) &
198
+ (Carries<TSlices, "sorting"> extends true
199
+ ? {
200
+ sorting?: ReadonlyArray<{
201
+ id: TMDataGridUrlSortableId<TInfo>;
202
+ /** Defaults to `false`. */
203
+ desc?: boolean;
204
+ }>;
205
+ }
206
+ : unknown) &
207
+ (Carries<TSlices, "pagination"> extends true
208
+ ? { pagination?: { pageIndex: number; pageSize: number } }
209
+ : unknown);
210
+
211
+ // ---------------------------------------------------------------------------
212
+ // The payload and the definition
213
+ // ---------------------------------------------------------------------------
214
+
215
+ /** What the grid keeps in its URL param: the data slices, in the grid's own state shape. */
216
+ export type TMDataGridUrlPayload = {
217
+ columnFilters?: Array<TMDataGridColumnFilter>;
218
+ /** `null` is a search the user cleared, as opposed to one the link does not set. */
219
+ globalFilter?: string | null;
220
+ sorting?: Array<{ id: string; desc: boolean }>;
221
+ pagination?: { pageIndex: number; pageSize: number };
222
+ expanded?: true | Record<string, boolean>;
223
+ };
224
+
225
+ /**
226
+ * A router's search, read and written as values rather than strings, for
227
+ * routers that keep objects in their search params and encode them
228
+ * themselves, such as TanStack Router.
229
+ */
230
+ export type TMDataGridUrlSearchStorage = {
231
+ /** The param's current value in the router's search, or `undefined`. */
232
+ get(name: string): unknown;
233
+ /** Sets the param to `value`, or removes it on `undefined`. */
234
+ set(name: string, value: TMDataGridUrlPayload | undefined): void;
235
+ };
236
+
237
+ export type TMDataGridUrlState<
238
+ TKey extends string,
239
+ TSlices extends ReadonlyArray<TMDataGridDataSlice>,
240
+ TInfo extends TMDataGridColumnInfo,
241
+ > = {
242
+ /** The URL search param. */
243
+ readonly key: TKey;
244
+ /** The data slices the param carries. */
245
+ readonly slices: TSlices;
246
+ /** For `persist.urlKey`. */
247
+ readonly urlKey: TMDataGridPersistKey<TMDataGridDataSlice>;
248
+ /** The param's value for a link, or `undefined` when the input sets nothing. */
249
+ payload(input: TMDataGridUrlInput<TInfo, TSlices>): TMDataGridUrlPayload | undefined;
250
+ /** The search object for a router link, `{ [key]: payload }`. */
251
+ search(input: TMDataGridUrlInput<TInfo, TSlices>): {
252
+ [K in TKey]: TMDataGridUrlPayload | undefined;
253
+ };
254
+ /**
255
+ * The search params for a plain link, encoded the way the grid's default
256
+ * `serialize` encodes them. Pass the `serialize` you gave `persist`, if any.
257
+ */
258
+ searchParams(
259
+ input: TMDataGridUrlInput<TInfo, TSlices>,
260
+ options?: { serialize?: (payload: TMDataGridUrlPayload) => string },
261
+ ): URLSearchParams;
262
+ /**
263
+ * A route search validator: keeps the param when it is an object and drops
264
+ * anything else. The grid checks every slice itself when it reads the param.
265
+ */
266
+ validateSearch(search: Record<string, unknown>): {
267
+ [K in TKey]?: TMDataGridUrlPayload;
268
+ };
269
+ /**
270
+ * `urlKey`, plus a `urlStorage` over `storage` when one is given. Without
271
+ * one, the grid reads and writes the browser's URL itself.
272
+ */
273
+ persist(
274
+ storage?: TMDataGridUrlSearchStorage,
275
+ ): Pick<TMDataGridPersistence, "urlKey" | "urlStorage">;
276
+ };
277
+
278
+ /**
279
+ * Defines a grid's deep links from its columns. `columns` is the array the
280
+ * grid renders, built with `createTMDataGridColumnHelper`; it decides which
281
+ * filters, operators, values and sorts a link may name.
282
+ */
283
+ export function createTMDataGridUrlState<
284
+ const TKey extends string,
285
+ TColumns extends ReadonlyArray<unknown>,
286
+ const TSlices extends ReadonlyArray<TMDataGridDataSlice> = typeof DATA_STATE_SLICES,
287
+ >(options: {
288
+ /** The URL search param. */
289
+ key: TKey;
290
+ /** The grid's columns. */
291
+ columns: TColumns;
292
+ /** The data slices the param carries. Defaults to every data slice. */
293
+ slices?: TSlices;
294
+ }): TMDataGridUrlState<TKey, TSlices, TMDataGridColumnInfoOf<TColumns[number]>> {
295
+ const { key, columns } = options;
296
+ const slices = (options.slices ?? DATA_STATE_SLICES) as TSlices;
297
+ const urlKey: TMDataGridPersistKey<TMDataGridDataSlice> = options.slices
298
+ ? [key, options.slices]
299
+ : key;
300
+
301
+ const columnTypes = new Map<string, TMDataGridColumnType>();
302
+ forEachLeafColumnDef(columns, (id, record) => {
303
+ const type = (record.meta as { type?: TMDataGridColumnType } | undefined)?.type;
304
+ columnTypes.set(id, type ?? "string");
305
+ });
306
+
307
+ const payload = (input: LooseUrlInput): TMDataGridUrlPayload | undefined =>
308
+ toPayload(input, columnTypes, slices);
309
+
310
+ return {
311
+ key,
312
+ slices,
313
+ urlKey,
314
+ payload: (input) => payload(input as LooseUrlInput),
315
+ search: (input) =>
316
+ ({ [key]: payload(input as LooseUrlInput) }) as {
317
+ [K in TKey]: TMDataGridUrlPayload | undefined;
318
+ },
319
+ searchParams: (input, { serialize = JSON.stringify } = {}) => {
320
+ const value = payload(input as LooseUrlInput);
321
+ const params = new URLSearchParams();
322
+ if (value !== undefined) params.set(key, serialize(value));
323
+ return params;
324
+ },
325
+ validateSearch: (search) => {
326
+ const value = search[key];
327
+ return (isPlainObject(value) ? { [key]: value } : {}) as {
328
+ [K in TKey]?: TMDataGridUrlPayload;
329
+ };
330
+ },
331
+ persist: (storage) =>
332
+ storage ? { urlKey, urlStorage: toUrlStorage(storage) } : { urlKey },
333
+ };
334
+ }
335
+
336
+ // ---------------------------------------------------------------------------
337
+ // Runtime
338
+ // ---------------------------------------------------------------------------
339
+
340
+ type LooseFilterInput = { operator: TMDataGridFilterOperator; value?: unknown };
341
+
342
+ type LooseUrlInput = {
343
+ filters?: Record<string, LooseFilterInput | undefined>;
344
+ globalFilter?: string;
345
+ sorting?: ReadonlyArray<{ id: string; desc?: boolean }>;
346
+ pagination?: { pageIndex: number; pageSize: number };
347
+ };
348
+
349
+ /**
350
+ * The input in the grid's own state shape. A slice the definition does not
351
+ * carry is left out even when an untyped caller passes it, since the grid
352
+ * would ignore it in the URL anyway.
353
+ */
354
+ function toPayload(
355
+ input: LooseUrlInput,
356
+ columnTypes: ReadonlyMap<string, TMDataGridColumnType>,
357
+ slices: ReadonlyArray<TMDataGridDataSlice>,
358
+ ): TMDataGridUrlPayload | undefined {
359
+ const payload: TMDataGridUrlPayload = {};
360
+
361
+ if (input.filters && slices.includes("columnFilters")) {
362
+ const columnFilters = Object.entries(input.filters).flatMap(([id, filter]) =>
363
+ filter ? [{ id, value: toFilterValue(filter, columnTypes.get(id)) }] : [],
364
+ );
365
+ if (columnFilters.length > 0) payload.columnFilters = columnFilters;
366
+ }
367
+ if (input.globalFilter && slices.includes("globalFilter")) {
368
+ payload.globalFilter = input.globalFilter;
369
+ }
370
+ if (input.sorting && input.sorting.length > 0 && slices.includes("sorting")) {
371
+ payload.sorting = input.sorting.map(({ id, desc = false }) => ({ id, desc }));
372
+ }
373
+ if (input.pagination && slices.includes("pagination")) {
374
+ payload.pagination = {
375
+ pageIndex: input.pagination.pageIndex,
376
+ pageSize: input.pagination.pageSize,
377
+ };
378
+ }
379
+
380
+ return Object.keys(payload).length === 0 ? undefined : payload;
381
+ }
382
+
383
+ /** Native values in, the grid's filter envelope out: every value a string. */
384
+ function toFilterValue(
385
+ filter: LooseFilterInput,
386
+ type: TMDataGridColumnType | undefined,
387
+ ): TMDataGridFilterValue {
388
+ const { operator, value } = filter;
389
+ if (operator === "isEmpty" || operator === "isNotEmpty") {
390
+ return { operator, value: "" };
391
+ }
392
+ if (operator === "isAnyOf" || operator === "isNoneOf") {
393
+ return { operator, value: (value as ReadonlyArray<unknown>).map(String) };
394
+ }
395
+ if (operator === "between") {
396
+ const [from, to] = value as readonly [unknown, unknown];
397
+ return { operator, value: [toFilterString(from, type), toFilterString(to, type)] };
398
+ }
399
+ return { operator, value: toFilterString(value, type) };
400
+ }
401
+
402
+ function toFilterString(value: unknown, type: TMDataGridColumnType | undefined): string {
403
+ if (value === null || value === undefined) return "";
404
+ if (value instanceof Date) return localIsoDay(value);
405
+ // The date controls write `YYYY-MM-DD`; a full ISO timestamp is cut to it.
406
+ if (type === "date" && typeof value === "string") return value.slice(0, 10);
407
+ return String(value);
408
+ }
409
+
410
+ /** The calendar day in local time. `toISOString()` would give the UTC day. */
411
+ function localIsoDay(date: Date): string {
412
+ const month = String(date.getMonth() + 1).padStart(2, "0");
413
+ const day = String(date.getDate()).padStart(2, "0");
414
+ return `${date.getFullYear()}-${month}-${day}`;
415
+ }
416
+
417
+ /**
418
+ * The grid reads and writes its param as the string `serialize` produced
419
+ * (JSON by default); a router like TanStack Router keeps the parsed object and
420
+ * encodes it itself. Both directions go through JSON, so the grid's "the param
421
+ * is unchanged, skip the write" comparison keeps holding.
422
+ */
423
+ function toUrlStorage(storage: TMDataGridUrlSearchStorage): TMDataGridUrlStorage {
424
+ return {
425
+ get: (name) => {
426
+ const value = storage.get(name);
427
+ if (value === undefined || value === null) return null;
428
+ // A router that left the value as text hands back what the grid wrote.
429
+ return typeof value === "string" ? value : JSON.stringify(value);
430
+ },
431
+ set: (name, value) => {
432
+ storage.set(name, value === null ? undefined : (JSON.parse(value) as TMDataGridUrlPayload));
433
+ },
434
+ };
435
+ }
436
+
437
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
438
+ return typeof value === "object" && value !== null && !Array.isArray(value);
439
+ }
@@ -0,0 +1,46 @@
1
+ import type { TMDataGridOption } from "./columnOptions";
2
+ import type { TMDataGridColumnType } from "./filterOperators";
3
+ import type { TMDataGridLabels } from "./labels";
4
+
5
+ /** The English boolean labels - the defaults `TMDATAGRID_LABELS_EN` takes. */
6
+ export const BOOLEAN_LABELS_EN = {
7
+ booleanTrue: "Yes",
8
+ booleanFalse: "No",
9
+ } as const satisfies Pick<TMDataGridLabels, "booleanTrue" | "booleanFalse">;
10
+
11
+ export type TMDataGridValueTextOptions = {
12
+ /** The column's `meta.type`. A `boolean` column's `"true"` / `"false"` filter strings read as booleans. */
13
+ type?: TMDataGridColumnType;
14
+ /** The column's resolved options: a value with an option reads as its label. */
15
+ options?: ReadonlyArray<TMDataGridOption>;
16
+ /** What `true` and `false` read as. Defaults to English. */
17
+ labels?: Pick<TMDataGridLabels, "booleanTrue" | "booleanFalse">;
18
+ };
19
+
20
+ /**
21
+ * A value as the grid's chrome writes it in words - on a filter pill, in a
22
+ * group row - rather than `String(value)`, which writes `true`, an option's
23
+ * key instead of its label, and a `Date` as an English timestamp.
24
+ */
25
+ export function formatValueText(
26
+ value: unknown,
27
+ { type, options, labels = BOOLEAN_LABELS_EN }: TMDataGridValueTextOptions = {},
28
+ ): string {
29
+ if (Array.isArray(value)) {
30
+ return value
31
+ .map((entry) => formatValueText(entry, { type, options, labels }))
32
+ .join(", ");
33
+ }
34
+ if (value === true || (type === "boolean" && value === "true")) {
35
+ return labels.booleanTrue;
36
+ }
37
+ if (value === false || (type === "boolean" && value === "false")) {
38
+ return labels.booleanFalse;
39
+ }
40
+ if (value instanceof Date) {
41
+ return Number.isNaN(value.getTime()) ? "" : value.toLocaleDateString();
42
+ }
43
+ const text = String(value);
44
+ const option = options?.find((candidate) => candidate.value === text);
45
+ return option?.label ?? text;
46
+ }
package/src/index.ts CHANGED
@@ -262,7 +262,21 @@ export {
262
262
  type TMDataGridPersistKey,
263
263
  type TMDataGridSettingsSlice,
264
264
  type TMDataGridStorageMode,
265
+ type TMDataGridUrlStorage,
265
266
  } from "./core/persistence";
267
+ export {
268
+ createTMDataGridUrlState,
269
+ type TMDataGridColumnInfo,
270
+ type TMDataGridColumnInfoOf,
271
+ type TMDataGridDateInput,
272
+ type TMDataGridTypedColumn,
273
+ type TMDataGridUrlFiltersInput,
274
+ type TMDataGridUrlInput,
275
+ type TMDataGridUrlPayload,
276
+ type TMDataGridUrlSearchStorage,
277
+ type TMDataGridUrlSortableId,
278
+ type TMDataGridUrlState,
279
+ } from "./core/urlState";
266
280
  export type {
267
281
  TMDataGridCellEventArgs,
268
282
  TMDataGridRowContextMenuArgs,