@snack-uikit/table 0.28.7 → 0.29.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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 0.29.0 (2025-02-25)
7
+
8
+
9
+ ### Features
10
+
11
+ * **PDS-569:** table bulk rows multiselect with shift key ([e5802e3](https://github.com/cloud-ru-tech/snack-uikit/commit/e5802e390c0beeb0d4cae20235f3e82fccb83b24))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.28.7 (2025-02-25)
7
18
 
8
19
  ### Only dependencies have been changed
@@ -130,12 +130,12 @@ function Table(_a) {
130
130
  }, [tableColumns]);
131
131
  const enableRowSelection = (0, react_1.useCallback)(row => {
132
132
  const parent = row.getParentRow();
133
- const isParentEnable = parent ? parent.getCanSelect() : true;
134
- let isCurrentRowSelectionEnable = true;
133
+ const isParentSelected = parent ? parent.getCanSelect() : true;
134
+ let isCurrentRowSelected = true;
135
135
  if ((rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable) !== undefined) {
136
- isCurrentRowSelectionEnable = typeof rowSelectionProp.enable === 'boolean' ? rowSelectionProp.enable : rowSelectionProp.enable(row);
136
+ isCurrentRowSelected = typeof rowSelectionProp.enable === 'boolean' ? rowSelectionProp.enable : rowSelectionProp.enable(row);
137
137
  }
138
- return isParentEnable && isCurrentRowSelectionEnable;
138
+ return isParentSelected && isCurrentRowSelected;
139
139
  }, [rowSelectionProp]);
140
140
  const table = (0, react_table_1.useReactTable)({
141
141
  data,
@@ -167,6 +167,7 @@ function Table(_a) {
167
167
  onGlobalFilterChange,
168
168
  getRowId,
169
169
  onRowSelectionChange,
170
+ enableGrouping: true,
170
171
  enableRowSelection,
171
172
  enableMultiRowSelection: rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow,
172
173
  enableFilters: true,
@@ -209,7 +210,7 @@ function Table(_a) {
209
210
  return (_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action, table.getState().rowSelection, table.resetRowSelection);
210
211
  }
211
212
  })) : undefined, [bulkActionsProp, enableSelection, table]);
