/**
 * @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 alignment/alignmentui
 */
import { Plugin, icons } from 'ckeditor5/src/core.js';
import { ButtonView, createDropdown, addToolbarToDropdown, MenuBarMenuListItemView, MenuBarMenuListItemButtonView, MenuBarMenuView, MenuBarMenuListView } from 'ckeditor5/src/ui.js';
import { isSupported, normalizeAlignmentOptions } from './utils.js';
const iconsMap = new Map([
    ['left', icons.alignLeft],
    ['right', icons.alignRight],
    ['center', icons.alignCenter],
    ['justify', icons.alignJustify]
]);
/**
 * The default alignment UI plugin.
 *
 * It introduces the `'alignment:left'`, `'alignment:right'`, `'alignment:center'` and `'alignment:justify'` buttons
 * and the `'alignment'` dropdown.
 */
export default class AlignmentUI extends Plugin {
    /**
     * Returns the localized option titles provided by the plugin.
     *
     * The following localized titles corresponding with
     * {@link module:alignment/alignmentconfig~AlignmentConfig#options} are available:
     *
     * * `'left'`,
     * * `'right'`,
     * * `'center'`,
     * * `'justify'`.
     *
     * @readonly
     */
    get localizedOptionTitles() {
        const t = this.editor.t;
        return {
            'left': t('Align left'),
            'right': t('Align right'),
            'center': t('Align center'),
            'justify': t('Justify')
        };
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'AlignmentUI';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const options = normalizeAlignmentOptions(editor.config.get('alignment.options'));
        options
            .map(option => option.name)
            .filter(isSupported)
            .forEach(option => this._addButton(option));
        this._addToolbarDropdown(options);
        this._addMenuBarMenu(options);
    }
    /**
     * Helper method for initializing the button and linking it with an appropriate command.
     *
     * @param option The name of the alignment option for which the button is added.
     */
    _addButton(option) {
        const editor = this.editor;
        editor.ui.componentFactory.add(`alignment:${option}`, locale => this._createButton(locale, option));
    }
    /**
     * Helper method for creating the button view element.
     *
     * @param locale Editor locale.
     * @param option The name of the alignment option for which the button is added.
     * @param buttonAttrs Optional parameters passed to button view instance.
     */
    _createButton(locale, option, buttonAttrs = {}) {
        const editor = this.editor;
        const command = editor.commands.get('alignment');
        const buttonView = new ButtonView(locale);
        buttonView.set({
            label: this.localizedOptionTitles[option],
            icon: iconsMap.get(option),
            tooltip: true,
            isToggleable: true,
            ...buttonAttrs
        });
        // Bind button model to command.
        buttonView.bind('isEnabled').to(command);
        buttonView.bind('isOn').to(command, 'value', value => value === option);
        // Execute command.
        this.listenTo(buttonView, 'execute', () => {
            editor.execute('alignment', { value: option });
            editor.editing.view.focus();
        });
        return buttonView;
    }
    /**
     * Helper method for initializing the toolnar dropdown and linking it with an appropriate command.
     *
     * @param option The name of the alignment option for which the button is added.
     */
    _addToolbarDropdown(options) {
        const editor = this.editor;
        const factory = editor.ui.componentFactory;
        factory.add('alignment', locale => {
            const dropdownView = createDropdown(locale);
            const tooltipPosition = locale.uiLanguageDirection === 'rtl' ? 'w' : 'e';
            const t = locale.t;
            // Add existing alignment buttons to dropdown's toolbar.
            addToolbarToDropdown(dropdownView, () => options.map(option => this._createButton(locale, option.name, { tooltipPosition })), {
                enableActiveItemFocusOnDropdownOpen: true,
                isVertical: true,
                ariaLabel: t('Text alignment toolbar')
            });
            // Configure dropdown properties an behavior.
            dropdownView.buttonView.set({
                label: t('Text alignment'),
                tooltip: true
            });
            dropdownView.extendTemplate({
                attributes: {
                    class: 'ck-alignment-dropdown'
                }
            });
            // The default icon depends on the direction of the content.
            const defaultIcon = locale.contentLanguageDirection === 'rtl' ? iconsMap.get('right') : iconsMap.get('left');
            const command = editor.commands.get('alignment');
            // Change icon to reflect current selection's alignment.
            dropdownView.buttonView.bind('icon').to(command, 'value', value => iconsMap.get(value) || defaultIcon);
            // Enable button if any of the buttons is enabled.
            dropdownView.bind('isEnabled').to(command, 'isEnabled');
            // Focus the editable after executing the command.
            // Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
            this.listenTo(dropdownView, 'execute', () => {
                editor.editing.view.focus();
            });
            return dropdownView;
        });
    }
    /**
     * Creates a menu for all alignment options to use either in menu bar.
     *
     * @param options Normalized alignment options from config.
     */
    _addMenuBarMenu(options) {
        const editor = this.editor;
        editor.ui.componentFactory.add('menuBar:alignment', locale => {
            const command = editor.commands.get('alignment');
            const t = locale.t;
            const menuView = new MenuBarMenuView(locale);
            const listView = new MenuBarMenuListView(locale);
            menuView.bind('isEnabled').to(command);
            listView.set({
                ariaLabel: t('Text alignment'),
                role: 'menu'
            });
            menuView.buttonView.set({
                label: t('Text alignment')
            });
            for (const option of options) {
                const listItemView = new MenuBarMenuListItemView(locale, menuView);
                const buttonView = new MenuBarMenuListItemButtonView(locale);
                buttonView.extendTemplate({
                    attributes: {
                        'aria-checked': buttonView.bindTemplate.to('isOn')
                    }
                });
                buttonView.delegate('execute').to(menuView);
                buttonView.set({
                    label: this.localizedOptionTitles[option.name],
                    icon: iconsMap.get(option.name)
                });
                buttonView.on('execute', () => {
                    editor.execute('alignment', { value: option.name });
                    editor.editing.view.focus();
                });
                buttonView.bind('isOn').to(command, 'value', value => value === option.name);
                buttonView.bind('isEnabled').to(command, 'isEnabled');
                listItemView.children.add(buttonView);
                listView.items.add(listItemView);
            }
            menuView.panelView.children.add(listView);
            return menuView;
        });
    }
}
