@wallarm-org/design-system 2.5.0 → 2.7.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.
@@ -56,6 +56,7 @@ export interface TableContextValue<T extends RowData> {
56
56
  onMasterCellClick?: (rowId: string) => void;
57
57
  activeRowId: string | null;
58
58
  onSettingsOpenChange?: (open: boolean) => void;
59
+ deferColumnVisibility: boolean;
59
60
  }
60
61
  export interface TableProviderProps<T extends RowData> extends Omit<TableProps<T>, 'children' | 'aria-label'> {
61
62
  children: ReactNode;
@@ -13,7 +13,7 @@ import { VStack } from "../../Stack/index.js";
13
13
  import { Tooltip, TooltipContent, TooltipTrigger } from "../../Tooltip/index.js";
14
14
  import { TABLE_DRAG_HANDLE_COLUMN_ID, TABLE_EXPAND_COLUMN_ID, TABLE_SELECT_COLUMN_ID, collectDirectChildren, withTableState } from "../lib/index.js";
15
15
  import { useTableContext } from "../TableContext/index.js";
16
- import { TableSettingsMenuContentProvider } from "./TableSettingsMenuContentContext.js";
16
+ import { EMPTY_VISIBILITY_DRAFT, TableSettingsMenuContentProvider } from "./TableSettingsMenuContentContext.js";
17
17
  import { useTableSettingsMenuContext } from "./TableSettingsMenuContext.js";
18
18
  import { TableSettingsMenuItem } from "./TableSettingsMenuItem.js";
19
19
  import { TableSettingsMenuReset } from "./TableSettingsMenuReset.js";
@@ -24,18 +24,31 @@ const TableSettingsMenu = ({ 'data-testid': testIdProp, children, ...rest })=>{
24
24
  const testId = useTestId('settings-menu', testIdProp);
25
25
  const { anchorNode } = useTableSettingsMenuContext();
26
26
  const ctx = useTableContext();
27
- const { table, alwaysPinnedLeft, masterColumnId, columnGroups, onSettingsOpenChange } = ctx;
27
+ const { table, alwaysPinnedLeft, masterColumnId, columnGroups, onSettingsOpenChange, deferColumnVisibility, defaultColumnVisibility } = ctx;
28
28
  const hasTextDescription = table.getAllLeafColumns().some((col)=>col.columnDef.meta?.description?.type === 'text');
29
29
  const [search, setSearch] = useState('');
30
30
  const [menuOpen, setMenuOpen] = useState(false);
31
+ const [visibilityDraft, setVisibilityDraft] = useState(EMPTY_VISIBILITY_DRAFT);
31
32
  const contentRef = useRef(null);
32
33
  const [lockedWidth, setLockedWidth] = useState();
33
34
  const handleOpenChange = (open)=>{
34
35
  setMenuOpen(open);
35
36
  if (open) setSearch('');
36
37
  else setLockedWidth(void 0);
38
+ if (!open && deferColumnVisibility) applyVisibilityDraft();
39
+ setVisibilityDraft(EMPTY_VISIBILITY_DRAFT);
37
40
  onSettingsOpenChange?.(open);
38
41
  };
42
+ const applyVisibilityDraft = ()=>{
43
+ const { fromDefault, toggles } = visibilityDraft;
44
+ const base = fromDefault ? defaultColumnVisibility ?? {} : table.atoms.columnVisibility.get();
45
+ const changed = Object.entries(toggles).filter(([id, visible])=>false !== base[id] !== visible);
46
+ if (!fromDefault && 0 === changed.length) return;
47
+ table.setColumnVisibility({
48
+ ...base,
49
+ ...Object.fromEntries(changed)
50
+ });
51
+ };
39
52
  useLayoutEffect(()=>{
40
53
  if (!menuOpen) return;
41
54
  let raf = requestAnimationFrame(function measure() {
@@ -137,7 +150,9 @@ const TableSettingsMenu = ({ 'data-testid': testIdProp, children, ...rest })=>{
137
150
  const content = /*#__PURE__*/ jsx(TableSettingsMenuContentProvider, {
138
151
  value: {
139
152
  search,
140
- setSearch
153
+ setSearch,
154
+ visibilityDraft,
155
+ setVisibilityDraft
141
156
  },
142
157
  children: /*#__PURE__*/ jsx("div", {
143
158
  "data-testid": testId,
@@ -1,7 +1,20 @@
1
1
  interface TableSettingsMenuContentContextValue {
2
2
  search: string;
3
3
  setSearch: (value: string) => void;
4
+ /**
5
+ * Visibility held while the menu is open (`deferColumnVisibility`), applied
6
+ * to the table on close. Untouched when not deferring.
7
+ */
8
+ visibilityDraft: VisibilityDraft;
9
+ setVisibilityDraft: (draft: VisibilityDraft) => void;
4
10
  }
11
+ export interface VisibilityDraft {
12
+ /** "Reset to default" was pressed: the draft starts from the default, not the table. */
13
+ fromDefault: boolean;
14
+ /** Per-column toggles on top of that base. */
15
+ toggles: Record<string, boolean>;
16
+ }
17
+ export declare const EMPTY_VISIBILITY_DRAFT: VisibilityDraft;
5
18
  export declare const TableSettingsMenuContentProvider: import("react").Provider<TableSettingsMenuContentContextValue | null>;
6
19
  export declare const useTableSettingsMenuContentContext: () => TableSettingsMenuContentContextValue;
7
20
  export {};
@@ -1,4 +1,8 @@
1
1
  import { createContext, useContext } from "react";
2
+ const EMPTY_VISIBILITY_DRAFT = {
3
+ fromDefault: false,
4
+ toggles: {}
5
+ };
2
6
  const TableSettingsMenuContentContext = /*#__PURE__*/ createContext(null);
3
7
  const TableSettingsMenuContentProvider = TableSettingsMenuContentContext.Provider;
4
8
  const useTableSettingsMenuContentContext = ()=>{
@@ -6,4 +10,4 @@ const useTableSettingsMenuContentContext = ()=>{
6
10
  if (!ctx) throw new Error("TableSettingsMenu item components must be rendered inside <TableSettingsMenu>");
7
11
  return ctx;
8
12
  };
9
- export { TableSettingsMenuContentProvider, useTableSettingsMenuContentContext };
13
+ export { EMPTY_VISIBILITY_DRAFT, TableSettingsMenuContentProvider, useTableSettingsMenuContentContext };
@@ -8,8 +8,9 @@ import { useTestId } from "../../../utils/testId.js";
8
8
  import { Switch, SwitchControl, SwitchLabel } from "../../Switch/index.js";
9
9
  import { useTableValue } from "../lib/index.js";
10
10
  import { useTableContext } from "../TableContext/index.js";
11
+ import { useTableSettingsMenuContentContext } from "./TableSettingsMenuContentContext.js";
11
12
  const TableSettingsMenuItem = (t0)=>{
12
- const $ = c(53);
13
+ const $ = c(57);
13
14
  let className;
14
15
  let columnId;
15
16
  let rest;
@@ -27,7 +28,8 @@ const TableSettingsMenuItem = (t0)=>{
27
28
  rest = $[3];
28
29
  testIdProp = $[4];
29
30
  }
30
- const { masterColumnId, columnDndEnabled, columnGroups, table } = useTableContext();
31
+ const { masterColumnId, columnDndEnabled, columnGroups, table, deferColumnVisibility, defaultColumnVisibility } = useTableContext();
32
+ const { visibilityDraft, setVisibilityDraft } = useTableSettingsMenuContentContext();
31
33
  const grouped = !!columnGroups;
32
34
  const slotTestId = useTestId("settings-menu-item");
33
35
  const switchTestId = useTestId(`settings-menu-item-${columnId}`, testIdProp);
@@ -45,7 +47,8 @@ const TableSettingsMenuItem = (t0)=>{
45
47
  $[8] = column;
46
48
  $[9] = t2;
47
49
  } else t2 = $[9];
48
- const isVisible = useTableValue(t2);
50
+ const isTableVisible = useTableValue(t2);
51
+ const isVisible = visibilityDraft.toggles[columnId] ?? (visibilityDraft.fromDefault ? defaultColumnVisibility?.[columnId] !== false : isTableVisible);
49
52
  let t3;
50
53
  if ($[10] !== column) {
51
54
  t3 = ()=>column?.getCanHide() ?? false;
@@ -121,33 +124,43 @@ const TableSettingsMenuItem = (t0)=>{
121
124
  $[30] = t13;
122
125
  } else t13 = $[30];
123
126
  let t14;
124
- if ($[31] !== column) {
125
- t14 = (details)=>column.toggleVisibility(details.checked);
127
+ if ($[31] !== column || $[32] !== columnId || $[33] !== deferColumnVisibility || $[34] !== setVisibilityDraft || $[35] !== visibilityDraft) {
128
+ t14 = (details)=>deferColumnVisibility ? setVisibilityDraft({
129
+ ...visibilityDraft,
130
+ toggles: {
131
+ ...visibilityDraft.toggles,
132
+ [columnId]: details.checked
133
+ }
134
+ }) : column.toggleVisibility(details.checked);
126
135
  $[31] = column;
127
- $[32] = t14;
128
- } else t14 = $[32];
136
+ $[32] = columnId;
137
+ $[33] = deferColumnVisibility;
138
+ $[34] = setVisibilityDraft;
139
+ $[35] = visibilityDraft;
140
+ $[36] = t14;
141
+ } else t14 = $[36];
129
142
  const t15 = isMasterColumn || !canHide;
130
143
  let t16;
131
- if ($[33] === Symbol.for("react.memo_cache_sentinel")) {
144
+ if ($[37] === Symbol.for("react.memo_cache_sentinel")) {
132
145
  t16 = cn("flex-1 truncate");
133
- $[33] = t16;
134
- } else t16 = $[33];
146
+ $[37] = t16;
147
+ } else t16 = $[37];
135
148
  let t17;
136
- if ($[34] !== header) {
149
+ if ($[38] !== header) {
137
150
  t17 = /*#__PURE__*/ jsx(SwitchLabel, {
138
151
  className: t16,
139
152
  children: header
140
153
  });
141
- $[34] = header;
142
- $[35] = t17;
143
- } else t17 = $[35];
154
+ $[38] = header;
155
+ $[39] = t17;
156
+ } else t17 = $[39];
144
157
  let t18;
145
- if ($[36] === Symbol.for("react.memo_cache_sentinel")) {
158
+ if ($[40] === Symbol.for("react.memo_cache_sentinel")) {
146
159
  t18 = /*#__PURE__*/ jsx(SwitchControl, {});
147
- $[36] = t18;
148
- } else t18 = $[36];
160
+ $[40] = t18;
161
+ } else t18 = $[40];
149
162
  let t19;
150
- if ($[37] !== isVisible || $[38] !== rest || $[39] !== switchTestId || $[40] !== t13 || $[41] !== t14 || $[42] !== t15 || $[43] !== t17) {
163
+ if ($[41] !== isVisible || $[42] !== rest || $[43] !== switchTestId || $[44] !== t13 || $[45] !== t14 || $[46] !== t15 || $[47] !== t17) {
151
164
  t19 = /*#__PURE__*/ jsxs(Switch, {
152
165
  ...rest,
153
166
  "data-testid": switchTestId,
@@ -160,17 +173,17 @@ const TableSettingsMenuItem = (t0)=>{
160
173
  t18
161
174
  ]
162
175
  });
163
- $[37] = isVisible;
164
- $[38] = rest;
165
- $[39] = switchTestId;
166
- $[40] = t13;
167
- $[41] = t14;
168
- $[42] = t15;
169
- $[43] = t17;
170
- $[44] = t19;
171
- } else t19 = $[44];
176
+ $[41] = isVisible;
177
+ $[42] = rest;
178
+ $[43] = switchTestId;
179
+ $[44] = t13;
180
+ $[45] = t14;
181
+ $[46] = t15;
182
+ $[47] = t17;
183
+ $[48] = t19;
184
+ } else t19 = $[48];
172
185
  let t20;
173
- if ($[45] !== setNodeRef || $[46] !== slotTestId || $[47] !== style || $[48] !== t10 || $[49] !== t11 || $[50] !== t12 || $[51] !== t19) {
186
+ if ($[49] !== setNodeRef || $[50] !== slotTestId || $[51] !== style || $[52] !== t10 || $[53] !== t11 || $[54] !== t12 || $[55] !== t19) {
174
187
  t20 = /*#__PURE__*/ jsxs("div", {
175
188
  ref: setNodeRef,
176
189
  style: style,
@@ -182,15 +195,15 @@ const TableSettingsMenuItem = (t0)=>{
182
195
  t19
183
196
  ]
184
197
  });
185
- $[45] = setNodeRef;
186
- $[46] = slotTestId;
187
- $[47] = style;
188
- $[48] = t10;
189
- $[49] = t11;
190
- $[50] = t12;
191
- $[51] = t19;
192
- $[52] = t20;
193
- } else t20 = $[52];
198
+ $[49] = setNodeRef;
199
+ $[50] = slotTestId;
200
+ $[51] = style;
201
+ $[52] = t10;
202
+ $[53] = t11;
203
+ $[54] = t12;
204
+ $[55] = t19;
205
+ $[56] = t20;
206
+ } else t20 = $[56];
194
207
  return t20;
195
208
  };
196
209
  TableSettingsMenuItem.displayName = "TableSettingsMenuItem";
@@ -4,8 +4,9 @@ import { isEqual } from "../../../utils/isEqual.js";
4
4
  import { useTestId } from "../../../utils/testId.js";
5
5
  import { Button } from "../../Button/index.js";
6
6
  import { useTableContext } from "../TableContext/index.js";
7
+ import { useTableSettingsMenuContentContext } from "./TableSettingsMenuContentContext.js";
7
8
  const TableSettingsMenuReset = (t0)=>{
8
- const $ = c(23);
9
+ const $ = c(32);
9
10
  let onClick;
10
11
  let rest;
11
12
  let testIdProp;
@@ -21,47 +22,71 @@ const TableSettingsMenuReset = (t0)=>{
21
22
  testIdProp = $[3];
22
23
  }
23
24
  const testId = useTestId("settings-menu-reset", testIdProp);
24
- const { table, visibilityEnabled, columnDndEnabled, defaultColumnVisibility, defaultColumnOrder, setColumnOrder } = useTableContext();
25
+ const { table, visibilityEnabled, columnDndEnabled, defaultColumnVisibility, defaultColumnOrder, setColumnOrder, deferColumnVisibility } = useTableContext();
26
+ const { visibilityDraft, setVisibilityDraft } = useTableSettingsMenuContentContext();
25
27
  let t1;
26
- if ($[4] !== columnDndEnabled || $[5] !== defaultColumnOrder || $[6] !== defaultColumnVisibility || $[7] !== onClick || $[8] !== setColumnOrder || $[9] !== table || $[10] !== visibilityEnabled) {
28
+ if ($[4] !== columnDndEnabled || $[5] !== defaultColumnOrder || $[6] !== defaultColumnVisibility || $[7] !== deferColumnVisibility || $[8] !== onClick || $[9] !== setColumnOrder || $[10] !== setVisibilityDraft || $[11] !== table || $[12] !== visibilityEnabled) {
27
29
  t1 = (event)=>{
28
30
  onClick?.(event);
29
31
  if (event.defaultPrevented) return;
30
- if (defaultColumnVisibility && visibilityEnabled) table.setColumnVisibility(defaultColumnVisibility);
32
+ if (defaultColumnVisibility && visibilityEnabled) if (deferColumnVisibility) setVisibilityDraft({
33
+ fromDefault: true,
34
+ toggles: {}
35
+ });
36
+ else table.setColumnVisibility(defaultColumnVisibility);
31
37
  if (defaultColumnOrder && columnDndEnabled) setColumnOrder(defaultColumnOrder);
32
38
  };
33
39
  $[4] = columnDndEnabled;
34
40
  $[5] = defaultColumnOrder;
35
41
  $[6] = defaultColumnVisibility;
36
- $[7] = onClick;
37
- $[8] = setColumnOrder;
38
- $[9] = table;
39
- $[10] = visibilityEnabled;
40
- $[11] = t1;
41
- } else t1 = $[11];
42
+ $[7] = deferColumnVisibility;
43
+ $[8] = onClick;
44
+ $[9] = setColumnOrder;
45
+ $[10] = setVisibilityDraft;
46
+ $[11] = table;
47
+ $[12] = visibilityEnabled;
48
+ $[13] = t1;
49
+ } else t1 = $[13];
42
50
  const handleClick = t1;
43
51
  const { columnVisibility: currentVisibility, columnOrder: currentOrder } = table.state;
44
52
  const effectiveOrder = 0 === currentOrder.length ? defaultColumnOrder : currentOrder;
53
+ const { fromDefault, toggles } = visibilityDraft;
45
54
  let t2;
46
- if ($[12] !== currentVisibility || $[13] !== defaultColumnVisibility) {
47
- t2 = !defaultColumnVisibility || isEqual(currentVisibility, defaultColumnVisibility);
48
- $[12] = currentVisibility;
49
- $[13] = defaultColumnVisibility;
50
- $[14] = t2;
51
- } else t2 = $[14];
52
- const visibilityMatch = t2;
55
+ if ($[14] !== fromDefault || $[15] !== toggles) {
56
+ t2 = fromDefault || Object.keys(toggles).length > 0;
57
+ $[14] = fromDefault;
58
+ $[15] = toggles;
59
+ $[16] = t2;
60
+ } else t2 = $[16];
61
+ const hasDraft = t2;
53
62
  let t3;
54
- if ($[15] !== defaultColumnOrder || $[16] !== effectiveOrder) {
55
- t3 = !defaultColumnOrder || isEqual(effectiveOrder, defaultColumnOrder);
56
- $[15] = defaultColumnOrder;
57
- $[16] = effectiveOrder;
58
- $[17] = t3;
59
- } else t3 = $[17];
60
- const orderMatch = t3;
61
- const isDefaultState = visibilityMatch && orderMatch;
63
+ if ($[17] !== currentVisibility || $[18] !== defaultColumnVisibility || $[19] !== fromDefault || $[20] !== hasDraft || $[21] !== table || $[22] !== toggles) {
64
+ t3 = !defaultColumnVisibility || (hasDraft ? table.getAllLeafColumns().every((column)=>{
65
+ const base = fromDefault ? defaultColumnVisibility : currentVisibility;
66
+ const shown = toggles[column.id] ?? false !== base[column.id];
67
+ return shown === (false !== defaultColumnVisibility[column.id]);
68
+ }) : isEqual(currentVisibility, defaultColumnVisibility));
69
+ $[17] = currentVisibility;
70
+ $[18] = defaultColumnVisibility;
71
+ $[19] = fromDefault;
72
+ $[20] = hasDraft;
73
+ $[21] = table;
74
+ $[22] = toggles;
75
+ $[23] = t3;
76
+ } else t3 = $[23];
77
+ const visibilityMatch = t3;
62
78
  let t4;
63
- if ($[18] !== handleClick || $[19] !== isDefaultState || $[20] !== rest || $[21] !== testId) {
64
- t4 = /*#__PURE__*/ jsx(Button, {
79
+ if ($[24] !== defaultColumnOrder || $[25] !== effectiveOrder) {
80
+ t4 = !defaultColumnOrder || isEqual(effectiveOrder, defaultColumnOrder);
81
+ $[24] = defaultColumnOrder;
82
+ $[25] = effectiveOrder;
83
+ $[26] = t4;
84
+ } else t4 = $[26];
85
+ const orderMatch = t4;
86
+ const isDefaultState = visibilityMatch && orderMatch;
87
+ let t5;
88
+ if ($[27] !== handleClick || $[28] !== isDefaultState || $[29] !== rest || $[30] !== testId) {
89
+ t5 = /*#__PURE__*/ jsx(Button, {
65
90
  ...rest,
66
91
  type: "button",
67
92
  "data-testid": testId,
@@ -73,13 +98,13 @@ const TableSettingsMenuReset = (t0)=>{
73
98
  fullWidth: true,
74
99
  children: "Reset to default"
75
100
  });
76
- $[18] = handleClick;
77
- $[19] = isDefaultState;
78
- $[20] = rest;
79
- $[21] = testId;
80
- $[22] = t4;
81
- } else t4 = $[22];
82
- return t4;
101
+ $[27] = handleClick;
102
+ $[28] = isDefaultState;
103
+ $[29] = rest;
104
+ $[30] = testId;
105
+ $[31] = t5;
106
+ } else t5 = $[31];
107
+ return t5;
83
108
  };
84
109
  TableSettingsMenuReset.displayName = "TableSettingsMenuReset";
85
110
  export { TableSettingsMenuReset };
@@ -2,6 +2,10 @@ import type { ColumnDef, RowData } from '@tanstack/react-table';
2
2
  import type { DSTableFeatures } from './dsTableFeatures.js';
3
3
  /**
4
4
  * Creates an expand/collapse column for use in Table.
5
- * Automatically injected by Table when `renderExpandedRow` is provided.
5
+ * Automatically injected by Table when `renderExpandedRow` is provided, or
6
+ * with `groupExpandColumn` in sub-row grouping mode — where every row can
7
+ * expand, so `groupsOnly` limits the toggle to rows that have sub-rows.
6
8
  */
7
- export declare const createExpandColumn: <T extends RowData>() => ColumnDef<DSTableFeatures, T, unknown>;
9
+ export declare const createExpandColumn: <T extends RowData>({ groupsOnly, }?: {
10
+ groupsOnly?: boolean;
11
+ }) => ColumnDef<DSTableFeatures, T, unknown>;
@@ -3,7 +3,7 @@ import { ChevronDown, ChevronRight } from "../../../icons/index.js";
3
3
  import { ToggleButton } from "../../ToggleButton/index.js";
4
4
  import { Tooltip, TooltipContent, TooltipTrigger } from "../../Tooltip/index.js";
5
5
  import { TABLE_EXPAND_COLUMN_ID, TABLE_EXPAND_COLUMN_WIDTH } from "./constants.js";
6
- const createExpandColumn = ()=>({
6
+ const createExpandColumn = ({ groupsOnly = false } = {})=>({
7
7
  id: TABLE_EXPAND_COLUMN_ID,
8
8
  size: TABLE_EXPAND_COLUMN_WIDTH,
9
9
  minSize: TABLE_EXPAND_COLUMN_WIDTH,
@@ -18,7 +18,7 @@ const createExpandColumn = ()=>({
18
18
  },
19
19
  header: ()=>null,
20
20
  cell: ({ row })=>{
21
- if (!row.getCanExpand()) return null;
21
+ if (!row.getCanExpand() || groupsOnly && 0 === row.subRows.length) return null;
22
22
  const expanded = row.getIsExpanded();
23
23
  return /*#__PURE__*/ jsxs(Tooltip, {
24
24
  children: [
@@ -241,6 +241,13 @@ export interface TableProps<T> extends TestableProps {
241
241
  * right edge of the table. Requires `renderGroupRow`.
242
242
  */
243
243
  groupRowSpan?: 'master' | 'full';
244
+ /**
245
+ * Keep the expand column in sub-row grouping mode (`renderGroupRow`), which
246
+ * otherwise drops it and leaves the toggle to the group row's content. The
247
+ * column comes first, ahead of the drag handle, and shows a toggle on group
248
+ * rows only — so the rows of a group, drag handle included, sit inside it.
249
+ */
250
+ groupExpandColumn?: boolean;
244
251
  /** Sub-row accessor for hierarchical/tree data (used with renderGroupRow) */
245
252
  getSubRows?: (row: T) => T[] | undefined;
246
253
  /**
@@ -271,6 +278,13 @@ export interface TableProps<T> extends TestableProps {
271
278
  * the close edge here.
272
279
  */
273
280
  onSettingsOpenChange?: (open: boolean) => void;
281
+ /**
282
+ * Hold column-visibility toggles in the built-in settings menu until it
283
+ * closes, then apply them as one `columnVisibility` change. Without it each
284
+ * switch shows or hides its column at once — a column whose data the consumer
285
+ * only fetches on close then sits empty while the menu is open.
286
+ */
287
+ deferColumnVisibility?: boolean;
274
288
  /**
275
289
  * Enable row virtualization. `'container'` virtualizes within the table's own scroll container;
276
290
  * `'window'` follows the page scroll — the nearest scrollable ancestor, or the browser window.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "2.4.0",
3
- "generatedAt": "2026-10-13T09:13:52.244Z",
2
+ "version": "2.6.0",
3
+ "generatedAt": "2026-10-13T14:34:30.624Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Accordion",
@@ -101537,6 +101537,12 @@
101537
101537
  "required": false,
101538
101538
  "description": "How a group row lays out its content.\n\n- `'master'` (default): the master column's cell, then empty filler cells —\n the content is confined to the master column width.\n- `'full'`: `renderGroupRow(row)` in a single cell spanning every data\n column (system cells — selection, drag handle — keep their own cells).\n Use for a group header with a title on the left and actions pinned to the\n right edge of the table. Requires `renderGroupRow`."
101539
101539
  },
101540
+ {
101541
+ "name": "groupExpandColumn",
101542
+ "type": "boolean | undefined",
101543
+ "required": false,
101544
+ "description": "Keep the expand column in sub-row grouping mode (`renderGroupRow`), which\notherwise drops it and leaves the toggle to the group row's content. The\ncolumn comes first, ahead of the drag handle, and shows a toggle on group\nrows only — so the rows of a group, drag handle included, sit inside it."
101545
+ },
101540
101546
  {
101541
101547
  "name": "getSubRows",
101542
101548
  "type": "((row: T) => T[] | undefined) | undefined",
@@ -101580,6 +101586,12 @@
101580
101586
  "required": false,
101581
101587
  "description": "Labeled sections for the column-settings menu. When set, the menu renders\nthese groups (flat, non-reorderable — like the FilterInput field menu)\ninstead of the default pinned/unpinned drag-reorder list. See\n{@link TableColumnGroup}."
101582
101588
  },
101589
+ {
101590
+ "name": "deferColumnVisibility",
101591
+ "type": "boolean | undefined",
101592
+ "required": false,
101593
+ "description": "Hold column-visibility toggles in the built-in settings menu until it\ncloses, then apply them as one `columnVisibility` change. Without it each\nswitch shows or hides its column at once — a column whose data the consumer\nonly fetches on close then sits empty while the menu is open."
101594
+ },
101583
101595
  {
101584
101596
  "name": "virtualized",
101585
101597
  "type": "TableVirtualized | undefined",
@@ -107828,6 +107840,11 @@
107828
107840
  "code": "() => {\n const [data, setData] = useState<SecurityHeaderEntry[]>(() => [\n {\n ...groupedHeaderData[0]!,\n children: [\n { ...groupedHeaderData[0]!, id: `${BANNER_ROW_PREFIX}csp`, children: undefined },\n ...(groupedHeaderData[0]!.children ?? []),\n ],\n },\n ...groupedHeaderData.slice(1, 3),\n { ...groupedHeaderData[0]!, id: 'group-empty', objectName: 'Nothing here yet', children: [] },\n ]);\n const [expanded, setExpanded] = useState<TableExpandedState>(true);\n\n const handleRowReorder = useCallback((event: TableRowReorderEvent) => {\n setData(prev =>\n prev.map(group => {\n const children = group.children ?? [];\n const oldIndex = children.findIndex(row => row.id === event.activeRowId);\n const newIndex = children.findIndex(row => row.id === event.overRowId);\n if (oldIndex === -1 || newIndex === -1) return group;\n return { ...group, children: arrayMove(children, oldIndex, newIndex) };\n }),\n );\n }, []);\n\n return (\n <Table<SecurityHeaderEntry>\n data={data}\n columns={headerColumns}\n getRowId={row => row.id}\n getSubRows={row => row.children}\n showEmptyGroups\n expanded={expanded}\n onExpandedChange={setExpanded}\n onRowReorder={handleRowReorder}\n groupRowSpan='full'\n renderGroupRow={row => {\n const isExpanded = row.getIsExpanded();\n return (\n <HStack gap={8} align='center'>\n <Button\n variant='ghost'\n color='neutral'\n size='small'\n aria-expanded={isExpanded}\n aria-label={isExpanded ? 'Collapse group' : 'Expand group'}\n onClick={row.getToggleExpandedHandler()}\n >\n {isExpanded ? <ChevronDown /> : <ChevronRight />}\n </Button>\n <Text weight='medium'>{row.original.objectName}</Text>\n <Badge color='gray' type='secondary' size='medium'>\n {row.subRows.filter(r => !r.id.startsWith(BANNER_ROW_PREFIX)).length}\n </Badge>\n <HStack gap={4} className='ml-auto'>\n <Button variant='ghost' color='neutral' size='small'>\n <Plus />\n Add header\n </Button>\n <Button variant='ghost' color='neutral' size='small' aria-label='More actions'>\n <Ellipsis />\n </Button>\n </HStack>\n </HStack>\n );\n }}\n renderFullWidthRow={row =>\n row.id.startsWith(BANNER_ROW_PREFIX) ? (\n <HStack gap={8} align='center'>\n <Shield size='sm' />\n <Text size='sm' weight='medium'>\n Wallarm baseline headers\n </Text>\n <Text size='sm' color='secondary'>\n · applied first · built-in, always on\n </Text>\n <Button variant='ghost' color='neutral' size='small' className='ml-auto'>\n Manage baseline →\n </Button>\n </HStack>\n ) : undefined\n }\n getRowCanDrag={row => !row.id.startsWith(BANNER_ROW_PREFIX) && !LOCKED_ROW_IDS.has(row.id)}\n data-testid='grouped-full-width-table'\n />\n );\n}",
107829
107841
  "description": "`groupRowSpan: 'full'` draws `renderGroupRow` across every data column — title on the left,\nactions on the right edge; the content owns the collapse toggle. `renderFullWidthRow` turns a\nregular row into a single spanning banner, and `getRowCanDrag` keeps the banner and the locked\nfirst header in place while the other rows of the group reorder around them."
107830
107842
  },
107843
+ {
107844
+ "name": "GroupedWithExpandColumn",
107845
+ "code": "() => {\n const [data, setData] = useState<SecurityHeaderEntry[]>(() => [\n ...groupedHeaderData,\n { ...groupedHeaderData[0]!, id: 'group-empty', objectName: 'Nothing here yet', children: [] },\n ]);\n const [expanded, setExpanded] = useState<TableExpandedState>(true);\n\n const handleRowReorder = useCallback((event: TableRowReorderEvent) => {\n setData(prev =>\n prev.map(group => {\n const children = group.children ?? [];\n const oldIndex = children.findIndex(row => row.id === event.activeRowId);\n const newIndex = children.findIndex(row => row.id === event.overRowId);\n if (oldIndex === -1 || newIndex === -1) return group;\n return { ...group, children: arrayMove(children, oldIndex, newIndex) };\n }),\n );\n }, []);\n\n return (\n <Table<SecurityHeaderEntry>\n data={data}\n columns={headerColumns}\n getRowId={row => row.id}\n getSubRows={row => row.children}\n showEmptyGroups\n expanded={expanded}\n onExpandedChange={setExpanded}\n onRowReorder={handleRowReorder}\n groupRowSpan='full'\n groupExpandColumn\n renderGroupRow={row => (\n <HStack gap={8} align='center'>\n <Text weight='medium'>{row.original.objectName}</Text>\n <Badge color='gray' type='secondary' size='medium'>\n {row.subRows.length}\n </Badge>\n <Button variant='ghost' color='neutral' size='small' className='ml-auto'>\n <Plus />\n Add header\n </Button>\n </HStack>\n )}\n data-testid='grouped-expand-column-table'\n />\n );\n}",
107846
+ "description": "`groupExpandColumn` keeps the expand column in grouping mode: the toggle sits in its own column,\nfirst, and every row of a group — drag handle included — sits to the right of it, while\n`renderGroupRow` keeps the title and the actions."
107847
+ },
107831
107848
  {
107832
107849
  "name": "RowReorderingWithSelection",
107833
107850
  "code": "() => {\n const [data, setData] = useState(() => securityEvents.slice(0, 6));\n const [rowSelection, setRowSelection] = useState<TableRowSelectionState>({});\n\n const handleRowReorder = useCallback((event: TableRowReorderEvent) => {\n setData(prev => {\n const oldIndex = prev.findIndex(row => row.id === event.activeRowId);\n const newIndex = prev.findIndex(row => row.id === event.overRowId);\n if (oldIndex === -1 || newIndex === -1) return prev;\n return arrayMove(prev, oldIndex, newIndex);\n });\n }, []);\n\n return (\n <Table\n data={data}\n columns={securityColumns}\n getRowId={row => row.id}\n onRowReorder={handleRowReorder}\n rowSelection={rowSelection}\n onRowSelectionChange={setRowSelection}\n />\n );\n}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",