@drivenets/design-system 0.13.0 → 0.15.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 +29 -0
- package/README.md +1 -1
- package/dist/index.cjs +1155 -381
- package/dist/index.d.cts +371 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +371 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1151 -381
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +16 -13
- package/src/components/ds-button-v3/ds-button-v3.figma.ts +99 -0
- package/src/components/ds-button-v3/ds-button-v3.module.scss +22 -22
- package/src/components/ds-card/figma/ds-card-content.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card-footer.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card-header.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card.figma.ts +72 -0
- package/src/components/ds-date-picker/ds-date-picker.figma.ts +19 -0
- package/src/components/ds-drawer/ds-drawer.module.scss +14 -0
- package/src/components/ds-drawer/ds-drawer.tsx +20 -1
- package/src/components/ds-drawer/ds-drawer.types.ts +7 -0
- package/src/components/ds-file-upload/hooks/use-file-upload.ts +7 -0
- package/src/components/ds-form-control/ds-form-control.figma.ts +76 -0
- package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +10 -0
- package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +18 -0
- package/src/components/ds-icon/figma/ds-icon.figma.batch.json +2873 -0
- package/src/components/ds-modal/figma/ds-modal-content.figma.ts +18 -0
- package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +19 -0
- package/src/components/ds-modal/figma/ds-modal-header.figma.ts +21 -0
- package/src/components/ds-modal/figma/ds-modal.figma.ts +36 -0
- package/src/components/ds-popover/ds-popover.module.scss +58 -16
- package/src/components/ds-popover/ds-popover.tsx +148 -24
- package/src/components/ds-popover/ds-popover.types.ts +95 -12
- package/src/components/ds-popover/index.ts +1 -1
- package/src/components/ds-progress-task-bar/ds-progress-task-bar.figma.ts +29 -0
- package/src/components/ds-progress-task-bar/ds-progress-task-bar.module.scss +39 -0
- package/src/components/ds-progress-task-bar/ds-progress-task-bar.tsx +122 -0
- package/src/components/ds-progress-task-bar/ds-progress-task-bar.types.ts +65 -0
- package/src/components/ds-progress-task-bar/index.ts +2 -0
- package/src/components/ds-progress-task-bar/utils.ts +51 -0
- package/src/components/ds-select/ds-select.figma.ts +53 -0
- package/src/components/ds-split-button/ds-split-button.figma.ts +95 -0
- package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +41 -0
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +8 -3
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +31 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +48 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.types.ts +12 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +2 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +4 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +55 -39
- package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +5 -1
- package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +4 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +5 -1
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +4 -0
- package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.module.scss +14 -2
- package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +19 -0
- package/src/components/ds-table/context/ds-table-context.ts +4 -0
- package/src/components/ds-table/ds-table-column.types.ts +24 -0
- package/src/components/ds-table/ds-table.tsx +48 -6
- package/src/components/ds-table/ds-table.types.ts +62 -0
- package/src/components/ds-table/figma/ds-table-expandable.figma.ts +17 -0
- package/src/components/ds-table/figma/ds-table.figma.ts +16 -0
- package/src/components/ds-table/grouping/hooks/use-column-groups.ts +73 -0
- package/src/components/ds-table/grouping/index.ts +5 -0
- package/src/components/ds-table/grouping/types/column-group-meta.augmentation.ts +26 -0
- package/src/components/ds-table/grouping/types/column-group.types.ts +35 -0
- package/src/components/ds-table/grouping/utils/column-group.ts +119 -0
- package/src/components/ds-table/index.ts +4 -0
- package/src/components/ds-table/styles/_variables.scss +1 -0
- package/src/components/ds-table/utils/constants.ts +5 -0
- package/src/components/ds-tabs/figma/ds-tabs-tab-vertical.figma.ts +78 -0
- package/src/components/ds-tabs/figma/ds-tabs-tab.figma.ts +89 -0
- package/src/components/ds-tabs/figma/ds-tabs-vertical.figma.ts +31 -0
- package/src/components/ds-tabs/figma/ds-tabs.figma.ts +29 -0
- package/src/components/ds-tag/ds-tag.module.scss +22 -1
- package/src/components/ds-tag/ds-tag.tsx +16 -3
- package/src/components/ds-tag/ds-tag.types.ts +22 -8
- package/src/components/ds-tag/ds-tag.unit.test.ts +32 -0
- package/src/components/ds-tag-filter/ds-tag-filter.tsx +14 -22
- package/src/components/ds-text-input/ds-text-input.figma.ts +60 -0
- package/src/components/ds-time-picker/ds-time-picker.figma.ts +18 -0
- package/src/index.ts +1 -0
- package/src/styles/_root_new.scss +2 -0
- package/src/styles/_scrollbars.scss +27 -50
package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import classnames from 'classnames';
|
|
|
2
2
|
import styles from './ds-table-row-virtualized.module.scss';
|
|
3
3
|
import { useDsTableContext } from '../../context/ds-table-context';
|
|
4
4
|
import { getColumnSizeStyle } from '../../utils/column-size';
|
|
5
|
+
import { isFirstLeafColumnOfGroup } from '../../grouping';
|
|
5
6
|
import { TableCell, TableRow } from '../core-table';
|
|
6
7
|
import type { DsTableRowVirtualizedProps } from './ds-table-row-virtualized.types';
|
|
7
8
|
import { DsTableCell } from '../ds-table-cell';
|
|
@@ -25,6 +26,7 @@ export const DsTableRowVirtualized = <TData,>({
|
|
|
25
26
|
renderExpandedRow,
|
|
26
27
|
onRowClick,
|
|
27
28
|
onRowDoubleClick,
|
|
29
|
+
loading,
|
|
28
30
|
} = useDsTableContext<TData, unknown>();
|
|
29
31
|
const isExpanded = row.getIsExpanded();
|
|
30
32
|
const isActive = activeRowId === row.id;
|
|
@@ -57,6 +59,7 @@ export const DsTableRowVirtualized = <TData,>({
|
|
|
57
59
|
onRowClick && styles.clickableRow,
|
|
58
60
|
!bordered && styles.rowNoBorder,
|
|
59
61
|
isExpanded && styles.expanded,
|
|
62
|
+
loading && styles.loadingRow,
|
|
60
63
|
)}
|
|
61
64
|
onClick={isExpandedRowContent ? undefined : () => onRowClick?.(row.original)}
|
|
62
65
|
onDoubleClick={isExpandedRowContent ? undefined : () => onRowDoubleClick?.(row.original)}
|
|
@@ -77,6 +80,7 @@ export const DsTableRowVirtualized = <TData,>({
|
|
|
77
80
|
styles.cell,
|
|
78
81
|
cell.column.id === EXPANDER_COLUMN_ID && styles.expandableCell,
|
|
79
82
|
cell.column.id === SELECT_COLUMN_ID && styles.selectableCell,
|
|
83
|
+
isFirstLeafColumnOfGroup(cell.column) && styles.groupStart,
|
|
80
84
|
typeof cell.column.columnDef.editCell === 'function' && styles.editableCell,
|
|
81
85
|
)}
|
|
82
86
|
>
|
|
@@ -11,18 +11,30 @@
|
|
|
11
11
|
outline: none;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
.editButton
|
|
14
|
+
.editButton,
|
|
15
|
+
.lockIcon {
|
|
15
16
|
flex-shrink: 0;
|
|
16
17
|
opacity: 0;
|
|
17
18
|
pointer-events: none;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
.lockIcon {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
&:hover,
|
|
21
28
|
&:focus-within,
|
|
22
29
|
&[data-active] {
|
|
23
|
-
.editButton
|
|
30
|
+
.editButton,
|
|
31
|
+
.lockIcon {
|
|
24
32
|
opacity: 1;
|
|
25
33
|
pointer-events: auto;
|
|
26
34
|
}
|
|
27
35
|
}
|
|
36
|
+
|
|
37
|
+
&[data-locked] {
|
|
38
|
+
cursor: default;
|
|
39
|
+
}
|
|
28
40
|
}
|
package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { MouseEvent } from 'react';
|
|
2
2
|
import { DsButtonV3 } from '../../../../ds-button-v3';
|
|
3
|
+
import { DsIcon } from '../../../../ds-icon';
|
|
4
|
+
import { DsTooltip } from '../../../../ds-tooltip';
|
|
3
5
|
import styles from './ds-table-editable-cell.module.scss';
|
|
4
6
|
import type { DsTableEditableCellProps } from './ds-table-editable-cell.types';
|
|
5
7
|
import { useDsTableContext } from '../../../context/ds-table-context';
|
|
@@ -16,6 +18,23 @@ export const DsTableEditableCell = <TData, TValue>({
|
|
|
16
18
|
beginEdit(cell);
|
|
17
19
|
};
|
|
18
20
|
|
|
21
|
+
const editDisabled = cell.column.columnDef.editDisabled?.(cell.getContext());
|
|
22
|
+
const locked = editDisabled === true || typeof editDisabled === 'object';
|
|
23
|
+
const reason = typeof editDisabled === 'object' ? editDisabled.reason : undefined;
|
|
24
|
+
|
|
25
|
+
if (locked) {
|
|
26
|
+
return (
|
|
27
|
+
<div className={styles.root} data-locked="">
|
|
28
|
+
{children}
|
|
29
|
+
<DsTooltip content={reason}>
|
|
30
|
+
<span className={styles.lockIcon} aria-label="Editing disabled" role="img">
|
|
31
|
+
<DsIcon icon="lock" size="small" color="secondary" />
|
|
32
|
+
</span>
|
|
33
|
+
</DsTooltip>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
19
38
|
return (
|
|
20
39
|
<div
|
|
21
40
|
className={styles.root}
|
|
@@ -23,6 +23,10 @@ export interface DsTableContextType<TData, TValue> extends Partial<DsDataTablePr
|
|
|
23
23
|
* ID of the currently active row
|
|
24
24
|
*/
|
|
25
25
|
activeRowId?: string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Toggles the collapsed state of a column group by its id.
|
|
28
|
+
*/
|
|
29
|
+
onToggleColumnGroup?: (groupId: string) => void;
|
|
26
30
|
/** The cell currently in edit mode, if any. */
|
|
27
31
|
editing: EditingState<TData, TValue> | null;
|
|
28
32
|
beginEdit: (cell: Cell<TData, TValue>) => void;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { CellContext, RowData } from '@tanstack/react-table';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Result of a column's `editDisabled` resolver for a single cell.
|
|
6
|
+
*
|
|
7
|
+
* - `false` (or `undefined`) keeps the cell editable.
|
|
8
|
+
* - `true` locks the cell with no tooltip.
|
|
9
|
+
* - `{ reason }` locks the cell and shows `reason` in a tooltip on hover.
|
|
10
|
+
*/
|
|
11
|
+
export type DsTableEditDisabledResult = boolean | { reason?: ReactNode };
|
|
12
|
+
|
|
4
13
|
declare module '@tanstack/react-table' {
|
|
5
14
|
interface ColumnDefBase<TData extends RowData, TValue> {
|
|
6
15
|
/**
|
|
@@ -8,5 +17,20 @@ declare module '@tanstack/react-table' {
|
|
|
8
17
|
* When omitted, the column is not editable.
|
|
9
18
|
*/
|
|
10
19
|
editCell?: (context: CellContext<TData, TValue>) => ReactNode;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Locks individual cells of an otherwise editable column (one that defines
|
|
23
|
+
* `editCell`). When this returns a truthy value for a cell, that cell shows a
|
|
24
|
+
* lock icon instead of the edit affordance and cannot enter edit mode. Return
|
|
25
|
+
* `{ reason }` to surface an optional tooltip explaining why. Has no effect on
|
|
26
|
+
* columns without `editCell`.
|
|
27
|
+
*/
|
|
28
|
+
editDisabled?: (context: CellContext<TData, TValue>) => DsTableEditDisabledResult;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Renders this column's cell content while the table is `loading`.
|
|
32
|
+
* When omitted, a default text skeleton is rendered.
|
|
33
|
+
*/
|
|
34
|
+
loadingCell?: (context: CellContext<TData, TValue>) => ReactNode;
|
|
11
35
|
}
|
|
12
36
|
}
|
|
@@ -25,6 +25,8 @@ import { DsTableHeaderSelectableCell } from './components/ds-table-header-select
|
|
|
25
25
|
import { useDragAndDrop } from './hooks/use-drag-and-drop';
|
|
26
26
|
import { type DsTableContextType, DsTableContext, useEditingState } from './context/ds-table-context';
|
|
27
27
|
import { DsTableBodyVirtualized } from './components/ds-table-body-virtualized';
|
|
28
|
+
import { useColumnGroups } from './grouping';
|
|
29
|
+
import { DsSkeletonText } from '../ds-skeleton';
|
|
28
30
|
import {
|
|
29
31
|
EMPTY_TABLE_STATE_TEXT,
|
|
30
32
|
EXPANDER_COLUMN_ID,
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
REORDER_COLUMN_WIDTH,
|
|
34
36
|
SELECT_COLUMN_ID,
|
|
35
37
|
SELECT_COLUMN_WIDTH,
|
|
38
|
+
SKELETON_ROW_COUNT,
|
|
36
39
|
} from './utils/constants';
|
|
37
40
|
|
|
38
41
|
// Row size to pixel height mapping (matches CSS variables)
|
|
@@ -58,6 +61,7 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
58
61
|
bordered = true,
|
|
59
62
|
fullWidth = true,
|
|
60
63
|
rowSize = 'medium',
|
|
64
|
+
loading = false,
|
|
61
65
|
expandable = false,
|
|
62
66
|
renderExpandedRow,
|
|
63
67
|
selectable = false,
|
|
@@ -75,6 +79,9 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
75
79
|
onColumnFiltersChange,
|
|
76
80
|
columnVisibility: externalColumnVisibility,
|
|
77
81
|
onColumnVisibilityChange,
|
|
82
|
+
collapsedColumnGroups: externalCollapsedColumnGroups,
|
|
83
|
+
onCollapsedColumnGroupsChange,
|
|
84
|
+
locale,
|
|
78
85
|
activeRowId,
|
|
79
86
|
infiniteScroll,
|
|
80
87
|
onCellEdit,
|
|
@@ -109,12 +116,12 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
109
116
|
}
|
|
110
117
|
};
|
|
111
118
|
|
|
112
|
-
const
|
|
119
|
+
const baseColumnVisibility = externalColumnVisibility ?? internalColumnVisibility;
|
|
113
120
|
const handleColumnVisibilityChange = (
|
|
114
121
|
updaterOrValue: VisibilityState | ((old: VisibilityState) => VisibilityState),
|
|
115
122
|
) => {
|
|
116
123
|
const newVisibility =
|
|
117
|
-
typeof updaterOrValue === 'function' ? updaterOrValue(
|
|
124
|
+
typeof updaterOrValue === 'function' ? updaterOrValue(baseColumnVisibility) : updaterOrValue;
|
|
118
125
|
|
|
119
126
|
if (onColumnVisibilityChange) {
|
|
120
127
|
onColumnVisibilityChange(newVisibility);
|
|
@@ -123,6 +130,17 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
123
130
|
}
|
|
124
131
|
};
|
|
125
132
|
|
|
133
|
+
const { collapsedColumnGroups, toggleColumnGroup, collapsedVisibility } = useColumnGroups({
|
|
134
|
+
columns: columnsProp,
|
|
135
|
+
collapsedColumnGroups: externalCollapsedColumnGroups,
|
|
136
|
+
onCollapsedColumnGroupsChange,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const columnVisibility = useMemo(
|
|
140
|
+
() => ({ ...baseColumnVisibility, ...collapsedVisibility }),
|
|
141
|
+
[baseColumnVisibility, collapsedVisibility],
|
|
142
|
+
);
|
|
143
|
+
|
|
126
144
|
const handleSortingChange = (updaterOrValue: SortingState | ((old: SortingState) => SortingState)) => {
|
|
127
145
|
const newSorting = typeof updaterOrValue === 'function' ? updaterOrValue(sorting) : updaterOrValue;
|
|
128
146
|
setSorting(newSorting);
|
|
@@ -186,9 +204,29 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
186
204
|
return augmentedColumns;
|
|
187
205
|
}, [columnsProp, hasExpanderColumn, hasReorderColumn, hasSelectColumn, showSelectAllCheckbox]);
|
|
188
206
|
|
|
207
|
+
const skeletonColumns = useMemo<ColumnDef<TData, TValue>[]>(() => {
|
|
208
|
+
const toSkeleton = (cols: ColumnDef<TData, TValue>[]): ColumnDef<TData, TValue>[] =>
|
|
209
|
+
cols.map((column) =>
|
|
210
|
+
'columns' in column && column.columns
|
|
211
|
+
? { ...column, columns: toSkeleton(column.columns) }
|
|
212
|
+
: { ...column, cell: column.loadingCell ?? (() => <DsSkeletonText width="60%" />) },
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return toSkeleton(columns);
|
|
216
|
+
}, [columns]);
|
|
217
|
+
|
|
218
|
+
const skeletonData = useMemo<TData[]>(
|
|
219
|
+
() =>
|
|
220
|
+
Array.from(
|
|
221
|
+
{ length: SKELETON_ROW_COUNT },
|
|
222
|
+
(_, i) => ({ id: `ds-table-skeleton-${String(i)}` }) as TData,
|
|
223
|
+
),
|
|
224
|
+
[],
|
|
225
|
+
);
|
|
226
|
+
|
|
189
227
|
const table = useReactTable({
|
|
190
|
-
data: reorderable ? data : tableData,
|
|
191
|
-
columns,
|
|
228
|
+
data: loading ? skeletonData : reorderable ? data : tableData,
|
|
229
|
+
columns: loading ? skeletonColumns : columns,
|
|
192
230
|
getCoreRowModel: getCoreRowModel(),
|
|
193
231
|
onSortingChange: handleSortingChange,
|
|
194
232
|
getSortedRowModel: getSortedRowModel(),
|
|
@@ -280,13 +318,17 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
280
318
|
selectable,
|
|
281
319
|
reorderable,
|
|
282
320
|
showSelectAllCheckbox,
|
|
283
|
-
|
|
284
|
-
|
|
321
|
+
loading,
|
|
322
|
+
onRowClick: loading ? undefined : onRowClick,
|
|
323
|
+
onRowDoubleClick: loading ? undefined : onRowDoubleClick,
|
|
285
324
|
primaryRowActions,
|
|
286
325
|
secondaryRowActions,
|
|
287
326
|
renderExpandedRow,
|
|
288
327
|
virtualized,
|
|
289
328
|
activeRowId,
|
|
329
|
+
collapsedColumnGroups,
|
|
330
|
+
onToggleColumnGroup: toggleColumnGroup,
|
|
331
|
+
locale,
|
|
290
332
|
onCellEdit,
|
|
291
333
|
...editingState,
|
|
292
334
|
};
|
|
@@ -16,6 +16,31 @@ import type { InfiniteScrollConfig, ScrollParams } from './components/ds-table-b
|
|
|
16
16
|
*/
|
|
17
17
|
export type DsTableRowSize = 'small' | 'medium' | 'large';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Overridable, user-facing strings used by the table (e.g. accessible labels for
|
|
21
|
+
* the column-group collapse toggle). Pass a partial object via the `locale` prop
|
|
22
|
+
* to localize.
|
|
23
|
+
*/
|
|
24
|
+
export interface DsTableLocale {
|
|
25
|
+
/**
|
|
26
|
+
* Accessible label for the toggle that collapses a column group.
|
|
27
|
+
*/
|
|
28
|
+
collapseColumnGroup: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Accessible label for the toggle that expands a column group.
|
|
32
|
+
*/
|
|
33
|
+
expandColumnGroup: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Default English strings for {@link DsTableLocale}.
|
|
38
|
+
*/
|
|
39
|
+
export const defaultDsTableLocale: DsTableLocale = Object.freeze({
|
|
40
|
+
collapseColumnGroup: 'Collapse column group',
|
|
41
|
+
expandColumnGroup: 'Expand column group',
|
|
42
|
+
});
|
|
43
|
+
|
|
19
44
|
/**
|
|
20
45
|
* API for programmatically controlling a DsTable component.
|
|
21
46
|
* This interface provides methods to interact with the table's selection, sorting, filtering, and pagination features.
|
|
@@ -289,6 +314,13 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
289
314
|
*/
|
|
290
315
|
rowSize?: DsTableRowSize;
|
|
291
316
|
|
|
317
|
+
/**
|
|
318
|
+
* When `true`, renders placeholder skeleton rows instead of the data. Column
|
|
319
|
+
* headers stay intact and row interactions are disabled while loading.
|
|
320
|
+
* @default false
|
|
321
|
+
*/
|
|
322
|
+
loading?: boolean;
|
|
323
|
+
|
|
292
324
|
/**
|
|
293
325
|
* Whether the table is expandable or if an individual row should be expandable
|
|
294
326
|
*/
|
|
@@ -442,6 +474,36 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
442
474
|
*/
|
|
443
475
|
onColumnVisibilityChange?: (visibility: VisibilityState) => void;
|
|
444
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Controlled list of collapsed column-group ids. A group is collapsible when its
|
|
479
|
+
* column def sets `meta.group.collapsible`; collapsing hides every leaf column in
|
|
480
|
+
* the group except those marked `meta.keepVisibleWhenCollapsed`. Omit for
|
|
481
|
+
* uncontrolled behavior (seeded from `meta.group.defaultCollapsed`).
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```tsx
|
|
485
|
+
* const [collapsed, setCollapsed] = useState<string[]>(['equipment']);
|
|
486
|
+
*
|
|
487
|
+
* <DsTable
|
|
488
|
+
* collapsedColumnGroups={collapsed}
|
|
489
|
+
* onCollapsedColumnGroupsChange={setCollapsed}
|
|
490
|
+
* />
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
collapsedColumnGroups?: string[];
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Callback fired with the next list of collapsed group ids when a group's
|
|
497
|
+
* collapse toggle is clicked.
|
|
498
|
+
*/
|
|
499
|
+
onCollapsedColumnGroupsChange?: (collapsedGroupIds: string[]) => void;
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Overrides for user-facing strings (e.g. column-group toggle labels). Merged
|
|
503
|
+
* over the built-in English defaults.
|
|
504
|
+
*/
|
|
505
|
+
locale?: Partial<DsTableLocale>;
|
|
506
|
+
|
|
445
507
|
/**
|
|
446
508
|
* Callback fired when an editable cell commits a new value via one of the
|
|
447
509
|
* `DsTableEditCell*` wrappers or the `useCellEditor` hook. The parent is
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=36535-44213
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-table
|
|
3
|
+
// component=DsTable
|
|
4
|
+
//
|
|
5
|
+
// `DAP_Table Expandable_v03` is the expandable table on the "Table New (Data grid)"
|
|
6
|
+
// page. It maps to the same `DsTable` code component as the simple table, with the
|
|
7
|
+
// `expandable` prop enabled and a `renderExpandedRow` callback for the nested content.
|
|
8
|
+
// The Simple vs Expandable split exists only because of a Figma restriction (a
|
|
9
|
+
// component can't contain an instance of itself); in code it's one `DsTable`.
|
|
10
|
+
import figma from 'figma';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
example: figma.code`<DsTable columns={columns} data={data} expandable renderExpandedRow={(row) => null} />`,
|
|
14
|
+
imports: ["import { DsTable } from '@drivenets/design-system'"],
|
|
15
|
+
id: 'ds-table-expandable',
|
|
16
|
+
metadata: { nestable: false },
|
|
17
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=36354-1965
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-table
|
|
3
|
+
// component=DsTable
|
|
4
|
+
//
|
|
5
|
+
// `DAP_Table Simple_v03` is the non-expandable table on the "Table New (Data grid)"
|
|
6
|
+
// page. Figma models the table as separate components (header/row/cell),
|
|
7
|
+
// while code exposes a single declarative `DsTable` driven by `columns`/`data`,
|
|
8
|
+
// so the snippet is a representative usage that references those consts.
|
|
9
|
+
import figma from 'figma';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
example: figma.code`<DsTable columns={columns} data={data} />`,
|
|
13
|
+
imports: ["import { DsTable } from '@drivenets/design-system'"],
|
|
14
|
+
id: 'ds-table',
|
|
15
|
+
metadata: { nestable: false },
|
|
16
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import type { ColumnDef, VisibilityState } from '@tanstack/react-table';
|
|
3
|
+
import { useControlled } from '../../../../utils/use-controlled';
|
|
4
|
+
import { getCollapsedColumnVisibility, getInitialCollapsedGroups } from '../utils/column-group';
|
|
5
|
+
|
|
6
|
+
interface UseColumnGroupsParams<TData, TValue> {
|
|
7
|
+
/**
|
|
8
|
+
* Consumer-provided columns (before utility columns are injected). Group defs
|
|
9
|
+
* nest child columns via `columns`.
|
|
10
|
+
*/
|
|
11
|
+
columns: ColumnDef<TData, TValue>[];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Controlled list of collapsed group ids. When provided, the table no longer
|
|
15
|
+
* manages collapse state internally.
|
|
16
|
+
*/
|
|
17
|
+
collapsedColumnGroups?: string[];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Called with the next list of collapsed group ids whenever a group is toggled.
|
|
21
|
+
*/
|
|
22
|
+
onCollapsedColumnGroupsChange?: (collapsedGroupIds: string[]) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface UseColumnGroupsResult {
|
|
26
|
+
/**
|
|
27
|
+
* The effective list of collapsed group ids (controlled or internal).
|
|
28
|
+
*/
|
|
29
|
+
collapsedColumnGroups: string[];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Toggles a single group's collapsed state.
|
|
33
|
+
*/
|
|
34
|
+
toggleColumnGroup: (groupId: string) => void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Visibility overrides derived from the collapsed groups, to be layered on top
|
|
38
|
+
* of the consumer's own column visibility.
|
|
39
|
+
*/
|
|
40
|
+
collapsedVisibility: VisibilityState;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Manages column-group collapse state. Supports both controlled
|
|
45
|
+
* (`collapsedColumnGroups` + `onCollapsedColumnGroupsChange`) and uncontrolled
|
|
46
|
+
* usage (seeded from `meta.group.defaultCollapsed`).
|
|
47
|
+
*/
|
|
48
|
+
export const useColumnGroups = <TData, TValue>({
|
|
49
|
+
columns,
|
|
50
|
+
collapsedColumnGroups,
|
|
51
|
+
onCollapsedColumnGroupsChange,
|
|
52
|
+
}: UseColumnGroupsParams<TData, TValue>): UseColumnGroupsResult => {
|
|
53
|
+
const [collapsed, setCollapsed] = useControlled(
|
|
54
|
+
collapsedColumnGroups,
|
|
55
|
+
onCollapsedColumnGroupsChange,
|
|
56
|
+
getInitialCollapsedGroups(columns),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const toggleColumnGroup = (groupId: string) => {
|
|
60
|
+
const next = collapsed.includes(groupId)
|
|
61
|
+
? collapsed.filter((id) => id !== groupId)
|
|
62
|
+
: [...collapsed, groupId];
|
|
63
|
+
|
|
64
|
+
setCollapsed(next);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const collapsedVisibility = useMemo(
|
|
68
|
+
() => getCollapsedColumnVisibility(columns, collapsed),
|
|
69
|
+
[columns, collapsed],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return { collapsedColumnGroups: collapsed, toggleColumnGroup, collapsedVisibility };
|
|
73
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import './types/column-group-meta.augmentation';
|
|
2
|
+
|
|
3
|
+
export type { DsColumnGroupMeta } from './types/column-group.types';
|
|
4
|
+
export { useColumnGroups } from './hooks/use-column-groups';
|
|
5
|
+
export { getGroupColumnSizeStyle, isFirstLeafColumnOfGroup } from './utils/column-group';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import '@tanstack/react-table';
|
|
2
|
+
import type { DsColumnGroupMeta } from './column-group.types';
|
|
3
|
+
|
|
4
|
+
declare module '@tanstack/react-table' {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
6
|
+
interface ColumnMeta<TData, TValue> {
|
|
7
|
+
/**
|
|
8
|
+
* Set on a grouping column def (one that nests child `columns`) to configure
|
|
9
|
+
* the group header and its collapse behavior.
|
|
10
|
+
*/
|
|
11
|
+
group?: DsColumnGroupMeta;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Set on a leaf column to keep it visible while its parent group is collapsed
|
|
15
|
+
* (e.g. a key identifier column that should always stay on screen).
|
|
16
|
+
*
|
|
17
|
+
* Only affects the collapsed state. When `false` or omitted (the default),
|
|
18
|
+
* the column is hidden while the group is collapsed and shown again when the
|
|
19
|
+
* group is expanded — it is never permanently hidden by this flag.
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
keepVisibleWhenCollapsed?: boolean;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for a grouping column, set on a column definition that nests
|
|
3
|
+
* child columns via TanStack's `columns` array. Lives under `meta.group`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const columns: ColumnDef<Row>[] = [
|
|
8
|
+
* {
|
|
9
|
+
* id: 'equipment',
|
|
10
|
+
* header: 'Equipment',
|
|
11
|
+
* meta: { group: { collapsible: true } },
|
|
12
|
+
* columns: [
|
|
13
|
+
* { accessorKey: 'cabinetType', header: 'Cabinet Type', meta: { keepVisibleWhenCollapsed: true } },
|
|
14
|
+
* { accessorKey: 'shelf', header: 'Shelf#' },
|
|
15
|
+
* ],
|
|
16
|
+
* },
|
|
17
|
+
* ];
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface DsColumnGroupMeta {
|
|
21
|
+
/**
|
|
22
|
+
* Renders a collapse/expand toggle in the group header. When collapsed, every
|
|
23
|
+
* leaf column in the group is hidden except those marked
|
|
24
|
+
* `meta.keepVisibleWhenCollapsed`.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
collapsible?: boolean;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* When the table manages collapse state internally (uncontrolled), start this
|
|
31
|
+
* group collapsed. Ignored once `collapsedColumnGroups` is provided.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
defaultCollapsed?: boolean;
|
|
35
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
type Column,
|
|
4
|
+
type ColumnDef,
|
|
5
|
+
type VisibilityState,
|
|
6
|
+
defaultColumnSizing,
|
|
7
|
+
} from '@tanstack/react-table';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builds the flex style for a group header cell so it lines up with the combined
|
|
11
|
+
* width of its visible leaf columns.
|
|
12
|
+
*
|
|
13
|
+
* Fixed-width leaves contribute to `flexBasis`; default-sized leaves each add one
|
|
14
|
+
* unit of `flexGrow`, mirroring how {@link getColumnSizeStyle} sizes leaf cells so
|
|
15
|
+
* both header rows distribute free space identically.
|
|
16
|
+
*/
|
|
17
|
+
export const getGroupColumnSizeStyle = <TData, TValue>(
|
|
18
|
+
leafColumns: Column<TData, TValue>[],
|
|
19
|
+
): CSSProperties => {
|
|
20
|
+
let fixedWidth = 0;
|
|
21
|
+
let flexCount = 0;
|
|
22
|
+
|
|
23
|
+
for (const leaf of leafColumns) {
|
|
24
|
+
const size = leaf.getSize();
|
|
25
|
+
if (size !== defaultColumnSizing.size) {
|
|
26
|
+
fixedWidth += size;
|
|
27
|
+
} else {
|
|
28
|
+
flexCount += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (flexCount === 0) {
|
|
33
|
+
return { width: fixedWidth, minWidth: fixedWidth, flexShrink: 0 };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { flexGrow: flexCount, flexShrink: 0, flexBasis: fixedWidth, minWidth: fixedWidth };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether a column is the first visible leaf of its parent group. Used to draw a
|
|
41
|
+
* single side border at the start of each group in both the header and body.
|
|
42
|
+
*/
|
|
43
|
+
export const isFirstLeafColumnOfGroup = <TData, TValue>(column: Column<TData, TValue>): boolean => {
|
|
44
|
+
const parent = column.parent;
|
|
45
|
+
if (!parent) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const visibleLeaves = parent.getLeafColumns().filter((leaf) => leaf.getIsVisible());
|
|
50
|
+
|
|
51
|
+
return visibleLeaves[0]?.id === column.id;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type GroupColumnDef<TData, TValue> = ColumnDef<TData, TValue> & {
|
|
55
|
+
id?: string;
|
|
56
|
+
accessorKey?: string;
|
|
57
|
+
columns?: ColumnDef<TData, TValue>[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getLeafColumnId = <TData, TValue>(def: GroupColumnDef<TData, TValue>): string | undefined =>
|
|
61
|
+
def.id ?? def.accessorKey;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collects the ids of groups declared with `meta.group.defaultCollapsed`, used to
|
|
65
|
+
* seed the uncontrolled collapse state.
|
|
66
|
+
*/
|
|
67
|
+
export const getInitialCollapsedGroups = <TData, TValue>(columns: ColumnDef<TData, TValue>[]): string[] => {
|
|
68
|
+
const collapsed: string[] = [];
|
|
69
|
+
|
|
70
|
+
const visit = (defs: ColumnDef<TData, TValue>[]) => {
|
|
71
|
+
for (const def of defs as GroupColumnDef<TData, TValue>[]) {
|
|
72
|
+
if (def.columns) {
|
|
73
|
+
if (def.id && def.meta?.group?.defaultCollapsed) {
|
|
74
|
+
collapsed.push(def.id);
|
|
75
|
+
}
|
|
76
|
+
visit(def.columns);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
visit(columns);
|
|
82
|
+
|
|
83
|
+
return collapsed;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Maps collapsed groups to a {@link VisibilityState} that hides every leaf column
|
|
88
|
+
* inside a collapsed group except those flagged `meta.keepVisibleWhenCollapsed`.
|
|
89
|
+
* Layered on top of the consumer's own visibility so collapse never mutates it.
|
|
90
|
+
*/
|
|
91
|
+
export const getCollapsedColumnVisibility = <TData, TValue>(
|
|
92
|
+
columns: ColumnDef<TData, TValue>[],
|
|
93
|
+
collapsedGroupIds: string[],
|
|
94
|
+
): VisibilityState => {
|
|
95
|
+
const visibility: VisibilityState = {};
|
|
96
|
+
|
|
97
|
+
const visit = (defs: ColumnDef<TData, TValue>[], isInsideCollapsedGroup: boolean) => {
|
|
98
|
+
for (const def of defs as GroupColumnDef<TData, TValue>[]) {
|
|
99
|
+
if (def.columns) {
|
|
100
|
+
const isGroupCollapsed = isInsideCollapsedGroup || (!!def.id && collapsedGroupIds.includes(def.id));
|
|
101
|
+
visit(def.columns, isGroupCollapsed);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!isInsideCollapsedGroup || def.meta?.keepVisibleWhenCollapsed) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const columnId = getLeafColumnId(def);
|
|
110
|
+
if (columnId) {
|
|
111
|
+
visibility[columnId] = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
visit(columns, false);
|
|
117
|
+
|
|
118
|
+
return visibility;
|
|
119
|
+
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import './grouping/types/column-group-meta.augmentation';
|
|
2
|
+
|
|
1
3
|
export type { SecondaryRowAction, RowAction } from './components/ds-table-cell';
|
|
2
4
|
export type { ScrollParams } from './components/ds-table-body-virtualized';
|
|
3
5
|
|
|
4
6
|
export * from './filters';
|
|
7
|
+
export type { DsColumnGroupMeta } from './grouping';
|
|
8
|
+
export type { DsTableEditDisabledResult } from './ds-table-column.types';
|
|
5
9
|
|
|
6
10
|
export { default as DsTable } from './ds-table';
|
|
7
11
|
export * from './ds-table.types';
|
|
@@ -33,3 +33,8 @@ export const REORDER_COLUMN_ID = 'reorder';
|
|
|
33
33
|
* Width (in px) of the reorder column.
|
|
34
34
|
*/
|
|
35
35
|
export const REORDER_COLUMN_WIDTH = 60;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Number of placeholder skeleton rows rendered while the table is `loading`.
|
|
39
|
+
*/
|
|
40
|
+
export const SKELETON_ROW_COUNT = 5;
|