UNPKG

ckeditor5-image-upload-base64

Version:

The development environment of CKEditor 5 – the best browser-based rich text editor.

410 lines (354 loc) 11.5 kB
/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module font/ui/colortableview */ import View from '@ckeditor/ckeditor5-ui/src/view'; import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview'; import ColorTileView from '@ckeditor/ckeditor5-ui/src/colorgrid/colortileview'; import ColorGridView from '@ckeditor/ckeditor5-ui/src/colorgrid/colorgridview'; import LabelView from '@ckeditor/ckeditor5-ui/src/label/labelview'; import DocumentColorCollection from '../documentcolorcollection'; import Template from '@ckeditor/ckeditor5-ui/src/template'; import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker'; import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler'; import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler'; import removeButtonIcon from '@ckeditor/ckeditor5-core/theme/icons/eraser.svg'; import '../../theme/fontcolor.css'; /** * A class which represents a view with the following sub–components: * * * A remove color button, * * A static {@link module:ui/colorgrid/colorgrid~ColorGridView} of colors defined in the configuration, * * A dynamic {@link module:ui/colorgrid/colorgrid~ColorGridView} of colors used in the document. * * @extends module:ui/view~View */ export default class ColorTableView extends View { /** * Creates a view to be inserted as a child of {@link module:ui/dropdown/dropdownview~DropdownView}. * * @param {module:utils/locale~Locale} [locale] The localization services instance. * @param {Object} config The configuration object. * @param {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>} config.colors An array with definitions of colors to * be displayed in the table. * @param {Number} config.columns The number of columns in the color grid. * @param {String} config.removeButtonLabel The label of the button responsible for removing the color. * @param {String} config.documentColorsLabel The label for the section with the document colors. * @param {String} config.documentColorsCount The number of colors in the document colors section inside the color dropdown. */ constructor( locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount } ) { super( locale ); /** * A collection of the children of the table. * * @readonly * @member {module:ui/viewcollection~ViewCollection} */ this.items = this.createCollection(); /** * An array with objects representing colors to be displayed in the grid. * * @type {Arrray.<module:ui/colorgrid/colorgrid~ColorDefinition>} */ this.colorDefinitions = colors; /** * Tracks information about the DOM focus in the list. * * @readonly * @member {module:utils/focustracker~FocusTracker} */ this.focusTracker = new FocusTracker(); /** * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. * * @readonly * @member {module:utils/keystrokehandler~KeystrokeHandler} */ this.keystrokes = new KeystrokeHandler(); /** * Keeps the value of the command associated with the table for the current selection. * * @type {String} */ this.set( 'selectedColor' ); /** * The label of the button responsible for removing color attributes. * * @type {String} */ this.removeButtonLabel = removeButtonLabel; /** * The number of columns in the color grid. * * @type {Number} */ this.columns = columns; /** * A collection of definitions that store the document colors. * * @readonly * @member {module:font/documentcolorcollection~DocumentColorCollection} */ this.documentColors = new DocumentColorCollection(); /** * The maximum number of colors in the document colors section. * If it equals 0, the document colors section is not added. * * @readonly * @type {Number} */ this.documentColorsCount = documentColorsCount; /** * Preserves the reference to {@link module:ui/colorgrid/colorgrid~ColorGridView} used to create * the default (static) color set. * * The property is loaded once the the parent dropdown is opened the first time. * * @readonly * @member {module:ui/colorgrid/colorgrid~ColorGridView|undefined} #staticColorsGrid */ /** * Preserves the reference to {@link module:ui/colorgrid/colorgrid~ColorGridView} used to create * the document colors. It remains undefined if the document colors feature is disabled. * * The property is loaded once the the parent dropdown is opened the first time. * * @readonly * @member {module:ui/colorgrid/colorgrid~ColorGridView|undefined} #documentColorsGrid */ /** * Helps cycling over focusable {@link #items} in the list. * * @readonly * @protected * @member {module:ui/focuscycler~FocusCycler} */ this._focusCycler = new FocusCycler( { focusables: this.items, focusTracker: this.focusTracker, keystrokeHandler: this.keystrokes, actions: { // Navigate list items backwards using the Arrow Up key. focusPrevious: 'arrowup', // Navigate list items forwards using the Arrow Down key. focusNext: 'arrowdown' } } ); /** * Document color section's label. * * @private * @readonly * @type {String} */ this._documentColorsLabel = documentColorsLabel; this.setTemplate( { tag: 'div', attributes: { class: [ 'ck', 'ck-color-table' ] }, children: this.items } ); this.items.add( this._removeColorButton() ); } /** * Scans through the editor model and searches for text node attributes with the given attribute name. * Found entries are set as document colors. * * All the previously stored document colors will be lost in the process. * * @param {module:engine/model/model~Model} model The model used as a source to obtain the document colors. * @param {String} attributeName Determines the name of the related model's attribute for a given dropdown. */ updateDocumentColors( model, attributeName ) { const document = model.document; const maxCount = this.documentColorsCount; this.documentColors.clear(); for ( const rootName of document.getRootNames() ) { const root = document.getRoot( rootName ); const range = model.createRangeIn( root ); for ( const node of range.getItems() ) { if ( node.is( '$textProxy' ) && node.hasAttribute( attributeName ) ) { this._addColorToDocumentColors( node.getAttribute( attributeName ) ); if ( this.documentColors.length >= maxCount ) { return; } } } } } /** * Refreshes the state of the selected color in one or both {@link module:ui/colorgrid/colorgrid~ColorGridView}s * available in the {@link module:font/ui/colortableview~ColorTableView}. It guarantees that the selection will occur only in one * of them. */ updateSelectedColors() { const documentColorsGrid = this.documentColorsGrid; const staticColorsGrid = this.staticColorsGrid; const selectedColor = this.selectedColor; staticColorsGrid.selectedColor = selectedColor; if ( documentColorsGrid ) { documentColorsGrid.selectedColor = selectedColor; } } /** * @inheritDoc */ render() { super.render(); // Items added before rendering should be known to the #focusTracker. for ( const item of this.items ) { this.focusTracker.add( item.element ); } // Start listening for the keystrokes coming from #element. this.keystrokes.listenTo( this.element ); } /** * Appends {@link #staticColorsGrid} and {@link #documentColorsGrid} views. */ appendGrids() { if ( this.staticColorsGrid ) { return; } this.staticColorsGrid = this._createStaticColorsGrid(); this.items.add( this.staticColorsGrid ); if ( this.documentColorsCount ) { // Create a label for document colors. const bind = Template.bind( this.documentColors, this.documentColors ); const label = new LabelView( this.locale ); label.text = this._documentColorsLabel; label.extendTemplate( { attributes: { class: [ 'ck', 'ck-color-grid__label', bind.if( 'isEmpty', 'ck-hidden' ) ] } } ); this.items.add( label ); this.documentColorsGrid = this._createDocumentColorsGrid(); this.items.add( this.documentColorsGrid ); } } /** * Focuses the first focusable element in {@link #items}. */ focus() { this._focusCycler.focusFirst(); } /** * Focuses the last focusable element in {@link #items}. */ focusLast() { this._focusCycler.focusLast(); } /** * Adds the remove color button as a child of the current view. * * @private * @returns {module:ui/button/buttonview~ButtonView} */ _removeColorButton() { const buttonView = new ButtonView(); buttonView.set( { withText: true, icon: removeButtonIcon, tooltip: true, label: this.removeButtonLabel } ); buttonView.class = 'ck-color-table__remove-color'; buttonView.on( 'execute', () => { this.fire( 'execute', { value: null } ); } ); return buttonView; } /** * Creates a static color table grid based on the editor configuration. * * @private * @returns {module:ui/colorgrid/colorgrid~ColorGridView} */ _createStaticColorsGrid() { const colorGrid = new ColorGridView( this.locale, { colorDefinitions: this.colorDefinitions, columns: this.columns } ); colorGrid.delegate( 'execute' ).to( this ); return colorGrid; } /** * Creates the document colors section view and binds it to {@link #documentColors}. * * @private * @returns {module:ui/colorgrid/colorgrid~ColorGridView} */ _createDocumentColorsGrid() { const bind = Template.bind( this.documentColors, this.documentColors ); const documentColorsGrid = new ColorGridView( this.locale, { columns: this.columns } ); documentColorsGrid.delegate( 'execute' ).to( this ); documentColorsGrid.extendTemplate( { attributes: { class: bind.if( 'isEmpty', 'ck-hidden' ) } } ); documentColorsGrid.items.bindTo( this.documentColors ).using( colorObj => { const colorTile = new ColorTileView(); colorTile.set( { color: colorObj.color, hasBorder: colorObj.options && colorObj.options.hasBorder } ); if ( colorObj.label ) { colorTile.set( { label: colorObj.label, tooltip: true } ); } colorTile.on( 'execute', () => { this.fire( 'execute', { value: colorObj.color } ); } ); return colorTile; } ); // Selected color should be cleared when document colors became empty. this.documentColors.on( 'change:isEmpty', ( evt, name, val ) => { if ( val ) { documentColorsGrid.selectedColor = null; } } ); return documentColorsGrid; } /** * Adds a given color to the document colors list. If possible, the method will attempt to use * data from the {@link #colorDefinitions} (label, color options). * * @private * @param {String} color A string that stores the value of the recently applied color. */ _addColorToDocumentColors( color ) { const predefinedColor = this.colorDefinitions .find( definition => definition.color === color ); if ( !predefinedColor ) { this.documentColors.add( { color, label: color, options: { hasBorder: false } } ); } else { this.documentColors.add( Object.assign( {}, predefinedColor ) ); } } }