wix-style-react
Version:
wix-style-react
201 lines • 10.5 kB
JavaScript
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