@drivenets/design-system 0.18.1 → 0.20.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 +48 -0
- package/dist/index.cjs +17038 -10114
- package/dist/index.d.cts +1355 -444
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1354 -443
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17073 -10172
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +41 -41
- package/src/components/ds-bot-button/ds-bot-button.figma.ts +38 -0
- package/src/components/ds-bot-button/ds-bot-button.module.scss +41 -0
- package/src/components/ds-bot-button/ds-bot-button.tsx +48 -0
- package/src/components/ds-bot-button/ds-bot-button.types.ts +33 -0
- package/src/components/ds-bot-button/index.ts +2 -0
- package/src/components/ds-bot-button/net-gen-logo.tsx +46 -0
- package/src/components/ds-bulk-actions/components/action-button.tsx +42 -0
- package/src/components/ds-bulk-actions/components/menu-entries.tsx +76 -0
- package/src/components/ds-bulk-actions/components/overflow.tsx +75 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.figma.ts +28 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.module.scss +199 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.tsx +220 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.types.ts +170 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.unit.test.ts +195 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.utils.ts +91 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.utils.unit.test.ts +134 -0
- package/src/components/ds-bulk-actions/hooks/compute-bulk-actions-overflow.ts +85 -0
- package/src/components/ds-bulk-actions/hooks/use-bulk-actions-overflow.ts +153 -0
- package/src/components/ds-bulk-actions/index.ts +18 -0
- package/src/components/ds-button-v3/ds-button-v3.module.scss +2 -2
- package/src/components/ds-catalog-layout/ds-catalog-layout.figma.ts +1 -1
- package/src/components/ds-checkbox/ds-checkbox.figma.ts +3 -0
- package/src/components/ds-checkbox/ds-checkbox.module.scss +102 -20
- package/src/components/ds-checkbox/ds-checkbox.tsx +30 -2
- package/src/components/ds-checkbox/ds-checkbox.types.ts +10 -0
- package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.module.scss +169 -0
- package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.tsx +92 -0
- package/src/components/ds-code-input/collapsed-code-field/index.ts +1 -0
- package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.module.scss +110 -0
- package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.tsx +61 -0
- package/src/components/ds-code-input/ds-code-input-panel/index.ts +1 -0
- package/src/components/ds-code-input/ds-code-input.module.scss +25 -0
- package/src/components/ds-code-input/ds-code-input.tsx +401 -0
- package/src/components/ds-code-input/ds-code-input.types.ts +166 -0
- package/src/components/ds-code-input/ds-code-input.utils.ts +94 -0
- package/src/components/ds-code-input/ds-code-input.utils.unit.test.ts +166 -0
- package/src/components/ds-code-input/index.ts +2 -0
- package/src/components/ds-code-input/use-collapsed-viewport.ts +289 -0
- package/src/components/ds-code-input/use-editor-host.ts +87 -0
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
- package/src/components/ds-empty-state/ds-empty-state.module.scss +16 -0
- package/src/components/ds-empty-state/ds-empty-state.tsx +45 -0
- package/src/components/ds-empty-state/ds-empty-state.types.ts +38 -0
- package/src/components/ds-empty-state/index.ts +3 -0
- package/src/components/ds-file-upload/ds-file-upload.tsx +4 -1
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.types.ts +41 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-clear-all.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.types.ts +30 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-pinned.ts +31 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.types.ts +22 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-saved-filters.ts +19 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-search.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-summary.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-toolbar.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-view-switch.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-view.ts +11 -0
- package/src/components/ds-filters-bar/components/index.ts +11 -0
- package/src/components/ds-filters-bar/ds-filters-bar.context.ts +69 -0
- package/src/components/ds-filters-bar/ds-filters-bar.tsx +120 -0
- package/src/components/ds-filters-bar/ds-filters-bar.types.ts +440 -0
- package/src/components/ds-filters-bar/ds-filters-bar.unit.test.ts +216 -0
- package/src/components/ds-filters-bar/ds-filters-bar.utils.ts +141 -0
- package/src/components/ds-filters-bar/index.ts +1 -0
- package/src/components/ds-form-control/ds-form-control.module.scss +18 -0
- package/src/components/ds-form-control/ds-form-control.tsx +61 -19
- package/src/components/ds-form-control/ds-form-control.types.ts +2 -1
- package/src/components/ds-icon/custom-icons/index.ts +2 -0
- package/src/components/ds-icon/custom-icons/special-adv-filters.tsx +28 -0
- package/src/components/ds-illustration/ds-illustration.figma.ts +34 -0
- package/src/components/ds-illustration/ds-illustration.module.scss +6 -0
- package/src/components/ds-illustration/ds-illustration.tsx +29 -0
- package/src/components/ds-illustration/ds-illustration.types.ts +45 -0
- package/src/components/ds-illustration/illustrations/configuration.tsx +51 -0
- package/src/components/ds-illustration/illustrations/device.tsx +133 -0
- package/src/components/ds-illustration/illustrations/done.tsx +107 -0
- package/src/components/{ds-catalog-layout/catalog-layout-empty-illustration.tsx → ds-illustration/illustrations/empty-inbox.tsx} +10 -14
- package/src/components/ds-illustration/illustrations/error.tsx +86 -0
- package/src/components/ds-illustration/illustrations/go-filter.tsx +51 -0
- package/src/components/ds-illustration/illustrations/illustration-clouds.tsx +10 -0
- package/src/components/ds-illustration/illustrations/index.ts +39 -0
- package/src/components/ds-illustration/illustrations/no-connection.tsx +54 -0
- package/src/components/ds-illustration/illustrations/no-credit-card.tsx +48 -0
- package/src/components/ds-illustration/illustrations/no-device.tsx +61 -0
- package/src/components/ds-illustration/illustrations/no-documents.tsx +52 -0
- package/src/components/ds-illustration/illustrations/no-gps.tsx +57 -0
- package/src/components/ds-illustration/illustrations/no-images.tsx +82 -0
- package/src/components/ds-illustration/illustrations/no-items-cart.tsx +86 -0
- package/src/components/ds-illustration/illustrations/no-messages.tsx +51 -0
- package/src/components/ds-illustration/illustrations/no-search-result.tsx +76 -0
- package/src/components/ds-illustration/illustrations/no-tasks.tsx +61 -0
- package/src/components/ds-illustration/illustrations/site.tsx +80 -0
- package/src/components/ds-illustration/index.ts +3 -0
- package/src/components/ds-main-menu/components/expanded-item/expanded-item.tsx +108 -0
- package/src/components/ds-main-menu/components/expanded-item/expanded-item.types.ts +8 -0
- package/src/components/ds-main-menu/components/expanded-item/index.ts +1 -0
- package/src/components/ds-main-menu/components/menu-graphic/index.ts +1 -0
- package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.tsx +16 -0
- package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.types.ts +7 -0
- package/src/components/ds-main-menu/components/tile-link/index.ts +1 -0
- package/src/components/ds-main-menu/components/tile-link/tile-link.tsx +85 -0
- package/src/components/ds-main-menu/components/tile-link/tile-link.types.ts +8 -0
- package/src/components/ds-main-menu/components/utility-link/index.ts +1 -0
- package/src/components/ds-main-menu/components/utility-link/utility-link.tsx +37 -0
- package/src/components/ds-main-menu/components/utility-link/utility-link.types.ts +7 -0
- package/src/components/ds-main-menu/ds-main-menu.figma.ts +17 -0
- package/src/components/ds-main-menu/ds-main-menu.module.scss +349 -0
- package/src/components/ds-main-menu/ds-main-menu.tsx +102 -0
- package/src/components/ds-main-menu/ds-main-menu.types.ts +86 -0
- package/src/components/ds-main-menu/ds-main-menu.utils.ts +40 -0
- package/src/components/ds-main-menu/index.ts +9 -0
- package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +1 -1
- package/src/components/ds-pin-toggle/ds-pin-toggle.figma.ts +27 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.module.scss +41 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.tsx +43 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.types.ts +29 -0
- package/src/components/ds-pin-toggle/index.ts +2 -0
- package/src/components/ds-popover/components/popover-trigger/index.ts +1 -0
- package/src/components/ds-popover/components/popover-trigger/popover-trigger.tsx +15 -0
- package/src/components/ds-popover/ds-popover.context.ts +18 -0
- package/src/components/ds-popover/ds-popover.hover-intent.ts +65 -0
- package/src/components/ds-popover/ds-popover.tsx +164 -41
- package/src/components/ds-popover/ds-popover.types.ts +65 -0
- package/src/components/ds-popover/ds-popover.utils.ts +60 -0
- package/src/components/ds-popover/ds-popover.utils.unit.test.ts +25 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-delete-confirm.tsx +53 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-name-entry.tsx +64 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-name-modal.tsx +50 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.module.scss +68 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.tsx +83 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/index.ts +1 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-save.tsx +164 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.module.scss +21 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.tsx +237 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/index.ts +1 -0
- package/src/components/ds-saved-filters/components/index.ts +2 -0
- package/src/components/ds-saved-filters/ds-saved-filters.ts +6 -0
- package/src/components/ds-saved-filters/ds-saved-filters.types.ts +106 -0
- package/src/components/ds-saved-filters/ds-saved-filters.utils.ts +35 -0
- package/src/components/ds-saved-filters/hooks/index.ts +1 -0
- package/src/components/ds-saved-filters/hooks/use-picker-outside-dismiss.ts +42 -0
- package/src/components/ds-saved-filters/index.ts +2 -0
- package/src/components/ds-segment-group/ds-segment-group.module.scss +65 -37
- package/src/components/ds-segment-group/ds-segment-group.tsx +17 -5
- package/src/components/ds-segment-group/ds-segment-group.types.ts +20 -5
- package/src/components/ds-segment-group/figma/ds-segment-group-v2-item.figma.ts +109 -0
- package/src/components/ds-segment-group/figma/ds-segment-group-v2.figma.ts +31 -0
- package/src/components/ds-site-node/ds-site-node.tsx +2 -2
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +12 -2
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +2 -3
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +1 -6
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +6 -3
- package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +18 -7
- package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +27 -21
- package/src/components/ds-table/components/ds-table-header-label.module.scss +12 -0
- package/src/components/ds-table/components/ds-table-header-label.tsx +28 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +0 -1
- package/src/components/ds-table/context/ds-table-context.tsx +8 -0
- package/src/components/ds-table/ds-table.module.scss +26 -16
- package/src/components/ds-table/ds-table.tsx +46 -14
- package/src/components/ds-table/ds-table.types.ts +33 -3
- package/src/components/ds-table/hooks/use-column-resize.ts +114 -11
- package/src/components/ds-table/hooks/use-scrollbar-spacer.ts +108 -0
- package/src/components/ds-table/hooks/use-scrollbar-spacer.unit.test.ts +26 -0
- package/src/components/ds-table/styles/_variables.scss +0 -2
- package/src/components/ds-table/utils/column-size.ts +274 -1
- package/src/components/ds-table/utils/column-size.unit.test.ts +157 -0
- package/src/components/ds-table/utils/constants.ts +13 -5
- package/src/components/ds-table/utils/table-columns.tsx +27 -13
- package/src/components/ds-table/utils/table-columns.unit.test.ts +44 -0
- package/src/components/ds-tag/ds-tag.figma.ts +23 -3
- package/src/components/ds-tag/ds-tag.module.scss +71 -7
- package/src/components/ds-tag/ds-tag.tsx +115 -14
- package/src/components/ds-tag/ds-tag.types.ts +70 -3
- package/src/components/ds-tag/ds-tag.unit.test.ts +23 -4
- package/src/components/ds-tag/index.ts +2 -2
- package/src/components/ds-text-input/ds-input-field-v02.figma.ts +2 -0
- package/src/components/ds-toggle/ds-toggle.module.scss +18 -0
- package/src/components/ds-toggle/ds-toggle.tsx +15 -9
- package/src/components/ds-toggle/ds-toggle.types.ts +6 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.module.scss +69 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.tsx +55 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.types.ts +43 -0
- package/src/components/ds-toggle-filter-data/index.ts +1 -0
- package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -0
- package/src/components/ds-tooltip/ds-tooltip.tsx +7 -3
- package/src/components/ds-tooltip/ds-tooltip.types.ts +17 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.figma.ts +35 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.module.scss +150 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.tsx +161 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.types.ts +117 -0
- package/src/components/ds-top-bar-navigation/index.ts +2 -0
- package/src/components/ds-tree/ds-tree.tsx +10 -10
- package/src/components/ds-tree/ds-tree.types.ts +40 -7
- package/src/index.ts +8 -0
- package/src/utils/use-is-horizontally-overflowing.ts +35 -0
- package/src/components/ds-site-node/hooks/use-is-overflowing.ts +0 -31
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +0 -112
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +0 -67
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +0 -41
- package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +0 -2
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from '@tanstack/react-table';
|
|
15
15
|
import classnames from 'classnames';
|
|
16
16
|
import { Table, TableBody, TableCell, TableRow } from './components/core-table';
|
|
17
|
-
import {
|
|
17
|
+
import { DsBulkActions } from '../ds-bulk-actions';
|
|
18
18
|
import { DsTableHeader } from './components/ds-table-header';
|
|
19
19
|
import { DsTableResizeOverlay } from './components/ds-table-resize-overlay';
|
|
20
20
|
import styles from './ds-table.module.scss';
|
|
@@ -26,7 +26,8 @@ import { useColumnResize } from './hooks/use-column-resize';
|
|
|
26
26
|
import { DsTableContextProvider } from './context/ds-table-context';
|
|
27
27
|
import { DsTableBodyVirtualized } from './components/ds-table-body-virtualized';
|
|
28
28
|
import { useColumnGroups } from './grouping';
|
|
29
|
-
import {
|
|
29
|
+
import { DsEmptyState } from '../ds-empty-state';
|
|
30
|
+
import { getUtilityColumnSizing } from './utils/column-size';
|
|
30
31
|
import { createSkeletonRows, getAugmentedColumns, toSkeletonColumns } from './utils/table-columns';
|
|
31
32
|
import { createTableApi } from './utils/table-api';
|
|
32
33
|
import { areBodiesFrozen } from './utils/frozen-body';
|
|
@@ -75,6 +76,9 @@ const DsTable = <TData extends { id: string }, TValue>(props: DsDataTableProps<T
|
|
|
75
76
|
resizableColumns,
|
|
76
77
|
columnSizing,
|
|
77
78
|
onColumnSizingChange,
|
|
79
|
+
selectableColumnWidth,
|
|
80
|
+
expandableColumnWidth,
|
|
81
|
+
reorderableColumnWidth,
|
|
78
82
|
} = tableProps;
|
|
79
83
|
const [data, setData] = React.useState(tableData);
|
|
80
84
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
@@ -155,12 +159,32 @@ const DsTable = <TData extends { id: string }, TValue>(props: DsDataTableProps<T
|
|
|
155
159
|
reorderable,
|
|
156
160
|
virtualized,
|
|
157
161
|
showSelectAllCheckbox,
|
|
162
|
+
selectableColumnWidth,
|
|
163
|
+
expandableColumnWidth,
|
|
164
|
+
reorderableColumnWidth,
|
|
158
165
|
}),
|
|
159
|
-
[
|
|
166
|
+
[
|
|
167
|
+
columnsProp,
|
|
168
|
+
hasSelectColumn,
|
|
169
|
+
hasExpanderColumn,
|
|
170
|
+
reorderable,
|
|
171
|
+
virtualized,
|
|
172
|
+
showSelectAllCheckbox,
|
|
173
|
+
selectableColumnWidth,
|
|
174
|
+
expandableColumnWidth,
|
|
175
|
+
reorderableColumnWidth,
|
|
176
|
+
],
|
|
160
177
|
);
|
|
161
178
|
|
|
162
179
|
const skeletonColumns = useMemo(() => toSkeletonColumns(columns), [columns]);
|
|
163
180
|
const skeletonData = useMemo(() => createSkeletonRows<TData>(), []);
|
|
181
|
+
const utilityColumnSizing = useMemo(() => getUtilityColumnSizing(columns), [columns]);
|
|
182
|
+
const sourceDataLength = (reorderable ? data : tableData).length;
|
|
183
|
+
const inferredEmptyState = useMemo(
|
|
184
|
+
() => <DsEmptyState variant={sourceDataLength === 0 ? 'noData' : 'noMatches'} />,
|
|
185
|
+
[sourceDataLength],
|
|
186
|
+
);
|
|
187
|
+
const resolvedEmptyState = emptyState ?? inferredEmptyState;
|
|
164
188
|
|
|
165
189
|
const resize = useColumnResize({
|
|
166
190
|
enabled: resizableColumns,
|
|
@@ -168,6 +192,8 @@ const DsTable = <TData extends { id: string }, TValue>(props: DsDataTableProps<T
|
|
|
168
192
|
onColumnSizingChange,
|
|
169
193
|
columns,
|
|
170
194
|
columnVisibility,
|
|
195
|
+
overflowKey: (reorderable ? data : tableData).length,
|
|
196
|
+
utilityColumnSizing,
|
|
171
197
|
});
|
|
172
198
|
|
|
173
199
|
const table = useReactTable({
|
|
@@ -238,7 +264,7 @@ const DsTable = <TData extends { id: string }, TValue>(props: DsDataTableProps<T
|
|
|
238
264
|
{virtualized ? (
|
|
239
265
|
<MemoizedDsTableBodyVirtualized
|
|
240
266
|
table={table}
|
|
241
|
-
emptyState={
|
|
267
|
+
emptyState={resolvedEmptyState}
|
|
242
268
|
estimateSize={virtualizedOptions?.estimateSize || ROW_SIZE_HEIGHT_MAP[rowSize]}
|
|
243
269
|
overscan={virtualizedOptions?.overscan}
|
|
244
270
|
onScroll={onScroll}
|
|
@@ -249,7 +275,7 @@ const DsTable = <TData extends { id: string }, TValue>(props: DsDataTableProps<T
|
|
|
249
275
|
<MemoizedDsTableRowsBody
|
|
250
276
|
table={table}
|
|
251
277
|
rowSelection={rowSelection}
|
|
252
|
-
emptyState={
|
|
278
|
+
emptyState={resolvedEmptyState}
|
|
253
279
|
SortableWrapper={SortableWrapper}
|
|
254
280
|
/>
|
|
255
281
|
)}
|
|
@@ -260,14 +286,20 @@ const DsTable = <TData extends { id: string }, TValue>(props: DsDataTableProps<T
|
|
|
260
286
|
) : null}
|
|
261
287
|
</div>
|
|
262
288
|
{selectable && actions.length > 0 && (
|
|
263
|
-
<
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
...action,
|
|
267
|
-
onClick: () => action.onClick(selectedRows),
|
|
268
|
-
}))}
|
|
289
|
+
<DsBulkActions
|
|
290
|
+
placement="floating"
|
|
291
|
+
selectedCount={selectedRows.length}
|
|
269
292
|
onClearSelection={table.resetRowSelection}
|
|
270
|
-
|
|
293
|
+
>
|
|
294
|
+
{actions.map((action, index) => (
|
|
295
|
+
<DsBulkActions.Item
|
|
296
|
+
key={`${action.label}-${String(index)}`}
|
|
297
|
+
icon={action.icon}
|
|
298
|
+
label={action.label}
|
|
299
|
+
onClick={() => action.onClick(selectedRows)}
|
|
300
|
+
/>
|
|
301
|
+
))}
|
|
302
|
+
</DsBulkActions>
|
|
271
303
|
)}
|
|
272
304
|
</div>
|
|
273
305
|
</DsTableContextProvider>
|
|
@@ -296,9 +328,9 @@ const DsTableRowsBody = <TData extends { id: string }>({
|
|
|
296
328
|
{rows.length ? (
|
|
297
329
|
rows.map((row) => <DsTableRow key={row.id} row={row} isSelected={!!rowSelection[row.id]} />)
|
|
298
330
|
) : (
|
|
299
|
-
<TableRow>
|
|
331
|
+
<TableRow className={styles.emptyStateRow}>
|
|
300
332
|
<TableCell colSpan={columnCount} className={styles.emptyState}>
|
|
301
|
-
{emptyState
|
|
333
|
+
{emptyState}
|
|
302
334
|
</TableCell>
|
|
303
335
|
</TableRow>
|
|
304
336
|
)}
|
|
@@ -296,7 +296,9 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
296
296
|
activeRowId?: string | null;
|
|
297
297
|
|
|
298
298
|
/**
|
|
299
|
-
* Empty state
|
|
299
|
+
* Replaces the inferred **Empty state**. When omitted, the table renders
|
|
300
|
+
* `DsEmptyState` as `noData` if `data` is empty, or `noMatches` if `data`
|
|
301
|
+
* has rows but filters hide all of them.
|
|
300
302
|
*/
|
|
301
303
|
emptyState?: React.ReactNode;
|
|
302
304
|
|
|
@@ -340,14 +342,17 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
340
342
|
* from {@link onColumnSizingChange}. Pass it (typically on the next mount) to
|
|
341
343
|
* restore widths; columns absent from the map are measured and sized
|
|
342
344
|
* automatically. This seeds widths — live resizing still updates them
|
|
343
|
-
* internally — so it is not a fully controlled value.
|
|
345
|
+
* internally — so it is not a fully controlled value. Ids for the select,
|
|
346
|
+
* expander, and reorder utility columns are ignored; use the matching
|
|
347
|
+
* `*ColumnWidth` props instead.
|
|
344
348
|
*/
|
|
345
349
|
columnSizing?: Record<string, number>;
|
|
346
350
|
|
|
347
351
|
/**
|
|
348
352
|
* Called with the next column id → width (px) map when a resize finishes
|
|
349
353
|
* (drag end, double-click restore, or keyboard nudge). Use this to persist
|
|
350
|
-
* widths across sessions.
|
|
354
|
+
* widths across sessions. Select, expander, and reorder utility columns are
|
|
355
|
+
* omitted from the map.
|
|
351
356
|
*/
|
|
352
357
|
onColumnSizingChange?: (columnSizing: Record<string, number>) => void;
|
|
353
358
|
|
|
@@ -363,6 +368,14 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
363
368
|
*/
|
|
364
369
|
expandable?: boolean | ((row: TData) => boolean);
|
|
365
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Width in px of the expander utility column. Ignored when `expandable` is
|
|
373
|
+
* off. Non-finite or `<= 0` falls back to the default. The column is not
|
|
374
|
+
* user-resizable. Wins over a `columnSizing` entry for this column.
|
|
375
|
+
* @default 36
|
|
376
|
+
*/
|
|
377
|
+
expandableColumnWidth?: number;
|
|
378
|
+
|
|
366
379
|
/**
|
|
367
380
|
* Function to render the expanded row
|
|
368
381
|
*/
|
|
@@ -394,6 +407,14 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
394
407
|
*/
|
|
395
408
|
selectable?: boolean | ((rowData: TData) => boolean);
|
|
396
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Width in px of the select utility column. Ignored when `selectable` is
|
|
412
|
+
* off. Non-finite or `<= 0` falls back to the default. The column is not
|
|
413
|
+
* user-resizable. Wins over a `columnSizing` entry for this column.
|
|
414
|
+
* @default 36
|
|
415
|
+
*/
|
|
416
|
+
selectableColumnWidth?: number;
|
|
417
|
+
|
|
397
418
|
/**
|
|
398
419
|
* Whether to show the select/deselect all checkbox in the header
|
|
399
420
|
* @default true
|
|
@@ -474,6 +495,15 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
474
495
|
*/
|
|
475
496
|
reorderable?: boolean;
|
|
476
497
|
|
|
498
|
+
/**
|
|
499
|
+
* Width in px of the reorder utility column. Ignored when `reorderable` is
|
|
500
|
+
* off or the table is virtualized. Non-finite or `<= 0` falls back to the
|
|
501
|
+
* default. The column is not user-resizable. Wins over a `columnSizing`
|
|
502
|
+
* entry for this column.
|
|
503
|
+
* @default 60
|
|
504
|
+
*/
|
|
505
|
+
reorderableColumnWidth?: number;
|
|
506
|
+
|
|
477
507
|
/**
|
|
478
508
|
* Callback when the order of rows changes via drag & drop
|
|
479
509
|
*/
|
|
@@ -19,12 +19,19 @@ import {
|
|
|
19
19
|
collectMissingLeafSizing,
|
|
20
20
|
getColumnSizeCssVars,
|
|
21
21
|
getResizeOriginSize,
|
|
22
|
+
growFillLeavesToContainer,
|
|
23
|
+
isExplicitColumnWidth,
|
|
22
24
|
measureLeafHeaderWidths,
|
|
23
25
|
omitBuiltinColumnSizing,
|
|
26
|
+
shiftColumnTrack,
|
|
27
|
+
withUtilityColumnSizing,
|
|
24
28
|
type ColumnSizeBoundsSource,
|
|
25
29
|
} from '../utils/column-size';
|
|
26
|
-
import { RESIZE_MIN_COLUMN_WIDTH } from '../utils/constants';
|
|
30
|
+
import { RESIZE_DIVIDER_WIDTH, RESIZE_MIN_COLUMN_WIDTH } from '../utils/constants';
|
|
27
31
|
import type { DsTableResizePhase } from '../context/ds-table-context';
|
|
32
|
+
import { useScrollbarSpacer, type ScrollbarSpacerWidth } from './use-scrollbar-spacer';
|
|
33
|
+
|
|
34
|
+
const TRACK_MATCH_PX = 3;
|
|
28
35
|
|
|
29
36
|
const INITIAL_COLUMN_SIZING_INFO: ColumnSizingInfoState = {
|
|
30
37
|
startOffset: null,
|
|
@@ -35,6 +42,14 @@ const INITIAL_COLUMN_SIZING_INFO: ColumnSizingInfoState = {
|
|
|
35
42
|
columnSizingStart: [],
|
|
36
43
|
};
|
|
37
44
|
|
|
45
|
+
const clampResizeOverlayOffset = (offset: number, tableWidth: number): number => {
|
|
46
|
+
if (tableWidth <= RESIZE_DIVIDER_WIDTH) {
|
|
47
|
+
return offset;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Math.min(offset, tableWidth - RESIZE_DIVIDER_WIDTH);
|
|
51
|
+
};
|
|
52
|
+
|
|
38
53
|
export interface UseColumnResizeOptions {
|
|
39
54
|
enabled: boolean;
|
|
40
55
|
/**
|
|
@@ -49,6 +64,15 @@ export interface UseColumnResizeOptions {
|
|
|
49
64
|
*/
|
|
50
65
|
columns: unknown;
|
|
51
66
|
columnVisibility: VisibilityState;
|
|
67
|
+
/**
|
|
68
|
+
* Invalidates overflow measurement (row count). Read as a dependency only.
|
|
69
|
+
*/
|
|
70
|
+
overflowKey: number;
|
|
71
|
+
/**
|
|
72
|
+
* Authored widths for injected utility leaves. Overlaid last so they win
|
|
73
|
+
* over persist seed and live internal state.
|
|
74
|
+
*/
|
|
75
|
+
utilityColumnSizing?: ColumnSizingState;
|
|
52
76
|
}
|
|
53
77
|
|
|
54
78
|
export interface DsTableResizeContextSlice {
|
|
@@ -64,6 +88,10 @@ export interface DsTableResizeContextSlice {
|
|
|
64
88
|
* the persist callback, mirroring drag end.
|
|
65
89
|
*/
|
|
66
90
|
onResizeKeyboardNudge: (columnId: string, delta: number) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Overflow-only **Scrollbar spacer** width (0 or rest scrollbar size).
|
|
93
|
+
*/
|
|
94
|
+
scrollbarSpacerWidth: ScrollbarSpacerWidth;
|
|
67
95
|
}
|
|
68
96
|
|
|
69
97
|
export interface UseColumnResizeActiveResize {
|
|
@@ -92,6 +120,7 @@ export interface UseColumnResizeResult {
|
|
|
92
120
|
columnSizingInfo: ColumnSizingInfoState;
|
|
93
121
|
};
|
|
94
122
|
bind: <TData>(table: Table<TData>) => UseColumnResizeBindResult;
|
|
123
|
+
scrollbarSpacerWidth: ScrollbarSpacerWidth;
|
|
95
124
|
}
|
|
96
125
|
|
|
97
126
|
/**
|
|
@@ -108,6 +137,8 @@ export const useColumnResize = ({
|
|
|
108
137
|
onColumnSizingChange,
|
|
109
138
|
columns,
|
|
110
139
|
columnVisibility,
|
|
140
|
+
overflowKey,
|
|
141
|
+
utilityColumnSizing,
|
|
111
142
|
}: UseColumnResizeOptions): UseColumnResizeResult => {
|
|
112
143
|
const [columnResizeState, setColumnResizeState] = useState<{
|
|
113
144
|
columnSizing: ColumnSizingState;
|
|
@@ -125,10 +156,11 @@ export const useColumnResize = ({
|
|
|
125
156
|
// drag (or seed measurement) of a persisted column still takes effect. Passed
|
|
126
157
|
// to TanStack and read for CSS vars / persist so restore, drag, and persist
|
|
127
158
|
// all see the same widths.
|
|
128
|
-
const columnSizing = useMemo<ColumnSizingState>(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
159
|
+
const columnSizing = useMemo<ColumnSizingState>(() => {
|
|
160
|
+
const merged = columnSizingProp ? { ...columnSizingProp, ...internalColumnSizing } : internalColumnSizing;
|
|
161
|
+
|
|
162
|
+
return withUtilityColumnSizing(merged, utilityColumnSizing ?? {});
|
|
163
|
+
}, [columnSizingProp, internalColumnSizing, utilityColumnSizing]);
|
|
132
164
|
const [resizeHover, setResizeHover] = useState<{
|
|
133
165
|
columnId: string;
|
|
134
166
|
offset: number;
|
|
@@ -136,6 +168,10 @@ export const useColumnResize = ({
|
|
|
136
168
|
const dragStartOffsetRef = useRef<number | null>(null);
|
|
137
169
|
const resizeOriginSizingRef = useRef<ColumnSizingState>({});
|
|
138
170
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
171
|
+
const scrollbarSpacerWidth = useScrollbarSpacer(containerRef, overflowKey);
|
|
172
|
+
const spacerWidthRef = useRef(scrollbarSpacerWidth);
|
|
173
|
+
spacerWidthRef.current = scrollbarSpacerWidth;
|
|
174
|
+
const prevSpacerWidthRef = useRef<ScrollbarSpacerWidth | null>(null);
|
|
139
175
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- bind is generic; the ref only calls sizing APIs
|
|
140
176
|
const tableRef = useRef<Table<any> | null>(null);
|
|
141
177
|
|
|
@@ -235,6 +271,8 @@ export const useColumnResize = ({
|
|
|
235
271
|
);
|
|
236
272
|
|
|
237
273
|
useLayoutEffect(() => {
|
|
274
|
+
prevSpacerWidthRef.current = null;
|
|
275
|
+
|
|
238
276
|
if (!enabled) {
|
|
239
277
|
return;
|
|
240
278
|
}
|
|
@@ -258,6 +296,11 @@ export const useColumnResize = ({
|
|
|
258
296
|
const leaves = table.getVisibleLeafColumns();
|
|
259
297
|
const leafIds = leaves.map((column) => column.id);
|
|
260
298
|
const measured = measureLeafHeaderWidths(tableEl, leafIds);
|
|
299
|
+
const fillableIds = leaves
|
|
300
|
+
.filter((column) => !isExplicitColumnWidth(column, {}))
|
|
301
|
+
.map((column) => column.id);
|
|
302
|
+
const trackWidth = container.clientWidth - spacerWidthRef.current;
|
|
303
|
+
const missing = collectMissingLeafSizing(measured, columnSizingRef.current, trackWidth, fillableIds);
|
|
261
304
|
|
|
262
305
|
for (const column of leaves) {
|
|
263
306
|
const measuredWidth = measured[column.id];
|
|
@@ -265,17 +308,31 @@ export const useColumnResize = ({
|
|
|
265
308
|
continue;
|
|
266
309
|
}
|
|
267
310
|
|
|
268
|
-
resizeOriginSizingRef.current[column.id] = getResizeOriginSize(
|
|
311
|
+
resizeOriginSizingRef.current[column.id] = getResizeOriginSize(
|
|
312
|
+
column,
|
|
313
|
+
missing[column.id] ?? Math.round(measuredWidth),
|
|
314
|
+
);
|
|
269
315
|
}
|
|
270
316
|
|
|
271
|
-
const missing = collectMissingLeafSizing(measured, columnSizingRef.current);
|
|
272
317
|
if (Object.keys(missing).length > 0) {
|
|
318
|
+
const bounds = leafSizeBoundsRef.current;
|
|
319
|
+
const clamped = clampColumnSizing(missing, bounds);
|
|
320
|
+
const grown = growFillLeavesToContainer(
|
|
321
|
+
clamped,
|
|
322
|
+
columnSizingRef.current,
|
|
323
|
+
Object.keys(measured),
|
|
324
|
+
trackWidth,
|
|
325
|
+
fillableIds,
|
|
326
|
+
bounds,
|
|
327
|
+
);
|
|
273
328
|
handleColumnSizingChange((prev) => ({
|
|
274
|
-
...
|
|
329
|
+
...grown,
|
|
275
330
|
...prev,
|
|
276
331
|
}));
|
|
277
332
|
}
|
|
278
333
|
|
|
334
|
+
prevSpacerWidthRef.current = spacerWidthRef.current;
|
|
335
|
+
|
|
279
336
|
return true;
|
|
280
337
|
};
|
|
281
338
|
|
|
@@ -293,6 +350,48 @@ export const useColumnResize = ({
|
|
|
293
350
|
return () => observer.disconnect();
|
|
294
351
|
}, [enabled, columns, columnVisibility, handleColumnSizingChange]);
|
|
295
352
|
|
|
353
|
+
// After seed, a vertical scrollbar can appear or disappear (Infinite Scroll,
|
|
354
|
+
// filter, shorter viewport). The **Scrollbar spacer** then toggles 0↔10px.
|
|
355
|
+
// Shrink or grow the column track by that delta so leaves + spacer still
|
|
356
|
+
// fill the container — internally only, never via onColumnSizingChange.
|
|
357
|
+
// Skip when the leaves were not filling the previous track (already
|
|
358
|
+
// overflowing horizontally) so authored-wide columns are left alone.
|
|
359
|
+
useLayoutEffect(() => {
|
|
360
|
+
if (!enabled) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const prevSpacerWidth = prevSpacerWidthRef.current;
|
|
365
|
+
if (prevSpacerWidth === null || prevSpacerWidth === scrollbarSpacerWidth) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const container = containerRef.current;
|
|
370
|
+
const table = tableRef.current;
|
|
371
|
+
if (!container || !table) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const leafIds = table.getVisibleLeafColumns().map((column) => column.id);
|
|
376
|
+
let leafTotal = 0;
|
|
377
|
+
|
|
378
|
+
for (const id of leafIds) {
|
|
379
|
+
leafTotal += columnSizingRef.current[id] ?? 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const prevTrack = container.clientWidth - prevSpacerWidth;
|
|
383
|
+
prevSpacerWidthRef.current = scrollbarSpacerWidth;
|
|
384
|
+
|
|
385
|
+
if (Math.abs(leafTotal - prevTrack) > TRACK_MATCH_PX) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const delta = prevSpacerWidth - scrollbarSpacerWidth;
|
|
390
|
+
handleColumnSizingChange(
|
|
391
|
+
shiftColumnTrack(columnSizingRef.current, leafIds, delta, leafSizeBoundsRef.current),
|
|
392
|
+
);
|
|
393
|
+
}, [enabled, scrollbarSpacerWidth, handleColumnSizingChange]);
|
|
394
|
+
|
|
296
395
|
const tableOptions = useMemo<UseColumnResizeTableOptions>(
|
|
297
396
|
() => ({
|
|
298
397
|
enableColumnResizing: enabled,
|
|
@@ -319,7 +418,7 @@ export const useColumnResize = ({
|
|
|
319
418
|
const columnSizeVars: CSSProperties | undefined = resizeSizingReady
|
|
320
419
|
? {
|
|
321
420
|
...getColumnSizeCssVars(table.getFlatHeaders()),
|
|
322
|
-
width: table.getTotalSize(),
|
|
421
|
+
width: table.getTotalSize() + scrollbarSpacerWidth,
|
|
323
422
|
}
|
|
324
423
|
: undefined;
|
|
325
424
|
|
|
@@ -335,6 +434,7 @@ export const useColumnResize = ({
|
|
|
335
434
|
onResizeDragStart: handleResizeDragStart,
|
|
336
435
|
onResizeReset: handleResizeReset,
|
|
337
436
|
onResizeKeyboardNudge: handleResizeKeyboardNudge,
|
|
437
|
+
scrollbarSpacerWidth,
|
|
338
438
|
},
|
|
339
439
|
};
|
|
340
440
|
}
|
|
@@ -349,14 +449,15 @@ export const useColumnResize = ({
|
|
|
349
449
|
resizingHeader && startSize !== null
|
|
350
450
|
? resizingHeader.getSize() - startSize
|
|
351
451
|
: (columnSizingInfo.deltaOffset ?? 0);
|
|
452
|
+
const tableWidth = table.getTotalSize();
|
|
352
453
|
const activeResize: UseColumnResizeActiveResize | null =
|
|
353
454
|
isResizingColumn && dragStartOffsetRef.current !== null
|
|
354
455
|
? {
|
|
355
|
-
offset: dragStartOffsetRef.current + clampedResizeDelta,
|
|
456
|
+
offset: clampResizeOverlayOffset(dragStartOffsetRef.current + clampedResizeDelta, tableWidth),
|
|
356
457
|
phase: 'dragging',
|
|
357
458
|
}
|
|
358
459
|
: resizeHover
|
|
359
|
-
? { offset: resizeHover.offset, phase: 'hover' }
|
|
460
|
+
? { offset: clampResizeOverlayOffset(resizeHover.offset, tableWidth), phase: 'hover' }
|
|
360
461
|
: null;
|
|
361
462
|
|
|
362
463
|
return {
|
|
@@ -370,6 +471,7 @@ export const useColumnResize = ({
|
|
|
370
471
|
onResizeDragStart: handleResizeDragStart,
|
|
371
472
|
onResizeReset: handleResizeReset,
|
|
372
473
|
onResizeKeyboardNudge: handleResizeKeyboardNudge,
|
|
474
|
+
scrollbarSpacerWidth,
|
|
373
475
|
},
|
|
374
476
|
};
|
|
375
477
|
};
|
|
@@ -381,5 +483,6 @@ export const useColumnResize = ({
|
|
|
381
483
|
columnSizingInfo,
|
|
382
484
|
},
|
|
383
485
|
bind,
|
|
486
|
+
scrollbarSpacerWidth,
|
|
384
487
|
};
|
|
385
488
|
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useLayoutEffect, useState, type RefObject } from 'react';
|
|
2
|
+
import { SCROLLBAR_SPACER_WIDTH } from '../utils/constants';
|
|
3
|
+
|
|
4
|
+
export type ScrollbarSpacerWidth = 0 | typeof SCROLLBAR_SPACER_WIDTH;
|
|
5
|
+
|
|
6
|
+
const BODY_SCROLLBAR_COMPENSATION_CSS_VAR = '--ds-table-body-scrollbar-compensation';
|
|
7
|
+
|
|
8
|
+
const bodyOverflowsVertically = (body: HTMLElement): boolean => body.scrollHeight > body.clientHeight;
|
|
9
|
+
|
|
10
|
+
const verticalScrollbarOccupancy = (body: HTMLElement): number =>
|
|
11
|
+
Math.max(0, body.offsetWidth - body.clientWidth);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extra `padding-inline-end` on body rows so Fill columns match the header
|
|
15
|
+
* **Scrollbar spacer** when the vertical scrollbar occupies no layout (overlay).
|
|
16
|
+
* Classic scrollbars already inset `clientWidth`; do not add that occupancy twice.
|
|
17
|
+
*/
|
|
18
|
+
export const getBodyScrollbarCompensation = (
|
|
19
|
+
occupancyPx: number,
|
|
20
|
+
spacerWidth: ScrollbarSpacerWidth,
|
|
21
|
+
): number => Math.max(0, spacerWidth - occupancyPx);
|
|
22
|
+
|
|
23
|
+
const syncBodyScrollbarCompensation = (
|
|
24
|
+
container: HTMLElement,
|
|
25
|
+
body: HTMLElement | null,
|
|
26
|
+
spacerWidth: ScrollbarSpacerWidth,
|
|
27
|
+
): void => {
|
|
28
|
+
if (!body) {
|
|
29
|
+
container.style.removeProperty(BODY_SCROLLBAR_COMPENSATION_CSS_VAR);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
container.style.setProperty(
|
|
34
|
+
BODY_SCROLLBAR_COMPENSATION_CSS_VAR,
|
|
35
|
+
`${String(getBodyScrollbarCompensation(verticalScrollbarOccupancy(body), spacerWidth))}px`,
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Overflow-only **Scrollbar spacer** width: `SCROLLBAR_SPACER_WIDTH` while the
|
|
41
|
+
* body scroll container overflows vertically, otherwise 0.
|
|
42
|
+
*
|
|
43
|
+
* Observes container/tbody size and tbody children (rows added/removed). Does
|
|
44
|
+
* not watch `style` attributes — virtualized rows rewrite `transform` on scroll.
|
|
45
|
+
*/
|
|
46
|
+
export const useScrollbarSpacer = (
|
|
47
|
+
containerRef: RefObject<HTMLElement | null>,
|
|
48
|
+
overflowKey: number,
|
|
49
|
+
): ScrollbarSpacerWidth => {
|
|
50
|
+
const [width, setWidth] = useState<ScrollbarSpacerWidth>(0);
|
|
51
|
+
|
|
52
|
+
useLayoutEffect(() => {
|
|
53
|
+
const container = containerRef.current;
|
|
54
|
+
if (!container) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const read = () => {
|
|
59
|
+
const body = container.querySelector('tbody');
|
|
60
|
+
if (!(body instanceof HTMLElement)) {
|
|
61
|
+
setWidth(0);
|
|
62
|
+
syncBodyScrollbarCompensation(container, null, 0);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const nextWidth = bodyOverflowsVertically(body) ? SCROLLBAR_SPACER_WIDTH : 0;
|
|
67
|
+
setWidth(nextWidth);
|
|
68
|
+
syncBodyScrollbarCompensation(container, body, nextWidth);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const resizeObserver = new ResizeObserver(read);
|
|
72
|
+
const bodyChildObserver = new MutationObserver(read);
|
|
73
|
+
resizeObserver.observe(container);
|
|
74
|
+
|
|
75
|
+
let observedBody: HTMLElement | null = null;
|
|
76
|
+
|
|
77
|
+
const bindBody = () => {
|
|
78
|
+
const body = container.querySelector('tbody');
|
|
79
|
+
if (!(body instanceof HTMLElement) || body === observedBody) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
bodyChildObserver.disconnect();
|
|
84
|
+
observedBody = body;
|
|
85
|
+
resizeObserver.observe(body);
|
|
86
|
+
bodyChildObserver.observe(body, { childList: true });
|
|
87
|
+
read();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
bindBody();
|
|
91
|
+
read();
|
|
92
|
+
|
|
93
|
+
const table = container.querySelector('table');
|
|
94
|
+
const tableChildObserver = new MutationObserver(bindBody);
|
|
95
|
+
if (table) {
|
|
96
|
+
tableChildObserver.observe(table, { childList: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
resizeObserver.disconnect();
|
|
101
|
+
bodyChildObserver.disconnect();
|
|
102
|
+
tableChildObserver.disconnect();
|
|
103
|
+
container.style.removeProperty(BODY_SCROLLBAR_COMPENSATION_CSS_VAR);
|
|
104
|
+
};
|
|
105
|
+
}, [containerRef, overflowKey]);
|
|
106
|
+
|
|
107
|
+
return width;
|
|
108
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { SCROLLBAR_SPACER_WIDTH } from '../utils/constants';
|
|
3
|
+
import { getBodyScrollbarCompensation } from './use-scrollbar-spacer';
|
|
4
|
+
|
|
5
|
+
describe('getBodyScrollbarCompensation', () => {
|
|
6
|
+
it('reserves the full spacer when the scrollbar occupies no layout (overlay)', () => {
|
|
7
|
+
expect(getBodyScrollbarCompensation(0, SCROLLBAR_SPACER_WIDTH)).toBe(SCROLLBAR_SPACER_WIDTH);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('adds nothing when a classic scrollbar already occupies the spacer width', () => {
|
|
11
|
+
expect(getBodyScrollbarCompensation(SCROLLBAR_SPACER_WIDTH, SCROLLBAR_SPACER_WIDTH)).toBe(0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('fills only the gap when a thinner scrollbar occupies part of the spacer', () => {
|
|
15
|
+
expect(getBodyScrollbarCompensation(8, SCROLLBAR_SPACER_WIDTH)).toBe(2);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('does not shrink the body when occupancy is wider than the spacer', () => {
|
|
19
|
+
expect(getBodyScrollbarCompensation(15, SCROLLBAR_SPACER_WIDTH)).toBe(0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('adds nothing when the spacer is collapsed', () => {
|
|
23
|
+
expect(getBodyScrollbarCompensation(0, 0)).toBe(0);
|
|
24
|
+
expect(getBodyScrollbarCompensation(SCROLLBAR_SPACER_WIDTH, 0)).toBe(0);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
$border-radius: 4px;
|
|
2
2
|
$border-width: 1px;
|
|
3
3
|
$group-border-width: 2px;
|
|
4
|
-
$reorder-column-width: 60px;
|
|
5
4
|
$container-margin-vertical: 1rem;
|
|
6
5
|
$filter-input-gap: 0.5rem;
|
|
7
6
|
$filter-input-max-width: 24rem;
|
|
8
7
|
$header-sort-gap: 0.5rem;
|
|
9
|
-
$empty-state-height: 6rem;
|
|
10
8
|
$resize-handle-hit-width: 12px;
|
|
11
9
|
$resize-divider-width: 2px;
|
|
12
10
|
|