@semcore/data-table 3.0.9 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +67 -0
- package/lib/cjs/Body.js +8 -7
- package/lib/cjs/Body.js.map +1 -1
- package/lib/cjs/DataTable.js +38 -39
- package/lib/cjs/DataTable.js.map +1 -1
- package/lib/cjs/Head.js +6 -6
- package/lib/cjs/Head.js.map +1 -1
- package/lib/es6/Body.js +8 -7
- package/lib/es6/Body.js.map +1 -1
- package/lib/es6/DataTable.js +39 -40
- package/lib/es6/DataTable.js.map +1 -1
- package/lib/es6/Head.js +6 -6
- package/lib/es6/Head.js.map +1 -1
- package/lib/types/Body.d.ts +1 -0
- package/lib/types/DataTable.d.ts +4 -0
- package/package.json +2 -2
- package/src/Body.tsx +0 -262
- package/src/DataTable.tsx +0 -443
- package/src/Head.tsx +0 -142
- package/src/index.ts +0 -2
- package/src/style/data-table.shadow.css +0 -295
- package/src/style/scroll-area.shadow.css +0 -6
- package/src/types.ts +0 -53
- package/src/utils.ts +0 -55
package/src/Body.tsx
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Component, sstyled, Root } from '@semcore/core';
|
|
3
|
-
import { Box, Flex, IBoxProps } from '@semcore/flex-box';
|
|
4
|
-
import ScrollArea from '@semcore/scroll-area';
|
|
5
|
-
import { getFixedStyle, getScrollOffsetValue } from './utils';
|
|
6
|
-
import { RowData, Column, NestedCells, PropsLayer, Cell } from './types';
|
|
7
|
-
import assignProps, { callAllEventHandlers } from '@semcore/utils/lib/assignProps';
|
|
8
|
-
import ResizeObserver from 'resize-observer-polyfill';
|
|
9
|
-
import scrollStyles from './style/scroll-area.shadow.css';
|
|
10
|
-
import syncScroll from '@semcore/utils/lib/syncScroll';
|
|
11
|
-
import trottle from '@semcore/utils/lib/rafTrottle';
|
|
12
|
-
|
|
13
|
-
const testEnv = process.env.NODE_ENV === 'test';
|
|
14
|
-
|
|
15
|
-
const getCellsByColumn = (cells: NestedCells): RowData => {
|
|
16
|
-
const flattenCells = cells.flat(20) as Cell[];
|
|
17
|
-
return Object.fromEntries(flattenCells.map((cell) => [cell.name, cell.data]));
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
type AsProps = {
|
|
21
|
-
rows: NestedCells[];
|
|
22
|
-
columns: Column[];
|
|
23
|
-
$scrollRef: ReturnType<ReturnType<typeof syncScroll>>;
|
|
24
|
-
onResize: ResizeObserverCallback;
|
|
25
|
-
rowPropsLayers: PropsLayer[];
|
|
26
|
-
use: 'primary' | 'secondary';
|
|
27
|
-
uniqueKey: string;
|
|
28
|
-
virtualScroll?: boolean | { tollerance?: number; rowHeight?: number };
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type State = {
|
|
32
|
-
rowHeight: number | undefined;
|
|
33
|
-
scrollAreaHeight: undefined | number;
|
|
34
|
-
scrollOffset: number;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
class Body extends Component<AsProps, State> {
|
|
38
|
-
state: State = {
|
|
39
|
-
rowHeight: undefined,
|
|
40
|
-
scrollAreaHeight: undefined,
|
|
41
|
-
scrollOffset: 0,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
firstRowRef = React.createRef<HTMLElement>();
|
|
45
|
-
firstRowResizeObserver: ResizeObserver | null = null;
|
|
46
|
-
|
|
47
|
-
getRowHeight = () => {
|
|
48
|
-
const { virtualScroll } = this.asProps;
|
|
49
|
-
const rowHeightFromProps = typeof virtualScroll === 'object' && virtualScroll?.rowHeight;
|
|
50
|
-
return rowHeightFromProps || this.state.rowHeight;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
renderCells(cells: NestedCells, rowData: RowData, index: number) {
|
|
54
|
-
const SCell = Flex;
|
|
55
|
-
const { styles, columns, use } = this.asProps;
|
|
56
|
-
return cells.map((cell) => {
|
|
57
|
-
if (Array.isArray(cell)) {
|
|
58
|
-
const SGroupCell = 'div';
|
|
59
|
-
return sstyled(styles)(
|
|
60
|
-
<SGroupCell data-ui-name="group-cell">
|
|
61
|
-
{this.renderRows(cell as NestedCells[])}
|
|
62
|
-
</SGroupCell>,
|
|
63
|
-
);
|
|
64
|
-
} else {
|
|
65
|
-
const column = columns.find((c) => c.name === cell.name);
|
|
66
|
-
const [name, value] = getFixedStyle(cell, columns);
|
|
67
|
-
const vars = (Array.isArray(cell.cssVar) ? cell.cssVar : [cell.cssVar]).map(
|
|
68
|
-
(name) => `var(${name})`,
|
|
69
|
-
);
|
|
70
|
-
type CellProps = IBoxProps & {
|
|
71
|
-
name: string;
|
|
72
|
-
children: React.ReactNode;
|
|
73
|
-
style: React.CSSProperties;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
let props: CellProps = {
|
|
77
|
-
name: cell.name,
|
|
78
|
-
children: <>{cell.data}</>,
|
|
79
|
-
justifyContent: column?.props?.justifyContent,
|
|
80
|
-
style: {
|
|
81
|
-
width: vars.length === 1 ? vars[0] : `calc(${vars.join(' + ')})`,
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
if (name !== undefined && value !== undefined) {
|
|
85
|
-
props.style[name] = value;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const cellPropLayer of cell.cellPropsLayers || []) {
|
|
89
|
-
const { childrenPropsGetter = (p) => p, ...other } = cellPropLayer;
|
|
90
|
-
const propsCell = assignProps(other, props);
|
|
91
|
-
props = assignProps(childrenPropsGetter(propsCell, rowData, index), propsCell);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return sstyled(styles)(
|
|
95
|
-
<SCell key={cell.name} {...props} fixed={cell.fixed} theme={props.theme} use={use} />,
|
|
96
|
-
) as React.ReactElement;
|
|
97
|
-
}
|
|
98
|
-
}, [] as React.ReactElement[]);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
renderRow(
|
|
102
|
-
cells: NestedCells,
|
|
103
|
-
{ dataIndex, topOffset, nested }: { dataIndex: number; topOffset?: number; nested: boolean },
|
|
104
|
-
) {
|
|
105
|
-
const SRow = Box;
|
|
106
|
-
const { styles, rowPropsLayers, uniqueKey, virtualScroll } = this.asProps;
|
|
107
|
-
const rowHeightFromProps = typeof virtualScroll === 'object' && virtualScroll?.rowHeight;
|
|
108
|
-
|
|
109
|
-
const rowData = cells.flatRowData || getCellsByColumn(cells);
|
|
110
|
-
const key = rowData[uniqueKey] ? String(rowData[uniqueKey]) : `row_${dataIndex}`;
|
|
111
|
-
const needToMeasureHeight = dataIndex === 0 && !nested && !rowHeightFromProps;
|
|
112
|
-
|
|
113
|
-
let props = {
|
|
114
|
-
children: this.renderCells(cells, rowData, dataIndex),
|
|
115
|
-
theme: undefined,
|
|
116
|
-
active: undefined,
|
|
117
|
-
positioned: topOffset !== undefined,
|
|
118
|
-
top: topOffset,
|
|
119
|
-
ref: needToMeasureHeight ? this.firstRowRef : undefined,
|
|
120
|
-
key,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
for (const rowPropsLayer of rowPropsLayers) {
|
|
124
|
-
const { childrenPropsGetter = (p) => p, ...other } = rowPropsLayer;
|
|
125
|
-
const propsRow = assignProps(other, props);
|
|
126
|
-
props = assignProps(childrenPropsGetter(propsRow, rowData, dataIndex), propsRow);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return sstyled(styles)(<SRow {...props} />);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
renderRows(rows: NestedCells[]) {
|
|
133
|
-
return rows.map((cells, dataIndex) => this.renderRow(cells, { dataIndex, nested: false }));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
renderVirtualizedRows(rows: NestedCells[]) {
|
|
137
|
-
if (rows.length === 0) return [];
|
|
138
|
-
|
|
139
|
-
const { virtualScroll } = this.asProps;
|
|
140
|
-
const { scrollOffset, scrollAreaHeight } = this.state;
|
|
141
|
-
const rowHeight = this.getRowHeight();
|
|
142
|
-
|
|
143
|
-
const tollerance = (typeof virtualScroll === 'object' ? virtualScroll?.tollerance : 2) ?? 2;
|
|
144
|
-
const startIndex = Math.max(Math.floor(scrollOffset / rowHeight!) - tollerance, 0);
|
|
145
|
-
const lastIndex = Math.min(
|
|
146
|
-
Math.ceil((scrollOffset + scrollAreaHeight!) / rowHeight!) + tollerance,
|
|
147
|
-
rows.length,
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const rowHeightFromProps = typeof virtualScroll === 'object' && virtualScroll?.rowHeight;
|
|
151
|
-
const needToMeasureFirstRowHeight = !rowHeightFromProps;
|
|
152
|
-
|
|
153
|
-
const firstRow = { cells: rows[0], dataIndex: 0, topOffset: 0 };
|
|
154
|
-
const visibleRows = rowHeight !== undefined ? rows.slice(startIndex, lastIndex) : [];
|
|
155
|
-
const processedVisibleRows = visibleRows.map((cells, index) => ({
|
|
156
|
-
cells,
|
|
157
|
-
dataIndex: startIndex + index,
|
|
158
|
-
topOffset: rowHeight! * (startIndex + index),
|
|
159
|
-
}));
|
|
160
|
-
if (needToMeasureFirstRowHeight && startIndex !== 0) {
|
|
161
|
-
processedVisibleRows.unshift(firstRow);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return processedVisibleRows.map(({ cells, dataIndex, topOffset }) =>
|
|
165
|
-
this.renderRow(cells, { dataIndex, topOffset, nested: false }),
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
handleFirstRowResize = trottle((entries: ResizeObserverEntry[]) => {
|
|
170
|
-
const { contentRect } = entries[0];
|
|
171
|
-
const { height } = contentRect;
|
|
172
|
-
this.setState((oldState: State) => {
|
|
173
|
-
if (oldState.rowHeight === height) return oldState;
|
|
174
|
-
return { rowHeight: height };
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
handleScrollAreaResize = trottle((entries: ResizeObserverEntry[]) => {
|
|
179
|
-
const { virtualScroll } = this.asProps;
|
|
180
|
-
if (!virtualScroll) return;
|
|
181
|
-
const { contentRect } = entries[0];
|
|
182
|
-
const { height } = contentRect;
|
|
183
|
-
this.setState((oldState: State) => {
|
|
184
|
-
if (oldState.scrollAreaHeight === height) return oldState;
|
|
185
|
-
return { scrollAreaHeight: height };
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
handleScrollAreaScroll = (event: React.SyntheticEvent<HTMLElement>) => {
|
|
190
|
-
const { scrollTop } = event.target as HTMLElement;
|
|
191
|
-
const { virtualScroll } = this.asProps;
|
|
192
|
-
if (virtualScroll) {
|
|
193
|
-
this.setState((oldState: State) => {
|
|
194
|
-
if (oldState.scrollOffset === scrollTop) return oldState;
|
|
195
|
-
return { scrollOffset: scrollTop };
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
setupRowSizeObserver = () => {
|
|
201
|
-
if (!this.firstRowRef.current) return;
|
|
202
|
-
if (!this.asProps.virtualScroll) return;
|
|
203
|
-
this.firstRowResizeObserver = new ResizeObserver(this.handleFirstRowResize);
|
|
204
|
-
this.firstRowResizeObserver.observe(this.firstRowRef.current);
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
componentWillUnmount() {
|
|
208
|
-
this.firstRowResizeObserver?.disconnect();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
render() {
|
|
212
|
-
const SBody = Root;
|
|
213
|
-
const SBodyWrapper = Box;
|
|
214
|
-
const SScrollAreaBar = ScrollArea.Bar;
|
|
215
|
-
const SHeightHold = Box;
|
|
216
|
-
const { Children, styles, rows, columns, $scrollRef, virtualScroll, onResize } = this.asProps;
|
|
217
|
-
|
|
218
|
-
const columnsInitialized = columns.reduce((sum, { width }) => sum + width, 0) > 0 || testEnv;
|
|
219
|
-
|
|
220
|
-
const [offsetLeftSum, offsetRightSum] = getScrollOffsetValue(columns);
|
|
221
|
-
const offsetSum = offsetLeftSum + offsetRightSum;
|
|
222
|
-
|
|
223
|
-
const rowHeight = this.getRowHeight();
|
|
224
|
-
const holdHeight =
|
|
225
|
-
rowHeight !== undefined && virtualScroll ? rowHeight * rows.length : undefined;
|
|
226
|
-
|
|
227
|
-
if (virtualScroll && columnsInitialized && !rowHeight) {
|
|
228
|
-
new Promise(() => this.setupRowSizeObserver());
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return sstyled(styles)(
|
|
232
|
-
<SBodyWrapper>
|
|
233
|
-
<ScrollArea
|
|
234
|
-
shadow
|
|
235
|
-
styles={scrollStyles}
|
|
236
|
-
use:left={`${offsetLeftSum}px`}
|
|
237
|
-
use:right={`${offsetRightSum}px`}
|
|
238
|
-
onResize={callAllEventHandlers(onResize, this.handleScrollAreaResize)}
|
|
239
|
-
onScroll={this.handleScrollAreaScroll}
|
|
240
|
-
>
|
|
241
|
-
<ScrollArea.Container ref={$scrollRef}>
|
|
242
|
-
<SBody render={Box}>
|
|
243
|
-
{holdHeight && <SHeightHold hMin={holdHeight} aria-hidden={true} />}
|
|
244
|
-
{columnsInitialized && !virtualScroll ? this.renderRows(rows) : null}
|
|
245
|
-
{columnsInitialized && virtualScroll ? this.renderVirtualizedRows(rows) : null}
|
|
246
|
-
</SBody>
|
|
247
|
-
</ScrollArea.Container>
|
|
248
|
-
<SScrollAreaBar
|
|
249
|
-
orientation="horizontal"
|
|
250
|
-
left={`${offsetLeftSum}px`}
|
|
251
|
-
right={`${offsetRightSum}px`}
|
|
252
|
-
offsetSum={`${offsetSum}px`}
|
|
253
|
-
/>
|
|
254
|
-
<SScrollAreaBar orientation="vertical" />
|
|
255
|
-
</ScrollArea>
|
|
256
|
-
{Children.origin}
|
|
257
|
-
</SBodyWrapper>,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export default Body;
|
package/src/DataTable.tsx
DELETED
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import createComponent, { Component, sstyled, Root, PropGetterFn } from '@semcore/core';
|
|
3
|
-
import { Box, IBoxProps, IFlexProps } from '@semcore/flex-box';
|
|
4
|
-
import syncScroll from '@semcore/utils/lib/syncScroll';
|
|
5
|
-
import { callAllEventHandlers } from '@semcore/utils/lib/assignProps';
|
|
6
|
-
import fire from '@semcore/utils/lib/fire';
|
|
7
|
-
import { flattenColumns } from './utils';
|
|
8
|
-
import type {
|
|
9
|
-
RowData,
|
|
10
|
-
SortDirection,
|
|
11
|
-
PseudoChildPropsGetter,
|
|
12
|
-
PropsLayer,
|
|
13
|
-
NestedCells,
|
|
14
|
-
Column,
|
|
15
|
-
} from './types';
|
|
16
|
-
import Head from './Head';
|
|
17
|
-
import Body from './Body';
|
|
18
|
-
|
|
19
|
-
import style from './style/data-table.shadow.css';
|
|
20
|
-
|
|
21
|
-
const REVERSED_SORT_DIRECTION: { [direction in SortDirection]: SortDirection } = {
|
|
22
|
-
desc: 'asc',
|
|
23
|
-
asc: 'desc',
|
|
24
|
-
};
|
|
25
|
-
const DEFAULT_SORT_DIRECTION: SortDirection = 'desc';
|
|
26
|
-
|
|
27
|
-
const ROW_GROUP = Symbol('ROW_GROUP');
|
|
28
|
-
|
|
29
|
-
const cssVarReg = /[:;]/g;
|
|
30
|
-
|
|
31
|
-
const createCssVarForWidth = (name: string) => {
|
|
32
|
-
return `--${name.replace(cssVarReg, '_')}_width`;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
type AsProps = {
|
|
36
|
-
use: 'primary' | 'secondary';
|
|
37
|
-
sort: SortDirection[];
|
|
38
|
-
data: RowData[];
|
|
39
|
-
uniqueKey: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type HeadAsProps = {
|
|
43
|
-
children: React.ReactChild;
|
|
44
|
-
};
|
|
45
|
-
type BodyAsProps = {
|
|
46
|
-
children: React.ReactChild;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/* utils type */
|
|
50
|
-
type CProps<Props, Ctx = {}, UCProps = {}> = Props & {
|
|
51
|
-
children?: ((props: Props & Ctx, handlers: UCProps) => React.ReactNode) | React.ReactNode;
|
|
52
|
-
};
|
|
53
|
-
type ReturnEl = React.ReactElement | null;
|
|
54
|
-
type ChildRenderFn<Props> = Props & {
|
|
55
|
-
children?: (props: Props, column: DataTableData, index: number) => { [key: string]: unknown };
|
|
56
|
-
};
|
|
57
|
-
/* utils type */
|
|
58
|
-
|
|
59
|
-
export type DataTableData = { [key: string]: unknown };
|
|
60
|
-
export type DataTableSort = [string, 'desc' | 'asc'];
|
|
61
|
-
export type DataTableTheme = 'muted' | 'info' | 'success' | 'warning' | 'danger';
|
|
62
|
-
export type DataTableUse = 'primary' | 'secondary';
|
|
63
|
-
export type DataTableRow = DataTableCell[];
|
|
64
|
-
export type DataTableCell = {
|
|
65
|
-
/** Name of column */
|
|
66
|
-
name: string;
|
|
67
|
-
/** Data of column */
|
|
68
|
-
data: React.ReactNode;
|
|
69
|
-
[key: string]: unknown;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export interface IDataTableProps extends IBoxProps {
|
|
73
|
-
/** Theme for table
|
|
74
|
-
* @default primary
|
|
75
|
-
* */
|
|
76
|
-
use?: DataTableUse;
|
|
77
|
-
/** Data for table */
|
|
78
|
-
data?: DataTableData[];
|
|
79
|
-
/** Active sort object */
|
|
80
|
-
sort?: DataTableSort;
|
|
81
|
-
/** Handler call when will request change sort */
|
|
82
|
-
onSortChange?: (sort: DataTableSort, e?: React.SyntheticEvent) => void;
|
|
83
|
-
/** Field name in one data entity that is unique accross all set of data
|
|
84
|
-
* @default id
|
|
85
|
-
*/
|
|
86
|
-
uniqueKey?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface IDataTableHeadProps extends IBoxProps {
|
|
90
|
-
/** Sticky header table
|
|
91
|
-
* @deprecated
|
|
92
|
-
* */
|
|
93
|
-
sticky?: boolean;
|
|
94
|
-
|
|
95
|
-
/** Hidden header */
|
|
96
|
-
hidden?: boolean;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface IDataTableColumnProps extends IFlexProps {
|
|
100
|
-
/** Unique name column */
|
|
101
|
-
name?: string;
|
|
102
|
-
/** Enable sort for column also if you pass string you can set default sort */
|
|
103
|
-
sortable?: boolean | 'desc' | 'asc';
|
|
104
|
-
/** Enable resize for column
|
|
105
|
-
* @ignore */
|
|
106
|
-
resizable?: boolean;
|
|
107
|
-
/** Fixed column on the left/right */
|
|
108
|
-
fixed?: 'left' | 'right';
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface IDataTableBodyProps extends IBoxProps {
|
|
112
|
-
/** Rows table */
|
|
113
|
-
rows?: DataTableRow[];
|
|
114
|
-
/** When enabled, only visually acessable rows are rendered.
|
|
115
|
-
* `tollerance` property controls how many rows outside of viewport are render.
|
|
116
|
-
* `rowHeight` fixes the rows height if it known. If not provided, first row node height is measured.
|
|
117
|
-
* @default { tollerance: 2 }
|
|
118
|
-
*/
|
|
119
|
-
virtualScroll?: boolean | { tollerance?: number; rowHeight?: number };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface IDataTableRowProps extends IBoxProps {
|
|
123
|
-
/** Theme for row */
|
|
124
|
-
theme?: DataTableTheme;
|
|
125
|
-
/** Displays row as active/hover */
|
|
126
|
-
active?: boolean;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export interface IDataTableCellProps extends IFlexProps {
|
|
130
|
-
/** Unique name column or columns separated by / */
|
|
131
|
-
name: string;
|
|
132
|
-
/** Theme for cell */
|
|
133
|
-
theme?: DataTableTheme;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
class RootDefinitionTable extends Component<AsProps> {
|
|
137
|
-
static displayName = 'DefinitionTable';
|
|
138
|
-
|
|
139
|
-
static style = style;
|
|
140
|
-
|
|
141
|
-
static defaultProps = {
|
|
142
|
-
use: 'primary',
|
|
143
|
-
uniqueKey: 'id',
|
|
144
|
-
sort: [],
|
|
145
|
-
data: [],
|
|
146
|
-
} as AsProps;
|
|
147
|
-
|
|
148
|
-
columns: Column[] = [];
|
|
149
|
-
|
|
150
|
-
tableRef = React.createRef<HTMLElement>();
|
|
151
|
-
scrollBodyRef: null | ReturnType<ReturnType<typeof syncScroll>> = null;
|
|
152
|
-
scrollHeadRef: null | ReturnType<ReturnType<typeof syncScroll>> = null;
|
|
153
|
-
|
|
154
|
-
constructor(props: AsProps) {
|
|
155
|
-
super(props);
|
|
156
|
-
|
|
157
|
-
const createRef = syncScroll();
|
|
158
|
-
// first create body ref for master scroll
|
|
159
|
-
this.scrollBodyRef = createRef('body');
|
|
160
|
-
this.scrollHeadRef = createRef('head');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
handlerSortClick = (name: string, event: React.MouseEvent) => {
|
|
164
|
-
const column = this.columns.find((column) => column.name === name)!;
|
|
165
|
-
return fire(
|
|
166
|
-
this,
|
|
167
|
-
'onSortChange',
|
|
168
|
-
[
|
|
169
|
-
column.name,
|
|
170
|
-
column.active ? REVERSED_SORT_DIRECTION[column.sortDirection] : column.sortDirection,
|
|
171
|
-
],
|
|
172
|
-
event,
|
|
173
|
-
);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
handlerResize = () => {
|
|
177
|
-
this.forceUpdate();
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
scrollToUp = () => {
|
|
181
|
-
this.tableRef?.current?.scrollIntoView({
|
|
182
|
-
block: 'nearest',
|
|
183
|
-
inline: 'nearest',
|
|
184
|
-
behavior: 'smooth',
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
setVarStyle(columns: Column[]) {
|
|
189
|
-
for (const column of columns) {
|
|
190
|
-
if (Array.isArray(column.cssVar)) {
|
|
191
|
-
for (const cssVar of column.cssVar) {
|
|
192
|
-
this.tableRef.current?.style.setProperty(cssVar, `${column.width}px`);
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
this.tableRef.current?.style.setProperty(column.cssVar, `${column.width}px`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
childrenToColumns(
|
|
201
|
-
children: React.ReactNode,
|
|
202
|
-
options: { fixed?: 'left' | 'right' } = { fixed: undefined },
|
|
203
|
-
) {
|
|
204
|
-
const { sort } = this.asProps;
|
|
205
|
-
const columnsChildren: Column[] = [];
|
|
206
|
-
React.Children.forEach(children, (child) => {
|
|
207
|
-
if (!React.isValidElement(child)) return;
|
|
208
|
-
if (child.type !== DefinitionTable.Column) return;
|
|
209
|
-
|
|
210
|
-
let {
|
|
211
|
-
children,
|
|
212
|
-
name,
|
|
213
|
-
fixed = options.fixed,
|
|
214
|
-
resizable,
|
|
215
|
-
sortable,
|
|
216
|
-
...props
|
|
217
|
-
} = child.props as Column['props'];
|
|
218
|
-
const isGroup = !name;
|
|
219
|
-
let columns: Column[] = [];
|
|
220
|
-
|
|
221
|
-
if (isGroup) {
|
|
222
|
-
columns = this.childrenToColumns(children, { fixed });
|
|
223
|
-
name = flattenColumns(columns)
|
|
224
|
-
.map(({ name }) => name)
|
|
225
|
-
.join('/');
|
|
226
|
-
if (!columns.length) return;
|
|
227
|
-
children = React.Children.toArray(children).filter(
|
|
228
|
-
(child) => !(React.isValidElement(child) && child.type === DefinitionTable.Column),
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const column = this.columns.find((column) => column.name === name);
|
|
233
|
-
|
|
234
|
-
columnsChildren.push({
|
|
235
|
-
get width() {
|
|
236
|
-
return this.props.ref.current?.getBoundingClientRect().width || 0;
|
|
237
|
-
},
|
|
238
|
-
name,
|
|
239
|
-
cssVar: createCssVarForWidth(name),
|
|
240
|
-
fixed,
|
|
241
|
-
resizable,
|
|
242
|
-
active: sort[0] === name,
|
|
243
|
-
sortable,
|
|
244
|
-
sortDirection:
|
|
245
|
-
sort[0] === name
|
|
246
|
-
? sort[1]
|
|
247
|
-
: column?.sortDirection ||
|
|
248
|
-
(typeof sortable == 'string' ? sortable : DEFAULT_SORT_DIRECTION),
|
|
249
|
-
columns,
|
|
250
|
-
props: {
|
|
251
|
-
name,
|
|
252
|
-
ref: column?.props?.ref || React.createRef(),
|
|
253
|
-
children,
|
|
254
|
-
...props,
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
return columnsChildren;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
getHeadProps(props: HeadAsProps) {
|
|
262
|
-
const { use } = this.asProps;
|
|
263
|
-
const columnsChildren = this.childrenToColumns(props.children);
|
|
264
|
-
this.columns = flattenColumns(columnsChildren);
|
|
265
|
-
return {
|
|
266
|
-
$onSortClick: callAllEventHandlers(this.handlerSortClick, this.scrollToUp),
|
|
267
|
-
columnsChildren,
|
|
268
|
-
use,
|
|
269
|
-
onResize: this.handlerResize,
|
|
270
|
-
$scrollRef: this.scrollHeadRef,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
getBodyProps(props: BodyAsProps) {
|
|
275
|
-
const { data, use, uniqueKey } = this.asProps;
|
|
276
|
-
|
|
277
|
-
const cellPropsLayers: { [columnName: string]: PropsLayer[] } = {};
|
|
278
|
-
const rowPropsLayers: PropsLayer[] = [];
|
|
279
|
-
|
|
280
|
-
React.Children.forEach(props.children, (child) => {
|
|
281
|
-
if (React.isValidElement(child)) {
|
|
282
|
-
const { name, children, ...other } = child.props as {
|
|
283
|
-
name?: string;
|
|
284
|
-
children?: PseudoChildPropsGetter;
|
|
285
|
-
} & { [propName: string]: unknown };
|
|
286
|
-
if (child.type === DefinitionTable.Cell && name) {
|
|
287
|
-
name.split('/').forEach((name) => {
|
|
288
|
-
cellPropsLayers[name] = cellPropsLayers[name] || [];
|
|
289
|
-
cellPropsLayers[name].push({
|
|
290
|
-
...other,
|
|
291
|
-
childrenPropsGetter: children,
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
if (child.type === DefinitionTable.Row) {
|
|
296
|
-
rowPropsLayers.push({
|
|
297
|
-
...other,
|
|
298
|
-
childrenPropsGetter: children,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
const result = {
|
|
305
|
-
columns: this.columns,
|
|
306
|
-
rows: this.dataToRows(data, cellPropsLayers),
|
|
307
|
-
uniqueKey,
|
|
308
|
-
use,
|
|
309
|
-
rowPropsLayers,
|
|
310
|
-
$scrollRef: this.scrollBodyRef,
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
return result;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
dataToRows(data: RowData[], cellPropsLayers: { [columnName: string]: PropsLayer[] }) {
|
|
317
|
-
const parseData = (data: RowData[], exclude: { [columnName: string]: true }) =>
|
|
318
|
-
data.map((row) => {
|
|
319
|
-
const groupByName: {
|
|
320
|
-
[columnName: string]: {
|
|
321
|
-
groupedColumns: string[];
|
|
322
|
-
groupData: { [columnName: string]: unknown };
|
|
323
|
-
};
|
|
324
|
-
} = {};
|
|
325
|
-
const groupedColumns: { [columnname: string]: true } = {};
|
|
326
|
-
const ungroupedColumns: { [columnname: string]: true } = {};
|
|
327
|
-
for (const rowKey in row) {
|
|
328
|
-
const columnNames = rowKey.split('/');
|
|
329
|
-
if (columnNames.length >= 2) {
|
|
330
|
-
for (const column of columnNames) {
|
|
331
|
-
groupByName[column] = {
|
|
332
|
-
groupedColumns: columnNames,
|
|
333
|
-
groupData: row[rowKey] as { [columnName: string]: unknown },
|
|
334
|
-
};
|
|
335
|
-
groupedColumns[rowKey] = true;
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
ungroupedColumns[rowKey] = true;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
const rowsGroup = row[ROW_GROUP] || [];
|
|
342
|
-
const rowsGroupedNames = Object.fromEntries(
|
|
343
|
-
rowsGroup
|
|
344
|
-
.map((subRow) => Object.keys(subRow))
|
|
345
|
-
.flat()
|
|
346
|
-
.map((key) => [key, true]),
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
let isGroup = false;
|
|
350
|
-
|
|
351
|
-
const cells: NestedCells = this.columns
|
|
352
|
-
.map((column) => {
|
|
353
|
-
if (groupByName[column.name]) {
|
|
354
|
-
const { groupedColumns, groupData } = groupByName[column.name];
|
|
355
|
-
if (groupedColumns[0] === column.name) {
|
|
356
|
-
return {
|
|
357
|
-
name: groupedColumns.join('/'),
|
|
358
|
-
cssVar: groupedColumns.map(createCssVarForWidth),
|
|
359
|
-
fixed: column.fixed,
|
|
360
|
-
data: groupData,
|
|
361
|
-
cellPropsLayers: cellPropsLayers[column.name] || [],
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
} else if (column.name in row) {
|
|
365
|
-
return {
|
|
366
|
-
name: column.name,
|
|
367
|
-
cssVar: column.cssVar,
|
|
368
|
-
fixed: column.fixed,
|
|
369
|
-
data: row[column.name],
|
|
370
|
-
cellPropsLayers: cellPropsLayers[column.name] || [],
|
|
371
|
-
};
|
|
372
|
-
} else if (!isGroup && rowsGroupedNames[column.name]) {
|
|
373
|
-
// TODO: make it work not only with first group
|
|
374
|
-
isGroup = true;
|
|
375
|
-
return parseData(rowsGroup, {
|
|
376
|
-
...ungroupedColumns,
|
|
377
|
-
...groupedColumns,
|
|
378
|
-
});
|
|
379
|
-
} else if (!exclude[column.name] && !rowsGroupedNames[column.name]) {
|
|
380
|
-
return {
|
|
381
|
-
name: column.name,
|
|
382
|
-
cssVar: column.cssVar,
|
|
383
|
-
fixed: column.fixed,
|
|
384
|
-
data: null,
|
|
385
|
-
cellPropsLayers: cellPropsLayers[column.name] || [],
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
})
|
|
389
|
-
.filter((column) => column !== undefined)
|
|
390
|
-
.map((column) => column!);
|
|
391
|
-
|
|
392
|
-
cells.flatRowData = row;
|
|
393
|
-
return cells;
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
return parseData(data, {});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
componentDidUpdate() {
|
|
400
|
-
this.setVarStyle(this.columns);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
render() {
|
|
404
|
-
const SDataTable = Root;
|
|
405
|
-
const { Children, styles } = this.asProps;
|
|
406
|
-
|
|
407
|
-
return sstyled(styles)(
|
|
408
|
-
<SDataTable render={Box} __excludeProps={['data']} ref={this.tableRef}>
|
|
409
|
-
<Children />
|
|
410
|
-
</SDataTable>,
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
interface IDataTableCtx {
|
|
416
|
-
getHeadProps: PropGetterFn;
|
|
417
|
-
getBodyProps: PropGetterFn;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function ComponentDefinition() {
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const DefinitionTable = createComponent(
|
|
425
|
-
RootDefinitionTable,
|
|
426
|
-
{
|
|
427
|
-
Head,
|
|
428
|
-
Body,
|
|
429
|
-
Column: ComponentDefinition,
|
|
430
|
-
Cell: ComponentDefinition,
|
|
431
|
-
Row: ComponentDefinition,
|
|
432
|
-
},
|
|
433
|
-
{},
|
|
434
|
-
) as (<T>(props: CProps<IDataTableProps & T, IDataTableCtx>) => ReturnEl) & {
|
|
435
|
-
Head: <T>(props: IDataTableHeadProps & T) => ReturnEl;
|
|
436
|
-
Body: <T>(props: IDataTableBodyProps & T) => ReturnEl;
|
|
437
|
-
Column: <T>(props: IDataTableColumnProps & T) => ReturnEl;
|
|
438
|
-
Cell: <T>(props: ChildRenderFn<IDataTableCellProps & T>) => ReturnEl;
|
|
439
|
-
Row: <T>(props: ChildRenderFn<IDataTableRowProps & T>) => ReturnEl;
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
export { ROW_GROUP };
|
|
443
|
-
export default DefinitionTable;
|