UNPKG

@ckeditor/ckeditor5-image

Version:

Image feature for CKEditor 5.

330 lines (329 loc) 13.6 kB
/** * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module image/imagestyle/utils */ import { icons } from 'ckeditor5/src/core'; import { logWarning } from 'ckeditor5/src/utils'; const { objectFullWidth, objectInline, objectLeft, objectRight, objectCenter, objectBlockLeft, objectBlockRight } = icons; /** * Default image style options provided by the plugin that can be referred in the {@link module:image/imageconfig~ImageConfig#styles} * configuration. * * There are available 5 styles focused on formatting: * * * **`'alignLeft'`** aligns the inline or block image to the left and wraps it with the text using the `image-style-align-left` class, * * **`'alignRight'`** aligns the inline or block image to the right and wraps it with the text using the `image-style-align-right` class, * * **`'alignCenter'`** centers the block image using the `image-style-align-center` class, * * **`'alignBlockLeft'`** aligns the block image to the left using the `image-style-block-align-left` class, * * **`'alignBlockRight'`** aligns the block image to the right using the `image-style-block-align-right` class, * * and 3 semantic styles: * * * **`'inline'`** is an inline image without any CSS class, * * **`'block'`** is a block image without any CSS class, * * **`'side'`** is a block image styled with the `image-style-side` CSS class. */ export const DEFAULT_OPTIONS = { // This style represents an image placed in the line of text. get inline() { return { name: 'inline', title: 'In line', icon: objectInline, modelElements: ['imageInline'], isDefault: true }; }, // This style represents an image aligned to the left and wrapped with text. get alignLeft() { return { name: 'alignLeft', title: 'Left aligned image', icon: objectLeft, modelElements: ['imageBlock', 'imageInline'], className: 'image-style-align-left' }; }, // This style represents an image aligned to the left. get alignBlockLeft() { return { name: 'alignBlockLeft', title: 'Left aligned image', icon: objectBlockLeft, modelElements: ['imageBlock'], className: 'image-style-block-align-left' }; }, // This style represents a centered image. get alignCenter() { return { name: 'alignCenter', title: 'Centered image', icon: objectCenter, modelElements: ['imageBlock'], className: 'image-style-align-center' }; }, // This style represents an image aligned to the right and wrapped with text. get alignRight() { return { name: 'alignRight', title: 'Right aligned image', icon: objectRight, modelElements: ['imageBlock', 'imageInline'], className: 'image-style-align-right' }; }, // This style represents an image aligned to the right. get alignBlockRight() { return { name: 'alignBlockRight', title: 'Right aligned image', icon: objectBlockRight, modelElements: ['imageBlock'], className: 'image-style-block-align-right' }; }, // This option is equal to the situation when no style is applied. get block() { return { name: 'block', title: 'Centered image', icon: objectCenter, modelElements: ['imageBlock'], isDefault: true }; }, // This represents a side image. get side() { return { name: 'side', title: 'Side image', icon: objectRight, modelElements: ['imageBlock'], className: 'image-style-side' }; } }; /** * Default image style icons provided by the plugin that can be referred in the {@link module:image/imageconfig~ImageConfig#styles} * configuration. * * See {@link module:image/imageconfig~ImageStyleOptionDefinition#icon} to learn more. * * There are 7 default icons available: `'full'`, `'left'`, `'inlineLeft'`, `'center'`, `'right'`, `'inlineRight'`, and `'inline'`. */ export const DEFAULT_ICONS = { full: objectFullWidth, left: objectBlockLeft, right: objectBlockRight, center: objectCenter, inlineLeft: objectLeft, inlineRight: objectRight, inline: objectInline }; /** * Default drop-downs provided by the plugin that can be referred in the {@link module:image/imageconfig~ImageConfig#toolbar} * configuration. The drop-downs are containers for the {@link module:image/imageconfig~ImageStyleConfig#options image style options}. * * If both of the `ImageEditing` plugins are loaded, there are 2 predefined drop-downs available: * * * **`'imageStyle:wrapText'`**, which contains the `alignLeft` and `alignRight` options, that is, * those that wraps the text around the image, * * **`'imageStyle:breakText'`**, which contains the `alignBlockLeft`, `alignCenter` and `alignBlockRight` options, that is, * those that breaks the text around the image. */ export const DEFAULT_DROPDOWN_DEFINITIONS = [{ name: 'imageStyle:wrapText', title: 'Wrap text', defaultItem: 'imageStyle:alignLeft', items: ['imageStyle:alignLeft', 'imageStyle:alignRight'] }, { name: 'imageStyle:breakText', title: 'Break text', defaultItem: 'imageStyle:block', items: ['imageStyle:alignBlockLeft', 'imageStyle:block', 'imageStyle:alignBlockRight'] }]; /** * Returns a list of the normalized and validated image style options. * * @param config * @param config.isInlinePluginLoaded * Determines whether the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin has been loaded. * @param config.isBlockPluginLoaded * Determines whether the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin has been loaded. * @param config.configuredStyles * The image styles configuration provided in the image styles {@link module:image/imageconfig~ImageConfig#styles configuration} * as a default or custom value. * @returns * * Each of options contains a complete icon markup. * * The image style options not supported by any of the loaded plugins are filtered out. */ function normalizeStyles(config) { const configuredStyles = config.configuredStyles.options || []; const styles = configuredStyles .map(arrangement => normalizeDefinition(arrangement)) .filter(arrangement => isValidOption(arrangement, config)); return styles; } /** * Returns the default image styles configuration depending on the loaded image editing plugins. * * @param isInlinePluginLoaded * Determines whether the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin has been loaded. * * @param isBlockPluginLoaded * Determines whether the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin has been loaded. * * @returns * It returns an object with the lists of the image style options and groups defined as strings related to the * {@link module:image/imagestyle/utils#DEFAULT_OPTIONS default options} */ function getDefaultStylesConfiguration(isBlockPluginLoaded, isInlinePluginLoaded) { if (isBlockPluginLoaded && isInlinePluginLoaded) { return { options: [ 'inline', 'alignLeft', 'alignRight', 'alignCenter', 'alignBlockLeft', 'alignBlockRight', 'block', 'side' ] }; } else if (isBlockPluginLoaded) { return { options: ['block', 'side'] }; } else if (isInlinePluginLoaded) { return { options: ['inline', 'alignLeft', 'alignRight'] }; } return {}; } /** * Returns a list of the available predefined drop-downs' definitions depending on the loaded image editing plugins. */ function getDefaultDropdownDefinitions(pluginCollection) { if (pluginCollection.has('ImageBlockEditing') && pluginCollection.has('ImageInlineEditing')) { return [...DEFAULT_DROPDOWN_DEFINITIONS]; } else { return []; } } /** * Normalizes an image style option or group provided in the {@link module:image/imageconfig~ImageConfig#styles} * and returns it in a {@link module:image/imageconfig~ImageStyleOptionDefinition}/ */ function normalizeDefinition(definition) { if (typeof definition === 'string') { // Just the name of the style has been passed, but none of the defaults. if (!DEFAULT_OPTIONS[definition]) { // Normalize the style anyway to prevent errors. definition = { name: definition }; } // Just the name of the style has been passed and it's one of the defaults, just use it. // Clone the style to avoid overriding defaults. else { definition = { ...DEFAULT_OPTIONS[definition] }; } } else { // If an object style has been passed and if the name matches one of the defaults, // extend it with defaults – the user wants to customize a default style. // Note: Don't override the user–defined style object, clone it instead. definition = extendStyle(DEFAULT_OPTIONS[definition.name], definition); } // If an icon is defined as a string and correspond with a name // in default icons, use the default icon provided by the plugin. if (typeof definition.icon === 'string') { definition.icon = DEFAULT_ICONS[definition.icon] || definition.icon; } return definition; } /** * Checks if the image style option is valid: * * if it has the modelElements fields defined and filled, * * if the defined modelElements are supported by any of the loaded image editing plugins. * It also displays a console warning these conditions are not met. * * @param option image style option */ function isValidOption(option, { isBlockPluginLoaded, isInlinePluginLoaded }) { const { modelElements, name } = option; if (!modelElements || !modelElements.length || !name) { warnInvalidStyle({ style: option }); return false; } else { const supportedElements = [isBlockPluginLoaded ? 'imageBlock' : null, isInlinePluginLoaded ? 'imageInline' : null]; // Check if the option is supported by any of the loaded plugins. if (!modelElements.some(elementName => supportedElements.includes(elementName))) { /** * In order to work correctly, each image style {@link module:image/imageconfig~ImageStyleOptionDefinition option} * requires specific model elements (also: types of images) to be supported by the editor. * * Model element names to which the image style option can be applied are defined in the * {@link module:image/imageconfig~ImageStyleOptionDefinition#modelElements} property of the style option * definition. * * Explore the warning in the console to find out precisely which option is not supported and which editor plugins * are missing. Make sure these plugins are loaded in your editor to get this image style option working. * * @error image-style-missing-dependency * @param {String} [option] The name of the unsupported option. * @param {String} [missingPlugins] The names of the plugins one of which has to be loaded for the particular option. */ logWarning('image-style-missing-dependency', { style: option, missingPlugins: modelElements.map(name => name === 'imageBlock' ? 'ImageBlockEditing' : 'ImageInlineEditing') }); return false; } } return true; } /** * Extends the default style with a style provided by the developer. * Note: Don't override the custom–defined style object, clone it instead. */ function extendStyle(source, style) { const extendedStyle = { ...style }; for (const prop in source) { if (!Object.prototype.hasOwnProperty.call(style, prop)) { extendedStyle[prop] = source[prop]; } } return extendedStyle; } /** * Displays a console warning with the 'image-style-configuration-definition-invalid' error. */ function warnInvalidStyle(info) { /** * The image style definition provided in the configuration is invalid. * * Please make sure the definition implements properly one of the following: * * * {@link module:image/imageconfig~ImageStyleOptionDefinition image style option definition}, * * {@link module:image/imageconfig~ImageStyleDropdownDefinition image style dropdown definition} * * @error image-style-configuration-definition-invalid * @param {String} [dropdown] The name of the invalid drop-down * @param {String} [style] The name of the invalid image style option */ logWarning('image-style-configuration-definition-invalid', info); } export default { normalizeStyles, getDefaultStylesConfiguration, getDefaultDropdownDefinitions, warnInvalidStyle, DEFAULT_OPTIONS, DEFAULT_ICONS, DEFAULT_DROPDOWN_DEFINITIONS };