UNPKG

@bigfishtv/cockpit

Version:

201 lines (186 loc) 6.38 kB
import PropTypes from 'prop-types' import React, { Component } from 'react' import { Table, Column } from 'fixed-data-table' import deepEqual from 'deep-equal' import classnames from 'classnames' import { titleCase } from '../../utils/stringUtils' import { sortByObjectKey } from '../../utils/tableUtils' import * as SortTypes from '../../constants/SortTypes' import FixedDataTableTextCell from './cell/FixedDataTableTextCell' import FixedDataTableHeaderCellSort from './cell/FixedDataTableHeaderCellSort' // we define this because react-docgen fails when defaultProp directly references an imported component const DefaultHeaderCell = props => <FixedDataTableHeaderCellSort {...props} /> const DefaultCell = props => <FixedDataTableTextCell {...props} /> /** * Typically wrapped by Table component, simply displays a fixed-data-table with the data provided */ export default class FixedDataTable extends Component { static propTypes = { /** Array of objects representing table rows */ data: PropTypes.arrayOf(PropTypes.object), /** Array of objects representing table columns/schema */ fields: PropTypes.arrayOf(PropTypes.object), /** Array of selected row ids */ selectedIds: PropTypes.arrayOf(PropTypes.number), /** Whether or not component should control its own sorting */ uncontrolled: PropTypes.bool, /** Whether or not to inject a checkbox column to control selection state */ checkboxSelection: PropTypes.bool, tableWidth: PropTypes.number, tableHeight: PropTypes.number, cellWidth: PropTypes.number, rowHeight: PropTypes.number, headerHeight: PropTypes.number, /** column key to sort by, can even be nested e.g. 'collection.title' */ defaultSortField: PropTypes.string, defaultSortDirections: PropTypes.oneOf([SortTypes.ASC, SortTypes.DESC]), /** On row select */ onSelect: PropTypes.func, /** On row double click */ onSelected: PropTypes.func, /** For updating selectedIds in bulk */ onSelectionChange: PropTypes.func, /** Component to replace default HeaderCell */ HeaderCell: PropTypes.func, /** Component to replace default table cell component */ DefaultCell: PropTypes.func, } static defaultProps = { data: [], fields: [], selectedIds: [], uncontrolled: true, tableWidth: 600, tableHeight: 500, cellWidth: 250, rowHeight: 50, headerHeight: 36, defaultSortField: 'modified', defaultSortDirection: SortTypes.DESC, onSelect: () => console.warn('[FixedDataTable] no onSelect prop provided'), onSelected: () => console.warn('[FixedDataTable] no onSelected prop provided'), DefaultHeaderCell, DefaultCell, } constructor(props) { super() const columnWidths = {} props.fields.map(field => { columnWidths[field.key] = field.width || props.cellWidth }) this.state = { data: props.data, columnSortDirections: props.columnKey && props.sortDirection ? { [props.columnKey]: props.sortDirection } : {}, columnWidths, } } componentWillReceiveProps(nextProps) { if (!deepEqual(this.state.data, nextProps.data)) { const columnKey = !this.state.data.length ? this.props.defaultSortField : Object.keys(this.state.columnSortDirections)[0] const sortDirection = !this.state.data.length ? this.props.defaultSortDirection : this.state.columnSortDirections[columnKey] this.setState({ data: nextProps.data }, () => { if (this.props.uncontrolled) this.onSortChange(columnKey, sortDirection) }) } } onSortChange = (columnKey, sortDirection) => { const { fields, uncontrolled, onSortChange } = this.props const field = fields.filter(field => field.key === columnKey)[0] const sortType = 'sortType' in field ? field.sortType : 'string' if (!uncontrolled) { onSortChange(columnKey, sortDirection, sortType) } else { const data = [...this.state.data].sort(sortByObjectKey(columnKey, sortDirection, sortType)) this.setState({ data }) } this.setState({ columnSortDirections: { [columnKey]: sortDirection } }) } handleColumnResize = (newColumnWidth, columnKey) => { this.setState({ columnWidths: { ...this.state.columnWidths, [columnKey]: newColumnWidth, }, }) } handleGetRowClassName = rowIndex => { const row = this.state.data[rowIndex] const selected = this.props.selectedIds.indexOf(row.id) >= 0 return selected ? 'selected' : '' } handleRowClick = (event, rowIndex) => { event.stopPropagation() this.props.onSelect(this.state.data[rowIndex]) } handleRowDoubleClick = (event, rowIndex) => { event.stopPropagation() this.props.onSelected(this.state.data[rowIndex]) } render() { const { data, columnSortDirections, columnWidths } = this.state const { fields, selectedIds, checkboxSelection, tableWidth, tableHeight, headerHeight, rowHeight, HeaderCell, DefaultCell, DefaultHeaderCell, ...props } = this.props return ( <Table {...props} rowsCount={data.length} headerHeight={headerHeight} rowHeight={rowHeight} width={tableWidth} height={tableHeight} onColumnResizeEndCallback={this.handleColumnResize} isColumnResizing={false} rowClassNameGetter={this.handleGetRowClassName} onRowClick={checkboxSelection ? () => {} : this.handleRowClick} onRowDoubleClick={this.handleRowDoubleClick}> {fields.map((field, i) => { const BodyCell = field.Cell || DefaultCell const HeaderCell = field.HeaderCell || DefaultHeaderCell const width = columnWidths[field.key] const headerTitle = field.value || titleCase(field.key) const sortDir = columnSortDirections[field.key] const schema = field.schema || {} return ( <Column key={i} columnKey={field.key} fixed={field.fixed} isResizable={field.resizable} minWidth={field.minWidth} maxWidth={field.maxWidth} flexGrow={field.flexGrow} header={ <HeaderCell data={data} selectedIds={selectedIds} onSelectionChange={this.props.onSelectionChange} onSortChange={this.onSortChange} sortDir={sortDir} className={classnames(field.sortable && 'sortable')}> {headerTitle} </HeaderCell> } cell={<BodyCell data={data} schema={schema} onSelect={this.props.onSelect} selectedIds={selectedIds} />} width={width} /> ) })} </Table> ) } }