import React, { Component, } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { SoftContextMenuPanel } from './SoftContextMenuPanel';
import { SoftContextMenuItem } from './SoftContextMenuItem';
function mapIdsToPanels(panels) {
    const map = {};
    panels.forEach(panel => {
        map[panel.id] = panel;
    });
    return map;
}
function mapIdsToPreviousPanels(panels) {
    const idToPreviousPanelIdMap = {};
    panels.forEach(panel => {
        if (Array.isArray(panel.items)) {
            panel.items.forEach(item => {
                const isCloseable = item.panel !== undefined;
                if (isCloseable) {
                    idToPreviousPanelIdMap[item.panel] = panel.id;
                }
            });
        }
    });
    return idToPreviousPanelIdMap;
}
function mapPanelItemsToPanels(panels) {
    const idAndItemIndexToPanelIdMap = {};
    panels.forEach(panel => {
        idAndItemIndexToPanelIdMap[panel.id] = {};
        if (panel.items) {
            panel.items.forEach((item, index) => {
                if (item.panel) {
                    idAndItemIndexToPanelIdMap[panel.id][index] = item.panel;
                }
            });
        }
    });
    return idAndItemIndexToPanelIdMap;
}
export const SoftContextMenuPanelItemShape = PropTypes.shape({
    name: PropTypes.string,
    icon: PropTypes.node,
    onClick: PropTypes.func,
    // If given, shows the panel with this id when clicked:
    panel: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    disabled: PropTypes.bool,
});
export const SoftContextMenuPanelShape = PropTypes.shape({
    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    width: PropTypes.number,
    content: PropTypes.node,
    items: PropTypes.arrayOf(SoftContextMenuPanelItemShape),
    title: PropTypes.string,
});
export class SoftContextMenu extends Component {
    constructor(props) {
        super(props);
        this.state = {
            prevProps: {},
            idToPanelMap: {},
            idToPreviousPanelIdMap: {},
            idAndItemIndexToPanelIdMap: {},
            idToRenderedItemsMap: null,
            height: undefined,
            outgoingPanelId: undefined,
            incomingPanelId: this.props.initialPanelId,
            transitionDirection: undefined,
            isOutgoingPanelVisible: false,
            focusedItemIndex: undefined,
            isUsingKeyboardToNavigate: false,
        };
        this.hasPreviousPanel = panelId => {
            const previousPanelId = this.state.idToPreviousPanelIdMap[panelId];
            return typeof previousPanelId !== 'undefined';
        };
        this.showNextPanel = itemIndex => {
            const nextPanelId = this.state.idAndItemIndexToPanelIdMap[this.state.incomingPanelId][itemIndex];
            if (nextPanelId) {
                if (this.state.isUsingKeyboardToNavigate) {
                    this.setState({
                        focusedItemIndex: 0,
                    });
                }
                this.showPanel(nextPanelId, 'next');
            }
        };
        this.showPreviousPanel = () => {
            // If there's a previous panel, then we can close the current panel to go back to it.
            if (this.hasPreviousPanel(this.state.incomingPanelId)) {
                const previousPanelId = this.state.idToPreviousPanelIdMap[this.state.incomingPanelId];
                // Set focus on the item which shows the panel we're leaving.
                const previousPanel = this.state.idToPanelMap[previousPanelId];
                const focusedItemIndex = previousPanel.items.findIndex(item => item.panel === this.state.incomingPanelId);
                if (focusedItemIndex !== -1) {
                    this.setState({
                        focusedItemIndex,
                    });
                }
                this.showPanel(previousPanelId, 'previous');
            }
        };
        this.onIncomingPanelHeightChange = height => {
            // @ts-ignore
            this.setState(({ height: prevHeight }) => {
                if (height === prevHeight) {
                    return null;
                }
                else {
                    return { height };
                }
            });
        };
        this.onOutGoingPanelTransitionComplete = () => {
            this.setState({
                isOutgoingPanelVisible: false,
            });
        };
        this.onUseKeyboardToNavigate = () => {
            if (!this.state.isUsingKeyboardToNavigate) {
                this.setState({
                    isUsingKeyboardToNavigate: true,
                });
            }
        };
        this.mapIdsToRenderedItems = (panels) => {
            const idToRenderedItemsMap = {};
            // Pre-rendering the items lets us check reference equality inside of SoftContextMenuPanel.
            panels.forEach(panel => {
                idToRenderedItemsMap[panel.id] = this.renderItems(panel.items);
            });
            return idToRenderedItemsMap;
        };
        this.state = {
            ...this.state,
            idToRenderedItemsMap: this.mapIdsToRenderedItems(this.props.panels),
        };
    }
    static getDerivedStateFromProps(nextProps, prevState) {
        const { panels } = nextProps;
        if (prevState.prevProps.panels !== panels) {
            return {
                prevProps: { panels },
                idToPanelMap: mapIdsToPanels(panels),
                idToPreviousPanelIdMap: mapIdsToPreviousPanels(panels),
                idAndItemIndexToPanelIdMap: mapPanelItemsToPanels(panels)
            };
        }
        return null;
    }
    componentDidUpdate(prevProps) {
        if (prevProps.panels !== this.props.panels) {
            this.setState({
                idToRenderedItemsMap: this.mapIdsToRenderedItems(this.props.panels),
            });
        }
    }
    showPanel(panelId, direction) {
        this.setState({
            outgoingPanelId: this.state.incomingPanelId,
            incomingPanelId: panelId,
            transitionDirection: direction,
            isOutgoingPanelVisible: true,
        });
    }
    renderItems(items = []) {
        return items.map((item, index) => {
            const { panel, name, icon, onClick, toolTipTitle, toolTipContent, ...rest } = item;
            const onClickHandler = panel
                ? (event) => {
                    if (onClick && event) {
                        event.persist();
                    }
                    // This component is commonly wrapped in a SoftOutsideClickDetector, which means we'll
                    // need to wait for that logic to complete before re-rendering the DOM via showPanel.
                    window.requestAnimationFrame(() => {
                        if (onClick)
                            onClick(event);
                        this.showNextPanel(index);
                    });
                } : onClick;
            return (React.createElement(SoftContextMenuItem, Object.assign({ key: name, icon: icon, onClick: onClickHandler, hasPanel: Boolean(panel), toolTipTitle: toolTipTitle, toolTipContent: toolTipContent }, rest), name));
        });
    }
    renderPanel(panelId, transitionType) {
        const panel = this.state.idToPanelMap[panelId];
        if (!panel) {
            return;
        }
        // As above, we need to wait for SoftOutsideClickDetector to complete its logic before
        // re-rendering via showPanel.
        let onClose;
        if (this.hasPreviousPanel(panelId)) {
            onClose = () => window.requestAnimationFrame(this.showPreviousPanel);
        }
        return (React.createElement(SoftContextMenuPanel, { key: panelId, className: "softContextMenu__panel", onHeightChange: (transitionType === 'in') ? this.onIncomingPanelHeightChange : undefined, onTransitionComplete: (transitionType === 'out') ? this.onOutGoingPanelTransitionComplete : undefined, title: panel.title, onClose: onClose, transitionType: this.state.isOutgoingPanelVisible ? transitionType : undefined, transitionDirection: this.state.isOutgoingPanelVisible ? this.state.transitionDirection : undefined, hasFocus: transitionType === 'in', items: this.state.idToRenderedItemsMap[panelId], initialFocusedItemIndex: this.state.isUsingKeyboardToNavigate ? this.state.focusedItemIndex : undefined, onUseKeyboardToNavigate: this.onUseKeyboardToNavigate, showNextPanel: this.showNextPanel, showPreviousPanel: this.showPreviousPanel }, panel.content));
    }
    render() {
        const { panels, // eslint-disable-line no-unused-vars
        className, initialPanelId, // eslint-disable-line no-unused-vars
        ...rest } = this.props;
        const incomingPanel = this.renderPanel(this.state.incomingPanelId, 'in');
        let outgoingPanel;
        if (this.state.isOutgoingPanelVisible) {
            outgoingPanel = this.renderPanel(this.state.outgoingPanelId, 'out');
        }
        const width = this.state.idToPanelMap[this.state.incomingPanelId] &&
            this.state.idToPanelMap[this.state.incomingPanelId].width ?
            this.state.idToPanelMap[this.state.incomingPanelId].width : undefined;
        const classes = classNames('softContextMenu', className);
        return (React.createElement("div", Object.assign({ className: classes, style: { height: this.state.height, width: width } }, rest),
            outgoingPanel,
            incomingPanel));
    }
}
SoftContextMenu.propTypes = {
    className: PropTypes.string,
    panels: PropTypes.arrayOf(SoftContextMenuPanelShape),
    initialPanelId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
SoftContextMenu.defaultProps = {
    panels: [],
};
//# sourceMappingURL=SoftContextMenu.js.map