@ckeditor/ckeditor5-widget 28.0.0 → 30.0.0

Sign up to get free protection for your applications and to get access to all the features.
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))