@crispy-seed/data-table 0.10.4 → 0.10.5
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.cjs +138 -17
- package/dist/index.js +138 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1696,24 +1696,33 @@ function useDataTableSort(props) {
|
|
|
1696
1696
|
// already passes its "cleared" shape through unconditionally; this now matches.
|
|
1697
1697
|
onChange: onSortChange
|
|
1698
1698
|
});
|
|
1699
|
+
// 3-state cycle (DATATABLE-ATTIO-SPEC.md: "asc/desc/none cycle") -- clicking a
|
|
1700
|
+
// DIFFERENT column always starts it fresh at asc, discarding whatever the previous
|
|
1701
|
+
// column's direction was (this stays a SINGLE-column secondary affordance, never a
|
|
1702
|
+
// second multi-sort list of its own). Clicking the SAME column steps asc -> desc ->
|
|
1703
|
+
// undefined ("none", i.e. this column drops out of the sort entirely) -> asc again.
|
|
1704
|
+
// Before this, the cycle wrapped desc -> asc forever, with no click-driven way back
|
|
1705
|
+
// to unsorted at all (a reported gap: "sort asc/desc has no option for no sort on
|
|
1706
|
+
// columns") -- `clearSort` below was the ONLY way there, now it's one of two.
|
|
1699
1707
|
const toggleSort = React.useCallback((key)=>{
|
|
1700
1708
|
setSort((current)=>{
|
|
1701
1709
|
if (!current || current.key !== key) return {
|
|
1702
1710
|
key,
|
|
1703
1711
|
dir: "asc"
|
|
1704
1712
|
};
|
|
1705
|
-
return {
|
|
1713
|
+
if (current.dir === "asc") return {
|
|
1706
1714
|
key,
|
|
1707
|
-
dir:
|
|
1715
|
+
dir: "desc"
|
|
1708
1716
|
};
|
|
1717
|
+
return undefined;
|
|
1709
1718
|
});
|
|
1710
1719
|
}, [
|
|
1711
1720
|
setSort
|
|
1712
1721
|
]);
|
|
1713
|
-
/** Attio/GRC's "Remove sort" --
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1722
|
+
/** Attio/GRC's "Remove sort" -- a second way back to no sort at all (the header
|
|
1723
|
+
* click cycle above reaches it too, on a column's third click), exposed for a
|
|
1724
|
+
* column's own header menu (`column-menu.tsx`'s `ColumnMenu`) to offer as a
|
|
1725
|
+
* "Clear sort" item when that column is the sorted one, without three clicks. */ const clearSort = React.useCallback(()=>setSort(undefined), [
|
|
1717
1726
|
setSort
|
|
1718
1727
|
]);
|
|
1719
1728
|
const getHeaderCellProps = React.useCallback((columnKey, sortable)=>{
|
|
@@ -1746,7 +1755,12 @@ function useDataTableSort(props) {
|
|
|
1746
1755
|
sort,
|
|
1747
1756
|
toggleSort,
|
|
1748
1757
|
clearSort,
|
|
1749
|
-
getHeaderCellProps
|
|
1758
|
+
getHeaderCellProps,
|
|
1759
|
+
// Exposed for `useDataTable.ts`'s own composition ONLY (unifying this single
|
|
1760
|
+
// click-to-sort column with `useDataTableToolbarSort`'s ordered list) -- not part
|
|
1761
|
+
// of the documented header-click contract (`toggleSort`/`clearSort` above are),
|
|
1762
|
+
// so no consumer-facing doc comment promotes it as a control of its own.
|
|
1763
|
+
setSort
|
|
1750
1764
|
};
|
|
1751
1765
|
}
|
|
1752
1766
|
|
|
@@ -1850,11 +1864,14 @@ const EMPTY_SORTS = [];
|
|
|
1850
1864
|
* concern rather than folded into `useDataTableSort` (DATATABLE-ATTIO-SPEC.md: "compose,
|
|
1851
1865
|
* don't fatten one hook"). `useDataTableSort` still owns the single click-to-sort
|
|
1852
1866
|
* column/direction a header's own click toggles -- that stays Attio's SECONDARY sort
|
|
1853
|
-
* affordance,
|
|
1854
|
-
*
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
1857
|
-
* toolbar
|
|
1867
|
+
* affordance, a separate hook from this one. They are NOT independent state, though:
|
|
1868
|
+
* `useDataTable.ts`'s own composition keeps a header-driven column's entry in THIS
|
|
1869
|
+
* array in sync (added/updated/removed as that column's header cycles asc/desc/none),
|
|
1870
|
+
* so a consumer controlling only `toolbarSorts`/`onToolbarSortsChange` still sees a
|
|
1871
|
+
* header click, and the toolbar's own "Remove sort" x on that same entry clears the
|
|
1872
|
+
* header's side too. Multiple conditions apply in list order (sort[0] wins, later ones
|
|
1873
|
+
* break ties; see the styled layer's `applyToolbarSorts`). The styled layer
|
|
1874
|
+
* (`DataTable.tsx`) decides how the merged result actually sorts `data`.
|
|
1858
1875
|
*/ function useDataTableToolbarSort(props) {
|
|
1859
1876
|
const { toolbarSorts: sortsProp, defaultToolbarSorts = EMPTY_SORTS, onToolbarSortsChange } = props;
|
|
1860
1877
|
const [toolbarSorts = EMPTY_SORTS, setToolbarSorts] = useControllableState$1({
|
|
@@ -1939,8 +1956,13 @@ const EMPTY_SORTS = [];
|
|
|
1939
1956
|
// header "is/is not" picker) -- DATATABLE-ATTIO-SPEC.md's Attio/crisp `TableToolbar`
|
|
1940
1957
|
// model is now the PRIMARY filter/sort UX, not a per-column header affordance. They stay
|
|
1941
1958
|
// SEPARATE sub-hooks from `useDataTableSort` rather than folded into it ("compose, don't
|
|
1942
|
-
// fatten one hook") --
|
|
1943
|
-
//
|
|
1959
|
+
// fatten one hook") -- but this composition point below DOES reconcile the two into one
|
|
1960
|
+
// state (the two `useEffect`s after `infiniteScroll`), so a header click and the
|
|
1961
|
+
// toolbar's own Sort popover always agree on what's sorted, regardless of which prop
|
|
1962
|
+
// pair (`sort`/`onSortChange` or `toolbarSorts`/`onToolbarSortsChange`) a consumer
|
|
1963
|
+
// actually controls. `DataTable.tsx` (styled) still decides how the RESULT actually
|
|
1964
|
+
// sorts `data` (`effectiveSorts`/`applyToolbarSorts`) -- this layer only keeps the two
|
|
1965
|
+
// STATE pools in agreement.
|
|
1944
1966
|
function useDataTable(props) {
|
|
1945
1967
|
const sort = useDataTableSort(props);
|
|
1946
1968
|
const selection = useDataTableSelection(props);
|
|
@@ -1963,6 +1985,105 @@ function useDataTable(props) {
|
|
|
1963
1985
|
...props,
|
|
1964
1986
|
scrollRootRef: scroll.elementRef
|
|
1965
1987
|
});
|
|
1988
|
+
// Unifies `sort` (the header's own single click-to-sort column, above) with
|
|
1989
|
+
// `toolbarSort.toolbarSorts` (the toolbar Sort popover's ordered list, above) into
|
|
1990
|
+
// ONE state instead of two -- before this, a header click only ever moved `sort`,
|
|
1991
|
+
// invisible to a consumer controlling `toolbarSorts`/`onToolbarSortsChange` (the
|
|
1992
|
+
// reported bug: clicking a header did nothing to the array the consumer's own Sort
|
|
1993
|
+
// popover reads, so the toolbar's "Sort" trigger/count and its popover rows silently
|
|
1994
|
+
// ignored every header click).
|
|
1995
|
+
//
|
|
1996
|
+
// ONE effect, not two -- an earlier version split this into a forward
|
|
1997
|
+
// (`sort` -> `toolbarSorts`) and a reverse (`toolbarSorts` -> `sort`) effect, each
|
|
1998
|
+
// keyed off its own dependency. That raced on the very first render: `useEffect`
|
|
1999
|
+
// runs every effect once on mount regardless of what "changed", so with
|
|
2000
|
+
// `defaultSort` set, the reverse effect ran in the SAME commit as the forward one,
|
|
2001
|
+
// read `toolbarSorts` BEFORE the forward effect's own (still-queued, not yet
|
|
2002
|
+
// re-rendered) write had landed, saw no entry for the column yet, and immediately
|
|
2003
|
+
// cleared `sort` right back out -- the defaultSort silently never took effect.
|
|
2004
|
+
// `settledRef` sidesteps this: it remembers the last `{key,dir}` this effect itself
|
|
2005
|
+
// confirmed, so on every run it can tell WHICH side moved since then, instead of
|
|
2006
|
+
// guessing from which dependency happens to have triggered the re-run.
|
|
2007
|
+
const settledRef = React.useRef({
|
|
2008
|
+
key: undefined,
|
|
2009
|
+
dir: undefined
|
|
2010
|
+
});
|
|
2011
|
+
React.useEffect(()=>{
|
|
2012
|
+
const curSort = sort.sort;
|
|
2013
|
+
const curEntry = curSort ? toolbarSort.toolbarSorts.find((s)=>s.key === curSort.key) : undefined;
|
|
2014
|
+
const settled = settledRef.current;
|
|
2015
|
+
const sortUnchangedSinceSettled = settled.key === curSort?.key && settled.dir === curSort?.dir;
|
|
2016
|
+
if (sortUnchangedSinceSettled) {
|
|
2017
|
+
// `sort` itself hasn't moved since we last settled -- if this effect is running
|
|
2018
|
+
// at all, `toolbarSorts` is what changed, so REVERSE-sync: the toolbar Sort
|
|
2019
|
+
// popover's own "Remove sort" x, or its direction select, touching this exact
|
|
2020
|
+
// column. Never reaches across to adopt some OTHER column added independently
|
|
2021
|
+
// via that popover -- only the column `sort` is already tracking.
|
|
2022
|
+
if (!curSort) return;
|
|
2023
|
+
if (!curEntry) {
|
|
2024
|
+
sort.clearSort();
|
|
2025
|
+
settledRef.current = {
|
|
2026
|
+
key: undefined,
|
|
2027
|
+
dir: undefined
|
|
2028
|
+
};
|
|
2029
|
+
} else if (curEntry.dir !== curSort.dir) {
|
|
2030
|
+
sort.setSort({
|
|
2031
|
+
key: curSort.key,
|
|
2032
|
+
dir: curEntry.dir
|
|
2033
|
+
});
|
|
2034
|
+
settledRef.current = {
|
|
2035
|
+
key: curSort.key,
|
|
2036
|
+
dir: curEntry.dir
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
// `sort` moved (a header click, Enter/Space, or `clearSort` -- column-menu.tsx's
|
|
2042
|
+
// menu item calls that directly, bypassing `toggleSort`) -- FORWARD-sync:
|
|
2043
|
+
// add/update/remove THIS column's own entry in `toolbarSorts`, leaving every
|
|
2044
|
+
// OTHER entry (added independently via the toolbar's own "+ Add sort") untouched.
|
|
2045
|
+
// The functional updater reads the CURRENT array off `current`, never off
|
|
2046
|
+
// `toolbarSort.toolbarSorts` in this closure, so a stale render can never clobber
|
|
2047
|
+
// an entry the toolbar's own popover added a moment earlier.
|
|
2048
|
+
const prevKey = settled.key;
|
|
2049
|
+
toolbarSort.setToolbarSorts((current)=>{
|
|
2050
|
+
let updated = current;
|
|
2051
|
+
if (prevKey && prevKey !== curSort?.key) {
|
|
2052
|
+
updated = updated.filter((s)=>s.key !== prevKey);
|
|
2053
|
+
}
|
|
2054
|
+
if (curSort) {
|
|
2055
|
+
const idx = updated.findIndex((s)=>s.key === curSort.key);
|
|
2056
|
+
if (idx === -1) {
|
|
2057
|
+
updated = [
|
|
2058
|
+
...updated,
|
|
2059
|
+
{
|
|
2060
|
+
key: curSort.key,
|
|
2061
|
+
dir: curSort.dir
|
|
2062
|
+
}
|
|
2063
|
+
];
|
|
2064
|
+
} else if (updated[idx].dir !== curSort.dir) {
|
|
2065
|
+
updated = updated.map((s, i)=>i === idx ? {
|
|
2066
|
+
...s,
|
|
2067
|
+
dir: curSort.dir
|
|
2068
|
+
} : s);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
return updated;
|
|
2072
|
+
});
|
|
2073
|
+
settledRef.current = curSort ? {
|
|
2074
|
+
key: curSort.key,
|
|
2075
|
+
dir: curSort.dir
|
|
2076
|
+
} : {
|
|
2077
|
+
key: undefined,
|
|
2078
|
+
dir: undefined
|
|
2079
|
+
};
|
|
2080
|
+
}, [
|
|
2081
|
+
sort.sort,
|
|
2082
|
+
toolbarSort.toolbarSorts,
|
|
2083
|
+
toolbarSort.setToolbarSorts,
|
|
2084
|
+
sort.clearSort,
|
|
2085
|
+
sort.setSort
|
|
2086
|
+
]);
|
|
1966
2087
|
return {
|
|
1967
2088
|
...sort,
|
|
1968
2089
|
...selection,
|
|
@@ -5400,9 +5521,9 @@ function ColumnRenameInput({ column, initialLabel, onDone }) {
|
|
|
5400
5521
|
const columnLabel = typeof label === "string" ? label : column.key;
|
|
5401
5522
|
const pinned = isColumnPinned(column.key);
|
|
5402
5523
|
// Attio/GRC's "Remove sort" (live-review fix D) -- the header's own click-to-sort
|
|
5403
|
-
// (`SortIndicator`/`toggleSort`, above)
|
|
5404
|
-
//
|
|
5405
|
-
// sense to show on the column that IS currently sorted.
|
|
5524
|
+
// (`SortIndicator`/`toggleSort`, above) already cycles asc -> desc -> none on its
|
|
5525
|
+
// own third click, but this stays as a one-click shortcut to the same place, so it
|
|
5526
|
+
// only makes sense to show on the column that IS currently sorted.
|
|
5406
5527
|
const isSorted = sort?.key === column.key;
|
|
5407
5528
|
const stopPropagation = (event)=>event.stopPropagation();
|
|
5408
5529
|
return(// Live-review fix (D): a `MenuItem` inside `MenuPositioner` renders through a
|
package/dist/index.js
CHANGED
|
@@ -1677,24 +1677,33 @@ function useDataTableSort(props) {
|
|
|
1677
1677
|
// already passes its "cleared" shape through unconditionally; this now matches.
|
|
1678
1678
|
onChange: onSortChange
|
|
1679
1679
|
});
|
|
1680
|
+
// 3-state cycle (DATATABLE-ATTIO-SPEC.md: "asc/desc/none cycle") -- clicking a
|
|
1681
|
+
// DIFFERENT column always starts it fresh at asc, discarding whatever the previous
|
|
1682
|
+
// column's direction was (this stays a SINGLE-column secondary affordance, never a
|
|
1683
|
+
// second multi-sort list of its own). Clicking the SAME column steps asc -> desc ->
|
|
1684
|
+
// undefined ("none", i.e. this column drops out of the sort entirely) -> asc again.
|
|
1685
|
+
// Before this, the cycle wrapped desc -> asc forever, with no click-driven way back
|
|
1686
|
+
// to unsorted at all (a reported gap: "sort asc/desc has no option for no sort on
|
|
1687
|
+
// columns") -- `clearSort` below was the ONLY way there, now it's one of two.
|
|
1680
1688
|
const toggleSort = useCallback((key)=>{
|
|
1681
1689
|
setSort((current)=>{
|
|
1682
1690
|
if (!current || current.key !== key) return {
|
|
1683
1691
|
key,
|
|
1684
1692
|
dir: "asc"
|
|
1685
1693
|
};
|
|
1686
|
-
return {
|
|
1694
|
+
if (current.dir === "asc") return {
|
|
1687
1695
|
key,
|
|
1688
|
-
dir:
|
|
1696
|
+
dir: "desc"
|
|
1689
1697
|
};
|
|
1698
|
+
return undefined;
|
|
1690
1699
|
});
|
|
1691
1700
|
}, [
|
|
1692
1701
|
setSort
|
|
1693
1702
|
]);
|
|
1694
|
-
/** Attio/GRC's "Remove sort" --
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1703
|
+
/** Attio/GRC's "Remove sort" -- a second way back to no sort at all (the header
|
|
1704
|
+
* click cycle above reaches it too, on a column's third click), exposed for a
|
|
1705
|
+
* column's own header menu (`column-menu.tsx`'s `ColumnMenu`) to offer as a
|
|
1706
|
+
* "Clear sort" item when that column is the sorted one, without three clicks. */ const clearSort = useCallback(()=>setSort(undefined), [
|
|
1698
1707
|
setSort
|
|
1699
1708
|
]);
|
|
1700
1709
|
const getHeaderCellProps = useCallback((columnKey, sortable)=>{
|
|
@@ -1727,7 +1736,12 @@ function useDataTableSort(props) {
|
|
|
1727
1736
|
sort,
|
|
1728
1737
|
toggleSort,
|
|
1729
1738
|
clearSort,
|
|
1730
|
-
getHeaderCellProps
|
|
1739
|
+
getHeaderCellProps,
|
|
1740
|
+
// Exposed for `useDataTable.ts`'s own composition ONLY (unifying this single
|
|
1741
|
+
// click-to-sort column with `useDataTableToolbarSort`'s ordered list) -- not part
|
|
1742
|
+
// of the documented header-click contract (`toggleSort`/`clearSort` above are),
|
|
1743
|
+
// so no consumer-facing doc comment promotes it as a control of its own.
|
|
1744
|
+
setSort
|
|
1731
1745
|
};
|
|
1732
1746
|
}
|
|
1733
1747
|
|
|
@@ -1831,11 +1845,14 @@ const EMPTY_SORTS = [];
|
|
|
1831
1845
|
* concern rather than folded into `useDataTableSort` (DATATABLE-ATTIO-SPEC.md: "compose,
|
|
1832
1846
|
* don't fatten one hook"). `useDataTableSort` still owns the single click-to-sort
|
|
1833
1847
|
* column/direction a header's own click toggles -- that stays Attio's SECONDARY sort
|
|
1834
|
-
* affordance,
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
* toolbar
|
|
1848
|
+
* affordance, a separate hook from this one. They are NOT independent state, though:
|
|
1849
|
+
* `useDataTable.ts`'s own composition keeps a header-driven column's entry in THIS
|
|
1850
|
+
* array in sync (added/updated/removed as that column's header cycles asc/desc/none),
|
|
1851
|
+
* so a consumer controlling only `toolbarSorts`/`onToolbarSortsChange` still sees a
|
|
1852
|
+
* header click, and the toolbar's own "Remove sort" x on that same entry clears the
|
|
1853
|
+
* header's side too. Multiple conditions apply in list order (sort[0] wins, later ones
|
|
1854
|
+
* break ties; see the styled layer's `applyToolbarSorts`). The styled layer
|
|
1855
|
+
* (`DataTable.tsx`) decides how the merged result actually sorts `data`.
|
|
1839
1856
|
*/ function useDataTableToolbarSort(props) {
|
|
1840
1857
|
const { toolbarSorts: sortsProp, defaultToolbarSorts = EMPTY_SORTS, onToolbarSortsChange } = props;
|
|
1841
1858
|
const [toolbarSorts = EMPTY_SORTS, setToolbarSorts] = useControllableState$1({
|
|
@@ -1920,8 +1937,13 @@ const EMPTY_SORTS = [];
|
|
|
1920
1937
|
// header "is/is not" picker) -- DATATABLE-ATTIO-SPEC.md's Attio/crisp `TableToolbar`
|
|
1921
1938
|
// model is now the PRIMARY filter/sort UX, not a per-column header affordance. They stay
|
|
1922
1939
|
// SEPARATE sub-hooks from `useDataTableSort` rather than folded into it ("compose, don't
|
|
1923
|
-
// fatten one hook") --
|
|
1924
|
-
//
|
|
1940
|
+
// fatten one hook") -- but this composition point below DOES reconcile the two into one
|
|
1941
|
+
// state (the two `useEffect`s after `infiniteScroll`), so a header click and the
|
|
1942
|
+
// toolbar's own Sort popover always agree on what's sorted, regardless of which prop
|
|
1943
|
+
// pair (`sort`/`onSortChange` or `toolbarSorts`/`onToolbarSortsChange`) a consumer
|
|
1944
|
+
// actually controls. `DataTable.tsx` (styled) still decides how the RESULT actually
|
|
1945
|
+
// sorts `data` (`effectiveSorts`/`applyToolbarSorts`) -- this layer only keeps the two
|
|
1946
|
+
// STATE pools in agreement.
|
|
1925
1947
|
function useDataTable(props) {
|
|
1926
1948
|
const sort = useDataTableSort(props);
|
|
1927
1949
|
const selection = useDataTableSelection(props);
|
|
@@ -1944,6 +1966,105 @@ function useDataTable(props) {
|
|
|
1944
1966
|
...props,
|
|
1945
1967
|
scrollRootRef: scroll.elementRef
|
|
1946
1968
|
});
|
|
1969
|
+
// Unifies `sort` (the header's own single click-to-sort column, above) with
|
|
1970
|
+
// `toolbarSort.toolbarSorts` (the toolbar Sort popover's ordered list, above) into
|
|
1971
|
+
// ONE state instead of two -- before this, a header click only ever moved `sort`,
|
|
1972
|
+
// invisible to a consumer controlling `toolbarSorts`/`onToolbarSortsChange` (the
|
|
1973
|
+
// reported bug: clicking a header did nothing to the array the consumer's own Sort
|
|
1974
|
+
// popover reads, so the toolbar's "Sort" trigger/count and its popover rows silently
|
|
1975
|
+
// ignored every header click).
|
|
1976
|
+
//
|
|
1977
|
+
// ONE effect, not two -- an earlier version split this into a forward
|
|
1978
|
+
// (`sort` -> `toolbarSorts`) and a reverse (`toolbarSorts` -> `sort`) effect, each
|
|
1979
|
+
// keyed off its own dependency. That raced on the very first render: `useEffect`
|
|
1980
|
+
// runs every effect once on mount regardless of what "changed", so with
|
|
1981
|
+
// `defaultSort` set, the reverse effect ran in the SAME commit as the forward one,
|
|
1982
|
+
// read `toolbarSorts` BEFORE the forward effect's own (still-queued, not yet
|
|
1983
|
+
// re-rendered) write had landed, saw no entry for the column yet, and immediately
|
|
1984
|
+
// cleared `sort` right back out -- the defaultSort silently never took effect.
|
|
1985
|
+
// `settledRef` sidesteps this: it remembers the last `{key,dir}` this effect itself
|
|
1986
|
+
// confirmed, so on every run it can tell WHICH side moved since then, instead of
|
|
1987
|
+
// guessing from which dependency happens to have triggered the re-run.
|
|
1988
|
+
const settledRef = useRef({
|
|
1989
|
+
key: undefined,
|
|
1990
|
+
dir: undefined
|
|
1991
|
+
});
|
|
1992
|
+
useEffect(()=>{
|
|
1993
|
+
const curSort = sort.sort;
|
|
1994
|
+
const curEntry = curSort ? toolbarSort.toolbarSorts.find((s)=>s.key === curSort.key) : undefined;
|
|
1995
|
+
const settled = settledRef.current;
|
|
1996
|
+
const sortUnchangedSinceSettled = settled.key === curSort?.key && settled.dir === curSort?.dir;
|
|
1997
|
+
if (sortUnchangedSinceSettled) {
|
|
1998
|
+
// `sort` itself hasn't moved since we last settled -- if this effect is running
|
|
1999
|
+
// at all, `toolbarSorts` is what changed, so REVERSE-sync: the toolbar Sort
|
|
2000
|
+
// popover's own "Remove sort" x, or its direction select, touching this exact
|
|
2001
|
+
// column. Never reaches across to adopt some OTHER column added independently
|
|
2002
|
+
// via that popover -- only the column `sort` is already tracking.
|
|
2003
|
+
if (!curSort) return;
|
|
2004
|
+
if (!curEntry) {
|
|
2005
|
+
sort.clearSort();
|
|
2006
|
+
settledRef.current = {
|
|
2007
|
+
key: undefined,
|
|
2008
|
+
dir: undefined
|
|
2009
|
+
};
|
|
2010
|
+
} else if (curEntry.dir !== curSort.dir) {
|
|
2011
|
+
sort.setSort({
|
|
2012
|
+
key: curSort.key,
|
|
2013
|
+
dir: curEntry.dir
|
|
2014
|
+
});
|
|
2015
|
+
settledRef.current = {
|
|
2016
|
+
key: curSort.key,
|
|
2017
|
+
dir: curEntry.dir
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
// `sort` moved (a header click, Enter/Space, or `clearSort` -- column-menu.tsx's
|
|
2023
|
+
// menu item calls that directly, bypassing `toggleSort`) -- FORWARD-sync:
|
|
2024
|
+
// add/update/remove THIS column's own entry in `toolbarSorts`, leaving every
|
|
2025
|
+
// OTHER entry (added independently via the toolbar's own "+ Add sort") untouched.
|
|
2026
|
+
// The functional updater reads the CURRENT array off `current`, never off
|
|
2027
|
+
// `toolbarSort.toolbarSorts` in this closure, so a stale render can never clobber
|
|
2028
|
+
// an entry the toolbar's own popover added a moment earlier.
|
|
2029
|
+
const prevKey = settled.key;
|
|
2030
|
+
toolbarSort.setToolbarSorts((current)=>{
|
|
2031
|
+
let updated = current;
|
|
2032
|
+
if (prevKey && prevKey !== curSort?.key) {
|
|
2033
|
+
updated = updated.filter((s)=>s.key !== prevKey);
|
|
2034
|
+
}
|
|
2035
|
+
if (curSort) {
|
|
2036
|
+
const idx = updated.findIndex((s)=>s.key === curSort.key);
|
|
2037
|
+
if (idx === -1) {
|
|
2038
|
+
updated = [
|
|
2039
|
+
...updated,
|
|
2040
|
+
{
|
|
2041
|
+
key: curSort.key,
|
|
2042
|
+
dir: curSort.dir
|
|
2043
|
+
}
|
|
2044
|
+
];
|
|
2045
|
+
} else if (updated[idx].dir !== curSort.dir) {
|
|
2046
|
+
updated = updated.map((s, i)=>i === idx ? {
|
|
2047
|
+
...s,
|
|
2048
|
+
dir: curSort.dir
|
|
2049
|
+
} : s);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
return updated;
|
|
2053
|
+
});
|
|
2054
|
+
settledRef.current = curSort ? {
|
|
2055
|
+
key: curSort.key,
|
|
2056
|
+
dir: curSort.dir
|
|
2057
|
+
} : {
|
|
2058
|
+
key: undefined,
|
|
2059
|
+
dir: undefined
|
|
2060
|
+
};
|
|
2061
|
+
}, [
|
|
2062
|
+
sort.sort,
|
|
2063
|
+
toolbarSort.toolbarSorts,
|
|
2064
|
+
toolbarSort.setToolbarSorts,
|
|
2065
|
+
sort.clearSort,
|
|
2066
|
+
sort.setSort
|
|
2067
|
+
]);
|
|
1947
2068
|
return {
|
|
1948
2069
|
...sort,
|
|
1949
2070
|
...selection,
|
|
@@ -5381,9 +5502,9 @@ function ColumnRenameInput({ column, initialLabel, onDone }) {
|
|
|
5381
5502
|
const columnLabel = typeof label === "string" ? label : column.key;
|
|
5382
5503
|
const pinned = isColumnPinned(column.key);
|
|
5383
5504
|
// Attio/GRC's "Remove sort" (live-review fix D) -- the header's own click-to-sort
|
|
5384
|
-
// (`SortIndicator`/`toggleSort`, above)
|
|
5385
|
-
//
|
|
5386
|
-
// sense to show on the column that IS currently sorted.
|
|
5505
|
+
// (`SortIndicator`/`toggleSort`, above) already cycles asc -> desc -> none on its
|
|
5506
|
+
// own third click, but this stays as a one-click shortcut to the same place, so it
|
|
5507
|
+
// only makes sense to show on the column that IS currently sorted.
|
|
5387
5508
|
const isSorted = sort?.key === column.key;
|
|
5388
5509
|
const stopPropagation = (event)=>event.stopPropagation();
|
|
5389
5510
|
return(// Live-review fix (D): a `MenuItem` inside `MenuPositioner` renders through a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crispy-seed/data-table",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5",
|
|
4
4
|
"description": "Self-contained, publishable build of seed-design's Attio-measured DataTable, ResizablePanel and RecordPreview — one install, one CSS import.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|