@trackunit/react-table-helpers 1.19.8 → 1.19.9-alpha-58b6b568fca.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/index.cjs.js +130 -0
- package/index.esm.js +132 -4
- package/package.json +17 -15
- package/src/index.d.ts +1 -0
- package/src/useTablePersistence/useTablePersistence.d.ts +84 -0
package/index.cjs.js
CHANGED
|
@@ -11,6 +11,8 @@ var reactTableBaseComponents = require('@trackunit/react-table-base-components')
|
|
|
11
11
|
var customFieldComponents = require('@trackunit/custom-field-components');
|
|
12
12
|
var filtersGraphqlHook = require('@trackunit/filters-graphql-hook');
|
|
13
13
|
var reactTable = require('@trackunit/react-table');
|
|
14
|
+
var dequal = require('dequal');
|
|
15
|
+
var zod = require('zod');
|
|
14
16
|
|
|
15
17
|
var defaultTranslations = {
|
|
16
18
|
"export.table.button": "Export"
|
|
@@ -494,6 +496,132 @@ const useColumnDefinitionsFromCustomFieldDefinition = ({ customFieldDefinitions,
|
|
|
494
496
|
}, [filterBarDefinition, customFieldDefinitions, unitTranslation, unitPreference, language]);
|
|
495
497
|
};
|
|
496
498
|
|
|
499
|
+
const expandedStateSchema = zod.z.union([zod.z.literal(true), zod.z.record(zod.z.string(), zod.z.boolean())]);
|
|
500
|
+
/** Zod schema describing the persisted subset of table state (column order, sizing, visibility, sorting, pinning, and expanded). */
|
|
501
|
+
const tableStateSchema = zod.z.object({
|
|
502
|
+
columnOrder: zod.z.array(zod.z.string()).optional(),
|
|
503
|
+
sorting: zod.z.array(zod.z.object({ id: zod.z.string(), desc: zod.z.boolean() })).optional(),
|
|
504
|
+
columnVisibility: zod.z.record(zod.z.string(), zod.z.boolean()).optional(),
|
|
505
|
+
columnSizing: zod.z.record(zod.z.string(), zod.z.number()).optional(),
|
|
506
|
+
columnPinning: zod.z
|
|
507
|
+
.object({
|
|
508
|
+
left: zod.z.array(zod.z.string()).optional(),
|
|
509
|
+
right: zod.z.array(zod.z.string()).optional(),
|
|
510
|
+
})
|
|
511
|
+
.optional(),
|
|
512
|
+
expanded: expandedStateSchema.optional(),
|
|
513
|
+
});
|
|
514
|
+
/**
|
|
515
|
+
* Type guard that checks whether a string is a recognised sorting property.
|
|
516
|
+
*
|
|
517
|
+
* @param sortingState - Array of valid sort property names.
|
|
518
|
+
* @param value - The value to check.
|
|
519
|
+
* @returns {boolean} `true` when {@link value} matches one of the entries in {@link sortingState}.
|
|
520
|
+
*/
|
|
521
|
+
const isSortByProperty = (sortingState, value) => {
|
|
522
|
+
const sortByPropertyValidated = sortingState.find(validName => validName === value);
|
|
523
|
+
return !!sortByPropertyValidated;
|
|
524
|
+
};
|
|
525
|
+
const MAX_URL_LENGTH = 5000;
|
|
526
|
+
const getStorageKey = (persistenceKey, userId) => `table-${persistenceKey}-${userId}`;
|
|
527
|
+
const safeParse = (raw) => {
|
|
528
|
+
if (!raw) {
|
|
529
|
+
return undefined;
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const parsed = JSON.parse(raw);
|
|
533
|
+
const result = tableStateSchema.safeParse(parsed);
|
|
534
|
+
return result.success ? result.data : undefined;
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
return undefined;
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* Persists and restores table column state (order, sizing, visibility, sorting, pinning, expanded) using
|
|
542
|
+
* URL search parameters and localStorage.
|
|
543
|
+
*
|
|
544
|
+
* On mount the state is loaded from the URL (if present) or from localStorage.
|
|
545
|
+
* Changes are debounced (300 ms) and synced to both the URL and localStorage.
|
|
546
|
+
*
|
|
547
|
+
* URL updates use `window.history.replaceState` directly rather than the application router,
|
|
548
|
+
* so table state changes never interfere with other URL-persisting hooks (e.g. filter bar).
|
|
549
|
+
*
|
|
550
|
+
* @param persistenceKey - Unique key used to store and retrieve the table state.
|
|
551
|
+
* @returns {UseTablePersistenceResult} An object containing:
|
|
552
|
+
* - `onTableStateChange` — callback to invoke when the table state changes.
|
|
553
|
+
* - `initialState` — the previously persisted {@link TableColumnOptions}, or `undefined` if none exists.
|
|
554
|
+
*/
|
|
555
|
+
const useTablePersistence = (persistenceKey) => {
|
|
556
|
+
const { clientSideUserId } = reactCoreHooks.useCurrentUser();
|
|
557
|
+
const { encode, decode } = reactComponents.useCustomEncoding();
|
|
558
|
+
const storageKey = getStorageKey(persistenceKey, clientSideUserId);
|
|
559
|
+
const [initialState] = react.useState(() => {
|
|
560
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
561
|
+
const fromUrl = urlParams.get(persistenceKey);
|
|
562
|
+
if (fromUrl) {
|
|
563
|
+
try {
|
|
564
|
+
const decoded = decode(fromUrl);
|
|
565
|
+
const result = tableStateSchema.safeParse(decoded);
|
|
566
|
+
if (result.success) {
|
|
567
|
+
return result.data;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
// fall through to localStorage
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const fromStorage = localStorage.getItem(storageKey);
|
|
575
|
+
return safeParse(fromStorage);
|
|
576
|
+
});
|
|
577
|
+
const [pendingState, setPendingState] = react.useState(null);
|
|
578
|
+
const debouncedPending = reactComponents.useDebounce(pendingState, { delay: 300 });
|
|
579
|
+
const lastSavedRef = react.useRef(initialState);
|
|
580
|
+
const currentState = react.useMemo(() => {
|
|
581
|
+
if (!debouncedPending && !initialState) {
|
|
582
|
+
return undefined;
|
|
583
|
+
}
|
|
584
|
+
return {
|
|
585
|
+
columnOrder: debouncedPending?.columnOrder ?? initialState?.columnOrder ?? [],
|
|
586
|
+
columnSizing: debouncedPending?.columnSizing ?? initialState?.columnSizing ?? {},
|
|
587
|
+
columnVisibility: debouncedPending?.columnVisibility ?? initialState?.columnVisibility ?? {},
|
|
588
|
+
sorting: debouncedPending?.sorting ?? initialState?.sorting ?? [],
|
|
589
|
+
columnPinning: debouncedPending?.columnPinning ?? initialState?.columnPinning ?? {},
|
|
590
|
+
expanded: debouncedPending?.expanded ?? initialState?.expanded ?? {},
|
|
591
|
+
};
|
|
592
|
+
}, [debouncedPending, initialState]);
|
|
593
|
+
const onTableStateChange = react.useCallback((newTableState) => {
|
|
594
|
+
if (!newTableState) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
setPendingState(newTableState);
|
|
598
|
+
}, []);
|
|
599
|
+
react.useEffect(() => {
|
|
600
|
+
const stateChanged = Boolean(currentState) && !dequal.dequal(lastSavedRef.current, currentState);
|
|
601
|
+
if (!stateChanged || !currentState) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
lastSavedRef.current = currentState;
|
|
605
|
+
localStorage.setItem(storageKey, JSON.stringify(currentState));
|
|
606
|
+
const encoded = encode(currentState);
|
|
607
|
+
void requestAnimationFrame(() => {
|
|
608
|
+
const url = new URL(window.location.href);
|
|
609
|
+
url.searchParams.set(persistenceKey, encoded);
|
|
610
|
+
if (url.href.length <= MAX_URL_LENGTH) {
|
|
611
|
+
window.history.replaceState(window.history.state, "", url.href);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
url.searchParams.delete(persistenceKey);
|
|
615
|
+
window.history.replaceState(window.history.state, "", url.href);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}, [currentState, storageKey, encode, persistenceKey]);
|
|
619
|
+
return react.useMemo(() => ({
|
|
620
|
+
onTableStateChange,
|
|
621
|
+
initialState,
|
|
622
|
+
}), [initialState, onTableStateChange]);
|
|
623
|
+
};
|
|
624
|
+
|
|
497
625
|
/*
|
|
498
626
|
* ----------------------------
|
|
499
627
|
* | SETUP TRANSLATIONS START |
|
|
@@ -506,4 +634,6 @@ setupLibraryTranslations();
|
|
|
506
634
|
exports.ExportTableButton = ExportTableButton;
|
|
507
635
|
exports.customFieldToCell = customFieldToCell;
|
|
508
636
|
exports.customFieldToExportString = customFieldToExportString;
|
|
637
|
+
exports.isSortByProperty = isSortByProperty;
|
|
509
638
|
exports.useColumnDefinitionsFromCustomFieldDefinition = useColumnDefinitionsFromCustomFieldDefinition;
|
|
639
|
+
exports.useTablePersistence = useTablePersistence;
|
package/index.esm.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
|
|
3
|
-
import { Button, ExternalLink } from '@trackunit/react-components';
|
|
4
|
-
import { useExportDataContext } from '@trackunit/react-core-hooks';
|
|
5
|
-
import { useMemo } from 'react';
|
|
3
|
+
import { Button, ExternalLink, useCustomEncoding, useDebounce } from '@trackunit/react-components';
|
|
4
|
+
import { useExportDataContext, useCurrentUser } from '@trackunit/react-core-hooks';
|
|
5
|
+
import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
|
|
6
6
|
import { Temporal } from '@trackunit/date-and-time-utils';
|
|
7
7
|
import { getCustomFieldValueForDisplayInUI } from '@trackunit/iris-app-runtime-core';
|
|
8
8
|
import { TextCell, NumberCell, TagsCell, PlainDateCell, LinkCell, CheckboxCell } from '@trackunit/react-table-base-components';
|
|
9
9
|
import { useUnitTranslation } from '@trackunit/custom-field-components';
|
|
10
10
|
import { CustomFieldPrefix } from '@trackunit/filters-graphql-hook';
|
|
11
11
|
import { createColumnHelper } from '@trackunit/react-table';
|
|
12
|
+
import { dequal } from 'dequal';
|
|
13
|
+
import { z } from 'zod';
|
|
12
14
|
|
|
13
15
|
var defaultTranslations = {
|
|
14
16
|
"export.table.button": "Export"
|
|
@@ -492,6 +494,132 @@ const useColumnDefinitionsFromCustomFieldDefinition = ({ customFieldDefinitions,
|
|
|
492
494
|
}, [filterBarDefinition, customFieldDefinitions, unitTranslation, unitPreference, language]);
|
|
493
495
|
};
|
|
494
496
|
|
|
497
|
+
const expandedStateSchema = z.union([z.literal(true), z.record(z.string(), z.boolean())]);
|
|
498
|
+
/** Zod schema describing the persisted subset of table state (column order, sizing, visibility, sorting, pinning, and expanded). */
|
|
499
|
+
const tableStateSchema = z.object({
|
|
500
|
+
columnOrder: z.array(z.string()).optional(),
|
|
501
|
+
sorting: z.array(z.object({ id: z.string(), desc: z.boolean() })).optional(),
|
|
502
|
+
columnVisibility: z.record(z.string(), z.boolean()).optional(),
|
|
503
|
+
columnSizing: z.record(z.string(), z.number()).optional(),
|
|
504
|
+
columnPinning: z
|
|
505
|
+
.object({
|
|
506
|
+
left: z.array(z.string()).optional(),
|
|
507
|
+
right: z.array(z.string()).optional(),
|
|
508
|
+
})
|
|
509
|
+
.optional(),
|
|
510
|
+
expanded: expandedStateSchema.optional(),
|
|
511
|
+
});
|
|
512
|
+
/**
|
|
513
|
+
* Type guard that checks whether a string is a recognised sorting property.
|
|
514
|
+
*
|
|
515
|
+
* @param sortingState - Array of valid sort property names.
|
|
516
|
+
* @param value - The value to check.
|
|
517
|
+
* @returns {boolean} `true` when {@link value} matches one of the entries in {@link sortingState}.
|
|
518
|
+
*/
|
|
519
|
+
const isSortByProperty = (sortingState, value) => {
|
|
520
|
+
const sortByPropertyValidated = sortingState.find(validName => validName === value);
|
|
521
|
+
return !!sortByPropertyValidated;
|
|
522
|
+
};
|
|
523
|
+
const MAX_URL_LENGTH = 5000;
|
|
524
|
+
const getStorageKey = (persistenceKey, userId) => `table-${persistenceKey}-${userId}`;
|
|
525
|
+
const safeParse = (raw) => {
|
|
526
|
+
if (!raw) {
|
|
527
|
+
return undefined;
|
|
528
|
+
}
|
|
529
|
+
try {
|
|
530
|
+
const parsed = JSON.parse(raw);
|
|
531
|
+
const result = tableStateSchema.safeParse(parsed);
|
|
532
|
+
return result.success ? result.data : undefined;
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
return undefined;
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* Persists and restores table column state (order, sizing, visibility, sorting, pinning, expanded) using
|
|
540
|
+
* URL search parameters and localStorage.
|
|
541
|
+
*
|
|
542
|
+
* On mount the state is loaded from the URL (if present) or from localStorage.
|
|
543
|
+
* Changes are debounced (300 ms) and synced to both the URL and localStorage.
|
|
544
|
+
*
|
|
545
|
+
* URL updates use `window.history.replaceState` directly rather than the application router,
|
|
546
|
+
* so table state changes never interfere with other URL-persisting hooks (e.g. filter bar).
|
|
547
|
+
*
|
|
548
|
+
* @param persistenceKey - Unique key used to store and retrieve the table state.
|
|
549
|
+
* @returns {UseTablePersistenceResult} An object containing:
|
|
550
|
+
* - `onTableStateChange` — callback to invoke when the table state changes.
|
|
551
|
+
* - `initialState` — the previously persisted {@link TableColumnOptions}, or `undefined` if none exists.
|
|
552
|
+
*/
|
|
553
|
+
const useTablePersistence = (persistenceKey) => {
|
|
554
|
+
const { clientSideUserId } = useCurrentUser();
|
|
555
|
+
const { encode, decode } = useCustomEncoding();
|
|
556
|
+
const storageKey = getStorageKey(persistenceKey, clientSideUserId);
|
|
557
|
+
const [initialState] = useState(() => {
|
|
558
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
559
|
+
const fromUrl = urlParams.get(persistenceKey);
|
|
560
|
+
if (fromUrl) {
|
|
561
|
+
try {
|
|
562
|
+
const decoded = decode(fromUrl);
|
|
563
|
+
const result = tableStateSchema.safeParse(decoded);
|
|
564
|
+
if (result.success) {
|
|
565
|
+
return result.data;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
// fall through to localStorage
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const fromStorage = localStorage.getItem(storageKey);
|
|
573
|
+
return safeParse(fromStorage);
|
|
574
|
+
});
|
|
575
|
+
const [pendingState, setPendingState] = useState(null);
|
|
576
|
+
const debouncedPending = useDebounce(pendingState, { delay: 300 });
|
|
577
|
+
const lastSavedRef = useRef(initialState);
|
|
578
|
+
const currentState = useMemo(() => {
|
|
579
|
+
if (!debouncedPending && !initialState) {
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
return {
|
|
583
|
+
columnOrder: debouncedPending?.columnOrder ?? initialState?.columnOrder ?? [],
|
|
584
|
+
columnSizing: debouncedPending?.columnSizing ?? initialState?.columnSizing ?? {},
|
|
585
|
+
columnVisibility: debouncedPending?.columnVisibility ?? initialState?.columnVisibility ?? {},
|
|
586
|
+
sorting: debouncedPending?.sorting ?? initialState?.sorting ?? [],
|
|
587
|
+
columnPinning: debouncedPending?.columnPinning ?? initialState?.columnPinning ?? {},
|
|
588
|
+
expanded: debouncedPending?.expanded ?? initialState?.expanded ?? {},
|
|
589
|
+
};
|
|
590
|
+
}, [debouncedPending, initialState]);
|
|
591
|
+
const onTableStateChange = useCallback((newTableState) => {
|
|
592
|
+
if (!newTableState) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
setPendingState(newTableState);
|
|
596
|
+
}, []);
|
|
597
|
+
useEffect(() => {
|
|
598
|
+
const stateChanged = Boolean(currentState) && !dequal(lastSavedRef.current, currentState);
|
|
599
|
+
if (!stateChanged || !currentState) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
lastSavedRef.current = currentState;
|
|
603
|
+
localStorage.setItem(storageKey, JSON.stringify(currentState));
|
|
604
|
+
const encoded = encode(currentState);
|
|
605
|
+
void requestAnimationFrame(() => {
|
|
606
|
+
const url = new URL(window.location.href);
|
|
607
|
+
url.searchParams.set(persistenceKey, encoded);
|
|
608
|
+
if (url.href.length <= MAX_URL_LENGTH) {
|
|
609
|
+
window.history.replaceState(window.history.state, "", url.href);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
url.searchParams.delete(persistenceKey);
|
|
613
|
+
window.history.replaceState(window.history.state, "", url.href);
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}, [currentState, storageKey, encode, persistenceKey]);
|
|
617
|
+
return useMemo(() => ({
|
|
618
|
+
onTableStateChange,
|
|
619
|
+
initialState,
|
|
620
|
+
}), [initialState, onTableStateChange]);
|
|
621
|
+
};
|
|
622
|
+
|
|
495
623
|
/*
|
|
496
624
|
* ----------------------------
|
|
497
625
|
* | SETUP TRANSLATIONS START |
|
|
@@ -501,4 +629,4 @@ const useColumnDefinitionsFromCustomFieldDefinition = ({ customFieldDefinitions,
|
|
|
501
629
|
*/
|
|
502
630
|
setupLibraryTranslations();
|
|
503
631
|
|
|
504
|
-
export { ExportTableButton, customFieldToCell, customFieldToExportString, useColumnDefinitionsFromCustomFieldDefinition };
|
|
632
|
+
export { ExportTableButton, customFieldToCell, customFieldToExportString, isSortByProperty, useColumnDefinitionsFromCustomFieldDefinition, useTablePersistence };
|
package/package.json
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-table-helpers",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.9-alpha-58b6b568fca.0",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@trackunit/date-and-time-utils": "1.11.
|
|
11
|
-
"@trackunit/filters-filter-bar": "1.18.
|
|
12
|
-
"@trackunit/filters-graphql-hook": "1.21.
|
|
13
|
-
"@trackunit/i18n-library-translation": "1.15.
|
|
14
|
-
"@trackunit/custom-field-components": "1.17.
|
|
15
|
-
"@trackunit/iris-app-runtime-core": "1.14.
|
|
16
|
-
"@trackunit/iris-app-runtime-core-api": "1.13.
|
|
17
|
-
"@trackunit/react-table-base-components": "1.17.
|
|
18
|
-
"@trackunit/react-table": "1.17.
|
|
19
|
-
"@trackunit/react-components": "1.20.
|
|
20
|
-
"@trackunit/react-core-hooks": "1.14.
|
|
21
|
-
"@trackunit/react-graphql-hooks": "1.18.
|
|
22
|
-
"@trackunit/iris-app-api": "1.15.
|
|
10
|
+
"@trackunit/date-and-time-utils": "1.11.81-alpha-58b6b568fca.0",
|
|
11
|
+
"@trackunit/filters-filter-bar": "1.18.8-alpha-58b6b568fca.0",
|
|
12
|
+
"@trackunit/filters-graphql-hook": "1.21.9-alpha-58b6b568fca.0",
|
|
13
|
+
"@trackunit/i18n-library-translation": "1.15.6-alpha-58b6b568fca.0",
|
|
14
|
+
"@trackunit/custom-field-components": "1.17.6-alpha-58b6b568fca.0",
|
|
15
|
+
"@trackunit/iris-app-runtime-core": "1.14.4-alpha-58b6b568fca.0",
|
|
16
|
+
"@trackunit/iris-app-runtime-core-api": "1.13.4-alpha-58b6b568fca.0",
|
|
17
|
+
"@trackunit/react-table-base-components": "1.17.6-alpha-58b6b568fca.0",
|
|
18
|
+
"@trackunit/react-table": "1.17.7-alpha-58b6b568fca.0",
|
|
19
|
+
"@trackunit/react-components": "1.20.6-alpha-58b6b568fca.0",
|
|
20
|
+
"@trackunit/react-core-hooks": "1.14.5-alpha-58b6b568fca.0",
|
|
21
|
+
"@trackunit/react-graphql-hooks": "1.18.6-alpha-58b6b568fca.0",
|
|
22
|
+
"@trackunit/iris-app-api": "1.15.11-alpha-58b6b568fca.0",
|
|
23
23
|
"@graphql-codegen/cli": "^5.0.3",
|
|
24
|
-
"@graphql-typed-document-node/core": "^3.2.0"
|
|
24
|
+
"@graphql-typed-document-node/core": "^3.2.0",
|
|
25
|
+
"dequal": "^2.0.3",
|
|
26
|
+
"zod": "^3.23.8"
|
|
25
27
|
},
|
|
26
28
|
"peerDependencies": {
|
|
27
29
|
"react": "^19.0.0",
|
package/src/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export * from "./useColumnDefinitionsFromCustomFieldDefinition/customFieldToCell
|
|
|
3
3
|
export * from "./useColumnDefinitionsFromCustomFieldDefinition/customFieldToExportString";
|
|
4
4
|
export type { CustomFieldData, DataWithCustomFields } from "./useColumnDefinitionsFromCustomFieldDefinition/types";
|
|
5
5
|
export * from "./useColumnDefinitionsFromCustomFieldDefinition/useColumnDefinitionsFromCustomFieldDefinition";
|
|
6
|
+
export * from "./useTablePersistence/useTablePersistence";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Zod schema describing the persisted subset of table state (column order, sizing, visibility, sorting, pinning, and expanded). */
|
|
3
|
+
declare const tableStateSchema: z.ZodObject<{
|
|
4
|
+
columnOrder: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
5
|
+
sorting: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
desc: z.ZodBoolean;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
id: string;
|
|
10
|
+
desc: boolean;
|
|
11
|
+
}, {
|
|
12
|
+
id: string;
|
|
13
|
+
desc: boolean;
|
|
14
|
+
}>, "many">>;
|
|
15
|
+
columnVisibility: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
16
|
+
columnSizing: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
17
|
+
columnPinning: z.ZodOptional<z.ZodObject<{
|
|
18
|
+
left: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
19
|
+
right: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
left?: string[] | undefined;
|
|
22
|
+
right?: string[] | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
left?: string[] | undefined;
|
|
25
|
+
right?: string[] | undefined;
|
|
26
|
+
}>>;
|
|
27
|
+
expanded: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<true>, z.ZodRecord<z.ZodString, z.ZodBoolean>]>>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
columnOrder?: string[] | undefined;
|
|
30
|
+
sorting?: {
|
|
31
|
+
id: string;
|
|
32
|
+
desc: boolean;
|
|
33
|
+
}[] | undefined;
|
|
34
|
+
columnVisibility?: Record<string, boolean> | undefined;
|
|
35
|
+
columnSizing?: Record<string, number> | undefined;
|
|
36
|
+
columnPinning?: {
|
|
37
|
+
left?: string[] | undefined;
|
|
38
|
+
right?: string[] | undefined;
|
|
39
|
+
} | undefined;
|
|
40
|
+
expanded?: true | Record<string, boolean> | undefined;
|
|
41
|
+
}, {
|
|
42
|
+
columnOrder?: string[] | undefined;
|
|
43
|
+
sorting?: {
|
|
44
|
+
id: string;
|
|
45
|
+
desc: boolean;
|
|
46
|
+
}[] | undefined;
|
|
47
|
+
columnVisibility?: Record<string, boolean> | undefined;
|
|
48
|
+
columnSizing?: Record<string, number> | undefined;
|
|
49
|
+
columnPinning?: {
|
|
50
|
+
left?: string[] | undefined;
|
|
51
|
+
right?: string[] | undefined;
|
|
52
|
+
} | undefined;
|
|
53
|
+
expanded?: true | Record<string, boolean> | undefined;
|
|
54
|
+
}>;
|
|
55
|
+
export type TableColumnOptions = z.infer<typeof tableStateSchema>;
|
|
56
|
+
/**
|
|
57
|
+
* Type guard that checks whether a string is a recognised sorting property.
|
|
58
|
+
*
|
|
59
|
+
* @param sortingState - Array of valid sort property names.
|
|
60
|
+
* @param value - The value to check.
|
|
61
|
+
* @returns {boolean} `true` when {@link value} matches one of the entries in {@link sortingState}.
|
|
62
|
+
*/
|
|
63
|
+
export declare const isSortByProperty: <TSortByType extends string>(sortingState: Array<TSortByType>, value?: string) => value is TSortByType;
|
|
64
|
+
type UseTablePersistenceResult = {
|
|
65
|
+
readonly onTableStateChange: (newTableState: Partial<TableColumnOptions> | null) => void;
|
|
66
|
+
readonly initialState: TableColumnOptions | undefined;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Persists and restores table column state (order, sizing, visibility, sorting, pinning, expanded) using
|
|
70
|
+
* URL search parameters and localStorage.
|
|
71
|
+
*
|
|
72
|
+
* On mount the state is loaded from the URL (if present) or from localStorage.
|
|
73
|
+
* Changes are debounced (300 ms) and synced to both the URL and localStorage.
|
|
74
|
+
*
|
|
75
|
+
* URL updates use `window.history.replaceState` directly rather than the application router,
|
|
76
|
+
* so table state changes never interfere with other URL-persisting hooks (e.g. filter bar).
|
|
77
|
+
*
|
|
78
|
+
* @param persistenceKey - Unique key used to store and retrieve the table state.
|
|
79
|
+
* @returns {UseTablePersistenceResult} An object containing:
|
|
80
|
+
* - `onTableStateChange` — callback to invoke when the table state changes.
|
|
81
|
+
* - `initialState` — the previously persisted {@link TableColumnOptions}, or `undefined` if none exists.
|
|
82
|
+
*/
|
|
83
|
+
export declare const useTablePersistence: (persistenceKey: string) => UseTablePersistenceResult;
|
|
84
|
+
export {};
|