UNPKG

wix-style-react

Version:
296 lines 17.3 kB
import React from 'react'; import PropTypes from 'prop-types'; import defaultTo from 'lodash/defaultTo'; import classNames from 'classnames'; import { ScrollSync } from 'react-scroll-sync'; import { classes } from './Table.st.css'; import DataTable from './DataTable'; import Checkbox from '../Checkbox'; import { TableContext } from './TableContext'; import { BulkSelection } from './BulkSelection'; import { TooltipCommonProps } from '../common/PropTypes/TooltipCommon'; import * as dataTableRowVirtualStyle from './DataTable/components/DataTableRowVirtual.st.css'; import { TableBulkSelectionCheckbox, TableContent, TableEmptyState, TableSubToolbar, TableTitleBar, TableToolbarContainer, } from './components'; const hasUnselectablesSymbol = Symbol('hasUnselectables'); export function createColumns({ tableProps, bulkSelectionContext }) { const { dragAndDrop, isDragAndDropDisabled } = tableProps; const includeDragHandleColumn = dragAndDrop != null && dragAndDrop.createDragHandleColumn != null && !isDragAndDropDisabled; const createCheckboxColumn = ({ toggleSelectionById, isSelected, selectionDisabled, }) => { const isRowSelectionDisabled = rowData => selectionDisabled === true || (typeof selectionDisabled === 'function' && selectionDisabled(rowData)); return { key: 'bulk-selection-cell', title: tableProps.hideBulkSelectionCheckbox ? ('') : (React.createElement(TableBulkSelectionCheckbox, { dataHook: "table-select" })), dataHook: 'bulk-selection-cell', onCellClick: (column, row, rowNum, event) => { if (row.unselectable) { return; } event.stopPropagation(); if (isRowSelectionDisabled(row)) { return; } const id = defaultTo(row.id, rowNum); toggleSelectionById(id, 'Checkbox'); }, render: (row, rowNum) => { const id = defaultTo(row.id, rowNum); const tooltipContent = row.checkboxTooltipContent; return row.unselectable ? null : (React.createElement(Checkbox, { disabled: isRowSelectionDisabled(row), dataHook: "row-select", checked: isSelected(id), tooltipProps: { disabled: !tooltipContent }, tooltipContent: tooltipContent })); }, width: '12px', style: (_, row) => (row.unselectable ? undefined : { cursor: 'pointer' }), }; }; const checkboxColumn = tableProps.showSelection ? createCheckboxColumn(bulkSelectionContext) : null; const columns = [ ...(includeDragHandleColumn ? [dragAndDrop.createDragHandleColumn(tableProps)] : []), ...(checkboxColumn ? [checkboxColumn] : []), ...tableProps.columns, ]; // Apply different column width when checkbox column is the last sticky column // (looks weird otherwise when stickied with default 12px width). if (checkboxColumn && columns.indexOf(checkboxColumn) + 1 === tableProps.stickyColumns) { checkboxColumn.width = '34px'; } return columns; } export function getDataTableProps(tableProps) { /* eslint-disable no-unused-vars */ const { showSelection, onSelectionChanged, onSelectionStarted, dataHook, ...props } = tableProps; return { ...props, rowClass: classNames(tableProps.rowClass, classes.tableRow), }; } /** * Table is a composite component that allows adding SelectionColumn, Toolbar (on top of the TitleBar). * It is a context provider, and thus the Table.Consumer, Table.TitleBar and Table.Content can be rendered separately. */ export class Table extends React.Component { constructor() { super(...arguments); this.state = { leftShadowVisible: false, rightShadowVisible: false, }; this._handleUpdateScrollShadows = (leftShadowVisible, rightShadowVisible) => { if (leftShadowVisible !== this.state.leftShadowVisible) { this.setState({ leftShadowVisible }); } if (rightShadowVisible !== this.state.rightShadowVisible) { this.setState({ rightShadowVisible }); } }; } shouldComponentUpdate() { // Table is not really a PureComponent return true; } setSelectedIds(selectedIds) { this.bulkSelection.setSelectedIds(selectedIds); } renderChildren() { const { children, withWrapper, dataHook } = this.props; return withWrapper ? React.createElement("div", { "data-hook": dataHook }, children) : children; } render() { const { data, selectedIds, showSelection, deselectRowsByDefault, infiniteScroll, totalSelectableCount, onSelectionChanged, onSelectionStarted, hasMore, horizontalScroll, selectionDisabled, dragAndDrop, onDragEnd, } = this.props; let hasUnselectables = null; let allIds = data.map((rowData, rowIndex) => rowData.unselectable || (typeof selectionDisabled === 'function' && selectionDisabled(rowData)) ? (hasUnselectables = hasUnselectablesSymbol) : defaultTo(rowData.id, rowIndex)); if (hasUnselectables === hasUnselectablesSymbol) { allIds = allIds.filter(rowId => rowId !== hasUnselectablesSymbol); } const { leftShadowVisible, rightShadowVisible } = this.state; const contextValue = { ...this.props, leftShadowVisible, rightShadowVisible, onUpdateScrollShadows: this._handleUpdateScrollShadows, }; let table = (React.createElement(TableContext.Provider, { value: contextValue }, showSelection ? (React.createElement(BulkSelection, { ref: _ref => (this.bulkSelection = _ref), selectedIds: selectedIds, deselectRowsByDefault: deselectRowsByDefault, selectionDisabled: selectionDisabled, hasMoreInBulkSelection: infiniteScroll && Boolean(totalSelectableCount) && hasMore, totalCount: totalSelectableCount, allIds: allIds, onSelectionChanged: onSelectionChanged, onSelectionStarted: onSelectionStarted }, this.renderChildren())) : (this.renderChildren()))); table = horizontalScroll ? (React.createElement(ScrollSync, { proportional: false, horizontal: true, vertical: false }, table)) : (table); return table; } } Table.ToolbarContainer = TableToolbarContainer; Table.Titlebar = TableTitleBar; Table.Content = TableContent; Table.SubToolbar = TableSubToolbar; Table.EmptyState = TableEmptyState; Table.BulkSelectionCheckbox = TableBulkSelectionCheckbox; Table.displayName = 'Table'; Table.dataTableRowVirtualStyle = dataTableRowVirtualStyle; Table.defaultProps = { ...DataTable.defaultProps, showSelection: false, hideBulkSelectionCheckbox: false, children: [React.createElement(Table.Content, { key: "content" })], withWrapper: true, showLastRowDivider: false, horizontalScroll: false, stickyColumns: 0, isRowDisabled: () => false, deselectRowsByDefault: false, }; Table.propTypes = { /** Accept any wrapper component as a child element. It must eventually include <Table.Content/>. */ children: PropTypes.any, /** Applies a data-hook HTML attribute to be used in the tests */ dataHook: PropTypes.string, /** Defines a callback function which is called when selection happens. <br /> * There are 3 types of selections: * * `ALL` - bulk selection checkbox has been triggered to select all items * * `SINGLE_TOGGLE` - a checkbox to select one item from the list has been triggered. The change object will also include an `id` prop with the unique item identifier and a `value` prop with the new boolean selection state of the item. * * `NONE` - bulk selection has been triggered to unselect all items. * */ onSelectionChanged: PropTypes.func, /** Called when item selection triggered but not changed yet. */ onSelectionStarted: PropTypes.func, /** Displays selection checkbox as a first column in each row. To hide the selection checkbox from a specific row, set its `row.unselectable` value to true in the data array. */ showSelection: PropTypes.bool, /** Hides bulk selection checkbox in the table titlebar */ hideBulkSelectionCheckbox: PropTypes.bool, /** Defines an array of selected rows */ selectedIds: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.number), ]), /** Specifies whether table row selection is restricted at a given moment. Can be defined as: * * `bool` disables selection for all table rows * * `function` that will be called for each row to check whether to disable selection or not depending on specified condition */ selectionDisabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), /** Controls bulk selection checkbox behaviour in indeterminate state. By default, first click on indeterminate state selects all items in the table. If set to `true`, first click will deselect all items instead. */ deselectRowsByDefault: PropTypes.bool, /** * When false then Table would not create a `<div/>` wrapper around it's children. * Useful when using `<Table/>` to wrap a `<Page/>` component, in that case we use the `<Table/>` only as a context provider and it does not render anything to the DOM by itself.*/ withWrapper: PropTypes.bool, /** * Defines a callback function which is called on column title click */ onSortClick: PropTypes.func, // The following props are derived directly from <DataTable/> component /** Allows to expand multiple row details at once */ allowMultiDetailsExpansion: PropTypes.bool, /** Defines any amount of data to be displayed in the table. Properties for each entry:<br> * * `id`: should be provided and will be used as a React key internally * * `unselectable`: specifies whether a row can be selected by a user when `showSelection` is enabled for the whole table * * `checkboxTooltipContent`: defines tooltip content to show when user hovers selection checkbox * * any number of records with unique identifiers for column rendering * */ data: PropTypes.array, // Not performing any shape validation to not hurt performance. /** Configures table columns. Required properties for each column:<br /> * * `title`: specifies a text string or an element to display in the table titlebar * * `render`: defines a function which will be called to display this row value for this column<br> * * Optional properties for each column: * * `onCellClick`: defines a callback function which is called each time a cell in this column is clicked. Signature: `onCellClick(column, rowData, rowNum, event)` * * `sortable`: specifies whether field is sortable. If true clicking the header will call `onSortClick`. * * `sortDescending`: specifies what sort icon to display in the column header. `true` shows an up arrow, `false` shows a down arrow, `undefined' shows no icon. * * `infoTooltipProps`: controls info [tooltip](/?path=/story/components-overlays--tooltip) appearance in titlebar. Note: `dataHook`, `moveBy` and `children` will not be passed to tooltip. * * `style`: applies a custom CSS class to the component’s root element * * `align`: controls the alignment of the column content * * `width`: sets the width of a column. No value means column will try to contain its children, if possible. * * `important`: increases text size of this column data across all rows, sets font color to D10 * * `stickyActionCell`: specifies whether `<TableActionCell/>` controls are fixed to the right side of the table * */ columns: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]).isRequired, render: PropTypes.func.isRequired, onCellClick: PropTypes.func, sortable: PropTypes.bool, sortDescending: PropTypes.bool, infoTooltipProps: PropTypes.shape(TooltipCommonProps), style: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), align: PropTypes.oneOf(['start', 'center', 'end']), width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), important: PropTypes.bool, stickyActionCell: PropTypes.bool, })).isRequired, /** Defines a function that gets row data and returns a class(es) to apply to that specific row */ dynamicRowClass: PropTypes.func, /** Defines a function that gets row data and returns boolean value specifying whether row is highlighted or not */ isRowHighlight: PropTypes.func, /** Indicates whether there is additional data to load from external sources though `loadMore` function. When set to true `loadMore` property will be called until false value is received. */ hasMore: PropTypes.bool, /** Invokes `loadMore` on the initial table render */ initialLoad: PropTypes.bool, /** Assigns an unique identifier for the table */ id: PropTypes.string, /** Renders data gradually as user scrolls down the list */ infiniteScroll: PropTypes.bool, /** Specifies the total number of selectable items in the table (including items not loaded yet). * When ‘Select all’ is triggered in infinite scroll tables, newly loaded items will be selected automatically. In this case, `SelectionContext` holds not-selected items rather than the selected ones. * SelectionContext.infiniteBulkSelected is true and SelectionContext.selectedCount is the value of totalSelectableCount minus the count of unselected items. */ totalSelectableCount: PropTypes.number, /** Determines the number of rows to rendered on each load of an infinite scroll table */ itemsPerPage: PropTypes.number, /** Pass a loader to show when loading more items in infinite scroll table. Most commonly pass <Loader/> or <Text/> components. */ loader: PropTypes.node, /** Defines a callback function which is called each time user request to load more items */ loadMore: PropTypes.func, /** Defines a callback function which is called each time user clicks on the row. This prop is required to enable row hover effect. */ onRowClick: PropTypes.func, /** Defines a callback function which is called each time mouse enters the row */ onMouseEnterRow: PropTypes.func, /** Defines a callback function which is called each time mouse leaves the row */ onMouseLeaveRow: PropTypes.func, /** Add scroll listeners to the window, or else, the component's parentNode. */ useWindow: PropTypes.bool, /** Adds scroll listeners to specified DOM object */ scrollElement: PropTypes.object, /** Controls the amount of white space above and below row content: * - `large`: 24px * - `medium`: 18px * - `small`: with the feature toggle: 15px, without the feature toggle: 12px * - `tiny`: 12px * */ rowVerticalPadding: PropTypes.oneOf(['tiny', 'small', 'medium', 'large']), /** Defines a function that returns a component to render in the expanded row content area */ rowDetails: PropTypes.func, /** Removes paddings from an expanded row content area */ removeRowDetailsPadding: PropTypes.bool, /** Applies a data-hook HTML attribute or a function that calculates the data-hook for each row on the table */ rowDataHook: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** A class to apply to all table body rows */ rowClass: PropTypes.string, /** Specifies whether to show table title bar when there’s no data in the table */ showHeaderWhenEmpty: PropTypes.bool, /** Specifies whether last row in the table should have a bottom divider */ showLastRowDivider: PropTypes.bool, /** A flag specifying weather to apply column width style to table cells */ isApplyColumnWidthStyle: PropTypes.bool, /** ++EXPERIMENTAL++ Virtualize the table scrolling for long list items */ virtualized: PropTypes.bool, /** ++EXPERIMENTAL++ Controls virtualized table height */ virtualizedTableHeight: PropTypes.number, /** ++EXPERIMENTAL++ Set virtualized table row height */ virtualizedLineHeight: PropTypes.number, /** ++EXPERIMENTAL++ Set ref on virtualized list containing table rows */ virtualizedListRef: PropTypes.any, /** Defines the width of the table in percentages or pixels. */ width: PropTypes.string, /** Controls the appearance of a table title bar */ skin: PropTypes.oneOf(['standard', 'neutral']), /** Specifies whether table content can exceed the width of the table. If enabled, table can be scrolled horizontally. */ horizontalScroll: PropTypes.bool, /** Specifies the number of columns to stick to the left side in tables that support horizontal scroll */ stickyColumns: PropTypes.number, /** Defines a function to call for each row to check whether row should appear as disabled or not */ isRowDisabled: PropTypes.func, dragAndDrop: PropTypes.object, onDragEnd: PropTypes.func, }; // export default Table; //# sourceMappingURL=Table.js.map