/**
 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/dropdown/menu/dropdownmenunestedmenuview
 */
import { FocusTracker, KeystrokeHandler, global } from '@ckeditor/ckeditor5-utils';
import DropdownMenuButtonView from './dropdownmenubuttonview.js';
import DropdownMenuListView from './dropdownmenulistview.js';
import { DropdownMenuPanelPositioningFunctions } from './utils.js';
import { DropdownMenuBehaviors } from './dropdownmenubehaviors.js';
import View from '../../view.js';
import DropdownMenuNestedMenuPanelView from './dropdownmenunestedmenupanelview.js';
import '../../../theme/components/dropdown/menu/dropdownmenu.css';
/**
 * Represents a nested menu view.
 */
class DropdownMenuNestedMenuView extends View {
    /**
     * Creates a new instance of the DropdownMenuView class.
     *
     * @param locale
     * @param bodyCollection
     * @param id
     * @param label The label for the dropdown menu button.
     * @param parentMenuView The parent dropdown menu view, if any.
     */
    constructor(locale, bodyCollection, id, label, parentMenuView) {
        super(locale);
        this._bodyCollection = bodyCollection;
        this.id = id;
        this.set({
            isOpen: false,
            isEnabled: true,
            panelPosition: 'w',
            class: undefined,
            parentMenuView: null
        });
        this.keystrokes = new KeystrokeHandler();
        this.focusTracker = new FocusTracker();
        this.buttonView = new DropdownMenuButtonView(locale);
        this.buttonView.delegate('mouseenter').to(this);
        this.buttonView.bind('isOn', 'isEnabled').to(this, 'isOpen', 'isEnabled');
        this.buttonView.label = label;
        this.panelView = new DropdownMenuNestedMenuPanelView(locale);
        this.panelView.isVisible = true;
        this.listView = new DropdownMenuListView(locale);
        this.listView.bind('ariaLabel').to(this.buttonView, 'label');
        this.panelView.content.add(this.listView);
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-dropdown-menu-list__nested-menu',
                    bind.to('class'),
                    bind.if('isEnabled', 'ck-disabled', value => !value)
                ],
                role: 'presentation'
            },
            children: [
                this.buttonView
            ]
        });
        this.parentMenuView = parentMenuView;
        if (this.parentMenuView) {
            this._handleParentMenuView();
        }
        this._attachBehaviors();
    }
    /**
     * An array of positioning functions used to determine the position of the dropdown menu panel.
     * The order of the functions in the array determines the priority of the positions to be tried.
     * The first function that returns a valid position will be used.
     *
     * @returns {Array<PositioningFunction>} An array of positioning functions.
     * @internal
     */
    get _panelPositions() {
        const { westSouth, eastSouth, westNorth, eastNorth } = DropdownMenuPanelPositioningFunctions;
        if (this.locale.uiLanguageDirection === 'ltr') {
            return [eastSouth, eastNorth, westSouth, westNorth];
        }
        else {
            return [westSouth, westNorth, eastSouth, eastNorth];
        }
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.panelView.render();
        this.focusTracker.add(this.buttonView.element);
        this.focusTracker.add(this.panelView.element);
        // Listen for keystrokes coming from within #element.
        this.keystrokes.listenTo(this.element);
        this._mountPanelOnOpen();
    }
    /**
     * @inheritDoc
     */
    destroy() {
        this._removePanelFromBody();
        this.panelView.destroy();
        super.destroy();
    }
    /**
     * @inheritDoc
     */
    focus() {
        this.buttonView.focus();
    }
    _handleParentMenuView() {
        // Propagate events from this component to parent-menu.
        this.delegate(...DropdownMenuNestedMenuView.DELEGATED_EVENTS).to(this.parentMenuView);
        // Close this menu if its parent closes.
        DropdownMenuBehaviors.closeOnParentClose(this, this.parentMenuView);
    }
    /**
     * Attach all keyboard behaviors for the menu view.
     */
    _attachBehaviors() {
        DropdownMenuBehaviors.openOnButtonClick(this);
        DropdownMenuBehaviors.openAndFocusOnEnterKeyPress(this);
        DropdownMenuBehaviors.openOnArrowRightKey(this);
        DropdownMenuBehaviors.closeOnEscKey(this);
        DropdownMenuBehaviors.closeOnArrowLeftKey(this);
    }
    /**
     * Mounts the portal view in the body when the menu is open and removes it when the menu is closed.
     * Binds keystrokes to the portal view when the menu is open.
     */
    _mountPanelOnOpen() {
        const { panelView } = this;
        this.on('change:isOpen', (evt, name, isOpen) => {
            // Ensure that the event was triggered by this instance.
            // TODO: Remove checking `evt.source` if `change:isOpen` is no longer delegated.
            if (evt.source !== this) {
                return;
            }
            // Removes the panel view from the body when the menu is closed.
            if (!isOpen && this._bodyCollection.has(panelView)) {
                this._removePanelFromBody();
                return;
            }
            // Adds the panel view to the body when the menu is open.
            if (isOpen && !this._bodyCollection.has(panelView)) {
                this._addPanelToBody();
            }
        });
    }
    /**
     * Removes the panel view from the editor's body and removes it from the focus tracker.
     */
    _removePanelFromBody() {
        const { panelView, keystrokes } = this;
        if (this._bodyCollection.has(panelView)) {
            this._bodyCollection.remove(panelView);
            keystrokes.stopListening(panelView.element);
        }
    }
    /**
     * Adds the panel view to the editor's body and sets up event listeners.
     */
    _addPanelToBody() {
        const { panelView, buttonView, keystrokes } = this;
        if (!this._bodyCollection.has(panelView)) {
            this._bodyCollection.add(panelView);
            keystrokes.listenTo(panelView.element);
            panelView.pin({
                positions: this._panelPositions,
                limiter: global.document.body,
                element: panelView.element,
                target: buttonView.element,
                fitInViewport: true
            });
        }
    }
}
/**
 * An array of delegated events for the dropdown menu definition controller.
 * These events are delegated to the dropdown menu element.
 */
// Due to some spaghetti code we need to delegate `change:isOpen`.
DropdownMenuNestedMenuView.DELEGATED_EVENTS = [
    'mouseenter', 'execute', 'change:isOpen'
];
export default DropdownMenuNestedMenuView;