212
- const handleOnCheck = (0, react_1.useCallback)(() => {
213
+ const handleOnToolbarCheck = (0, react_1.useCallback)(() => {
213
214
  if (!loading && !enableSelectPinned && table.getTopRows().length) {
214
215
  const centerRows = table.getCenterRows();
215
216
  const isSomeRowsSelected = table.getIsSomePageRowsSelected();
@@ -349,7 +350,7 @@ function Table(_a) {
349
350
  selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single',
350
351
  checked: table.getIsAllPageRowsSelected(),
351
352
  indeterminate: table.getIsSomePageRowsSelected(),
352
- onCheck: enableSelection ? handleOnCheck : undefined,
353
+ onCheck: enableSelection ? handleOnToolbarCheck : undefined,
353
354
  outline: outline,
354
355
  after: toolbarAfter || exportSettings ? (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
355
356
  children: [toolbarAfter, exportSettings && (0, jsx_runtime_1.jsx)(helperComponents_1.ExportButton, {
@@ -1,13 +1,5 @@
1
1
  "use strict";
2
2
 
3
- var __rest = void 0 && (void 0).__rest || function (s, e) {
4
- var t = {};
5
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
6
- if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
8
- }
9
- return t;
10
- };
11
3
  var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
12
4
  return mod && mod.__esModule ? mod : {
13
5
  "default": mod
@@ -21,31 +13,32 @@ const jsx_runtime_1 = require("react/jsx-runtime");
21
13
  const toggles_1 = require("@snack-uikit/toggles");
22
14
  const constants_1 = require("../../../constants");
23
15
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
24
- function SelectionCell(_a) {
25
- var {
26
- isMulti,
27
- onChange
28
- } = _a,
29
- props = __rest(_a, ["isMulti", "onChange"]);
30
- const handleCellClick = e => {
31
- e.stopPropagation();
32
- onChange(props.checked);
16
+ function getRowsToToggle(rows, clickedRowId, previousClickedRowId) {
17
+ const rowsToToggle = [];
18
+ const processedRowsMap = {
19
+ [clickedRowId]: false,
20
+ [previousClickedRowId]: false
33
21
  };
34
- return (
35
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
36
- (0, jsx_runtime_1.jsx)("div", {
37
- onClick: handleCellClick,
38
- className: styles_module_scss_1.default.selectionCell,
39
- "data-test-id": constants_1.TEST_IDS.rowSelect,
40
- children: isMulti ? (0, jsx_runtime_1.jsx)(toggles_1.Checkbox, Object.assign({}, props, {
41
- size: 's'
42
- })) : (0, jsx_runtime_1.jsx)(toggles_1.Radio, Object.assign({}, props, {
43
- size: 's'
44
- }))
45
- })
46
- );
22
+ const engagedRows = [clickedRowId, previousClickedRowId];
23
+ for (const row of rows) {
24
+ if (engagedRows.includes(row.id)) {
25
+ if (previousClickedRowId === '') {
26
+ rowsToToggle.push(row);
27
+ break;
28
+ }
29
+ processedRowsMap[row.id] = true;
30
+ }
31
+ if ((processedRowsMap[clickedRowId] || processedRowsMap[previousClickedRowId]) && !row.getIsGrouped()) {
32
+ rowsToToggle.push(row);
33
+ }
34
+ if (processedRowsMap[clickedRowId] && processedRowsMap[previousClickedRowId]) {
35
+ break;
36
+ }
37
+ }
38
+ return rowsToToggle;
47
39
  }
48
40
  function getSelectionCellColumnDef(enableSelectPinned) {
41
+ let previousClickedRowId = '';
49
42
  return {
50
43
  id: 'selectionCell',
51
44
  pinned: constants_1.COLUMN_PIN_POSITION.Left,
@@ -64,11 +57,39 @@ function getSelectionCellColumnDef(enableSelectPinned) {
64
57
  enableMultiRowSelection
65
58
  } = table.options;
66
59
  const isMulti = typeof enableMultiRowSelection === 'boolean' ? enableMultiRowSelection : true;
67
- return (0, jsx_runtime_1.jsx)(SelectionCell, {
68
- isMulti: isMulti,
69
- checked: row.getIsSelected(),
70
- onChange: row.getToggleSelectedHandler()
71
- });
60
+ const checked = row.getIsSelected();
61
+ const handleCellClick = e => {
62
+ var _a, _b;
63
+ e.stopPropagation();
64
+ (_a = globalThis.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
65
+ if (e.shiftKey) {
66
+ const {
67
+ rows,
68
+ rowsById
69
+ } = table.getRowModel();
70
+ const rowsToToggle = getRowsToToggle(rows, row.id, rows.map(r => r.id).includes(previousClickedRowId) ? previousClickedRowId : '');
71
+ const isSelected = !((_b = rowsById[row.id]) === null || _b === void 0 ? void 0 : _b.getIsSelected()) || false;
72
+ rowsToToggle.forEach(row => row.toggleSelected(isSelected));
73
+ } else {
74
+ row.toggleSelected(!checked);
75
+ }
76
+ previousClickedRowId = row.id;
77
+ };
78
+ return (
79
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
80
+ (0, jsx_runtime_1.jsx)("div", {
81
+ onClick: handleCellClick,
82
+ className: styles_module_scss_1.default.selectionCell,
83
+ "data-test-id": constants_1.TEST_IDS.rowSelect,
84
+ children: isMulti ? (0, jsx_runtime_1.jsx)(toggles_1.Checkbox, {
85
+ size: 's',
86
+ checked: checked
87
+ }) : (0, jsx_runtime_1.jsx)(toggles_1.Radio, {
88
+ size: 's',
89
+ checked: checked
90
+ })
91
+ })
92
+ );
72
93
  },
73
94
  meta: {
74
95
  skipOnExport: true
@@ -65,13 +65,13 @@ export function Table(_a) {
65
65
  }, [tableColumns]);
66
66
  const enableRowSelection = useCallback((row) => {
67
67
  const parent = row.getParentRow();
68
- const isParentEnable = parent ? parent.getCanSelect() : true;
69
- let isCurrentRowSelectionEnable = true;
68
+ const isParentSelected = parent ? parent.getCanSelect() : true;
69
+ let isCurrentRowSelected = true;
70
70
  if ((rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable) !== undefined) {
71
- isCurrentRowSelectionEnable =
71
+ isCurrentRowSelected =
72
72
  typeof rowSelectionProp.enable === 'boolean' ? rowSelectionProp.enable : rowSelectionProp.enable(row);
73
73
  }
74
- return isParentEnable && isCurrentRowSelectionEnable;
74
+ return isParentSelected && isCurrentRowSelected;
75
75
  }, [rowSelectionProp]);
76
76
  const table = useReactTable({
77
77
  data,
@@ -98,6 +98,7 @@ export function Table(_a) {
98
98
  onGlobalFilterChange,
99
99
  getRowId,
100
100
  onRowSelectionChange,
101
+ enableGrouping: true,
101
102
  enableRowSelection,
102
103
  enableMultiRowSelection: rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow,
103
104
  enableFilters: true,
@@ -135,7 +136,7 @@ export function Table(_a) {
135
136
  const bulkActions = useMemo(() => enableSelection
136
137
  ? bulkActionsProp === null || bulkActionsProp === void 0 ? void 0 : bulkActionsProp.map(action => (Object.assign(Object.assign({}, action), { onClick: () => { var _a; return (_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action, table.getState().rowSelection, table.resetRowSelection); } })))
137
138
  : undefined, [bulkActionsProp, enableSelection, table]);
138
- const handleOnCheck = useCallback(() => {
139
+ const handleOnToolbarCheck = useCallback(() => {
139
140
  if (!loading && !enableSelectPinned && table.getTopRows().length) {
140
141
  const centerRows = table.getCenterRows();
141
142
  const isSomeRowsSelected = table.getIsSomePageRowsSelected();
@@ -244,7 +245,7 @@ export function Table(_a) {
244
245
  onChange: onGlobalFilterChange,
245
246
  loading: search === null || search === void 0 ? void 0 : search.loading,
246
247
  placeholder: (search === null || search === void 0 ? void 0 : search.placeholder) || t('searchPlaceholder'),
247
- }, className: styles.toolbar, onRefresh: onRefresh ? handleOnRefresh : undefined, bulkActions: bulkActions, selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single', checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected(), onCheck: enableSelection ? handleOnCheck : undefined, outline: outline, after: toolbarAfter || exportSettings ? (_jsxs(_Fragment, { children: [toolbarAfter, exportSettings && (_jsx(ExportButton, { settings: exportSettings, columnDefinitions: columnDefinitions, data: data, topRows: filteredTopRows, centerRows: centerRows }))] })) : undefined, moreActions: moreActions, filterRow: columnFilters, "data-test-id": TEST_IDS.toolbar }) })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsxs(Scroll, { size: 's', className: styles.table, ref: scrollContainerRef, children: [_jsx("div", { className: styles.tableContent, style: columnSizeVars, children: _jsx(TableContext.Provider, { value: { table }, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [centerRows.length || filteredTopRows.length ? _jsx(HeaderRow, {}) : null, filteredTopRows.length ? (_jsx("div", { className: styles.topRowWrapper, children: filteredTopRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))) })) : null, centerRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length + filteredTopRows.length })] })) }) }), _jsx("div", { className: styles.scrollStub, ref: scrollRef })] }) }), !suppressPagination && (_jsx(TablePagination, { table: table, options: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.options, optionsLabel: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsLabel, pageCount: pageCount, optionsRender: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsRender }))] })) }));
248
+ }, className: styles.toolbar, onRefresh: onRefresh ? handleOnRefresh : undefined, bulkActions: bulkActions, selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single', checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected(), onCheck: enableSelection ? handleOnToolbarCheck : undefined, outline: outline, after: toolbarAfter || exportSettings ? (_jsxs(_Fragment, { children: [toolbarAfter, exportSettings && (_jsx(ExportButton, { settings: exportSettings, columnDefinitions: columnDefinitions, data: data, topRows: filteredTopRows, centerRows: centerRows }))] })) : undefined, moreActions: moreActions, filterRow: columnFilters, "data-test-id": TEST_IDS.toolbar }) })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsxs(Scroll, { size: 's', className: styles.table, ref: scrollContainerRef, children: [_jsx("div", { className: styles.tableContent, style: columnSizeVars, children: _jsx(TableContext.Provider, { value: { table }, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [centerRows.length || filteredTopRows.length ? _jsx(HeaderRow, {}) : null, filteredTopRows.length ? (_jsx("div", { className: styles.topRowWrapper, children: filteredTopRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))) })) : null, centerRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length + filteredTopRows.length })] })) }) }), _jsx("div", { className: styles.scrollStub, ref: scrollRef })] }) }), !suppressPagination && (_jsx(TablePagination, { table: table, options: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.options, optionsLabel: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsLabel, pageCount: pageCount, optionsRender: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsRender }))] })) }));
248
249
  }
249
250
  Table.getStatusColumnDef = getStatusColumnDef;
250
251
  Table.statusAppearances = STATUS_APPEARANCE;
@@ -1,29 +1,33 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
1
  import { jsx as _jsx } from "react/jsx-runtime";
13
2
  import { Checkbox, Radio } from '@snack-uikit/toggles';
14
3
  import { COLUMN_PIN_POSITION, TEST_IDS } from '../../../constants';
15
4
  import styles from './styles.module.css';
16
- function SelectionCell(_a) {
17
- var { isMulti, onChange } = _a, props = __rest(_a, ["isMulti", "onChange"]);
18
- const handleCellClick = (e) => {
19
- e.stopPropagation();
20
- onChange(props.checked);
5
+ function getRowsToToggle(rows, clickedRowId, previousClickedRowId) {
6
+ const rowsToToggle = [];
7
+ const processedRowsMap = {
8
+ [clickedRowId]: false,
9
+ [previousClickedRowId]: false,
21
10
  };
22
- return (
23
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
24
- _jsx("div", { onClick: handleCellClick, className: styles.selectionCell, "data-test-id": TEST_IDS.rowSelect, children: isMulti ? _jsx(Checkbox, Object.assign({}, props, { size: 's' })) : _jsx(Radio, Object.assign({}, props, { size: 's' })) }));
11
+ const engagedRows = [clickedRowId, previousClickedRowId];
12
+ for (const row of rows) {
13
+ if (engagedRows.includes(row.id)) {
14
+ if (previousClickedRowId === '') {
15
+ rowsToToggle.push(row);
16
+ break;
17
+ }
18
+ processedRowsMap[row.id] = true;
19
+ }
20
+ if ((processedRowsMap[clickedRowId] || processedRowsMap[previousClickedRowId]) && !row.getIsGrouped()) {
21
+ rowsToToggle.push(row);
22
+ }
23
+ if (processedRowsMap[clickedRowId] && processedRowsMap[previousClickedRowId]) {
24
+ break;
25
+ }
26
+ }
27
+ return rowsToToggle;
25
28
  }
26
29
  export function getSelectionCellColumnDef(enableSelectPinned) {
30
+ let previousClickedRowId = '';
27
31
  return {
28
32
  id: 'selectionCell',
29
33
  pinned: COLUMN_PIN_POSITION.Left,
@@ -37,7 +41,25 @@ export function getSelectionCellColumnDef(enableSelectPinned) {
37
41
  return null;
38
42
  const { enableMultiRowSelection } = table.options;
39
43
  const isMulti = typeof enableMultiRowSelection === 'boolean' ? enableMultiRowSelection : true;
40
- return (_jsx(SelectionCell, { isMulti: isMulti, checked: row.getIsSelected(), onChange: row.getToggleSelectedHandler() }));
44
+ const checked = row.getIsSelected();
45
+ const handleCellClick = (e) => {
46
+ var _a, _b;
47
+ e.stopPropagation();
48
+ (_a = globalThis.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
49
+ if (e.shiftKey) {
50
+ const { rows, rowsById } = table.getRowModel();
51
+ const rowsToToggle = getRowsToToggle(rows, row.id, rows.map(r => r.id).includes(previousClickedRowId) ? previousClickedRowId : '');
52
+ const isSelected = !((_b = rowsById[row.id]) === null || _b === void 0 ? void 0 : _b.getIsSelected()) || false;
53
+ rowsToToggle.forEach(row => row.toggleSelected(isSelected));
54
+ }
55
+ else {
56
+ row.toggleSelected(!checked);
57
+ }
58
+ previousClickedRowId = row.id;
59
+ };
60
+ return (
61
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
62
+ _jsx("div", { onClick: handleCellClick, className: styles.selectionCell, "data-test-id": TEST_IDS.rowSelect, children: isMulti ? _jsx(Checkbox, { size: 's', checked: checked }) : _jsx(Radio, { size: 's', checked: checked }) }));
41
63
  },
42
64
  meta: {
43
65
  skipOnExport: true,
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Table",
7
- "version": "0.28.7",
7
+ "version": "0.29.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -61,5 +61,5 @@
61
61
  "peerDependencies": {
62
62
  "@snack-uikit/locale": "*"
63
63
  },
64
- "gitHead": "ad5274413320bf7880ba3c2d1124610281d0de81"
64
+ "gitHead": "59426d0dd21ec9b22909d8350ca1d988665b4215"
65
65
  }
@@ -144,13 +144,13 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
144
144
  const enableRowSelection = useCallback(
145
145
  (row: Row<TData>) => {
146
146
  const parent = row.getParentRow();
147
- const isParentEnable = parent ? parent.getCanSelect() : true;
148
- let isCurrentRowSelectionEnable = true;
147
+ const isParentSelected = parent ? parent.getCanSelect() : true;
148
+ let isCurrentRowSelected = true;
149
149
  if (rowSelectionProp?.enable !== undefined) {
150
- isCurrentRowSelectionEnable =
150
+ isCurrentRowSelected =
151
151
  typeof rowSelectionProp.enable === 'boolean' ? rowSelectionProp.enable : rowSelectionProp.enable(row);
152
152
  }
153
- return isParentEnable && isCurrentRowSelectionEnable;
153
+ return isParentSelected && isCurrentRowSelected;
154
154
  },
155
155
  [rowSelectionProp],
156
156
  );
@@ -182,6 +182,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
182
182
 
183
183
  getRowId,
184
184
  onRowSelectionChange,
185
+ enableGrouping: true,
185
186
  enableRowSelection,
186
187
  enableMultiRowSelection: rowSelectionProp?.multiRow,
187
188
  enableFilters: true,
@@ -231,7 +232,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
231
232
  [bulkActionsProp, enableSelection, table],
232
233
  );
233
234
 
234
- const handleOnCheck = useCallback(() => {
235
+ const handleOnToolbarCheck = useCallback(() => {
235
236
  if (!loading && !enableSelectPinned && table.getTopRows().length) {
236
237
  const centerRows = table.getCenterRows();
237
238
  const isSomeRowsSelected = table.getIsSomePageRowsSelected();
@@ -385,7 +386,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
385
386
  selectionMode={rowSelectionProp?.multiRow ? 'multiple' : 'single'}
386
387
  checked={table.getIsAllPageRowsSelected()}
387
388
  indeterminate={table.getIsSomePageRowsSelected()}
388
- onCheck={enableSelection ? handleOnCheck : undefined}
389
+ onCheck={enableSelection ? handleOnToolbarCheck : undefined}
389
390
  outline={outline}
390
391
  after={
391
392
  toolbarAfter || exportSettings ? (
@@ -1,3 +1,4 @@
1
+ import { Row } from '@tanstack/react-table';
1
2
  import { MouseEvent } from 'react';
2
3
 
3
4
  import { Checkbox, Radio } from '@snack-uikit/toggles';
@@ -6,28 +7,40 @@ import { COLUMN_PIN_POSITION, TEST_IDS } from '../../../constants';
6
7
  import { ColumnDefinition } from '../../../types';
7
8
  import styles from './styles.module.scss';
8
9
 
9
- type SelectionCellProps = {
10
- checked: boolean;
11
- onChange(checked: boolean): void;
12
- isMulti?: boolean;
13
- };
10
+ function getRowsToToggle<TData>(rows: Row<TData>[], clickedRowId: string, previousClickedRowId: string) {
11
+ const rowsToToggle: Row<TData>[] = [];
12
+ const processedRowsMap: Record<string, boolean> = {
13
+ [clickedRowId]: false,
14
+ [previousClickedRowId]: false,
15
+ };
14
16
 
15
- function SelectionCell({ isMulti, onChange, ...props }: SelectionCellProps) {
16
- const handleCellClick = (e: MouseEvent<HTMLDivElement>) => {
17
- e.stopPropagation();
17
+ const engagedRows = [clickedRowId, previousClickedRowId];
18
18
 
19
- onChange(props.checked);
20
- };
19
+ for (const row of rows) {
20
+ if (engagedRows.includes(row.id)) {
21
+ if (previousClickedRowId === '') {
22
+ rowsToToggle.push(row);
23
+ break;
24
+ }
25
+
26
+ processedRowsMap[row.id] = true;
27
+ }
21
28
 
22
- return (
23
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
24
- <div onClick={handleCellClick} className={styles.selectionCell} data-test-id={TEST_IDS.rowSelect}>
25
- {isMulti ? <Checkbox {...props} size='s' /> : <Radio {...props} size='s' />}
26
- </div>
27
- );
29
+ if ((processedRowsMap[clickedRowId] || processedRowsMap[previousClickedRowId]) && !row.getIsGrouped()) {
30
+ rowsToToggle.push(row);
31
+ }
32
+
33
+ if (processedRowsMap[clickedRowId] && processedRowsMap[previousClickedRowId]) {
34
+ break;
35
+ }
36
+ }
37
+
38
+ return rowsToToggle;
28
39
  }
29
40
 
30
41
  export function getSelectionCellColumnDef<TData>(enableSelectPinned: boolean): ColumnDefinition<TData> {
42
+ let previousClickedRowId = '';
43
+
31
44
  return {
32
45
  id: 'selectionCell',
33
46
  pinned: COLUMN_PIN_POSITION.Left,
@@ -44,8 +57,34 @@ export function getSelectionCellColumnDef<TData>(enableSelectPinned: boolean): C
44
57
 
45
58
  const isMulti = typeof enableMultiRowSelection === 'boolean' ? enableMultiRowSelection : true;
46
59
 
60
+ const checked = row.getIsSelected();
61
+
62
+ const handleCellClick = (e: MouseEvent<HTMLDivElement>) => {
63
+ e.stopPropagation();
64
+
65
+ globalThis.getSelection()?.removeAllRanges();
66
+
67
+ if (e.shiftKey) {
68
+ const { rows, rowsById } = table.getRowModel();
69
+ const rowsToToggle = getRowsToToggle(
70
+ rows,
71
+ row.id,
72
+ rows.map(r => r.id).includes(previousClickedRowId) ? previousClickedRowId : '',
73
+ );
74
+ const isSelected = !rowsById[row.id]?.getIsSelected() || false;
75
+ rowsToToggle.forEach(row => row.toggleSelected(isSelected));
76
+ } else {
77
+ row.toggleSelected(!checked);
78
+ }
79
+
80
+ previousClickedRowId = row.id;
81
+ };
82
+
47
83
  return (
48
- <SelectionCell isMulti={isMulti} checked={row.getIsSelected()} onChange={row.getToggleSelectedHandler()} />
84
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
85
+ <div onClick={handleCellClick} className={styles.selectionCell} data-test-id={TEST_IDS.rowSelect}>
86
+ {isMulti ? <Checkbox size='s' checked={checked} /> : <Radio size='s' checked={checked} />}
87
+ </div>
49
88
  );
50
89
  },
51
90
  meta: {