@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.
Files changed (85) hide show
  1. package/dist/_internal/components/data-display/LazyImage/LazyImage.js +1 -1
  2. package/dist/_internal/components/data-display/Table/Table.helpers.js +10 -3
  3. package/dist/_internal/components/data-display/Table/Table.hooks.js +1 -1
  4. package/dist/_internal/components/data-display/Table/Table.js +4 -4
  5. package/dist/_internal/components/data-display/Table/TableParts.hooks.js +2 -3
  6. package/dist/_internal/components/data-display/Table/TableParts.js +2 -3
  7. package/dist/_internal/components/data-display/index.js +5 -5
  8. package/dist/_internal/components/index.js +11 -11
  9. package/dist/_internal/components/layout/Separator/Separator.js +2 -2
  10. package/dist/_internal/components/layout/Separator/Separator.styles.js +3 -1
  11. package/dist/_internal/components/layout/index.js +2 -2
  12. package/dist/_internal/components/utility/WorkflowStep/WorkflowStep.helpers.js +15 -1
  13. package/dist/_internal/components/utility/WorkflowStep/WorkflowStep.js +17 -4
  14. package/dist/_internal/components/utility/WorkflowStep/WorkflowStep.parts.js +15 -2
  15. package/dist/_internal/components/utility/WorkflowStep/WorkflowStepRow.js +16 -3
  16. package/dist/_internal/components/utility/index.js +17 -4
  17. package/dist/_internal/social-login-button/social-login-button.js +3 -3
  18. package/dist/{chunk-3FNXOCBD.js → chunk-5CQ5HYJW.js} +2 -2
  19. package/dist/{chunk-YZVR5ROE.js → chunk-5HQNHZ6Z.js} +12 -10
  20. package/dist/chunk-5HQNHZ6Z.js.map +1 -0
  21. package/dist/{chunk-3YOXVNO4.js → chunk-BOGTTXPA.js} +32 -30
  22. package/dist/chunk-BOGTTXPA.js.map +1 -0
  23. package/dist/{chunk-MC4KYGSX.js → chunk-GFQ4U6WZ.js} +3 -5
  24. package/dist/chunk-GFQ4U6WZ.js.map +1 -0
  25. package/dist/{chunk-5LWB4BYX.js → chunk-RGT6DM5W.js} +21 -47
  26. package/dist/chunk-RGT6DM5W.js.map +1 -0
  27. package/dist/{chunk-FF2D7V7T.js → chunk-SDK5LPPC.js} +13 -10
  28. package/dist/chunk-SDK5LPPC.js.map +1 -0
  29. package/dist/{chunk-D46SLMCX.js → chunk-SYNB3IIB.js} +12 -1
  30. package/dist/chunk-SYNB3IIB.js.map +1 -0
  31. package/dist/{chunk-SNURSSTN.js → chunk-VLOUXTEM.js} +3 -3
  32. package/dist/{chunk-V5KKBMAT.js → chunk-VXQOSADY.js} +6 -3
  33. package/dist/{chunk-V5KKBMAT.js.map → chunk-VXQOSADY.js.map} +1 -1
  34. package/dist/{chunk-BVLMKSHX.js → chunk-WDVNRW3Q.js} +2 -2
  35. package/dist/chunk-XS3REOPN.js +86 -0
  36. package/dist/chunk-XS3REOPN.js.map +1 -0
  37. package/dist/{chunk-IG23YZZR.js → chunk-XSSCSRUX.js} +7 -4
  38. package/dist/chunk-XSSCSRUX.js.map +1 -0
  39. package/dist/data-display/LazyImage.js +1 -1
  40. package/dist/data-display/Table.js +4 -4
  41. package/dist/layout/Separator.js +2 -2
  42. package/dist/social-login-button.js +3 -3
  43. package/dist/types/components/data-display/LazyImage/LazyImage.d.ts.map +1 -1
  44. package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts +15 -3
  45. package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts.map +1 -1
  46. package/dist/types/components/data-display/Table/Table.d.ts.map +1 -1
  47. package/dist/types/components/data-display/Table/Table.helpers.d.ts +20 -0
  48. package/dist/types/components/data-display/Table/Table.helpers.d.ts.map +1 -1
  49. package/dist/types/components/data-display/Table/Table.hooks.d.ts +8 -11
  50. package/dist/types/components/data-display/Table/Table.hooks.d.ts.map +1 -1
  51. package/dist/types/components/data-display/Table/Table.types.d.ts +17 -2
  52. package/dist/types/components/data-display/Table/Table.types.d.ts.map +1 -1
  53. package/dist/types/components/data-display/Table/TableParts.d.ts.map +1 -1
  54. package/dist/types/components/layout/Separator/Separator.d.ts.map +1 -1
  55. package/dist/types/components/layout/Separator/Separator.styles.d.ts +21 -0
  56. package/dist/types/components/layout/Separator/Separator.styles.d.ts.map +1 -1
  57. package/dist/types/components/utility/WorkflowStep/WorkflowStep.helpers.d.ts +6 -1
  58. package/dist/types/components/utility/WorkflowStep/WorkflowStep.helpers.d.ts.map +1 -1
  59. package/dist/types/components/utility/WorkflowStep/WorkflowStep.parts.d.ts.map +1 -1
  60. package/dist/utility/WorkflowStep.js +17 -4
  61. package/package.json +1 -1
  62. package/src/components/data-display/LazyImage/LazyImage.md +22 -15
  63. package/src/components/data-display/LazyImage/LazyImage.tsx +26 -9
  64. package/src/components/data-display/LazyImage/LazyImage.types.ts +15 -3
  65. package/src/components/data-display/Table/Table.helpers.ts +93 -0
  66. package/src/components/data-display/Table/Table.hooks.ts +20 -11
  67. package/src/components/data-display/Table/Table.tsx +34 -31
  68. package/src/components/data-display/Table/Table.types.ts +18 -2
  69. package/src/components/data-display/Table/TableParts.tsx +23 -52
  70. package/src/components/layout/Separator/Separator.styles.ts +32 -0
  71. package/src/components/layout/Separator/Separator.tsx +4 -2
  72. package/src/components/utility/WorkflowStep/WorkflowStep.helpers.ts +9 -3
  73. package/src/components/utility/WorkflowStep/WorkflowStep.parts.tsx +1 -5
  74. package/dist/chunk-3YOXVNO4.js.map +0 -1
  75. package/dist/chunk-5LWB4BYX.js.map +0 -1
  76. package/dist/chunk-D46SLMCX.js.map +0 -1
  77. package/dist/chunk-FF2D7V7T.js.map +0 -1
  78. package/dist/chunk-IG23YZZR.js.map +0 -1
  79. package/dist/chunk-JWZJSXI5.js +0 -29
  80. package/dist/chunk-JWZJSXI5.js.map +0 -1
  81. package/dist/chunk-MC4KYGSX.js.map +0 -1
  82. package/dist/chunk-YZVR5ROE.js.map +0 -1
  83. /package/dist/{chunk-3FNXOCBD.js.map → chunk-5CQ5HYJW.js.map} +0 -0
  84. /package/dist/{chunk-SNURSSTN.js.map → chunk-VLOUXTEM.js.map} +0 -0
  85. /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
