"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Toolbar
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.GroupButtonItem = exports.ToolbarGroupItemComponent = void 0;
const classnames_1 = require("classnames");
const React = require("react");
const core_bentley_1 = require("@itwin/core-bentley");
const appui_abstract_1 = require("@itwin/appui-abstract");
const core_react_1 = require("@itwin/core-react");
const appui_layout_react_1 = require("@itwin/appui-layout-react");
const FrontstageComposer_1 = require("../frontstage/FrontstageComposer");
const UiFramework_1 = require("../UiFramework");
const PropsHelper_1 = require("../utils/PropsHelper");
const DragInteraction_1 = require("./DragInteraction");
const ToolbarHelper_1 = require("./ToolbarHelper");
const useEscapeSetFocusToHome_1 = require("../hooks/useEscapeSetFocusToHome");
const InternalFrontstageManager_1 = require("../frontstage/InternalFrontstageManager");
// eslint-disable-next-line @typescript-eslint/naming-convention, deprecation/deprecation
const ToolGroup = (0, core_react_1.withOnOutsideClick)(appui_layout_react_1.Group, undefined, false);
// eslint-disable-next-line @typescript-eslint/naming-convention, deprecation/deprecation
const NestedToolGroup = (0, core_react_1.withOnOutsideClick)(appui_layout_react_1.NestedGroup, undefined, false);
// eslint-disable-next-line @typescript-eslint/naming-convention, deprecation/deprecation
const ItemWithDragInteraction = (0, appui_layout_react_1.withDragInteraction)(appui_layout_react_1.Item);
/** Group Item React component.
 * @internal
 */
