UNPKG

wix-style-react

Version:
201 lines • 10.5 kB
import React from 'react'; import PropTypes from 'prop-types'; import { AddSmall as AddSmallIcon } from '@wix/wix-ui-icons-common'; import cloneDeep from 'lodash/cloneDeep'; import uniqueId from 'lodash/uniqueId'; import { stVars } from '../Foundation/stylable/easing.st.css'; import { DataHook, DataAttribute } from './constants'; import { st, stVars as timeTableStVars, classes } from './TimeTable.st.css'; import SortableListBase from '../SortableListBase'; import AddItem from '../AddItem'; import Item from './components/Item'; const getSortableListBaseItems = columns => columns.map(({ items, disabled: columnDisabled = false }) => items.map(({ content, disabled, draggable }) => { const isDisabled = disabled !== undefined ? disabled : columnDisabled; const isDraggable = draggable !== undefined ? draggable : !isDisabled; return { id: uniqueId(), content, draggable: !!isDraggable, disabled: !!isDisabled, }; })); class TimeTable extends React.PureComponent { constructor() { super(...arguments); this.state = { hoveredColumnIndex: null, dragging: false, columns: [], sortableListBaseItems: [], groupName: uniqueId('time-table'), }; this._renderHeader = () => (React.createElement("div", { className: classes.headerContainer }, this.props.columns.map(({ active = false, disabled = false, title, subtitle, droppable = !disabled, }, index) => (React.createElement("div", { key: index, className: st(classes.header, { active, disabled, droppable }) }, React.createElement("div", { "data-hook": DataHook.Title, className: st(classes.title, { active }) }, title), React.createElement("div", { "data-hook": DataHook.Subtitle, className: st(classes.subtitle, { active }) }, subtitle)))))); this._renderContent = () => { const { columns, insertPosition, onAdd, height } = this.props; const { sortableListBaseItems, hoveredColumnIndex, groupName } = this.state; return (React.createElement("div", { className: classes.contentContainer }, columns.map(({ active, disabled, droppable = !disabled, actions }, columnIndex) => { const columnIsHovered = hoveredColumnIndex === columnIndex; const actionsVisible = !disabled && columnIsHovered; const addItemVisible = actionsVisible && !!onAdd && !(actions && actions.length); return (React.createElement("div", { key: columnIndex, "data-hook": DataHook.Column, onMouseEnter: () => this._handleMouseEnter(columnIndex), onMouseLeave: this._handleMouseLeave, className: st(classes.column, { droppable, }), [DataAttribute.ColumnActive]: !!active, [DataAttribute.ColumnDisabled]: !!disabled, [DataAttribute.ColumnDroppable]: droppable }, React.createElement(SortableListBase, { usePortal: true, className: classes.content, groupName: groupName, onDragStart: this._handleDragStart, onDragEnd: this._handleDragEnd, containerId: String(columnIndex), items: sortableListBaseItems[columnIndex], droppable: droppable, onDrop: this._handleDrop, canDrag: this._handleCanDrag, renderItem: this._renderItem, insertPosition: insertPosition, animationDuration: 500, animationTiming: stVars['ease-7'], style: { height: this.state.dragging ? `calc(${height} - ${timeTableStVars.headerHeight} - ${timeTableStVars.columnHeaderMargin} - ${timeTableStVars.addItemPlaceholder})` : undefined, } }), addItemVisible && this._renderAddItemButton(columnIndex), actionsVisible && this._renderActions(columnIndex))); }))); }; this._renderItem = ({ isPlaceholder, previewStyles, item: { content, disabled, draggable }, }) => (React.createElement("div", { "data-hook": DataHook.Item, className: st(classes.item, { isPlaceholder }), style: previewStyles, [DataAttribute.ItemDraggable]: draggable, [DataAttribute.ItemDisabled]: disabled }, typeof content === 'function' ? (content({ Item, disabled, draggable })) : (React.createElement(Item, { disabled: disabled, draggable: draggable }, content)))); this._renderActions = columnIndex => { if (!this.props.columns || !this.props.columns[columnIndex] || !this.props.columns[columnIndex].actions) { return null; } const column = this.props.columns[columnIndex]; const columnActions = column.actions; return (React.createElement("div", { className: classes.actionsContainer }, columnActions.map((action, index) => { return (React.createElement(AddItem, { key: index, className: classes.addItemButton, dataHook: DataHook.ColumnAction, showIcon: false, onClick: e => { action.onClick(e, column, columnIndex); } }, React.createElement("span", { className: classes.addItemLabel }, action.prefixIcon, action.label))); }))); }; this._renderAddItemButton = columnIndex => (React.createElement("div", null, React.createElement(AddItem, { className: classes.addItemButton, dataHook: DataHook.AddItemButton, showIcon: false, onClick: () => this.props.onAdd(columnIndex) }, React.createElement("span", { className: classes.addItemLabel }, React.createElement(AddSmallIcon, null), this.props.addItemButtonLabel)))); this._handleDrop = ({ addedIndex: addedItemIndex, removedIndex: removedItemIndex, addedToContainerId, removedFromContainerId, }) => { const { columns } = this.props; const addedToColumnIndex = Number(addedToContainerId); const removedFromColumnIndex = Number(removedFromContainerId); this.setState({ hoveredColumnIndex: addedToColumnIndex }); if (addedToColumnIndex === removedFromColumnIndex && addedItemIndex === removedItemIndex) { return; } const newColumns = cloneDeep(columns); const removedItem = columns[removedFromColumnIndex].items[removedItemIndex]; newColumns[removedFromColumnIndex].items.splice(removedItemIndex, 1); newColumns[addedToColumnIndex].items.splice(addedItemIndex, 0, removedItem); if (this.props.onChange) { this.props.onChange(newColumns, { addedToColumnIndex, removedFromColumnIndex, addedItemIndex, removedItemIndex, }); } }; this._handleMouseEnter = columnIndex => { if (!this.state.dragging) { this.setState({ hoveredColumnIndex: columnIndex }); } }; this._handleMouseLeave = () => { if (!this.state.dragging) { this.setState({ hoveredColumnIndex: null }); } }; this._handleDragStart = () => { this.setState({ dragging: true, hoveredColumnIndex: null, }); }; this._handleDragEnd = () => { this.setState({ dragging: false, }); }; this._handleCanDrag = ({ item }) => !!item.draggable; } static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.columns === prevState.columns) { return null; } return { ...prevState, columns: nextProps.columns, sortableListBaseItems: getSortableListBaseItems(nextProps.columns), }; } render() { const { dataHook, height } = this.props; const { dragging } = this.state; return (React.createElement("div", { className: st(classes.root, { dragging }), style: { height }, "data-hook": dataHook }, this._renderHeader(), this._renderContent())); } } TimeTable.displayName = 'TimeTable'; TimeTable.propTypes = { /** Hook for testing purposes. */ dataHook: PropTypes.string, /** * Event triggered on column data change: * `onChange(columns, { addedToColumnIndex, removedFromColumnIndex, addedItemIndex, removedItemIndex })` */ onChange: PropTypes.func, /** * Column data configuration. Item content is provided as a simple node or a * render function with `content` property. When render function is used the * signature is: * `({ Item, draggable, disabled }) => {}`: * - `Item` - component used to provide default item visual representation. * You should render `<Item draggable={draggable} disabled={disabled}>...</Item>` * - `draggable` - item is draggable. * - `disabled` - item is disabled. */ columns: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.string.isRequired, subtitle: PropTypes.string.isRequired, actions: PropTypes.arrayOf(PropTypes.shape({ onClick: PropTypes.func, prefixIcon: PropTypes.node, label: PropTypes.string, })), items: PropTypes.arrayOf(PropTypes.shape({ content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]) .isRequired, draggable: PropTypes.bool, disabled: PropTypes.bool, })).isRequired, disabled: PropTypes.bool, droppable: PropTypes.bool, active: PropTypes.bool, })), /** * Event triggered on add button click: `onAdd(columnIndex)`. * When not provided the button will be hidden. */ onAdd: PropTypes.func, /** Title of add button. */ addItemButtonLabel: PropTypes.node, /** * Position where dragged items will be inserted. Using `any` value will * allow the items to be re-ordered within the same column. */ insertPosition: PropTypes.oneOf(['start', 'end', 'any']), /** Custom table height. */ height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), }; TimeTable.defaultProps = { columns: [], insertPosition: 'any', height: '283px', }; export default TimeTable; //# sourceMappingURL=TimeTable.js.map