@ckeditor/ckeditor5-widget 28.0.0 → 30.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.
package/README.md CHANGED
@@ -2,8 +2,8 @@ CKEditor 5 widget API
2
2
  ========================================
3
3
 
4
4
  [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-widget.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-widget)
5
- [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-widget/status.svg)](https://david-dm.org/ckeditor/ckeditor5-widget)
6
- [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-widget/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-widget?type=dev)
5
+ [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
6
+ [![Build Status](https://travis-ci.com/ckeditor/ckeditor5.svg?branch=master)](https://travis-ci.com/ckeditor/ckeditor5)
7
7
 
8
8
  This package implements the widget API for CKEditor 5.
9
9
 
@@ -22,8 +22,8 @@ msgstr "Bară widget"
22
22
 
23
23
  msgctxt "The title displayed when a mouse is over a button that inserts a paragraph before a block."
24
24
  msgid "Insert paragraph before block"
25
- msgstr ""
25
+ msgstr "Inserează un paragraf înaintea blocului"
26
26
 
27
27
  msgctxt "The title displayed when a mouse is over a button that inserts a paragraph after a block."
28
28
  msgid "Insert paragraph after block"
29
- msgstr ""
29
+ msgstr "Inserează un paragraf după bloc"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-widget",
3
- "version": "28.0.0",
3
+ "version": "30.0.0",
4
4
  "description": "Widget API for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -11,29 +11,29 @@
11
11
  ],
12
12
  "main": "src/index.js",
13
13
  "dependencies": {
14
- "@ckeditor/ckeditor5-core": "^28.0.0",
15
- "@ckeditor/ckeditor5-engine": "^28.0.0",
16
- "@ckeditor/ckeditor5-enter": "^28.0.0",
17
- "@ckeditor/ckeditor5-ui": "^28.0.0",
18
- "@ckeditor/ckeditor5-utils": "^28.0.0",
19
- "@ckeditor/ckeditor5-typing": "^28.0.0",
14
+ "@ckeditor/ckeditor5-core": "^30.0.0",
15
+ "@ckeditor/ckeditor5-engine": "^30.0.0",
16
+ "@ckeditor/ckeditor5-enter": "^30.0.0",
17
+ "@ckeditor/ckeditor5-ui": "^30.0.0",
18
+ "@ckeditor/ckeditor5-utils": "^30.0.0",
19
+ "@ckeditor/ckeditor5-typing": "^30.0.0",
20
20
  "lodash-es": "^4.17.15"
21
21
  },
22
22
  "devDependencies": {
23
- "@ckeditor/ckeditor5-basic-styles": "^28.0.0",
24
- "@ckeditor/ckeditor5-block-quote": "^28.0.0",
25
- "@ckeditor/ckeditor5-clipboard": "^28.0.0",
26
- "@ckeditor/ckeditor5-editor-balloon": "^28.0.0",
27
- "@ckeditor/ckeditor5-editor-classic": "^28.0.0",
28
- "@ckeditor/ckeditor5-essentials": "^28.0.0",
29
- "@ckeditor/ckeditor5-heading": "^28.0.0",
30
- "@ckeditor/ckeditor5-horizontal-line": "^28.0.0",
31
- "@ckeditor/ckeditor5-image": "^28.0.0",
32
- "@ckeditor/ckeditor5-link": "^28.0.0",
33
- "@ckeditor/ckeditor5-media-embed": "^28.0.0",
34
- "@ckeditor/ckeditor5-paragraph": "^28.0.0",
35
- "@ckeditor/ckeditor5-table": "^28.0.0",
36
- "@ckeditor/ckeditor5-undo": "^28.0.0"
23
+ "@ckeditor/ckeditor5-basic-styles": "^30.0.0",
24
+ "@ckeditor/ckeditor5-block-quote": "^30.0.0",
25
+ "@ckeditor/ckeditor5-clipboard": "^30.0.0",
26
+ "@ckeditor/ckeditor5-editor-balloon": "^30.0.0",
27
+ "@ckeditor/ckeditor5-editor-classic": "^30.0.0",
28
+ "@ckeditor/ckeditor5-essentials": "^30.0.0",
29
+ "@ckeditor/ckeditor5-heading": "^30.0.0",
30
+ "@ckeditor/ckeditor5-horizontal-line": "^30.0.0",
31
+ "@ckeditor/ckeditor5-image": "^30.0.0",
32
+ "@ckeditor/ckeditor5-link": "^30.0.0",
33
+ "@ckeditor/ckeditor5-media-embed": "^30.0.0",
34
+ "@ckeditor/ckeditor5-paragraph": "^30.0.0",
35
+ "@ckeditor/ckeditor5-table": "^30.0.0",
36
+ "@ckeditor/ckeditor5-undo": "^30.0.0"
37
37
  },
38
38
  "engines": {
39
39
  "node": ">=12.0.0",
@@ -51,6 +51,7 @@
51
51
  "files": [
52
52
  "lang",
53
53
  "src",
54
- "theme"
54
+ "theme",
55
+ "ckeditor5-metadata.json"
55
56
  ]
56
57
  }
package/src/utils.js CHANGED
@@ -7,10 +7,6 @@
7
7
  * @module widget/utils
8
8
  */
9
9
 
10
- import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';
11
-
12
- import global from '@ckeditor/ckeditor5-utils/src/dom/global';
13
- import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
14
10
  import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
15
11
  import toArray from '@ckeditor/ckeditor5-utils/src/toarray';
16
12
 
@@ -124,7 +120,7 @@ export function toWidget( element, writer, options = {} ) {
124
120
  addSelectionHandle( element, writer );
125
121
  }
126
122
 
127
- setHighlightHandling( element, writer, addHighlight, removeHighlight );
123
+ setHighlightHandling( element, writer );
128
124
 
129
125
  return element;
130
126
  }
@@ -171,10 +167,10 @@ function removeHighlight( element, descriptor, writer ) {
171
167
  *
172
168
  * @param {module:engine/view/element~Element} element
173
169
  * @param {module:engine/view/downcastwriter~DowncastWriter} writer
174
- * @param {Function} add
175
- * @param {Function} remove
170
+ * @param {Function} [add]
171
+ * @param {Function} [remove]
176
172
  */
177
- export function setHighlightHandling( element, writer, add, remove ) {
173
+ export function setHighlightHandling( element, writer, add = addHighlight, remove = removeHighlight ) {
178
174
  const stack = new HighlightStack();
179
175
 
180
176
  stack.on( 'change:top', ( evt, data ) => {
@@ -227,6 +223,7 @@ export function getLabel( element ) {
227
223
  * otherwise sets it to `false`,
228
224
  * * adds the `ck-editor__editable` and `ck-editor__nested-editable` CSS classes,
229
225
  * * adds the `ck-editor__nested-editable_focused` CSS class when the editable is focused and removes it when it is blurred.
226
+ * * implements the {@link ~setHighlightHandling view highlight on widget's editable}.
230
227
  *
231
228
  * Similarly to {@link ~toWidget `toWidget()`} this function should be used in `editingDowncast` only and it is usually
232
229
  * used together with {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `elementToElement()`}.
@@ -278,26 +275,28 @@ export function toWidgetEditable( editable, writer ) {
278
275
  }
279
276
  } );
280
277
 
278
+ setHighlightHandling( editable, writer );
279
+
281
280
  return editable;
282
281
  }
283
282
 
284
283
  /**
285
- * Returns a model position which is optimal (in terms of UX) for inserting a widget block.
284
+ * Returns a model range which is optimal (in terms of UX) for inserting a widget block.
286
285
  *
287
- * For instance, if a selection is in the middle of a paragraph, the position before this paragraph
286
+ * For instance, if a selection is in the middle of a paragraph, the collapsed range before this paragraph
288
287
  * will be returned so that it is not split. If the selection is at the end of a paragraph,
289
- * the position after this paragraph will be returned.
288
+ * the collapsed range after this paragraph will be returned.
290
289
  *
291
- * Note: If the selection is placed in an empty block, that block will be returned. If that position
292
- * is then passed to {@link module:engine/model/model~Model#insertContent},
293
- * the block will be fully replaced by the image.
290
+ * Note: If the selection is placed in an empty block, the range in that block will be returned. If that range
291
+ * is then passed to {@link module:engine/model/model~Model#insertContent}, the block will be fully replaced
292
+ * by the inserted widget block.
294
293
  *
295
294
  * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
296
295
  * The selection based on which the insertion position should be calculated.
297
296
  * @param {module:engine/model/model~Model} model Model instance.
298
- * @returns {module:engine/model/position~Position} The optimal position.
297
+ * @returns {module:engine/model/range~Range} The optimal range.
299
298
  */
300
- export function findOptimalInsertionPosition( selection, model ) {
299
+ export function findOptimalInsertionRange( selection, model ) {
301
300
  const selectedElement = selection.getSelectedElement();
302
301
 
303
302
  if ( selectedElement ) {
@@ -306,11 +305,11 @@ export function findOptimalInsertionPosition( selection, model ) {
306
305
  // If the WidgetTypeAround "fake caret" is displayed, use its position for the insertion
307
306
  // to provide the most predictable UX (https://github.com/ckeditor/ckeditor5/issues/7438).
308
307
  if ( typeAroundFakeCaretPosition ) {
309
- return model.createPositionAt( selectedElement, typeAroundFakeCaretPosition );
308
+ return model.createRange( model.createPositionAt( selectedElement, typeAroundFakeCaretPosition ) );
310
309
  }
311
310
 
312
- if ( model.schema.isBlock( selectedElement ) ) {
313
- return model.createPositionAfter( selectedElement );
311
+ if ( model.schema.isObject( selectedElement ) && !model.schema.isInline( selectedElement ) ) {
312
+ return model.createRangeOn( selectedElement );
314
313
  }
315
314
  }
316
315
 
@@ -320,34 +319,21 @@ export function findOptimalInsertionPosition( selection, model ) {
320
319
  // If inserting into an empty block – return position in that block. It will get
321
320
  // replaced with the image by insertContent(). #42.
322
321
  if ( firstBlock.isEmpty ) {
323
- return model.createPositionAt( firstBlock, 0 );
322
+ return model.createRange( model.createPositionAt( firstBlock, 0 ) );
324
323
  }
325
324
 
326
325
  const positionAfter = model.createPositionAfter( firstBlock );
327
326
 
328
327
  // If selection is at the end of the block - return position after the block.
329
328
  if ( selection.focus.isTouching( positionAfter ) ) {
330
- return positionAfter;
329
+ return model.createRange( positionAfter );
331
330
  }
332
331
 
333
332
  // Otherwise return position before the block.
334
- return model.createPositionBefore( firstBlock );
333
+ return model.createRange( model.createPositionBefore( firstBlock ) );
335
334
  }
336
335
 
337
- return selection.focus;
338
- }
339
-
340
- /**
341
- * Checks if the selection is on an object.
342
- *
343
- * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
344
- * @param {module:engine/model/schema~Schema} schema
345
- * @returns {Boolean}
346
- */
347
- export function checkSelectionOnObject( selection, schema ) {
348
- const selectedElement = selection.getSelectedElement();
349
-
350
- return !!selectedElement && schema.isObject( selectedElement );
336
+ return model.createRange( selection.focus );
351
337
  }
352
338
 
353
339
  /**
@@ -409,65 +395,6 @@ export function viewToModelPositionOutsideModelElement( model, viewElementMatche
409
395
  };
410
396
  }
411
397
 
412
- /**
413
- * A positioning function passed to the {@link module:utils/dom/position~getOptimalPosition} helper as a last resort
414
- * when attaching {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon UI} to widgets.
415
- * It comes in handy when a widget is longer than the visual viewport of the web browser and/or upper/lower boundaries
416
- * of a widget are off screen because of the web page scroll.
417
- *
418
- * ┌─┄┄┄┄┄┄┄┄┄Widget┄┄┄┄┄┄┄┄┄┐
419
- * ┊ ┊
420
- * ┌────────────Viewport───────────┐ ┌──╁─────────Viewport────────╁──┐
421
- * │ ┏━━━━━━━━━━Widget━━━━━━━━━┓ │ │ ┃ ^ ┃ │
422
- * │ ┃ ^ ┃ │ │ ┃ ╭───────/ \───────╮ ┃ │
423
- * │ ┃ ╭───────/ \───────╮ ┃ │ │ ┃ │ Balloon │ ┃ │
424
- * │ ┃ │ Balloon │ ┃ │ │ ┃ ╰─────────────────╯ ┃ │
425
- * │ ┃ ╰─────────────────╯ ┃ │ │ ┃ ┃ │
426
- * │ ┃ ┃ │ │ ┃ ┃ │
427
- * │ ┃ ┃ │ │ ┃ ┃ │
428
- * │ ┃ ┃ │ │ ┃ ┃ │
429
- * │ ┃ ┃ │ │ ┃ ┃ │
430
- * │ ┃ ┃ │ │ ┃ ┃ │
431
- * │ ┃ ┃ │ │ ┃ ┃ │
432
- * └──╀─────────────────────────╀──┘ └──╀─────────────────────────╀──┘
433
- * ┊ ┊ ┊ ┊
434
- * ┊ ┊ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
435
- * ┊ ┊
436
- * └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
437
- *
438
- * **Note**: Works best if used together with
439
- * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions default `BalloonPanelView` positions}
440
- * like `northArrowSouth` and `southArrowNorth`; the transition between these two and this position is smooth.
441
- *
442
- * @param {module:utils/dom/rect~Rect} widgetRect A rect of the widget.
443
- * @param {module:utils/dom/rect~Rect} balloonRect A rect of the balloon.
444
- * @returns {module:utils/dom/position~Position|null}
445
- */
446
- export function centeredBalloonPositionForLongWidgets( widgetRect, balloonRect ) {
447
- const viewportRect = new Rect( global.window );
448
- const viewportWidgetInsersectionRect = viewportRect.getIntersection( widgetRect );
449
-
450
- const balloonTotalHeight = balloonRect.height + BalloonPanelView.arrowVerticalOffset;
451
-
452
- // If there is enough space above or below the widget then this position should not be used.
453
- if ( widgetRect.top - balloonTotalHeight > viewportRect.top || widgetRect.bottom + balloonTotalHeight < viewportRect.bottom ) {
454
- return null;
455
- }
456
-
457
- // Because this is a last resort positioning, to keep things simple we're not playing with positions of the arrow
458
- // like, for instance, "south west" or whatever. Just try to keep the balloon in the middle of the visible area of
459
- // the widget for as long as it is possible. If the widgets becomes invisible (because cropped by the viewport),
460
- // just... place the balloon in the middle of it (because why not?).
461
- const targetRect = viewportWidgetInsersectionRect || widgetRect;
462
- const left = targetRect.left + targetRect.width / 2 - balloonRect.width / 2;
463
-
464
- return {
465
- top: Math.max( widgetRect.top, 0 ) + BalloonPanelView.arrowVerticalOffset,
466
- left,
467
- name: 'arrow_n'
468
- };
469
- }
470
-
471
398
  // Default filler offset function applied to all widget elements.
472
399
  //
473
400
  // @returns {null}
@@ -40,15 +40,29 @@ export default function verticalNavigationHandler( editing ) {
40
40
  // Find a range between selection and closest limit element.
41
41
  const range = findTextRangeFromSelection( editing, selection, isForward );
42
42
 
43
- if ( !range || range.isCollapsed ) {
43
+ // There is no selection position inside the limit element.
44
+ if ( !range ) {
44
45
  return;
45
46
  }
46
47
 
48
+ // If already at the edge of a limit element.
49
+ if ( range.isCollapsed ) {
50
+ // A collapsed selection at limit edge - nothing more to do.
51
+ if ( selection.isCollapsed ) {
52
+ return;
53
+ }
54
+
55
+ // A non collapsed selection is at the limit edge while expanding the selection - let others do their stuff.
56
+ else if ( expandSelection ) {
57
+ return;
58
+ }
59
+ }
60
+
47
61
  // If the range is a single line (there is no word wrapping) then move the selection to the position closest to the limit element.
48
62
  //
49
63
  // We can't move the selection directly to the isObject element (eg. table cell) because of dual position at the end/beginning
50
64
  // of wrapped line (it's at the same time at the end of one line and at the start of the next line).
51
- if ( isSingleLineRange( editing, range, isForward ) ) {
65
+ if ( range.isCollapsed || isSingleLineRange( editing, range, isForward ) ) {
52
66
  model.change( writer => {
53
67
  const newPosition = isForward ? range.end : range.start;
54
68
 
@@ -94,7 +108,7 @@ function findTextRangeFromSelection( editing, selection, isForward ) {
94
108
  const range = model.createRange( startPosition, endPosition );
95
109
  const lastRangePosition = getNearestTextPosition( model.schema, range, 'backward' );
96
110
 
97
- if ( lastRangePosition && startPosition.isBefore( lastRangePosition ) ) {
111
+ if ( lastRangePosition ) {
98
112
  return model.createRange( startPosition, lastRangePosition );
99
113
  }
100
114
 
@@ -111,7 +125,7 @@ function findTextRangeFromSelection( editing, selection, isForward ) {
111
125
  const range = model.createRange( startPosition, endPosition );
112
126
  const firstRangePosition = getNearestTextPosition( model.schema, range, 'forward' );
113
127
 
114
- if ( firstRangePosition && endPosition.isAfter( firstRangePosition ) ) {
128
+ if ( firstRangePosition ) {
115
129
  return model.createRange( firstRangePosition, endPosition );
116
130
  }
117
131
 
@@ -152,7 +166,7 @@ function getNearestNonInlineLimit( model, startPosition, direction ) {
152
166
  // @param {module:engine/model/schema~Schema} schema The schema.
153
167
  // @param {module:engine/model/range~Range} range The range to find the position in.
154
168
  // @param {'forward'|'backward'} direction Search direction.
155
- // @returns {module:engine/model/position~Position} The nearest selection range.
169
+ // @returns {module:engine/model/position~Position|null} The nearest selection position.
156
170
  //
157
171
  function getNearestTextPosition( schema, range, direction ) {
158
172
  const position = direction == 'backward' ? range.end : range.start;
@@ -166,6 +180,8 @@ function getNearestTextPosition( schema, range, direction ) {
166
180
  return nextPosition;
167
181
  }
168
182
  }
183
+
184
+ return null;
169
185
  }
170
186
 
171
187
  // Checks if the DOM range corresponding to the provided model range renders as a single line by analyzing DOMRects
package/src/widget.js CHANGED
@@ -11,12 +11,13 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
11
11
  import MouseObserver from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver';
12
12
  import WidgetTypeAround from './widgettypearound/widgettypearound';
13
13
  import Delete from '@ckeditor/ckeditor5-typing/src/delete';
14
- import { getLabel, isWidget, WIDGET_SELECTED_CLASS_NAME } from './utils';
15
- import { isForwardArrowKeyCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
16
14
  import env from '@ckeditor/ckeditor5-utils/src/env';
15
+ import { getLocalizedArrowKeyCodeDirection } from '@ckeditor/ckeditor5-utils/src/keyboard';
17
16
 
18
- import '../theme/widget.css';
19
17
  import verticalNavigationHandler from './verticalnavigation';
18
+ import { getLabel, isWidget, WIDGET_SELECTED_CLASS_NAME } from './utils';
19
+
20
+ import '../theme/widget.css';
20
21
 
21
22
  /**
22
23
  * The widget plugin. It enables base support for widgets.
@@ -52,7 +53,8 @@ export default class Widget extends Plugin {
52
53
  * @inheritDoc
53
54
  */
54
55
  init() {
55
- const view = this.editor.editing.view;
56
+ const editor = this.editor;
57
+ const view = editor.editing.view;
56
58
  const viewDocument = view.document;
57
59
 
58
60
  /**
@@ -64,17 +66,69 @@ export default class Widget extends Plugin {
64
66
  this._previouslySelected = new Set();
65
67
 
66
68
  // Model to view selection converter.
67
- // Converts selection placed over widget element to fake selection
69
+ // Converts selection placed over widget element to fake selection.
70
+ //
71
+ // By default, the selection is downcasted by the engine to surround the attribute element, even though its only
72
+ // child is an inline widget. A similar thing also happens when a collapsed marker is rendered as a UI element
73
+ // next to an inline widget: the view selection contains both the widget and the marker.
74
+ //
75
+ // This prevents creating a correct fake selection when this inline widget is selected. Normalize the selection
76
+ // in these cases based on the model:
77
+ //
78
+ // [<attributeElement><inlineWidget /></attributeElement>] -> <attributeElement>[<inlineWidget />]</attributeElement>
79
+ // [<uiElement></uiElement><inlineWidget />] -> <uiElement></uiElement>[<inlineWidget />]
80
+ //
81
+ // Thanks to this:
82
+ //
83
+ // * fake selection can be set correctly,
84
+ // * any logic depending on (View)Selection#getSelectedElement() also works OK.
85
+ //
86
+ // See https://github.com/ckeditor/ckeditor5/issues/9524.
87
+ this.editor.editing.downcastDispatcher.on( 'selection', ( evt, data, conversionApi ) => {
88
+ const viewWriter = conversionApi.writer;
89
+ const modelSelection = data.selection;
90
+
91
+ // The collapsed selection can't contain any widget.
92
+ if ( modelSelection.isCollapsed ) {
93
+ return;
94
+ }
95
+
96
+ const selectedModelElement = modelSelection.getSelectedElement();
97
+
98
+ if ( !selectedModelElement ) {
99
+ return;
100
+ }
101
+
102
+ const selectedViewElement = editor.editing.mapper.toViewElement( selectedModelElement );
103
+
104
+ if ( !isWidget( selectedViewElement ) ) {
105
+ return;
106
+ }
107
+
108
+ if ( !conversionApi.consumable.consume( modelSelection, 'selection' ) ) {
109
+ return;
110
+ }
111
+
112
+ viewWriter.setSelection( viewWriter.createRangeOn( selectedViewElement ), {
113
+ fake: true,
114
+ label: getLabel( selectedViewElement )
115
+ } );
116
+ } );
117
+
118
+ // Mark all widgets inside the selection with the css class.
119
+ // This handler is registered at the 'low' priority so it's triggered after the real selection conversion.
68
120
  this.editor.editing.downcastDispatcher.on( 'selection', ( evt, data, conversionApi ) => {
69
121
  // Remove selected class from previously selected widgets.
70
122
  this._clearPreviouslySelectedWidgets( conversionApi.writer );
71
123
 
72
124
  const viewWriter = conversionApi.writer;
73
125
  const viewSelection = viewWriter.document.selection;
74
- const selectedElement = viewSelection.getSelectedElement();
126
+
75
127
  let lastMarked = null;
76
128
 
77
129
  for ( const range of viewSelection.getRanges() ) {
130
+ // Note: There could be multiple selected widgets in a range but no fake selection.
131
+ // All of them must be marked as selected, for instance [<widget></widget><widget></widget>]
78
132
  for ( const value of range ) {
79
133
  const node = value.item;
80
134
 
@@ -84,11 +138,6 @@ export default class Widget extends Plugin {
84
138
 
85
139
  this._previouslySelected.add( node );
86
140
  lastMarked = node;
87
-
88
- // Check if widget is a single element selected.
89
- if ( node == selectedElement ) {
90
- viewWriter.setSelection( viewSelection.getRanges(), { fake: true, label: getLabel( selectedElement ) } );
91
- }
92
141
  }
93
142
  }
94
143
  }
@@ -208,7 +257,9 @@ export default class Widget extends Plugin {
208
257
  const schema = model.schema;
209
258
  const modelSelection = model.document.selection;
210
259
  const objectElement = modelSelection.getSelectedElement();
211
- const isForward = isForwardArrowKeyCode( keyCode, this.editor.locale.contentLanguageDirection );
260
+ const direction = getLocalizedArrowKeyCodeDirection( keyCode, this.editor.locale.contentLanguageDirection );
261
+ const isForward = direction == 'down' || direction == 'right';
262
+ const isVerticalNavigation = direction == 'up' || direction == 'down';
212
263
 
213
264
  // If object element is selected.
214
265
  if ( objectElement && schema.isObject( objectElement ) ) {
@@ -227,15 +278,42 @@ export default class Widget extends Plugin {
227
278
  return;
228
279
  }
229
280
 
230
- // If selection is next to object element.
281
+ // Handle collapsing of the selection when there is any widget on the edge of selection.
282
+ // This is needed because browsers have problems with collapsing such selection.
283
+ if ( !modelSelection.isCollapsed && !domEventData.shiftKey ) {
284
+ const firstPosition = modelSelection.getFirstPosition();
285
+ const lastPosition = modelSelection.getLastPosition();
286
+
287
+ const firstSelectedNode = firstPosition.nodeAfter;
288
+ const lastSelectedNode = lastPosition.nodeBefore;
289
+
290
+ if ( firstSelectedNode && schema.isObject( firstSelectedNode ) || lastSelectedNode && schema.isObject( lastSelectedNode ) ) {
291
+ model.change( writer => {
292
+ writer.setSelection( isForward ? lastPosition : firstPosition );
293
+ } );
294
+
295
+ domEventData.preventDefault();
296
+ eventInfo.stop();
297
+ }
298
+
299
+ return;
300
+ }
301
+
231
302
  // Return if not collapsed.
232
303
  if ( !modelSelection.isCollapsed ) {
233
304
  return;
234
305
  }
235
306
 
307
+ // If selection is next to object element.
308
+
236
309
  const objectElementNextToSelection = this._getObjectElementNextToSelection( isForward );
237
310
 
238
311
  if ( objectElementNextToSelection && schema.isObject( objectElementNextToSelection ) ) {
312
+ // Do not select an inline widget while handling up/down arrow.
313
+ if ( schema.isInline( objectElementNextToSelection ) && isVerticalNavigation ) {
314
+ return;
315
+ }
316
+
239
317
  this._setSelectionOverElement( objectElementNextToSelection );
240
318
 
241
319
  domEventData.preventDefault();
@@ -338,6 +416,12 @@ export default class Widget extends Plugin {
338
416
  // to its current state after undo.
339
417
  const probe = model.createSelection( modelSelection );
340
418
  model.modifySelection( probe, { direction: forward ? 'forward' : 'backward' } );
419
+
420
+ // The selection didn't change so there is nothing there.
421
+ if ( probe.isEqual( modelSelection ) ) {
422
+ return null;
423
+ }
424
+
341
425
  const objectElement = forward ? probe.focus.nodeBefore : probe.focus.nodeAfter;
342
426
 
343
427
  if ( !!objectElement && schema.isObject( objectElement ) ) {
@@ -7,7 +7,6 @@
7
7
  * @module widget/widgetresize/resizer
8
8
  */
9
9
 
10
- import View from '@ckeditor/ckeditor5-ui/src/view';
11
10
  import Template from '@ckeditor/ckeditor5-ui/src/template';
12
11
  import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
13
12
  import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays';
@@ -16,6 +15,7 @@ import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
16
15
  import mix from '@ckeditor/ckeditor5-utils/src/mix';
17
16
 
18
17
  import ResizeState from './resizerstate';
18
+ import SizeView from './sizeview';
19
19
 
20
20
  /**
21
21
  * Represents a resizer for a single resizable object.
@@ -41,7 +41,7 @@ export default class Resizer {
41
41
  *
42
42
  * @protected
43
43
  * @readonly
44
- * @member {module:widget/widgetresize/resizer~SizeView} #_sizeUI
44
+ * @member {module:widget/widgetresize/sizeview~SizeView} #_sizeView
45
45
  */
46
46
 
47
47
  /**
@@ -52,17 +52,6 @@ export default class Resizer {
52
52
  */
53
53
  this._options = options;
54
54
 
55
- /**
56
- * Container of the entire resize UI.
57
- *
58
- * Note that this property is initialized only after the element bound with the resizer is drawn
59
- * so it will be a `null` when uninitialized.
60
- *
61
- * @private
62
- * @type {HTMLElement|null}
63
- */
64
- this._domResizerWrapper = null;
65
-
66
55
  /**
67
56
  * A wrapper that is controlled by the resizer. This is usually a widget element.
68
57
  *
@@ -123,8 +112,6 @@ export default class Resizer {
123
112
  that._appendHandles( domElement );
124
113
  that._appendSizeUI( domElement );
125
114
 
126
- that._domResizerWrapper = domElement;
127
-
128
115
  that.on( 'change:isEnabled', ( evt, propName, newValue ) => {
129
116
  domElement.style.display = newValue ? '' : 'none';
130
117
  } );
@@ -153,7 +140,7 @@ export default class Resizer {
153
140
  begin( domResizeHandle ) {
154
141
  this.state = new ResizeState( this._options );
155
142
 
156
- this._sizeUI.bindToState( this._options, this.state );
143
+ this._sizeView._bindToState( this._options, this.state );
157
144
 
158
145
  this._initialViewWidth = this._options.viewElement.getStyle( 'width' );
159
146
 
@@ -307,8 +294,7 @@ export default class Resizer {
307
294
  * @protected
308
295
  */
309
296
  _cleanup() {
310
- this._sizeUI.dismiss();
311
- this._sizeUI.isVisible = false;
297
+ this._sizeView._dismiss();
312
298
 
313
299
  const editingView = this._options.editor.editing.view;
314
300
 
@@ -420,6 +406,19 @@ export default class Resizer {
420
406
  return this._options.getHandleHost( widgetWrapper );
421
407
  }
422
408
 
409
+ /**
410
+ * DOM container of the entire resize UI.
411
+ *
412
+ * Note that this property will have a value only after the element bound with the resizer is rendered
413
+ * (otherwise `null`).
414
+ *
415
+ * @private
416
+ * @member {HTMLElement|null}
417
+ */
418
+ get _domResizerWrapper() {
419
+ return this._options.editor.editing.view.domConverter.mapViewToDom( this._viewResizerWrapper );
420
+ }
421
+
423
422
  /**
424
423
  * Renders the resize handles in the DOM.
425
424
  *
@@ -440,20 +439,18 @@ export default class Resizer {
440
439
  }
441
440
 
442
441
  /**
443
- * Sets up the {@link #_sizeUI} property and adds it to the passed `domElement`.
442
+ * Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
444
443
  *
445
444
  * @private
446
445
  * @param {HTMLElement} domElement
447
446
  */
448
447
  _appendSizeUI( domElement ) {
449
- const sizeUI = new SizeView();
448
+ this._sizeView = new SizeView();
450
449
 
451
450
  // Make sure icon#element is rendered before passing to appendChild().
452
- sizeUI.render();
453
-
454
- this._sizeUI = sizeUI;
451
+ this._sizeView.render();
455
452
 
456
- domElement.appendChild( sizeUI.element );
453
+ domElement.appendChild( this._sizeView.element );
457
454
  }
458
455
 
459
456
  /**
@@ -475,61 +472,6 @@ export default class Resizer {
475
472
 
476
473
  mix( Resizer, ObservableMixin );
477
474
 
478
- /**
479
- * A view displaying the proposed new element size during the resizing.
480
- *
481
- * @extends {module:ui/view~View}
482
- */
483
- class SizeView extends View {
484
- constructor() {
485
- super();
486
-
487
- const bind = this.bindTemplate;
488
-
489
- this.setTemplate( {
490
- tag: 'div',
491
- attributes: {
492
- class: [
493
- 'ck',
494
- 'ck-size-view',
495
- bind.to( 'activeHandlePosition', value => value ? `ck-orientation-${ value }` : '' )
496
- ],
497
- style: {
498
- display: bind.if( 'isVisible', 'none', visible => !visible )
499
- }
500
- },
501
- children: [ {
502
- text: bind.to( 'label' )
503
- } ]
504
- } );
505
- }
506
-
507
- bindToState( options, resizerState ) {
508
- this.bind( 'isVisible' ).to( resizerState, 'proposedWidth', resizerState, 'proposedHeight', ( width, height ) =>
509
- width !== null && height !== null );
510
-
511
- this.bind( 'label' ).to(
512
- resizerState, 'proposedHandleHostWidth',
513
- resizerState, 'proposedHandleHostHeight',
514
- resizerState, 'proposedWidthPercents',
515
- ( width, height, widthPercents ) => {
516
- if ( options.unit === 'px' ) {
517
- return `${ width }×${ height }`;
518
- } else {
519
- return `${ widthPercents }%`;
520
- }
521
- }
522
- );
523
-
524
- this.bind( 'activeHandlePosition' ).to( resizerState );
525
- }
526
-
527
- dismiss() {
528
- this.unbind();
529
- this.isVisible = false;
530
- }
531
- }
532
-
533
475
  // @private
534
476
  // @param {String} resizerPosition Expected resizer position like `"top-left"`, `"bottom-right"`.
535
477
  // @returns {String} A prefixed HTML class name for the resizer element
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ /**
7
+ * @module widget/widgetresize/sizeview
8
+ */
9
+
10
+ import View from '@ckeditor/ckeditor5-ui/src/view';
11
+
12
+ /**
13
+ * A view displaying the proposed new element size during the resizing.
14
+ *
15
+ * @protected
16
+ * @extends {module:ui/view~View}
17
+ */
18
+ export default class SizeView extends View {
19
+ constructor() {
20
+ super();
21
+
22
+ /**
23
+ * The visibility of the view defined based on the existence of the host proposed dimensions.
24
+ *
25
+ * @private
26
+ * @observable
27
+ * @readonly
28
+ * @member {Boolean} #_isVisible
29
+ */
30
+
31
+ /**
32
+ * The text that will be displayed in the `SizeView` child.
33
+ * It can be formatted as the pixel values (e.g. 10x20) or the percentage value (e.g. 10%).
34
+ *
35
+ * @private
36
+ * @observable
37
+ * @readonly
38
+ * @member {Boolean} #_label
39
+ */
40
+
41
+ /**
42
+ * The position of the view defined based on the host size and active handle position.
43
+ *
44
+ * @private
45
+ * @observable
46
+ * @readonly
47
+ * @member {String} #_viewPosition
48
+ */
49
+
50
+ const bind = this.bindTemplate;
51
+
52
+ this.setTemplate( {
53
+ tag: 'div',
54
+ attributes: {
55
+ class: [
56
+ 'ck',
57
+ 'ck-size-view',
58
+ bind.to( '_viewPosition', value => value ? `ck-orientation-${ value }` : '' )
59
+ ],
60
+ style: {
61
+ display: bind.if( '_isVisible', 'none', visible => !visible )
62
+ }
63
+ },
64
+ children: [ {
65
+ text: bind.to( '_label' )
66
+ } ]
67
+ } );
68
+ }
69
+
70
+ /**
71
+ * A method used for binding the `SizeView` instance properties to the `ResizeState` instance observable properties.
72
+ *
73
+ * @protected
74
+ * @param {module:widget/widgetresize~ResizerOptions} options
75
+ * An object defining the resizer options, used for setting the proper size label.
76
+ * @param {module:widget/widgetresize/resizerstate~ResizeState} resizeState
77
+ * The `ResizeState` class instance, used for keeping the `SizeView` state up to date.
78
+ */
79
+ _bindToState( options, resizeState ) {
80
+ this.bind( '_isVisible' ).to( resizeState, 'proposedWidth', resizeState, 'proposedHeight', ( width, height ) =>
81
+ width !== null && height !== null );
82
+
83
+ this.bind( '_label' ).to(
84
+ resizeState, 'proposedHandleHostWidth',
85
+ resizeState, 'proposedHandleHostHeight',
86
+ resizeState, 'proposedWidthPercents',
87
+ ( width, height, widthPercents ) => {
88
+ if ( options.unit === 'px' ) {
89
+ return `${ width }×${ height }`;
90
+ } else {
91
+ return `${ widthPercents }%`;
92
+ }
93
+ }
94
+ );
95
+
96
+ this.bind( '_viewPosition' ).to(
97
+ resizeState, 'activeHandlePosition',
98
+ resizeState, 'proposedHandleHostWidth',
99
+ resizeState, 'proposedHandleHostHeight',
100
+ // If the widget is too small to contain the size label, display the label above.
101
+ ( position, width, height ) => width < 50 || height < 50 ? 'above-center' : position
102
+ );
103
+ }
104
+
105
+ /**
106
+ * A method used for cleaning up. It removes the bindings and hides the view.
107
+ *
108
+ * @protected
109
+ */
110
+ _dismiss() {
111
+ this.unbind();
112
+ this._isVisible = false;
113
+ }
114
+ }
@@ -38,6 +38,9 @@ export default class WidgetResize extends Plugin {
38
38
  * @inheritDoc
39
39
  */
40
40
  init() {
41
+ const editing = this.editor.editing;
42
+ const domDocument = global.window.document;
43
+
41
44
  /**
42
45
  * The currently visible resizer.
43
46
  *
@@ -60,22 +63,16 @@ export default class WidgetResize extends Plugin {
60
63
  /**
61
64
  * A map of resizers created using this plugin instance.
62
65
  *
63
- * @private
66
+ * @protected
64
67
  * @type {Map.<module:engine/view/containerelement~ContainerElement, module:widget/widgetresize/resizer~Resizer>}
65
68
  */
66
69
  this._resizers = new Map();
67
70
 
68
- const domDocument = global.window.document;
69
-
70
- this.editor.model.schema.setAttributeProperties( 'width', {
71
- isFormatting: true
72
- } );
73
-
74
- this.editor.editing.view.addObserver( MouseObserver );
71
+ editing.view.addObserver( MouseObserver );
75
72
 
76
73
  this._observer = Object.create( DomEmitterMixin );
77
74
 
78
- this.listenTo( this.editor.editing.view.document, 'mousedown', this._mouseDownListener.bind( this ), { priority: 'high' } );
75
+ this.listenTo( editing.view.document, 'mousedown', this._mouseDownListener.bind( this ), { priority: 'high' } );
79
76
 
80
77
  this._observer.listenTo( domDocument, 'mousemove', this._mouseMoveListener.bind( this ) );
81
78
  this._observer.listenTo( domDocument, 'mouseup', this._mouseUpListener.bind( this ) );
@@ -95,6 +92,18 @@ export default class WidgetResize extends Plugin {
95
92
  // Redrawing on any change of the UI of the editor (including content changes).
96
93
  this.editor.ui.on( 'update', this._redrawFocusedResizerThrottled );
97
94
 
95
+ // Remove view widget-resizer mappings for widgets that have been removed from the document.
96
+ // https://github.com/ckeditor/ckeditor5/issues/10156
97
+ // https://github.com/ckeditor/ckeditor5/issues/10266
98
+ this.editor.model.document.on( 'change', () => {
99
+ for ( const [ viewElement, resizer ] of this._resizers ) {
100
+ if ( !viewElement.isAttached() ) {
101
+ this._resizers.delete( viewElement );
102
+ resizer.destroy();
103
+ }
104
+ }
105
+ }, { priority: 'lowest' } );
106
+
98
107
  // Resizers need to be redrawn upon window resize, because new window might shrink resize host.
99
108
  this._observer.listenTo( global.window, 'resize', this._redrawFocusedResizerThrottled );
100
109
 
@@ -11,10 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
11
11
  import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
12
12
  import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
13
13
  import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';
14
- import {
15
- isWidget,
16
- centeredBalloonPositionForLongWidgets
17
- } from './utils';
14
+ import { isWidget } from './utils';
18
15
  import CKEditorError, { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
19
16
 
20
17
  /**
@@ -36,7 +33,7 @@ import CKEditorError, { logWarning } from '@ckeditor/ckeditor5-utils/src/ckedito
36
33
  *
37
34
  * widgetToolbarRepository.register( 'image', {
38
35
  * items: editor.config.get( 'image.toolbar' ),
39
- * getRelatedElement: getSelectedImageWidget
36
+ * getRelatedElement: getClosestSelectedImageWidget
40
37
  * } );
41
38
  * }
42
39
  * }
@@ -289,7 +286,7 @@ function getBalloonPositionData( editor, relatedElement ) {
289
286
  defaultPositions.southArrowNorth,
290
287
  defaultPositions.southArrowNorthWest,
291
288
  defaultPositions.southArrowNorthEast,
292
- centeredBalloonPositionForLongWidgets
289
+ defaultPositions.viewportStickyNorth
293
290
  ]
294
291
  };
295
292
  }
@@ -398,6 +398,10 @@ export default class WidgetTypeAround extends Plugin {
398
398
  else if ( modelSelection.isCollapsed ) {
399
399
  shouldStopAndPreventDefault = this._handleArrowKeyPressWhenSelectionNextToAWidget( isForward );
400
400
  }
401
+ // Handle collapsing a non-collapsed selection that is wider than on a single widget.
402
+ else if ( !domEventData.shiftKey ) {
403
+ shouldStopAndPreventDefault = this._handleArrowKeyPressWhenNonCollapsedSelection( isForward );
404
+ }
401
405
 
402
406
  if ( shouldStopAndPreventDefault ) {
403
407
  domEventData.preventDefault();
@@ -492,6 +496,42 @@ export default class WidgetTypeAround extends Plugin {
492
496
  return false;
493
497
  }
494
498
 
499
+ /**
500
+ * Handles the keyboard navigation on "keydown" when a widget is currently selected (together with some other content)
501
+ * and the widget is the first or last element in the selection. It activates or deactivates the fake caret for that widget.
502
+ *
503
+ * @private
504
+ * @param {Boolean} isForward `true` when the pressed arrow key was responsible for the forward model selection movement
505
+ * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
506
+ * @returns {Boolean} Returns `true` when the keypress was handled and no other keydown listener of the editor should
507
+ * process the event any further. Returns `false` otherwise.
508
+ */
509
+ _handleArrowKeyPressWhenNonCollapsedSelection( isForward ) {
510
+ const editor = this.editor;
511
+ const model = editor.model;
512
+ const schema = model.schema;
513
+ const mapper = editor.editing.mapper;
514
+ const modelSelection = model.document.selection;
515
+
516
+ const selectedModelNode = isForward ?
517
+ modelSelection.getLastPosition().nodeBefore :
518
+ modelSelection.getFirstPosition().nodeAfter;
519
+
520
+ const selectedViewNode = mapper.toViewElement( selectedModelNode );
521
+
522
+ // There is a widget at the collapse position so collapse the selection to the fake caret on it.
523
+ if ( isTypeAroundWidget( selectedViewNode, selectedModelNode, schema ) ) {
524
+ model.change( writer => {
525
+ writer.setSelection( selectedModelNode, 'on' );
526
+ writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, isForward ? 'after' : 'before' );
527
+ } );
528
+
529
+ return true;
530
+ }
531
+
532
+ return false;
533
+ }
534
+
495
535
  /**
496
536
  * Registers a `mousedown` listener for the view document which intercepts events
497
537
  * coming from the widget type around UI, which happens when a user clicks one of the buttons
package/theme/widget.css CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  --ck-resizer-border-radius: var(--ck-border-radius);
12
12
  --ck-resizer-tooltip-offset: 10px;
13
+ --ck-resizer-tooltip-height: calc(var(--ck-spacing-small) * 2 + 10px);
13
14
  }
14
15
 
15
16
  .ck .ck-widget {
@@ -49,12 +50,15 @@
49
50
  border-radius: var(--ck-resizer-border-radius);
50
51
  font-size: var(--ck-font-size-tiny);
51
52
  display: block;
52
- padding: var(--ck-spacing-small);
53
+ padding: 0 var(--ck-spacing-small);
54
+ height: var(--ck-resizer-tooltip-height);
55
+ line-height: var(--ck-resizer-tooltip-height);
53
56
 
54
57
  &.ck-orientation-top-left,
55
58
  &.ck-orientation-top-right,
56
59
  &.ck-orientation-bottom-right,
57
- &.ck-orientation-bottom-left {
60
+ &.ck-orientation-bottom-left,
61
+ &.ck-orientation-above-center {
58
62
  position: absolute;
59
63
  }
60
64
 
@@ -77,4 +81,11 @@
77
81
  bottom: var(--ck-resizer-tooltip-offset);
78
82
  left: var(--ck-resizer-tooltip-offset);
79
83
  }
84
+
85
+ /* Class applied if the widget is too small to contain the size label */
86
+ &.ck-orientation-above-center {
87
+ top: calc(var(--ck-resizer-tooltip-height) * -1);
88
+ left: 50%;
89
+ transform: translate(-50%);
90
+ }
80
91
  }
package/CHANGELOG.md DELETED
@@ -1,260 +0,0 @@
1
- Changelog
2
- =========
3
-
4
- All changes in the package are documented in the main repository. See: https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md.
5
-
6
- Changes for the past releases are available below.
7
-
8
- ## [19.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v18.0.0...v19.0.0) (2020-04-29)
9
-
10
- ### MINOR BREAKING CHANGES
11
-
12
- * Make sure the latest version of the [`Essentials`](https://ckeditor.com/docs/ckeditor5/latest/api/essentials.html) plugin or the [`SelectAll`](https://ckeditor.com/docs/ckeditor5/latest/api/module_select-all_selectall-SelectAll.html) plugin is installed in your integration. Either is required for proper keystroke handling in editor widgets.
13
-
14
- ### Bug fixes
15
-
16
- * Image resize now cleans up temporary view `width` style changes. Closes [ckeditor/ckeditor5#6060](https://github.com/ckeditor/ckeditor5/issues/6060). ([92226f9](https://github.com/ckeditor/ckeditor5-widget/commit/92226f9))
17
-
18
- ### Other changes
19
-
20
- * Moved the <kbd>Ctrl</kbd>+<kbd>A</kbd> keystroke handling in widgets to the [`SelectAll`](https://ckeditor.com/docs/ckeditor5/latest/api/module_select-all_selectall-SelectAll.html) plugin (see [ckeditor/ckeditor5#6536](https://github.com/ckeditor/ckeditor5/issues/6536)). ([57eb263](https://github.com/ckeditor/ckeditor5-widget/commit/57eb263))
21
- * Updated translations. ([79d1a21](https://github.com/ckeditor/ckeditor5-widget/commit/79d1a21))
22
-
23
-
24
- ## [18.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v17.0.0...v18.0.0) (2020-03-19)
25
-
26
- Internal changes only (updated dependencies, documentation, etc.).
27
-
28
-
29
- ## [17.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v16.0.0...v17.0.0) (2020-02-19)
30
-
31
- ### MINOR BREAKING CHANGES
32
-
33
- * Resizer options object now also takes an editor instance.
34
-
35
- ### Features
36
-
37
- * Introduced API to temporarily disable the `WidgetToolbarRepository` plugin (prevent the toolbar from showing up). Closes [ckeditor/ckeditor5#5964](https://github.com/ckeditor/ckeditor5/issues/5964). ([b9cf062](https://github.com/ckeditor/ckeditor5-widget/commit/b9cf062))
38
-
39
- ### Bug fixes
40
-
41
- * Fixed image resize behavior upon short clicking a handle without dragging. Image will no longer became full width, nor will it briefly flash an unexpected size. Closes [ckeditor/ckeditor5#5189](https://github.com/ckeditor/ckeditor5/issues/5189). Closes [ckeditor/ckeditor5#5195](https://github.com/ckeditor/ckeditor5/issues/5195). ([d6a5c93](https://github.com/ckeditor/ckeditor5-widget/commit/d6a5c93))
42
-
43
- ### Other changes
44
-
45
- * Align code to changes in `Plugin` API. ([81bb636](https://github.com/ckeditor/ckeditor5-widget/commit/81bb636))
46
- * Updated translations. ([75b8c83](https://github.com/ckeditor/ckeditor5-widget/commit/75b8c83))
47
-
48
-
49
- ## [16.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v15.0.0...v16.0.0) (2019-12-04)
50
-
51
- ### Other changes
52
-
53
- * Updated translations. ([b3bf5f0](https://github.com/ckeditor/ckeditor5-widget/commit/b3bf5f0))
54
-
55
-
56
- ## [15.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v11.1.0...v15.0.0) (2019-10-23)
57
-
58
- ### MAJOR BREAKING CHANGES
59
-
60
- * The `drag-handler.svg` icon is now `drag-handle.svg`. If you use it in your integration, please update the path.
61
- * The `hasSelectionHandler` option of the [`toWidget()`](https://ckeditor.com/docs/ckeditor5/latest/api/module_widget_utils.html#static-function-toWidget) utility has been renamed to `hasSelectionHandle`. Consider this change if you create your own widgets using this helper.
62
- * `.ck-widget__selection-handler` and `.ck-widget_with-selection-handler` CSS classes set on widgets have been renamed to `.ck-widget__selection-handle` and `.ck-widget_with-selection-handle`. This change may affect styling in your integration.
63
-
64
- ### Bug fixes
65
-
66
- * Initial resize of a side image with no width predefined now gives correct percentage values. ([6c2c52e](https://github.com/ckeditor/ckeditor5-widget/commit/6c2c52e))
67
- * Keyboard navigation should work around widgets in RTL content. Closes [#97](https://github.com/ckeditor/ckeditor5-widget/issues/97). ([dfbf88d](https://github.com/ckeditor/ckeditor5-widget/commit/dfbf88d))
68
-
69
- ### Other changes
70
-
71
- * Improved the resizer performance. Closes [ckeditor/ckeditor5#5191](https://github.com/ckeditor/ckeditor5/issues/5191). ([1d1de77](https://github.com/ckeditor/ckeditor5-widget/commit/1d1de77))
72
- * Renamed "handler" to "handle" in the entire package. Closes [#99](https://github.com/ckeditor/ckeditor5-widget/issues/99). ([1d35884](https://github.com/ckeditor/ckeditor5-widget/commit/1d35884))
73
- * Updated translations. ([b9cb673](https://github.com/ckeditor/ckeditor5-widget/commit/b9cb673)) ([daea4f5](https://github.com/ckeditor/ckeditor5-widget/commit/daea4f5))
74
-
75
-
76
- ## [11.1.0](https://github.com/ckeditor/ckeditor5-widget/compare/v11.0.4...v11.1.0) (2019-08-26)
77
-
78
- ### Features
79
-
80
- * Introduced image widget resizer. See [ckeditor/ckeditor5-image#241](https://github.com/ckeditor/ckeditor5-image/issues/241). ([c84cd73](https://github.com/ckeditor/ckeditor5-widget/commit/c84cd73))
81
-
82
- ### Bug fixes
83
-
84
- * Improved balloon positioning when there is more than one stack in the rotator. ([763c9ba](https://github.com/ckeditor/ckeditor5-widget/commit/763c9ba))
85
- * Reposition visible toolbar when it is in a not visible stack of rotator. Closes [ckeditor/ckeditor5#1957](https://github.com/ckeditor/ckeditor5/issues/1957). ([a438c8b](https://github.com/ckeditor/ckeditor5-widget/commit/a438c8b))
86
-
87
- ### Other changes
88
-
89
- * The issue tracker for this package was moved to https://github.com/ckeditor/ckeditor5/issues. See [ckeditor/ckeditor5#1988](https://github.com/ckeditor/ckeditor5/issues/1988). ([cfd41c1](https://github.com/ckeditor/ckeditor5-widget/commit/cfd41c1))
90
- * The widget toolbar should have a proper `aria-label` attribute (see [ckeditor/ckeditor5#1404](https://github.com/ckeditor/ckeditor5/issues/1404)). ([aec5888](https://github.com/ckeditor/ckeditor5-widget/commit/aec5888))
91
-
92
-
93
- ## [11.0.4](https://github.com/ckeditor/ckeditor5-widget/compare/v11.0.3...v11.0.4) (2019-07-10)
94
-
95
- Internal changes only (updated dependencies, documentation, etc.).
96
-
97
-
98
- ## [11.0.3](https://github.com/ckeditor/ckeditor5-widget/compare/v11.0.2...v11.0.3) (2019-07-04)
99
-
100
- ### Bug fixes
101
-
102
- * A proper `DomConverter` method should be used to map a view to DOM when getting balloon position data. Closes [#87](https://github.com/ckeditor/ckeditor5-widget/issues/87). ([160333a](https://github.com/ckeditor/ckeditor5-widget/commit/160333a))
103
-
104
-
105
- ## [11.0.2](https://github.com/ckeditor/ckeditor5-widget/compare/v11.0.1...v11.0.2) (2019-06-05)
106
-
107
- Internal changes only (updated dependencies, documentation, etc.).
108
-
109
-
110
- ## [11.0.1](https://github.com/ckeditor/ckeditor5-widget/compare/v11.0.0...v11.0.1) (2019-04-10)
111
-
112
- ### Bug fixes
113
-
114
- * Triple clicking inside a nested editable should not select the entire widget in Safari. Closes [ckeditor/ckeditor5#1463](https://github.com/ckeditor/ckeditor5/issues/1463). ([b7c4765](https://github.com/ckeditor/ckeditor5-widget/commit/b7c4765))
115
-
116
-
117
- ## [11.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v10.3.1...v11.0.0) (2019-02-28)
118
-
119
- ### Bug fixes
120
-
121
- * Editor crashes after <kbd>Enter</kbd> key on an image that is inside a blockquote. Closes [ckeditor/ckeditor5#1555](https://github.com/ckeditor/ckeditor5/issues/1555). ([8a8842b](https://github.com/ckeditor/ckeditor5-widget/commit/8a8842b))
122
- * Ensured only the widget toolbar attached to the view element which is deepest in the view tree will show up. Code and documentation refactoring in the `WidgetToolbarRepository`. Closes [#60](https://github.com/ckeditor/ckeditor5-widget/issues/60). ([7e11a24](https://github.com/ckeditor/ckeditor5-widget/commit/7e11a24))
123
- * Make widget in editable clickable. Closes [ckeditor/ckeditor5-table#98](https://github.com/ckeditor/ckeditor5-table/issues/98). ([8226829](https://github.com/ckeditor/ckeditor5-widget/commit/8226829))
124
- * Pressing <kbd>Enter</kbd> should split parent element when the inline widget is inside a `$block`. Closes [ckeditor/ckeditor5#1529](https://github.com/ckeditor/ckeditor5/issues/1529). ([847d2ab](https://github.com/ckeditor/ckeditor5-widget/commit/847d2ab))
125
- * Fixed memory leaks during editor initialization and destruction (see [ckeditor/ckeditor5#1341](https://github.com/ckeditor/ckeditor5/issues/1341)). ([2e8f20d](https://github.com/ckeditor/ckeditor5-widget/commit/2e8f20d))
126
-
127
- ### Other changes
128
-
129
- * Introduce support and utils for creating inline widgets. Closes [[ckeditor/ckeditor5#1096](https://github.com/ckeditor/ckeditor5/issues/1096)](https://github.com/ckeditor/ckeditor5/issues/1096). ([38fa159](https://github.com/ckeditor/ckeditor5-widget/commit/38fa159))
130
- * Renamed the `.ck-widget_selectable` class to `.ck-widget_with-selection-handler` for better semantics. Closes [#66](https://github.com/ckeditor/ckeditor5-widget/issues/66). ([178ad5f](https://github.com/ckeditor/ckeditor5-widget/commit/178ad5f))
131
-
132
- ### BREAKING CHANGES
133
-
134
- * Upgraded minimal versions of Node to `8.0.0` and npm to `5.7.1`. See: [ckeditor/ckeditor5#1507](https://github.com/ckeditor/ckeditor5/issues/1507). ([612ea3c](https://github.com/ckeditor/ckeditor5-cloud-services/commit/612ea3c))
135
- * The `.ck-widget_selectable` class has been renamed to `.ck-widget_with-selection-handler` for better semantics.
136
- * The `visibleWhen()` function, a property of an object passed into `WidgetToolbarRepository.register()`, has been renamed to `getRelatedElement()` and must return an editing `View` element the toolbar should be attached to (instead of `Boolean`).
137
-
138
-
139
- ## [10.3.1](https://github.com/ckeditor/ckeditor5-widget/compare/v10.3.0...v10.3.1) (2018-12-05)
140
-
141
- ### Bug fixes
142
-
143
- * Selection converter will mark only the topmost widget in case of selecting a widget with another widget nested inside it. Closes [#57](https://github.com/ckeditor/ckeditor5-widget/issues/57). ([a78efec](https://github.com/ckeditor/ckeditor5-widget/commit/a78efec))
144
-
145
- ### Other changes
146
-
147
- * Improved SVG icons size. See [ckeditor/ckeditor5-theme-lark#206](https://github.com/ckeditor/ckeditor5-theme-lark/issues/206). ([5b7a457](https://github.com/ckeditor/ckeditor5-widget/commit/5b7a457))
148
-
149
-
150
- ## [10.3.0](https://github.com/ckeditor/ckeditor5-widget/compare/v10.2.0...v10.3.0) (2018-10-08)
151
-
152
- ### Features
153
-
154
- * Introduced the `findOptimalInsertionPostion()` utility function. ([9c0d4ce](https://github.com/ckeditor/ckeditor5-widget/commit/9c0d4ce))
155
- * Introduced the widget toolbar repository. Closes [ckeditor/ckeditor5-ui#442](https://github.com/ckeditor/ckeditor5-ui/issues/442). ([bc45176](https://github.com/ckeditor/ckeditor5-widget/commit/bc45176))
156
-
157
-
158
- ## [10.2.0](https://github.com/ckeditor/ckeditor5-widget/compare/v10.1.0...v10.2.0) (2018-07-18)
159
-
160
- ### Features
161
-
162
- * Implemented the widget selection handle. Closes [#40](https://github.com/ckeditor/ckeditor5-widget/issues/40). ([bbf9298](https://github.com/ckeditor/ckeditor5-widget/commit/bbf9298))
163
-
164
- ### Other changes
165
-
166
- * Do not set the `contenteditable` property for widgets and their nested editables on Edge due to an awful instability which it causes in this browser. Closes [ckeditor/ckeditor5#1079](https://github.com/ckeditor/ckeditor5/issues/1079). Closes [ckeditor/ckeditor5#1067](https://github.com/ckeditor/ckeditor5/issues/1067). ([ee530b1](https://github.com/ckeditor/ckeditor5-widget/commit/ee530b1))
167
-
168
-
169
- ## [10.1.0](https://github.com/ckeditor/ckeditor5-widget/compare/v10.0.0...v10.1.0) (2018-06-21)
170
-
171
- ### Features
172
-
173
- * Creating a paragraph next to the selected widget is possible using the (<kbd>Shift</kbd>+)<kbd>Enter</kbd> key (see [ckeditor/ckeditor5#407](https://github.com/ckeditor/ckeditor5/issues/407)). ([d68b7d0](https://github.com/ckeditor/ckeditor5-widget/commit/d68b7d0))
174
-
175
-
176
- ## [10.0.0](https://github.com/ckeditor/ckeditor5-widget/compare/v1.0.0-beta.4...v10.0.0) (2018-04-25)
177
-
178
- ### Other changes
179
-
180
- * Changed the license to GPL2+ only. See [ckeditor/ckeditor5#991](https://github.com/ckeditor/ckeditor5/issues/991). ([88ef879](https://github.com/ckeditor/ckeditor5-widget/commit/88ef879))
181
-
182
- ### BREAKING CHANGES
183
-
184
- * The license under which CKEditor 5 is released has been changed from a triple GPL, LGPL and MPL license to a GPL2+ only. See [ckeditor/ckeditor5#991](https://github.com/ckeditor/ckeditor5/issues/991) for more information.
185
-
186
-
187
- ## [1.0.0-beta.4](https://github.com/ckeditor/ckeditor5-widget/compare/v1.0.0-beta.2...v1.0.0-beta.4) (2018-04-19)
188
-
189
- Internal changes only (updated dependencies, documentation, etc.).
190
-
191
-
192
- ## [1.0.0-beta.2](https://github.com/ckeditor/ckeditor5-widget/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2018-04-10)
193
-
194
- ### Bug fixes
195
-
196
- * Replaced nested editable's `.ck-editable` class with `.ck-editor__editable` + `.ck-editor__nested-editable` to stop Grammarly throwing errors. Closes [ckeditor/ckeditor5#578](https://github.com/ckeditor/ckeditor5/issues/578). ([051b326](https://github.com/ckeditor/ckeditor5-widget/commit/051b326))
197
-
198
- ### Other changes
199
-
200
- * Increased the specificity of CSS rules. Introduced the .ck class for editor UI components (see: [ckeditor/ckeditor5#494](https://github.com/ckeditor/ckeditor5/issues/494)). ([abc7def](https://github.com/ckeditor/ckeditor5-widget/commit/abc7def))
201
-
202
- ### BREAKING CHANGES
203
-
204
- * The `.ck-editable` class is no longer available. Use the `.ck-editor__nested-editable` class instead.
205
-
206
-
207
- ## [1.0.0-beta.1](https://github.com/ckeditor/ckeditor5-widget/compare/v1.0.0-alpha.2...v1.0.0-beta.1) (2018-03-15)
208
-
209
- ### Other changes
210
-
211
- * Aligned feature class naming to the new scheme. ([23991a4](https://github.com/ckeditor/ckeditor5-widget/commit/23991a4))
212
- * Migrated package styles to PostCSS. Moved visual styles to `@ckeditor/ckeditor5-theme-lark` (see [ckeditor/ckeditor5-ui#144](https://github.com/ckeditor/ckeditor5-ui/issues/144)). ([857d6d4](https://github.com/ckeditor/ckeditor5-widget/commit/857d6d4))
213
- * Switched to handling deletion around widgets by using the `delete` event instead of listening directly on key events. Closes [#29](https://github.com/ckeditor/ckeditor5-widget/issues/29). ([ee6cc95](https://github.com/ckeditor/ckeditor5-widget/commit/ee6cc95))
214
-
215
-
216
- ## [1.0.0-alpha.2](https://github.com/ckeditor/ckeditor5-widget/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2017-11-14)
217
-
218
- ### Bug fixes
219
-
220
- * The <kbd>Ctrl</kbd>+<kbd>A</kbd> keystroke will be now correctly handled when a widget is selected. Closes [#23](https://github.com/ckeditor/ckeditor5-widget/issues/23). ([3e8f91f](https://github.com/ckeditor/ckeditor5-widget/commit/3e8f91f))
221
- * View element's `setAttribute()` method should be used with string values of the `contenteditable` attribute. Closes [#26](https://github.com/ckeditor/ckeditor5-widget/issues/26). ([d2a6cf5](https://github.com/ckeditor/ckeditor5-widget/commit/d2a6cf5))
222
-
223
- ### Other changes
224
-
225
- * Widgets highlight remove handler will now use only descriptor id, instead of the full descriptor. ([1dfdc83](https://github.com/ckeditor/ckeditor5-widget/commit/1dfdc83))
226
-
227
-
228
- ## [1.0.0-alpha.1](https://github.com/ckeditor/ckeditor5-widget/compare/v0.2.0...v1.0.0-alpha.1) (2017-10-03)
229
-
230
- ### Bug fixes
231
-
232
- * <kbd>Backspace</kbd> and <kbd>Delete</kbd> should not delete a widget when the editor is in the read-only mode. Closes [#6](https://github.com/ckeditor/ckeditor5-widget/issues/6). ([5f64125](https://github.com/ckeditor/ckeditor5-widget/commit/5f64125))
233
- * Nested element structures next to widgets will be correctly removed when pressing <kbd>Backspace</kbd> or <kbd>Delete</kbd>. Closes [#19](https://github.com/ckeditor/ckeditor5-widget/issues/19). ([27ee848](https://github.com/ckeditor/ckeditor5-widget/commit/27ee848))
234
-
235
-
236
- ## [0.2.0](https://github.com/ckeditor/ckeditor5-widget/compare/v0.1.1...v0.2.0) (2017-09-03)
237
-
238
- ### Bug fixes
239
-
240
- * Added initial contenteditable state for editable widget. Closes [#9](https://github.com/ckeditor/ckeditor5-widget/issues/9). ([c6321ff](https://github.com/ckeditor/ckeditor5-widget/commit/c6321ff))
241
-
242
- ### Features
243
-
244
- * <kbd>Ctrl</kbd>+<kbd>A</kbd> in a nested editable should select nested editable's content. Closes [#13](https://github.com/ckeditor/ckeditor5-widget/issues/13). ([35a8aff](https://github.com/ckeditor/ckeditor5-widget/commit/35a8aff))
245
-
246
- ### Other changes
247
-
248
- * Adjusted widget to the editor read-only mode. Closes [#7](https://github.com/ckeditor/ckeditor5-widget/issues/7). ([2726873](https://github.com/ckeditor/ckeditor5-widget/commit/2726873))
249
- * Introduced highlights support for widgets. Closes [#11](https://github.com/ckeditor/ckeditor5-widget/issues/11). ([0bd3d66](https://github.com/ckeditor/ckeditor5-widget/commit/0bd3d66))
250
-
251
-
252
- ## [0.1.1](https://github.com/ckeditor/ckeditor5-widget/compare/v0.1.0...v0.1.1) (2017-05-07)
253
-
254
- Internal changes only (updated dependencies, documentation, etc.).
255
-
256
- ## 0.1.0 (2017-04-05)
257
-
258
- ### Features
259
-
260
- * Initial implementation (the code was moved from the `ckeditor5-image` package). Closes [#1](https://github.com/ckeditor/ckeditor5-widget/issues/1). ([564dd97](https://github.com/ckeditor/ckeditor5-widget/commit/564dd97))