class ToolbarGroupItemComponent extends React.Component {
    constructor(props) {
        super(props);
        this._trayIndex = 0;
        this._closeOnPanelOpened = true;
        this._ref = React.createRef();
        this._handleOpenPanel = () => {
            this.setState({
                isPressed: true,
            });
            this._closeOnPanelOpened = false;
            InternalFrontstageManager_1.InternalFrontstageManager.onToolPanelOpenedEvent.emit();
            this._closeOnPanelOpened = true;
        };
        this._handleClick = () => {
            this.setState((prevState) => ({
                isPressed: !prevState.isPressed,
            }));
        };
        this._handleDragInteractionClick = () => {
            const activeItem = this.getItemById(this.state.activeItemId);
            activeItem && appui_abstract_1.ToolbarItemUtilities.isActionButton(activeItem) && activeItem.execute();
        };
        this._handleDragInteractionOutsideClick = (_e) => {
            this.closeGroupButton();
        };
        this._handleOutsideClick = (e) => {
            this._ref.current && (e.target instanceof Node) && !this._ref.current.contains(e.target) && this.closeGroupButton();
        };
        // istanbul ignore next
        this._handleToolActivatedEvent = ({ toolId }) => {
            this.setState({
                activeToolId: toolId,
            });
        };
        // istanbul ignore next
        this._handleToolPanelOpenedEvent = () => {
            if (!this._closeOnPanelOpened)
                return;
            this.closeGroupButton();
        };
        this._handleBack = () => {
            this.setState((prevState) => {
                const trayId = prevState.backTrays.length > 0 ? prevState.backTrays[prevState.backTrays.length - 1] : /* istanbul ignore next */ prevState.trayId;
                const backTrays = prevState.backTrays.slice(0, -1);
                return {
                    trayId,
                    backTrays,
                };
            });
        };
        this._handleExpanderClick = (trayId) => {
            this.setState((prevState) => {
                return {
                    trayId,
                    backTrays: [...prevState.backTrays, prevState.trayId],
                };
            });
        };
        const state = this.getGroupItemState(this.props);
        // TODO: The defaultActiveItemId should come from session storage
        const activeItemId = getFirstItemId(this.props.groupItem);
        this.state = {
            ...state,
            activeItemId,
        };
    }
    componentDidMount() {
        UiFramework_1.UiFramework.frontstages.onToolActivatedEvent.addListener(this._handleToolActivatedEvent);
        InternalFrontstageManager_1.InternalFrontstageManager.onToolPanelOpenedEvent.addListener(this._handleToolPanelOpenedEvent);
    }
    componentWillUnmount() {
        UiFramework_1.UiFramework.frontstages.onToolActivatedEvent.removeListener(this._handleToolActivatedEvent);
        InternalFrontstageManager_1.InternalFrontstageManager.onToolPanelOpenedEvent.removeListener(this._handleToolPanelOpenedEvent);
    }
    getGroupItemState(props) {
        const groupItem = props.groupItem;
        // Separate into trays
        const trays = new Map();
        const trayId = this.resetTrayId();
        this.processGroupItem(groupItem, trayId, trays);
        const isHidden = appui_abstract_1.ConditionalBooleanValue.getValue(groupItem.isHidden);
        const isDisabled = appui_abstract_1.ConditionalBooleanValue.getValue(groupItem.isDisabled);
        return {
            activeToolId: UiFramework_1.UiFramework.frontstages.activeToolId,
            groupItem,
            isEnabled: !isDisabled,
            isPressed: false,
            isVisible: !isHidden,
            trayId,
            backTrays: [],
            trays,
        };
    }
    resetTrayId() {
        this._trayIndex = 1;
        return `tray-${this._trayIndex}`;
    }
    generateTrayId() {
        return `tray-${this._trayIndex}`;
    }
    processGroupItem(groupItem, trayId, trays) {
        // Separate into column items
        const items = new Map();
        groupItem.items.forEach((item) => {
            if (appui_abstract_1.ToolbarItemUtilities.isGroupButton(item)) {
                this._trayIndex++;
                const itemTrayId = this.generateTrayId();
                const toolGroupItem = {
                    trayId: itemTrayId,
                    item,
                };
                items.set(item.id, toolGroupItem);
                this.processGroupItem(item, itemTrayId, trays);
            }
            else {
                const toolItem = { item };
                items.set(item.id, toolItem);
            }
        });
        trays.set(trayId, {
            items,
            title: appui_abstract_1.ConditionalStringValue.getValue(groupItem.panelLabel),
            groupItem,
        });
    }
    componentDidUpdate(prevProps, _prevState) {
        if (!PropsHelper_1.PropsHelper.isShallowEqual(this.props, prevProps)) {
            // istanbul ignore else
            if (this.props.groupItem !== prevProps.groupItem)
                core_bentley_1.Logger.logTrace(UiFramework_1.UiFramework.loggerCategory(this), `Different ToolbarGroupItem for same groupId of ${this.state.groupItem.id}`);
            this.setState((_, props) => this.getGroupItemState(props));
        }
    }
    get _tray() {
        const tray = this.state.trays.get(this.state.trayId);
        // istanbul ignore next
        if (!tray)
            throw new RangeError();
        return tray;
    }
    getTray(trayId) {
        const tray = this.state.trays.get(trayId);
        // istanbul ignore next
        if (!tray)
            throw new RangeError();
        return tray;
    }
    render() {
        if (!this.state.isVisible)
            return null;
        const activeItem = this.getItemById(this.state.activeItemId);
        // istanbul ignore next
        if (!activeItem)
            return null;
        const { groupItem, className, ...props } = this.props;
        const classNames = (0, classnames_1.default)(className);
        const badge = core_react_1.BadgeUtilities.getComponentForBadgeType(groupItem.badgeType); // eslint-disable-line deprecation/deprecation
        return (React.createElement(appui_layout_react_1.ToolbarDirectionContext.Consumer, null, (direction) => (React.createElement(DragInteraction_1.ToolbarDragInteractionContext.Consumer, null, (dragInteraction) => (React.createElement(appui_layout_react_1.ExpandableItem // eslint-disable-line deprecation/deprecation
        , { ...props, className: classNames, key: this.state.groupItem.id, panel: this.getGroupTray(dragInteraction) }, dragInteraction ?
            (React.createElement("div", { ref: this._ref },
                React.createElement(ItemWithDragInteraction, { badge: badge, direction: direction, icon: ToolbarHelper_1.ToolbarHelper.getIconReactNode(activeItem), isActive: this.state.activeToolId === this.state.activeItemId, isDisabled: !this.state.isEnabled, onClick: this._handleDragInteractionClick, onKeyDown: useEscapeSetFocusToHome_1.onEscapeSetFocusToHome, onOpenPanel: this._handleOpenPanel, title: appui_abstract_1.ConditionalStringValue.getValue(activeItem.label) }))) :
            (React.createElement("div", { ref: this._ref },
                React.createElement(appui_layout_react_1.Item // eslint-disable-line deprecation/deprecation
                , { badge: badge, icon: ToolbarHelper_1.ToolbarHelper.getIconReactNode(groupItem), isDisabled: !this.state.isEnabled, onClick: this._handleClick, onKeyDown: useEscapeSetFocusToHome_1.onEscapeSetFocusToHome, title: appui_abstract_1.ConditionalStringValue.getValue(this.state.groupItem.label) })))))))));
    }
    closeGroupButton() {
        const trayId = this.resetTrayId();
        this.setState({
            isPressed: false,
            trayId,
            backTrays: [],
        });
    }
    handleToolGroupItemClicked(trayKey, itemKey) {
        const trayId = this.resetTrayId();
        this.setState({
            activeItemId: itemKey,
            isPressed: false,
            trayId,
            backTrays: [],
        }, () => {
            const tray = this.getTray(trayKey);
            const childItem = tray.groupItem.items.find((item) => itemKey === item.id);
            // istanbul ignore else
            if (childItem) {
                // istanbul ignore else
                if (appui_abstract_1.ToolbarItemUtilities.isActionButton(childItem)) {
                    childItem.execute();
                    if (this.props.onItemExecuted)
                        this.props.onItemExecuted(childItem);
                }
            }
        });
    }
    getItemsInColumn(numTotalItems) {
        if (numTotalItems <= 6)
            return numTotalItems;
        if (numTotalItems <= 24)
            return Math.ceil(numTotalItems / 2);
        if (numTotalItems <= 36)
            return Math.ceil(numTotalItems / 3);
        return Math.ceil(numTotalItems / 4);
    }
    getGroupTray(dragInteraction) {
        if (!this.state.isPressed)
            return undefined;
        return (React.createElement(FrontstageComposer_1.ToolGroupPanelContext.Consumer, null, (activateOnPointerUp) => {
            const tray = this._tray;
            const items = [...tray.items.keys()];
            const itemsInColumn = this.getItemsInColumn(items.length); // tray.groupItem.itemsInColumn;
            activateOnPointerUp = activateOnPointerUp && dragInteraction;
            // Divide items equally between columns.
            const numberOfColumns = Math.ceil(items.length / itemsInColumn);
            const numberItemsInColumn = Math.ceil(items.length / numberOfColumns);
            const columnToItems = items.reduce((acc, item, index) => {
                const columnIndex = Math.floor(index / numberItemsInColumn);
                if (columnIndex >= acc.length) {
                    return [
                        ...acc,
                        [item],
                    ];
                }
                return [
                    ...acc.slice(0, columnIndex),
                    [
                        ...acc[columnIndex],
                        item,
                    ],
                    ...acc.slice(columnIndex + 1),
                ];
            }, []);
            const columns = (columnToItems.map((columnItems, columnIndex) => React.createElement(appui_layout_react_1.GroupColumn, { key: columnIndex },
                " ",
                columnItems.map((itemKey) => {
                    const tgItem = tray.items.get(itemKey);
                    const item = tgItem.item;
                    const icon = ToolbarHelper_1.ToolbarHelper.getIconReactNode(item);
                    const isVisible = true;
                    const isActive = itemKey === this.state.activeToolId;
                    const badge = core_react_1.BadgeUtilities.getComponentForBadgeType(item.badgeType);
                    if (tgItem.trayId) {
                        return (isVisible &&
                            React.createElement(appui_layout_react_1.GroupToolExpander, { isDisabled: !!item.isDisabled, key: itemKey, label: appui_abstract_1.ConditionalStringValue.getValue(item.label), icon: icon, badge: badge, onClick: () => this._handleExpanderClick(tgItem.trayId), onPointerUp: activateOnPointerUp ? () => this._handleExpanderClick(tgItem.trayId) : undefined }));
                    }
                    return (isVisible &&
                        React.createElement(appui_layout_react_1.GroupTool, { isDisabled: !!item.isDisabled, isActive: isActive, key: itemKey, label: appui_abstract_1.ConditionalStringValue.getValue(item.label), onClick: 
                            // istanbul ignore next
                            () => this.handleToolGroupItemClicked(this.state.trayId, itemKey), onPointerUp: activateOnPointerUp ? () => this.handleToolGroupItemClicked(this.state.trayId, itemKey) : undefined, icon: icon, badge: badge }));
                }))));
            if (this.state.backTrays.length > 0)
                return (React.createElement(NestedToolGroup, { columns: columns, onBack: this._handleBack, onBackPointerUp: activateOnPointerUp ? this._handleBack : undefined, onOutsideClick: dragInteraction ? this._handleDragInteractionOutsideClick : /* istanbul ignore next */ this._handleOutsideClick, title: tray.title }));
            return (React.createElement(ToolGroup, { columns: columns, onOutsideClick: dragInteraction ? this._handleDragInteractionOutsideClick : this._handleOutsideClick, title: tray.title }));
        }));
    }
    getItemById(id) {
        for (const [, tray] of this.state.trays) {
            const item = tray.groupItem.items.find((childItem) => id === childItem.id);
            if (item)
                return item;
        }
        // istanbul ignore next
        return undefined;
    }
}
exports.ToolbarGroupItemComponent = ToolbarGroupItemComponent;
/** @internal */
const getFirstItem = (groupItem) => {
    for (const item of groupItem.items) {
        const isHidden = appui_abstract_1.ConditionalBooleanValue.getValue(item.isHidden);
        const isDisabled = appui_abstract_1.ConditionalBooleanValue.getValue(item.isDisabled);
        if (appui_abstract_1.ToolbarItemUtilities.isGroupButton(item)) {
            const firstItem = getFirstItem(item);
            // istanbul ignore else
            if (firstItem)
                return firstItem;
            // istanbul ignore next
            continue;
        }
        if (!isHidden && !isDisabled)
            return item;
    }
    return undefined;
};
/** @internal */
const getFirstItemId = (groupItem) => {
    const item = getFirstItem(groupItem);
    return item ? item.id : "";
};
/** Group Button React component
 * @internal
 */
function GroupButtonItem(props) {
    return (React.createElement(ToolbarGroupItemComponent, { groupItem: props.item, key: props.item.id, onItemExecuted: props.onItemExecuted }));
}
exports.GroupButtonItem = GroupButtonItem;
//# sourceMappingURL=GroupButtonItem.js.map