UNPKG

@ckeditor/ckeditor5-image

Version:

Image feature for CKEditor 5.

169 lines (168 loc) 6.12 kB
/** * @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 image/imageresize/imagecustomresizeui */ import { Plugin } from 'ckeditor5/src/core.js'; import { ContextualBalloon, clickOutsideHandler, CssTransitionDisablerMixin } from 'ckeditor5/src/ui.js'; import { getBalloonPositionData } from '../image/ui/utils.js'; import { getSelectedImageWidthInUnits } from './utils/getselectedimagewidthinunits.js'; import ImageCustomResizeFormView from './ui/imagecustomresizeformview.js'; import { getSelectedImagePossibleResizeRange } from './utils/getselectedimagepossibleresizerange.js'; /** * The custom resize image UI plugin. * * The plugin uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon}. */ export default class ImageCustomResizeUI extends Plugin { /** * @inheritDoc */ static get requires() { return [ContextualBalloon]; } /** * @inheritDoc */ static get pluginName() { return 'ImageCustomResizeUI'; } /** * @inheritDoc */ destroy() { super.destroy(); // Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341). if (this._form) { this._form.destroy(); } } /** * Creates the {@link module:image/imageresize/ui/imagecustomresizeformview~ImageCustomResizeFormView} * form. */ _createForm(unit) { const editor = this.editor; this._balloon = this.editor.plugins.get('ContextualBalloon'); this._form = new (CssTransitionDisablerMixin(ImageCustomResizeFormView))(editor.locale, unit, getFormValidators(editor)); // Render the form so its #element is available for clickOutsideHandler. this._form.render(); this.listenTo(this._form, 'submit', () => { if (this._form.isValid()) { editor.execute('resizeImage', { width: this._form.sizeWithUnits }); this._hideForm(true); } }); // Update balloon position when form error label is added . this.listenTo(this._form.labeledInput, 'change:errorText', () => { editor.ui.update(); }); this.listenTo(this._form, 'cancel', () => { this._hideForm(true); }); // Close the form on Esc key press. this._form.keystrokes.set('Esc', (data, cancel) => { this._hideForm(true); cancel(); }); // Close on click outside of balloon panel element. clickOutsideHandler({ emitter: this._form, activator: () => this._isVisible, contextElements: () => [this._balloon.view.element], callback: () => this._hideForm() }); } /** * Shows the {@link #_form} in the {@link #_balloon}. * * @internal */ _showForm(unit) { if (this._isVisible) { return; } if (!this._form) { this._createForm(unit); } const editor = this.editor; const labeledInput = this._form.labeledInput; this._form.disableCssTransitions(); this._form.resetFormStatus(); if (!this._isInBalloon) { this._balloon.add({ view: this._form, position: getBalloonPositionData(editor) }); } // Make sure that each time the panel shows up, the field remains in sync with the value of // the command. If the user typed in the input, then canceled the balloon (`labeledInput#value` // stays unaltered) and re-opened it without changing the value of the command, they would see the // old value instead of the actual value of the command. const currentParsedWidth = getSelectedImageWidthInUnits(editor, unit); const initialInputValue = currentParsedWidth ? currentParsedWidth.value.toFixed(1) : ''; const possibleRange = getSelectedImagePossibleResizeRange(editor, unit); labeledInput.fieldView.value = labeledInput.fieldView.element.value = initialInputValue; if (possibleRange) { Object.assign(labeledInput.fieldView, { min: possibleRange.lower.toFixed(1), max: Math.ceil(possibleRange.upper).toFixed(1) }); } this._form.labeledInput.fieldView.select(); this._form.enableCssTransitions(); } /** * Removes the {@link #_form} from the {@link #_balloon}. * * @param focusEditable Controls whether the editing view is focused afterwards. */ _hideForm(focusEditable = false) { if (!this._isInBalloon) { return; } // Blur the input element before removing it from DOM to prevent issues in some browsers. // See https://github.com/ckeditor/ckeditor5/issues/1501. if (this._form.focusTracker.isFocused) { this._form.saveButtonView.focus(); } this._balloon.remove(this._form); if (focusEditable) { this.editor.editing.view.focus(); } } /** * Returns `true` when the {@link #_form} is the visible view in the {@link #_balloon}. */ get _isVisible() { return !!this._balloon && this._balloon.visibleView === this._form; } /** * Returns `true` when the {@link #_form} is in the {@link #_balloon}. */ get _isInBalloon() { return !!this._balloon && this._balloon.hasView(this._form); } } /** * Returns image resize form validation callbacks. * * @param editor Editor instance. */ function getFormValidators(editor) { const t = editor.t; return [ form => { if (form.rawSize.trim() === '') { return t('The value must not be empty.'); } if (form.parsedSize === null) { return t('The value should be a plain number.'); } } ]; }