@ckeditor/ckeditor5-alignment
Version:
Text alignment feature for CKEditor 5.
196 lines (195 loc) • 7.82 kB
JavaScript
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
/**
* @module alignment/alignmentui
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { ButtonView, createDropdown, addToolbarToDropdown, MenuBarMenuListItemView, MenuBarMenuListItemButtonView, MenuBarMenuView, MenuBarMenuListView } from 'ckeditor5/src/ui.js';
import { IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignRight } from 'ckeditor5/src/icons.js';
import { isSupported, normalizeAlignmentOptions } from './utils.js';
const iconsMap = /* #__PURE__ */ (() => new Map([
['left', IconAlignLeft],
['right', IconAlignRight],
['center', IconAlignCenter],
['justify', IconAlignJustify]
]))();
/**
* 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
*/
static get isOfficialPlugin() {
return true;
}
/**
* @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 options 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.delegate('execute').to(menuView);
buttonView.set({
label: this.localizedOptionTitles[option.name],
icon: iconsMap.get(option.name),
role: 'menuitemcheckbox',
isToggleable: true
});
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;
});
}
}