@ckeditor/ckeditor5-image
Version:
Image feature for CKEditor 5.
113 lines (112 loc) • 4.96 kB
JavaScript
/**
* @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
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { WidgetResize } from 'ckeditor5/src/widget.js';
import ImageUtils from '../imageutils.js';
import ImageLoadObserver from '../image/imageloadobserver.js';
const RESIZABLE_IMAGES_CSS_SELECTOR = 'figure.image.ck-widget > img,' +
'figure.image.ck-widget > picture > img,' +
'figure.image.ck-widget > a > img,' +
'figure.image.ck-widget > a > picture > img,' +
'span.image-inline.ck-widget > img,' +
'span.image-inline.ck-widget > picture > img';
const RESIZED_IMAGE_CLASS = 'image_resized';
/**
* The image resize by handles feature.
*
* It adds the ability to resize each image using handles or manually by
* {@link module:image/imageresize/imageresizebuttons~ImageResizeButtons} buttons.
*/
export default class ImageResizeHandles extends Plugin {
/**
* @inheritDoc
*/
static get requires() {
return [WidgetResize, ImageUtils];
}
/**
* @inheritDoc
*/
static get pluginName() {
return 'ImageResizeHandles';
}
/**
* @inheritDoc
*/
init() {
const command = this.editor.commands.get('resizeImage');
this.bind('isEnabled').to(command);
this._setupResizerCreator();
}
/**
* Attaches the listeners responsible for creating a resizer for each image, except for images inside the HTML embed preview.
*/
_setupResizerCreator() {
const editor = this.editor;
const editingView = editor.editing.view;
const imageUtils = editor.plugins.get('ImageUtils');
editingView.addObserver(ImageLoadObserver);
this.listenTo(editingView.document, 'imageLoaded', (evt, domEvent) => {
// The resizer must be attached only to images loaded by the `ImageInsert`, `ImageUpload` or `LinkImage` plugins.
if (!domEvent.target.matches(RESIZABLE_IMAGES_CSS_SELECTOR)) {
return;
}
const domConverter = editor.editing.view.domConverter;
const imageView = domConverter.domToView(domEvent.target);
const widgetView = imageUtils.getImageWidgetFromImageView(imageView);
let resizer = this.editor.plugins.get(WidgetResize).getResizerByViewElement(widgetView);
if (resizer) {
// There are rare cases when the image will be triggered multiple times for the same widget, e.g. when
// the image's source was changed after upload (https://github.com/ckeditor/ckeditor5/pull/8108#issuecomment-708302992).
resizer.redraw();
return;
}
const mapper = editor.editing.mapper;
const imageModel = mapper.toModelElement(widgetView);
resizer = editor.plugins
.get(WidgetResize)
.attachTo({
unit: editor.config.get('image.resizeUnit'),
modelElement: imageModel,
viewElement: widgetView,
editor,
getHandleHost(domWidgetElement) {
return domWidgetElement.querySelector('img');
},
getResizeHost() {
return domConverter.mapViewToDom(mapper.toViewElement(imageModel));
},
isCentered() {
const imageStyle = imageModel.getAttribute('imageStyle');
return imageStyle == 'alignCenter';
},
onCommit(newValue) {
// Get rid of the CSS class in case the command execution that follows is unsuccessful
// (e.g. Track Changes can override it and the new dimensions will not apply). Otherwise,
// the presence of the class and the absence of the width style will cause it to take 100%
// of the horizontal space.
editingView.change(writer => {
writer.removeClass(RESIZED_IMAGE_CLASS, widgetView);
});
editor.execute('resizeImage', { width: newValue });
}
});
resizer.on('updateSize', () => {
if (!widgetView.hasClass(RESIZED_IMAGE_CLASS)) {
editingView.change(writer => {
writer.addClass(RESIZED_IMAGE_CLASS, widgetView);
});
}
const target = imageModel.name === 'imageInline' ? imageView : widgetView;
if (target.getStyle('height')) {
editingView.change(writer => {
writer.removeStyle('height', target);
});
}
});
resizer.bind('isEnabled').to(this);
});
}
}