- const [scrollTop, setScrollTop] = useState(0);
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 startIndex = Math.floor(scrollTop / pitch);
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((scrollTop + remPx(theme, containerHeight)) / pitch)
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, scrollTop, overscan, theme]);
50
+ }, [data, rowHeight, containerHeight, scroll, overscan, theme]);
45
51
 
46
52
  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
47
- setScrollTop(e.currentTarget.scrollTop);
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, type Theme } from '@mui/material/styles/index.js';
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 { TABLE_DEFAULTS, definedProps } from './Table.helpers';
23
- import { useTableSelection } from './Table.hooks';
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
- <StyledTableContainer>
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
- </StyledTableContainer>
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 StyledTableContainer and StyledTable
181
- // cannot cross a module boundary (TS2742).
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
- <StyledTableContainer
202
- virtualScrolling={p.virtualScrolling}
203
- containerHeight={p.containerHeight}
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
- containerHeight={typeof p.containerHeight === 'number' ? p.containerHeight : undefined}
247
+ virtualWindow={windowHeight ? visibleItems : undefined}
244
248
  rowHeight={p.rowHeight}
245
- overscan={p.overscan}
246
249
  />
247
250
  </StyledTable>
248
- </StyledTableContainer>
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
- const rest = props as unknown as Record<string, unknown>;
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
- containerHeight?: number;
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
- // spacer sized to the full data set. `rowHeight`, `containerHeight` and every
209
- // offset are design px, drawn through `rem`.
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, containerHeight, onScroll, renderTableRow }) => {
227
- const theme = useTheme();
228
- return (
229
- <Box
230
- onScroll={onScroll}
231
- style={{
232
- height: rem(theme, containerHeight),
233
- overflow: 'auto',
234
- position: 'relative' }}
235
- >
236
- <TableBody
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, virtualScrolling, containerHeight, overscan = 5 } = props;
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 (virtualScrolling && containerHeight && rowHeight) {
247
+ if (virtualWindow) {
275
248
  return (
276
249
  <VirtualisedBody
277
- visibleItems={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 ruleStyles = { ...styles, margin: 0 };
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
- /** `neutral` has no MUI palette entry, so its pair is derived from grey. */
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
- if (color === 'neutral') {
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 colorKey = color || 'primary';
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":[]}