@gravity-ui/react-data-table 1.0.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.
@@ -0,0 +1,838 @@
1
+ import { __rest } from "tslib";
2
+ import cn from 'bem-cn-lite';
3
+ import PropTypes from 'prop-types';
4
+ import * as React from 'react';
5
+ import ReactList from 'react-list';
6
+ import './DataTable.scss';
7
+ import { ASCENDING, CENTER, DESCENDING, FIXED, INDEX_COLUMN, LEFT, MOVING, RIGHT } from './constants';
8
+ import { positionStickySupported } from './featureSupport';
9
+ import { HeightObserver } from './height-observer';
10
+ import { externalToInternalSortOrder, getSortOrder, getSortedData, internalToExternalSortOrder, } from './util';
11
+ const b = cn('data-table');
12
+ const ICON_ASC = (React.createElement("svg", { className: b('icon'), viewBox: "0 0 10 6", width: "10", height: "6" },
13
+ React.createElement("path", { fill: "currentColor", d: "M0 5h10l-5 -5z" })));
14
+ const ICON_DESC = (React.createElement("svg", { className: b('icon'), viewBox: "0 0 10 6", width: "10", height: "6" },
15
+ React.createElement("path", { fill: "currentColor", d: "M0 1h10l-5 5z" })));
16
+ const ICONS = {
17
+ ICON_ASC,
18
+ ICON_DESC,
19
+ };
20
+ function getSortIcon(order) {
21
+ switch (order) {
22
+ case ASCENDING:
23
+ return ICONS.ICON_ASC;
24
+ case DESCENDING:
25
+ return ICONS.ICON_DESC;
26
+ default:
27
+ return false;
28
+ }
29
+ }
30
+ const ColumnSortIcon = ({ sortOrder, sortIndex, sortable, defaultOrder }) => {
31
+ if (sortable) {
32
+ return (React.createElement("span", { className: b('sort-icon', { shadow: !sortOrder }), "data-index": sortIndex }, getSortIcon(sortOrder || defaultOrder)));
33
+ }
34
+ else {
35
+ return null;
36
+ }
37
+ };
38
+ ColumnSortIcon.propTypes = {
39
+ sortOrder: PropTypes.oneOf([ASCENDING, DESCENDING]),
40
+ sortable: PropTypes.bool,
41
+ defaultOrder: PropTypes.oneOf([ASCENDING, DESCENDING]),
42
+ };
43
+ class TableRow extends React.PureComponent {
44
+ constructor() {
45
+ super(...arguments);
46
+ this.onClick = (event) => {
47
+ if (this.props.onClick) {
48
+ const { row, index } = this.props;
49
+ this.props.onClick(row, index, event);
50
+ }
51
+ };
52
+ }
53
+ render() {
54
+ const { className, columns, row, index, odd, footer, span, headerData } = this.props;
55
+ return (React.createElement("tr", { className: b('row', { odd, footer, 'header-data': headerData }, className), onClick: this.onClick }, columns.map((column, columnIndex) => {
56
+ let rowSpan;
57
+ if (span) {
58
+ if (span[column.name] === 0) {
59
+ return null;
60
+ }
61
+ else {
62
+ rowSpan = span[column.name];
63
+ }
64
+ }
65
+ const value = column._getValue(row);
66
+ return (React.createElement("td", { key: columnIndex, className: column._className, title: column._getTitle(row), style: column.customStyle({
67
+ row,
68
+ index,
69
+ name: column.name,
70
+ header: false,
71
+ footer,
72
+ headerData,
73
+ }), rowSpan: rowSpan, onClick: column._getOnClick({ row, index, footer, headerData }) }, column._renderValue({ value, row, index, footer, headerData })));
74
+ })));
75
+ }
76
+ }
77
+ TableRow.defaultProps = {
78
+ footer: false,
79
+ };
80
+ class TableHead extends React.Component {
81
+ constructor() {
82
+ super(...arguments);
83
+ this._dataRowsRef = null;
84
+ this.renderedColumns = [];
85
+ this.renderHeadCell = (headCell) => {
86
+ const { column, rowSpan, colSpan } = headCell;
87
+ const { sortable = false, header = column.name, className, index, columnIndex, align, } = column;
88
+ const { headerTitle = (typeof header === 'string' && header) || undefined } = column;
89
+ return (React.createElement("th", { ref: column.dataColumn ? this._getColumnRef(columnIndex) : null, className: b('th', { sortable, align }, className), key: column.name, title: headerTitle, "data-index": index, colSpan: colSpan, rowSpan: rowSpan, style: column.customStyle && column.customStyle({ header: true, name: column.name }), onClick: this._getOnSortClick(column) },
90
+ React.createElement("div", { className: b('head-cell') },
91
+ header,
92
+ React.createElement(ColumnSortIcon, Object.assign({}, column)))));
93
+ };
94
+ this.renderHeadLevel = (row, rowIndex) => {
95
+ return (React.createElement("tr", { key: rowIndex, className: b('head-row') }, row.map(this.renderHeadCell)));
96
+ };
97
+ this.dataRowsRef = (ref) => {
98
+ var _a;
99
+ this._dataRowsRef = ref;
100
+ if (ref) {
101
+ (_a = this.dataRowsHeightObserver) === null || _a === void 0 ? void 0 : _a.checkAndUpdateHeight();
102
+ }
103
+ };
104
+ this._getColumnRef = (index) => {
105
+ return (node) => {
106
+ this.renderedColumns[index] = node;
107
+ };
108
+ };
109
+ }
110
+ componentDidMount() {
111
+ this._calculateColumnsWidth();
112
+ if ('function' === typeof this.props.onDataRowsHeightChange) {
113
+ this.dataRowsHeightObserver = new HeightObserver({
114
+ getSrcElement: () => this._dataRowsRef,
115
+ onHeightChange: (height) => {
116
+ if ('function' === typeof this.props.onDataRowsHeightChange) {
117
+ this.props.onDataRowsHeightChange(height);
118
+ }
119
+ },
120
+ });
121
+ }
122
+ }
123
+ componentDidUpdate() {
124
+ var _a;
125
+ this._calculateColumnsWidth();
126
+ (_a = this.dataRowsHeightObserver) === null || _a === void 0 ? void 0 : _a.checkAndUpdateHeight();
127
+ }
128
+ componentWillUnmount() {
129
+ var _a;
130
+ (_a = this.dataRowsHeightObserver) === null || _a === void 0 ? void 0 : _a.destroy();
131
+ }
132
+ render() {
133
+ const { headColumns, dataColumns, renderedDataRows } = this.props;
134
+ this.renderedColumns.length = dataColumns.length;
135
+ return (React.createElement(React.Fragment, null,
136
+ React.createElement("thead", { className: b('head') }, headColumns.map(this.renderHeadLevel)),
137
+ renderedDataRows === undefined ? null : (React.createElement("tbody", { ref: this.dataRowsRef }, renderedDataRows))));
138
+ }
139
+ _calculateColumnsWidth() {
140
+ const { onColumnsUpdated } = this.props;
141
+ if (typeof onColumnsUpdated === 'function') {
142
+ requestAnimationFrame(() => {
143
+ const widths = this.renderedColumns.map((col) => col && col.getBoundingClientRect().width);
144
+ onColumnsUpdated(widths);
145
+ });
146
+ }
147
+ }
148
+ onSort(column, multisort) {
149
+ const { onSort } = this.props;
150
+ if (typeof onSort === 'function') {
151
+ onSort(column, multisort);
152
+ }
153
+ }
154
+ _getOnSortClick(column) {
155
+ const { sortable = false, name } = column;
156
+ if (name === INDEX_COLUMN) {
157
+ return () => {
158
+ this.onSort();
159
+ };
160
+ }
161
+ return sortable
162
+ ? (event) => {
163
+ this.onSort(column, event.ctrlKey || event.metaKey);
164
+ }
165
+ : undefined;
166
+ }
167
+ }
168
+ TableHead.propTypes = {
169
+ headColumns: PropTypes.array.isRequired,
170
+ dataColumns: PropTypes.array.isRequired,
171
+ displayIndices: PropTypes.bool.isRequired,
172
+ onSort: PropTypes.func,
173
+ onColumnsUpdated: PropTypes.func,
174
+ renderedDataRows: PropTypes.node,
175
+ };
176
+ class StickyHead extends React.Component {
177
+ constructor() {
178
+ super(...arguments);
179
+ this.state = {
180
+ style: { top: StickyHead.defaultProps.top },
181
+ };
182
+ this.onDataRowsHeightChange = (height) => {
183
+ this.props.onDataRowsHeightChange(height + 1);
184
+ };
185
+ this._nodeRef = (node) => {
186
+ this._node = node;
187
+ };
188
+ }
189
+ static getDerivedStateFromProps(props, state) {
190
+ var _a;
191
+ if (props.top !== ((_a = state.style) === null || _a === void 0 ? void 0 : _a.top)) {
192
+ return props.top === undefined ? null : { style: { top: props.top } };
193
+ }
194
+ return null;
195
+ }
196
+ render() {
197
+ const _a = this.props, { mode, top: _top } = _a, props = __rest(_a, ["mode", "top"]);
198
+ if (mode === MOVING) {
199
+ const { style } = this.state;
200
+ return (React.createElement("div", { className: b('sticky', { moving: true, head: true }), style: style }, this.renderHeader(props)));
201
+ }
202
+ else {
203
+ const { widths = [], right = 0 } = this.state;
204
+ const totalWidth = widths.reduce((sum, val) => sum + val, 0);
205
+ return (React.createElement("div", { ref: this._nodeRef, className: b('sticky', { fixed: true, head: true }), style: { right: right, display: totalWidth ? undefined : 'none' } }, this.renderHeader(props)));
206
+ }
207
+ }
208
+ setScrollLeft(scrollLeft) {
209
+ requestAnimationFrame(() => {
210
+ if (this._node) {
211
+ this._node.scrollLeft = scrollLeft;
212
+ }
213
+ });
214
+ }
215
+ setRightPosition(value) {
216
+ if (this.state.right !== value && !this.props.top && this.props.mode !== MOVING) {
217
+ this.setState({ right: value });
218
+ }
219
+ }
220
+ renderHeader(props) {
221
+ const { widths = [] } = this.state;
222
+ const totalWidth = widths.reduce((sum, val) => sum + val, 0);
223
+ return (React.createElement("div", { className: b('table-wrapper', { sticky: true }) },
224
+ React.createElement("table", { className: b('table', { sticky: true }), style: { width: totalWidth || 'auto' } },
225
+ React.createElement("colgroup", null, widths.map((width, index) => (React.createElement("col", { key: index, style: { width } })))),
226
+ React.createElement(TableHead, Object.assign({}, props, { onDataRowsHeightChange: this.onDataRowsHeightChange })))));
227
+ }
228
+ updateWidths(newWidths = []) {
229
+ const { widths = [] } = this.state;
230
+ if (newWidths.some((v, i) => v !== widths[i])) {
231
+ this.setState({ widths: newWidths });
232
+ }
233
+ }
234
+ }
235
+ StickyHead.propTypes = {
236
+ mode: PropTypes.oneOf([FIXED, MOVING]),
237
+ top: PropTypes.number,
238
+ renderedDataRows: PropTypes.node,
239
+ };
240
+ StickyHead.defaultProps = {
241
+ top: 0,
242
+ };
243
+ class StickyFooter extends React.PureComponent {
244
+ constructor() {
245
+ super(...arguments);
246
+ this.state = {
247
+ style: { bottom: 0 },
248
+ };
249
+ this._nodeFixed = null;
250
+ this._nodeMoving = null;
251
+ this._nodeFixedRef = (node) => {
252
+ this._nodeFixed = node;
253
+ };
254
+ this._nodeMovingRef = (node) => {
255
+ var _a;
256
+ this._nodeMoving = node;
257
+ if (node) {
258
+ (_a = this.heightObserver) === null || _a === void 0 ? void 0 : _a.checkAndUpdateHeight();
259
+ }
260
+ };
261
+ }
262
+ static getDerivedStateFromProps(props, state) {
263
+ var _a;
264
+ if (props.bottom !== ((_a = state.style) === null || _a === void 0 ? void 0 : _a.bottom)) {
265
+ return props.bottom === undefined ? null : { style: { bottom: props.bottom } };
266
+ }
267
+ return null;
268
+ }
269
+ componentDidMount() {
270
+ this.heightObserver = new HeightObserver({
271
+ getSrcElement: () => this._nodeMoving,
272
+ onHeightChange: this.props.onMovingHeightChange,
273
+ });
274
+ }
275
+ componentDidUpdate() {
276
+ var _a;
277
+ (_a = this.heightObserver) === null || _a === void 0 ? void 0 : _a.checkAndUpdateHeight();
278
+ }
279
+ componentWillUnmount() {
280
+ var _a;
281
+ (_a = this.heightObserver) === null || _a === void 0 ? void 0 : _a.destroy();
282
+ }
283
+ render() {
284
+ if (!this.props.renderedRows) {
285
+ return null;
286
+ }
287
+ const { mode, renderedRows } = this.props;
288
+ if (mode === MOVING) {
289
+ const { style } = this.state;
290
+ return (React.createElement("div", { ref: this._nodeMovingRef, className: b('sticky', { footer: true, moving: true }), style: style }, this.renderFooter(renderedRows)));
291
+ }
292
+ else {
293
+ const { widths = [], right = 0 } = this.state;
294
+ const totalWidth = widths.reduce((sum, val) => sum + val, 0);
295
+ return (React.createElement("div", { ref: this._nodeFixedRef, className: b('sticky', { footer: true, fixed: true }), style: { right: right, display: totalWidth ? undefined : 'none' } }, this.renderFooter(renderedRows)));
296
+ }
297
+ }
298
+ setScrollLeft(scrollLeft) {
299
+ requestAnimationFrame(() => {
300
+ if (this._nodeFixed) {
301
+ this._nodeFixed.scrollLeft = scrollLeft;
302
+ }
303
+ });
304
+ }
305
+ setRightPosition(value) {
306
+ if (this.state.right !== value && !this.props.bottom && this._nodeFixed) {
307
+ this.setState({ right: value });
308
+ }
309
+ }
310
+ renderFooter(renderedRows) {
311
+ const { widths = [] } = this.state;
312
+ const totalWidth = widths.reduce((sum, val) => sum + val, 0);
313
+ return (React.createElement("div", { className: b('table-wrapper', { sticky: true }) },
314
+ React.createElement("table", { className: b('table', { sticky: true }), style: { width: totalWidth || 'auto' } },
315
+ React.createElement("colgroup", null, widths.map((width, index) => (React.createElement("col", { key: index, style: { width } })))),
316
+ React.createElement("tbody", null, renderedRows))));
317
+ }
318
+ updateWidths(newWidths = []) {
319
+ const { widths = [] } = this.state;
320
+ if (newWidths.some((v, i) => v !== widths[i])) {
321
+ this.setState({ widths: newWidths });
322
+ }
323
+ }
324
+ }
325
+ StickyFooter.defaultProps = {
326
+ bottom: 0,
327
+ };
328
+ class Table extends React.PureComponent {
329
+ constructor() {
330
+ super(...arguments);
331
+ this.state = {};
332
+ this._refBody = (node) => {
333
+ this._body = node;
334
+ };
335
+ this._refBox = (node) => {
336
+ this._box = node;
337
+ };
338
+ this._refHead = (node) => {
339
+ this._head = node;
340
+ };
341
+ this._refStickyHead = (node) => {
342
+ this._stickyHead = node;
343
+ };
344
+ this._refStickyFooter = (node) => {
345
+ this._stickyFooter = node;
346
+ };
347
+ this._onBoxScroll = () => {
348
+ this._updateBoxConstraints();
349
+ };
350
+ this._onColumnsUpdated = (widths) => {
351
+ if (this._stickyHead) {
352
+ this._stickyHead.updateWidths(widths);
353
+ }
354
+ if (this._stickyFooter) {
355
+ this._stickyFooter.updateWidths(widths);
356
+ }
357
+ };
358
+ this.onMovingHeaderDataRowsHeightChange = (height) => {
359
+ var _a;
360
+ if (-height !== ((_a = this.state.movingHeaderStyle) === null || _a === void 0 ? void 0 : _a.marginTop)) {
361
+ this.setState({ movingHeaderStyle: { marginTop: -height } });
362
+ }
363
+ };
364
+ this.onMovingFooterHeightChange = (height) => {
365
+ var _a;
366
+ if (-height !== ((_a = this.state.movingFooterStyle) === null || _a === void 0 ? void 0 : _a.marginBottom)) {
367
+ this.setState({ movingFooterStyle: { marginBottom: -height } });
368
+ }
369
+ };
370
+ this.renderRow = (vIndex) => {
371
+ const { data, onRowClick } = this.props;
372
+ const { row, index, span } = data[vIndex];
373
+ return this.renderRowImpl(row, index, { onRowClick, odd: vIndex % 2 === 0, span });
374
+ };
375
+ this.renderFooterRow = (rowData, index) => {
376
+ return this.renderRowImpl(rowData, index, { footer: true });
377
+ };
378
+ this.renderHeaderRow = (rowData, index) => {
379
+ return this.renderRowImpl(rowData, index, { headerData: true });
380
+ };
381
+ this.renderRowImpl = (row, index, { onRowClick, odd, span, footer, headerData } = {}) => {
382
+ const { columns: { dataColumns }, rowClassName, rowKey, } = this.props;
383
+ const className = typeof rowClassName === 'function' ? rowClassName(row, index, footer, headerData) : '';
384
+ return (React.createElement(TableRow, { key: rowKey(row, index), className: className, columns: dataColumns, row: row, index: index, span: span, odd: odd, onClick: onRowClick, footer: footer, headerData: headerData }));
385
+ };
386
+ this.renderTable = (items, ref) => {
387
+ const { footerData, columns: { dataColumns }, settings: { stickyHead }, } = this.props;
388
+ const { movingHeaderStyle, movingFooterStyle } = this.state;
389
+ const stickyFooterMode = this.getStickyFooterMode();
390
+ return (React.createElement("div", { className: b('table-wrapper'), style: stickyFooterMode === MOVING ? movingFooterStyle : undefined },
391
+ React.createElement("table", { className: b('table'), style: stickyHead === MOVING ? movingHeaderStyle : undefined },
392
+ React.createElement("colgroup", null, dataColumns.map(({ width }, index) => {
393
+ return React.createElement("col", { key: index, width: width });
394
+ })),
395
+ this.renderHead(),
396
+ React.createElement("tbody", { ref: ref }, items.length ? items : this._getEmptyRow()),
397
+ footerData && (React.createElement("tfoot", { className: b('foot', { 'has-sticky-footer': stickyFooterMode }) }, footerData.map(this.renderFooterRow))))));
398
+ };
399
+ }
400
+ componentDidMount() {
401
+ const { stickyHead, syncHeadOnResize } = this.props.settings;
402
+ this._updateBoxConstraints();
403
+ if (stickyHead && syncHeadOnResize && !this._onWindowResize) {
404
+ this._onWindowResize = () => {
405
+ this.syncHeadWidths();
406
+ };
407
+ window.addEventListener('resize', this._onWindowResize);
408
+ }
409
+ }
410
+ componentDidUpdate() {
411
+ this._updateBoxConstraints();
412
+ }
413
+ componentWillUnmount() {
414
+ if (this._onWindowResize) {
415
+ window.removeEventListener('resize', this._onWindowResize);
416
+ delete this._onWindowResize;
417
+ }
418
+ }
419
+ render() {
420
+ const { className } = this.props;
421
+ const { stickyHead, dynamicRender } = this.props.settings;
422
+ const stickyFooter = this.getStickyFooterMode();
423
+ return (React.createElement("div", { className: className, ref: this._refBody },
424
+ stickyHead && this.renderStickyHead(),
425
+ React.createElement("div", { ref: this._refBox, className: b('box', { 'sticky-head': stickyHead, 'sticky-footer': stickyFooter }), onScroll: this._onBoxScroll }, dynamicRender ? this.renderTableDynamic() : this.renderTableSimple()),
426
+ stickyFooter && this.renderStickyFooter()));
427
+ }
428
+ _updateBoxConstraints() {
429
+ const hasSticky = this._stickyHead || this._stickyFooter;
430
+ if (this._box && hasSticky) {
431
+ const scrollWidth = this._box.offsetWidth - this._box.clientWidth;
432
+ if (this._stickyHead) {
433
+ this._stickyHead.setRightPosition(scrollWidth);
434
+ this._stickyHead.setScrollLeft(this._box.scrollLeft);
435
+ }
436
+ if (this._stickyFooter) {
437
+ this._stickyFooter.setRightPosition(scrollWidth);
438
+ this._stickyFooter.setScrollLeft(this._box.scrollLeft);
439
+ }
440
+ }
441
+ }
442
+ syncHeadWidths() {
443
+ if (this._head) {
444
+ this._head._calculateColumnsWidth();
445
+ }
446
+ }
447
+ _getEmptyRow() {
448
+ const { columns: { dataColumns }, emptyDataMessage, renderEmptyRow, } = this.props;
449
+ if (typeof renderEmptyRow === 'function') {
450
+ return renderEmptyRow(dataColumns);
451
+ }
452
+ else {
453
+ return (React.createElement("tr", { className: b('row') },
454
+ React.createElement("td", { className: b('td', b('no-data')), colSpan: dataColumns.length }, emptyDataMessage)));
455
+ }
456
+ }
457
+ renderHead() {
458
+ const { columns, onSort } = this.props;
459
+ const { displayIndices } = this.props.settings;
460
+ const rows = this.renderHeaderRows();
461
+ return (React.createElement(TableHead, Object.assign({ ref: this._refHead }, columns, { displayIndices: Boolean(displayIndices), onSort: onSort, onColumnsUpdated: this._onColumnsUpdated, renderedDataRows: rows })));
462
+ }
463
+ renderStickyHead() {
464
+ const { columns, onSort } = this.props;
465
+ const { displayIndices, stickyTop, stickyHead } = this.props.settings;
466
+ const top = stickyTop === 'auto' && this._body && this._body.parentNode
467
+ ? this._body.parentNode.offsetTop
468
+ : Number(stickyTop) || 0;
469
+ const rows = this.renderHeaderRows();
470
+ return (React.createElement(StickyHead, Object.assign({ mode: stickyHead, top: top, ref: this._refStickyHead }, columns, { displayIndices: displayIndices, onSort: onSort, renderedDataRows: rows, onDataRowsHeightChange: this.onMovingHeaderDataRowsHeightChange })));
471
+ }
472
+ renderStickyFooter() {
473
+ const { columns } = this.props;
474
+ const { stickyBottom } = this.props.settings;
475
+ let bottom = Number(stickyBottom) || 0;
476
+ if (stickyBottom === 'auto' && this._body && this._body.parentNode) {
477
+ const el = this._body.parentNode;
478
+ bottom = el.offsetTop + el.offsetHeight;
479
+ }
480
+ const rows = this.renderFooterRows();
481
+ return (React.createElement(StickyFooter, { ref: this._refStickyFooter, mode: this.getStickyFooterMode(), bottom: bottom, dataColumns: columns.dataColumns, renderedRows: rows, onMovingHeightChange: this.onMovingFooterHeightChange }));
482
+ }
483
+ renderTableDynamic() {
484
+ const { data, settings: { dynamicInnerRef, dynamicRenderType = 'uniform', dynamicRenderUseStaticSize, dynamicRenderThreshold, dynamicRenderMinSize, dynamicRenderScrollParentGetter, dynamicRenderScrollParentViewportSizeGetter, dynamicItemSizeEstimator, dynamicItemSizeGetter, } = {}, } = this.props;
485
+ return (React.createElement(ReactList, Object.assign({ ref: dynamicInnerRef, type: dynamicRenderType, useStaticSize: dynamicRenderUseStaticSize, threshold: dynamicRenderThreshold, minSize: dynamicRenderMinSize, itemSizeEstimator: dynamicItemSizeEstimator, itemSizeGetter: dynamicItemSizeGetter, length: data.length, itemRenderer: this.renderRow, itemsRenderer: this.renderTable, scrollParentGetter: dynamicRenderScrollParentGetter }, { scrollParentViewportSizeGetter: dynamicRenderScrollParentViewportSizeGetter })));
486
+ }
487
+ renderTableSimple() {
488
+ const { data } = this.props;
489
+ const rows = data.map((_row, index) => this.renderRow(index));
490
+ return this.renderTable(rows, null);
491
+ }
492
+ renderHeaderRows() {
493
+ const { headerData } = this.props;
494
+ return headerData && headerData.map(this.renderHeaderRow);
495
+ }
496
+ renderFooterRows() {
497
+ const { footerData } = this.props;
498
+ return footerData === null || footerData === void 0 ? void 0 : footerData.map(this.renderFooterRow);
499
+ }
500
+ getStickyFooterMode() {
501
+ const { footerData } = this.props;
502
+ if (!(footerData === null || footerData === void 0 ? void 0 : footerData.length)) {
503
+ return false;
504
+ }
505
+ const { stickyFooter } = this.props.settings;
506
+ return stickyFooter;
507
+ }
508
+ }
509
+ Table.propTypes = {
510
+ className: PropTypes.string,
511
+ settings: PropTypes.object,
512
+ refs: PropTypes.object,
513
+ headerData: PropTypes.array,
514
+ data: PropTypes.array,
515
+ footerData: PropTypes.array,
516
+ columns: PropTypes.object,
517
+ emptyDataMessage: PropTypes.string,
518
+ onRowClick: PropTypes.func,
519
+ rowClassName: PropTypes.func,
520
+ rowKey: PropTypes.func,
521
+ startIndex: PropTypes.number,
522
+ onSort: PropTypes.func,
523
+ renderEmptyRow: PropTypes.func,
524
+ };
525
+ const MemoizedCell = React.memo(CellImpl);
526
+ function CellImpl(props) {
527
+ const { column, value, row, index, footer, headerData } = props;
528
+ return (React.createElement(React.Fragment, null, column.render({ value, row, index, footer, headerData })));
529
+ }
530
+ const sortOrderShape = {
531
+ columnId: PropTypes.string,
532
+ order: PropTypes.oneOf([ASCENDING, DESCENDING]),
533
+ };
534
+ class DataTableView extends React.Component {
535
+ constructor() {
536
+ super(...arguments);
537
+ this.state = Object.assign({ settings: {} }, externalToInternalSortOrder(this.props.initialSortOrder, this.props.settings));
538
+ this._tableRef = (node) => {
539
+ this.table = node;
540
+ };
541
+ this.renderMemoizedCell = ({ column, value, row, index, footer, headerData }) => {
542
+ return (React.createElement(MemoizedCell, Object.assign({}, {
543
+ column,
544
+ value,
545
+ row,
546
+ index,
547
+ footer,
548
+ headerData,
549
+ })));
550
+ };
551
+ this.getColumn = (column, columnIndex) => {
552
+ const { settings } = this.state;
553
+ const { defaultOrder } = settings;
554
+ const { sortOrder = {}, sortColumns, indexColumn } = this.state;
555
+ const indexAdjustment = Number(Boolean(indexColumn));
556
+ const isSortEnabled = this.isSortEnabled();
557
+ const { name, accessor = column.name, align, sortable = settings.sortable, group, autogroup = true, } = column;
558
+ const { sortAccessor, onClick } = column;
559
+ const _className = b('td', { align }, column.className);
560
+ const _getValue = typeof accessor === 'function'
561
+ ? (row) => accessor(row)
562
+ : (row) => {
563
+ return Object.prototype.hasOwnProperty.call(row, accessor)
564
+ ? row[accessor]
565
+ : undefined;
566
+ };
567
+ const _getTitle = typeof column.title === 'function'
568
+ ? (row) => column.title(row)
569
+ : () => (typeof column.title === 'string' && column.title) || undefined;
570
+ const _getSortValue = typeof sortAccessor === 'function' ? (data) => sortAccessor(data) : _getValue;
571
+ const _renderValue = typeof column.render === 'function'
572
+ ? ({ value, row, index, footer, headerData }) => this.renderMemoizedCell({ column, value, row, index, footer, headerData })
573
+ : ({ value }) => value;
574
+ const customStyle = typeof column.customStyle === 'function' ? column.customStyle : () => undefined;
575
+ const _getOnClick = typeof onClick === 'function'
576
+ ? (data) => () => onClick(data, column)
577
+ : () => undefined;
578
+ return Object.assign(Object.assign({ index: columnIndex - indexAdjustment, columnIndex, dataColumn: true, defaultOrder }, column), { sortable: sortable && isSortEnabled, _className,
579
+ _getValue,
580
+ _getTitle,
581
+ _getSortValue,
582
+ _renderValue,
583
+ _getOnClick,
584
+ customStyle,
585
+ group,
586
+ autogroup, sortOrder: sortOrder[name] || undefined, sortIndex: sortColumns.length > 1 ? sortColumns.indexOf(name) + 1 : undefined });
587
+ };
588
+ this.isSortEnabled = () => {
589
+ const { data } = this.props;
590
+ return Array.isArray(data) && data.length > 1;
591
+ };
592
+ this.onSort = (column, multisort) => {
593
+ if (column) {
594
+ const { sortOrder, sortColumns } = getSortOrder(column, this.state, multisort, this.props.settings);
595
+ this.setState({ sortOrder, sortColumns });
596
+ const { onSort } = this.props;
597
+ if (typeof onSort === 'function') {
598
+ const formattedSortOrder = internalToExternalSortOrder(sortOrder);
599
+ onSort(formattedSortOrder);
600
+ }
601
+ }
602
+ else {
603
+ this.setState({ sortOrder: {}, sortColumns: [] });
604
+ const { onSort } = this.props;
605
+ if (typeof onSort === 'function') {
606
+ onSort([]);
607
+ }
608
+ }
609
+ };
610
+ }
611
+ static normalizeStickyHead(stickyHead = false) {
612
+ if (stickyHead === MOVING && !positionStickySupported) {
613
+ console.warn('Your browser does not support position: sticky, moving sticky headers will be disabled.');
614
+ return false;
615
+ }
616
+ return stickyHead;
617
+ }
618
+ static calculateSettings(nextSettings) {
619
+ return Object.assign(Object.assign(Object.assign({}, DataTableView.defaultProps.settings), nextSettings), { stickyHead: DataTableView.normalizeStickyHead(nextSettings.stickyHead), stickyFooter: DataTableView.normalizeStickyHead(nextSettings.stickyFooter) });
620
+ }
621
+ static getIndexColumn({ startIndex, data, visibleRowIndex, }) {
622
+ const lastIndex = startIndex + data.length + 1;
623
+ return {
624
+ name: INDEX_COLUMN,
625
+ header: '#',
626
+ className: b('index'),
627
+ render: ({ row, index, footer, headerData }) => {
628
+ var _a, _b;
629
+ if (headerData) {
630
+ return (_a = row.headerIndex) !== null && _a !== void 0 ? _a : startIndex + index;
631
+ }
632
+ if (footer) {
633
+ return (_b = row.footerIndex) !== null && _b !== void 0 ? _b : startIndex + index;
634
+ }
635
+ if (typeof visibleRowIndex === 'function') {
636
+ return visibleRowIndex(row, index);
637
+ }
638
+ return startIndex + index;
639
+ },
640
+ sortable: false,
641
+ width: 20 + Math.ceil(Math.log10(lastIndex)) * 10,
642
+ };
643
+ }
644
+ static getDerivedStateFromProps(nextProps) {
645
+ const settings = DataTableView.calculateSettings(nextProps.settings);
646
+ return Object.assign({ settings, indexColumn: Boolean(settings.displayIndices) && DataTableView.getIndexColumn(nextProps) }, (nextProps.sortOrder
647
+ ? Object.assign({}, externalToInternalSortOrder(nextProps.sortOrder, nextProps.settings)) : undefined));
648
+ }
649
+ render() {
650
+ const { headerData, data, footerData, columns, startIndex, emptyDataMessage, rowClassName, rowKey, onRowClick, theme, renderEmptyRow, nullBeforeNumbers, } = this.props;
651
+ const { settings, sortOrder, sortColumns } = this.state;
652
+ const { highlightRows = false, stripedRows = false, headerMod = false } = settings;
653
+ const tableClassName = b({
654
+ 'highlight-rows': highlightRows,
655
+ 'striped-rows': stripedRows,
656
+ header: headerMod,
657
+ theme: theme,
658
+ });
659
+ const dataColumns = this.getComplexColumns(columns);
660
+ if (settings.dynamicRender && dataColumns.dataColumns.some((column) => column.group)) {
661
+ console.warn('Simultaneously used grouping cells and dynamic render. The table will render unpredictable.');
662
+ }
663
+ return (React.createElement(Table, { ref: this._tableRef, className: tableClassName, settings: settings, startIndex: startIndex, columns: dataColumns, emptyDataMessage: emptyDataMessage, renderEmptyRow: renderEmptyRow, rowClassName: rowClassName, rowKey: rowKey || DataTableView.defaultProps.rowKey, onRowClick: onRowClick, headerData: headerData, data: getSortedData(data, dataColumns.dataColumns, {
664
+ sortOrder,
665
+ sortColumns,
666
+ }, {
667
+ nullBeforeNumbers,
668
+ externalSort: settings === null || settings === void 0 ? void 0 : settings.externalSort,
669
+ }), footerData: footerData, onSort: this.onSort }));
670
+ }
671
+ getComplexColumns(columns) {
672
+ const headColumns = [];
673
+ const dataColumns = [];
674
+ const headCells = [];
675
+ const { indexColumn } = this.state;
676
+ const allColumns = indexColumn ? [indexColumn, ...columns] : columns;
677
+ const processLevel = (list, level) => {
678
+ if (!headColumns[level]) {
679
+ headColumns[level] = [];
680
+ }
681
+ const items = headColumns[level];
682
+ return list.reduce((subCount, item) => {
683
+ let colSpan = 1;
684
+ let itemLevel = -1;
685
+ let column = item;
686
+ if (Array.isArray(item.sub)) {
687
+ colSpan = processLevel(item.sub, level + 1);
688
+ }
689
+ else {
690
+ const tmp = this.getColumn(item, dataColumns.length);
691
+ dataColumns.push(tmp);
692
+ itemLevel = level;
693
+ column = tmp;
694
+ }
695
+ const headCell = { column, itemLevel, colSpan, rowSpan: 0 };
696
+ headCells.push(headCell);
697
+ items.push(headCell);
698
+ return colSpan + subCount;
699
+ }, 0);
700
+ };
701
+ processLevel(allColumns, 0);
702
+ headCells.forEach((cell) => {
703
+ cell.rowSpan = cell.itemLevel < 0 ? 1 : headColumns.length - cell.itemLevel;
704
+ });
705
+ return { headColumns, dataColumns };
706
+ }
707
+ resize() {
708
+ if (this.table) {
709
+ this.table.syncHeadWidths();
710
+ }
711
+ }
712
+ }
713
+ DataTableView.propTypes = {
714
+ columns: PropTypes.array.isRequired,
715
+ headerData: PropTypes.array,
716
+ data: PropTypes.array.isRequired,
717
+ footerData: PropTypes.array,
718
+ startIndex: PropTypes.number,
719
+ emptyDataMessage: PropTypes.string,
720
+ renderEmptyRow: PropTypes.func,
721
+ rowClassName: PropTypes.func,
722
+ rowKey: PropTypes.func,
723
+ visibleRowIndex: PropTypes.func,
724
+ initialSortOrder: PropTypes.oneOfType([
725
+ PropTypes.shape(sortOrderShape),
726
+ PropTypes.arrayOf(PropTypes.shape(sortOrderShape)),
727
+ ]),
728
+ sortOrder: PropTypes.oneOfType([
729
+ PropTypes.shape(sortOrderShape),
730
+ PropTypes.arrayOf(PropTypes.shape(sortOrderShape)),
731
+ ]),
732
+ settings: PropTypes.shape({
733
+ displayIndices: PropTypes.bool,
734
+ dynamicRender: PropTypes.bool,
735
+ dynamicRenderType: PropTypes.oneOf(['simple', 'uniform', 'variable']),
736
+ dynamicRenderUseStaticSize: PropTypes.bool,
737
+ dynamicRenderThreshold: PropTypes.number,
738
+ dynamicRenderMinSize: PropTypes.number,
739
+ dynamicRenderScrollParentGetter: PropTypes.func,
740
+ dynamicRenderScrollParentViewportSizeGetter: PropTypes.func,
741
+ dynamicItemSizeEstimator: PropTypes.func,
742
+ dynamicItemSizeGetter: PropTypes.func,
743
+ stickyHead: PropTypes.oneOf([false, FIXED, MOVING]),
744
+ stickyTop: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['auto'])]),
745
+ sortable: PropTypes.bool,
746
+ externalSort: PropTypes.bool,
747
+ highlightRows: PropTypes.bool,
748
+ stripedRows: PropTypes.bool,
749
+ headerMod: PropTypes.oneOf(['multiline', 'pre']),
750
+ defaultOrder: PropTypes.oneOf([ASCENDING, DESCENDING]),
751
+ syncHeadOnResize: PropTypes.bool,
752
+ }),
753
+ theme: PropTypes.string,
754
+ onSort: PropTypes.func,
755
+ };
756
+ DataTableView.defaultProps = {
757
+ startIndex: 0,
758
+ emptyDataMessage: 'No data',
759
+ settings: {
760
+ displayIndices: true,
761
+ dynamicRenderMinSize: 1,
762
+ stickyHead: false,
763
+ stickyFooter: false,
764
+ sortable: true,
765
+ externalSort: false,
766
+ defaultOrder: ASCENDING,
767
+ },
768
+ rowKey: (row, index) => Object.prototype.hasOwnProperty.call(row, 'id') ? row.id : index,
769
+ initialSortOrder: {},
770
+ initialSortColumns: [],
771
+ theme: 'yandex-cloud',
772
+ };
773
+ DataTableView.getSortedData = getSortedData;
774
+ function doColumnsHaveDuplicateNames(columns) {
775
+ const names = new Set(columns.map((column) => column.name));
776
+ return names.size !== columns.length;
777
+ }
778
+ const DUPLICATE_COLUMN_NAMES_WARNING = 'It is strongly recommended against using duplicate column names. ' +
779
+ 'They act as default accessors and titles, so doing so may lead to confusing titles and the wrong ' +
780
+ 'data being extracted.';
781
+ export default class DataTable extends React.PureComponent {
782
+ constructor() {
783
+ super(...arguments);
784
+ this.state = {};
785
+ this._tableRef = (node) => {
786
+ this.table = node;
787
+ };
788
+ }
789
+ static setCustomIcons(customIcons) {
790
+ ICONS.ICON_ASC = customIcons.ICON_ASC || ICON_ASC;
791
+ ICONS.ICON_DESC = customIcons.ICON_DESC || ICON_DESC;
792
+ }
793
+ componentDidMount() {
794
+ if (doColumnsHaveDuplicateNames(this.props.columns)) {
795
+ console.warn(DUPLICATE_COLUMN_NAMES_WARNING);
796
+ }
797
+ }
798
+ componentDidUpdate(prevProps) {
799
+ const { columns } = this.props;
800
+ if (columns !== prevProps.columns && doColumnsHaveDuplicateNames(columns)) {
801
+ console.warn(DUPLICATE_COLUMN_NAMES_WARNING);
802
+ }
803
+ }
804
+ componentDidCatch(error) {
805
+ console.error(error);
806
+ this.setState({
807
+ error,
808
+ });
809
+ const { onError } = this.props;
810
+ if ('function' === typeof onError) {
811
+ onError(error);
812
+ }
813
+ }
814
+ render() {
815
+ const { error } = this.state;
816
+ if (error) {
817
+ return (React.createElement("pre", { className: b('error') },
818
+ "DataTable got stuck in invalid state. Please tell developers about it.",
819
+ '\n\n',
820
+ (error.stack && String(error.stack)) || String(error)));
821
+ }
822
+ else {
823
+ return React.createElement(DataTableView, Object.assign({ ref: this._tableRef }, this.props));
824
+ }
825
+ }
826
+ resize() {
827
+ if (this.table) {
828
+ this.table.resize();
829
+ }
830
+ }
831
+ }
832
+ DataTable.FIXED = FIXED;
833
+ DataTable.MOVING = MOVING;
834
+ DataTable.ASCENDING = ASCENDING;
835
+ DataTable.DESCENDING = DESCENDING;
836
+ DataTable.LEFT = LEFT;
837
+ DataTable.CENTER = CENTER;
838
+ DataTable.RIGHT = RIGHT;