Skip to content

Commit 0675e58

Browse files
committed
Implement "Default" columns mode (spread to available space)
1 parent 28a09f9 commit 0675e58

File tree

6 files changed

+384
-57
lines changed

6 files changed

+384
-57
lines changed

packages/main/src/components/AnalyticalTableV2/AnalyticalTableV2.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
}
77

88
.cell {
9+
box-sizing: border-box;
910
display: flex;
1011
background-color: var(--sapList_Background);
12+
overflow: hidden;
13+
/*todo: dev*/
14+
border-inline: solid 1px black;
1115
}
1216

1317
/* ============================================================= */

packages/main/src/components/AnalyticalTableV2/AnalyticalTableV2.stories.tsx

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,78 @@ import type { ColumnDef } from '@tanstack/react-table';
44
import { Button } from '@ui5/webcomponents-react';
55
import { AnalyticalTableV2 } from './index.js';
66

7+
//todo make id mandatory, or take this into account for custom implementations: https://tanstack.com/table/latest/docs/api/core/column-def --> imo id mandatory is the easiest way
8+
79
//todo: any
810
const columns: ColumnDef<any>[] = [
9-
{ header: 'Name', accessorKey: 'name' },
10-
{ header: 'Age', accessorKey: 'age' },
11-
{ header: 'Friend Name', accessorKey: 'friend.name' },
12-
{ header: 'Friend Age', accessorKey: 'friend.age' },
1311
{
14-
header: 'Column Pinned',
15-
id: 'c_pinned',
16-
cell: ({ row }) => {
17-
return 'Pinned';
18-
}
12+
header: 'Person',
13+
id: 'A',
14+
columns: [
15+
{ header: 'Name', accessorKey: 'name', id: 'B' },
16+
{ header: 'Age', accessorKey: 'age', id: 'C' }
17+
]
18+
},
19+
{
20+
id: 'D',
21+
header: 'Friend',
22+
columns: [
23+
{ header: 'Friend Name', accessorKey: 'friend.name', id: 'E' },
24+
{ header: 'Friend Age', accessorKey: 'friend.age', id: 'F' }
25+
]
1926
},
2027
{
21-
header: 'Pin Row',
22-
id: 'r_pinned',
23-
size: 300,
24-
cell: ({ row }) => {
25-
return (
26-
<>
27-
<Button
28-
onClick={() => {
29-
row.pin('top');
30-
}}
31-
>
32-
Pin Top
33-
</Button>
34-
<Button
35-
onClick={() => {
36-
row.pin('bottom');
37-
}}
38-
>
39-
Pin Bottom
40-
</Button>
41-
<Button
42-
onClick={() => {
43-
row.pin(false);
44-
}}
45-
>
46-
Reset Pin
47-
</Button>
48-
</>
49-
);
50-
}
28+
id: 'G',
29+
header: 'Pinnable',
30+
columns: [
31+
{
32+
header: 'Column Pinned',
33+
id: 'c_pinned',
34+
cell: ({ row }) => {
35+
return 'Pinned';
36+
}
37+
},
38+
{
39+
header: 'Pin Row',
40+
id: 'r_pinned',
41+
size: 300,
42+
cell: ({ row }) => {
43+
return (
44+
<>
45+
<Button
46+
onClick={() => {
47+
row.pin('top');
48+
}}
49+
>
50+
Pin Top
51+
</Button>
52+
<Button
53+
onClick={() => {
54+
row.pin('bottom');
55+
}}
56+
>
57+
Pin Bottom
58+
</Button>
59+
<Button
60+
onClick={() => {
61+
row.pin(false);
62+
}}
63+
>
64+
Reset Pin
65+
</Button>
66+
</>
67+
);
68+
}
69+
}
70+
]
5171
}
5272
];
5373

