@gravity-ui/react-data-table 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -20
- package/build/esm/lib/DataTable.d.ts +13 -1
- package/build/esm/lib/DataTable.js +44 -20
- package/build/esm/lib/DataTable.scss +20 -0
- package/build/esm/lib/ResizeHandler.d.ts +11 -0
- package/build/esm/lib/ResizeHandler.js +61 -0
- package/build/esm/lib/index.d.ts +1 -0
- package/build/esm/lib/index.js +1 -0
- package/build/esm/lib/types.d.ts +6 -0
- package/build/esm/lib/useTableResize.d.ts +9 -0
- package/build/esm/lib/useTableResize.js +25 -0
- package/build/esm/lib/util.d.ts +3 -0
- package/build/esm/lib/util.js +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ of the sort list. A simple Click sorts the table by this column only. Sorting or
|
|
|
25
25
|
| theme | `String` | | 'yandex-cloud' | The name of the theme. To use the built-in table theme, use the `'legacy'` value. For compatibility with @gravity-ui/uikit colors, use `'yandex-cloud'` (default) |
|
|
26
26
|
| onSort | `Function` | | | Sort listener `([{columnId: <string>, order: <DataTable.ASCENDING `|` DataTable.DESCENDING>}]) => void`. Use it to save the sorting state and pass it to the `initialSortOrder` on page refresh. |
|
|
27
27
|
| sortOrder | `Object` | | | Sorting in the format `{columnId: <string>, order: <DataTable.ASCENDING `|` DataTable.DESCENDING>}` or a list of such objects. |
|
|
28
|
+
| onResize | `Function` | | | Resize listener `(columnId: string, newWidth: number) => void`. Use it to save new column width. |
|
|
28
29
|
| visibleRowIndex | `Function` | | | A method `(row, index) => number` that returns the row index shown in the column enabled by the `displayIndices` flag. Use it to specify a correct row index when applying external sorting |
|
|
29
30
|
| onRowClick | `Function` | | | Click listener for a row `(row, index, event) => void` |
|
|
30
31
|
| nullBeforeNumbers | `Boolean` | | | A flag that sets the position of `null`/`undefined` values when sorting. If true, `null`/`undefined` values are shown at the top in the case of ascending order and at the bottom in the case of descending order. If `false`/`undefined`, they are shown at the bottom |
|
|
@@ -54,6 +55,7 @@ of the sort list. A simple Click sorts the table by this column only. Sorting or
|
|
|
54
55
|
| externalSort | `Boolean` | `false` | Disable data sorting on the component side. [Read more](#externalSort) |
|
|
55
56
|
| disableSortReset | `Boolean` | `false` | Disable sort reset (enables the `none -> default <-> reversed default` sort scheme) |
|
|
56
57
|
| defaultOrder | `DataTable.ASCENDING DataTable.DESCENDING` | `DataTable.ASCENDING` | Default sort order in the table |
|
|
58
|
+
| defaultResizeable | `Boolean` | `false` | Value that is used to determine whether column is resizeable, if no explicit value for column is set |
|
|
57
59
|
| highlightRows | `Boolean` | | Highlighting table rows on hover |
|
|
58
60
|
| stripedRows | `Boolean` | | Whether to color even/odd rows |
|
|
59
61
|
| headerMod | `multiline` `pre` | | Enable a multi-line (the `multiline` value) or pre-formatted header (the `pre` value) |
|
|
@@ -83,26 +85,29 @@ The cell will show `row.column_name` as it is.
|
|
|
83
85
|
|
|
84
86
|
The `name` field is the only mandatory field. The remaining fields are described below:
|
|
85
87
|
|
|
86
|
-
| Property
|
|
87
|
-
|
|
|
88
|
-
| name
|
|
89
|
-
| header
|
|
90
|
-
| headerTitle
|
|
91
|
-
| className
|
|
92
|
-
| width
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
88
|
+
| Property | Type | Description |
|
|
89
|
+
| :------------- | :------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
90
|
+
| name | `string` | Column name and default value for the `header` and `accessor` |
|
|
91
|
+
| header | `string` or `node` | Column header |
|
|
92
|
+
| headerTitle | `string` | Header title |
|
|
93
|
+
| className | `string` | Column class |
|
|
94
|
+
| width | `string \| number` | Column width |
|
|
95
|
+
| resizeable | `boolean` | Determines whether column width can be changed. Applied only if `onResize` function is passed. |
|
|
96
|
+
| resizeMinWidth | `number` | Min column width for resize. |
|
|
97
|
+
| resizeMaxWidth | `number` | Max column width for resize. |
|
|
98
|
+
| align | `DataTable.LEFT DataTable.CENTER DataTable.RIGHT` | Text alignment in a cell |
|
|
99
|
+
| accessor | `func` | Callback `(row) => (value)`. Use it if you want to display deeply "hidden" values in the column. For example: `(row) => (row.child1.child2[0].value)` |
|
|
100
|
+
| title | `string` or `func:string` | Callback `(row) => (title)`. Lets you set a fixed title (in the case of `string`) or a dynamic title for column cells. Returns a string. |
|
|
101
|
+
| render | `func:node` | Callback `({value, row, index}) => (node)`. Lets you render a cell in an arbitrary way. As an input, it gets a value, a whole row, or a row index. Returns a React node. |
|
|
102
|
+
| customStyle | `func` | Callback `({row, index, header, name}) => (styleObject)`. Lets you set a custom style for the cell. In the callback, you pass a string with data, an index, a column name, and a flag whether the cell is a header. At the output, you expect an object with styles following the React syntax. |
|
|
103
|
+
| onClick | `func` | Callback `(row, column) => ()`. The onClick listener on the cell. |
|
|
104
|
+
| sortable | `Boolean` | Whether the column is sortable. |
|
|
105
|
+
| defaultOrder | `DataTable.ASCENDING DataTable.DESCENDING` | A default sorting order in the column. |
|
|
106
|
+
| sortAccessor | `func` | Callback `(row) => (value)`. Use it if you want to sort by a value other than `value`, or if `value` is non-scalar. |
|
|
107
|
+
| sortAscending | `func` | Callback `({row, index, span?}, {row, index, span?}) => (sortValue)`. Use if you want to customize sorting completely. |
|
|
108
|
+
| sub | `array` | Nested columns |
|
|
109
|
+
| group | `Boolean` | Groups the same values into one cell, allowing you to create complex tables. [More details](#group) |
|
|
110
|
+
| autogroup | `Boolean` | Enables ascending sorting across all columns with group, resulting in autogrouping. Defaults to `true` |
|
|
106
111
|
|
|
107
112
|
### group
|
|
108
113
|
|
|
@@ -29,6 +29,7 @@ interface TableHeadProps<T> {
|
|
|
29
29
|
headColumns: HeadColumns<T>;
|
|
30
30
|
displayIndices?: boolean;
|
|
31
31
|
onSort?: TableProps<T>['onSort'];
|
|
32
|
+
onResize?: TableProps<T>['onResize'];
|
|
32
33
|
onColumnsUpdated?: (widths: number[]) => void;
|
|
33
34
|
renderedDataRows?: React.ReactNode;
|
|
34
35
|
onDataRowsHeightChange?: StickyHeadProps<T>['onDataRowsHeightChange'];
|
|
@@ -36,7 +37,7 @@ interface TableHeadProps<T> {
|
|
|
36
37
|
declare class TableHead<T> extends React.Component<TableHeadProps<T>> {
|
|
37
38
|
_dataRowsRef: HTMLTableSectionElement | null;
|
|
38
39
|
dataRowsHeightObserver?: HeightObserver;
|
|
39
|
-
renderedColumns:
|
|
40
|
+
renderedColumns: HTMLTableCellElement[];
|
|
40
41
|
componentDidMount(): void;
|
|
41
42
|
componentDidUpdate(): void;
|
|
42
43
|
componentWillUnmount(): void;
|
|
@@ -48,6 +49,7 @@ declare class TableHead<T> extends React.Component<TableHeadProps<T>> {
|
|
|
48
49
|
renderHeadLevel: (row: HeadColumnsItem<T>, rowIndex: number) => JSX.Element;
|
|
49
50
|
dataRowsRef: (ref: HTMLTableSectionElement) => void;
|
|
50
51
|
_getColumnRef: (index: number) => (node: HTMLTableHeaderCellElement) => void;
|
|
52
|
+
_getRenderedColumn: (index?: number) => HTMLTableCellElement | undefined;
|
|
51
53
|
}
|
|
52
54
|
interface StickyHeadProps<T> {
|
|
53
55
|
dataColumns: DataColumns<T>;
|
|
@@ -55,6 +57,7 @@ interface StickyHeadProps<T> {
|
|
|
55
57
|
mode: HeadPositionInner;
|
|
56
58
|
displayIndices?: Settings['displayIndices'];
|
|
57
59
|
onSort?: TableProps<T>['onSort'];
|
|
60
|
+
onResize?: TableProps<T>['onResize'];
|
|
58
61
|
top: number;
|
|
59
62
|
renderedDataRows: React.ReactNode;
|
|
60
63
|
onDataRowsHeightChange: (height: number) => void;
|
|
@@ -129,6 +132,7 @@ interface TableProps<T> {
|
|
|
129
132
|
rowKey: (row: T, index: number) => string | number;
|
|
130
133
|
startIndex: DataTableProps<T>['startIndex'];
|
|
131
134
|
onSort: DataTableView<T>['onSort'];
|
|
135
|
+
onResize: DataTableProps<T>['onResize'];
|
|
132
136
|
renderEmptyRow: unknown;
|
|
133
137
|
nullBeforeNumbers?: boolean;
|
|
134
138
|
getColSpansOfRow?: (p: Omit<TableRowProps<T>, 'getColSpansOfRow' | 'onClick' | 'className'>) => {
|
|
@@ -214,6 +218,7 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
214
218
|
sortable: boolean;
|
|
215
219
|
externalSort: boolean;
|
|
216
220
|
defaultOrder: OrderType;
|
|
221
|
+
defaultResizeable: boolean;
|
|
217
222
|
};
|
|
218
223
|
rowKey: (row: any, index: number) => any;
|
|
219
224
|
initialSortOrder: {};
|
|
@@ -243,6 +248,7 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
243
248
|
externalSort: boolean;
|
|
244
249
|
disableSortReset?: boolean | undefined;
|
|
245
250
|
defaultOrder: OrderType;
|
|
251
|
+
defaultResizeable: boolean;
|
|
246
252
|
highlightRows?: boolean | undefined;
|
|
247
253
|
stripedRows?: boolean | undefined;
|
|
248
254
|
headerMod?: "pre" | "multiline" | undefined;
|
|
@@ -270,6 +276,7 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
270
276
|
externalSort: boolean;
|
|
271
277
|
disableSortReset?: boolean | undefined;
|
|
272
278
|
defaultOrder: OrderType;
|
|
279
|
+
defaultResizeable: boolean;
|
|
273
280
|
highlightRows?: boolean | undefined;
|
|
274
281
|
stripedRows?: boolean | undefined;
|
|
275
282
|
headerMod?: "pre" | "multiline" | undefined;
|
|
@@ -299,6 +306,7 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
299
306
|
externalSort: boolean;
|
|
300
307
|
disableSortReset?: boolean | undefined;
|
|
301
308
|
defaultOrder: OrderType;
|
|
309
|
+
defaultResizeable: boolean;
|
|
302
310
|
highlightRows?: boolean | undefined;
|
|
303
311
|
stripedRows?: boolean | undefined;
|
|
304
312
|
headerMod?: "pre" | "multiline" | undefined;
|
|
@@ -328,6 +336,7 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
328
336
|
externalSort: boolean;
|
|
329
337
|
disableSortReset?: boolean | undefined;
|
|
330
338
|
defaultOrder: OrderType;
|
|
339
|
+
defaultResizeable: boolean;
|
|
331
340
|
highlightRows?: boolean | undefined;
|
|
332
341
|
stripedRows?: boolean | undefined;
|
|
333
342
|
headerMod?: "pre" | "multiline" | undefined;
|
|
@@ -340,6 +349,7 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
340
349
|
_tableRef: (node: Table<T>) => void;
|
|
341
350
|
renderMemoizedCell: ({ column, value, row, index, footer, headerData }: CellProps<T>) => JSX.Element;
|
|
342
351
|
getColumn: (column: Column<T>, columnIndex: number) => {
|
|
352
|
+
resizeable: boolean;
|
|
343
353
|
sortable: boolean;
|
|
344
354
|
_className: string;
|
|
345
355
|
_getValue: (row: T) => any;
|
|
@@ -382,6 +392,8 @@ declare class DataTableView<T> extends React.Component<DataTableProps<T>, DataTa
|
|
|
382
392
|
sortAccessor?: string | ((row: T) => any) | undefined;
|
|
383
393
|
sortAscending?: import("./types").Comparator<T> | undefined;
|
|
384
394
|
sub?: Column<T>[] | undefined;
|
|
395
|
+
resizeMinWidth?: number | undefined;
|
|
396
|
+
resizeMaxWidth?: number | undefined;
|
|
385
397
|
index: number;
|
|
386
398
|
columnIndex: number;
|
|
387
399
|
dataColumn: boolean;
|
|
@@ -2,11 +2,11 @@ import { __rest } from "tslib";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import ReactList from 'react-list';
|
|
4
4
|
import './DataTable.scss';
|
|
5
|
+
import { ResizeHandler } from './ResizeHandler';
|
|
5
6
|
import { ASCENDING, CENTER, DESCENDING, FIXED, INDEX_COLUMN, LEFT, MOVING, RIGHT } from './constants';
|
|
6
7
|
import { positionStickySupported } from './featureSupport';
|
|
7
8
|
import { HeightObserver } from './height-observer';
|
|
8
|
-
import {
|
|
9
|
-
const b = cn('data-table');
|
|
9
|
+
import { b, externalToInternalSortOrder, getSortOrder, getSortedData, internalToExternalSortOrder, } from './util';
|
|
10
10
|
const ICON_ASC = (React.createElement("svg", { className: b('icon'), viewBox: "0 0 10 6", width: "10", height: "6" },
|
|
11
11
|
React.createElement("path", { fill: "currentColor", d: "M0 5h10l-5 -5z" })));
|
|
12
12
|
const ICON_DESC = (React.createElement("svg", { className: b('icon'), viewBox: "0 0 10 6", width: "10", height: "6" },
|
|
@@ -66,14 +66,19 @@ class TableRow extends React.PureComponent {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
const value = column._getValue(row);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
let style = column.customStyle({
|
|
70
|
+
row,
|
|
71
|
+
index,
|
|
72
|
+
name: column.name,
|
|
73
|
+
header: false,
|
|
74
|
+
footer,
|
|
75
|
+
headerData,
|
|
76
|
+
});
|
|
77
|
+
// Fixed cell width for resizeable columns for proper content wrap
|
|
78
|
+
if (column.resizeable) {
|
|
79
|
+
style = Object.assign(Object.assign({}, style), { width: column.width, maxWidth: column.width });
|
|
80
|
+
}
|
|
81
|
+
return (React.createElement("td", { key: columnIndex, className: column._className, title: column._getTitle(row), style: style, colSpan: colSpans ? colSpans[column.name] : undefined, rowSpan: rowSpan, onClick: column._getOnClick({ row, index, footer, headerData }) }, column._renderValue({ value, row, index, footer, headerData })));
|
|
77
82
|
})));
|
|
78
83
|
}
|
|
79
84
|
}
|
|
@@ -86,13 +91,21 @@ class TableHead extends React.Component {
|
|
|
86
91
|
this._dataRowsRef = null;
|
|
87
92
|
this.renderedColumns = [];
|
|
88
93
|
this.renderHeadCell = (headCell) => {
|
|
94
|
+
var _a;
|
|
95
|
+
const { onResize } = this.props;
|
|
89
96
|
const { column, rowSpan, colSpan } = headCell;
|
|
90
|
-
const { sortable = false, header = column.name, className, index, columnIndex, align, } = column;
|
|
97
|
+
const { sortable = false, header = column.name, className, index, columnIndex, align, name, width, resizeable, resizeMinWidth, resizeMaxWidth, } = column;
|
|
91
98
|
const { headerTitle = (typeof header === 'string' && header) || undefined } = column;
|
|
92
|
-
|
|
99
|
+
let style = (_a = column.customStyle) === null || _a === void 0 ? void 0 : _a.call(column, { header: true, name });
|
|
100
|
+
// Fixed cell width for resizeable columns for proper content wrap
|
|
101
|
+
if (resizeable) {
|
|
102
|
+
style = Object.assign(Object.assign({}, style), { width, maxWidth: width });
|
|
103
|
+
}
|
|
104
|
+
return (React.createElement("th", { ref: column.dataColumn ? this._getColumnRef(columnIndex) : null, className: b('th', { sortable, align }, className), key: name, title: headerTitle, "data-index": index, colSpan: colSpan, rowSpan: rowSpan, style: style, onClick: this._getOnSortClick(column) },
|
|
93
105
|
React.createElement("div", { className: b('head-cell') },
|
|
94
106
|
header,
|
|
95
|
-
React.createElement(ColumnSortIcon, Object.assign({}, column)))
|
|
107
|
+
React.createElement(ColumnSortIcon, Object.assign({}, column))),
|
|
108
|
+
resizeable && (React.createElement(ResizeHandler, { getColumn: this._getRenderedColumn, columnIndex: columnIndex, onResize: onResize, columnId: name, minWidth: resizeMinWidth, maxWidth: resizeMaxWidth }))));
|
|
96
109
|
};
|
|
97
110
|
this.renderHeadLevel = (row, rowIndex) => {
|
|
98
111
|
return (React.createElement("tr", { key: rowIndex, className: b('head-row') }, row.map(this.renderHeadCell)));
|
|
@@ -109,6 +122,12 @@ class TableHead extends React.Component {
|
|
|
109
122
|
this.renderedColumns[index] = node;
|
|
110
123
|
};
|
|
111
124
|
};
|
|
125
|
+
this._getRenderedColumn = (index) => {
|
|
126
|
+
if (index) {
|
|
127
|
+
return this.renderedColumns[index];
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
};
|
|
112
131
|
}
|
|
113
132
|
componentDidMount() {
|
|
114
133
|
this._calculateColumnsWidth();
|
|
@@ -445,19 +464,19 @@ class Table extends React.PureComponent {
|
|
|
445
464
|
}
|
|
446
465
|
}
|
|
447
466
|
renderHead() {
|
|
448
|
-
const { columns, onSort } = this.props;
|
|
467
|
+
const { columns, onSort, onResize } = this.props;
|
|
449
468
|
const { displayIndices } = this.props.settings;
|
|
450
469
|
const rows = this.renderHeaderRows();
|
|
451
|
-
return (React.createElement(TableHead, Object.assign({ ref: this._refHead }, columns, { displayIndices: Boolean(displayIndices), onSort: onSort, onColumnsUpdated: this._onColumnsUpdated, renderedDataRows: rows })));
|
|
470
|
+
return (React.createElement(TableHead, Object.assign({ ref: this._refHead }, columns, { displayIndices: Boolean(displayIndices), onSort: onSort, onResize: onResize, onColumnsUpdated: this._onColumnsUpdated, renderedDataRows: rows })));
|
|
452
471
|
}
|
|
453
472
|
renderStickyHead() {
|
|
454
|
-
const { columns, onSort } = this.props;
|
|
473
|
+
const { columns, onSort, onResize } = this.props;
|
|
455
474
|
const { displayIndices, stickyTop, stickyHead } = this.props.settings;
|
|
456
475
|
const top = stickyTop === 'auto' && this._body && this._body.parentNode
|
|
457
476
|
? this._body.parentNode.offsetTop
|
|
458
477
|
: Number(stickyTop) || 0;
|
|
459
478
|
const rows = this.renderHeaderRows();
|
|
460
|
-
return (React.createElement(StickyHead, Object.assign({ mode: stickyHead, top: top, ref: this._refStickyHead }, columns, { displayIndices: displayIndices, onSort: onSort, renderedDataRows: rows, onDataRowsHeightChange: this.onMovingHeaderDataRowsHeightChange })));
|
|
479
|
+
return (React.createElement(StickyHead, Object.assign({ mode: stickyHead, top: top, ref: this._refStickyHead }, columns, { displayIndices: displayIndices, onSort: onSort, onResize: onResize, renderedDataRows: rows, onDataRowsHeightChange: this.onMovingHeaderDataRowsHeightChange })));
|
|
461
480
|
}
|
|
462
481
|
renderStickyFooter() {
|
|
463
482
|
const { columns } = this.props;
|
|
@@ -517,6 +536,8 @@ class DataTableView extends React.Component {
|
|
|
517
536
|
headerData }));
|
|
518
537
|
};
|
|
519
538
|
this.getColumn = (column, columnIndex) => {
|
|
539
|
+
var _a;
|
|
540
|
+
const { onResize } = this.props;
|
|
520
541
|
const { settings } = this.state;
|
|
521
542
|
const { defaultOrder } = settings;
|
|
522
543
|
const { sortOrder = {}, sortColumns, indexColumn } = this.state;
|
|
@@ -525,6 +546,7 @@ class DataTableView extends React.Component {
|
|
|
525
546
|
const { name, accessor = column.name, align, sortable = settings.sortable, group, autogroup = true, } = column;
|
|
526
547
|
const { sortAccessor, onClick } = column;
|
|
527
548
|
const _className = b('td', { align }, column.className);
|
|
549
|
+
const resizeable = ((_a = column.resizeable) !== null && _a !== void 0 ? _a : settings.defaultResizeable) && Boolean(onResize);
|
|
528
550
|
const _getValue = typeof accessor === 'function'
|
|
529
551
|
? (row) => accessor(row)
|
|
530
552
|
: (row) => {
|
|
@@ -543,7 +565,7 @@ class DataTableView extends React.Component {
|
|
|
543
565
|
const _getOnClick = typeof onClick === 'function'
|
|
544
566
|
? (data) => (event) => onClick(data, column, event)
|
|
545
567
|
: () => undefined;
|
|
546
|
-
return Object.assign(Object.assign({ index: columnIndex - indexAdjustment, columnIndex, dataColumn: true, defaultOrder }, column), { sortable: sortable && isSortEnabled, _className,
|
|
568
|
+
return Object.assign(Object.assign({ index: columnIndex - indexAdjustment, columnIndex, dataColumn: true, defaultOrder }, column), { resizeable, sortable: sortable && isSortEnabled, _className,
|
|
547
569
|
_getValue,
|
|
548
570
|
_getTitle,
|
|
549
571
|
_getSortValue,
|
|
@@ -606,6 +628,7 @@ class DataTableView extends React.Component {
|
|
|
606
628
|
return startIndex + index;
|
|
607
629
|
},
|
|
608
630
|
sortable: false,
|
|
631
|
+
resizeable: false,
|
|
609
632
|
width: 20 + Math.ceil(Math.log10(lastIndex)) * 10,
|
|
610
633
|
};
|
|
611
634
|
}
|
|
@@ -615,7 +638,7 @@ class DataTableView extends React.Component {
|
|
|
615
638
|
? Object.assign({}, externalToInternalSortOrder(nextProps.sortOrder, nextProps.settings)) : undefined));
|
|
616
639
|
}
|
|
617
640
|
render() {
|
|
618
|
-
const { getColSpansOfRow, headerData, data, footerData, columns, startIndex, emptyDataMessage, rowClassName, rowKey, onRowClick, theme, renderEmptyRow, nullBeforeNumbers, } = this.props;
|
|
641
|
+
const { getColSpansOfRow, headerData, data, footerData, columns, startIndex, emptyDataMessage, rowClassName, rowKey, onRowClick, onResize, theme, renderEmptyRow, nullBeforeNumbers, } = this.props;
|
|
619
642
|
const { settings, sortOrder, sortColumns } = this.state;
|
|
620
643
|
const { highlightRows = false, stripedRows = false, headerMod = false } = settings;
|
|
621
644
|
const tableClassName = b({
|
|
@@ -634,7 +657,7 @@ class DataTableView extends React.Component {
|
|
|
634
657
|
}, {
|
|
635
658
|
nullBeforeNumbers,
|
|
636
659
|
externalSort: settings === null || settings === void 0 ? void 0 : settings.externalSort,
|
|
637
|
-
}), footerData: footerData, onSort: this.onSort }));
|
|
660
|
+
}), footerData: footerData, onSort: this.onSort, onResize: onResize }));
|
|
638
661
|
}
|
|
639
662
|
getComplexColumns(columns) {
|
|
640
663
|
const headColumns = [];
|
|
@@ -689,6 +712,7 @@ DataTableView.defaultProps = {
|
|
|
689
712
|
sortable: true,
|
|
690
713
|
externalSort: false,
|
|
691
714
|
defaultOrder: ASCENDING,
|
|
715
|
+
defaultResizeable: false,
|
|
692
716
|
},
|
|
693
717
|
rowKey: (row, index) => Object.prototype.hasOwnProperty.call(row, 'id') ? row.id : index,
|
|
694
718
|
initialSortOrder: {},
|
|
@@ -67,6 +67,7 @@ $cell-border: 1px solid var(--data-table-border-color);
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
&__th {
|
|
70
|
+
box-sizing: border-box;
|
|
70
71
|
font-weight: 500;
|
|
71
72
|
cursor: default;
|
|
72
73
|
padding: var(--data-table-header-vertical-padding) var(--data-table-cell-horizontal-padding);
|
|
@@ -98,6 +99,7 @@ $cell-border: 1px solid var(--data-table-border-color);
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
&__td {
|
|
102
|
+
box-sizing: border-box;
|
|
101
103
|
padding: var(--data-table-cell-vertical-padding) var(--data-table-cell-horizontal-padding);
|
|
102
104
|
border: $cell-border;
|
|
103
105
|
vertical-align: var(--data-table-cell-align);
|
|
@@ -267,4 +269,22 @@ $cell-border: 1px solid var(--data-table-border-color);
|
|
|
267
269
|
--data-table-color-hover-area: #ffeba0;
|
|
268
270
|
--data-table-color-footer-area: var(--data-table-color-base);
|
|
269
271
|
}
|
|
272
|
+
|
|
273
|
+
&__resize-handler {
|
|
274
|
+
visibility: hidden;
|
|
275
|
+
position: absolute;
|
|
276
|
+
right: 0;
|
|
277
|
+
top: 0;
|
|
278
|
+
cursor: col-resize;
|
|
279
|
+
width: 6px;
|
|
280
|
+
height: 100%;
|
|
281
|
+
background-color: var(--g-color-base-generic);
|
|
282
|
+
|
|
283
|
+
&_resizing {
|
|
284
|
+
visibility: visible;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
&__th:hover > &__resize-handler {
|
|
288
|
+
visibility: visible;
|
|
289
|
+
}
|
|
270
290
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface ResizeHandlerProps {
|
|
3
|
+
getColumn: (index?: number) => HTMLTableCellElement | undefined;
|
|
4
|
+
columnIndex?: number;
|
|
5
|
+
columnId: string;
|
|
6
|
+
maxWidth?: number;
|
|
7
|
+
minWidth?: number;
|
|
8
|
+
onResize?: (columnId: string, width: number) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function ResizeHandler({ getColumn, columnIndex, columnId, minWidth, maxWidth, onResize, }: ResizeHandlerProps): JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { b, calculateColumnWidth, rafThrottle } from './util';
|
|
3
|
+
export function ResizeHandler({ getColumn, columnIndex, columnId, minWidth, maxWidth, onResize, }) {
|
|
4
|
+
const elementRef = React.useRef(null);
|
|
5
|
+
const [resizing, setResizing] = React.useState(false);
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
const element = elementRef.current;
|
|
8
|
+
if (!element) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
let mouseXPosition;
|
|
12
|
+
let initialColumnWidth;
|
|
13
|
+
let currentColumnWidth;
|
|
14
|
+
const onMouseMove = rafThrottle((e) => {
|
|
15
|
+
restrictMouseEvent(e);
|
|
16
|
+
if (typeof mouseXPosition !== 'number' || typeof initialColumnWidth !== 'number') {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const xDiff = e.clientX - mouseXPosition;
|
|
20
|
+
const newWidth = calculateColumnWidth(initialColumnWidth + xDiff, minWidth, maxWidth);
|
|
21
|
+
if (newWidth === currentColumnWidth) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
currentColumnWidth = newWidth;
|
|
25
|
+
onResize === null || onResize === void 0 ? void 0 : onResize(columnId, currentColumnWidth);
|
|
26
|
+
});
|
|
27
|
+
const onMouseUp = (e) => {
|
|
28
|
+
restrictMouseEvent(e);
|
|
29
|
+
if (currentColumnWidth !== undefined) {
|
|
30
|
+
onResize === null || onResize === void 0 ? void 0 : onResize(columnId, currentColumnWidth);
|
|
31
|
+
}
|
|
32
|
+
setResizing(false);
|
|
33
|
+
mouseXPosition = undefined;
|
|
34
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
35
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
36
|
+
};
|
|
37
|
+
const onMouseDown = (e) => {
|
|
38
|
+
var _a;
|
|
39
|
+
initialColumnWidth = (_a = getColumn(columnIndex)) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width;
|
|
40
|
+
restrictMouseEvent(e);
|
|
41
|
+
mouseXPosition = e.clientX;
|
|
42
|
+
setResizing(true);
|
|
43
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
44
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
45
|
+
};
|
|
46
|
+
element.addEventListener('mousedown', onMouseDown);
|
|
47
|
+
return () => {
|
|
48
|
+
element.removeEventListener('mousedown', onMouseDown);
|
|
49
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
50
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
51
|
+
};
|
|
52
|
+
}, [columnId, onResize, minWidth, maxWidth, getColumn, columnIndex]);
|
|
53
|
+
return (React.createElement("span", { ref: elementRef, className: b('resize-handler', { resizing }),
|
|
54
|
+
// Prevent sort trigger on resize
|
|
55
|
+
onClick: (e) => restrictMouseEvent(e) }));
|
|
56
|
+
}
|
|
57
|
+
// Prevent sort trigger and text selection on resize
|
|
58
|
+
function restrictMouseEvent(e) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
}
|
package/build/esm/lib/index.d.ts
CHANGED
package/build/esm/lib/index.js
CHANGED
package/build/esm/lib/types.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface DataTableProps<T> {
|
|
|
24
24
|
sortOrder?: SortOrder | SortOrder[];
|
|
25
25
|
theme: THEMES | string;
|
|
26
26
|
onSort?: (sortOrder: DataTableProps<T>['initialSortOrder']) => void;
|
|
27
|
+
onResize?: HandleResize;
|
|
27
28
|
onRowClick?: (row: T, index: number, event: React.MouseEvent<HTMLTableRowElement>) => void;
|
|
28
29
|
onError?: (error: unknown) => void;
|
|
29
30
|
nullBeforeNumbers?: boolean;
|
|
@@ -61,6 +62,9 @@ export interface Column<T> {
|
|
|
61
62
|
sub?: Column<T>[];
|
|
62
63
|
group?: boolean;
|
|
63
64
|
autogroup?: boolean;
|
|
65
|
+
resizeable?: boolean;
|
|
66
|
+
resizeMinWidth?: number;
|
|
67
|
+
resizeMaxWidth?: number;
|
|
64
68
|
}
|
|
65
69
|
export type DynamicInnerRefT = ReactList;
|
|
66
70
|
export interface Settings {
|
|
@@ -84,6 +88,7 @@ export interface Settings {
|
|
|
84
88
|
externalSort?: boolean;
|
|
85
89
|
disableSortReset?: boolean;
|
|
86
90
|
defaultOrder?: OrderType;
|
|
91
|
+
defaultResizeable?: boolean;
|
|
87
92
|
highlightRows?: boolean;
|
|
88
93
|
stripedRows?: boolean;
|
|
89
94
|
headerMod?: 'multiline' | 'pre';
|
|
@@ -112,4 +117,5 @@ export interface SortedDataItem<T> {
|
|
|
112
117
|
};
|
|
113
118
|
}
|
|
114
119
|
export type Comparator<T> = (l: SortedDataItem<T>, r: SortedDataItem<T>) => number;
|
|
120
|
+
export type HandleResize = (columnId: string, newWidth: number) => void;
|
|
115
121
|
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Column, HandleResize } from './types';
|
|
2
|
+
export type ColumnWidthByName = Record<string, number>;
|
|
3
|
+
export type SaveColumnWidthByName = (data: ColumnWidthByName) => void;
|
|
4
|
+
export type GetSavedColumnWidthByName = () => ColumnWidthByName;
|
|
5
|
+
export declare function updateColumnsWidth<T>(columns: Column<T>[], columnsWidthSetup: ColumnWidthByName, defaultResizeable?: boolean): Column<T>[];
|
|
6
|
+
export declare function useTableResize({ saveSizes, getSizes, }: {
|
|
7
|
+
saveSizes: SaveColumnWidthByName;
|
|
8
|
+
getSizes: GetSavedColumnWidthByName;
|
|
9
|
+
}): [ColumnWidthByName, HandleResize];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export function updateColumnsWidth(columns, columnsWidthSetup, defaultResizeable = false) {
|
|
3
|
+
return columns.map((column) => {
|
|
4
|
+
var _a;
|
|
5
|
+
let sub;
|
|
6
|
+
if (column.sub) {
|
|
7
|
+
sub = updateColumnsWidth(column.sub, columnsWidthSetup, defaultResizeable);
|
|
8
|
+
}
|
|
9
|
+
const newWidth = (_a = columnsWidthSetup[column.name]) !== null && _a !== void 0 ? _a : column.width;
|
|
10
|
+
return Object.assign(Object.assign({}, column), { width: newWidth, sub });
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function useTableResize({ saveSizes, getSizes, }) {
|
|
14
|
+
const [tableColumnsWidthSetup, setTableColumnsWidth] = React.useState(() => {
|
|
15
|
+
return getSizes();
|
|
16
|
+
});
|
|
17
|
+
const handleSetupChange = React.useCallback((columnId, columnWidth) => {
|
|
18
|
+
setTableColumnsWidth((previousSetup) => {
|
|
19
|
+
const setup = Object.assign(Object.assign({}, previousSetup), { [columnId]: columnWidth });
|
|
20
|
+
saveSizes(setup);
|
|
21
|
+
return setup;
|
|
22
|
+
});
|
|
23
|
+
}, [saveSizes]);
|
|
24
|
+
return [tableColumnsWidthSetup, handleSetupChange];
|
|
25
|
+
}
|
package/build/esm/lib/util.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ColumnExtended, NameToOrderTypeMap, SortOrderWithSortColumns } from './DataTable';
|
|
2
2
|
import { OrderType, Settings, SortOrder, SortedDataItem } from './types';
|
|
3
3
|
export declare const cn: import("@bem-react/classname").ClassNameInitilizer;
|
|
4
|
+
export declare const b: import("@bem-react/classname").ClassNameFormatter;
|
|
4
5
|
export type SlimColumn = {
|
|
5
6
|
name: string;
|
|
6
7
|
defaultOrder?: OrderType;
|
|
@@ -32,4 +33,6 @@ export declare function internalToExternalSortOrder(sortOrder: NameToOrderTypeMa
|
|
|
32
33
|
columnId: string;
|
|
33
34
|
order: OrderType;
|
|
34
35
|
}[];
|
|
36
|
+
export declare function rafThrottle<Fn extends (...args: any[]) => any>(fn: Fn): (...args: Parameters<Fn>) => void;
|
|
37
|
+
export declare function calculateColumnWidth(newWidth: number, minWidth?: number, maxWidth?: number): number;
|
|
35
38
|
export {};
|
package/build/esm/lib/util.js
CHANGED
|
@@ -2,6 +2,7 @@ import { __rest } from "tslib";
|
|
|
2
2
|
import { withNaming } from '@bem-react/classname';
|
|
3
3
|
import { ASCENDING, DESCENDING } from './constants';
|
|
4
4
|
export const cn = withNaming({ e: '__', m: '_' });
|
|
5
|
+
export const b = cn('data-table');
|
|
5
6
|
export function getSortOrder({ name: columnName, defaultOrder: columnDefaultOrder }, { sortOrder = {}, sortColumns = [] }, multiColumnSort = false, { defaultOrder: settingsDefaultOrder, disableSortReset, } = {}) {
|
|
6
7
|
const defaultOrder = columnDefaultOrder || settingsDefaultOrder;
|
|
7
8
|
const emptyResult = {
|
|
@@ -146,3 +147,24 @@ export function internalToExternalSortOrder(sortOrder) {
|
|
|
146
147
|
};
|
|
147
148
|
});
|
|
148
149
|
}
|
|
150
|
+
// invoke passed function at most once per animation frame
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
+
export function rafThrottle(fn) {
|
|
153
|
+
let rafId = null;
|
|
154
|
+
let latestArgs;
|
|
155
|
+
return function throttled(...args) {
|
|
156
|
+
// call throttled function with latest args
|
|
157
|
+
latestArgs = args;
|
|
158
|
+
if (typeof rafId === 'number') {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
rafId = requestAnimationFrame(() => {
|
|
162
|
+
fn(...latestArgs);
|
|
163
|
+
rafId = null;
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// 40px minWidth so sort icon won't overlap wrapped column title
|
|
168
|
+
export function calculateColumnWidth(newWidth, minWidth = 40, maxWidth = Infinity) {
|
|
169
|
+
return Math.max(minWidth, Math.min(newWidth, maxWidth));
|
|
170
|
+
}
|