import React from 'react';
import { classes as theme } from './NestableListTheme.st.css.js';
import { classes, st } from './NestableList.st.css.js';
import NestableListBase from '../NestableListBase';
import TableListItem from '../TableListItem';
import Box from '../Box';
import { Arrow } from './Arrow';
import TextButton from '../TextButton';
import { Add } from '@wix/wix-ui-icons-common';
import { isFistItem, isLastItem, isRootItem, moveItemOutsideOfTheParent, moveItemToTheChildOfPrevSibling, moveItemVertically, setCollapse, VerticalMovementDirection, } from '../NestableListBase/utils';
import { DEFAULT_INDENT_SIZE, indentSizes } from './constants';
import { getDepthThreshold, normalizeIndentSize } from './utils';
import { IconThemeContext } from '../WixDesignSystemIconThemeProvider/IconThemeContext';
/** A styled list with drag and drop and nesting capabilities. */
class NestableList extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = {
            movedItem: null,
            backup: null,
            isDragging: false,
            isInternalStateUpdate: false,
            items: this.props.items,
            normalizedIndentSize: DEFAULT_INDENT_SIZE,
        };
        // according to make possible use setState along with getDerivedStateFromProps
        // https://stackoverflow.com/questions/51019936/why-getderivedstatefromprops-is-called-after-setstate
        this._setStateDecorator = state => {
            this.setState({
                ...state,
                isInternalStateUpdate: true,
            });
        };
        this._renderArrow = ({ isLastChild = false, isPreview = false, isPlaceholder, } = {}) => {
            return (React.createElement(Box, { className: st(classes.arrowContainer, {
                    indent: this.state.normalizedIndentSize,
                }), direction: "vertical" },
                React.createElement("div", { className: st(classes.arrow, {
                        lastChild: isLastChild,
                        preview: isPreview,
                        placeholder: isPlaceholder,
                        offset: this.state.normalizedIndentSize,
                    }) },
                    React.createElement(Arrow, { className: classes.horizontalArrow }))));
        };
        this._renderActions = data => {
            const { isRootAction, veryLastItem, item } = data;
            const actions = isRootAction ? this.props.actions : item.actions;
            if (!actions) {
                return null;
            }
            const dataHook = isRootAction
                ? 'nestable-list-root-actions'
                : item.actionsDataHook;
            return (React.createElement("div", { "data-hook": dataHook, className: st(classes.item, {
                    rootAction: isRootAction,
                    last: veryLastItem,
                    dragging: this.state.isDragging,
                    withoutDividers: this.props.dividers === false,
                    withoutBottomBorder: !this.props.withBottomBorder && (veryLastItem || isRootAction),
                    indent: this.state.normalizedIndentSize,
                }, classes.actionItem) },
                React.createElement(TableListItem, { onClick: e => {
                        if (actions.length === 1) {
                            actions[0].onClick(e, item);
                        }
                    }, options: actions.map((action, index, arr) => {
                        return {
                            width: `fit-content(${100 / arr.length}%)`,
                            value: (React.createElement(TextButton, { dataHook: "nestable-list-action", ellipsis: true, size: "small", onClick: e => {
                                    // if one element event handler is fired on table list item
                                    if (arr.length !== 1) {
                                        action.onClick(e, item);
                                    }
                                }, prefixIcon: action.prefixIcon }, action.label)),
                        };
                    }) })));
        };
        this._renderAction = (data, AddIcon = Add) => {
            const label = data.isRootAction
                ? this.props.addItemLabel
                : data.item.addItemLabel;
            if (!label || this.props.maxDepth <= data.depth) {
                return;
            }
            return (React.createElement(Box, { direction: "vertical", className: st(classes.item, {
                    rootAction: data.isRootAction,
                    last: data.veryLastItem,
                    dragging: this.state.isDragging,
                    withoutDividers: this.props.dividers === false,
                    withoutBottomBorder: !this.props.withBottomBorder &&
                        ((data.veryLastItem && !data.addItemLabel) || data.isRootAction),
                    indent: this.state.normalizedIndentSize,
                }, classes.actionItem) },
                React.createElement(TableListItem, { onClick: () => this.props.onAddItem(data.item), options: [
                        {
                            value: (React.createElement(Box, null,
                                React.createElement(TextButton, { size: "small", prefixIcon: React.createElement(AddIcon, null) }, label))),
                        },
                    ] })));
        };
        this._renderPrefix = data => {
            if (data.isPreview ||
                isLastItem(data.siblings, data.item) ||
                isRootItem(data.depth)) {
                return null;
            }
            return (React.createElement("div", { className: st(classes.childArrow, {
                    offset: this.state.normalizedIndentSize,
                }) }));
        };
        this._renderItem = options => {
            const { isPlaceholder, depth, isPreview, isVeryLastItem, siblings, item } = options;
            const { id, children, isCollapsed, draggable = true, dragDisabled, hideDragHandle, ...rest } = item;
            const isLastChild = isLastItem(siblings, item);
            const focused = this.state.movedItem && id === this.state.movedItem.id;
            const hasExpandableSiblings = siblings?.some(sibling => sibling.expandable);
            const hasDraggableSiblings = siblings?.some(sibling => !sibling.hideDragHandle) &&
                !this.props.readOnly;
            const isStatic = (draggable === false && hideDragHandle === true) || this.props.readOnly;
            const isDraggableWithDisabledHandle = !isStatic && (draggable === false || dragDisabled === true);
            const isDraggableWithoutHandle = !isStatic && hideDragHandle === true;
            const onBlur = () => {
                this._onBlur();
                rest.onBlur?.();
            };
            return (React.createElement("div", { className: st(classes.itemWrapper, {
                    dragEnabled: draggable && !this.props.readOnly,
                }), "data-hook": item.dataHook
                    ? `${item.dataHook}-item`
                    : `styled-nestable-list-item-${item.id}` },
                this.props.hierarchyIndicator &&
                    !isRootItem(depth) &&
                    this._renderArrow({
                        isLastChild,
                        isPreview,
                        isPlaceholder,
                    }),
                React.createElement("div", { className: st(classes.item, {
                        root: isRootItem(depth),
                        firstSibling: isFistItem(siblings, item),
                        last: isVeryLastItem,
                        placeholder: isPlaceholder,
                        preview: isPreview,
                        focused,
                        dragging: this.state.isDragging || focused,
                        withoutDividers: this.props.dividers === false && !focused && !isPreview,
                        withoutBottomBorder: isVeryLastItem &&
                            !this.props.withBottomBorder &&
                            !this.props.addItemLabel,
                    }) },
                    React.createElement(TableListItem, { ...rest, className: st(rest.className, {}, classes.tableListItem), reserveExpandHandleSpace: hasExpandableSiblings, reserveDragHandleSpace: hasDraggableSiblings, focused: focused, onBlur: onBlur, onKeyUp: e => this._moveItemViaKeyboard(e, options), showDivider: false, dragging: isPreview || focused, draggable: !isStatic, dragDisabled: isDraggableWithDisabledHandle, hideDragHandle: isDraggableWithoutHandle, options: rest.options ? rest.options : [], dragHandleSize: this.props.dragHandleSize, showDragHandleOnHover: this.props.showDragHandleOnHover }))));
        };
        this._onBlur = () => {
            this._releaseItems();
        };
        this._moveItemViaKeyboard = (e, itemOptions) => {
            const left = 'ArrowLeft';
            const top = 'ArrowUp';
            const right = 'ArrowRight';
            const bottom = 'ArrowDown';
            const enter = 'Enter';
            const esc = 'Escape';
            // start dragend-drop
            if (e.key === enter && !this.state.movedItem) {
                this._setStateDecorator({
                    backup: this.state.items,
                    movedItem: itemOptions.item,
                    items: setCollapse(this.state.items, itemOptions.item.id, true),
                });
            }
            if (!this.state.movedItem) {
                return;
            }
            switch (e.key) {
                case esc:
                    this._setStateDecorator({
                        items: this.state.backup,
                        backup: null,
                        movedItem: null,
                    });
                    break;
                case left:
                    if (!this.props.preventChangeDepth) {
                        this._setStateDecorator({
                            items: moveItemOutsideOfTheParent(this.state.items, itemOptions.item),
                        });
                    }
                    break;
                case right:
                    if (!this.props.preventChangeDepth) {
                        this._setStateDecorator({
                            items: moveItemToTheChildOfPrevSibling(this.state.items, itemOptions.item),
                        });
                    }
                    break;
                case top:
                    this._setStateDecorator({
                        items: moveItemVertically({
                            items: this.state.items,
                            item: itemOptions.item,
                            step: VerticalMovementDirection.top,
                            preventChangeParent: this.props.preventChangeParent,
                            enforcePinnedOrder: this.props.enforcePinnedOrder,
                        }),
                    });
                    break;
                case bottom:
                    this._setStateDecorator({
                        items: moveItemVertically({
                            items: this.state.items,
                            item: itemOptions.item,
                            step: VerticalMovementDirection.bottom,
                            preventChangeParent: this.props.preventChangeParent,
                            enforcePinnedOrder: this.props.enforcePinnedOrder,
                        }),
                    });
                    break;
                default:
                case enter:
                    this._releaseItems();
                    break;
            }
        };
        this._onDragEnd = () => {
            this.props.onDragEnd?.();
            this._setStateDecorator({
                isDragging: false,
            });
        };
        this._onDragStart = () => {
            this.props.onDragStart?.();
            this._setStateDecorator({
                isDragging: true,
            });
        };
        this._renderActionsFactory = (...args) => {
            const data = args[0];
            const actions = data.isRootAction ? this.props.actions : data.item.actions;
            if (actions) {
                return this._renderActions(...args);
            }
            return this._renderAction(...args);
        };
    }
    static getDerivedStateFromProps(props, state) {
        if (state.isInternalStateUpdate) {
            return {
                ...state,
                isInternalStateUpdate: false,
            };
        }
        return {
            items: props.items,
            normalizedIndentSize: normalizeIndentSize(props.indentSize),
        };
    }
    _releaseItems() {
        if (this.state.movedItem) {
            const releaseItems = setCollapse(this.state.items, this.state.movedItem.id, this.state.movedItem.isCollapsed);
            this.props.onChange({
                items: releaseItems,
                item: this.state.movedItem,
            });
            this._setStateDecorator({
                movedItem: null,
                backup: null,
                items: releaseItems,
            });
        }
    }
    render() {
        const { dataHook, className, maxDepth, onChange, preventChangeDepth, preventChangeParent, enforcePinnedOrder, readOnly, zIndex, hierarchyIndicator, } = this.props;
        const depthThreshold = getDepthThreshold(this.state.normalizedIndentSize);
        return (React.createElement(IconThemeContext.Consumer, null, ({ icons = {} }) => {
            const { NestableList: nestableListIcons = {} } = icons;
            const { Add: AddIcon = Add } = nestableListIcons;
            return (React.createElement("div", { "data-hook": dataHook, className: st(classes.root, { dragEnabled: !readOnly }, className) },
                React.createElement(NestableListBase, { items: this.state.items, renderItem: this._renderItem, renderPrefix: hierarchyIndicator ? this._renderPrefix : undefined, renderAction: data => this._renderActionsFactory(data, AddIcon), onDragEnd: this._onDragEnd, onDragStart: this._onDragStart, onUpdate: ({ items, item }) => {
                        this._setStateDecorator({
                            items,
                        });
                        onChange({ items, item: item.data });
                    }, maxDepth: maxDepth, threshold: depthThreshold, theme: theme, readOnly: readOnly, preventChangeDepth: preventChangeDepth, preventChangeParent: preventChangeParent, enforcePinnedOrder: enforcePinnedOrder, childrenStyle: {
                        position: 'relative',
                        marginLeft: `${depthThreshold}px`,
                    }, childrenProperty: "children", isRenderDraggingChildren: false, zIndex: zIndex }),
                this._renderActionsFactory({
                    depth: 0,
                    isRootAction: true,
                }, AddIcon)));
        }));
    }
}
NestableList.displayName = 'NestableList';
NestableList.defaultProps = {
    maxDepth: 10,
    withBottomBorder: false,
    preventChangeDepth: false,
    preventChangeParent: false,
    dragHandleSize: 'large',
    showDragHandleOnHover: false,
    enforcePinnedOrder: false,
    hierarchyIndicator: true,
    items: [],
    onAddItem: () => { },
};
export default NestableList;
//# sourceMappingURL=NestableList.js.map