@ckeditor/ckeditor5-widget 38.1.1 → 38.2.0-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,372 +1,372 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module widget/widgetresize/resizer
7
- */
8
- import { Template } from '@ckeditor/ckeditor5-ui';
9
- import { Rect, ObservableMixin, compareArrays } from '@ckeditor/ckeditor5-utils';
10
- import ResizeState from './resizerstate';
11
- import SizeView from './sizeview';
12
- /**
13
- * Represents a resizer for a single resizable object.
14
- */
15
- export default class Resizer extends ObservableMixin() {
16
- /**
17
- * @param options Resizer options.
18
- */
19
- constructor(options) {
20
- super();
21
- /**
22
- * A wrapper that is controlled by the resizer. This is usually a widget element.
23
- */
24
- this._viewResizerWrapper = null;
25
- this._options = options;
26
- this.set('isEnabled', true);
27
- this.set('isSelected', false);
28
- this.bind('isVisible').to(this, 'isEnabled', this, 'isSelected', (isEnabled, isSelected) => isEnabled && isSelected);
29
- this.decorate('begin');
30
- this.decorate('cancel');
31
- this.decorate('commit');
32
- this.decorate('updateSize');
33
- this.on('commit', event => {
34
- // State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
35
- // cleaned up (#5195).
36
- if (!this.state.proposedWidth && !this.state.proposedWidthPercents) {
37
- this._cleanup();
38
- event.stop();
39
- }
40
- }, { priority: 'high' });
41
- }
42
- /**
43
- * Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
44
- *
45
- * Note that a new state is created for each resize transaction.
46
- */
47
- get state() {
48
- return this._state;
49
- }
50
- /**
51
- * Makes resizer visible in the UI.
52
- */
53
- show() {
54
- const editingView = this._options.editor.editing.view;
55
- editingView.change(writer => {
56
- writer.removeClass('ck-hidden', this._viewResizerWrapper);
57
- });
58
- }
59
- /**
60
- * Hides resizer in the UI.
61
- */
62
- hide() {
63
- const editingView = this._options.editor.editing.view;
64
- editingView.change(writer => {
65
- writer.addClass('ck-hidden', this._viewResizerWrapper);
66
- });
67
- }
68
- /**
69
- * Attaches the resizer to the DOM.
70
- */
71
- attach() {
72
- // eslint-disable-next-line @typescript-eslint/no-this-alias
73
- const that = this;
74
- const widgetElement = this._options.viewElement;
75
- const editingView = this._options.editor.editing.view;
76
- editingView.change(writer => {
77
- const viewResizerWrapper = writer.createUIElement('div', {
78
- class: 'ck ck-reset_all ck-widget__resizer'
79
- }, function (domDocument) {
80
- const domElement = this.toDomElement(domDocument);
81
- that._appendHandles(domElement);
82
- that._appendSizeUI(domElement);
83
- return domElement;
84
- });
85
- // Append the resizer wrapper to the widget's wrapper.
86
- writer.insert(writer.createPositionAt(widgetElement, 'end'), viewResizerWrapper);
87
- writer.addClass('ck-widget_with-resizer', widgetElement);
88
- this._viewResizerWrapper = viewResizerWrapper;
89
- if (!this.isVisible) {
90
- this.hide();
91
- }
92
- });
93
- this.on('change:isVisible', () => {
94
- if (this.isVisible) {
95
- this.show();
96
- this.redraw();
97
- }
98
- else {
99
- this.hide();
100
- }
101
- });
102
- }
103
- /**
104
- * Starts the resizing process.
105
- *
106
- * Creates a new {@link #state} for the current process.
107
- *
108
- * @fires begin
109
- * @param domResizeHandle Clicked handle.
110
- */
111
- begin(domResizeHandle) {
112
- this._state = new ResizeState(this._options);
113
- this._sizeView._bindToState(this._options, this.state);
114
- this._initialViewWidth = this._options.viewElement.getStyle('width');
115
- this.state.begin(domResizeHandle, this._getHandleHost(), this._getResizeHost());
116
- }
117
- /**
118
- * Updates the proposed size based on `domEventData`.
119
- *
120
- * @fires updateSize
121
- */
122
- updateSize(domEventData) {
123
- const newSize = this._proposeNewSize(domEventData);
124
- const editingView = this._options.editor.editing.view;
125
- editingView.change(writer => {
126
- const unit = this._options.unit || '%';
127
- const newWidth = (unit === '%' ? newSize.widthPercents : newSize.width) + unit;
128
- writer.setStyle('width', newWidth, this._options.viewElement);
129
- });
130
- // Get an actual image width, and:
131
- // * reflect this size to the resize wrapper
132
- // * apply this **real** size to the state
133
- const domHandleHost = this._getHandleHost();
134
- const domHandleHostRect = new Rect(domHandleHost);
135
- const handleHostWidth = Math.round(domHandleHostRect.width);
136
- const handleHostHeight = Math.round(domHandleHostRect.height);
137
- // Handle max-width limitation.
138
- const domResizeHostRect = new Rect(domHandleHost);
139
- newSize.width = Math.round(domResizeHostRect.width);
140
- newSize.height = Math.round(domResizeHostRect.height);
141
- this.redraw(domHandleHostRect);
142
- this.state.update({
143
- ...newSize,
144
- handleHostWidth,
145
- handleHostHeight
146
- });
147
- }
148
- /**
149
- * Applies the geometry proposed with the resizer.
150
- *
151
- * @fires commit
152
- */
153
- commit() {
154
- const unit = this._options.unit || '%';
155
- const newValue = (unit === '%' ? this.state.proposedWidthPercents : this.state.proposedWidth) + unit;
156
- // Both cleanup and onCommit callback are very likely to make view changes. Ensure that it is made in a single step.
157
- this._options.editor.editing.view.change(() => {
158
- this._cleanup();
159
- this._options.onCommit(newValue);
160
- });
161
- }
162
- /**
163
- * Cancels and rejects the proposed resize dimensions, hiding the UI.
164
- *
165
- * @fires cancel
166
- */
167
- cancel() {
168
- this._cleanup();
169
- }
170
- /**
171
- * Destroys the resizer.
172
- */
173
- destroy() {
174
- this.cancel();
175
- }
176
- /**
177
- * Redraws the resizer.
178
- *
179
- * @param handleHostRect Handle host rectangle might be given to improve performance.
180
- */
181
- redraw(handleHostRect) {
182
- const domWrapper = this._domResizerWrapper;
183
- // Refresh only if resizer exists in the DOM.
184
- if (!existsInDom(domWrapper)) {
185
- return;
186
- }
187
- const widgetWrapper = domWrapper.parentElement;
188
- const handleHost = this._getHandleHost();
189
- const resizerWrapper = this._viewResizerWrapper;
190
- const currentDimensions = [
191
- resizerWrapper.getStyle('width'),
192
- resizerWrapper.getStyle('height'),
193
- resizerWrapper.getStyle('left'),
194
- resizerWrapper.getStyle('top')
195
- ];
196
- let newDimensions;
197
- if (widgetWrapper.isSameNode(handleHost)) {
198
- const clientRect = handleHostRect || new Rect(handleHost);
199
- newDimensions = [
200
- clientRect.width + 'px',
201
- clientRect.height + 'px',
202
- undefined,
203
- undefined
204
- ];
205
- }
206
- // In case a resizing host is not a widget wrapper, we need to compensate
207
- // for any additional offsets the resize host might have. E.g. wrapper padding
208
- // or simply another editable. By doing that the border and resizers are shown
209
- // only around the resize host.
210
- else {
211
- newDimensions = [
212
- handleHost.offsetWidth + 'px',
213
- handleHost.offsetHeight + 'px',
214
- handleHost.offsetLeft + 'px',
215
- handleHost.offsetTop + 'px'
216
- ];
217
- }
218
- // Make changes to the view only if the resizer should actually get new dimensions.
219
- // Otherwise, if View#change() was always called, this would cause EditorUI#update
220
- // loops because the WidgetResize plugin listens to EditorUI#update and updates
221
- // the resizer.
222
- // https://github.com/ckeditor/ckeditor5/issues/7633
223
- if (compareArrays(currentDimensions, newDimensions) !== 'same') {
224
- this._options.editor.editing.view.change(writer => {
225
- writer.setStyle({
226
- width: newDimensions[0],
227
- height: newDimensions[1],
228
- left: newDimensions[2],
229
- top: newDimensions[3]
230
- }, resizerWrapper);
231
- });
232
- }
233
- }
234
- containsHandle(domElement) {
235
- return this._domResizerWrapper.contains(domElement);
236
- }
237
- static isResizeHandle(domElement) {
238
- return domElement.classList.contains('ck-widget__resizer__handle');
239
- }
240
- /**
241
- * Cleans up the context state.
242
- */
243
- _cleanup() {
244
- this._sizeView._dismiss();
245
- const editingView = this._options.editor.editing.view;
246
- editingView.change(writer => {
247
- writer.setStyle('width', this._initialViewWidth, this._options.viewElement);
248
- });
249
- }
250
- /**
251
- * Calculates the proposed size as the resize handles are dragged.
252
- *
253
- * @param domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
254
- */
255
- _proposeNewSize(domEventData) {
256
- const state = this.state;
257
- const currentCoordinates = extractCoordinates(domEventData);
258
- const isCentered = this._options.isCentered ? this._options.isCentered(this) : true;
259
- // Enlargement defines how much the resize host has changed in a given axis. Naturally it could be a negative number
260
- // meaning that it has been shrunk.
261
- //
262
- // +----------------+--+
263
- // | | |
264
- // | img | |
265
- // | /handle host | |
266
- // +----------------+ | ^
267
- // | | | - enlarge y
268
- // +-------------------+ v
269
- // <-->
270
- // enlarge x
271
- const enlargement = {
272
- x: state._referenceCoordinates.x - (currentCoordinates.x + state.originalWidth),
273
- y: (currentCoordinates.y - state.originalHeight) - state._referenceCoordinates.y
274
- };
275
- if (isCentered && state.activeHandlePosition.endsWith('-right')) {
276
- enlargement.x = currentCoordinates.x - (state._referenceCoordinates.x + state.originalWidth);
277
- }
278
- // Objects needs to be resized twice as much in horizontal axis if centered, since enlargement is counted from
279
- // one resized corner to your cursor. It needs to be duplicated to compensate for the other side too.
280
- if (isCentered) {
281
- enlargement.x *= 2;
282
- }
283
- // const resizeHost = this._getResizeHost();
284
- // The size proposed by the user. It does not consider the aspect ratio.
285
- let width = Math.abs(state.originalWidth + enlargement.x);
286
- let height = Math.abs(state.originalHeight + enlargement.y);
287
- // Dominant determination must take the ratio into account.
288
- const dominant = width / state.aspectRatio > height ? 'width' : 'height';
289
- if (dominant == 'width') {
290
- height = width / state.aspectRatio;
291
- }
292
- else {
293
- width = height * state.aspectRatio;
294
- }
295
- return {
296
- width: Math.round(width),
297
- height: Math.round(height),
298
- widthPercents: Math.min(Math.round(state.originalWidthPercents / state.originalWidth * width * 100) / 100, 100)
299
- };
300
- }
301
- /**
302
- * Obtains the resize host.
303
- *
304
- * Resize host is an object that receives dimensions which are the result of resizing.
305
- */
306
- _getResizeHost() {
307
- const widgetWrapper = this._domResizerWrapper.parentElement;
308
- return this._options.getResizeHost(widgetWrapper);
309
- }
310
- /**
311
- * Obtains the handle host.
312
- *
313
- * Handle host is an object that the handles are aligned to.
314
- *
315
- * Handle host will not always be an entire widget itself. Take an image as an example. The image widget
316
- * contains an image and a caption. Only the image should be surrounded with handles.
317
- */
318
- _getHandleHost() {
319
- const widgetWrapper = this._domResizerWrapper.parentElement;
320
- return this._options.getHandleHost(widgetWrapper);
321
- }
322
- /**
323
- * DOM container of the entire resize UI.
324
- *
325
- * Note that this property will have a value only after the element bound with the resizer is rendered
326
- * (otherwise `null`).
327
- */
328
- get _domResizerWrapper() {
329
- return this._options.editor.editing.view.domConverter.mapViewToDom(this._viewResizerWrapper);
330
- }
331
- /**
332
- * Renders the resize handles in the DOM.
333
- *
334
- * @param domElement The resizer wrapper.
335
- */
336
- _appendHandles(domElement) {
337
- const resizerPositions = ['top-left', 'top-right', 'bottom-right', 'bottom-left'];
338
- for (const currentPosition of resizerPositions) {
339
- domElement.appendChild((new Template({
340
- tag: 'div',
341
- attributes: {
342
- class: `ck-widget__resizer__handle ${getResizerClass(currentPosition)}`
343
- }
344
- }).render()));
345
- }
346
- }
347
- /**
348
- * Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
349
- */
350
- _appendSizeUI(domElement) {
351
- this._sizeView = new SizeView();
352
- // Make sure icon#element is rendered before passing to appendChild().
353
- this._sizeView.render();
354
- domElement.appendChild(this._sizeView.element);
355
- }
356
- }
357
- /**
358
- * @param resizerPosition Expected resizer position like `"top-left"`, `"bottom-right"`.
359
- * @returns A prefixed HTML class name for the resizer element
360
- */
361
- function getResizerClass(resizerPosition) {
362
- return `ck-widget__resizer__handle-${resizerPosition}`;
363
- }
364
- function extractCoordinates(event) {
365
- return {
366
- x: event.pageX,
367
- y: event.pageY
368
- };
369
- }
370
- function existsInDom(element) {
371
- return element && element.ownerDocument && element.ownerDocument.contains(element);
372
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module widget/widgetresize/resizer
7
+ */
8
+ import { Template } from '@ckeditor/ckeditor5-ui';
9
+ import { Rect, ObservableMixin, compareArrays } from '@ckeditor/ckeditor5-utils';
10
+ import ResizeState from './resizerstate.js';
11
+ import SizeView from './sizeview.js';
12
+ /**
13
+ * Represents a resizer for a single resizable object.
14
+ */
15
+ export default class Resizer extends ObservableMixin() {
16
+ /**
17
+ * @param options Resizer options.
18
+ */
19
+ constructor(options) {
20
+ super();
21
+ /**
22
+ * A wrapper that is controlled by the resizer. This is usually a widget element.
23
+ */
24
+ this._viewResizerWrapper = null;
25
+ this._options = options;
26
+ this.set('isEnabled', true);
27
+ this.set('isSelected', false);
28
+ this.bind('isVisible').to(this, 'isEnabled', this, 'isSelected', (isEnabled, isSelected) => isEnabled && isSelected);
29
+ this.decorate('begin');
30
+ this.decorate('cancel');
31
+ this.decorate('commit');
32
+ this.decorate('updateSize');
33
+ this.on('commit', event => {
34
+ // State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
35
+ // cleaned up (#5195).
36
+ if (!this.state.proposedWidth && !this.state.proposedWidthPercents) {
37
+ this._cleanup();
38
+ event.stop();
39
+ }
40
+ }, { priority: 'high' });
41
+ }
42
+ /**
43
+ * Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
44
+ *
45
+ * Note that a new state is created for each resize transaction.
46
+ */
47
+ get state() {
48
+ return this._state;
49
+ }
50
+ /**
51
+ * Makes resizer visible in the UI.
52
+ */
53
+ show() {
54
+ const editingView = this._options.editor.editing.view;
55
+ editingView.change(writer => {
56
+ writer.removeClass('ck-hidden', this._viewResizerWrapper);
57
+ });
58
+ }
59
+ /**
60
+ * Hides resizer in the UI.
61
+ */
62
+ hide() {
63
+ const editingView = this._options.editor.editing.view;
64
+ editingView.change(writer => {
65
+ writer.addClass('ck-hidden', this._viewResizerWrapper);
66
+ });
67
+ }
68
+ /**
69
+ * Attaches the resizer to the DOM.
70
+ */
71
+ attach() {
72
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
73
+ const that = this;
74
+ const widgetElement = this._options.viewElement;
75
+ const editingView = this._options.editor.editing.view;
76
+ editingView.change(writer => {
77
+ const viewResizerWrapper = writer.createUIElement('div', {
78
+ class: 'ck ck-reset_all ck-widget__resizer'
79
+ }, function (domDocument) {
80
+ const domElement = this.toDomElement(domDocument);
81
+ that._appendHandles(domElement);
82
+ that._appendSizeUI(domElement);
83
+ return domElement;
84
+ });
85
+ // Append the resizer wrapper to the widget's wrapper.
86
+ writer.insert(writer.createPositionAt(widgetElement, 'end'), viewResizerWrapper);
87
+ writer.addClass('ck-widget_with-resizer', widgetElement);
88
+ this._viewResizerWrapper = viewResizerWrapper;
89
+ if (!this.isVisible) {
90
+ this.hide();
91
+ }
92
+ });
93
+ this.on('change:isVisible', () => {
94
+ if (this.isVisible) {
95
+ this.show();
96
+ this.redraw();
97
+ }
98
+ else {
99
+ this.hide();
100
+ }
101
+ });
102
+ }
103
+ /**
104
+ * Starts the resizing process.
105
+ *
106
+ * Creates a new {@link #state} for the current process.
107
+ *
108
+ * @fires begin
109
+ * @param domResizeHandle Clicked handle.
110
+ */
111
+ begin(domResizeHandle) {
112
+ this._state = new ResizeState(this._options);
113
+ this._sizeView._bindToState(this._options, this.state);
114
+ this._initialViewWidth = this._options.viewElement.getStyle('width');
115
+ this.state.begin(domResizeHandle, this._getHandleHost(), this._getResizeHost());
116
+ }
117
+ /**
118
+ * Updates the proposed size based on `domEventData`.
119
+ *
120
+ * @fires updateSize
121
+ */
122
+ updateSize(domEventData) {
123
+ const newSize = this._proposeNewSize(domEventData);
124
+ const editingView = this._options.editor.editing.view;
125
+ editingView.change(writer => {
126
+ const unit = this._options.unit || '%';
127
+ const newWidth = (unit === '%' ? newSize.widthPercents : newSize.width) + unit;
128
+ writer.setStyle('width', newWidth, this._options.viewElement);
129
+ });
130
+ // Get an actual image width, and:
131
+ // * reflect this size to the resize wrapper
132
+ // * apply this **real** size to the state
133
+ const domHandleHost = this._getHandleHost();
134
+ const domHandleHostRect = new Rect(domHandleHost);
135
+ const handleHostWidth = Math.round(domHandleHostRect.width);
136
+ const handleHostHeight = Math.round(domHandleHostRect.height);
137
+ // Handle max-width limitation.
138
+ const domResizeHostRect = new Rect(domHandleHost);
139
+ newSize.width = Math.round(domResizeHostRect.width);
140
+ newSize.height = Math.round(domResizeHostRect.height);
141
+ this.redraw(domHandleHostRect);
142
+ this.state.update({
143
+ ...newSize,
144
+ handleHostWidth,
145
+ handleHostHeight
146
+ });
147
+ }
148
+ /**
149
+ * Applies the geometry proposed with the resizer.
150
+ *
151
+ * @fires commit
152
+ */
153
+ commit() {
154
+ const unit = this._options.unit || '%';
155
+ const newValue = (unit === '%' ? this.state.proposedWidthPercents : this.state.proposedWidth) + unit;
156
+ // Both cleanup and onCommit callback are very likely to make view changes. Ensure that it is made in a single step.
157
+ this._options.editor.editing.view.change(() => {
158
+ this._cleanup();
159
+ this._options.onCommit(newValue);
160
+ });
161
+ }
162
+ /**
163
+ * Cancels and rejects the proposed resize dimensions, hiding the UI.
164
+ *
165
+ * @fires cancel
166
+ */
167
+ cancel() {
168
+ this._cleanup();
169
+ }
170
+ /**
171
+ * Destroys the resizer.
172
+ */
173
+ destroy() {
174
+ this.cancel();
175
+ }
176
+ /**
177
+ * Redraws the resizer.
178
+ *
179
+ * @param handleHostRect Handle host rectangle might be given to improve performance.
180
+ */
181
+ redraw(handleHostRect) {
182
+ const domWrapper = this._domResizerWrapper;
183
+ // Refresh only if resizer exists in the DOM.
184
+ if (!existsInDom(domWrapper)) {
185
+ return;
186
+ }
187
+ const widgetWrapper = domWrapper.parentElement;
188
+ const handleHost = this._getHandleHost();
189
+ const resizerWrapper = this._viewResizerWrapper;
190
+ const currentDimensions = [
191
+ resizerWrapper.getStyle('width'),
192
+ resizerWrapper.getStyle('height'),
193
+ resizerWrapper.getStyle('left'),
194
+ resizerWrapper.getStyle('top')
195
+ ];
196
+ let newDimensions;
197
+ if (widgetWrapper.isSameNode(handleHost)) {
198
+ const clientRect = handleHostRect || new Rect(handleHost);
199
+ newDimensions = [
200
+ clientRect.width + 'px',
201
+ clientRect.height + 'px',
202
+ undefined,
203
+ undefined
204
+ ];
205
+ }
206
+ // In case a resizing host is not a widget wrapper, we need to compensate
207
+ // for any additional offsets the resize host might have. E.g. wrapper padding
208
+ // or simply another editable. By doing that the border and resizers are shown
209
+ // only around the resize host.
210
+ else {
211
+ newDimensions = [
212
+ handleHost.offsetWidth + 'px',
213
+ handleHost.offsetHeight + 'px',
214
+ handleHost.offsetLeft + 'px',
215
+ handleHost.offsetTop + 'px'
216
+ ];
217
+ }
218
+ // Make changes to the view only if the resizer should actually get new dimensions.
219
+ // Otherwise, if View#change() was always called, this would cause EditorUI#update
220
+ // loops because the WidgetResize plugin listens to EditorUI#update and updates
221
+ // the resizer.
222
+ // https://github.com/ckeditor/ckeditor5/issues/7633
223
+ if (compareArrays(currentDimensions, newDimensions) !== 'same') {
224
+ this._options.editor.editing.view.change(writer => {
225
+ writer.setStyle({
226
+ width: newDimensions[0],
227
+ height: newDimensions[1],
228
+ left: newDimensions[2],
229
+ top: newDimensions[3]
230
+ }, resizerWrapper);
231
+ });
232
+ }
233
+ }
234
+ containsHandle(domElement) {
235
+ return this._domResizerWrapper.contains(domElement);
236
+ }
237
+ static isResizeHandle(domElement) {
238
+ return domElement.classList.contains('ck-widget__resizer__handle');
239
+ }
240
+ /**
241
+ * Cleans up the context state.
242
+ */
243
+ _cleanup() {
244
+ this._sizeView._dismiss();
245
+ const editingView = this._options.editor.editing.view;
246
+ editingView.change(writer => {
247
+ writer.setStyle('width', this._initialViewWidth, this._options.viewElement);
248
+ });
249
+ }
250
+ /**
251
+ * Calculates the proposed size as the resize handles are dragged.
252
+ *
253
+ * @param domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
254
+ */
255
+ _proposeNewSize(domEventData) {
256
+ const state = this.state;
257
+ const currentCoordinates = extractCoordinates(domEventData);
258
+ const isCentered = this._options.isCentered ? this._options.isCentered(this) : true;
259
+ // Enlargement defines how much the resize host has changed in a given axis. Naturally it could be a negative number
260
+ // meaning that it has been shrunk.
261
+ //
262
+ // +----------------+--+
263
+ // | | |
264
+ // | img | |
265
+ // | /handle host | |
266
+ // +----------------+ | ^
267
+ // | | | - enlarge y
268
+ // +-------------------+ v
269
+ // <-->
270
+ // enlarge x
271
+ const enlargement = {
272
+ x: state._referenceCoordinates.x - (currentCoordinates.x + state.originalWidth),
273
+ y: (currentCoordinates.y - state.originalHeight) - state._referenceCoordinates.y
274
+ };
275
+ if (isCentered && state.activeHandlePosition.endsWith('-right')) {
276
+ enlargement.x = currentCoordinates.x - (state._referenceCoordinates.x + state.originalWidth);
277
+ }
278
+ // Objects needs to be resized twice as much in horizontal axis if centered, since enlargement is counted from
279
+ // one resized corner to your cursor. It needs to be duplicated to compensate for the other side too.
280
+ if (isCentered) {
281
+ enlargement.x *= 2;
282
+ }
283
+ // const resizeHost = this._getResizeHost();
284
+ // The size proposed by the user. It does not consider the aspect ratio.
285
+ let width = Math.abs(state.originalWidth + enlargement.x);
286
+ let height = Math.abs(state.originalHeight + enlargement.y);
287
+ // Dominant determination must take the ratio into account.
288
+ const dominant = width / state.aspectRatio > height ? 'width' : 'height';
289
+ if (dominant == 'width') {
290
+ height = width / state.aspectRatio;
291
+ }
292
+ else {
293
+ width = height * state.aspectRatio;
294
+ }
295
+ return {
296
+ width: Math.round(width),
297
+ height: Math.round(height),
298
+ widthPercents: Math.min(Math.round(state.originalWidthPercents / state.originalWidth * width * 100) / 100, 100)
299
+ };
300
+ }
301
+ /**
302
+ * Obtains the resize host.
303
+ *
304
+ * Resize host is an object that receives dimensions which are the result of resizing.
305
+ */
306
+ _getResizeHost() {
307
+ const widgetWrapper = this._domResizerWrapper.parentElement;
308
+ return this._options.getResizeHost(widgetWrapper);
309
+ }
310
+ /**
311
+ * Obtains the handle host.
312
+ *
313
+ * Handle host is an object that the handles are aligned to.
314
+ *
315
+ * Handle host will not always be an entire widget itself. Take an image as an example. The image widget
316
+ * contains an image and a caption. Only the image should be surrounded with handles.
317
+ */
318
+ _getHandleHost() {
319
+ const widgetWrapper = this._domResizerWrapper.parentElement;
320
+ return this._options.getHandleHost(widgetWrapper);
321
+ }
322
+ /**
323
+ * DOM container of the entire resize UI.
324
+ *
325
+ * Note that this property will have a value only after the element bound with the resizer is rendered
326
+ * (otherwise `null`).
327
+ */
328
+ get _domResizerWrapper() {
329
+ return this._options.editor.editing.view.domConverter.mapViewToDom(this._viewResizerWrapper);
330
+ }
331
+ /**
332
+ * Renders the resize handles in the DOM.
333
+ *
334
+ * @param domElement The resizer wrapper.
335
+ */
336
+ _appendHandles(domElement) {
337
+ const resizerPositions = ['top-left', 'top-right', 'bottom-right', 'bottom-left'];
338
+ for (const currentPosition of resizerPositions) {
339
+ domElement.appendChild((new Template({
340
+ tag: 'div',
341
+ attributes: {
342
+ class: `ck-widget__resizer__handle ${getResizerClass(currentPosition)}`
343
+ }
344
+ }).render()));
345
+ }
346
+ }
347
+ /**
348
+ * Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
349
+ */
350
+ _appendSizeUI(domElement) {
351
+ this._sizeView = new SizeView();
352
+ // Make sure icon#element is rendered before passing to appendChild().
353
+ this._sizeView.render();
354
+ domElement.appendChild(this._sizeView.element);
355
+ }
356
+ }
357
+ /**
358
+ * @param resizerPosition Expected resizer position like `"top-left"`, `"bottom-right"`.
359
+ * @returns A prefixed HTML class name for the resizer element
360
+ */
361
+ function getResizerClass(resizerPosition) {
362
+ return `ck-widget__resizer__handle-${resizerPosition}`;
363
+ }
364
+ function extractCoordinates(event) {
365
+ return {
366
+ x: event.pageX,
367
+ y: event.pageY
368
+ };
369
+ }
370
+ function existsInDom(element) {
371
+ return element && element.ownerDocument && element.ownerDocument.contains(element);
372
+ }