@trackunit/react-table 2.4.2-alpha-3b19e5fe058.0 → 2.5.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/index.cjs.js +53 -19
- package/index.esm.js +54 -20
- package/package.json +10 -10
- package/src/hooks/useTablePersistence/useTablePersistence.d.ts +6 -0
- package/src/useTable.d.ts +8 -1
package/index.cjs.js
CHANGED
|
@@ -568,6 +568,12 @@ const validateTableState = (raw) => {
|
|
|
568
568
|
* the URL, such as `expanded`, still survive reloads. Changes are
|
|
569
569
|
* debounced (300 ms) and synced to both the hash and localStorage.
|
|
570
570
|
*
|
|
571
|
+
* `onTableStateChange` takes a *partial* state and merges it into whatever
|
|
572
|
+
* was reported earlier in the session, so a consumer can report a single
|
|
573
|
+
* slice (e.g. only `expanded`) without the omitted fields falling back to
|
|
574
|
+
* their mount-time values. A field reported empty is dropped from the stored
|
|
575
|
+
* state, and once every field is empty the entry is removed altogether.
|
|
576
|
+
*
|
|
571
577
|
* @param persistenceKey - Unique key used to store and retrieve the table state.
|
|
572
578
|
* Must be camelCase and at most 15 characters (e.g. "userTable", "assetList").
|
|
573
579
|
* @returns {UseTablePersistenceResult} An object containing:
|
|
@@ -583,7 +589,7 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
583
589
|
const { clientSideUserId } = reactCoreHooks.useCurrentUser();
|
|
584
590
|
const localStorageEnabled = clientSideUserId !== undefined;
|
|
585
591
|
const { toUrlValue, fromUrlValue } = useCompactTableUrl();
|
|
586
|
-
const { initialState, persistState } = reactComponents.usePersistedState({
|
|
592
|
+
const { initialState, persistState, clearState } = reactComponents.usePersistedState({
|
|
587
593
|
key: `${persistenceKey}Tp`,
|
|
588
594
|
validate: validateTableState,
|
|
589
595
|
toUrlValue,
|
|
@@ -602,6 +608,7 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
602
608
|
});
|
|
603
609
|
const [pendingState, setPendingState] = react.useState(null);
|
|
604
610
|
const debouncedPending = reactComponents.useDebounce(pendingState, { delay: 300 });
|
|
611
|
+
const hasReportedChange = debouncedPending !== null;
|
|
605
612
|
const currentState = react.useMemo(() => {
|
|
606
613
|
if (!debouncedPending && !initialState) {
|
|
607
614
|
return undefined;
|
|
@@ -616,10 +623,10 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
616
623
|
if (columnOrder && columnOrder.length > 0) {
|
|
617
624
|
state.columnOrder = columnOrder;
|
|
618
625
|
}
|
|
619
|
-
if (columnSizing &&
|
|
626
|
+
if (columnSizing && sharedUtils.objectKeys(columnSizing).length > 0) {
|
|
620
627
|
state.columnSizing = columnSizing;
|
|
621
628
|
}
|
|
622
|
-
if (columnVisibility &&
|
|
629
|
+
if (columnVisibility && sharedUtils.objectKeys(columnVisibility).length > 0) {
|
|
623
630
|
state.columnVisibility = columnVisibility;
|
|
624
631
|
}
|
|
625
632
|
if (sorting && sorting.length > 0) {
|
|
@@ -628,23 +635,27 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
628
635
|
if (columnPinning && ((columnPinning.left?.length ?? 0) > 0 || (columnPinning.right?.length ?? 0) > 0)) {
|
|
629
636
|
state.columnPinning = columnPinning;
|
|
630
637
|
}
|
|
631
|
-
if (expanded === true || (typeof expanded === "object" &&
|
|
638
|
+
if (expanded === true || (typeof expanded === "object" && sharedUtils.objectKeys(expanded).length > 0)) {
|
|
632
639
|
state.expanded = expanded;
|
|
633
640
|
}
|
|
634
|
-
return
|
|
635
|
-
}, [debouncedPending, initialState]);
|
|
641
|
+
return sharedUtils.objectKeys(state).length > 0 || hasReportedChange ? state : undefined;
|
|
642
|
+
}, [debouncedPending, hasReportedChange, initialState]);
|
|
636
643
|
const onTableStateChange = react.useCallback((newTableState) => {
|
|
637
644
|
if (!newTableState) {
|
|
638
645
|
return;
|
|
639
646
|
}
|
|
640
|
-
setPendingState(newTableState);
|
|
647
|
+
setPendingState(previousState => ({ ...previousState, ...newTableState }));
|
|
641
648
|
}, []);
|
|
642
649
|
react.useEffect(() => {
|
|
643
650
|
if (!currentState) {
|
|
644
651
|
return;
|
|
645
652
|
}
|
|
653
|
+
if (sharedUtils.objectKeys(currentState).length === 0) {
|
|
654
|
+
clearState();
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
646
657
|
persistState(currentState);
|
|
647
|
-
}, [currentState, persistState]);
|
|
658
|
+
}, [clearState, currentState, persistState]);
|
|
648
659
|
return react.useMemo(() => ({
|
|
649
660
|
onTableStateChange,
|
|
650
661
|
initialState,
|
|
@@ -1708,6 +1719,13 @@ const computeMergedPinning = (initialPinning, columnConfigPinning) => {
|
|
|
1708
1719
|
* - Do **not** call `useReactTable` directly — `useTable` adds required column-state
|
|
1709
1720
|
* orchestration that the raw hook does not provide.
|
|
1710
1721
|
*
|
|
1722
|
+
* Row expansion is managed like the column state: it starts from `initialState.expanded`, and
|
|
1723
|
+
* expand/collapse changes are reported to `onTableStateChange` so they can be persisted — unless
|
|
1724
|
+
* the consumer passes `state.expanded`, in which case it owns what is worth storing.
|
|
1725
|
+
*
|
|
1726
|
+
* `onTableStateChange` is called with the field that changed, not the whole state, so anything
|
|
1727
|
+
* storing it has to merge each report into what it already holds. `useTablePersistence` does.
|
|
1728
|
+
*
|
|
1711
1729
|
* @template TData - The type representing the data model associated with the table.
|
|
1712
1730
|
* @param {TableOptionsProps<TData>} props - The props object containing configuration options.
|
|
1713
1731
|
* @returns {object} An object containing the React Table instance and associated state management functions.
|
|
@@ -1732,7 +1750,7 @@ const computeMergedPinning = (initialPinning, columnConfigPinning) => {
|
|
|
1732
1750
|
* data,
|
|
1733
1751
|
* columns,
|
|
1734
1752
|
* initialState: { sorting: [{ id: "name", desc: false }] },
|
|
1735
|
-
* onTableStateChange:
|
|
1753
|
+
* onTableStateChange: changedField => setPreferences(previous => ({ ...previous, ...changedField })),
|
|
1736
1754
|
* });
|
|
1737
1755
|
*
|
|
1738
1756
|
* return <Table table={table} />;
|
|
@@ -1828,20 +1846,27 @@ const useTable = ({ onTableStateChange, initialState, columns, ...reactTableProp
|
|
|
1828
1846
|
const columnPinning = react.useMemo(() => userEdits.columnPinning ?? computeMergedPinning(initialState?.columnPinning, stableUpdatedInitialColumnPinning), [userEdits.columnPinning, initialState?.columnPinning, stableUpdatedInitialColumnPinning]);
|
|
1829
1847
|
const sorting = react.useMemo(() => userEdits.sorting ?? initialState?.sorting ?? [], [userEdits.sorting, initialState?.sorting]);
|
|
1830
1848
|
const columnSizing = react.useMemo(() => userEdits.columnSizing ?? initialState?.columnSizing ?? {}, [userEdits.columnSizing, initialState?.columnSizing]);
|
|
1831
|
-
const
|
|
1849
|
+
const expanded = react.useMemo(() => userEdits.expanded ?? initialState?.expanded ?? {}, [userEdits.expanded, initialState?.expanded]);
|
|
1850
|
+
const baseState = react.useMemo(() => ({ columnVisibility, columnOrder, sorting, columnSizing, columnPinning, expanded }), [columnVisibility, columnOrder, sorting, columnSizing, columnPinning, expanded]);
|
|
1832
1851
|
const state = react.useMemo(() => ({ ...baseState, ...reactTableProps.state }), [baseState, reactTableProps.state]);
|
|
1833
1852
|
// A ref exposes the latest derived state to the change callbacks without making them depend on
|
|
1834
|
-
// it (callbacks run on user events, outside render). Every
|
|
1853
|
+
// it (callbacks run on user events, outside render). Every managed key on `state` is always
|
|
1835
1854
|
// defined — it originates from `baseState` — so the callbacks read it directly.
|
|
1836
1855
|
const stateRef = react.useRef(state);
|
|
1837
1856
|
stateRef.current = state;
|
|
1838
|
-
//
|
|
1839
|
-
//
|
|
1857
|
+
// Reports only the field that changed — `useTablePersistence` merges partial reports — so a
|
|
1858
|
+
// change can never store another field's unsettled or transient value. Column sizes matching
|
|
1859
|
+
// their default are dropped so only meaningful overrides are stored.
|
|
1840
1860
|
const persistTableState = react.useCallback((nextState) => {
|
|
1841
1861
|
if (!onTableStateChange) {
|
|
1842
1862
|
return;
|
|
1843
1863
|
}
|
|
1844
|
-
const
|
|
1864
|
+
const nextColumnSizing = nextState.columnSizing;
|
|
1865
|
+
if (!nextColumnSizing) {
|
|
1866
|
+
onTableStateChange(nextState);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
const columnSizingToPersist = Object.fromEntries(sharedUtils.objectEntries(nextColumnSizing).filter(([columnId, size]) => defaultColumnSizingById[columnId] !== size));
|
|
1845
1870
|
onTableStateChange({ ...nextState, columnSizing: columnSizingToPersist });
|
|
1846
1871
|
}, [onTableStateChange, defaultColumnSizingById]);
|
|
1847
1872
|
const table = reactTable.useReactTable({
|
|
@@ -1855,37 +1880,46 @@ const useTable = ({ onTableStateChange, initialState, columns, ...reactTableProp
|
|
|
1855
1880
|
const current = stateRef.current.columnVisibility;
|
|
1856
1881
|
const next = typeof value === "function" ? value(current) : value;
|
|
1857
1882
|
setUserEdits(prev => ({ ...prev, columnVisibility: next }));
|
|
1858
|
-
persistTableState({
|
|
1883
|
+
persistTableState({ columnVisibility: next });
|
|
1859
1884
|
reactTableProps.onColumnVisibilityChange?.(value);
|
|
1860
1885
|
},
|
|
1861
1886
|
onColumnSizingChange: value => {
|
|
1862
1887
|
const current = stateRef.current.columnSizing;
|
|
1863
1888
|
const next = typeof value === "function" ? value(current) : value;
|
|
1864
1889
|
setUserEdits(prev => ({ ...prev, columnSizing: next }));
|
|
1865
|
-
persistTableState({
|
|
1890
|
+
persistTableState({ columnSizing: next });
|
|
1866
1891
|
reactTableProps.onColumnSizingChange?.(value);
|
|
1867
1892
|
},
|
|
1868
1893
|
onColumnOrderChange: value => {
|
|
1869
1894
|
const current = stateRef.current.columnOrder;
|
|
1870
1895
|
const next = typeof value === "function" ? value(current) : value;
|
|
1871
1896
|
setUserEdits(prev => ({ ...prev, columnOrder: next }));
|
|
1872
|
-
persistTableState({
|
|
1897
|
+
persistTableState({ columnOrder: next });
|
|
1873
1898
|
reactTableProps.onColumnOrderChange?.(value);
|
|
1874
1899
|
},
|
|
1875
1900
|
onSortingChange: value => {
|
|
1876
1901
|
const current = stateRef.current.sorting;
|
|
1877
1902
|
const next = typeof value === "function" ? value(current) : value;
|
|
1878
1903
|
setUserEdits(prev => ({ ...prev, sorting: next }));
|
|
1879
|
-
persistTableState({
|
|
1904
|
+
persistTableState({ sorting: next });
|
|
1880
1905
|
reactTableProps.onSortingChange?.(value);
|
|
1881
1906
|
},
|
|
1882
1907
|
onColumnPinningChange: value => {
|
|
1883
1908
|
const current = stateRef.current.columnPinning;
|
|
1884
1909
|
const next = typeof value === "function" ? value(current) : value;
|
|
1885
1910
|
setUserEdits(prev => ({ ...prev, columnPinning: next }));
|
|
1886
|
-
persistTableState({
|
|
1911
|
+
persistTableState({ columnPinning: next });
|
|
1887
1912
|
reactTableProps.onColumnPinningChange?.(value);
|
|
1888
1913
|
},
|
|
1914
|
+
onExpandedChange: value => {
|
|
1915
|
+
const current = stateRef.current.expanded;
|
|
1916
|
+
const next = typeof value === "function" ? value(current) : value;
|
|
1917
|
+
setUserEdits(prev => ({ ...prev, expanded: next }));
|
|
1918
|
+
if (reactTableProps.state?.expanded === undefined) {
|
|
1919
|
+
persistTableState({ expanded: next });
|
|
1920
|
+
}
|
|
1921
|
+
reactTableProps.onExpandedChange?.(value);
|
|
1922
|
+
},
|
|
1889
1923
|
columns,
|
|
1890
1924
|
state,
|
|
1891
1925
|
});
|
package/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { registerTranslations, useNamespaceTranslation, NamespaceTrans } from '@trackunit/i18n-library-translation';
|
|
3
3
|
import { Icon, MenuItem, Button, Tooltip, useOverflowItems, MoreMenu, MenuList, Spacer, usePersistedState, useDebounce, Text, cvaInteractableItem, Popover, PopoverTrigger, IconButton, PopoverContent, useBidirectionalScroll, noPagination, Card, Spinner, EmptyState } from '@trackunit/react-components';
|
|
4
|
-
import { objectValues, VISIBLE_ONLY_COLUMN_VISIBILITY_KEY,
|
|
4
|
+
import { objectValues, VISIBLE_ONLY_COLUMN_VISIBILITY_KEY, objectKeys, nonNullable, objectEntries } from '@trackunit/shared-utils';
|
|
5
5
|
import { useMemo, Children, cloneElement, useCallback, useState, useEffect, useRef, createElement } from 'react';
|
|
6
6
|
import { cvaMerge } from '@trackunit/css-class-variance-utilities';
|
|
7
7
|
import { Link } from '@tanstack/react-router';
|
|
@@ -567,6 +567,12 @@ const validateTableState = (raw) => {
|
|
|
567
567
|
* the URL, such as `expanded`, still survive reloads. Changes are
|
|
568
568
|
* debounced (300 ms) and synced to both the hash and localStorage.
|
|
569
569
|
*
|
|
570
|
+
* `onTableStateChange` takes a *partial* state and merges it into whatever
|
|
571
|
+
* was reported earlier in the session, so a consumer can report a single
|
|
572
|
+
* slice (e.g. only `expanded`) without the omitted fields falling back to
|
|
573
|
+
* their mount-time values. A field reported empty is dropped from the stored
|
|
574
|
+
* state, and once every field is empty the entry is removed altogether.
|
|
575
|
+
*
|
|
570
576
|
* @param persistenceKey - Unique key used to store and retrieve the table state.
|
|
571
577
|
* Must be camelCase and at most 15 characters (e.g. "userTable", "assetList").
|
|
572
578
|
* @returns {UseTablePersistenceResult} An object containing:
|
|
@@ -582,7 +588,7 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
582
588
|
const { clientSideUserId } = useCurrentUser();
|
|
583
589
|
const localStorageEnabled = clientSideUserId !== undefined;
|
|
584
590
|
const { toUrlValue, fromUrlValue } = useCompactTableUrl();
|
|
585
|
-
const { initialState, persistState } = usePersistedState({
|
|
591
|
+
const { initialState, persistState, clearState } = usePersistedState({
|
|
586
592
|
key: `${persistenceKey}Tp`,
|
|
587
593
|
validate: validateTableState,
|
|
588
594
|
toUrlValue,
|
|
@@ -601,6 +607,7 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
601
607
|
});
|
|
602
608
|
const [pendingState, setPendingState] = useState(null);
|
|
603
609
|
const debouncedPending = useDebounce(pendingState, { delay: 300 });
|
|
610
|
+
const hasReportedChange = debouncedPending !== null;
|
|
604
611
|
const currentState = useMemo(() => {
|
|
605
612
|
if (!debouncedPending && !initialState) {
|
|
606
613
|
return undefined;
|
|
@@ -615,10 +622,10 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
615
622
|
if (columnOrder && columnOrder.length > 0) {
|
|
616
623
|
state.columnOrder = columnOrder;
|
|
617
624
|
}
|
|
618
|
-
if (columnSizing &&
|
|
625
|
+
if (columnSizing && objectKeys(columnSizing).length > 0) {
|
|
619
626
|
state.columnSizing = columnSizing;
|
|
620
627
|
}
|
|
621
|
-
if (columnVisibility &&
|
|
628
|
+
if (columnVisibility && objectKeys(columnVisibility).length > 0) {
|
|
622
629
|
state.columnVisibility = columnVisibility;
|
|
623
630
|
}
|
|
624
631
|
if (sorting && sorting.length > 0) {
|
|
@@ -627,23 +634,27 @@ const useTablePersistence = (persistenceKey) => {
|
|
|
627
634
|
if (columnPinning && ((columnPinning.left?.length ?? 0) > 0 || (columnPinning.right?.length ?? 0) > 0)) {
|
|
628
635
|
state.columnPinning = columnPinning;
|
|
629
636
|
}
|
|
630
|
-
if (expanded === true || (typeof expanded === "object" &&
|
|
637
|
+
if (expanded === true || (typeof expanded === "object" && objectKeys(expanded).length > 0)) {
|
|
631
638
|
state.expanded = expanded;
|
|
632
639
|
}
|
|
633
|
-
return
|
|
634
|
-
}, [debouncedPending, initialState]);
|
|
640
|
+
return objectKeys(state).length > 0 || hasReportedChange ? state : undefined;
|
|
641
|
+
}, [debouncedPending, hasReportedChange, initialState]);
|
|
635
642
|
const onTableStateChange = useCallback((newTableState) => {
|
|
636
643
|
if (!newTableState) {
|
|
637
644
|
return;
|
|
638
645
|
}
|
|
639
|
-
setPendingState(newTableState);
|
|
646
|
+
setPendingState(previousState => ({ ...previousState, ...newTableState }));
|
|
640
647
|
}, []);
|
|
641
648
|
useEffect(() => {
|
|
642
649
|
if (!currentState) {
|
|
643
650
|
return;
|
|
644
651
|
}
|
|
652
|
+
if (objectKeys(currentState).length === 0) {
|
|
653
|
+
clearState();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
645
656
|
persistState(currentState);
|
|
646
|
-
}, [currentState, persistState]);
|
|
657
|
+
}, [clearState, currentState, persistState]);
|
|
647
658
|
return useMemo(() => ({
|
|
648
659
|
onTableStateChange,
|
|
649
660
|
initialState,
|
|
@@ -1707,6 +1718,13 @@ const computeMergedPinning = (initialPinning, columnConfigPinning) => {
|
|
|
1707
1718
|
* - Do **not** call `useReactTable` directly — `useTable` adds required column-state
|
|
1708
1719
|
* orchestration that the raw hook does not provide.
|
|
1709
1720
|
*
|
|
1721
|
+
* Row expansion is managed like the column state: it starts from `initialState.expanded`, and
|
|
1722
|
+
* expand/collapse changes are reported to `onTableStateChange` so they can be persisted — unless
|
|
1723
|
+
* the consumer passes `state.expanded`, in which case it owns what is worth storing.
|
|
1724
|
+
*
|
|
1725
|
+
* `onTableStateChange` is called with the field that changed, not the whole state, so anything
|
|
1726
|
+
* storing it has to merge each report into what it already holds. `useTablePersistence` does.
|
|
1727
|
+
*
|
|
1710
1728
|
* @template TData - The type representing the data model associated with the table.
|
|
1711
1729
|
* @param {TableOptionsProps<TData>} props - The props object containing configuration options.
|
|
1712
1730
|
* @returns {object} An object containing the React Table instance and associated state management functions.
|
|
@@ -1731,7 +1749,7 @@ const computeMergedPinning = (initialPinning, columnConfigPinning) => {
|
|
|
1731
1749
|
* data,
|
|
1732
1750
|
* columns,
|
|
1733
1751
|
* initialState: { sorting: [{ id: "name", desc: false }] },
|
|
1734
|
-
* onTableStateChange:
|
|
1752
|
+
* onTableStateChange: changedField => setPreferences(previous => ({ ...previous, ...changedField })),
|
|
1735
1753
|
* });
|
|
1736
1754
|
*
|
|
1737
1755
|
* return <Table table={table} />;
|
|
@@ -1827,20 +1845,27 @@ const useTable = ({ onTableStateChange, initialState, columns, ...reactTableProp
|
|
|
1827
1845
|
const columnPinning = useMemo(() => userEdits.columnPinning ?? computeMergedPinning(initialState?.columnPinning, stableUpdatedInitialColumnPinning), [userEdits.columnPinning, initialState?.columnPinning, stableUpdatedInitialColumnPinning]);
|
|
1828
1846
|
const sorting = useMemo(() => userEdits.sorting ?? initialState?.sorting ?? [], [userEdits.sorting, initialState?.sorting]);
|
|
1829
1847
|
const columnSizing = useMemo(() => userEdits.columnSizing ?? initialState?.columnSizing ?? {}, [userEdits.columnSizing, initialState?.columnSizing]);
|
|
1830
|
-
const
|
|
1848
|
+
const expanded = useMemo(() => userEdits.expanded ?? initialState?.expanded ?? {}, [userEdits.expanded, initialState?.expanded]);
|
|
1849
|
+
const baseState = useMemo(() => ({ columnVisibility, columnOrder, sorting, columnSizing, columnPinning, expanded }), [columnVisibility, columnOrder, sorting, columnSizing, columnPinning, expanded]);
|
|
1831
1850
|
const state = useMemo(() => ({ ...baseState, ...reactTableProps.state }), [baseState, reactTableProps.state]);
|
|
1832
1851
|
// A ref exposes the latest derived state to the change callbacks without making them depend on
|
|
1833
|
-
// it (callbacks run on user events, outside render). Every
|
|
1852
|
+
// it (callbacks run on user events, outside render). Every managed key on `state` is always
|
|
1834
1853
|
// defined — it originates from `baseState` — so the callbacks read it directly.
|
|
1835
1854
|
const stateRef = useRef(state);
|
|
1836
1855
|
stateRef.current = state;
|
|
1837
|
-
//
|
|
1838
|
-
//
|
|
1856
|
+
// Reports only the field that changed — `useTablePersistence` merges partial reports — so a
|
|
1857
|
+
// change can never store another field's unsettled or transient value. Column sizes matching
|
|
1858
|
+
// their default are dropped so only meaningful overrides are stored.
|
|
1839
1859
|
const persistTableState = useCallback((nextState) => {
|
|
1840
1860
|
if (!onTableStateChange) {
|
|
1841
1861
|
return;
|
|
1842
1862
|
}
|
|
1843
|
-
const
|
|
1863
|
+
const nextColumnSizing = nextState.columnSizing;
|
|
1864
|
+
if (!nextColumnSizing) {
|
|
1865
|
+
onTableStateChange(nextState);
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
const columnSizingToPersist = Object.fromEntries(objectEntries(nextColumnSizing).filter(([columnId, size]) => defaultColumnSizingById[columnId] !== size));
|
|
1844
1869
|
onTableStateChange({ ...nextState, columnSizing: columnSizingToPersist });
|
|
1845
1870
|
}, [onTableStateChange, defaultColumnSizingById]);
|
|
1846
1871
|
const table = useReactTable({
|
|
@@ -1854,37 +1879,46 @@ const useTable = ({ onTableStateChange, initialState, columns, ...reactTableProp
|
|
|
1854
1879
|
const current = stateRef.current.columnVisibility;
|
|
1855
1880
|
const next = typeof value === "function" ? value(current) : value;
|
|
1856
1881
|
setUserEdits(prev => ({ ...prev, columnVisibility: next }));
|
|
1857
|
-
persistTableState({
|
|
1882
|
+
persistTableState({ columnVisibility: next });
|
|
1858
1883
|
reactTableProps.onColumnVisibilityChange?.(value);
|
|
1859
1884
|
},
|
|
1860
1885
|
onColumnSizingChange: value => {
|
|
1861
1886
|
const current = stateRef.current.columnSizing;
|
|
1862
1887
|
const next = typeof value === "function" ? value(current) : value;
|
|
1863
1888
|
setUserEdits(prev => ({ ...prev, columnSizing: next }));
|
|
1864
|
-
persistTableState({
|
|
1889
|
+
persistTableState({ columnSizing: next });
|
|
1865
1890
|
reactTableProps.onColumnSizingChange?.(value);
|
|
1866
1891
|
},
|
|
1867
1892
|
onColumnOrderChange: value => {
|
|
1868
1893
|
const current = stateRef.current.columnOrder;
|
|
1869
1894
|
const next = typeof value === "function" ? value(current) : value;
|
|
1870
1895
|
setUserEdits(prev => ({ ...prev, columnOrder: next }));
|
|
1871
|
-
persistTableState({
|
|
1896
|
+
persistTableState({ columnOrder: next });
|
|
1872
1897
|
reactTableProps.onColumnOrderChange?.(value);
|
|
1873
1898
|
},
|
|
1874
1899
|
onSortingChange: value => {
|
|
1875
1900
|
const current = stateRef.current.sorting;
|
|
1876
1901
|
const next = typeof value === "function" ? value(current) : value;
|
|
1877
1902
|
setUserEdits(prev => ({ ...prev, sorting: next }));
|
|
1878
|
-
persistTableState({
|
|
1903
|
+
persistTableState({ sorting: next });
|
|
1879
1904
|
reactTableProps.onSortingChange?.(value);
|
|
1880
1905
|
},
|
|
1881
1906
|
onColumnPinningChange: value => {
|
|
1882
1907
|
const current = stateRef.current.columnPinning;
|
|
1883
1908
|
const next = typeof value === "function" ? value(current) : value;
|
|
1884
1909
|
setUserEdits(prev => ({ ...prev, columnPinning: next }));
|
|
1885
|
-
persistTableState({
|
|
1910
|
+
persistTableState({ columnPinning: next });
|
|
1886
1911
|
reactTableProps.onColumnPinningChange?.(value);
|
|
1887
1912
|
},
|
|
1913
|
+
onExpandedChange: value => {
|
|
1914
|
+
const current = stateRef.current.expanded;
|
|
1915
|
+
const next = typeof value === "function" ? value(current) : value;
|
|
1916
|
+
setUserEdits(prev => ({ ...prev, expanded: next }));
|
|
1917
|
+
if (reactTableProps.state?.expanded === undefined) {
|
|
1918
|
+
persistTableState({ expanded: next });
|
|
1919
|
+
}
|
|
1920
|
+
reactTableProps.onExpandedChange?.(value);
|
|
1921
|
+
},
|
|
1888
1922
|
columns,
|
|
1889
1923
|
state,
|
|
1890
1924
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-table",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
"react-dnd": "16.0.1",
|
|
12
12
|
"react-dnd-html5-backend": "16.0.1",
|
|
13
13
|
"tailwind-merge": "^2.0.0",
|
|
14
|
-
"@trackunit/react-components": "2.5.
|
|
15
|
-
"@trackunit/react-core-hooks": "1.20.1
|
|
16
|
-
"@trackunit/shared-utils": "1.16.1
|
|
17
|
-
"@trackunit/css-class-variance-utilities": "1.14.1
|
|
18
|
-
"@trackunit/ui-icons": "1.14.1
|
|
19
|
-
"@trackunit/react-table-base-components": "2.4.
|
|
20
|
-
"@trackunit/react-form-components": "2.4.
|
|
21
|
-
"@trackunit/i18n-library-translation": "2.3.1
|
|
22
|
-
"@trackunit/iris-app-runtime-core-api": "1.17.1
|
|
14
|
+
"@trackunit/react-components": "2.5.4",
|
|
15
|
+
"@trackunit/react-core-hooks": "1.20.1",
|
|
16
|
+
"@trackunit/shared-utils": "1.16.1",
|
|
17
|
+
"@trackunit/css-class-variance-utilities": "1.14.1",
|
|
18
|
+
"@trackunit/ui-icons": "1.14.1",
|
|
19
|
+
"@trackunit/react-table-base-components": "2.4.4",
|
|
20
|
+
"@trackunit/react-form-components": "2.4.4",
|
|
21
|
+
"@trackunit/i18n-library-translation": "2.3.1",
|
|
22
|
+
"@trackunit/iris-app-runtime-core-api": "1.17.1",
|
|
23
23
|
"zod": "^3.25.76"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
@@ -84,6 +84,12 @@ type UseTablePersistenceResult = {
|
|
|
84
84
|
* the URL, such as `expanded`, still survive reloads. Changes are
|
|
85
85
|
* debounced (300 ms) and synced to both the hash and localStorage.
|
|
86
86
|
*
|
|
87
|
+
* `onTableStateChange` takes a *partial* state and merges it into whatever
|
|
88
|
+
* was reported earlier in the session, so a consumer can report a single
|
|
89
|
+
* slice (e.g. only `expanded`) without the omitted fields falling back to
|
|
90
|
+
* their mount-time values. A field reported empty is dropped from the stored
|
|
91
|
+
* state, and once every field is empty the entry is removed altogether.
|
|
92
|
+
*
|
|
87
93
|
* @param persistenceKey - Unique key used to store and retrieve the table state.
|
|
88
94
|
* Must be camelCase and at most 15 characters (e.g. "userTable", "assetList").
|
|
89
95
|
* @returns {UseTablePersistenceResult} An object containing:
|
package/src/useTable.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export interface TableOptionsProps<TData extends object> extends Omit<TableOptio
|
|
|
16
16
|
* - Do **not** call `useReactTable` directly — `useTable` adds required column-state
|
|
17
17
|
* orchestration that the raw hook does not provide.
|
|
18
18
|
*
|
|
19
|
+
* Row expansion is managed like the column state: it starts from `initialState.expanded`, and
|
|
20
|
+
* expand/collapse changes are reported to `onTableStateChange` so they can be persisted — unless
|
|
21
|
+
* the consumer passes `state.expanded`, in which case it owns what is worth storing.
|
|
22
|
+
*
|
|
23
|
+
* `onTableStateChange` is called with the field that changed, not the whole state, so anything
|
|
24
|
+
* storing it has to merge each report into what it already holds. `useTablePersistence` does.
|
|
25
|
+
*
|
|
19
26
|
* @template TData - The type representing the data model associated with the table.
|
|
20
27
|
* @param {TableOptionsProps<TData>} props - The props object containing configuration options.
|
|
21
28
|
* @returns {object} An object containing the React Table instance and associated state management functions.
|
|
@@ -40,7 +47,7 @@ export interface TableOptionsProps<TData extends object> extends Omit<TableOptio
|
|
|
40
47
|
* data,
|
|
41
48
|
* columns,
|
|
42
49
|
* initialState: { sorting: [{ id: "name", desc: false }] },
|
|
43
|
-
* onTableStateChange:
|
|
50
|
+
* onTableStateChange: changedField => setPreferences(previous => ({ ...previous, ...changedField })),
|
|
44
51
|
* });
|
|
45
52
|
*
|
|
46
53
|
* return <Table table={table} />;
|