UNPKG

22.5 kBJavaScriptView Raw
1import React from 'react';
2import PropTypes from 'prop-types';
3import { Table, Column, Cell } from 'fixed-data-table-2';
4import classNames from 'classnames';
5import * as Constants from './constants';
6import * as CellUtils from './CellUtils';
7import { HeaderCell } from './HeaderCell';
8import { TextCell } from './TextCell';
9import TouchWrapper from './TouchWrapper';
10import { RETURNED_DATA_TYPES } from './constants';
11import _ from 'lodash';
12import { StyleSheet, css } from 'aphrodite';
13
14export class HugeTable extends React.Component {
15 static propTypes = {
16 data: PropTypes.arrayOf(PropTypes.object),
17 options: PropTypes.shape({
18 height: PropTypes.number,
19 width: PropTypes.number,
20 mixedContentImage: PropTypes.func,
21 tableScrolled: PropTypes.func,
22 id: PropTypes.string,
23 maxTitleWidth: PropTypes.number,
24 maxContentWidth: PropTypes.number,
25 minColumnWidth: PropTypes.number,
26 rowNumberColumnWidth: PropTypes.number,
27 fontDetails: PropTypes.string,
28 headerOffsetWidth: PropTypes.number,
29 }),
30 schema: PropTypes.arrayOf(PropTypes.shape({
31 name: PropTypes.string,
32 type: PropTypes.string,
33 })),
34 renderers: PropTypes.shape(RETURNED_DATA_TYPES.reduce((initial, next) => {
35 return {
36 ...initial,
37 [next]: PropTypes.func,
38 };
39 }, {HEADER: PropTypes.func})),
40 onSchemaChange: PropTypes.func,
41 resizeByContent: PropTypes.bool,
42 hideRowNumbers: PropTypes.bool,
43 showScrollingArrows: PropTypes.bool,
44 scrollToNewColumn: PropTypes.bool,
45 onScrollToNewColumn: PropTypes.func,
46 rowHeight: PropTypes.number,
47 headerHeight: PropTypes.number,
48 cellPadding: PropTypes.shape ({
49 top: PropTypes.number,
50 bottom: PropTypes.number,
51 left: PropTypes.number,
52 right: PropTypes.number,
53 }),
54 lineHeight: PropTypes.number,
55 buttonColumnWidth: PropTypes.number,
56 activeColumnIndex: PropTypes.number,
57 onActiveColumnChange: PropTypes.func,
58 scrollToColumn: PropTypes.number,
59 disableClickEvents: PropTypes.bool,
60 resizeableColumns: PropTypes.bool,
61 reorderableColumns: PropTypes.bool,
62 }
63
64 static defaultProps = {
65 resizeableColumns: true,
66 reorderableColumns: true,
67 };
68
69 constructor(props) {
70 super(props);
71 const
72 cellPadding = props.cellPadding || Constants.CELL_PADDING,
73 lineHeight = props.lineHeight || Constants.LINE_HEIGHT,
74 headerHeight = props.headerHeight || Constants.HEADER_HEIGHT,
75 rowHeight = props.rowHeight || Constants.ROW_HEIGHT;
76 this.state = {
77 columnWidths: {},
78 isColumnResizing: undefined,
79 columnNameToDataTypeMap: {},
80 columnOrder: [],
81 currentSchema: [],
82 shouldActivateLeftScroll: false,
83 shouldActivateRightScroll: false,
84 scrollLeft: 0,
85 cellPadding,
86 lineHeight,
87 headerHeight,
88 rowHeight,
89 contentHeight: 500,
90 contentWidth: 500,
91 };
92
93 this.uniqueId = props.options.id || null;
94 if (this.uniqueId){
95 this.savedColumnsWidth = JSON.parse(localStorage.getItem('huge-table-column-widths')) || {};
96 this.savedColumnsWidth[this.uniqueId] = this.savedColumnsWidth[this.uniqueId] || {};
97 }
98
99 this.maxTitleWidth = props.options.maxTitleWidth || Constants.MAX_TITLE_WIDTH;
100 this.maxContentWidth = props.options.maxContentWidth || Constants.MAX_CONTENT_WIDTH;
101 this.fontDetails = props.options.fontDetails || Constants.FONT_DETAILS;
102 this.minColumnWidth = props.options.minColumnWidth || Constants.MIN_COLUMN_WIDTH;
103 this.headerOffsetWidth = props.options.headerOffsetWidth || 0;
104 }
105
106 componentDidMount() {
107 this.generateColumnToDataTypeMap(this.props.schema);
108 this.generateColumnWidths(this.props.schema, this.props.options.width);
109 this.generateInitialColumnOrder(this.props.schema);
110 this.checkForScrollArrows(this.state.scrollLeft);
111 }
112
113 componentWillReceiveProps(nextProps) {
114 if(this.props.schema !== nextProps.schema) {
115 this.generateColumnToDataTypeMap(nextProps.schema);
116 this.generateInitialColumnOrder(nextProps.schema);
117 }
118
119 if(this.props.schema !== nextProps.schema || this.props.options.width !== nextProps.options.width) {
120 this.generateColumnWidths(nextProps.schema, nextProps.options.width);
121 this.checkForScrollArrows(this.state.scrollLeft);
122 }
123
124 if(this.props.data.length !== nextProps.data.length) {
125 this.setContentHeight(nextProps.data);
126 }
127 }
128
129 componentDidUpdate(prevProps, prevState) {
130 if (prevState.columnOrder !== this.state.columnOrder && !_.isEqual(prevState.columnOrder, this.state.columnOrder)) {
131 this.reorderSchema(this.props.schema, this.state.columnOrder);
132 }
133 if (prevState.currentSchema !== this.state.currentSchema && !_.isEqual(prevState.currentSchema, this.state.currentSchema)) {
134 this.onSchemaChange(this.state.currentSchema, prevState.currentSchema);
135 this.checkForScrollArrows(this.state.scrollLeft);
136 }
137
138 if (prevState.currentSchema < this.state.currentSchema && this.state.shouldShowScrolls) {
139 this.scrollNewColumnIntoView();
140 this.checkForScrollArrows(this.state.scrollLeft);
141 }
142
143 if (prevProps.activeColumnIndex !== this.props.activeColumnIndex && this.props.onActiveColumnChange) {
144 this.props.onActiveColumnChange();
145 }
146
147 }
148
149 onScrollEnd = (scrollLeft) => {
150 this.setState({ scrollLeft });
151 }
152
153 onSchemaChange = (schema, prevSchema) => {
154 if (this.props.onSchemaChange) {
155 this.props.onSchemaChange(schema, prevSchema);
156 }
157 }
158
159 scrollNewColumnIntoView = () => {
160 if (this.refs.table && this.props.scrollToNewColumn) {
161 this.scrollAndCheckForArrows(this.refs.table.state.maxScrollX);
162 if (this.props.onScrollToNewColumn) {
163 this.props.onScrollToNewColumn();
164 }
165 }
166 }
167
168 reorderSchema = (schema, columnOrder) => {
169 const newSchema = [];
170 columnOrder.forEach(col => {
171 const newSchemaItem = schema.find(s => (s.id || s.name) === col);
172 if (newSchemaItem) {
173 newSchema.push(newSchemaItem);
174 }
175 });
176 this.setState({ currentSchema: newSchema });
177 }
178
179 setContentHeight = (data) => {
180 this.setState({
181 contentHeight: this.state.rowHeight * data.length + this.state.headerHeight,
182 });
183 }
184
185 generateInitialColumnOrder = (schema) => {
186 const columnOrder = schema.map(schemaItem => schemaItem.id || schemaItem.name);
187 this.setState({
188 columnOrder,
189 });
190 this.reorderSchema(schema, columnOrder);
191 }
192
193 generateColumnToDataTypeMap = (schema) => {
194 const columnNameToDataTypeMap = {};
195
196 schema.forEach((schemaItem) => {
197 columnNameToDataTypeMap[schemaItem.name] = schemaItem.type;
198 });
199
200 this.setState({columnNameToDataTypeMap});
201 }
202
203 generateColumnWidths = (schema, width, columnKey, newColumnWidth) => {
204 const columnWidths = {};
205 let isColumnResizing;
206 let contentWidth;
207
208 // Table width - rowNumberColumn (width + border) - columns border / columnsCount
209 const calculatedWidth = (width - Constants.ROW_NUMBER_COLUMN_WIDTH - 5 - (schema.length * 2)) / Math.max(schema.length, 1);
210 const defaultColumnWidth = Math.max(calculatedWidth, this.minColumnWidth);
211
212 schema.forEach((schemaItem) => {
213 const maxColumnWidth = this.props.resizeByContent ? this.getMaxColumnWidth(schemaItem, this.minColumnWidth) : defaultColumnWidth;
214 if (this.uniqueId){
215 //Reference the content width over the width set in state if we have data and we are passed the resizeByContent prop
216 if (this.props.data.length > 0 && this.props.resizeByContent) {
217 this.state.columnWidths[schemaItem.id || schemaItem.name] = this.savedColumnsWidth[this.uniqueId][schemaItem.id || schemaItem.name] || maxColumnWidth || this.state.columnWidths[schemaItem.id || schemaItem.name] || defaultColumnWidth;
218 } else {
219 this.state.columnWidths[schemaItem.id || schemaItem.name] = this.savedColumnsWidth[this.uniqueId][schemaItem.id || schemaItem.name] || this.state.columnWidths[schemaItem.id || schemaItem.name] || maxColumnWidth || defaultColumnWidth;
220 }
221 } else {
222 this.state.columnWidths[schemaItem.id || schemaItem.name] = this.state.columnWidths[schemaItem.id || schemaItem.name] || maxColumnWidth || defaultColumnWidth;
223 }
224 columnWidths[schemaItem.id || schemaItem.name] = this.state.columnWidths[schemaItem.id || schemaItem.name];
225 });
226
227 if (columnKey) {
228 columnWidths[columnKey] = newColumnWidth;
229 if (this.uniqueId){
230 this.savedColumnsWidth[this.uniqueId][columnKey] = newColumnWidth;
231 localStorage.setItem('huge-table-column-widths', JSON.stringify(this.savedColumnsWidth));
232 }
233 isColumnResizing = false;
234 }
235
236 contentWidth = schema.reduce((sum, item) => sum + columnWidths[item.id || item.name], 0) + Constants.ROW_NUMBER_COLUMN_WIDTH;
237 this.setState({
238 columnWidths,
239 isColumnResizing,
240 contentWidth,
241 });
242 }
243
244 getMaxColumnWidth = (schemaItem, defaultColumnWidth) => {
245 let maxColumnWidth = 0;
246 //Calculate the column width unless the content is an image
247 if (schemaItem.type !== Constants.ColumnTypes.IMAGE) {
248 this.props.data.forEach(row => {
249 const cellContent = this.getCellContent(row, schemaItem);
250 const cellText = this.getCellText(cellContent);
251 const cellColumnWidth = this.getContentSize(cellText, this.fontDetails);
252 maxColumnWidth = maxColumnWidth > cellColumnWidth ? maxColumnWidth : cellColumnWidth;
253 });
254
255 //If the content width is less than the max title width
256 //Set the column width based off of max title width
257 //Else set column width based off of content width
258 if (maxColumnWidth < this.maxTitleWidth) {
259 const titleWidth = this.getContentSize(schemaItem.name, this.fontDetails) + this.headerOffsetWidth;
260 maxColumnWidth = Math.max(titleWidth, maxColumnWidth);
261 maxColumnWidth = Math.min(maxColumnWidth, this.maxTitleWidth);
262 } else {
263 maxColumnWidth = Math.min(this.maxContentWidth, maxColumnWidth);
264 }
265 }
266 return maxColumnWidth > defaultColumnWidth ? maxColumnWidth : defaultColumnWidth;
267 }
268
269 getContentSize = (txt, font) => {
270 this.element = document.createElement('canvas');
271 this.context = this.element.getContext('2d');
272 this.context.font = font;
273 const tsize = {'width':this.context.measureText(txt).width};
274 return tsize.width;
275 }
276
277 setMaxHeaderWidth = (field) => {
278 let maxColumnWidth = this.getContentSize(field.name, this.fontDetails) + this.headerOffsetWidth;
279 maxColumnWidth = maxColumnWidth > this.minColumnWidth ? maxColumnWidth : this.minColumnWidth;
280
281 if (this.uniqueId){
282 this.savedColumnsWidth[this.uniqueId][field.id] = maxColumnWidth;
283 localStorage.setItem('huge-table-column-widths', JSON.stringify(this.savedColumnsWidth));
284 }
285
286 return maxColumnWidth;
287 }
288
289 resizeHeader = (field) => {
290 const columnWidths = { ...this.state.columnWidths };
291
292 columnWidths[field.id] = this.setMaxHeaderWidth(field);
293
294 this.setState({
295 columnWidths,
296 });
297 }
298
299 resizeAllHeaders = (fields) => {
300 const columnWidths = { ...this.state.columnWidths };
301
302 fields.forEach(field => {
303 columnWidths[field.id] = this.setMaxHeaderWidth(field);
304 });
305
306 this.setState({
307 columnWidths,
308 });
309 }
310
311 getCellContent = (row, schemaItem) => {
312 let content;
313 if (schemaItem.type === Constants.ColumnTypes.TEXT) {
314 const cellData = Array.isArray(row[schemaItem.name]) ? row[schemaItem.name][0] : row[schemaItem.name];
315 if (cellData !== undefined) {
316 content = cellData.text !== undefined ? cellData.text : '';
317 } else {
318 content = '';
319 }
320 } else if (schemaItem.type === Constants.ColumnTypes.IMAGE) {
321 content = '';
322 } else {
323 content = row[schemaItem.name + '/_text'] !== undefined ? row[schemaItem.name + '/_text'] : row[schemaItem.name];
324 }
325 return content;
326 }
327
328 getCellText = cellContent => {
329 if (Array.isArray(cellContent)) {
330 return this.getCellText(cellContent[0]);
331 } else if (typeof cellContent === 'object') {
332 return JSON.stringify(cellContent);
333 } else {
334 return cellContent;
335 }
336 }
337
338 createColumn = (schemaItem, idx) => {
339 let width = this.state.columnWidths[schemaItem.id || schemaItem.name];
340 const lastColumn = idx === (this.state.currentSchema.length - 1) && this.state.currentSchema.length > 1;
341 if (this.state.shouldShowScrolls && lastColumn) {
342 // this adds some extra room to accomodate the scrolling arrows
343 width = width + 120;
344 }
345 let cellClass = '', headerClass = '';
346 if (this.props.showScrollingArrows && this.state.shouldShowScrolls) {
347 if (this.props.hideRowNumbers && idx === 0) {
348 cellClass = 'hugetable-index-column nudge';
349 } else if (lastColumn) {
350 cellClass = 'last-column';
351 }
352 }
353
354 if (idx === this.props.activeColumnIndex) {
355 cellClass = `${cellClass} active-column`;
356 headerClass = 'active-column-header';
357 }
358 // if we are hiding the row numbers but showing scrolling arrows, need to nudge this column with padding
359 return (
360 <Column
361 cellClassName={cellClass}
362 headerClassName={headerClass}
363 header={props => this.renderHeader({...props, cellData: {main: schemaItem.name}})}
364 columnKey={schemaItem.id || schemaItem.name}
365 minWidth={this.minColumnWidth}
366 width={width}
367 cell={(props) => this.cellRenderer({...props, schemaItem })}
368 key={schemaItem.name}
369 isResizable={this.props.resizeableColumns}
370 isReorderable={this.props.reorderableColumns}
371 />
372 );
373 }
374
375 renderHeader = (props) => {
376 if(this.props.renderers && this.props.renderers.HEADER && typeof this.props.renderers.HEADER === 'function') {
377 return (
378 <Cell>
379 {this.props.renderers.HEADER(props)}
380 </Cell>
381 );
382 } else {
383 return <HeaderCell {...props} />;
384 }
385 }
386
387 getCellStyles = (columnDataType) => {
388 let cellStyles = {};
389
390 if (columnDataType == Constants.ColumnTypes.IMAGE) {
391 cellStyles = StyleSheet.create({
392 cellStyle: {
393 paddingTop: Constants.IMAGE_CELL_PADDING.cellPaddingTop,
394 paddingBottom: Constants.IMAGE_CELL_PADDING.cellPaddingBottom,
395 paddingLeft: Constants.IMAGE_CELL_PADDING.cellPaddingLeft,
396 paddingRight: Constants.IMAGE_CELL_PADDING.cellPaddingRight,
397 },
398 });
399 } else {
400 cellStyles = StyleSheet.create({
401 cellStyle: {
402 paddingTop: Constants.CELL_PADDING.cellPaddingTop,
403 paddingBottom: Constants.CELL_PADDING.cellPaddingBottom,
404 paddingLeft: Constants.CELL_PADDING.cellPaddingLeft,
405 paddingRight: Constants.CELL_PADDING.cellPaddingRight,
406 },
407 });
408 }
409 return (cellStyles);
410 };
411
412 cellRenderer = ({rowIndex, width, height, schemaItem}) => {
413 const rowObject = this.props.data[rowIndex];
414 const cellData = {};
415 cellData.main = rowObject[schemaItem.name];
416 Constants.RETURNED_DATA_TYPES.forEach(dataType => {
417 cellData[dataType] = rowObject[schemaItem.name + '/_' + dataType] || null;
418 });
419 const columnDataType = this.state.columnNameToDataTypeMap[schemaItem.name];
420 const cellCustomRenderer = this.props.renderers && this.props.renderers[columnDataType];
421 cellData.type = columnDataType;
422
423 return (
424 <Cell className={css(this.getCellStyles(columnDataType).cellStyle)}>
425 {CellUtils.getComponentDataType({
426 disableClickEvents: this.props.disableClickEvents,
427 columnDataType,
428 cellData,
429 width,
430 height,
431 columnKey: schemaItem.id || schemaItem.name,
432 mixedContentImage: this.props.options.mixedContentImage,
433 cellCustomRenderer,
434 rowIndex,
435
436 })}
437 </Cell>
438 );
439 }
440
441 onColumnResizeEndCallback = (newColumnWidth, columnKey) => {
442 this.generateColumnWidths(this.props.schema, this.props.options.width, columnKey, newColumnWidth);
443 }
444
445 onScroll = (scrollLeft, scrollTop) => {
446 this.setState({
447 scrollLeft: scrollLeft ? scrollLeft : this.state.scrollLeft,
448 scrollTop,
449 });
450 if(this.props.options.tableScrolled) {
451 this.props.options.tableScrolled(scrollLeft, scrollTop);
452 }
453 }
454
455 onContentDimensionsChange = (contentHeight, contentWidth) => {
456 this.setState({
457 contentWidth,
458 contentHeight,
459 });
460 }
461
462 onColumnReorderEndCallback = (event) => {
463 const columnOrder = this.state.columnOrder.filter((columnKey) => {
464 return columnKey !== event.reorderColumn;
465 });
466
467 if (event.columnAfter) {
468 const index = columnOrder.indexOf(event.columnAfter);
469 columnOrder.splice(index, 0, event.reorderColumn);
470 } else {
471 columnOrder.push(event.reorderColumn);
472 }
473 this.setState({
474 columnOrder,
475 });
476 }
477
478 handleMouseEnter = (scrollVal) => {
479 this.intervalId = setInterval(() => this.moveScrollPos(scrollVal), 10);
480 }
481
482 stopScrollInterval = () => {
483 clearInterval(this.intervalId);
484 this.intervalId = undefined;
485 }
486
487 moveScrollPos = (val) => {
488 if (this.state.scrollLeft === 0 && val >= 0 || this.state.scrollLeft > 0) {
489 const scrollLeft = this.state.scrollLeft + val >= 0 ? this.state.scrollLeft + val : 0;
490 this.scrollAndCheckForArrows(scrollLeft);
491 }
492 if (this.state.scrollLeft >= this.refs.table.state.maxScrollX) {
493 this.stopScrollInterval();
494 }
495 }
496
497 calcElementsWidth = (elementsArr) => {
498 return elementsArr.map(e => e.getBoundingClientRect().width).reduce((i, n) => i+n, 0);
499 }
500
501 getChildElements = () => {
502 const headerContainer = this.getHeaderContainer();
503 const childElements = headerContainer ? Array.from(this.getHeaderContainer().children) : [];
504 return childElements;
505 }
506
507 handleScroll = (scrollLeft) => {
508 this.setState({
509 scrollLeft,
510 });
511 return true;
512 }
513
514 checkForScrollArrows = (scrollLeft, allElementsWidth) => {
515 const ALL_ELEMENTS_WIDTH = allElementsWidth ? allElementsWidth : this.calcElementsWidth(this.getChildElements());
516 const shouldShowScrolls = ALL_ELEMENTS_WIDTH > this.props.options.width && this.props.showScrollingArrows;
517 this.setState({
518 shouldShowScrolls,
519 shouldActivateLeftScroll: scrollLeft > 0,
520 shouldActivateRightScroll: ALL_ELEMENTS_WIDTH-1 > (this.props.options.width + scrollLeft),
521 });
522 return true;
523 }
524
525 scrollAndCheckForArrows(scrollLeft) {
526 this.checkForScrollArrows(scrollLeft);
527 this.handleScroll(scrollLeft);
528 }
529
530 getListContainerWidth = () => {
531 return this.getHeaderContainer().getBoundingClientRect().width;
532 }
533
534 getHeaderContainer = () => {
535 const headerCell = document.querySelector('.hugetable-index-column');
536 return headerCell ? headerCell.parentElement : null;
537 }
538
539 render() {
540 const tableWidth = this.props.options.width;
541 const tableHeight = this.props.options.height - this.state.headerHeight;
542 const rowNumberColumnWidth = this.props.options.rowNumberColumnWidth ? this.props.options.rowNumberColumnWidth : Constants.ROW_NUMBER_COLUMN_WIDTH;
543
544 let leftScroll, rightScroll;
545 if(this.state.shouldShowScrolls) {
546 // increase the size of the row number column so there is no overlap
547 leftScroll = (
548 <section style={{ height: this.state.headerHeight }} className={classNames('scroll-toggle', 'left', {'active': this.state.shouldActivateLeftScroll})} onMouseEnter={() => this.handleMouseEnter(-10)} onMouseLeave={() => this.stopScrollInterval()}>
549 <i className="fa fa-chevron-left fa-lg"></i>
550 </section>
551 );
552
553 rightScroll = (
554 <section style={{ height: this.state.headerHeight }} className={classNames('scroll-toggle', 'right', {'active': this.state.shouldActivateRightScroll})} onMouseEnter={() => this.handleMouseEnter(10)} onMouseLeave={() => this.stopScrollInterval()}>
555 <i className="fa fa-chevron-right fa-lg"></i>
556 </section>
557 );
558 }
559
560 return (
561 <TouchWrapper
562 onScroll={this.onScroll}
563 tableWidth={tableWidth}
564 tableHeight={tableHeight}
565 contentWidth={this.state.contentWidth}
566 contentHeight={this.state.contentHeight}
567 >
568 <div style={{ position: 'relative', height: tableHeight, width: tableWidth }}>
569 {leftScroll}
570 {rightScroll}
571 <Table
572 onHorizontalScroll={this.checkForScrollArrows}
573 onScrollEnd={this.onScrollEnd}
574 ref="table"
575 rowHeight={this.state.rowHeight}
576 rowsCount={this.props.data.length}
577 width={tableWidth}
578 scrollLeft={this.state.scrollLeft}
579 scrollTop={this.state.scrollTop}
580 height={tableHeight}
581 headerHeight={this.state.headerHeight}
582 isColumnResizing={this.state.isColumnResizing}
583 onColumnResizeEndCallback={this.onColumnResizeEndCallback}
584 onContentDimensionsChange={this.onContentDimensionsChange}
585 onColumnReorderEndCallback={this.onColumnReorderEndCallback}
586 scrollToColumn={this.props.scrollToColumn}
587 isColumnReordering={false}
588
589 >
590 {(() => {
591 if (!this.props.hideRowNumbers) {
592 return (
593 <Column
594 cellClassName="hugetable-index-column"
595 key="hugetable-index-column"
596 columnKey="hugetable-index-column"
597 width={rowNumberColumnWidth}
598 header={props => this.renderHeader({...props, cellData: {main: '#'}})}
599 cell={(props) => <Cell><TextCell {...props} cellData={{main: props.rowIndex+1}}/></Cell>}
600 />
601 );
602 }
603 })()}
604 {this.state.currentSchema.map(this.createColumn)}
605 {(() => {
606 if (this.state.shouldShowScrolls) {
607 return (
608 <Column
609 cellClassName="huge-table-right-scroll-column"
610 key="huge-table-right-scroll-column"
611 columnKey="huge-table-right-scroll-column"
612 width={this.props.buttonColumnWidth ? 40 + this.props.buttonColumnWidth : 40}
613 header={props => this.renderHeader({...props, cellData: {main: ''}})}
614 cell={(props) => <Cell><TextCell {...props} cellData={{main: ''}}/></Cell>}
615 />
616 );
617 }
618 })()}
619 </Table>
620 </div>
621 </TouchWrapper>
622 );
623 }
624}