@12-apps/ui 6.39.1 → 6.39.3
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/dist/_internal/components/data-display/LazyImage/LazyImage.js +1 -1
- package/dist/_internal/components/data-display/Table/Table.helpers.js +10 -3
- package/dist/_internal/components/data-display/Table/Table.hooks.js +1 -1
- package/dist/_internal/components/data-display/Table/Table.js +4 -4
- package/dist/_internal/components/data-display/Table/TableParts.hooks.js +2 -3
- package/dist/_internal/components/data-display/Table/TableParts.js +2 -3
- package/dist/_internal/components/data-display/index.js +5 -5
- package/dist/_internal/components/index.js +11 -11
- package/dist/_internal/components/layout/Separator/Separator.js +2 -2
- package/dist/_internal/components/layout/Separator/Separator.styles.js +3 -1
- package/dist/_internal/components/layout/index.js +2 -2
- package/dist/_internal/components/utility/WorkflowStep/WorkflowStep.helpers.js +15 -1
- package/dist/_internal/components/utility/WorkflowStep/WorkflowStep.js +17 -4
- package/dist/_internal/components/utility/WorkflowStep/WorkflowStep.parts.js +15 -2
- package/dist/_internal/components/utility/WorkflowStep/WorkflowStepRow.js +16 -3
- package/dist/_internal/components/utility/index.js +17 -4
- package/dist/_internal/social-login-button/social-login-button.js +3 -3
- package/dist/{chunk-3FNXOCBD.js → chunk-5CQ5HYJW.js} +2 -2
- package/dist/{chunk-YZVR5ROE.js → chunk-5HQNHZ6Z.js} +12 -10
- package/dist/chunk-5HQNHZ6Z.js.map +1 -0
- package/dist/{chunk-3YOXVNO4.js → chunk-BOGTTXPA.js} +32 -30
- package/dist/chunk-BOGTTXPA.js.map +1 -0
- package/dist/{chunk-MC4KYGSX.js → chunk-GFQ4U6WZ.js} +3 -5
- package/dist/chunk-GFQ4U6WZ.js.map +1 -0
- package/dist/{chunk-5LWB4BYX.js → chunk-RGT6DM5W.js} +21 -47
- package/dist/chunk-RGT6DM5W.js.map +1 -0
- package/dist/{chunk-FF2D7V7T.js → chunk-SDK5LPPC.js} +13 -10
- package/dist/chunk-SDK5LPPC.js.map +1 -0
- package/dist/{chunk-D46SLMCX.js → chunk-SYNB3IIB.js} +12 -1
- package/dist/chunk-SYNB3IIB.js.map +1 -0
- package/dist/{chunk-SNURSSTN.js → chunk-VLOUXTEM.js} +3 -3
- package/dist/{chunk-V5KKBMAT.js → chunk-VXQOSADY.js} +6 -3
- package/dist/{chunk-V5KKBMAT.js.map → chunk-VXQOSADY.js.map} +1 -1
- package/dist/{chunk-BVLMKSHX.js → chunk-WDVNRW3Q.js} +2 -2
- package/dist/chunk-XS3REOPN.js +86 -0
- package/dist/chunk-XS3REOPN.js.map +1 -0
- package/dist/{chunk-IG23YZZR.js → chunk-XSSCSRUX.js} +7 -4
- package/dist/chunk-XSSCSRUX.js.map +1 -0
- package/dist/data-display/LazyImage.js +1 -1
- package/dist/data-display/Table.js +4 -4
- package/dist/layout/Separator.js +2 -2
- package/dist/social-login-button.js +3 -3
- package/dist/types/components/data-display/LazyImage/LazyImage.d.ts.map +1 -1
- package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts +15 -3
- package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/Table.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/Table.helpers.d.ts +20 -0
- package/dist/types/components/data-display/Table/Table.helpers.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/Table.hooks.d.ts +8 -11
- package/dist/types/components/data-display/Table/Table.hooks.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/Table.types.d.ts +17 -2
- package/dist/types/components/data-display/Table/Table.types.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/TableParts.d.ts.map +1 -1
- package/dist/types/components/layout/Separator/Separator.d.ts.map +1 -1
- package/dist/types/components/layout/Separator/Separator.styles.d.ts +21 -0
- package/dist/types/components/layout/Separator/Separator.styles.d.ts.map +1 -1
- package/dist/types/components/utility/WorkflowStep/WorkflowStep.helpers.d.ts +6 -1
- package/dist/types/components/utility/WorkflowStep/WorkflowStep.helpers.d.ts.map +1 -1
- package/dist/types/components/utility/WorkflowStep/WorkflowStep.parts.d.ts.map +1 -1
- package/dist/utility/WorkflowStep.js +17 -4
- package/package.json +1 -1
- package/src/components/data-display/LazyImage/LazyImage.md +22 -15
- package/src/components/data-display/LazyImage/LazyImage.tsx +26 -9
- package/src/components/data-display/LazyImage/LazyImage.types.ts +15 -3
- package/src/components/data-display/Table/Table.helpers.ts +93 -0
- package/src/components/data-display/Table/Table.hooks.ts +20 -11
- package/src/components/data-display/Table/Table.tsx +34 -31
- package/src/components/data-display/Table/Table.types.ts +18 -2
- package/src/components/data-display/Table/TableParts.tsx +23 -52
- package/src/components/layout/Separator/Separator.styles.ts +32 -0
- package/src/components/layout/Separator/Separator.tsx +4 -2
- package/src/components/utility/WorkflowStep/WorkflowStep.helpers.ts +9 -3
- package/src/components/utility/WorkflowStep/WorkflowStep.parts.tsx +1 -5
- package/dist/chunk-3YOXVNO4.js.map +0 -1
- package/dist/chunk-5LWB4BYX.js.map +0 -1
- package/dist/chunk-D46SLMCX.js.map +0 -1
- package/dist/chunk-FF2D7V7T.js.map +0 -1
- package/dist/chunk-IG23YZZR.js.map +0 -1
- package/dist/chunk-JWZJSXI5.js +0 -29
- package/dist/chunk-JWZJSXI5.js.map +0 -1
- package/dist/chunk-MC4KYGSX.js.map +0 -1
- package/dist/chunk-YZVR5ROE.js.map +0 -1
- /package/dist/{chunk-3FNXOCBD.js.map → chunk-5CQ5HYJW.js.map} +0 -0
- /package/dist/{chunk-SNURSSTN.js.map → chunk-VLOUXTEM.js.map} +0 -0
- /package/dist/{chunk-BVLMKSHX.js.map → chunk-WDVNRW3Q.js.map} +0 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import type { TableProps as MuiTableProps } from '@mui/material/Table/index.js';
|
|
2
|
+
import type { Theme } from '@mui/material/styles/index.js';
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
|
|
5
|
+
import { rem } from '../../../tokens/relative';
|
|
6
|
+
|
|
1
7
|
import type { TableProps } from './Table.types';
|
|
2
8
|
|
|
3
9
|
/**
|
|
@@ -7,6 +13,30 @@ import type { TableProps } from './Table.types';
|
|
|
7
13
|
*/
|
|
8
14
|
export const tableRowHeight = (rowHeight: number | undefined): number => rowHeight ?? 52;
|
|
9
15
|
|
|
16
|
+
/** The scroll box's height: a number is design px, 400 unless the caller says otherwise. */
|
|
17
|
+
const scrollHeight = (theme: Theme, containerHeight: number | string | undefined): string => {
|
|
18
|
+
const box = containerHeight || 400;
|
|
19
|
+
return typeof box === 'number' ? rem(theme, box) : box;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The table's one scroll container (FUT-2658): `containerHeight` tall when
|
|
24
|
+
* `virtualScrolling` is on, holding the header and the body. Inline, so the
|
|
25
|
+
* height it was given reads back off the element.
|
|
26
|
+
*/
|
|
27
|
+
export const scrollerStyle = (theme: Theme, p: TableProps): React.CSSProperties | undefined =>
|
|
28
|
+
p.virtualScrolling ? { height: scrollHeight(theme, p.containerHeight), overflow: 'auto' } : undefined;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The window's height in design px when the body is virtualised — only a
|
|
32
|
+
* numeric `containerHeight` virtualises; a string sizes the scroller alone.
|
|
33
|
+
*/
|
|
34
|
+
export const virtualHeight = (p: TableProps): number | undefined =>
|
|
35
|
+
p.virtualScrolling && typeof p.containerHeight === 'number' && p.containerHeight > 0 &&
|
|
36
|
+
tableRowHeight(p.rowHeight) > 0
|
|
37
|
+
? p.containerHeight
|
|
38
|
+
: undefined;
|
|
39
|
+
|
|
10
40
|
export const TABLE_DEFAULTS: Partial<TableProps> = {
|
|
11
41
|
variant: 'default',
|
|
12
42
|
stripeColor: 'neutral',
|
|
@@ -35,3 +65,66 @@ export const definedProps = (props: TableProps): Partial<TableProps> =>
|
|
|
35
65
|
|
|
36
66
|
// The container + styled table + optional header, shared by the loading, empty
|
|
37
67
|
// and populated renders.
|
|
68
|
+
|
|
69
|
+
/** A key `Table` declares on top of MUI's own `TableProps`. */
|
|
70
|
+
type TableOwnKey = Exclude<keyof TableProps, keyof MuiTableProps>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Every key {@link TableOwnKey} names. None is an HTML attribute: each is read
|
|
74
|
+
* by the table and drawn by its parts, so none may reach the `<table>`
|
|
75
|
+
* (FUT-2658). A `Record` over the key union makes the list exhaustive — a prop
|
|
76
|
+
* added to `Table.types.ts` does not compile until it is named here.
|
|
77
|
+
*/
|
|
78
|
+
const TABLE_OWN_KEYS: Record<TableOwnKey, true> = {
|
|
79
|
+
emptyText: true,
|
|
80
|
+
variant: true,
|
|
81
|
+
stripeColor: true,
|
|
82
|
+
glow: true,
|
|
83
|
+
pulse: true,
|
|
84
|
+
hoverable: true,
|
|
85
|
+
loading: true,
|
|
86
|
+
onRowClick: true,
|
|
87
|
+
onRowFocus: true,
|
|
88
|
+
onRowBlur: true,
|
|
89
|
+
density: true,
|
|
90
|
+
selectable: true,
|
|
91
|
+
selectedRows: true,
|
|
92
|
+
onSelectionChange: true,
|
|
93
|
+
rowKeyExtractor: true,
|
|
94
|
+
sortable: true,
|
|
95
|
+
sortConfig: true,
|
|
96
|
+
onSortChange: true,
|
|
97
|
+
filterable: true,
|
|
98
|
+
filterConfig: true,
|
|
99
|
+
onFilterChange: true,
|
|
100
|
+
columns: true,
|
|
101
|
+
data: true,
|
|
102
|
+
virtualScrolling: true,
|
|
103
|
+
rowHeight: true,
|
|
104
|
+
overscan: true,
|
|
105
|
+
responsive: true,
|
|
106
|
+
columnPriorities: true,
|
|
107
|
+
responsiveBreakpoints: true,
|
|
108
|
+
showColumnToggle: true,
|
|
109
|
+
containerHeight: true,
|
|
110
|
+
loadingComponent: true,
|
|
111
|
+
emptyStateComponent: true,
|
|
112
|
+
keyboardNavigation: true,
|
|
113
|
+
renderRow: true,
|
|
114
|
+
renderCell: true,
|
|
115
|
+
draggableRows: true,
|
|
116
|
+
onRowsReorder: true,
|
|
117
|
+
fixedColumns: true,
|
|
118
|
+
rowStyleConfig: true,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const NOT_FORWARDED = new Set<string>([...Object.keys(TABLE_OWN_KEYS), 'children']);
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* What the caller passed that is MUI's or the DOM's — `className`, `id`,
|
|
125
|
+
* `style`, `aria-*`, `data-*`, DOM handlers, `sx`, `size`, `padding`,
|
|
126
|
+
* `component` — for `MuiTable` to spread onto the `<table>`. `children` is
|
|
127
|
+
* placed by the table itself.
|
|
128
|
+
*/
|
|
129
|
+
export const tableDomProps = (props: TableProps): Record<string, unknown> =>
|
|
130
|
+
Object.fromEntries(Object.entries(props).filter(([key]) => !NOT_FORWARDED.has(key)));
|
|
@@ -4,29 +4,37 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { rem, remPx } from '../../../tokens/relative';
|
|
6
6
|
|
|
7
|
-
import type { ColumnConfig, TableProps } from './Table.types';
|
|
7
|
+
import type { ColumnConfig, TableProps, VirtualWindow } from './Table.types';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The virtual window. `rowHeight` and `containerHeight` are design px: the
|
|
11
11
|
* window is computed in the px `scrollTop` is measured in (`remPx`), and what it
|
|
12
12
|
* hands back to draw with stays in design px (`offsetY`) or is CSS already
|
|
13
13
|
* (`totalHeight`), so the rows and the window share one pitch at any type scale.
|
|
14
|
+
*
|
|
15
|
+
* The scroller holds the header as well as the body, so the `<tbody>` starts
|
|
16
|
+
* the header's height into it. `handleScroll` measures that height from
|
|
17
|
+
* `headerRef` in the same live px as `scrollTop`, and the window subtracts it
|
|
18
|
+
* before dividing by the pitch (FUT-2658).
|
|
14
19
|
*/
|
|
15
20
|
export const useVirtualScrolling = (
|
|
16
21
|
data: Record<string, unknown>[],
|
|
17
22
|
rowHeight: number,
|
|
18
23
|
containerHeight: number,
|
|
19
|
-
overscan: number = 5
|
|
24
|
+
overscan: number = 5,
|
|
25
|
+
headerRef?: React.RefObject<HTMLElement | null>,
|
|
20
26
|
) => {
|
|
21
27
|
const theme = useTheme();
|
|
22
|
-
|
|
28
|
+
// Both live px, as the browser measures them.
|
|
29
|
+
const [scroll, setScroll] = useState({ scrollTop: 0, headerPx: 0 });
|
|
23
30
|
|
|
24
|
-
const visibleItems = useMemo(() => {
|
|
31
|
+
const visibleItems = useMemo((): VirtualWindow => {
|
|
25
32
|
const pitch = remPx(theme, rowHeight);
|
|
26
|
-
const
|
|
33
|
+
const bodyScrollTop = Math.max(0, scroll.scrollTop - scroll.headerPx);
|
|
34
|
+
const startIndex = Math.floor(bodyScrollTop / pitch);
|
|
27
35
|
const endIndex = Math.min(
|
|
28
36
|
data.length,
|
|
29
|
-
Math.ceil((
|
|
37
|
+
Math.ceil((bodyScrollTop + remPx(theme, containerHeight)) / pitch)
|
|
30
38
|
);
|
|
31
39
|
|
|
32
40
|
const start = Math.max(0, startIndex - overscan);
|
|
@@ -36,16 +44,17 @@ export const useVirtualScrolling = (
|
|
|
36
44
|
startIndex: start,
|
|
37
45
|
endIndex: end,
|
|
38
46
|
items: data.slice(start, end),
|
|
39
|
-
/** Every row at the scaled pitch, as CSS. */
|
|
40
47
|
totalHeight: rem(theme, data.length * rowHeight),
|
|
41
|
-
/** Where the first mounted row sits, in design px. */
|
|
42
48
|
offsetY: start * rowHeight,
|
|
43
49
|
};
|
|
44
|
-
}, [data, rowHeight, containerHeight,
|
|
50
|
+
}, [data, rowHeight, containerHeight, scroll, overscan, theme]);
|
|
45
51
|
|
|
46
52
|
const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
setScroll({
|
|
54
|
+
scrollTop: e.currentTarget.scrollTop,
|
|
55
|
+
headerPx: headerRef?.current?.offsetHeight ?? 0,
|
|
56
|
+
});
|
|
57
|
+
}, [headerRef]);
|
|
49
58
|
|
|
50
59
|
return { visibleItems, handleScroll };
|
|
51
60
|
};
|
|
@@ -11,7 +11,7 @@ import TableBody from '@mui/material/TableBody/index.js';
|
|
|
11
11
|
import TableCell from '@mui/material/TableCell/index.js';
|
|
12
12
|
import TableContainer from '@mui/material/TableContainer/index.js';
|
|
13
13
|
import TableRow from '@mui/material/TableRow/index.js';
|
|
14
|
-
import { styled, useTheme
|
|
14
|
+
import { styled, useTheme } from '@mui/material/styles/index.js';
|
|
15
15
|
import React from 'react';
|
|
16
16
|
|
|
17
17
|
import type {
|
|
@@ -19,30 +19,21 @@ import type {
|
|
|
19
19
|
TableDensity,
|
|
20
20
|
TableProps,
|
|
21
21
|
TableStripeColor} from './Table.types';
|
|
22
|
-
import {
|
|
23
|
-
|
|
22
|
+
import {
|
|
23
|
+
TABLE_DEFAULTS,
|
|
24
|
+
definedProps,
|
|
25
|
+
scrollerStyle,
|
|
26
|
+
tableDomProps,
|
|
27
|
+
tableRowHeight,
|
|
28
|
+
virtualHeight,
|
|
29
|
+
} from './Table.helpers';
|
|
30
|
+
import { useTableSelection, useVirtualScrolling } from './Table.hooks';
|
|
24
31
|
import { EnhancedTableBody, EnhancedTableHeader } from './TableParts';
|
|
25
32
|
import { EmptyRow, NoDataPlaceholder } from './TableStates';
|
|
26
33
|
import {
|
|
27
34
|
tableStyles } from './Table.styles';
|
|
28
35
|
import { rem } from '../../../tokens/relative';
|
|
29
36
|
|
|
30
|
-
/** The scroll box's height: a number is design px, 400 unless the caller says otherwise. */
|
|
31
|
-
const scrollHeight = (theme: Theme, containerHeight: number | string | undefined): string => {
|
|
32
|
-
const box = containerHeight || 400;
|
|
33
|
-
return typeof box === 'number' ? rem(theme, box) : box;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Define pulse animation
|
|
37
|
-
const StyledTableContainer = styled(TableContainer, {
|
|
38
|
-
shouldForwardProp: (prop) => !['virtualScrolling', 'containerHeight'].includes(prop as string) })<{
|
|
39
|
-
virtualScrolling?: boolean;
|
|
40
|
-
containerHeight?: number | string;
|
|
41
|
-
}>(({ theme, virtualScrolling, containerHeight }) => ({
|
|
42
|
-
...(virtualScrolling && {
|
|
43
|
-
height: scrollHeight(theme, containerHeight),
|
|
44
|
-
overflow: 'auto' }) }));
|
|
45
|
-
|
|
46
37
|
// Helper function to get stripe color from theme
|
|
47
38
|
const StyledTable = styled(MuiTable, {
|
|
48
39
|
shouldForwardProp: (prop) =>
|
|
@@ -91,7 +82,7 @@ const TableShell: React.FC<{
|
|
|
91
82
|
header,
|
|
92
83
|
rest,
|
|
93
84
|
children }) => (
|
|
94
|
-
<
|
|
85
|
+
<TableContainer>
|
|
95
86
|
<StyledTable
|
|
96
87
|
ref={innerRef}
|
|
97
88
|
customVariant={variant}
|
|
@@ -106,7 +97,7 @@ const TableShell: React.FC<{
|
|
|
106
97
|
{header}
|
|
107
98
|
{children}
|
|
108
99
|
</StyledTable>
|
|
109
|
-
</
|
|
100
|
+
</TableContainer>
|
|
110
101
|
);
|
|
111
102
|
|
|
112
103
|
const SKELETON_ROW_COUNT = 5;
|
|
@@ -177,8 +168,8 @@ const ColumnToggleMenu: React.FC<{
|
|
|
177
168
|
</Box>
|
|
178
169
|
);
|
|
179
170
|
|
|
180
|
-
// The populated table. Local because
|
|
181
|
-
//
|
|
171
|
+
// The populated table. Local because StyledTable cannot cross a module
|
|
172
|
+
// boundary (TS2742).
|
|
182
173
|
const columnsFor = (resolved: TableProps, selection: Selection): ColumnConfig[] =>
|
|
183
174
|
(resolved.responsive ? selection.visibleColumns : resolved.columns) ?? [];
|
|
184
175
|
|
|
@@ -192,15 +183,27 @@ const showsColumnToggle = (resolved: TableProps, selection: Selection): boolean
|
|
|
192
183
|
selection.hiddenColumns.length > 0,
|
|
193
184
|
);
|
|
194
185
|
|
|
186
|
+
// The virtual window lives here, with the one scroller whose offset drives it;
|
|
187
|
+
// the header's ref lets it subtract the height the `<thead>` pushes the body down.
|
|
195
188
|
const AdvancedTable: React.FC<RendererProps> = ({ resolved: p, selection, innerRef, rest }) => {
|
|
189
|
+
const theme = useTheme();
|
|
196
190
|
const finalColumns = columnsFor(p, selection);
|
|
197
191
|
const showToggle = showsColumnToggle(p, selection);
|
|
192
|
+
const headRef = React.useRef<globalThis.HTMLTableSectionElement>(null);
|
|
193
|
+
const windowHeight = virtualHeight(p);
|
|
194
|
+
const { visibleItems, handleScroll } = useVirtualScrolling(
|
|
195
|
+
p.data ?? [],
|
|
196
|
+
tableRowHeight(p.rowHeight) || 40,
|
|
197
|
+
windowHeight ?? 400,
|
|
198
|
+
p.overscan,
|
|
199
|
+
headRef,
|
|
200
|
+
);
|
|
198
201
|
|
|
199
202
|
return (
|
|
200
203
|
<Box position="relative">
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
<TableContainer
|
|
205
|
+
style={scrollerStyle(theme, p)}
|
|
206
|
+
onScroll={windowHeight ? handleScroll : undefined}
|
|
204
207
|
>
|
|
205
208
|
<StyledTable
|
|
206
209
|
ref={innerRef}
|
|
@@ -224,6 +227,7 @@ const AdvancedTable: React.FC<RendererProps> = ({ resolved: p, selection, innerR
|
|
|
224
227
|
onSelectAll={selection.handleSelectAll}
|
|
225
228
|
density={p.density}
|
|
226
229
|
stickyHeader={p.stickyHeader}
|
|
230
|
+
headRef={headRef}
|
|
227
231
|
/>
|
|
228
232
|
<EnhancedTableBody
|
|
229
233
|
data={p.data ?? []}
|
|
@@ -240,12 +244,11 @@ const AdvancedTable: React.FC<RendererProps> = ({ resolved: p, selection, innerR
|
|
|
240
244
|
renderRow={p.renderRow}
|
|
241
245
|
renderCell={p.renderCell}
|
|
242
246
|
virtualScrolling={p.virtualScrolling}
|
|
243
|
-
|
|
247
|
+
virtualWindow={windowHeight ? visibleItems : undefined}
|
|
244
248
|
rowHeight={p.rowHeight}
|
|
245
|
-
overscan={p.overscan}
|
|
246
249
|
/>
|
|
247
250
|
</StyledTable>
|
|
248
|
-
</
|
|
251
|
+
</TableContainer>
|
|
249
252
|
|
|
250
253
|
{showToggle && (
|
|
251
254
|
<ColumnToggleMenu
|
|
@@ -329,7 +332,6 @@ export const Table = React.forwardRef<globalThis.HTMLTableElement, TableProps>(
|
|
|
329
332
|
emptyStateComponent,
|
|
330
333
|
emptyText,
|
|
331
334
|
children,
|
|
332
|
-
...props
|
|
333
335
|
} = resolved;
|
|
334
336
|
|
|
335
337
|
const selection = useTableSelection({
|
|
@@ -341,7 +343,8 @@ export const Table = React.forwardRef<globalThis.HTMLTableElement, TableProps>(
|
|
|
341
343
|
rowKeyExtractor: resolved.rowKeyExtractor,
|
|
342
344
|
onSelectionChange: resolved.onSelectionChange });
|
|
343
345
|
|
|
344
|
-
|
|
346
|
+
// Only MUI's and the DOM's own props reach the `<table>` (FUT-2658).
|
|
347
|
+
const rest = tableDomProps(tableProps);
|
|
345
348
|
const renderer: RendererProps = { resolved, selection, innerRef: ref, rest };
|
|
346
349
|
const finalColumns = responsive ? selection.visibleColumns : columns;
|
|
347
350
|
const shell = buildShell({ ...renderer, finalColumns });
|
|
@@ -303,6 +303,22 @@ export interface TableHeaderProps {
|
|
|
303
303
|
onSelectAll?: (selected: boolean) => void;
|
|
304
304
|
density?: TableDensity;
|
|
305
305
|
stickyHeader?: boolean;
|
|
306
|
+
/** The `<thead>`, so the virtual window can subtract its height from the scroller's offset. */
|
|
307
|
+
headRef?: React.Ref<globalThis.HTMLTableSectionElement>;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* The rows a virtualised body mounts. Computed by `useVirtualScrolling` in the
|
|
312
|
+
* table, which owns the one scroll container, and handed down to the body.
|
|
313
|
+
*/
|
|
314
|
+
export interface VirtualWindow {
|
|
315
|
+
startIndex: number;
|
|
316
|
+
endIndex: number;
|
|
317
|
+
items: Record<string, unknown>[];
|
|
318
|
+
/** Every row at the scaled pitch, as CSS. */
|
|
319
|
+
totalHeight: string;
|
|
320
|
+
/** Where the first mounted row sits, in design px. */
|
|
321
|
+
offsetY: number;
|
|
306
322
|
}
|
|
307
323
|
|
|
308
324
|
export interface TableBodyProps {
|
|
@@ -320,9 +336,9 @@ export interface TableBodyProps {
|
|
|
320
336
|
renderRow?: (rowData: Record<string, unknown>, index: number, isSelected: boolean) => React.ReactNode;
|
|
321
337
|
renderCell?: (value: unknown, column: ColumnConfig, rowData: Record<string, unknown>, rowIndex: number) => React.ReactNode;
|
|
322
338
|
virtualScrolling?: boolean;
|
|
323
|
-
|
|
339
|
+
/** Set only when the body is virtualised: the rows in view and where they sit. */
|
|
340
|
+
virtualWindow?: VirtualWindow;
|
|
324
341
|
rowHeight?: number;
|
|
325
|
-
overscan?: number;
|
|
326
342
|
}
|
|
327
343
|
|
|
328
344
|
export interface VirtualTableBodyProps extends TableBodyProps {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Box from '@mui/material/Box/index.js';
|
|
2
1
|
import Checkbox from '@mui/material/Checkbox/index.js';
|
|
3
2
|
import TableBody from '@mui/material/TableBody/index.js';
|
|
4
3
|
import TableCell from '@mui/material/TableCell/index.js';
|
|
@@ -11,9 +10,8 @@ import React, { useCallback } from 'react';
|
|
|
11
10
|
import { rem } from '../../../tokens/relative';
|
|
12
11
|
|
|
13
12
|
import { tableRowHeight } from './Table.helpers';
|
|
14
|
-
import { useVirtualScrolling } from './Table.hooks';
|
|
15
13
|
import { useTableRowRenderer } from './TableParts.hooks';
|
|
16
|
-
import type { ColumnConfig, TableBodyProps, TableHeaderProps } from './Table.types';
|
|
14
|
+
import type { ColumnConfig, TableBodyProps, TableHeaderProps, VirtualWindow } from './Table.types';
|
|
17
15
|
|
|
18
16
|
// Mirrors the visible sort indicator for assistive technology; undefined when
|
|
19
17
|
// the column is not sorted, which is what removes the attribute entirely.
|
|
@@ -77,7 +75,8 @@ export const EnhancedTableHeader: React.FC<TableHeaderProps> = React.memo(({
|
|
|
77
75
|
onSortChange,
|
|
78
76
|
selectable,
|
|
79
77
|
selectedRows = [],
|
|
80
|
-
onSelectAll
|
|
78
|
+
onSelectAll,
|
|
79
|
+
headRef }) => {
|
|
81
80
|
const handleSort = useCallback(
|
|
82
81
|
(columnKey: string) => {
|
|
83
82
|
if (!sortable || !onSortChange) return;
|
|
@@ -98,7 +97,7 @@ export const EnhancedTableHeader: React.FC<TableHeaderProps> = React.memo(({
|
|
|
98
97
|
);
|
|
99
98
|
|
|
100
99
|
return (
|
|
101
|
-
<TableHead>
|
|
100
|
+
<TableHead ref={headRef}>
|
|
102
101
|
<TableRow>
|
|
103
102
|
{selectable && (
|
|
104
103
|
<TableCell padding="checkbox">
|
|
@@ -205,46 +204,28 @@ export const TableDataRow: React.FC<{
|
|
|
205
204
|
};
|
|
206
205
|
|
|
207
206
|
// Only the rows in view are rendered, positioned by absolute offset inside a
|
|
208
|
-
//
|
|
209
|
-
//
|
|
207
|
+
// body sized to the full data set. `rowHeight` and every offset are design px,
|
|
208
|
+
// drawn through `rem`. The `<tbody>` sits directly in the `<table>`; the one
|
|
209
|
+
// scroll container wraps the whole table, header included (FUT-2658).
|
|
210
210
|
const VirtualisedBody: React.FC<{
|
|
211
|
-
visibleItems:
|
|
212
|
-
items: Record<string, unknown>[];
|
|
213
|
-
startIndex: number;
|
|
214
|
-
offsetY: number;
|
|
215
|
-
/** As CSS. */
|
|
216
|
-
totalHeight: string;
|
|
217
|
-
};
|
|
211
|
+
visibleItems: VirtualWindow;
|
|
218
212
|
rowHeight: number;
|
|
219
|
-
containerHeight: number;
|
|
220
|
-
onScroll: (event: React.UIEvent<HTMLDivElement>) => void;
|
|
221
213
|
renderTableRow: (
|
|
222
214
|
rowData: Record<string, unknown>,
|
|
223
215
|
index: number,
|
|
224
216
|
offsetY?: number,
|
|
225
217
|
) => React.ReactNode;
|
|
226
|
-
}> = ({ visibleItems, rowHeight,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
style={{
|
|
238
|
-
height: visibleItems.totalHeight,
|
|
239
|
-
position: 'relative' }}
|
|
240
|
-
>
|
|
241
|
-
{visibleItems.items.map((rowData, index) =>
|
|
242
|
-
renderTableRow(rowData, visibleItems.startIndex + index, visibleItems.offsetY + index * rowHeight)
|
|
243
|
-
)}
|
|
244
|
-
</TableBody>
|
|
245
|
-
</Box>
|
|
246
|
-
);
|
|
247
|
-
};
|
|
218
|
+
}> = ({ visibleItems, rowHeight, renderTableRow }) => (
|
|
219
|
+
<TableBody
|
|
220
|
+
style={{
|
|
221
|
+
height: visibleItems.totalHeight,
|
|
222
|
+
position: 'relative' }}
|
|
223
|
+
>
|
|
224
|
+
{visibleItems.items.map((rowData, index) =>
|
|
225
|
+
renderTableRow(rowData, visibleItems.startIndex + index, visibleItems.offsetY + index * rowHeight)
|
|
226
|
+
)}
|
|
227
|
+
</TableBody>
|
|
228
|
+
);
|
|
248
229
|
|
|
249
230
|
const PlainBody: React.FC<{
|
|
250
231
|
data: TableBodyProps['data'];
|
|
@@ -258,26 +239,16 @@ const PlainBody: React.FC<{
|
|
|
258
239
|
|
|
259
240
|
// Enhanced Table Body Component
|
|
260
241
|
export const EnhancedTableBody: React.FC<TableBodyProps> = React.memo((props) => {
|
|
261
|
-
const { data,
|
|
242
|
+
const { data, virtualWindow } = props;
|
|
262
243
|
// The row renderer takes the body's own props; its `rowHeight` stays the
|
|
263
244
|
// caller's design px, and the row resolves the default where it draws it.
|
|
264
245
|
const renderTableRow = useTableRowRenderer({ ...props, selectedRows: props.selectedRows ?? [] });
|
|
265
|
-
const rowHeight = tableRowHeight(props.rowHeight);
|
|
266
|
-
|
|
267
|
-
const { visibleItems, handleScroll } = useVirtualScrolling(
|
|
268
|
-
data,
|
|
269
|
-
rowHeight || 40,
|
|
270
|
-
typeof containerHeight === 'number' ? containerHeight : 400,
|
|
271
|
-
overscan
|
|
272
|
-
);
|
|
273
246
|
|
|
274
|
-
if (
|
|
247
|
+
if (virtualWindow) {
|
|
275
248
|
return (
|
|
276
249
|
<VirtualisedBody
|
|
277
|
-
visibleItems={
|
|
278
|
-
rowHeight={rowHeight}
|
|
279
|
-
containerHeight={containerHeight}
|
|
280
|
-
onScroll={handleScroll}
|
|
250
|
+
visibleItems={virtualWindow}
|
|
251
|
+
rowHeight={tableRowHeight(props.rowHeight)}
|
|
281
252
|
renderTableRow={renderTableRow}
|
|
282
253
|
/>
|
|
283
254
|
);
|
|
@@ -128,6 +128,38 @@ const ruleLength = (theme: Theme, length: SeparatorProps['length']): string => {
|
|
|
128
128
|
return length <= 1 ? `${length * 100}%` : rem(theme, length);
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* What a labelled separator's rules add to {@link separatorStyles}, and whether
|
|
133
|
+
* the group stretches to its parent (FUT-2617).
|
|
134
|
+
*
|
|
135
|
+
* A labelled VERTICAL separator is a flex COLUMN — rule, label, rule — and with
|
|
136
|
+
* no `length` each rule is `height: 100%` of a column whose height is its own
|
|
137
|
+
* content: both resolved to 0px and only the label showed. So the column
|
|
138
|
+
* stretches to the row it sits in and the two rules split what the label
|
|
139
|
+
* leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum. With a
|
|
140
|
+
* `length` the rule is exactly that long: no grow, no shrink, no basis, and the
|
|
141
|
+
* column keeps its content height, centred as before.
|
|
142
|
+
*
|
|
143
|
+
* Only the labelled branch reads this. The plain separator keeps
|
|
144
|
+
* `separatorStyles` as is, since `flex: 1 1 0` there would widen a vertical
|
|
145
|
+
* separator sitting in a row. The horizontal rules already shrink around the
|
|
146
|
+
* label in their row, so they are left alone.
|
|
147
|
+
*/
|
|
148
|
+
export const labelledSeparatorLayout = (
|
|
149
|
+
theme: Theme,
|
|
150
|
+
isHorizontal: boolean,
|
|
151
|
+
length: SeparatorProps['length'],
|
|
152
|
+
): { rule: CSSObject; group: CSSObject } => {
|
|
153
|
+
if (isHorizontal) return { rule: {}, group: {} };
|
|
154
|
+
if (length) return { rule: { flex: 'none' }, group: {} };
|
|
155
|
+
return {
|
|
156
|
+
// A design 16px, as long as the gap beside the label at the default
|
|
157
|
+
// spacing, so a short or unsized row still shows two rules and not a word.
|
|
158
|
+
rule: { flex: '1 1 0', minHeight: rem(theme, 16) },
|
|
159
|
+
group: { alignSelf: 'stretch' },
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
131
163
|
export const separatorStyles = (
|
|
132
164
|
theme: Theme,
|
|
133
165
|
{ variant, orientation, size, color, blockMargin, length }: SeparatorStyleArgs,
|
|
@@ -3,7 +3,7 @@ import Typography from '@mui/material/Typography/index.js';
|
|
|
3
3
|
import { useTheme } from '@mui/material/styles/index.js';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
|
|
6
|
-
import { separatorBlockMargin, separatorStyles } from './Separator.styles';
|
|
6
|
+
import { labelledSeparatorLayout, separatorBlockMargin, separatorStyles } from './Separator.styles';
|
|
7
7
|
import type { SeparatorProps } from './Separator.types';
|
|
8
8
|
|
|
9
9
|
export const Separator: React.FC<SeparatorProps> = ({
|
|
@@ -25,7 +25,8 @@ export const Separator: React.FC<SeparatorProps> = ({
|
|
|
25
25
|
if (children) {
|
|
26
26
|
// The rules either side of the label sit INSIDE the row, which carries the
|
|
27
27
|
// margin; their own would add it a second time.
|
|
28
|
-
const
|
|
28
|
+
const layout = labelledSeparatorLayout(theme, isHorizontal, length);
|
|
29
|
+
const ruleStyles = { ...styles, margin: 0, ...layout.rule };
|
|
29
30
|
|
|
30
31
|
return (
|
|
31
32
|
<Box
|
|
@@ -40,6 +41,7 @@ export const Separator: React.FC<SeparatorProps> = ({
|
|
|
40
41
|
margin: blockMargin,
|
|
41
42
|
flexDirection: isHorizontal ? 'row' : 'column',
|
|
42
43
|
gap: theme.spacing(2),
|
|
44
|
+
...layout.group,
|
|
43
45
|
}}
|
|
44
46
|
>
|
|
45
47
|
<Box sx={ruleStyles} />
|
|
@@ -2,6 +2,7 @@ import type { Theme } from '@mui/material/styles/index.js';
|
|
|
2
2
|
|
|
3
3
|
import type { WorkflowStepItem, WorkflowStepProps } from './WorkflowStep.types';
|
|
4
4
|
import { neutralTones } from '../../../tokens/ink';
|
|
5
|
+
import { paletteKey } from '../../../tokens/scales';
|
|
5
6
|
|
|
6
7
|
type StepColor = NonNullable<WorkflowStepProps['color']>;
|
|
7
8
|
type StepSize = NonNullable<WorkflowStepProps['size']>;
|
|
@@ -22,9 +23,15 @@ interface StepPalette {
|
|
|
22
23
|
contrastText: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The step's colour pair, keyed through `paletteKey` so `danger` reads MUI's
|
|
28
|
+
* `error` (the palette has no `danger`). `neutral` lands on `grey`, a ramp with
|
|
29
|
+
* no `main`, so its pair is derived from the ramp — with the contrast computed,
|
|
30
|
+
* not assumed.
|
|
31
|
+
*/
|
|
26
32
|
export const stepPalette = (theme: Theme, color?: StepColor): StepPalette => {
|
|
27
|
-
|
|
33
|
+
const key = paletteKey(color || 'primary');
|
|
34
|
+
if (key === 'grey') {
|
|
28
35
|
const main = neutralTones(theme).emphasis;
|
|
29
36
|
return {
|
|
30
37
|
main,
|
|
@@ -32,7 +39,6 @@ export const stepPalette = (theme: Theme, color?: StepColor): StepPalette => {
|
|
|
32
39
|
};
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
const key = (color || 'primary') as 'primary' | 'secondary' | 'success' | 'warning' | 'error';
|
|
36
42
|
return theme.palette[key];
|
|
37
43
|
};
|
|
38
44
|
|
|
@@ -167,11 +167,7 @@ const StepConnector = styled(Box, {
|
|
|
167
167
|
color: WorkflowStepProps['color'];
|
|
168
168
|
variant: WorkflowStepProps['variant'];
|
|
169
169
|
}>(({ theme, orientation, isCompleted, color, variant }) => {
|
|
170
|
-
const
|
|
171
|
-
const colorValue = colorKey === 'neutral'
|
|
172
|
-
? { main: neutralTones(theme).emphasis }
|
|
173
|
-
: theme.palette[colorKey as 'primary' | 'secondary' | 'success' | 'warning' | 'error'];
|
|
174
|
-
const connectorColor = isCompleted ? colorValue.main : neutralTones(theme).track;
|
|
170
|
+
const connectorColor = isCompleted ? stepPalette(theme, color).main : neutralTones(theme).track;
|
|
175
171
|
|
|
176
172
|
return {
|
|
177
173
|
flex: 1,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/Table/Table.tsx"],"sourcesContent":["import MoreVertIcon from '@mui/icons-material/MoreVert';\nimport Box from '@mui/material/Box/index.js';\nimport FormControlLabel from '@mui/material/FormControlLabel/index.js';\nimport IconButton from '@mui/material/IconButton/index.js';\nimport Menu from '@mui/material/Menu/index.js';\nimport MenuItem from '@mui/material/MenuItem/index.js';\nimport Skeleton from '@mui/material/Skeleton/index.js';\nimport Switch from '@mui/material/Switch/index.js';\nimport MuiTable from '@mui/material/Table/index.js';\nimport TableBody from '@mui/material/TableBody/index.js';\nimport TableCell from '@mui/material/TableCell/index.js';\nimport TableContainer from '@mui/material/TableContainer/index.js';\nimport TableRow from '@mui/material/TableRow/index.js';\nimport { styled, useTheme, type Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport type {\n ColumnConfig,\n TableDensity,\n TableProps,\n TableStripeColor} from './Table.types';\nimport { TABLE_DEFAULTS, definedProps } from './Table.helpers';\nimport { useTableSelection } from './Table.hooks';\nimport { EnhancedTableBody, EnhancedTableHeader } from './TableParts';\nimport { EmptyRow, NoDataPlaceholder } from './TableStates';\nimport {\n tableStyles } from './Table.styles';\nimport { rem } from '../../../tokens/relative';\n\n/** The scroll box's height: a number is design px, 400 unless the caller says otherwise. */\nconst scrollHeight = (theme: Theme, containerHeight: number | string | undefined): string => {\n const box = containerHeight || 400;\n return typeof box === 'number' ? rem(theme, box) : box;\n};\n\n// Define pulse animation\nconst StyledTableContainer = styled(TableContainer, {\n shouldForwardProp: (prop) => !['virtualScrolling', 'containerHeight'].includes(prop as string) })<{ \n virtualScrolling?: boolean; \n containerHeight?: number | string;\n}>(({ theme, virtualScrolling, containerHeight }) => ({\n ...(virtualScrolling && {\n height: scrollHeight(theme, containerHeight),\n overflow: 'auto' }) }));\n\n// Helper function to get stripe color from theme\nconst StyledTable = styled(MuiTable, {\n shouldForwardProp: (prop) =>\n !['customVariant', 'glow', 'pulse', 'hoverable', 'density', 'stickyHeader', 'stripeColor'].includes(prop as string) })<{\n customVariant?: string;\n glow?: boolean;\n pulse?: boolean;\n hoverable?: boolean;\n density?: TableDensity;\n stickyHeader?: boolean;\n stripeColor?: TableStripeColor;\n}>(({ theme, customVariant, glow, pulse, hoverable, density, stickyHeader, stripeColor = 'neutral' }) => {\n return tableStyles({\n theme,\n customVariant,\n glow,\n pulse,\n hoverable,\n density,\n stickyHeader,\n stripeColor });\n});\n\n// Virtual Scrolling Hook\nconst TableShell: React.FC<{\n innerRef: React.Ref<globalThis.HTMLTableElement>;\n variant?: string;\n stripeColor?: TableStripeColor;\n glow?: boolean;\n pulse?: boolean;\n hoverable?: boolean;\n density?: TableDensity;\n stickyHeader?: boolean;\n header: React.ReactNode;\n rest: Record<string, unknown>;\n children: React.ReactNode;\n}> = ({\n innerRef,\n variant,\n stripeColor,\n glow,\n pulse,\n hoverable,\n density,\n stickyHeader,\n header,\n rest,\n children }) => (\n <StyledTableContainer>\n <StyledTable\n ref={innerRef}\n customVariant={variant}\n stripeColor={stripeColor}\n glow={glow}\n pulse={pulse}\n hoverable={hoverable}\n density={density}\n stickyHeader={stickyHeader}\n {...rest}\n >\n {header}\n {children}\n </StyledTable>\n </StyledTableContainer>\n);\n\nconst SKELETON_ROW_COUNT = 5;\n\nconst LoadingSkeletonRows: React.FC<{ columns?: ColumnConfig[] }> = ({ columns }) => {\n const theme = useTheme();\n return (\n <>\n {Array.from({ length: SKELETON_ROW_COUNT }, (_, index) => (\n <TableRow key={index}>\n {columns?.map((column) => (\n <TableCell key={column.key}>\n <Skeleton height={rem(theme, 20)} />\n </TableCell>\n ))}\n </TableRow>\n ))}\n </>\n );\n};\n\nexport type TableShellProps = Omit<React.ComponentProps<typeof TableShell>, 'children'>;\n\ntype Selection = ReturnType<typeof useTableSelection>;\n\ninterface RendererProps {\n resolved: TableProps;\n selection: Selection;\n innerRef: React.Ref<globalThis.HTMLTableElement>;\n rest: Record<string, unknown>;\n}\n\n// The column-visibility control, shown only when responsive mode has actually\n// hidden something on a narrow viewport.\nconst ColumnToggleMenu: React.FC<{\n columns: ColumnConfig[];\n hiddenColumns: number[];\n anchor: HTMLElement | null;\n setAnchor: (element: HTMLElement | null) => void;\n setHiddenColumns: (next: number[]) => void;\n}> = ({ columns, hiddenColumns, anchor, setAnchor, setHiddenColumns }) => (\n <Box position=\"absolute\" top={8} right={8}>\n <IconButton onClick={(event) => setAnchor(event.currentTarget)} size=\"small\">\n <MoreVertIcon />\n </IconButton>\n <Menu anchorEl={anchor} open={Boolean(anchor)} onClose={() => setAnchor(null)}>\n {columns.map((column, index) => (\n <MenuItem key={column.key}>\n <FormControlLabel\n control={\n <Switch\n checked={!hiddenColumns.includes(index)}\n onChange={(event) =>\n setHiddenColumns(\n event.target.checked\n ? hiddenColumns.filter((i: number) => i !== index)\n : [...hiddenColumns, index],\n )\n }\n size=\"small\"\n />\n }\n label={column.label}\n />\n </MenuItem>\n ))}\n </Menu>\n </Box>\n);\n\n// The populated table. Local because StyledTableContainer and StyledTable\n// cannot cross a module boundary (TS2742).\nconst columnsFor = (resolved: TableProps, selection: Selection): ColumnConfig[] =>\n (resolved.responsive ? selection.visibleColumns : resolved.columns) ?? [];\n\n// The toggle only earns its place when responsive mode has actually hidden\n// something on a narrow viewport.\nconst showsColumnToggle = (resolved: TableProps, selection: Selection): boolean =>\n Boolean(\n resolved.responsive &&\n selection.isMobile &&\n resolved.showColumnToggle &&\n selection.hiddenColumns.length > 0,\n );\n\nconst AdvancedTable: React.FC<RendererProps> = ({ resolved: p, selection, innerRef, rest }) => {\n const finalColumns = columnsFor(p, selection);\n const showToggle = showsColumnToggle(p, selection);\n\n return (\n <Box position=\"relative\">\n <StyledTableContainer\n virtualScrolling={p.virtualScrolling}\n containerHeight={p.containerHeight}\n >\n <StyledTable\n ref={innerRef}\n customVariant={p.variant}\n stripeColor={p.stripeColor}\n glow={p.glow}\n pulse={p.pulse}\n hoverable={p.hoverable}\n density={p.density}\n stickyHeader={p.stickyHeader}\n {...rest}\n >\n <EnhancedTableHeader\n columns={finalColumns}\n data={p.data ?? []}\n sortable={p.sortable}\n sortConfig={p.sortConfig}\n onSortChange={p.onSortChange}\n selectable={p.selectable}\n selectedRows={p.selectedRows}\n onSelectAll={selection.handleSelectAll}\n density={p.density}\n stickyHeader={p.stickyHeader}\n />\n <EnhancedTableBody\n data={p.data ?? []}\n columns={finalColumns}\n selectedRows={p.selectedRows}\n onRowClick={p.onRowClick}\n onRowFocus={p.onRowFocus}\n onRowBlur={p.onRowBlur}\n onSelectionChange={selection.handleSelectionChange}\n rowKeyExtractor={p.rowKeyExtractor}\n density={p.density}\n selectable={p.selectable}\n hoverable={p.hoverable}\n renderRow={p.renderRow}\n renderCell={p.renderCell}\n virtualScrolling={p.virtualScrolling}\n containerHeight={typeof p.containerHeight === 'number' ? p.containerHeight : undefined}\n rowHeight={p.rowHeight}\n overscan={p.overscan}\n />\n </StyledTable>\n </StyledTableContainer>\n\n {showToggle && (\n <ColumnToggleMenu\n columns={p.columns ?? []}\n hiddenColumns={selection.hiddenColumns}\n anchor={selection.columnMenuAnchor}\n setAnchor={selection.setColumnMenuAnchor}\n setHiddenColumns={selection.setHiddenColumns}\n />\n )}\n </Box>\n );\n};\n\n// The loading and empty states render the same shell as the populated table, so\n// they share TableShell rather than repeating it three times.\nconst buildShell = ({\n resolved,\n selection,\n innerRef,\n rest,\n finalColumns }: RendererProps & { finalColumns?: ColumnConfig[] }) => ({\n innerRef,\n variant: resolved.variant,\n stripeColor: resolved.stripeColor,\n glow: resolved.glow,\n pulse: resolved.pulse,\n hoverable: resolved.hoverable,\n density: resolved.density,\n stickyHeader: resolved.stickyHeader,\n header: resolved.columns ? (\n <EnhancedTableHeader\n columns={finalColumns ?? []}\n data={[]}\n sortable={resolved.sortable}\n sortConfig={resolved.sortConfig}\n onSortChange={resolved.onSortChange}\n selectable={resolved.selectable}\n selectedRows={resolved.selectedRows}\n onSelectAll={selection.handleSelectAll}\n density={resolved.density}\n stickyHeader={resolved.stickyHeader}\n />\n ) : null,\n rest: rest as Record<string, unknown> });\n\nconst TableLoadingState: React.FC<{\n shell: TableShellProps;\n loadingComponent?: React.ReactNode;\n columns?: ColumnConfig[];\n}> = ({ shell, loadingComponent, columns }) => (\n <TableShell {...shell}>\n <TableBody data-testid=\"table-loading\">\n {loadingComponent || <LoadingSkeletonRows columns={columns} />}\n </TableBody>\n </TableShell>\n);\n\nconst TableEmptyState: React.FC<{\n shell: TableShellProps;\n emptyStateComponent?: React.ReactNode;\n emptyText: string;\n colSpan: number;\n}> = ({ shell, emptyStateComponent, emptyText, colSpan }) => (\n <TableShell {...shell}>\n <EmptyRow colSpan={colSpan}>\n {emptyStateComponent || <NoDataPlaceholder emptyText={emptyText} />}\n </EmptyRow>\n </TableShell>\n);\n\nexport const Table = React.forwardRef<globalThis.HTMLTableElement, TableProps>(\n (tableProps, ref) => {\n const resolved = { ...TABLE_DEFAULTS, ...definedProps(tableProps) } as TableProps;\n const {\n columns,\n data,\n responsive,\n loading,\n loadingComponent,\n emptyStateComponent,\n emptyText,\n children,\n ...props\n } = resolved;\n\n const selection = useTableSelection({\n columns,\n data,\n responsive,\n columnPriorities: resolved.columnPriorities,\n selectedRows: resolved.selectedRows ?? [],\n rowKeyExtractor: resolved.rowKeyExtractor,\n onSelectionChange: resolved.onSelectionChange });\n\n const rest = props as unknown as Record<string, unknown>;\n const renderer: RendererProps = { resolved, selection, innerRef: ref, rest };\n const finalColumns = responsive ? selection.visibleColumns : columns;\n const shell = buildShell({ ...renderer, finalColumns });\n\n if (loading) {\n return (\n <TableLoadingState shell={shell} loadingComponent={loadingComponent} columns={columns} />\n );\n }\n\n if (data && data.length === 0) {\n return (\n <TableEmptyState\n shell={shell}\n emptyStateComponent={emptyStateComponent}\n emptyText={emptyText}\n colSpan={finalColumns?.length || 1}\n />\n );\n }\n\n if (columns && data) return <AdvancedTable {...renderer} />;\n\n // Basic table (backward compatibility)\n return (\n <StyledTable\n ref={ref}\n customVariant={resolved.variant}\n stripeColor={resolved.stripeColor}\n glow={resolved.glow}\n pulse={resolved.pulse}\n hoverable={resolved.hoverable}\n density={resolved.density}\n stickyHeader={resolved.stickyHeader}\n {...rest}\n >\n {children}\n </StyledTable>\n );\n },\n);\n\nTable.displayName = 'Table';"],"mappings":"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","names":[]}
|