-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
Copy pathimageresizehandles.ts
146 lines (119 loc) · 4.66 KB
/
imageresizehandles.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* @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/imageresizehandles
*/
import type { Element, ViewElement } from 'ckeditor5/src/engine.js';
import { Plugin } from 'ckeditor5/src/core.js';
import { WidgetResize } from 'ckeditor5/src/widget.js';
import ImageUtils from '../imageutils.js';
import ImageLoadObserver, { type ImageLoadedEvent } from '../image/imageloadobserver.js';
import type ResizeImageCommand from './resizeimagecommand.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
*/
public static get requires() {
return [ WidgetResize, ImageUtils ] as const;
}
/**
* @inheritDoc
*/
public static get pluginName() {
return 'ImageResizeHandles' as const;
}
/**
* @inheritDoc
*/
public init(): void {
const command: ResizeImageCommand = 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.
*/
private _setupResizerCreator(): void {
const editor = this.editor;
const editingView = editor.editing.view;
const imageUtils: ImageUtils = editor.plugins.get( 'ImageUtils' );
editingView.addObserver( ImageLoadObserver );
this.listenTo<ImageLoadedEvent>( editingView.document, 'imageLoaded', ( evt, domEvent ) => {
// The resizer must be attached only to images loaded by the `ImageInsert`, `ImageUpload` or `LinkImage` plugins.
if ( !( domEvent.target as HTMLElement ).matches( RESIZABLE_IMAGES_CSS_SELECTOR ) ) {
return;
}
const domConverter = editor.editing.view.domConverter;
const imageView = domConverter.domToView( domEvent.target as HTMLElement ) as ViewElement;
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 the model image element parent to avoid setting an inline element (<a>/<span>) as a resize host.
return domConverter.mapViewToDom( mapper.toViewElement( imageModel.parent as Element )! ) as HTMLElement;
},
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 );
} );
}
}