chordia-ui 4.0.2 → 4.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chordia-ui",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
4
4
  "description": "Chordia Design System - UI components, tokens, and Tailwind preset",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
@@ -1602,6 +1602,10 @@ export default function DataTable2({
1602
1602
  const [columnDropdownPosition, setColumnDropdownPosition] = useState({ top: 0, left: 0, width: 0 });
1603
1603
  const [pendingSelection, setPendingSelection] = useState(new Set());
1604
1604
  const [pendingDeselection, setPendingDeselection] = useState(new Set());
1605
+ // Per-column timers that delay the reorder after a checkbox toggle, so the row
1606
+ // is first shown checked/unchecked in place before it moves between the
1607
+ // selected/unselected groups (avoids the confusing instant jump).
1608
+ const columnToggleTimersRef = useRef({});
1605
1609
  // Snapshot of the columns visible on first mount; used as the Reset target
1606
1610
  const initialVisibleColumnsRef = useRef(null);
1607
1611
  if (initialVisibleColumnsRef.current === null) {
@@ -1920,23 +1924,85 @@ export default function DataTable2({
1920
1924
 
1921
1925
  // Toggle column selection — writes to staged state only. The actual table updates
1922
1926
  // when the user clicks Apply (or Reset).
1927
+ // How long a toggled row stays put (showing its new checked state) before it
1928
+ // animates up/down into the correct group.
1929
+ const COLUMN_TOGGLE_MOVE_DELAY = 250;
1930
+
1931
+ const clearColumnToggleTimers = () => {
1932
+ Object.values(columnToggleTimersRef.current).forEach((t) => clearTimeout(t));
1933
+ columnToggleTimersRef.current = {};
1934
+ };
1935
+
1936
+ // Effective staged list accounting for any in-flight (pending) toggles that
1937
+ // haven't committed their reorder yet. Used when committing on Apply.
1938
+ const resolveStagedColumns = () => {
1939
+ const cols = stagedVisibleColumns.filter((id) => !pendingDeselection.has(id));
1940
+ pendingSelection.forEach((id) => {
1941
+ if (!cols.includes(id)) cols.push(id);
1942
+ });
1943
+ return enforceActionPosition(cols);
1944
+ };
1945
+
1923
1946
  const toggleColumnSelection = (columnId) => {
1924
1947
  // Prevent toggling the action column visibility; it should always be shown
1925
1948
  if (columnId === ACTION_COLUMN_ID) {
1926
1949
  return;
1927
1950
  }
1928
- setStagedVisibleColumns(prev => {
1929
- if (prev.includes(columnId)) {
1930
- // Don't allow deselecting all columns
1931
- if (prev.length === 1) return prev;
1932
- return prev.filter(id => id !== columnId);
1933
- }
1934
- if (prev.length >= MAX_COLUMNS) {
1951
+
1952
+ // A commit is already scheduled for this column → the user is toggling back
1953
+ // before it moved. Cancel it and revert the pending visual state in place.
1954
+ if (columnToggleTimersRef.current[columnId]) {
1955
+ clearTimeout(columnToggleTimersRef.current[columnId]);
1956
+ delete columnToggleTimersRef.current[columnId];
1957
+ setPendingSelection((prev) => {
1958
+ const next = new Set(prev);
1959
+ next.delete(columnId);
1960
+ return next;
1961
+ });
1962
+ setPendingDeselection((prev) => {
1963
+ const next = new Set(prev);
1964
+ next.delete(columnId);
1965
+ return next;
1966
+ });
1967
+ return;
1968
+ }
1969
+
1970
+ const isSelected = stagedVisibleColumns.includes(columnId);
1971
+
1972
+ if (isSelected) {
1973
+ // Deselecting: keep the row in the selected group but render it unchecked
1974
+ // (via pendingDeselection), then drop it after a short delay.
1975
+ const effectiveVisible = stagedVisibleColumns.length - pendingDeselection.size;
1976
+ if (effectiveVisible <= 1) return; // never hide the last visible column
1977
+ setPendingDeselection((prev) => new Set(prev).add(columnId));
1978
+ columnToggleTimersRef.current[columnId] = setTimeout(() => {
1979
+ setStagedVisibleColumns((prev) => prev.filter((id) => id !== columnId));
1980
+ setPendingDeselection((prev) => {
1981
+ const next = new Set(prev);
1982
+ next.delete(columnId);
1983
+ return next;
1984
+ });
1985
+ delete columnToggleTimersRef.current[columnId];
1986
+ }, COLUMN_TOGGLE_MOVE_DELAY);
1987
+ } else {
1988
+ // Selecting: show the row checked in place (via pendingSelection), then
1989
+ // move it up into the selected group after a short delay.
1990
+ const effectiveVisible = stagedVisibleColumns.length + pendingSelection.size;
1991
+ if (effectiveVisible >= MAX_COLUMNS) {
1935
1992
  showMaxColumnsError("Maximum 9 column selection allowed");
1936
- return prev;
1993
+ return;
1937
1994
  }
1938
- return enforceActionPosition([...prev, columnId]);
1939
- });
1995
+ setPendingSelection((prev) => new Set(prev).add(columnId));
1996
+ columnToggleTimersRef.current[columnId] = setTimeout(() => {
1997
+ setStagedVisibleColumns((prev) => enforceActionPosition([...prev, columnId]));
1998
+ setPendingSelection((prev) => {
1999
+ const next = new Set(prev);
2000
+ next.delete(columnId);
2001
+ return next;
2002
+ });
2003
+ delete columnToggleTimersRef.current[columnId];
2004
+ }, COLUMN_TOGGLE_MOVE_DELAY);
2005
+ }
1940
2006
  };
1941
2007
 
1942
2008
  // Column drag handlers for reordering in dropdown
@@ -2126,12 +2192,16 @@ export default function DataTable2({
2126
2192
  // last applied state.
2127
2193
  useEffect(() => {
2128
2194
  if (isColumnDropdownOpen) {
2195
+ clearColumnToggleTimers();
2129
2196
  setStagedVisibleColumns(visibleColumns);
2130
2197
  setPendingSelection(new Set());
2131
2198
  setPendingDeselection(new Set());
2132
2199
  }
2133
2200
  }, [isColumnDropdownOpen, visibleColumns]);
2134
2201
 
2202
+ // Clear any pending reorder timers on unmount.
2203
+ useEffect(() => () => clearColumnToggleTimers(), []);
2204
+
2135
2205
  // Display text for dropdown button
2136
2206
  const displayText = visibleColumns.length === 1
2137
2207
  ? columns.find(col => (col.id || col.key) === visibleColumns[0])?.label || "1 selected"
@@ -2616,6 +2686,7 @@ export default function DataTable2({
2616
2686
  : columns.map(getColumnId).slice(0, MAX_COLUMNS);
2617
2687
  const constrained = enforceActionPosition(snapshot);
2618
2688
  userHasManuallyChangedColumns.current = false;
2689
+ clearColumnToggleTimers();
2619
2690
  setStagedVisibleColumns(constrained);
2620
2691
  setVisibleColumns(constrained);
2621
2692
  setPendingSelection(new Set());
@@ -2642,8 +2713,13 @@ export default function DataTable2({
2642
2713
  onClick={(e) => {
2643
2714
  e.preventDefault();
2644
2715
  e.stopPropagation();
2645
- // Commit staged selection to the table.
2646
- const constrained = enforceActionPosition(stagedVisibleColumns);
2716
+ // Commit staged selection to the table, flushing any in-flight
2717
+ // (pending) toggles that haven't finished their reorder delay.
2718
+ clearColumnToggleTimers();
2719
+ const constrained = resolveStagedColumns();
2720
+ setStagedVisibleColumns(constrained);
2721
+ setPendingSelection(new Set());
2722
+ setPendingDeselection(new Set());
2647
2723
  userHasManuallyChangedColumns.current = true;
2648
2724
  setVisibleColumns(constrained);
2649
2725
  saveColumnsToStorage(constrained);