UNPKG

wix-style-react

Version:
589 lines (516 loc) • 22.3 kB
var _class2, _temp2; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } import React, { Component } from 'react'; import PropTypes from 'prop-types'; import styles from './DataTable.scss'; import classNames from 'classnames'; import InfiniteScroll from '../utils/InfiniteScroll'; import SortByArrowUp from '../new-icons/system/SortByArrowUp'; import SortByArrowDown from '../new-icons/system/SortByArrowDown'; import { Animator } from 'wix-animations'; import InfoCircle from 'wix-ui-icons-common/InfoCircle'; import Tooltip from '../Tooltip/Tooltip'; import InfoIcon from '../common/InfoIcon'; import deprecationLog from '../utils/deprecationLog'; export var DataTableHeader = function DataTableHeader(props) { var dataHook = props.dataHook; return React.createElement( 'div', { 'data-hook': dataHook }, React.createElement( 'table', { style: { width: props.width }, className: styles.table }, React.createElement(TableHeader, props) ) ); }; DataTableHeader.propTypes = { width: PropTypes.string }; var DataTable = function (_React$Component) { _inherits(DataTable, _React$Component); function DataTable(props) { _classCallCheck(this, DataTable); var _this = _possibleConstructorReturn(this, (DataTable.__proto__ || Object.getPrototypeOf(DataTable)).call(this, props)); _this.wrapWithInfiniteScroll = function (table) { return React.createElement( InfiniteScroll, { pageStart: 0, loadMore: _this.loadMore, hasMore: _this.state.currentPage < _this.state.lastPage || _this.props.hasMore, loader: _this.props.loader, useWindow: _this.props.useWindow, scrollElement: _this.props.scrollElement }, table ); }; _this.renderTable = function (rowsToRender) { var dataHook = _this.props.dataHook; var style = { width: _this.props.width }; return React.createElement( 'div', { 'data-hook': dataHook }, React.createElement( 'table', { id: _this.props.id, style: style, className: classNames(_this.style.table, _defineProperty({}, _this.style.showLastRowDivider, _this.props.showLastRowDivider)) }, !_this.props.hideHeader && React.createElement(TableHeader, _this.props), _this.renderBody(rowsToRender) ) ); }; _this.renderBody = function (rows) { return React.createElement( 'tbody', null, rows.map(_this.renderRow) ); }; _this.onRowClick = function (rowData, rowNum) { var _this$props = _this.props, onRowClick = _this$props.onRowClick, rowDetails = _this$props.rowDetails; onRowClick && onRowClick(rowData, rowNum); rowDetails && _this.toggleRowDetails(rowNum); }; _this.renderRow = function (rowData, rowNum) { var _this$props2 = _this.props, onRowClick = _this$props2.onRowClick, onMouseEnterRow = _this$props2.onMouseEnterRow, onMouseLeaveRow = _this$props2.onMouseLeaveRow, rowDataHook = _this$props2.rowDataHook, dynamicRowClass = _this$props2.dynamicRowClass, rowDetails = _this$props2.rowDetails; var rowClasses = [_this.props.rowClass]; var optionalRowProps = {}; var handlers = [{ rowEventHandler: _this.onRowClick, eventHandler: 'onClick' }, { rowEventHandler: onMouseEnterRow, eventHandler: 'onMouseEnter' }, { rowEventHandler: onMouseLeaveRow, eventHandler: 'onMouseLeave' }]; handlers.forEach(function (_ref) { var rowEventHandler = _ref.rowEventHandler, eventHandler = _ref.eventHandler; if (rowEventHandler) { optionalRowProps[eventHandler] = function (event) { if (event.isDefaultPrevented()) { return; } rowEventHandler(rowData, rowNum); }; } }); if (onRowClick) { rowClasses.push(_this.style.clickableDataRow); } if (rowDetails) { rowClasses.push(_this.style.animatedDataRow); } if (rowDataHook) { if (typeof rowDataHook === 'string') { optionalRowProps['data-hook'] = rowDataHook; } else { optionalRowProps['data-hook'] = rowDataHook(rowData, rowNum); } } if (dynamicRowClass) { rowClasses.push(dynamicRowClass(rowData, rowNum)); } optionalRowProps.className = classNames(rowClasses); var key = rowData.id === undefined ? rowNum : rowData.id; var rowsToRender = [React.createElement( 'tr', _extends({ 'data-table-row': 'dataTableRow', key: key }, optionalRowProps), _this.props.columns.map(function (column, colNum) { return _this.renderCell(rowData, column, rowNum, colNum); }) )]; if (rowDetails) { var showDetails = !!_this.state.selectedRows[rowNum]; rowsToRender.push(React.createElement( 'tr', { key: key + '_details', className: classNames(_this.style.rowDetails) }, React.createElement( 'td', { 'data-hook': rowNum + '_details', className: classNames(_this.style.details, showDetails ? _this.style.active : ''), colSpan: _this.props.columns.length }, React.createElement( 'div', { className: classNames(_this.style.rowDetailsInner) }, React.createElement( Animator, { show: showDetails, height: true }, rowDetails(rowData, rowNum) ) ) ) )); } return rowsToRender; }; _this.renderCell = function (rowData, column, rowNum, colNum) { var _classNames2; var classes = classNames((_classNames2 = {}, _defineProperty(_classNames2, _this.style.important, column.important), _defineProperty(_classNames2, _this.style.largeVerticalPadding, _this.props.rowVerticalPadding === 'large'), _defineProperty(_classNames2, _this.style.mediumVerticalPadding, _this.props.rowVerticalPadding !== 'large'), _defineProperty(_classNames2, _this.style.alignStart, column.align === 'start'), _defineProperty(_classNames2, _this.style.alignCenter, column.align === 'center'), _defineProperty(_classNames2, _this.style.alignEnd, column.align === 'end'), _classNames2)); var width = rowNum === 0 && _this.props.hideHeader ? column.width : undefined; return React.createElement( 'td', { style: column.style, width: width, className: classes, key: colNum }, column.render && column.render(rowData, rowNum) ); }; _this.calcLastPage = function (_ref2) { var data = _ref2.data, itemsPerPage = _ref2.itemsPerPage; return Math.ceil(data.length / itemsPerPage) - 1; }; _this.loadMore = function () { if (_this.state.currentPage < _this.state.lastPage) { _this.setState({ currentPage: _this.state.currentPage + 1 }); } else { _this.props.loadMore && _this.props.loadMore(); } }; _this.toggleRowDetails = function (selectedRow) { var selectedRows = _defineProperty({}, selectedRow, !_this.state.selectedRows[selectedRow]); if (_this.props.allowMultiDetailsExpansion) { selectedRows = Object.assign({}, _this.state.selectedRows, _defineProperty({}, selectedRow, !_this.state.selectedRows[selectedRow])); } _this.setState({ selectedRows: selectedRows }); }; var state = { selectedRows: {} }; if (props.infiniteScroll) { state = _extends({}, state, _this.createInitialScrollingState(props)); } _this.state = state; return _this; } _createClass(DataTable, [{ key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { var isLoadingMore = false; if (this.props.infiniteScroll && nextProps.data !== this.props.data) { if (nextProps.data instanceof Array && this.props.data instanceof Array) { if (this.props.data.every(function (elem, index) { return nextProps.data.length > index && nextProps.data[index] === elem; })) { isLoadingMore = true; var lastPage = this.calcLastPage(nextProps); var currentPage = this.state.currentPage < lastPage ? this.state.currentPage + 1 : this.state.currentPage; this.setState({ lastPage: lastPage, currentPage: currentPage }); } } if (!isLoadingMore) { this.setState(this.createInitialScrollingState(nextProps)); } } } }, { key: 'shouldComponentUpdate', value: function shouldComponentUpdate() { // DataTable extends WixComponent which is a PureComponent, but DataTable is not pure. // returning true, disables the PureComponent optimization. return true; } }, { key: 'createInitialScrollingState', value: function createInitialScrollingState(props) { return { currentPage: 0, lastPage: this.calcLastPage(props) }; } }, { key: 'render', value: function render() { var _props = this.props, data = _props.data, showHeaderWhenEmpty = _props.showHeaderWhenEmpty, infiniteScroll = _props.infiniteScroll, itemsPerPage = _props.itemsPerPage; if (!data.length && !showHeaderWhenEmpty) { return null; } var rowsToRender = infiniteScroll ? data.slice(0, (this.state.currentPage + 1) * itemsPerPage) : data; var table = this.renderTable(rowsToRender); if (infiniteScroll) { return this.wrapWithInfiniteScroll(table); } return table; } }, { key: 'style', get: function get() { return styles; } }]); return DataTable; }(React.Component); var TableHeader = (_temp2 = _class2 = function (_Component) { _inherits(TableHeader, _Component); function TableHeader() { var _ref3; var _temp, _this2, _ret; _classCallCheck(this, TableHeader); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this2 = _possibleConstructorReturn(this, (_ref3 = TableHeader.__proto__ || Object.getPrototypeOf(TableHeader)).call.apply(_ref3, [this].concat(args))), _this2), _this2.renderSortingArrow = function (sortDescending, colNum) { if (sortDescending === undefined) { return null; } if (_this2.props.newDesign) { var Arrow = sortDescending ? SortByArrowUp : SortByArrowDown; return React.createElement( 'span', { 'data-hook': colNum + '_title', className: _this2.style.sortArrow }, React.createElement(Arrow, { height: 12, 'data-hook': sortDescending ? 'sort_arrow_dec' : 'sort_arrow_asc' }) ); } var sortDirectionClassName = sortDescending ? _this2.style.sortArrowAsc : _this2.style.sortArrowDesc; return React.createElement( 'span', { 'data-hook': colNum + '_title', className: sortDirectionClassName }, React.createElement(SortByArrowUp, null) ); }, _this2.renderInfoTooltip = function (tooltipProps, colNum) { if (tooltipProps === undefined) { return null; } if (_this2.props.newDesign) { return React.createElement(InfoIcon, { tooltipProps: tooltipProps, dataHook: colNum + '_info_tooltip', className: _this2.style.infoTooltipWrapper }); } else { var _tooltipProps = Object.assign({ theme: 'dark' }, tooltipProps, { dataHook: colNum + '_info_tooltip', moveBy: { x: 2.5, y: -7 } }); return React.createElement( Tooltip, _tooltipProps, React.createElement( 'span', { className: _this2.style.infoTooltipWrapper }, React.createElement(InfoCircle, { className: _this2.style.infoIcon, size: 24 }) ) ); } }, _this2.renderHeaderCell = function (column, colNum) { var _classNames4; var infoTooltipProps = column.infoTooltipProps; // Deprecate `infoTooltip` in favor of `infoTooltipProps` if (!infoTooltipProps && column.infoTooltip) { infoTooltipProps = column.infoTooltip; deprecationLog("Property `infoTooltip` of Table's `columns` prop is deprecated; use `infoTooltipProps` instead."); } var style = { width: column.width, padding: _this2.props.thPadding, height: _this2.props.thHeight, fontSize: _this2.props.thFontSize, border: _this2.props.thBorder, boxShadow: _this2.props.thBoxShadow, color: _this2.props.thColor, opacity: _this2.props.thOpacity, letterSpacing: _this2.props.thLetterSpacing, cursor: column.sortable === undefined ? 'arrow' : 'pointer' }; var thClasses = classNames(_defineProperty({}, _this2.style.thText, _this2.props.newDesign)); var optionalHeaderCellProps = {}; if (column.sortable) { optionalHeaderCellProps.onClick = function () { return _this2.props.onSortClick && _this2.props.onSortClick(column, colNum); }; } return React.createElement( 'th', _extends({ style: style, key: colNum, className: thClasses }, optionalHeaderCellProps), React.createElement( 'div', { className: classNames(_this2.style.thContainer, (_classNames4 = {}, _defineProperty(_classNames4, _this2.style.alignStart, !column.align || column.align === 'start'), _defineProperty(_classNames4, _this2.style.alignCenter, column.align === 'center'), _defineProperty(_classNames4, _this2.style.alignEnd, column.align === 'end'), _classNames4)) }, column.title, _this2.renderSortingArrow(column.sortDescending, colNum), _this2.renderInfoTooltip(infoTooltipProps, colNum) ) ); }, _temp), _possibleConstructorReturn(_this2, _ret); } _createClass(TableHeader, [{ key: 'render', value: function render() { return React.createElement( 'thead', null, React.createElement( 'tr', null, this.props.columns.map(this.renderHeaderCell) ) ); } }, { key: 'style', get: function get() { return styles; } }]); return TableHeader; }(Component), _class2.propTypes = { onSortClick: PropTypes.func, thPadding: PropTypes.string, thHeight: PropTypes.string, thFontSize: PropTypes.string, thBorder: PropTypes.string, thColor: PropTypes.string, thOpacity: PropTypes.string, thLetterSpacing: PropTypes.string, thBoxShadow: PropTypes.string, columns: PropTypes.array, newDesign: PropTypes.bool }, _temp2); function validateData(props, propName) { if (props[propName]) { if (props[propName].constructor && props[propName].constructor.name && props[propName].constructor.name.toLowerCase().indexOf('array') > -1) { return null; } else { return Error('Data element must be an array type'); } } return null; } DataTable.defaultProps = { data: [], columns: [], showHeaderWhenEmpty: false, infiniteScroll: false, itemsPerPage: 20, width: '100%', loadMore: null, hasMore: false, loader: React.createElement( 'div', { className: 'loader' }, 'Loading ...' ), scrollElement: null, useWindow: true, rowVerticalPadding: 'medium', showLastRowDivider: true }; /* eslint-disable no-unused-vars */ var _Tooltip$propTypes = Tooltip.propTypes, moveBy = _Tooltip$propTypes.moveBy, dataHook = _Tooltip$propTypes.dataHook, infoTooltipProps = _objectWithoutProperties(_Tooltip$propTypes, ['moveBy', 'dataHook']); DataTable.propTypes = { dataHook: PropTypes.string, /** An id to pass to the table */ id: PropTypes.string, /** The data to display. (If data.id exists then it will be used as the React key value for each row, otherwise, the rowIndex will be used) */ data: validateData, /** Configuration of the table's columns. See table below */ columns: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]).isRequired, render: PropTypes.func.isRequired, sortable: PropTypes.bool, infoTooltipProps: PropTypes.shape(infoTooltipProps), sortDescending: PropTypes.bool, align: PropTypes.oneOf(['start', 'center', 'end']) })).isRequired, /** Should the table show the header when data is empty */ showHeaderWhenEmpty: PropTypes.bool, /** A string data-hook to apply to all table body rows. or a func which calculates the data-hook for each row - Signature: `(rowData, rowNum) => string` */ rowDataHook: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** A class to apply to all table body rows */ rowClass: PropTypes.string, /** A func that gets row data and returns a class(es) to apply to that specific row */ dynamicRowClass: PropTypes.func, /** A callback method to be called on row click. Signature: `onRowClick(rowData, rowNum)` */ onRowClick: PropTypes.func, /** A callback method to be called on row mouse enter. Signature: `onMouseEnterRow(rowData, rowNum)` */ onMouseEnterRow: PropTypes.func, /** A callback method to be called on row mouse leave. Signature: `onMouseLeaveRow(rowData, rowNum)` */ onMouseLeaveRow: PropTypes.func, /** If true, table will not render all data to begin with, but will gradually render the data as the user scrolls */ infiniteScroll: PropTypes.bool, /** If infiniteScroll is on, this prop will determine how many rows will be rendered on each load */ itemsPerPage: PropTypes.number, /** The width of the fixed table. Can be in percentages or pixels. */ width: PropTypes.string, /** A callback when more items are requested by the user. */ loadMore: PropTypes.func, /** Whether there are more items to be loaded. Event listeners are removed if false. */ hasMore: PropTypes.bool, /** The loader to show when loading more items. */ loader: PropTypes.node, /** Add scroll listeners to the window, or else, the component's parentNode. */ useWindow: PropTypes.bool, /** Add scroll listeners to specified DOM Object. */ scrollElement: PropTypes.object, /** Table cell vertical padding. should be 'medium' or 'large' */ rowVerticalPadding: PropTypes.oneOf(['medium', 'large']), /** this prop is deprecated and should not be used * @deprecated */ thPadding: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thHeight: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thFontSize: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thBorder: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thColor: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thOpacity: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thBoxShadow: PropTypes.string, /** this prop is deprecated and should not be used * @deprecated */ thLetterSpacing: PropTypes.string, /** Function that returns React component that will be rendered in row details section. Example: `rowDetails={(row, rowNum) => <MyRowDetailsComponent {...row} />}` */ rowDetails: PropTypes.func, /** Allows to open multiple row details */ allowMultiDetailsExpansion: PropTypes.bool, /** Should we hide the header of the table. */ hideHeader: PropTypes.bool, /** A flag specifying weather to show a divider after the last row */ showLastRowDivider: PropTypes.bool, newDesign: PropTypes.bool }; DataTable.displayName = 'DataTable'; export default DataTable;