import React, { PureComponent } from 'react';
import uniqueId from 'lodash/uniqueId';
import { st, classes } from './DropdownBase.st.css.js';
import DropdownLayout from '../DropdownLayout';
import PopoverNext from '../PopoverNext';
import { WixDesignSystemContext } from '../WixDesignSystemProvider/context';
import Drawer from '../Drawer';
class DropdownBase extends PureComponent {
    constructor(props) {
        super(props);
        this.uniqueId = uniqueId('DropdownBase');
        this._dropdownLayoutRef = null;
        this._shouldCloseOnMouseLeave = false;
        this.state = {
            open: this.props.open,
            selectedId: this.props.selectedId ?? this.props.initialSelectedId ?? -1,
            activeDescendantId: undefined,
            listAutoFocus: false,
        };
        /**
         * Return `true` if the `open` prop is being controlled
         */
        this._isControllingOpen = (props = this.props) => {
            return typeof props.open !== 'undefined';
        };
        /**
         * Return `true` if the selection behaviour is being controlled
         */
        this._isControllingSelection = (props = this.props) => {
            return (typeof props.selectedId !== 'undefined' &&
                typeof props.onSelect !== 'undefined');
        };
        this._setIsOpen = (isOpen, _, newState) => {
            if (!this._isControllingOpen()) {
                this.setState({ ...newState, open: isOpen, listAutoFocus: false });
            }
        };
        this._handleClickOutside = () => {
            const { onClickOutside } = this.props;
            onClickOutside && onClickOutside();
        };
        this._handlePopoverMouseEnter = (renderedTrigger) => {
            const { onMouseEnter } = this.props;
            if (React.isValidElement(renderedTrigger) &&
                renderedTrigger.props.onMouseEnter) {
                renderedTrigger.props.onMouseEnter();
            }
            onMouseEnter && onMouseEnter();
        };
        this._handlePopoverMouseLeave = (renderedTrigger) => {
            const { onMouseLeave } = this.props;
            if (this._shouldCloseOnMouseLeave) {
                this._shouldCloseOnMouseLeave = false;
                this.setState({
                    open: false,
                    activeDescendantId: undefined,
                });
            }
            if (React.isValidElement(renderedTrigger) &&
                renderedTrigger.props.onMouseLeave) {
                renderedTrigger.props.onMouseLeave();
            }
            onMouseLeave && onMouseLeave();
        };
        this._handleSelect = (selectedOption) => {
            const newState = {};
            this._close();
            if (!this._isControllingSelection()) {
                newState.selectedId = selectedOption.id;
            }
            this.setState(newState, () => {
                const { onSelect } = this.props;
                onSelect && onSelect(selectedOption);
            });
        };
        this._handleOptionMarked = (_option, optionElementId) => {
            this.setState({ activeDescendantId: optionElementId });
        };
        this._open = () => {
            this._setIsOpen(true);
        };
        this._close = () => {
            this._setIsOpen(false);
        };
        this._handleClose = () => {
            if (this.state.open) {
                this._close();
            }
            if (this.triggerElementRef &&
                this.triggerElementRef.current &&
                this.triggerElementRef.current.focus) {
                this.triggerElementRef.current.focus();
            }
        };
        this._getSelectedOption = (selectedId) => {
            return this.props.options?.find(({ id }) => id === selectedId);
        };
        /**
         * Determine if a certain key should open the DropdownLayout
         */
        this._isOpenKey = (key) => {
            return ['Enter', ' ', 'ArrowDown'].includes(key);
        };
        this._isClosingKey = (key) => {
            if (this._isDialogMode()) {
                return ['Escape'].includes(key);
            }
            return ['Tab', 'Escape'].includes(key);
        };
        /**
         * A common `keydown` event that can be used for the target elements. It will automatically
         * delegate the event to the underlying <DropdownLayout/>, and will determine when to open the
         * dropdown depending on the pressed key.
         */
        this._handleKeyDown = (e) => {
            if (this._isControllingOpen()) {
                return;
            }
            const isHandledByDropdownLayout = this._delegateKeyDown(e);
            if (!isHandledByDropdownLayout) {
                if (this._isOpenKey(e.key) && !this.state.open) {
                    this.setState({ listAutoFocus: true });
                    this._open();
                    e.preventDefault();
                }
                else if (this._isClosingKey(e.key) && this.state.open) {
                    // Fallback: handle closing keys when DropdownLayout doesn't process them
                    // This happens in action list mode where DropdownLayout returns false immediately
                    // for container-level keydown events (listType !== ListType.select check)
                    this._close();
                }
            }
            // Prevent trigger element events when dropdown is open.
            if (this.state.open &&
                (e.key === 'Enter' || e.key === ' ') &&
                !this._isEventFromFixedRegion(e)) {
                e.preventDefault();
            }
        };
        /*
         * Delegate the event to the DropdownLayout. It'll handle the navigation, option selection and
         * closing of the dropdown.
         */
        this._delegateKeyDown = (e) => {
            if (!this._dropdownLayoutRef) {
                return false;
            }
            return this._dropdownLayoutRef._onSelectListKeyDown(e);
        };
        this._setDropdownLayoutRef = (ref) => {
            this._dropdownLayoutRef = ref;
            this.setState({
                activeDescendantId: this._dropdownLayoutRef?.getActiveDescendentElementId(),
            });
        };
        this._handleOutsideClick = (_, reason) => {
            if (reason === 'outside-press') {
                this._setIsOpen(false, reason);
                return;
            }
        };
        this._isDialogMode = () => {
            /** This component has two modes:
             *  1. Menu mode: when fixedHeader or fixedFooter is not present - https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
             *  2. Dialog mode: when fixedHeader or fixedFooter is present - https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
             */
            const { fixedHeader, fixedFooter } = this.props;
            return Boolean(fixedHeader || fixedFooter);
        };
        this._isEventFromFixedRegion = (event) => {
            const target = event.target;
            const region = target.closest('[data-dropdown-region]');
            if (!region) {
                return false;
            }
            const regionValue = region.getAttribute('data-dropdown-region');
            return regionValue === 'header' || regionValue === 'footer';
        };
        this.triggerElementRef = React.createRef();
    }
    UNSAFE_componentWillReceiveProps(nextProps) {
        // Keep internal state updated if needed
        if (this._isControllingOpen(nextProps) &&
            this.props.open !== nextProps.open) {
            this.setState({ open: nextProps.open });
        }
        if (this._isControllingSelection(nextProps) &&
            this.props.selectedId !== nextProps.selectedId) {
            this.setState({ selectedId: nextProps.selectedId });
        }
    }
    _renderChildren(renderedTrigger) {
        const { children } = this.props;
        if (!children) {
            return null;
        }
        return React.isValidElement(children)
            ? children
            : React.isValidElement(renderedTrigger)
                ? React.cloneElement(renderedTrigger, {
                    ...(renderedTrigger.props.onMouseEnter
                        ? { onMouseEnter: () => { } }
                        : {}),
                    ...(renderedTrigger.props.onMouseLeave
                        ? { onMouseLeave: () => { } }
                        : {}),
                })
                : renderedTrigger;
    }
    _renderDropdownLayout() {
        const { options, maxHeight, overflow, focusOnSelectedOption, infiniteScroll, loadMore, hasMore, focusOnOption, markedOption, scrollToOption, onMouseDown, listType, autoFocus, fixedHeader, fixedFooter, } = this.props;
        const { selectedId } = this.state;
        return (React.createElement(DropdownLayout, { dataHook: "dropdown-base-dropdownlayout", className: classes.list, ref: this._setDropdownLayoutRef, selectedId: selectedId, options: options, maxHeightPixels: maxHeight, onSelect: this._handleSelect, onOptionMarked: this._handleOptionMarked, onClose: this._handleClose, inContainer: true, visible: true, overflow: overflow, focusOnSelectedOption: focusOnSelectedOption, infiniteScroll: infiniteScroll, loadMore: loadMore, hasMore: hasMore, focusOnOption: focusOnOption, markedOption: markedOption, scrollToOption: scrollToOption, onMouseDown: onMouseDown, listType: listType, 
            // In dialog mode, FloatingFocusManager handles initial focus
            // In menu mode, DropdownLayout focuses the first option
            autoFocus: this._isDialogMode() ? false : autoFocus || this.state.listAutoFocus, fixedHeader: fixedHeader, fixedFooter: fixedFooter, listboxId: `${this.uniqueId}-listbox` }));
    }
    render() {
        const { dataHook, placement, appendTo, showArrow, zIndex, moveBy, minWidth, maxWidth, width, fixed, flip, dynamicWidth, fluid, animate, className, listType, popoverContentClassName, onMouseEnter, onMouseLeave, selectedId, children, } = this.props;
        const { open, activeDescendantId } = this.state;
        const { onShow, onHide, ...popoverProps } = this.props;
        const renderedTrigger = typeof children === 'function'
            ? children({
                toggle: Boolean(open) ? this._close : this._open,
                open: this._open,
                close: this._close,
                isOpen: Boolean(open),
                ref: this.triggerElementRef,
                delegateKeyDown: this._delegateKeyDown,
                selectedOption: this._getSelectedOption(selectedId),
                listboxId: `${this.uniqueId}-listbox`,
                activeDescendantId,
            })
            : children;
        return (React.createElement(WixDesignSystemContext.Consumer, null, ({ mobile }) => mobile ? (React.createElement("div", null,
            React.createElement("div", null, this._renderChildren(renderedTrigger)),
            React.createElement(Drawer, { dataHook: dataHook, open: Boolean(open), onClose: isOpen => this._setIsOpen(isOpen), zIndex: zIndex },
                React.createElement("div", { className: classes.drawerContent }, this._renderDropdownLayout())))) : (React.createElement(PopoverNext, { dataListType: listType, onOpenChange: this._handleOutsideClick, animate: animate, dataHook: dataHook, open: open, ...popoverProps, placement: placement, dynamicWidth: dynamicWidth, appendTo: appendTo, showArrow: showArrow, zIndex: zIndex, moveBy: moveBy, onKeyDown: this._handleKeyDown, onMouseEnter: onMouseEnter ||
                !!(React.isValidElement(renderedTrigger) &&
                    renderedTrigger.props.onMouseEnter)
                ? () => this._handlePopoverMouseEnter(renderedTrigger)
                : undefined, onMouseLeave: onMouseLeave ||
                !!(React.isValidElement(renderedTrigger) &&
                    renderedTrigger.props.onMouseLeave)
                ? () => this._handlePopoverMouseLeave(renderedTrigger)
                : undefined, onClickOutside: this._handleClickOutside, fixed: fixed, flip: flip, fluid: fluid, onHide: onHide, onShow: onShow, focusManagerEnabled: this._isDialogMode(), className: st(classes.root, {
                withWidth: Boolean(minWidth || maxWidth),
            }, className), minWidth: minWidth, maxWidth: maxWidth, width: width },
            React.createElement(PopoverNext.Trigger, { className: classes.trigger },
                React.createElement("div", null, this._renderChildren(renderedTrigger))),
            React.createElement(PopoverNext.Content, { className: st(classes.content, popoverContentClassName, {
                    withWidth: Boolean(minWidth || maxWidth),
                }) },
                React.createElement("div", { style: {
                        minWidth,
                        maxWidth,
                    } }, this._renderDropdownLayout()))))));
    }
}
DropdownBase.displayName = 'DropdownBase';
DropdownBase.defaultProps = {
    placement: 'bottom',
    appendTo: 'parent',
    showArrow: false,
    maxHeight: '260px',
    dynamicWidth: true,
    minWidth: 192,
    fluid: false,
    animate: false,
    listType: 'select',
    onShow: () => { },
    onHide: () => { },
    onMouseEnter: () => { },
    onMouseLeave: () => { },
};
export default DropdownBase;
//# sourceMappingURL=DropdownBase.js.map