@ckeditor/ckeditor5-widget 39.0.1 → 40.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/LICENSE.md +1 -1
  3. package/README.md +3 -3
  4. package/lang/translations/ar.po +1 -0
  5. package/lang/translations/az.po +1 -0
  6. package/lang/translations/bg.po +1 -0
  7. package/lang/translations/bn.po +1 -0
  8. package/lang/translations/ca.po +1 -0
  9. package/lang/translations/cs.po +1 -0
  10. package/lang/translations/da.po +1 -0
  11. package/lang/translations/de-ch.po +1 -0
  12. package/lang/translations/de.po +1 -0
  13. package/lang/translations/el.po +1 -0
  14. package/lang/translations/en-au.po +1 -0
  15. package/lang/translations/en.po +1 -0
  16. package/lang/translations/es.po +1 -0
  17. package/lang/translations/et.po +1 -0
  18. package/lang/translations/fa.po +1 -0
  19. package/lang/translations/fi.po +1 -0
  20. package/lang/translations/fr.po +1 -0
  21. package/lang/translations/gl.po +1 -0
  22. package/lang/translations/he.po +1 -0
  23. package/lang/translations/hi.po +1 -0
  24. package/lang/translations/hr.po +1 -0
  25. package/lang/translations/hu.po +1 -0
  26. package/lang/translations/id.po +1 -0
  27. package/lang/translations/it.po +1 -0
  28. package/lang/translations/ja.po +1 -0
  29. package/lang/translations/ko.po +1 -0
  30. package/lang/translations/ku.po +1 -0
  31. package/lang/translations/lt.po +1 -0
  32. package/lang/translations/lv.po +1 -0
  33. package/lang/translations/ms.po +1 -0
  34. package/lang/translations/nl.po +1 -0
  35. package/lang/translations/no.po +1 -0
  36. package/lang/translations/pl.po +1 -0
  37. package/lang/translations/pt-br.po +1 -0
  38. package/lang/translations/pt.po +1 -0
  39. package/lang/translations/ro.po +1 -0
  40. package/lang/translations/ru.po +1 -0
  41. package/lang/translations/sk.po +1 -0
  42. package/lang/translations/sq.po +1 -0
  43. package/lang/translations/sr-latn.po +1 -0
  44. package/lang/translations/sr.po +1 -0
  45. package/lang/translations/sv.po +1 -0
  46. package/lang/translations/th.po +1 -0
  47. package/lang/translations/tk.po +1 -0
  48. package/lang/translations/tr.po +1 -0
  49. package/lang/translations/uk.po +1 -0
  50. package/lang/translations/ur.po +1 -0
  51. package/lang/translations/uz.po +1 -0
  52. package/lang/translations/vi.po +1 -0
  53. package/lang/translations/zh-cn.po +1 -0
  54. package/lang/translations/zh.po +1 -0
  55. package/package.json +7 -11
  56. package/src/augmentation.d.ts +13 -13
  57. package/src/augmentation.js +5 -5
  58. package/src/highlightstack.d.ts +74 -74
  59. package/src/highlightstack.js +129 -129
  60. package/src/index.d.ts +13 -13
  61. package/src/index.js +13 -13
  62. package/src/utils.d.ts +198 -198
  63. package/src/utils.js +348 -348
  64. package/src/verticalnavigation.d.ts +15 -15
  65. package/src/verticalnavigation.js +196 -196
  66. package/src/widget.d.ts +91 -91
  67. package/src/widget.js +380 -380
  68. package/src/widgetresize/resizer.d.ts +177 -177
  69. package/src/widgetresize/resizer.js +372 -372
  70. package/src/widgetresize/resizerstate.d.ts +125 -125
  71. package/src/widgetresize/resizerstate.js +150 -150
  72. package/src/widgetresize/sizeview.d.ts +55 -55
  73. package/src/widgetresize/sizeview.js +63 -63
  74. package/src/widgetresize.d.ts +125 -125
  75. package/src/widgetresize.js +188 -188
  76. package/src/widgettoolbarrepository.d.ts +94 -94
  77. package/src/widgettoolbarrepository.js +268 -268
  78. package/src/widgettypearound/utils.d.ts +38 -38
  79. package/src/widgettypearound/utils.js +52 -52
  80. package/src/widgettypearound/widgettypearound.d.ts +229 -229
  81. package/src/widgettypearound/widgettypearound.js +773 -773
@@ -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';
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
+ }