@ckeditor/ckeditor5-widget 35.2.0 → 35.3.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.
- package/package.json +31 -23
- package/src/highlightstack.js +105 -139
- package/src/index.js +0 -1
- package/src/utils.js +127 -181
- package/src/verticalnavigation.js +144 -187
- package/src/widget.js +359 -435
- package/src/widgetresize/resizer.js +412 -505
- package/src/widgetresize/resizerstate.js +154 -176
- package/src/widgetresize/sizeview.js +79 -98
- package/src/widgetresize.js +199 -297
- package/src/widgettoolbarrepository.js +244 -296
- package/src/widgettypearound/utils.js +11 -20
- package/src/widgettypearound/widgettypearound.js +748 -876
@@ -2,528 +2,435 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module widget/widgetresize/resizer
|
8
7
|
*/
|
9
|
-
|
10
8
|
import Template from '@ckeditor/ckeditor5-ui/src/template';
|
11
9
|
import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
|
12
10
|
import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays';
|
13
|
-
|
14
|
-
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
15
|
-
import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
16
|
-
|
11
|
+
import { Observable } from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
17
12
|
import ResizeState from './resizerstate';
|
18
13
|
import SizeView from './sizeview';
|
19
|
-
|
20
14
|
/**
|
21
15
|
* Represents a resizer for a single resizable object.
|
22
16
|
*
|
23
17
|
* @mixes module:utils/observablemixin~ObservableMixin
|
24
18
|
*/
|
25
|
-
export default class Resizer {
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
return this._options.getResizeHost( widgetWrapper );
|
428
|
-
}
|
429
|
-
|
430
|
-
/**
|
431
|
-
* Obtains the handle host.
|
432
|
-
*
|
433
|
-
* Handle host is an object that the handles are aligned to.
|
434
|
-
*
|
435
|
-
* Handle host will not always be an entire widget itself. Take an image as an example. The image widget
|
436
|
-
* contains an image and a caption. Only the image should be surrounded with handles.
|
437
|
-
*
|
438
|
-
* @protected
|
439
|
-
* @returns {HTMLElement}
|
440
|
-
*/
|
441
|
-
_getHandleHost() {
|
442
|
-
const widgetWrapper = this._domResizerWrapper.parentElement;
|
443
|
-
|
444
|
-
return this._options.getHandleHost( widgetWrapper );
|
445
|
-
}
|
446
|
-
|
447
|
-
/**
|
448
|
-
* DOM container of the entire resize UI.
|
449
|
-
*
|
450
|
-
* Note that this property will have a value only after the element bound with the resizer is rendered
|
451
|
-
* (otherwise `null`).
|
452
|
-
*
|
453
|
-
* @private
|
454
|
-
* @member {HTMLElement|null}
|
455
|
-
*/
|
456
|
-
get _domResizerWrapper() {
|
457
|
-
return this._options.editor.editing.view.domConverter.mapViewToDom( this._viewResizerWrapper );
|
458
|
-
}
|
459
|
-
|
460
|
-
/**
|
461
|
-
* Renders the resize handles in the DOM.
|
462
|
-
*
|
463
|
-
* @private
|
464
|
-
* @param {HTMLElement} domElement The resizer wrapper.
|
465
|
-
*/
|
466
|
-
_appendHandles( domElement ) {
|
467
|
-
const resizerPositions = [ 'top-left', 'top-right', 'bottom-right', 'bottom-left' ];
|
468
|
-
|
469
|
-
for ( const currentPosition of resizerPositions ) {
|
470
|
-
domElement.appendChild( ( new Template( {
|
471
|
-
tag: 'div',
|
472
|
-
attributes: {
|
473
|
-
class: `ck-widget__resizer__handle ${ getResizerClass( currentPosition ) }`
|
474
|
-
}
|
475
|
-
} ).render() ) );
|
476
|
-
}
|
477
|
-
}
|
478
|
-
|
479
|
-
/**
|
480
|
-
* Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
|
481
|
-
*
|
482
|
-
* @private
|
483
|
-
* @param {HTMLElement} domElement
|
484
|
-
*/
|
485
|
-
_appendSizeUI( domElement ) {
|
486
|
-
this._sizeView = new SizeView();
|
487
|
-
|
488
|
-
// Make sure icon#element is rendered before passing to appendChild().
|
489
|
-
this._sizeView.render();
|
490
|
-
|
491
|
-
domElement.appendChild( this._sizeView.element );
|
492
|
-
}
|
493
|
-
|
494
|
-
/**
|
495
|
-
* @event begin
|
496
|
-
*/
|
497
|
-
|
498
|
-
/**
|
499
|
-
* @event updateSize
|
500
|
-
*/
|
501
|
-
|
502
|
-
/**
|
503
|
-
* @event commit
|
504
|
-
*/
|
505
|
-
|
506
|
-
/**
|
507
|
-
* @event cancel
|
508
|
-
*/
|
19
|
+
export default class Resizer extends Observable {
|
20
|
+
/**
|
21
|
+
* @param {module:widget/widgetresize~ResizerOptions} options Resizer options.
|
22
|
+
*/
|
23
|
+
constructor(options) {
|
24
|
+
super();
|
25
|
+
/**
|
26
|
+
* Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
|
27
|
+
*
|
28
|
+
* Note that a new state is created for each resize transaction.
|
29
|
+
*
|
30
|
+
* @readonly
|
31
|
+
* @member {module:widget/widgetresize/resizerstate~ResizerState} #state
|
32
|
+
*/
|
33
|
+
/**
|
34
|
+
* A view displaying the proposed new element size during the resizing.
|
35
|
+
*
|
36
|
+
* @protected
|
37
|
+
* @readonly
|
38
|
+
* @member {module:widget/widgetresize/sizeview~SizeView} #_sizeView
|
39
|
+
*/
|
40
|
+
/**
|
41
|
+
* Options passed to the {@link #constructor}.
|
42
|
+
*
|
43
|
+
* @private
|
44
|
+
* @type {module:widget/widgetresize~ResizerOptions}
|
45
|
+
*/
|
46
|
+
this._options = options;
|
47
|
+
/**
|
48
|
+
* A wrapper that is controlled by the resizer. This is usually a widget element.
|
49
|
+
*
|
50
|
+
* @private
|
51
|
+
* @type {module:engine/view/element~Element|null}
|
52
|
+
*/
|
53
|
+
this._viewResizerWrapper = null;
|
54
|
+
/**
|
55
|
+
* The width of the resized {@link module:widget/widgetresize~ResizerOptions#viewElement viewElement} before the resizing started.
|
56
|
+
*
|
57
|
+
* @private
|
58
|
+
* @member {Number|String|undefined} #_initialViewWidth
|
59
|
+
*/
|
60
|
+
/**
|
61
|
+
* Flag that indicates whether resizer can be used.
|
62
|
+
*
|
63
|
+
* @observable
|
64
|
+
*/
|
65
|
+
this.set('isEnabled', true);
|
66
|
+
/**
|
67
|
+
* Flag that indicates that resizer is currently focused.
|
68
|
+
*
|
69
|
+
* @observable
|
70
|
+
*/
|
71
|
+
this.set('isSelected', false);
|
72
|
+
/**
|
73
|
+
* Flag that indicates whether resizer is rendered (visible on the screen).
|
74
|
+
*
|
75
|
+
* @readonly
|
76
|
+
* @observable
|
77
|
+
*/
|
78
|
+
this.bind('isVisible').to(this, 'isEnabled', this, 'isSelected', (isEnabled, isSelected) => isEnabled && isSelected);
|
79
|
+
this.decorate('begin');
|
80
|
+
this.decorate('cancel');
|
81
|
+
this.decorate('commit');
|
82
|
+
this.decorate('updateSize');
|
83
|
+
this.on('commit', event => {
|
84
|
+
// State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
|
85
|
+
// cleaned up (#5195).
|
86
|
+
if (!this.state.proposedWidth && !this.state.proposedWidthPercents) {
|
87
|
+
this._cleanup();
|
88
|
+
event.stop();
|
89
|
+
}
|
90
|
+
}, { priority: 'high' });
|
91
|
+
}
|
92
|
+
get state() {
|
93
|
+
return this._state;
|
94
|
+
}
|
95
|
+
/**
|
96
|
+
* Makes resizer visible in the UI.
|
97
|
+
*/
|
98
|
+
show() {
|
99
|
+
const editingView = this._options.editor.editing.view;
|
100
|
+
editingView.change(writer => {
|
101
|
+
writer.removeClass('ck-hidden', this._viewResizerWrapper);
|
102
|
+
});
|
103
|
+
}
|
104
|
+
/**
|
105
|
+
* Hides resizer in the UI.
|
106
|
+
*/
|
107
|
+
hide() {
|
108
|
+
const editingView = this._options.editor.editing.view;
|
109
|
+
editingView.change(writer => {
|
110
|
+
writer.addClass('ck-hidden', this._viewResizerWrapper);
|
111
|
+
});
|
112
|
+
}
|
113
|
+
/**
|
114
|
+
* Attaches the resizer to the DOM.
|
115
|
+
*/
|
116
|
+
attach() {
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
118
|
+
const that = this;
|
119
|
+
const widgetElement = this._options.viewElement;
|
120
|
+
const editingView = this._options.editor.editing.view;
|
121
|
+
editingView.change(writer => {
|
122
|
+
const viewResizerWrapper = writer.createUIElement('div', {
|
123
|
+
class: 'ck ck-reset_all ck-widget__resizer'
|
124
|
+
}, function (domDocument) {
|
125
|
+
const domElement = this.toDomElement(domDocument);
|
126
|
+
that._appendHandles(domElement);
|
127
|
+
that._appendSizeUI(domElement);
|
128
|
+
return domElement;
|
129
|
+
});
|
130
|
+
// Append the resizer wrapper to the widget's wrapper.
|
131
|
+
writer.insert(writer.createPositionAt(widgetElement, 'end'), viewResizerWrapper);
|
132
|
+
writer.addClass('ck-widget_with-resizer', widgetElement);
|
133
|
+
this._viewResizerWrapper = viewResizerWrapper;
|
134
|
+
if (!this.isVisible) {
|
135
|
+
this.hide();
|
136
|
+
}
|
137
|
+
});
|
138
|
+
this.on('change:isVisible', () => {
|
139
|
+
if (this.isVisible) {
|
140
|
+
this.show();
|
141
|
+
this.redraw();
|
142
|
+
}
|
143
|
+
else {
|
144
|
+
this.hide();
|
145
|
+
}
|
146
|
+
});
|
147
|
+
}
|
148
|
+
/**
|
149
|
+
* Starts the resizing process.
|
150
|
+
*
|
151
|
+
* Creates a new {@link #state} for the current process.
|
152
|
+
*
|
153
|
+
* @fires begin
|
154
|
+
* @param {HTMLElement} domResizeHandle Clicked handle.
|
155
|
+
*/
|
156
|
+
begin(domResizeHandle) {
|
157
|
+
this._state = new ResizeState(this._options);
|
158
|
+
this._sizeView._bindToState(this._options, this.state);
|
159
|
+
this._initialViewWidth = this._options.viewElement.getStyle('width');
|
160
|
+
this.state.begin(domResizeHandle, this._getHandleHost(), this._getResizeHost());
|
161
|
+
}
|
162
|
+
/**
|
163
|
+
* Updates the proposed size based on `domEventData`.
|
164
|
+
*
|
165
|
+
* @fires updateSize
|
166
|
+
* @param {Event} domEventData
|
167
|
+
*/
|
168
|
+
updateSize(domEventData) {
|
169
|
+
const newSize = this._proposeNewSize(domEventData);
|
170
|
+
const editingView = this._options.editor.editing.view;
|
171
|
+
editingView.change(writer => {
|
172
|
+
const unit = this._options.unit || '%';
|
173
|
+
const newWidth = (unit === '%' ? newSize.widthPercents : newSize.width) + unit;
|
174
|
+
writer.setStyle('width', newWidth, this._options.viewElement);
|
175
|
+
});
|
176
|
+
// Get an actual image width, and:
|
177
|
+
// * reflect this size to the resize wrapper
|
178
|
+
// * apply this **real** size to the state
|
179
|
+
const domHandleHost = this._getHandleHost();
|
180
|
+
const domHandleHostRect = new Rect(domHandleHost);
|
181
|
+
const handleHostWidth = Math.round(domHandleHostRect.width);
|
182
|
+
const handleHostHeight = Math.round(domHandleHostRect.height);
|
183
|
+
// Handle max-width limitation.
|
184
|
+
const domResizeHostRect = new Rect(domHandleHost);
|
185
|
+
newSize.width = Math.round(domResizeHostRect.width);
|
186
|
+
newSize.height = Math.round(domResizeHostRect.height);
|
187
|
+
this.redraw(domHandleHostRect);
|
188
|
+
this.state.update({
|
189
|
+
...newSize,
|
190
|
+
handleHostWidth,
|
191
|
+
handleHostHeight
|
192
|
+
});
|
193
|
+
}
|
194
|
+
/**
|
195
|
+
* Applies the geometry proposed with the resizer.
|
196
|
+
*
|
197
|
+
* @fires commit
|
198
|
+
*/
|
199
|
+
commit() {
|
200
|
+
const unit = this._options.unit || '%';
|
201
|
+
const newValue = (unit === '%' ? this.state.proposedWidthPercents : this.state.proposedWidth) + unit;
|
202
|
+
// Both cleanup and onCommit callback are very likely to make view changes. Ensure that it is made in a single step.
|
203
|
+
this._options.editor.editing.view.change(() => {
|
204
|
+
this._cleanup();
|
205
|
+
this._options.onCommit(newValue);
|
206
|
+
});
|
207
|
+
}
|
208
|
+
/**
|
209
|
+
* Cancels and rejects the proposed resize dimensions, hiding the UI.
|
210
|
+
*
|
211
|
+
* @fires cancel
|
212
|
+
*/
|
213
|
+
cancel() {
|
214
|
+
this._cleanup();
|
215
|
+
}
|
216
|
+
/**
|
217
|
+
* Destroys the resizer.
|
218
|
+
*/
|
219
|
+
destroy() {
|
220
|
+
this.cancel();
|
221
|
+
}
|
222
|
+
/**
|
223
|
+
* Redraws the resizer.
|
224
|
+
*
|
225
|
+
* @param {module:utils/dom/rect~Rect} [handleHostRect] Handle host rectangle might be given to improve performance.
|
226
|
+
*/
|
227
|
+
redraw(handleHostRect) {
|
228
|
+
const domWrapper = this._domResizerWrapper;
|
229
|
+
// Refresh only if resizer exists in the DOM.
|
230
|
+
if (!existsInDom(domWrapper)) {
|
231
|
+
return;
|
232
|
+
}
|
233
|
+
const widgetWrapper = domWrapper.parentElement;
|
234
|
+
const handleHost = this._getHandleHost();
|
235
|
+
const resizerWrapper = this._viewResizerWrapper;
|
236
|
+
const currentDimensions = [
|
237
|
+
resizerWrapper.getStyle('width'),
|
238
|
+
resizerWrapper.getStyle('height'),
|
239
|
+
resizerWrapper.getStyle('left'),
|
240
|
+
resizerWrapper.getStyle('top')
|
241
|
+
];
|
242
|
+
let newDimensions;
|
243
|
+
if (widgetWrapper.isSameNode(handleHost)) {
|
244
|
+
const clientRect = handleHostRect || new Rect(handleHost);
|
245
|
+
newDimensions = [
|
246
|
+
clientRect.width + 'px',
|
247
|
+
clientRect.height + 'px',
|
248
|
+
undefined,
|
249
|
+
undefined
|
250
|
+
];
|
251
|
+
}
|
252
|
+
// In case a resizing host is not a widget wrapper, we need to compensate
|
253
|
+
// for any additional offsets the resize host might have. E.g. wrapper padding
|
254
|
+
// or simply another editable. By doing that the border and resizers are shown
|
255
|
+
// only around the resize host.
|
256
|
+
else {
|
257
|
+
newDimensions = [
|
258
|
+
handleHost.offsetWidth + 'px',
|
259
|
+
handleHost.offsetHeight + 'px',
|
260
|
+
handleHost.offsetLeft + 'px',
|
261
|
+
handleHost.offsetTop + 'px'
|
262
|
+
];
|
263
|
+
}
|
264
|
+
// Make changes to the view only if the resizer should actually get new dimensions.
|
265
|
+
// Otherwise, if View#change() was always called, this would cause EditorUI#update
|
266
|
+
// loops because the WidgetResize plugin listens to EditorUI#update and updates
|
267
|
+
// the resizer.
|
268
|
+
// https://github.com/ckeditor/ckeditor5/issues/7633
|
269
|
+
if (compareArrays(currentDimensions, newDimensions) !== 'same') {
|
270
|
+
this._options.editor.editing.view.change(writer => {
|
271
|
+
writer.setStyle({
|
272
|
+
width: newDimensions[0],
|
273
|
+
height: newDimensions[1],
|
274
|
+
left: newDimensions[2],
|
275
|
+
top: newDimensions[3]
|
276
|
+
}, resizerWrapper);
|
277
|
+
});
|
278
|
+
}
|
279
|
+
}
|
280
|
+
containsHandle(domElement) {
|
281
|
+
return this._domResizerWrapper.contains(domElement);
|
282
|
+
}
|
283
|
+
static isResizeHandle(domElement) {
|
284
|
+
return domElement.classList.contains('ck-widget__resizer__handle');
|
285
|
+
}
|
286
|
+
/**
|
287
|
+
* Cleans up the context state.
|
288
|
+
*
|
289
|
+
* @protected
|
290
|
+
*/
|
291
|
+
_cleanup() {
|
292
|
+
this._sizeView._dismiss();
|
293
|
+
const editingView = this._options.editor.editing.view;
|
294
|
+
editingView.change(writer => {
|
295
|
+
writer.setStyle('width', this._initialViewWidth, this._options.viewElement);
|
296
|
+
});
|
297
|
+
}
|
298
|
+
/**
|
299
|
+
* Calculates the proposed size as the resize handles are dragged.
|
300
|
+
*
|
301
|
+
* @private
|
302
|
+
* @param {Event} domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
|
303
|
+
* @returns {Object} return
|
304
|
+
* @returns {Number} return.width Proposed width.
|
305
|
+
* @returns {Number} return.height Proposed height.
|
306
|
+
*/
|
307
|
+
_proposeNewSize(domEventData) {
|
308
|
+
const state = this.state;
|
309
|
+
const currentCoordinates = extractCoordinates(domEventData);
|
310
|
+
const isCentered = this._options.isCentered ? this._options.isCentered(this) : true;
|
311
|
+
// Enlargement defines how much the resize host has changed in a given axis. Naturally it could be a negative number
|
312
|
+
// meaning that it has been shrunk.
|
313
|
+
//
|
314
|
+
// +----------------+--+
|
315
|
+
// | | |
|
316
|
+
// | img | |
|
317
|
+
// | /handle host | |
|
318
|
+
// +----------------+ | ^
|
319
|
+
// | | | - enlarge y
|
320
|
+
// +-------------------+ v
|
321
|
+
// <-->
|
322
|
+
// enlarge x
|
323
|
+
const enlargement = {
|
324
|
+
x: state._referenceCoordinates.x - (currentCoordinates.x + state.originalWidth),
|
325
|
+
y: (currentCoordinates.y - state.originalHeight) - state._referenceCoordinates.y
|
326
|
+
};
|
327
|
+
if (isCentered && state.activeHandlePosition.endsWith('-right')) {
|
328
|
+
enlargement.x = currentCoordinates.x - (state._referenceCoordinates.x + state.originalWidth);
|
329
|
+
}
|
330
|
+
// Objects needs to be resized twice as much in horizontal axis if centered, since enlargement is counted from
|
331
|
+
// one resized corner to your cursor. It needs to be duplicated to compensate for the other side too.
|
332
|
+
if (isCentered) {
|
333
|
+
enlargement.x *= 2;
|
334
|
+
}
|
335
|
+
// const resizeHost = this._getResizeHost();
|
336
|
+
// The size proposed by the user. It does not consider the aspect ratio.
|
337
|
+
let width = Math.abs(state.originalWidth + enlargement.x);
|
338
|
+
let height = Math.abs(state.originalHeight + enlargement.y);
|
339
|
+
// Dominant determination must take the ratio into account.
|
340
|
+
const dominant = width / state.aspectRatio > height ? 'width' : 'height';
|
341
|
+
if (dominant == 'width') {
|
342
|
+
height = width / state.aspectRatio;
|
343
|
+
}
|
344
|
+
else {
|
345
|
+
width = height * state.aspectRatio;
|
346
|
+
}
|
347
|
+
return {
|
348
|
+
width: Math.round(width),
|
349
|
+
height: Math.round(height),
|
350
|
+
widthPercents: Math.min(Math.round(state.originalWidthPercents / state.originalWidth * width * 100) / 100, 100)
|
351
|
+
};
|
352
|
+
}
|
353
|
+
/**
|
354
|
+
* Obtains the resize host.
|
355
|
+
*
|
356
|
+
* Resize host is an object that receives dimensions which are the result of resizing.
|
357
|
+
*
|
358
|
+
* @protected
|
359
|
+
* @returns {HTMLElement}
|
360
|
+
*/
|
361
|
+
_getResizeHost() {
|
362
|
+
const widgetWrapper = this._domResizerWrapper.parentElement;
|
363
|
+
return this._options.getResizeHost(widgetWrapper);
|
364
|
+
}
|
365
|
+
/**
|
366
|
+
* Obtains the handle host.
|
367
|
+
*
|
368
|
+
* Handle host is an object that the handles are aligned to.
|
369
|
+
*
|
370
|
+
* Handle host will not always be an entire widget itself. Take an image as an example. The image widget
|
371
|
+
* contains an image and a caption. Only the image should be surrounded with handles.
|
372
|
+
*
|
373
|
+
* @protected
|
374
|
+
* @returns {HTMLElement}
|
375
|
+
*/
|
376
|
+
_getHandleHost() {
|
377
|
+
const widgetWrapper = this._domResizerWrapper.parentElement;
|
378
|
+
return this._options.getHandleHost(widgetWrapper);
|
379
|
+
}
|
380
|
+
/**
|
381
|
+
* DOM container of the entire resize UI.
|
382
|
+
*
|
383
|
+
* Note that this property will have a value only after the element bound with the resizer is rendered
|
384
|
+
* (otherwise `null`).
|
385
|
+
*
|
386
|
+
* @private
|
387
|
+
* @member {HTMLElement|null}
|
388
|
+
*/
|
389
|
+
get _domResizerWrapper() {
|
390
|
+
return this._options.editor.editing.view.domConverter.mapViewToDom(this._viewResizerWrapper);
|
391
|
+
}
|
392
|
+
/**
|
393
|
+
* Renders the resize handles in the DOM.
|
394
|
+
*
|
395
|
+
* @private
|
396
|
+
* @param {HTMLElement} domElement The resizer wrapper.
|
397
|
+
*/
|
398
|
+
_appendHandles(domElement) {
|
399
|
+
const resizerPositions = ['top-left', 'top-right', 'bottom-right', 'bottom-left'];
|
400
|
+
for (const currentPosition of resizerPositions) {
|
401
|
+
domElement.appendChild((new Template({
|
402
|
+
tag: 'div',
|
403
|
+
attributes: {
|
404
|
+
class: `ck-widget__resizer__handle ${getResizerClass(currentPosition)}`
|
405
|
+
}
|
406
|
+
}).render()));
|
407
|
+
}
|
408
|
+
}
|
409
|
+
/**
|
410
|
+
* Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
|
411
|
+
*
|
412
|
+
* @private
|
413
|
+
* @param {HTMLElement} domElement
|
414
|
+
*/
|
415
|
+
_appendSizeUI(domElement) {
|
416
|
+
this._sizeView = new SizeView();
|
417
|
+
// Make sure icon#element is rendered before passing to appendChild().
|
418
|
+
this._sizeView.render();
|
419
|
+
domElement.appendChild(this._sizeView.element);
|
420
|
+
}
|
509
421
|
}
|
510
|
-
|
511
|
-
mix( Resizer, ObservableMixin );
|
512
|
-
|
513
422
|
// @private
|
514
423
|
// @param {String} resizerPosition Expected resizer position like `"top-left"`, `"bottom-right"`.
|
515
424
|
// @returns {String} A prefixed HTML class name for the resizer element
|
516
|
-
function getResizerClass(
|
517
|
-
|
425
|
+
function getResizerClass(resizerPosition) {
|
426
|
+
return `ck-widget__resizer__handle-${resizerPosition}`;
|
518
427
|
}
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
};
|
428
|
+
function extractCoordinates(event) {
|
429
|
+
return {
|
430
|
+
x: event.pageX,
|
431
|
+
y: event.pageY
|
432
|
+
};
|
525
433
|
}
|
526
|
-
|
527
|
-
|
528
|
-
return element && element.ownerDocument && element.ownerDocument.contains( element );
|
434
|
+
function existsInDom(element) {
|
435
|
+
return element && element.ownerDocument && element.ownerDocument.contains(element);
|
529
436
|
}
|