ckeditor_font-8.x-1.x-dev/js/ckeditor5_plugins/ckeditor5-font/src/ui/colorui.js
js/ckeditor5_plugins/ckeditor5-font/src/ui/colorui.js
/**
* @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 font/ui/colorui
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { createDropdown, normalizeColorOptions, getLocalizedColorOptions, focusChildOnDropdownOpen, MenuBarMenuView, ColorSelectorView } from 'ckeditor5/src/ui.js';
import { addColorSelectorToDropdown } from '../utils.js';
/**
* The color UI plugin which isolates the common logic responsible for displaying dropdowns with color grids.
*
* It is used to create the `'fontBackgroundColor'` and `'fontColor'` dropdowns, each hosting
* a {@link module:ui/colorselector/colorselectorview~ColorSelectorView}.
*/
export default class ColorUI extends Plugin {
/**
* Creates a plugin which introduces a dropdown with a pre–configured
* {@link module:ui/colorselector/colorselectorview~ColorSelectorView}.
*
* @param config The configuration object.
* @param config.commandName The name of the command which will be executed when a color tile is clicked.
* @param config.componentName The name of the dropdown in the {@link module:ui/componentfactory~ComponentFactory}
* and the configuration scope name in `editor.config`.
* @param config.icon The SVG icon used by the dropdown.
* @param config.dropdownLabel The label used by the dropdown.
*/
constructor(editor, { commandName, componentName, icon, dropdownLabel }) {
super(editor);
this.commandName = commandName;
this.componentName = componentName;
this.icon = icon;
this.dropdownLabel = dropdownLabel;
this.columns = editor.config.get(`${this.componentName}.columns`);
}
/**
* @inheritDoc
*/
init() {
const editor = this.editor;
const locale = editor.locale;
const t = locale.t;
const command = editor.commands.get(this.commandName);
const componentConfig = editor.config.get(this.componentName);
const colorsConfig = normalizeColorOptions(componentConfig.colors);
const localizedColors = getLocalizedColorOptions(locale, colorsConfig);
const documentColorsCount = componentConfig.documentColors;
const hasColorPicker = componentConfig.colorPicker !== false;
// Register the UI component.
editor.ui.componentFactory.add(this.componentName, locale => {
const dropdownView = createDropdown(locale);
// Font color dropdown rendering is deferred once it gets open to improve performance (#6192).
let dropdownContentRendered = false;
const colorSelectorView = addColorSelectorToDropdown({
dropdownView,
colors: localizedColors.map(option => ({
label: option.label,
color: option.model,
options: {
hasBorder: option.hasBorder
}
})),
columns: this.columns,
removeButtonLabel: t('Remove color'),
colorPickerLabel: t('Color picker'),
documentColorsLabel: documentColorsCount !== 0 ? t('Document colors') : '',
documentColorsCount: documentColorsCount === undefined ? this.columns : documentColorsCount,
colorPickerViewConfig: hasColorPicker ? (componentConfig.colorPicker || {}) : false
});
colorSelectorView.bind('selectedColor').to(command, 'value');
dropdownView.buttonView.set({
label: this.dropdownLabel,
icon: this.icon,
tooltip: true
});
dropdownView.extendTemplate({
attributes: {
class: 'ck-color-ui-dropdown'
}
});
dropdownView.bind('isEnabled').to(command);
colorSelectorView.on('execute', (evt, data) => {
if (dropdownView.isOpen) {
editor.execute(this.commandName, {
value: data.value,
batch: this._undoStepBatch
});
}
if (data.source !== 'colorPicker') {
editor.editing.view.focus();
}
if (data.source === 'colorPickerSaveButton') {
dropdownView.isOpen = false;
}
});
colorSelectorView.on('colorPicker:show', () => {
this._undoStepBatch = editor.model.createBatch();
});
colorSelectorView.on('colorPicker:cancel', () => {
if (this._undoStepBatch.operations.length) {
// We need to close the dropdown before the undo batch.
// Otherwise, ColorUI treats undo as a selected color change,
// propagating the update to the whole selection.
// That's an issue if spans with various colors were selected.
dropdownView.isOpen = false;
editor.execute('undo', this._undoStepBatch);
}
editor.editing.view.focus();
});
dropdownView.on('change:isOpen', (evt, name, isVisible) => {
if (!dropdownContentRendered) {
dropdownContentRendered = true;
dropdownView.colorSelectorView.appendUI();
}
if (isVisible) {
if (documentColorsCount !== 0) {
colorSelectorView.updateDocumentColors(editor.model, this.componentName);
}
colorSelectorView.updateSelectedColors();
colorSelectorView.showColorGridsFragment();
}
});
// Accessibility: focus the first active color when opening the dropdown.
focusChildOnDropdownOpen(dropdownView, () => dropdownView.colorSelectorView.colorGridsFragmentView.staticColorsGrid.items.find((item) => item.isOn));
return dropdownView;
});
// Register menu bar button..
editor.ui.componentFactory.add(`menuBar:${this.componentName}`, locale => {
const menuView = new MenuBarMenuView(locale);
menuView.buttonView.set({
label: this.dropdownLabel,
icon: this.icon
});
menuView.bind('isEnabled').to(command);
// Font color sub-menu rendering is deferred once it gets open to improve performance (#6192).
let contentRendered = false;
const colorSelectorView = new ColorSelectorView(locale, {
colors: localizedColors.map(option => ({
label: option.label,
color: option.model,
options: {
hasBorder: option.hasBorder
}
})),
columns: this.columns,
removeButtonLabel: t('Remove color'),
colorPickerLabel: t('Color picker'),
documentColorsLabel: documentColorsCount !== 0 ? t('Document colors') : '',
documentColorsCount: documentColorsCount === undefined ? this.columns : documentColorsCount,
colorPickerViewConfig: false
});
colorSelectorView.bind('selectedColor').to(command, 'value');
colorSelectorView.delegate('execute').to(menuView);
colorSelectorView.on('execute', (evt, data) => {
editor.execute(this.commandName, {
value: data.value,
batch: this._undoStepBatch
});
editor.editing.view.focus();
});
menuView.on('change:isOpen', (evt, name, isVisible) => {
if (!contentRendered) {
contentRendered = true;
colorSelectorView.appendUI();
}
if (isVisible) {
if (documentColorsCount !== 0) {
colorSelectorView.updateDocumentColors(editor.model, this.componentName);
}
colorSelectorView.updateSelectedColors();
colorSelectorView.showColorGridsFragment();
}
});
menuView.panelView.children.add(colorSelectorView);
return menuView;
});
}
}
