@drivenets/design-system 0.13.0 → 0.14.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 +16 -0
- package/dist/index.cjs +791 -326
- package/dist/index.d.cts +265 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +265 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +789 -326
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +13 -11
- 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-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-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 +4 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +2 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +4 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +2 -0
- package/src/components/ds-table/context/ds-table-context.ts +4 -0
- package/src/components/ds-table/ds-table.tsx +20 -2
- package/src/components/ds-table/ds-table.types.ts +55 -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 +3 -0
- package/src/components/ds-table/styles/_variables.scss +1 -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/styles/_root_new.scss +2 -0
- package/src/styles/_scrollbars.scss +27 -50
package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx
CHANGED
|
@@ -110,7 +110,7 @@ interface DsTableBodyProps<TData> {
|
|
|
110
110
|
rowSelection: RowSelectionState;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
function DsTableBody<TData>({
|
|
113
|
+
export function DsTableBody<TData>({
|
|
114
114
|
ref,
|
|
115
115
|
rowVirtualizer,
|
|
116
116
|
rowsMapRef,
|
|
@@ -122,6 +122,11 @@ function DsTableBody<TData>({
|
|
|
122
122
|
const virtualRows = rowVirtualizer.getVirtualItems();
|
|
123
123
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
124
124
|
|
|
125
|
+
// Gate the empty state on real data count, not on rendered virtual rows.
|
|
126
|
+
// getVirtualItems() transiently returns [] while the virtualizer re-measures
|
|
127
|
+
// after a `data` swap; rendering nothing for that frame avoids an empty-state flash.
|
|
128
|
+
const hasNoData = rowsAndExpandedRowContent.length === 0;
|
|
129
|
+
|
|
125
130
|
return (
|
|
126
131
|
<TableBody ref={ref} className={styles.body}>
|
|
127
132
|
{virtualRows.length > 0 ? (
|
|
@@ -144,11 +149,11 @@ function DsTableBody<TData>({
|
|
|
144
149
|
/>
|
|
145
150
|
);
|
|
146
151
|
})
|
|
147
|
-
) : (
|
|
152
|
+
) : hasNoData ? (
|
|
148
153
|
<TableRow>
|
|
149
154
|
<TableCell className={styles.emptyState}>{emptyState || EMPTY_TABLE_STATE_TEXT}</TableCell>
|
|
150
155
|
</TableRow>
|
|
151
|
-
)}
|
|
156
|
+
) : null}
|
|
152
157
|
|
|
153
158
|
<tr aria-hidden className={styles.sentinel} style={{ top: `${totalSize.toString()}px` }}>
|
|
154
159
|
<td />
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@use '../../../../styles/typography';
|
|
2
|
+
@use '../../styles/variables' as vars;
|
|
3
|
+
|
|
4
|
+
.groupHeaderCell {
|
|
5
|
+
@include typography.body-sm-md;
|
|
6
|
+
color: var(--font-main);
|
|
7
|
+
height: var(--ds-table-header-height);
|
|
8
|
+
padding: 0;
|
|
9
|
+
border-bottom: vars.$border-width solid var(--color-dap-gray-400);
|
|
10
|
+
border-left: vars.$group-border-width solid var(--border);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.groupHeaderContent {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
gap: vars.$header-sort-gap;
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
padding: 0 var(--standard);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.groupLabel {
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
text-overflow: ellipsis;
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.collapseToggle {
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
}
|
package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { flexRender } from '@tanstack/react-table';
|
|
2
|
+
import { DsButtonV3 } from '../../../ds-button-v3';
|
|
3
|
+
import { TableHead } from '../core-table';
|
|
4
|
+
import { useDsTableContext } from '../../context/ds-table-context';
|
|
5
|
+
import { getGroupColumnSizeStyle } from '../../grouping';
|
|
6
|
+
import { defaultDsTableLocale } from '../../ds-table.types';
|
|
7
|
+
import type { DsTableGroupHeaderCellProps } from './ds-table-group-header-cell.types';
|
|
8
|
+
import styles from './ds-table-group-header-cell.module.scss';
|
|
9
|
+
|
|
10
|
+
export const DsTableGroupHeaderCell = <TData, TValue>({
|
|
11
|
+
header,
|
|
12
|
+
}: DsTableGroupHeaderCellProps<TData, TValue>) => {
|
|
13
|
+
const { collapsedColumnGroups, onToggleColumnGroup, locale } = useDsTableContext<TData, TValue>();
|
|
14
|
+
|
|
15
|
+
const groupId = header.column.id;
|
|
16
|
+
const visibleLeafColumns = header.column.getLeafColumns().filter((leaf) => leaf.getIsVisible());
|
|
17
|
+
const groupStyle = getGroupColumnSizeStyle(visibleLeafColumns);
|
|
18
|
+
|
|
19
|
+
const isCollapsible = header.column.columnDef.meta?.group?.collapsible ?? false;
|
|
20
|
+
const isCollapsed = collapsedColumnGroups?.includes(groupId) ?? false;
|
|
21
|
+
|
|
22
|
+
const mergedLocale = { ...defaultDsTableLocale, ...locale };
|
|
23
|
+
const toggleLabel = isCollapsed ? mergedLocale.expandColumnGroup : mergedLocale.collapseColumnGroup;
|
|
24
|
+
|
|
25
|
+
const handleToggle = () => onToggleColumnGroup?.(groupId);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<TableHead className={styles.groupHeaderCell} style={groupStyle} colSpan={header.colSpan}>
|
|
29
|
+
<div className={styles.groupHeaderContent}>
|
|
30
|
+
<span className={styles.groupLabel}>
|
|
31
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
32
|
+
</span>
|
|
33
|
+
{isCollapsible && (
|
|
34
|
+
<DsButtonV3
|
|
35
|
+
variant="secondary"
|
|
36
|
+
size="tiny"
|
|
37
|
+
icon={isCollapsed ? 'chevron_right' : 'chevron_left'}
|
|
38
|
+
className={styles.collapseToggle}
|
|
39
|
+
aria-label={toggleLabel}
|
|
40
|
+
aria-expanded={!isCollapsed}
|
|
41
|
+
title={toggleLabel}
|
|
42
|
+
onClick={handleToggle}
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
</TableHead>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Header } from '@tanstack/react-table';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the grouping column header cell.
|
|
5
|
+
*/
|
|
6
|
+
export interface DsTableGroupHeaderCellProps<TData, TValue> {
|
|
7
|
+
/**
|
|
8
|
+
* The grouping header from `@tanstack/react-table` (a header whose column nests
|
|
9
|
+
* child columns).
|
|
10
|
+
*/
|
|
11
|
+
header: Header<TData, TValue>;
|
|
12
|
+
}
|
|
@@ -11,6 +11,8 @@ import { SELECT_COLUMN_ID } from '../../utils/constants';
|
|
|
11
11
|
import { DsStack } from '../../../ds-stack';
|
|
12
12
|
import { DsTableColumnFilterPopover } from '../../filters/components/column-filter-popover';
|
|
13
13
|
import type { ResolvedColumnFilter } from '../../filters/types/filter-adapter.types';
|
|
14
|
+
import { DsTableGroupHeaderCell } from '../ds-table-group-header-cell';
|
|
15
|
+
import { isFirstLeafColumnOfGroup } from '../../grouping';
|
|
14
16
|
|
|
15
17
|
const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
|
|
16
18
|
const { stickyHeader, bordered, virtualized } = useDsTableContext<TData, unknown>();
|
|
@@ -29,9 +31,24 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
|
|
|
29
31
|
)}
|
|
30
32
|
>
|
|
31
33
|
{headerGroup.headers.map((header) => {
|
|
34
|
+
if (header.isPlaceholder) {
|
|
35
|
+
return (
|
|
36
|
+
<TableHead
|
|
37
|
+
key={header.id}
|
|
38
|
+
className={styles.headerCell}
|
|
39
|
+
style={getColumnSizeStyle(header.getSize())}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (header.subHeaders.length > 0) {
|
|
45
|
+
return <DsTableGroupHeaderCell key={header.id} header={header} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
const headerStyle = getColumnSizeStyle(header.column.getSize());
|
|
33
49
|
const canSort = header.column.getCanSort();
|
|
34
50
|
const isSelectColumn = header.column.id === SELECT_COLUMN_ID;
|
|
51
|
+
const isGroupStart = isFirstLeafColumnOfGroup(header.column);
|
|
35
52
|
|
|
36
53
|
const metaFilter = header.column.columnDef.meta?.filter;
|
|
37
54
|
const resolvedFilter: ResolvedColumnFilter | undefined =
|
|
@@ -47,49 +64,48 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
|
|
|
47
64
|
isSelectColumn && styles.selectHeaderCell,
|
|
48
65
|
resolvedFilter && styles.filterableHeader,
|
|
49
66
|
hasActiveColumnFilter && styles.filterActive,
|
|
67
|
+
isGroupStart && styles.groupStart,
|
|
50
68
|
)}
|
|
51
69
|
onClick={header.column.getToggleSortingHandler()}
|
|
52
70
|
style={headerStyle}
|
|
53
71
|
>
|
|
54
|
-
{
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
</div>
|
|
92
|
-
)}
|
|
72
|
+
<div className={styles.headerSortContainer}>
|
|
73
|
+
<DsStack direction="column" justifyContent="center" width="100%">
|
|
74
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
75
|
+
</DsStack>
|
|
76
|
+
{canSort && (
|
|
77
|
+
<div className={styles.pageButtonIconContainer}>
|
|
78
|
+
{{
|
|
79
|
+
asc: (
|
|
80
|
+
<DsIcon
|
|
81
|
+
icon="arrow_drop_up"
|
|
82
|
+
className={classnames(styles.pageButtonIcon, stylesShared.pageButtonIcon)}
|
|
83
|
+
/>
|
|
84
|
+
),
|
|
85
|
+
desc: (
|
|
86
|
+
<DsIcon
|
|
87
|
+
icon="arrow_drop_down"
|
|
88
|
+
className={classnames(styles.pageButtonIcon, stylesShared.pageButtonIcon)}
|
|
89
|
+
/>
|
|
90
|
+
),
|
|
91
|
+
}[header.column.getIsSorted() as string] ?? (
|
|
92
|
+
<div className={classnames(styles.pageButtonIcon, stylesShared.pageButtonIcon)} />
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
{resolvedFilter && (
|
|
97
|
+
<div className={styles.filterTriggerContainer}>
|
|
98
|
+
<DsTableColumnFilterPopover
|
|
99
|
+
hasActiveFilter={resolvedFilter.hasActiveFilter}
|
|
100
|
+
onApply={resolvedFilter.onApply}
|
|
101
|
+
onClear={resolvedFilter.onClear}
|
|
102
|
+
onCancel={resolvedFilter.onCancel}
|
|
103
|
+
>
|
|
104
|
+
{resolvedFilter.render()}
|
|
105
|
+
</DsTableColumnFilterPopover>
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
93
109
|
</TableHead>
|
|
94
110
|
);
|
|
95
111
|
})}
|
|
@@ -10,6 +10,7 @@ import styles from './ds-table-row.module.scss';
|
|
|
10
10
|
import { useDsTableContext } from '../../context/ds-table-context';
|
|
11
11
|
import { mergeRefs } from '../../../../utils/merge-refs';
|
|
12
12
|
import { getColumnSizeStyle } from '../../utils/column-size';
|
|
13
|
+
import { isFirstLeafColumnOfGroup } from '../../grouping';
|
|
13
14
|
import { EXPANDER_COLUMN_ID, REORDER_COLUMN_ID, SELECT_COLUMN_ID } from '../../utils/constants';
|
|
14
15
|
|
|
15
16
|
interface DsRowDragHandleProps {
|
|
@@ -118,6 +119,7 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
|
|
|
118
119
|
styles.tableCell,
|
|
119
120
|
cell.column.id === EXPANDER_COLUMN_ID && styles.expandableCell,
|
|
120
121
|
cell.column.id === SELECT_COLUMN_ID && styles.selectableCell,
|
|
122
|
+
isFirstLeafColumnOfGroup(cell.column) && styles.groupStart,
|
|
121
123
|
typeof cell.column.columnDef.editCell === 'function' && styles.editableCell,
|
|
122
124
|
)}
|
|
123
125
|
style={cellStyle}
|
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';
|
|
@@ -77,6 +78,7 @@ export const DsTableRowVirtualized = <TData,>({
|
|
|
77
78
|
styles.cell,
|
|
78
79
|
cell.column.id === EXPANDER_COLUMN_ID && styles.expandableCell,
|
|
79
80
|
cell.column.id === SELECT_COLUMN_ID && styles.selectableCell,
|
|
81
|
+
isFirstLeafColumnOfGroup(cell.column) && styles.groupStart,
|
|
80
82
|
typeof cell.column.columnDef.editCell === 'function' && styles.editableCell,
|
|
81
83
|
)}
|
|
82
84
|
>
|
|
@@ -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;
|
|
@@ -25,6 +25,7 @@ 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';
|
|
28
29
|
import {
|
|
29
30
|
EMPTY_TABLE_STATE_TEXT,
|
|
30
31
|
EXPANDER_COLUMN_ID,
|
|
@@ -75,6 +76,9 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
75
76
|
onColumnFiltersChange,
|
|
76
77
|
columnVisibility: externalColumnVisibility,
|
|
77
78
|
onColumnVisibilityChange,
|
|
79
|
+
collapsedColumnGroups: externalCollapsedColumnGroups,
|
|
80
|
+
onCollapsedColumnGroupsChange,
|
|
81
|
+
locale,
|
|
78
82
|
activeRowId,
|
|
79
83
|
infiniteScroll,
|
|
80
84
|
onCellEdit,
|
|
@@ -109,12 +113,12 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
109
113
|
}
|
|
110
114
|
};
|
|
111
115
|
|
|
112
|
-
const
|
|
116
|
+
const baseColumnVisibility = externalColumnVisibility ?? internalColumnVisibility;
|
|
113
117
|
const handleColumnVisibilityChange = (
|
|
114
118
|
updaterOrValue: VisibilityState | ((old: VisibilityState) => VisibilityState),
|
|
115
119
|
) => {
|
|
116
120
|
const newVisibility =
|
|
117
|
-
typeof updaterOrValue === 'function' ? updaterOrValue(
|
|
121
|
+
typeof updaterOrValue === 'function' ? updaterOrValue(baseColumnVisibility) : updaterOrValue;
|
|
118
122
|
|
|
119
123
|
if (onColumnVisibilityChange) {
|
|
120
124
|
onColumnVisibilityChange(newVisibility);
|
|
@@ -123,6 +127,17 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
123
127
|
}
|
|
124
128
|
};
|
|
125
129
|
|
|
130
|
+
const { collapsedColumnGroups, toggleColumnGroup, collapsedVisibility } = useColumnGroups({
|
|
131
|
+
columns: columnsProp,
|
|
132
|
+
collapsedColumnGroups: externalCollapsedColumnGroups,
|
|
133
|
+
onCollapsedColumnGroupsChange,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const columnVisibility = useMemo(
|
|
137
|
+
() => ({ ...baseColumnVisibility, ...collapsedVisibility }),
|
|
138
|
+
[baseColumnVisibility, collapsedVisibility],
|
|
139
|
+
);
|
|
140
|
+
|
|
126
141
|
const handleSortingChange = (updaterOrValue: SortingState | ((old: SortingState) => SortingState)) => {
|
|
127
142
|
const newSorting = typeof updaterOrValue === 'function' ? updaterOrValue(sorting) : updaterOrValue;
|
|
128
143
|
setSorting(newSorting);
|
|
@@ -287,6 +302,9 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
287
302
|
renderExpandedRow,
|
|
288
303
|
virtualized,
|
|
289
304
|
activeRowId,
|
|
305
|
+
collapsedColumnGroups,
|
|
306
|
+
onToggleColumnGroup: toggleColumnGroup,
|
|
307
|
+
locale,
|
|
290
308
|
onCellEdit,
|
|
291
309
|
...editingState,
|
|
292
310
|
};
|
|
@@ -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.
|
|
@@ -442,6 +467,36 @@ export interface DsDataTableProps<TData, TValue> {
|
|
|
442
467
|
*/
|
|
443
468
|
onColumnVisibilityChange?: (visibility: VisibilityState) => void;
|
|
444
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Controlled list of collapsed column-group ids. A group is collapsible when its
|
|
472
|
+
* column def sets `meta.group.collapsible`; collapsing hides every leaf column in
|
|
473
|
+
* the group except those marked `meta.keepVisibleWhenCollapsed`. Omit for
|
|
474
|
+
* uncontrolled behavior (seeded from `meta.group.defaultCollapsed`).
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```tsx
|
|
478
|
+
* const [collapsed, setCollapsed] = useState<string[]>(['equipment']);
|
|
479
|
+
*
|
|
480
|
+
* <DsTable
|
|
481
|
+
* collapsedColumnGroups={collapsed}
|
|
482
|
+
* onCollapsedColumnGroupsChange={setCollapsed}
|
|
483
|
+
* />
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
486
|
+
collapsedColumnGroups?: string[];
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Callback fired with the next list of collapsed group ids when a group's
|
|
490
|
+
* collapse toggle is clicked.
|
|
491
|
+
*/
|
|
492
|
+
onCollapsedColumnGroupsChange?: (collapsedGroupIds: string[]) => void;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Overrides for user-facing strings (e.g. column-group toggle labels). Merged
|
|
496
|
+
* over the built-in English defaults.
|
|
497
|
+
*/
|
|
498
|
+
locale?: Partial<DsTableLocale>;
|
|
499
|
+
|
|
445
500
|
/**
|
|
446
501
|
* Callback fired when an editable cell commits a new value via one of the
|
|
447
502
|
* `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
|
+
}
|