5474
const meta = {
5575
title: 'Data Display / AnalyticalTableV2',
5676
component: AnalyticalTableV2,
5777
args: {
58-
data: dataLarge.map((item, index) => ({ ...item, friend: { ...item.friend, age: index } })),
78+
data: dataLarge.map((item, index) => ({ ...item, friend: { ...item.friend, age: index } })).slice(0),
5979
columns,
6080
visibleRows: 5
6181
},
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* eslint-disable */
2+
// @ts-nocheck
3+
// todo: dev
4+
5+
import type { OnChangeFn, RowData, Table, TableFeature, Updater } from '@tanstack/react-table';
6+
import { makeStateUpdater } from '@tanstack/react-table';
7+
import { functionalUpdate } from '@tanstack/react-table';
8+
import { useEffect } from 'react';
9+
10+
// Use declaration merging to add our new feature APIs and state types to TanStack Table's existing types.
11+
declare module '@tanstack/react-table' {
12+
//merge our new feature's state with the existing table state
13+
interface TableState extends DensityTableState {}
14+
//merge our new feature's options with the existing table options
15+
interface TableOptionsResolved<TData extends RowData> extends DensityOptions {}
16+
//merge our new feature's instance APIs with the existing table instance APIs
17+
interface Table<TData extends RowData> extends DensityInstance {}
18+
// if you need to add cell instance APIs...
19+
// interface Cell<TData extends RowData, TValue> extends DensityCell
20+
// if you need to add row instance APIs...
21+
// interface Row<TData extends RowData> extends DensityRow
22+
// if you need to add column instance APIs...
23+
// interface Column<TData extends RowData, TValue> extends DensityColumn
24+
// if you need to add header instance APIs...
25+
// interface Header<TData extends RowData, TValue> extends DensityHeader
26+
27+
// Note: declaration merging on `ColumnDef` is not possible because it is a type, not an interface.
28+
// But you can still use declaration merging on `ColumnDef.meta`
29+
}
30+
31+
export type DensityState = 'sm' | 'md' | 'lg';
32+
export interface DensityTableState {
33+
density: DensityState;
34+
}
35+
// define types for our new feature's table options
36+
export interface DensityOptions {
37+
enableDensity?: boolean;
38+
onDensityChange?: OnChangeFn<DensityState>;
39+
}
40+
41+
// Define types for our new feature's table APIs
42+
export interface DensityInstance {
43+
setDensity: (updater: Updater<DensityState>) => void;
44+
toggleDensity: (value?: DensityState) => void;
45+
}
46+
47+
export const DensityFeature: TableFeature<any> = {
48+
// define the new feature's initial state
49+
getInitialState: (state): DensityTableState => {
50+
return {
51+
density: 'md',
52+
...state
53+
};
54+
},
55+
56+
// define the new feature's default options
57+
getDefaultOptions: <TData extends RowData>(table: Table<TData>): DensityOptions => {
58+
return {
59+
enableDensity: true,
60+
onDensityChange: makeStateUpdater('density', table)
61+
} as DensityOptions;
62+
},
63+
// if you need to add a default column definition...
64+
// getDefaultColumnDef: <TData extends RowData>(): Partial<ColumnDef<TData>> => {
65+
// return { meta: {} } //use meta instead of directly adding to the columnDef to avoid typescript stuff that's hard to workaround
66+
// },
67+
68+
// define the new feature's table instance methods
69+
createTable: <TData extends RowData>(table: Table<TData>): void => {
70+
table.setDensity = (updater) => {
71+
const safeUpdater: Updater<DensityState> = (old) => {
72+
const newState = functionalUpdate(updater, old);
73+
return newState;
74+
};
75+
return table.options.onDensityChange?.(safeUpdater);
76+
};
77+
table.toggleDensity = (value) => {
78+
table.setDensity((old) => {
79+
if (value) return value;
80+
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg'; //cycle through the 3 options
81+
});
82+
};
83+
}
84+
85+
// if you need to add row instance APIs...
86+
// createRow: <TData extends RowData>(row, table): void => {},
87+
// if you need to add cell instance APIs...
88+
// createCell: <TData extends RowData>(cell, column, row, table): void => {},
89+
// if you need to add column instance APIs...
90+
// createColumn: <TData extends RowData>(column, table): void => {},
91+
// if you need to add header instance APIs...
92+
// createHeader: <TData extends RowData>(header, table): void => {},
93+
};
94+
95+
export const ColumnModesFeature: TableFeature<any> = {
96+
// define the new feature's initial state
97+
getInitialState: (state): any => {
98+
return { ...state, tableWidth: 0 };
99+
},
100+
101+
// define the new feature's default options
102+
getDefaultOptions: <TData extends RowData>(table: Table<TData>): any => {
103+
return {
104+
columnMode: 'Default'
105+
};
106+
},
107+
// if you need to add a default column definition...
108+
// getDefaultColumnDef: <TData extends RowData>(): Partial<ColumnDef<TData>> => {
109+
// return { meta: {} } //use meta instead of directly adding to the columnDef to avoid typescript stuff that's hard to workaround
110+
// },
111+
112+
// define the new feature's table instance methods
113+
createTable: <TData extends RowData>(table: Table<TData>): void => {
114+
const state = table.getState();
115+
// console.log(table);
116+
},
117+
118+
// if you need to add row instance APIs...
119+
// createRow: <TData extends RowData>(row, table): void => {},
120+
// if you need to add cell instance APIs...
121+
// createCell: <TData extends RowData>(cell, column, row, table): void => {},
122+
// if you need to add column instance APIs...
123+
createColumn: <TData extends RowData>(column, table): void => {
124+
console.log(column);
125+
// console.log(column);
126+
// column.columnDef.size = 2000;
127+
// console.log(column, table.getState());
128+
}
129+
// if you need to add header instance APIs...
130+
// createHeader: <TData extends RowData>(header, table): void => {},
131+
};

packages/main/src/components/AnalyticalTableV2/index.tsx

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { getCoreRowModel, useReactTable } from '@tanstack/react-table';
22
import { CssSizeVariables, useStylesheet } from '@ui5/webcomponents-react-base';
33
import { clsx } from 'clsx';
44
import type { CSSProperties, ReactElement } from 'react';
5-
import { useRef } from 'react';
5+
import { useRef, useState } from 'react';
66
import { classNames, styleData } from './AnalyticalTableV2.module.css.js';
77
import { Cell } from './core/Cell.js';
88
import { Row } from './core/Row.js';
9+
import { DensityFeature, ColumnModesFeature } from './features/exampleFeature.js';
10+
import { useColumnWidths } from './useColumnMode.js';
911
import { useRowVirtualizer } from './useRowVirtualizer.js';
1012
import { useTableContainerResizeObserver } from './utils/useTableContainerResizeObserver.js';
1113

@@ -14,6 +16,12 @@ interface AnalyticalTableV2Props {
1416
columns?: any[];
1517
rowHeight?: number;
1618
visibleRows?: number;
19+
// todo: fka scaleWidthMode
20+
columnMode?: string;
21+
22+
//todo: check if this should be controllable, if so add respective checks otherwise the table-option won't do anything
23+
enableRowPinning?: boolean;
24+
enableColumnPinning?: boolean;
1725
}
1826

1927
interface CSSPropertiesWithVars extends CSSProperties {
@@ -27,31 +35,60 @@ const ROW_HEIGHT_VAR = 'var(--_ui5WcrAnalyticalTableControlledRowHeight)';
2735

2836
//todo forwardRef or React19? --> prob forwardRef
2937
function AnalyticalTableV2(props: AnalyticalTableV2Props): ReactElement<AnalyticalTableV2Props, 'div'> {
30-
const { columns, data, rowHeight, visibleRows = 15 } = props;
38+
const { columns, data, rowHeight, visibleRows = 15, enableRowPinning, enableColumnPinning, columnMode } = props;
3139
useStylesheet(styleData, AnalyticalTableV2.displayName);
3240
const tableContainerRef = useRef<HTMLDivElement>(null);
33-
const { tableWidth: _tableWidth, horizontalScrollbarHeight } = useTableContainerResizeObserver(tableContainerRef);
34-
41+
const { tableWidth, horizontalScrollbarHeight, verticalScrollbarWidth } =
42+
useTableContainerResizeObserver(tableContainerRef);
43+
const [columnSizing, setColumnSizing] = useState({});
44+
//const setColumnSizing = useColumnWidths(tableWidth, reactTable.getAllLeafColumns().map((item) => item.columnDef)
3545
const reactTable = useReactTable({
46+
_features: [DensityFeature, ColumnModesFeature],
3647
data,
3748
columns,
3849
getCoreRowModel: getCoreRowModel(),
3950
//todo: remove
4051
debugTable: true,
4152
//todo: optional
42-
enableColumnPinning: true,
43-
initialState: {
44-
columnPinning: {
45-
left: ['c_pinned'],
46-
right: ['friend_age']
47-
},
48-
rowPinning: {
49-
bottom: ['0', '1'],
50-
top: ['499', '498']
51-
}
53+
// enableColumnPinning: false,
54+
// enableRowPinning: false,
55+
state: {
56+
columnSizing,
57+
//todo: add types & clarify how to inject types (declare module '@tanstack/react-table' is probably not the best approach - probably we have to cast a lot...)
58+
//ColumnModesFeature
59+
tableWidth,
60+
61+
//DensityFeature
62+
density: 'md'
5263
},
53-
enableRowPinning: true
64+
// initialState: {
65+
// columnPinning: {
66+
// left: ['c_pinned'],
67+
// right: ['friend_age']
68+
// },
69+
// rowPinning: {
70+
// bottom: ['0', '1'],
71+
// top: ['499', '498']
72+
// }
73+
// }
74+
// column sizing
75+
defaultColumn: {
76+
size: 0,
77+
minSize: 60
78+
},
79+
//ColumnModesFeature
80+
columnMode,
81+
onColumnSizingChange: setColumnSizing
5482
});
83+
console.log(enableRowPinning, enableColumnPinning);
84+
85+
useColumnWidths(
86+
tableWidth,
87+
//todo: refactor to use getAllLeafColumns directly, or use the `columns` array?
88+
reactTable.getAllLeafColumns().map((item) => item.columnDef),
89+
setColumnSizing,
90+
verticalScrollbarWidth
91+
);
5592

5693
const headerGroups = reactTable.getHeaderGroups();
5794
const topRows = reactTable.getTopRows();
@@ -68,6 +105,7 @@ function AnalyticalTableV2(props: AnalyticalTableV2Props): ReactElement<Analytic
68105
ref={tableContainerRef}
69106
style={
70107
{
108+
visibility: tableWidth === 0 ? 'hidden' : undefined,
71109
'--_ui5WcrAnalyticalTableControlledRowHeight':
72110
typeof rowHeight === 'number' ? `${rowHeight}px` : CssSizeVariables.ui5WcrAnalyticalTableRowHeight,
73111
'--_ui5WcrAnalyticalTableHeaderGroups': headerGroups.length,

0 commit comments

Comments
 (0)