浏览代码

Merge branch 'master' into i/6091

Piotrek Koszuliński 5 年之前
父节点
当前提交
745599c32e

+ 295 - 112
packages/ckeditor5-ui/src/panel/balloon/balloonpanelview.js

@@ -224,9 +224,13 @@ export default class BalloonPanelView extends View {
 			element: this.element,
 			positions: [
 				defaultPositions.southArrowNorth,
+				defaultPositions.southArrowNorthMiddleWest,
+				defaultPositions.southArrowNorthMiddleEast,
 				defaultPositions.southArrowNorthWest,
 				defaultPositions.southArrowNorthEast,
 				defaultPositions.northArrowSouth,
+				defaultPositions.northArrowSouthMiddleWest,
+				defaultPositions.northArrowSouthMiddleEast,
 				defaultPositions.northArrowSouthWest,
 				defaultPositions.northArrowSouthEast
 			],
@@ -435,86 +439,158 @@ BalloonPanelView._getOptimalPosition = getOptimalPosition;
  *
  * The available positioning functions are as follows:
  *
- * **North**
- *
- * * `northArrowSouth`
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		         V
- * 		    [ Target ]
  *
- * * `northArrowSouthEast`
+ * **North west**
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		               V
- * 		          [ Target ]
+ * * `northWestArrowSouthWest`
  *
- * * `northArrowSouthWest`
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		 V
+ *		 [ Target ]
  *
- * 		  +-----------------+
- * 		  |     Balloon     |
- * 		  +-----------------+
- * 		     V
- * 		[ Target ]
+ * * `northWestArrowSouthMiddleWest`
  *
- * **North west**
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		    V
+ *		    [ Target ]
  *
  * * `northWestArrowSouth`
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		         V
- * 		         [ Target ]
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		         V
+ *		         [ Target ]
  *
- * * `northWestArrowSouthWest`
+ * * `northWestArrowSouthMiddleEast`
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		   V
- * 		   [ Target ]
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		             V
+ *		             [ Target ]
  *
  * * `northWestArrowSouthEast`
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		               V
- * 		               [ Target ]
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		                 V
+ *		                 [ Target ]
+ *
+ *
+ *
+ * **North**
+ *
+ * * `northArrowSouthWest`
+ *
+ *		    +-----------------+
+ *		    |     Balloon     |
+ *		    +-----------------+
+ *		     V
+ *		[ Target ]
+ *
+ * * `northArrowSouthMiddleWest`
+ *
+ *		 +-----------------+
+ *		 |     Balloon     |
+ *		 +-----------------+
+ *		     V
+ *		[ Target ]
+ *
+ * * `northArrowSouth`
+ *
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		         V
+ *		    [ Target ]
+ *
+ * * `northArrowSouthMiddleEast`
+ *
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		             V
+ *		        [ Target ]
+ *
+ * * `northArrowSouthEast`
+ *
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		                V
+ *		           [ Target ]
  *
  * **North east**
  *
+ * * `northEastArrowSouthWest`
+ *
+ *		        +-----------------+
+ *		        |     Balloon     |
+ *		        +-----------------+
+ *		         V
+ *		[ Target ]
+ *
+ *
+ * * `northEastArrowSouthMiddleWest`
+ *
+ *		     +-----------------+
+ *		     |     Balloon     |
+ *		     +-----------------+
+ *		         V
+ *		[ Target ]
+ *
  * * `northEastArrowSouth`
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		         V
- * 		[ Target ]
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		         V
+ *		[ Target ]
+ *
+ * * `northEastArrowSouthMiddleEast`
+ *
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		             V
+ *		    [ Target ]
  *
  * * `northEastArrowSouthEast`
  *
- * 		+-----------------+
- * 		|     Balloon     |
- * 		+-----------------+
- * 		               V
- * 		      [ Target ]
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *		                 V
+ *		        [ Target ]
  *
- * * `northEastArrowSouthWest`
  *
- * 		      +-----------------+
- * 		      |     Balloon     |
- * 		      +-----------------+
- * 		         V
- * 		[ Target ]
  *
  * **South**
  *
+ *
+ * * `southArrowNorthWest`
+ *
+ *		[ Target ]
+ *		     ^
+ *		    +-----------------+
+ *		    |     Balloon     |
+ *		    +-----------------+
+ *
+ * * `southArrowNorthMiddleWest`
+ *
+ *		   [ Target ]
+ *		        ^
+ *		    +-----------------+
+ *		    |     Balloon     |
+ *		    +-----------------+
+ *
  * * `southArrowNorth`
  *
  *		    [ Target ]
@@ -523,24 +599,42 @@ BalloonPanelView._getOptimalPosition = getOptimalPosition;
  *		|     Balloon     |
  *		+-----------------+
  *
+ * * `southArrowNorthMiddleEast`
+ *
+ *		            [ Target ]
+ *		                 ^
+ *		   +-----------------+
+ *		   |     Balloon     |
+ *		   +-----------------+
+ *
  * * `southArrowNorthEast`
  *
- *		          [ Target ]
- *		               ^
+ *		            [ Target ]
+ *		                 ^
  *		+-----------------+
  *		|     Balloon     |
  *		+-----------------+
  *
- * * `southArrowNorthWest`
  *
- *		[ Target ]
- *		     ^
- *		   +-----------------+
- *		   |     Balloon     |
- *		   +-----------------+
  *
  * **South west**
  *
+ * * `southWestArrowNorthWest`
+ *
+ *		 [ Target ]
+ *		 ^
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
+ *
+ * * `southWestArrowNorthMiddleWest`
+ *
+ *		     [ Target ]
+ *		     ^
+ *		 +-----------------+
+ *		 |     Balloon     |
+ *		 +-----------------+
+ *
  * * `southWestArrowNorth`
  *
  *		         [ Target ]
@@ -549,24 +643,41 @@ BalloonPanelView._getOptimalPosition = getOptimalPosition;
  *		|     Balloon     |
  *		+-----------------+
  *
- * * `southWestArrowNorthWest`
+ * * `southWestArrowNorthMiddleEast`
  *
- *		  [ Target ]
- *		  ^
+ *		              [ Target ]
+ *		              ^
  *		+-----------------+
  *		|     Balloon     |
  *		+-----------------+
  *
  * * `southWestArrowNorthEast`
  *
- *		               [ Target ]
- *		               ^
+ *		                 [ Target ]
+ *		                 ^
  *		+-----------------+
  *		|     Balloon     |
  *		+-----------------+
  *
+ *
+ *
  * **South east**
  *
+ * * `southEastArrowNorthWest`
+ *
+ *		[ Target ]
+ *		         ^
+ *		        +-----------------+
+ *		        |     Balloon     |
+ *		        +-----------------+
+* * `southEastArrowNorthMiddleWest`
+ *
+ *		   [ Target ]
+ *		            ^
+ *		        +-----------------+
+ *		        |     Balloon     |
+ *		        +-----------------+
+ *
  * * `southEastArrowNorth`
  *
  *		[ Target ]
@@ -575,21 +686,22 @@ BalloonPanelView._getOptimalPosition = getOptimalPosition;
  *		|     Balloon     |
  *		+-----------------+
  *
- * * `southEastArrowNorthEast`
+ * * `southEastArrowNorthMiddleEast`
  *
- *		       [ Target ]
- *		                ^
+ *		     [ Target ]
+ *		              ^
  *		+-----------------+
  *		|     Balloon     |
  *		+-----------------+
  *
- * * `southEastArrowNorthWest`
+ * * `southEastArrowNorthEast`
+ *
+ *		        [ Target ]
+ *		                 ^
+ *		+-----------------+
+ *		|     Balloon     |
+ *		+-----------------+
  *
- *		[ Target ]
- *		         ^
- *		       +-----------------+
- *		       |     Balloon     |
- *		       +-----------------+
  *
  * See {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#attachTo}.
  *
@@ -601,125 +713,196 @@ BalloonPanelView._getOptimalPosition = getOptimalPosition;
  * @member {Object} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions
  */
 BalloonPanelView.defaultPositions = {
-	// ------- North
 
-	northArrowSouth: ( targetRect, balloonRect ) => ( {
+	// ------- North west
+
+	northWestArrowSouthWest: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
-		left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
+		left: targetRect.left - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_sw'
+	} ),
+
+	northWestArrowSouthMiddleWest: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.left - ( balloonRect.width * .25 ) - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_smw'
+	} ),
+
+	northWestArrowSouth: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.left - balloonRect.width / 2,
 		name: 'arrow_s'
 	} ),
 
-	northArrowSouthEast: ( targetRect, balloonRect ) => ( {
+	northWestArrowSouthMiddleEast: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
-		left: targetRect.left + targetRect.width / 2 - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
+		left: targetRect.left - ( balloonRect.width * .75 ) + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_sme'
+	} ),
+
+	northWestArrowSouthEast: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.left - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_se'
 	} ),
 
+	// ------- North
+
 	northArrowSouthWest: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
 		left: targetRect.left + targetRect.width / 2 - BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_sw'
 	} ),
 
-	// ------- North west
+	northArrowSouthMiddleWest: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * .25 ) - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_smw'
+	} ),
 
-	northWestArrowSouth: ( targetRect, balloonRect ) => ( {
+	northArrowSouth: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
-		left: targetRect.left - balloonRect.width / 2,
+		left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
 		name: 'arrow_s'
 	} ),
 
-	northWestArrowSouthWest: ( targetRect, balloonRect ) => ( {
+	northArrowSouthMiddleEast: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
-		left: targetRect.left - BalloonPanelView.arrowHorizontalOffset,
-		name: 'arrow_sw'
+		left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * .75 ) + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_sme'
 	} ),
 
-	northWestArrowSouthEast: ( targetRect, balloonRect ) => ( {
+	northArrowSouthEast: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
-		left: targetRect.left - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
+		left: targetRect.left + targetRect.width / 2 - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_se'
 	} ),
 
 	// ------- North east
 
+	northEastArrowSouthWest: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.right - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_sw'
+	} ),
+
+	northEastArrowSouthMiddleWest: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.right - ( balloonRect.width * .25 ) - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_smw'
+	} ),
 	northEastArrowSouth: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
 		left: targetRect.right - balloonRect.width / 2,
 		name: 'arrow_s'
 	} ),
 
+	northEastArrowSouthMiddleEast: ( targetRect, balloonRect ) => ( {
+		top: getNorthTop( targetRect, balloonRect ),
+		left: targetRect.right - ( balloonRect.width * .75 ) + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_sme'
+	} ),
+
 	northEastArrowSouthEast: ( targetRect, balloonRect ) => ( {
 		top: getNorthTop( targetRect, balloonRect ),
 		left: targetRect.right - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_se'
 	} ),
+	// ------- South west
 
-	northEastArrowSouthWest: ( targetRect, balloonRect ) => ( {
-		top: getNorthTop( targetRect, balloonRect ),
-		left: targetRect.right - BalloonPanelView.arrowHorizontalOffset,
-		name: 'arrow_sw'
+	southWestArrowNorthWest: ( targetRect, balloonRect ) => ( {
+		top: getSouthTop( targetRect, balloonRect ),
+		left: targetRect.left - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nw'
 	} ),
 
-	// ------- South
+	southWestArrowNorthMiddleWest: ( targetRect, balloonRect ) => ( {
+		top: getSouthTop( targetRect, balloonRect ),
+		left: targetRect.left - ( balloonRect.width * .25 ) - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nmw'
+	} ),
 
-	southArrowNorth: ( targetRect, balloonRect ) => ( {
+	southWestArrowNorth: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
+		left: targetRect.left - balloonRect.width / 2,
 		name: 'arrow_n'
 	} ),
 
-	southArrowNorthEast: ( targetRect, balloonRect ) => ( {
+	southWestArrowNorthMiddleEast: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.left + targetRect.width / 2 - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
+		left: targetRect.left - ( balloonRect.width * .75 ) + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nme'
+	} ),
+
+	southWestArrowNorthEast: ( targetRect, balloonRect ) => ( {
+		top: getSouthTop( targetRect, balloonRect ),
+		left: targetRect.left - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_ne'
 	} ),
 
+	// ------- South
+
 	southArrowNorthWest: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
 		left: targetRect.left + targetRect.width / 2 - BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_nw'
 	} ),
+	southArrowNorthMiddleWest: ( targetRect, balloonRect ) => ( {
+		top: getSouthTop( targetRect, balloonRect ),
+		left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * 0.25 ) - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nmw'
+	} ),
 
-	// ------- South west
-
-	southWestArrowNorth: ( targetRect, balloonRect ) => ( {
+	southArrowNorth: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.left - balloonRect.width / 2,
+		left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
 		name: 'arrow_n'
 	} ),
 
-	southWestArrowNorthWest: ( targetRect, balloonRect ) => ( {
+	southArrowNorthMiddleEast: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.left - BalloonPanelView.arrowHorizontalOffset,
-		name: 'arrow_nw'
+		left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * 0.75 ) + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nme'
 	} ),
 
-	southWestArrowNorthEast: ( targetRect, balloonRect ) => ( {
+	southArrowNorthEast: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.left - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
+		left: targetRect.left + targetRect.width / 2 - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
 		name: 'arrow_ne'
 	} ),
 
 	// ------- South east
 
+	southEastArrowNorthWest: ( targetRect, balloonRect ) => ( {
+		top: getSouthTop( targetRect, balloonRect ),
+		left: targetRect.right - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nw'
+	} ),
+
+	southEastArrowNorthMiddleWest: ( targetRect, balloonRect ) => ( {
+		top: getSouthTop( targetRect, balloonRect ),
+		left: targetRect.right - ( balloonRect.width * .25 ) - BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nmw'
+	} ),
+
 	southEastArrowNorth: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
 		left: targetRect.right - balloonRect.width / 2,
 		name: 'arrow_n'
 	} ),
 
-	southEastArrowNorthEast: ( targetRect, balloonRect ) => ( {
+	southEastArrowNorthMiddleEast: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.right - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
-		name: 'arrow_ne'
+		left: targetRect.right - ( balloonRect.width * .75 ) + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_nme'
 	} ),
 
-	southEastArrowNorthWest: ( targetRect, balloonRect ) => ( {
+	southEastArrowNorthEast: ( targetRect, balloonRect ) => ( {
 		top: getSouthTop( targetRect, balloonRect ),
-		left: targetRect.right - BalloonPanelView.arrowHorizontalOffset,
-		name: 'arrow_nw'
+		left: targetRect.right - balloonRect.width + BalloonPanelView.arrowHorizontalOffset,
+		name: 'arrow_ne'
 	} )
+
 };
 
 // Returns the top coordinate for positions starting with `north*`.

+ 72 - 5
packages/ckeditor5-ui/src/toolbar/balloon/balloontoolbar.js

@@ -15,6 +15,10 @@ import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
 import normalizeToolbarConfig from '../normalizetoolbarconfig';
 import { debounce } from 'lodash-es';
+import ResizeObserver from '@ckeditor/ckeditor5-utils/src/dom/resizeobserver';
+import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
+
+const toPx = toUnit( 'px' );
 
 /**
  * The contextual toolbar.
@@ -45,6 +49,14 @@ export default class BalloonToolbar extends Plugin {
 		super( editor );
 
 		/**
+		 * A cached and normalized `config.balloonToolbar` object.
+		 *
+		 * @type {module:core/editor/editorconfig~EditorConfig#balloonToolbar}
+		 * @private
+		 */
+		this._balloonConfig = normalizeToolbarConfig( editor.config.get( 'balloonToolbar' ) );
+
+		/**
 		 * The toolbar view displayed in the balloon.
 		 *
 		 * @type {module:ui/toolbar/toolbarview~ToolbarView}
@@ -67,6 +79,20 @@ export default class BalloonToolbar extends Plugin {
 		} );
 
 		/**
+		 * An instance of the resize observer that allows to respond to changes in editable's geometry
+		 * so the toolbar can stay within its boundaries (and group toolbar items that do not fit).
+		 *
+		 * **Note**: Used only when `shouldNotGroupWhenFull` was **not** set in the
+		 * {@link module:core/editor/editorconfig~EditorConfig#balloonToolbar configuration}.
+		 *
+		 * **Note:** Created in {@link #init}.
+		 *
+		 * @protected
+		 * @member {module:utils/dom/resizeobserver~ResizeObserver}
+		 */
+		this._resizeObserver = null;
+
+		/**
 		 * The contextual balloon plugin instance.
 		 *
 		 * @private
@@ -125,6 +151,20 @@ export default class BalloonToolbar extends Plugin {
 				this.show();
 			}
 		} );
+
+		if ( !this._balloonConfig.shouldNotGroupWhenFull ) {
+			this.listenTo( editor, 'ready', () => {
+				const editableElement = editor.ui.view.editable.element;
+
+				// Set #toolbarView's max-width on the initialization and update it on the editable resize.
+				this._resizeObserver = new ResizeObserver( editableElement, () => {
+					// The max-width equals 90% of the editable's width for the best user experience.
+					// The value keeps the balloon very close to the boundaries of the editable and limits the cases
+					// when the balloon juts out from the editable element it belongs to.
+					this.toolbarView.maxWidth = toPx( new Rect( editableElement ).width * .9 );
+				} );
+			} );
+		}
 	}
 
 	/**
@@ -134,10 +174,9 @@ export default class BalloonToolbar extends Plugin {
 	 * @inheritDoc
 	 */
 	afterInit() {
-		const config = normalizeToolbarConfig( this.editor.config.get( 'balloonToolbar' ) );
 		const factory = this.editor.ui.componentFactory;
 
-		this.toolbarView.fillFromConfig( config.items, factory );
+		this.toolbarView.fillFromConfig( this._balloonConfig.items, factory );
 	}
 
 	/**
@@ -147,7 +186,10 @@ export default class BalloonToolbar extends Plugin {
 	 * @returns {module:ui/toolbar/toolbarview~ToolbarView}
 	 */
 	_createToolbarView() {
-		const toolbarView = new ToolbarView( this.editor.locale );
+		const shouldGroupWhenFull = !this._balloonConfig.shouldNotGroupWhenFull;
+		const toolbarView = new ToolbarView( this.editor.locale, {
+			shouldGroupWhenFull
+		} );
 
 		toolbarView.extendTemplate( {
 			attributes: {
@@ -260,6 +302,10 @@ export default class BalloonToolbar extends Plugin {
 		this._fireSelectionChangeDebounced.cancel();
 		this.toolbarView.destroy();
 		this.focusTracker.destroy();
+
+		if ( this._resizeObserver ) {
+			this._resizeObserver.destroy();
+		}
 	}
 
 	/**
@@ -289,16 +335,24 @@ function getBalloonPositions( isBackward ) {
 		defaultPositions.northWestArrowSouth,
 		defaultPositions.northWestArrowSouthWest,
 		defaultPositions.northWestArrowSouthEast,
+		defaultPositions.northWestArrowSouthMiddleEast,
+		defaultPositions.northWestArrowSouthMiddleWest,
 		defaultPositions.southWestArrowNorth,
 		defaultPositions.southWestArrowNorthWest,
-		defaultPositions.southWestArrowNorthEast
+		defaultPositions.southWestArrowNorthEast,
+		defaultPositions.southWestArrowNorthMiddleWest,
+		defaultPositions.southWestArrowNorthMiddleEast
 	] : [
 		defaultPositions.southEastArrowNorth,
 		defaultPositions.southEastArrowNorthEast,
 		defaultPositions.southEastArrowNorthWest,
+		defaultPositions.southEastArrowNorthMiddleEast,
+		defaultPositions.southEastArrowNorthMiddleWest,
 		defaultPositions.northEastArrowSouth,
 		defaultPositions.northEastArrowSouthEast,
-		defaultPositions.northEastArrowSouthWest
+		defaultPositions.northEastArrowSouthWest,
+		defaultPositions.northEastArrowSouthMiddleEast,
+		defaultPositions.northEastArrowSouthMiddleWest
 	];
 }
 
@@ -306,6 +360,8 @@ function getBalloonPositions( isBackward ) {
  * Contextual toolbar configuration. Used by the {@link module:ui/toolbar/balloon/balloontoolbar~BalloonToolbar}
  * feature.
  *
+ * ## Configuring toolbar items
+ *
  *		const config = {
  *			balloonToolbar: [ 'bold', 'italic', 'undo', 'redo' ]
  *		};
@@ -318,5 +374,16 @@ function getBalloonPositions( isBackward ) {
  *
  * Read also about configuring the main editor toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
  *
+ * ## Configuring items grouping
+ *
+ * You can prevent automatic items grouping by setting the `shouldNotGroupWhenFull` option:
+ *
+ *		const config = {
+ *			balloonToolbar: {
+ *				items: [ 'bold', 'italic', 'undo', 'redo' ]
+ *			},
+ *			shouldNotGroupWhenFull: true
+ *		};
+ *
  * @member {Array.<String>|Object} module:core/editor/editorconfig~EditorConfig#balloonToolbar
  */

+ 32 - 1
packages/ckeditor5-ui/src/toolbar/toolbarview.js

@@ -62,6 +62,19 @@ export default class ToolbarView extends View {
 		this.set( 'ariaLabel', t( 'Editor toolbar' ) );
 
 		/**
+		 * The maximum width of the toolbar element.
+		 *
+		 * **Note**: When set to a specific value (e.g. `'200px'`), the value will affect the behavior of the
+		 * {@link module:ui/toolbar/toolbarview~ToolbarOptions#shouldGroupWhenFull}
+		 * option by changing the number of {@link #items} that will be displayed in the toolbar at a time.
+		 *
+		 * @observable
+		 * @default 'auto'
+		 * @member {String} #maxWidth
+		 */
+		this.set( 'maxWidth', 'auto' );
+
+		/**
 		 * A collection of toolbar items (buttons, dropdowns, etc.).
 		 *
 		 * @readonly
@@ -181,7 +194,10 @@ export default class ToolbarView extends View {
 					bind.if( 'isCompact', 'ck-toolbar_compact' )
 				],
 				role: 'toolbar',
-				'aria-label': bind.to( 'ariaLabel' )
+				'aria-label': bind.to( 'ariaLabel' ),
+				style: {
+					maxWidth: bind.to( 'maxWidth' )
+				}
 			},
 
 			children: this.children,
@@ -570,6 +586,7 @@ class DynamicGrouping {
 		this.viewElement = view.element;
 
 		this._enableGroupingOnResize();
+		this._enableGroupingOnMaxWidthChange( view );
 	}
 
 	/**
@@ -695,6 +712,18 @@ class DynamicGrouping {
 	}
 
 	/**
+	 * Enables the grouping functionality, just like {@link #_enableGroupingOnResize} but the difference is that
+	 * it listens to the changes of {@link module:ui/toolbar/toolbarview~ToolbarView#maxWidth} instead.
+	 *
+	 * @private
+	 */
+	_enableGroupingOnMaxWidthChange( view ) {
+		view.on( 'change:maxWidth', () => {
+			this._updateGrouping();
+		} );
+	}
+
+	/**
 	 * When called, it will remove the last item from {@link #ungroupedItems} and move it back
 	 * to the {@link #groupedItems} collection.
 	 *
@@ -798,6 +827,8 @@ class DynamicGrouping {
  * would normally wrap to the next line when there is not enough space to display them in a single row, for
  * instance, if the parent container of the toolbar is narrow.
  *
+ * Also see: {@link module:ui/toolbar/toolbarview~ToolbarView#maxWidth}.
+ *
  * @member {Boolean} module:ui/toolbar/toolbarview~ToolbarOptions#shouldGroupWhenFull
  */
 

+ 11 - 2
packages/ckeditor5-ui/tests/manual/panel/balloon/balloonpanelview.html

@@ -5,11 +5,18 @@
 		padding: 5em;
 	}
 
+	h1 {
+		font-size: 22px;
+		text-align: center;
+		margin: 70px 0;
+	}
+
 	.target {
 		width: 125px;
 		height: 20px;
-		background: green;
-		margin: 0 0 0 100px;
+		background: lightblue;
+		transform: translateX(-50%);
+		margin-left: 50%;
 	}
 
 	.target + .target {
@@ -18,5 +25,7 @@
 
 	.ck-balloon-panel {
 		padding: .3em;
+		width: 250px;
+		text-align: center;
 	}
 </style>

+ 31 - 0
packages/ckeditor5-ui/tests/manual/panel/balloon/balloonpanelview.js

@@ -10,9 +10,22 @@ import BalloonPanelView from '../../../../src/panel/balloon/balloonpanelview';
 const defaultPositions = BalloonPanelView.defaultPositions;
 const container = document.querySelector( '#container' );
 
+let currentHeading = '';
+
 for ( const i in defaultPositions ) {
 	const target = document.createElement( 'div' );
+	const heading = document.createElement( 'h1' );
+	const headingText = parseHeadingText( i );
+
+	heading.textContent = headingText;
 	target.classList.add( 'target' );
+
+	// Lazy heading
+	if ( currentHeading !== headingText ) {
+		container.appendChild( heading );
+		currentHeading = headingText;
+	}
+
 	container.appendChild( target );
 
 	const balloon = new BalloonPanelView();
@@ -27,3 +40,21 @@ for ( const i in defaultPositions ) {
 		]
 	} );
 }
+
+function parseHeadingText( text ) {
+	const normalizedText = getNormalizeHeading( text );
+	return getCapitalizedHeading( normalizedText );
+}
+
+// This helper function creates normalize heading text from a full name of the position,
+// removing `ArrowXyz` part, like in the example:
+// `southEastArrowNorthMiddleEast` -> `south East`.
+function getNormalizeHeading( text ) {
+	return text
+		.replace( /(w*)arrow\w*/i, '$1' )
+		.replace( /([a-z])([A-Z])/, '$1 $2' );
+}
+
+function getCapitalizedHeading( text ) {
+	return text.charAt( 0 ).toUpperCase() + text.slice( 1 );
+}

+ 1 - 1
packages/ckeditor5-ui/tests/manual/panel/balloon/balloonpanelview.md

@@ -1,5 +1,5 @@
 ## `BalloonPanelView` and `defaultPositions`
 
-1. A number of green rectangles should be displayed in the page.
+1. A number of colorful rectangles should be displayed in the page.
 2. Each rectangle should have a panel attached.
 3. Make sure the description in each panel matches the location of the panel.

+ 101 - 1
packages/ckeditor5-ui/tests/panel/balloon/balloonpanelview.js

@@ -181,9 +181,13 @@ describe( 'BalloonPanelView', () => {
 				target,
 				positions: [
 					BalloonPanelView.defaultPositions.southArrowNorth,
+					BalloonPanelView.defaultPositions.southArrowNorthMiddleWest,
+					BalloonPanelView.defaultPositions.southArrowNorthMiddleEast,
 					BalloonPanelView.defaultPositions.southArrowNorthWest,
 					BalloonPanelView.defaultPositions.southArrowNorthEast,
 					BalloonPanelView.defaultPositions.northArrowSouth,
+					BalloonPanelView.defaultPositions.northArrowSouthMiddleWest,
+					BalloonPanelView.defaultPositions.northArrowSouthMiddleEast,
 					BalloonPanelView.defaultPositions.northArrowSouthWest,
 					BalloonPanelView.defaultPositions.northArrowSouthEast
 				],
@@ -711,7 +715,7 @@ describe( 'BalloonPanelView', () => {
 		} );
 
 		it( 'should have a proper length', () => {
-			expect( Object.keys( positions ) ).to.have.length( 18 );
+			expect( Object.keys( positions ) ).to.have.length( 30 );
 		} );
 
 		// ------- North
@@ -732,6 +736,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "northArrowSouthMiddleEast" position', () => {
+			expect( positions.northArrowSouthMiddleEast( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 50 - arrowVOffset,
+				left: 112.5 + arrowHOffset,
+				name: 'arrow_sme'
+			} );
+		} );
+
 		it( 'should define the "northArrowSouthWest" position', () => {
 			expect( positions.northArrowSouthWest( targetRect, balloonRect ) ).to.deep.equal( {
 				top: 50 - arrowVOffset,
@@ -740,6 +752,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "northArrowSouthMiddleWest" position', () => {
+			expect( positions.northArrowSouthMiddleWest( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 50 - arrowVOffset,
+				left: 137.5 - arrowHOffset,
+				name: 'arrow_smw'
+			} );
+		} );
+
 		// ------- North west
 
 		it( 'should define the "northWestArrowSouth" position', () => {
@@ -758,6 +778,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "northWestArrowSouthMiddleWest" position', () => {
+			expect( positions.northWestArrowSouthMiddleWest( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 50 - arrowVOffset,
+				left: 87.5 - arrowHOffset,
+				name: 'arrow_smw'
+			} );
+		} );
+
 		it( 'should define the "northWestArrowSouthEast" position', () => {
 			expect( positions.northWestArrowSouthEast( targetRect, balloonRect ) ).to.deep.equal( {
 				top: 50 - arrowVOffset,
@@ -766,6 +794,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "northWestArrowSouthMiddleEast" position', () => {
+			expect( positions.northWestArrowSouthMiddleEast( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 50 - arrowVOffset,
+				left: 62.5 + arrowHOffset,
+				name: 'arrow_sme'
+			} );
+		} );
+
 		// ------- North east
 
 		it( 'should define the "northEastArrowSouth" position', () => {
@@ -784,6 +820,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "northEastArrowSouthMiddleEast" position', () => {
+			expect( positions.northEastArrowSouthMiddleEast( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 50 - arrowVOffset,
+				left: 162.5 + arrowHOffset,
+				name: 'arrow_sme'
+			} );
+		} );
+
 		it( 'should define the "northEastArrowSouthWest" position', () => {
 			expect( positions.northEastArrowSouthWest( targetRect, balloonRect ) ).to.deep.equal( {
 				top: 50 - arrowVOffset,
@@ -792,6 +836,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "northEastArrowSouthMiddleWest" position', () => {
+			expect( positions.northEastArrowSouthMiddleWest( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 50 - arrowVOffset,
+				left: 187.5 - arrowHOffset,
+				name: 'arrow_smw'
+			} );
+		} );
+
 		// ------- South
 
 		it( 'should define the "southArrowNorth" position', () => {
@@ -810,6 +862,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "southArrowNorthMiddleEast" position', () => {
+			expect( positions.southArrowNorthMiddleEast( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 200 + arrowVOffset,
+				left: 112.5 + arrowHOffset,
+				name: 'arrow_nme'
+			} );
+		} );
+
 		it( 'should define the "southArrowNorthWest" position', () => {
 			expect( positions.southArrowNorthWest( targetRect, balloonRect ) ).to.deep.equal( {
 				top: 200 + arrowVOffset,
@@ -818,6 +878,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "southArrowNorthMiddleWest" position', () => {
+			expect( positions.southArrowNorthMiddleWest( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 200 + arrowVOffset,
+				left: 137.5 - arrowHOffset,
+				name: 'arrow_nmw'
+			} );
+		} );
+
 		// ------- South west
 
 		it( 'should define the "southWestArrowNorth" position', () => {
@@ -836,6 +904,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "southWestArrowNorthMiddleWest" position', () => {
+			expect( positions.southWestArrowNorthMiddleWest( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 200 + arrowVOffset,
+				left: 87.5 - arrowHOffset,
+				name: 'arrow_nmw'
+			} );
+		} );
+
 		it( 'should define the "southWestArrowNorthEast" position', () => {
 			expect( positions.southWestArrowNorthEast( targetRect, balloonRect ) ).to.deep.equal( {
 				top: 200 + arrowVOffset,
@@ -844,6 +920,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "southWestArrowNorthMiddleEast" position', () => {
+			expect( positions.southWestArrowNorthMiddleEast( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 200 + arrowVOffset,
+				left: 62.5 + arrowHOffset,
+				name: 'arrow_nme'
+			} );
+		} );
+
 		// ------- South east
 
 		it( 'should define the "southEastArrowNorth" position', () => {
@@ -862,6 +946,14 @@ describe( 'BalloonPanelView', () => {
 			} );
 		} );
 
+		it( 'should define the "southEastArrowNorthMiddleEast" position', () => {
+			expect( positions.southEastArrowNorthMiddleEast( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 200 + arrowVOffset,
+				left: 162.5 + arrowHOffset,
+				name: 'arrow_nme'
+			} );
+		} );
+
 		it( 'should define the "southEastArrowNorthWest" position', () => {
 			expect( positions.southEastArrowNorthWest( targetRect, balloonRect ) ).to.deep.equal( {
 				top: 200 + arrowVOffset,
@@ -869,6 +961,14 @@ describe( 'BalloonPanelView', () => {
 				name: 'arrow_nw'
 			} );
 		} );
+
+		it( 'should define the "southEastArrowNorthMiddleWest" position', () => {
+			expect( positions.southEastArrowNorthMiddleWest( targetRect, balloonRect ) ).to.deep.equal( {
+				top: 200 + arrowVOffset,
+				left: 187.5 - arrowHOffset,
+				name: 'arrow_nmw'
+			} );
+		} );
 	} );
 } );
 

+ 85 - 2
packages/ckeditor5-ui/tests/toolbar/balloon/balloontoolbar.js

@@ -14,22 +14,45 @@ import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
 import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
 import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import global from '@ckeditor/ckeditor5-utils/src/dom/global';
+import ResizeObserver from '@ckeditor/ckeditor5-utils/src/dom/resizeobserver';
 
 import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { stringify as viewStringify } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
+import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
+import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
+
+const toPx = toUnit( 'px' );
+
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 /* global document, window, Event */
 
 describe( 'BalloonToolbar', () => {
 	let editor, model, selection, editingView, balloonToolbar, balloon, editorElement;
+	let resizeCallback;
+
 	testUtils.createSinonSandbox();
 
 	beforeEach( () => {
 		editorElement = document.createElement( 'div' );
 		document.body.appendChild( editorElement );
 
+		// Make sure other tests of the editor do not affect tests that follow.
+		// Without it, if an instance of ResizeObserver already exists somewhere undestroyed
+		// in DOM, the following DOM mock will have no effect.
+		ResizeObserver._observerInstance = null;
+
+		testUtils.sinon.stub( global.window, 'ResizeObserver' ).callsFake( callback => {
+			resizeCallback = callback;
+
+			return {
+				observe: sinon.spy(),
+				unobserve: sinon.spy()
+			};
+		} );
+
 		return ClassicTestEditor
 			.create( editorElement, {
 				plugins: [ Paragraph, Bold, Italic, BalloonToolbar ],
@@ -102,6 +125,27 @@ describe( 'BalloonToolbar', () => {
 			} );
 	} );
 
+	it( 'should not group items when the config.shouldNotGroupWhenFull option is enabled', () => {
+		const editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor.create( editorElement, {
+			plugins: [ Paragraph, Bold, Italic, Underline, BalloonToolbar ],
+			balloonToolbar: {
+				items: [ 'bold', 'italic', 'underline' ],
+				shouldNotGroupWhenFull: true
+			}
+		} ).then( editor => {
+			const balloonToolbar = editor.plugins.get( BalloonToolbar );
+
+			expect( balloonToolbar.toolbarView.options.shouldGroupWhenFull ).to.be.false;
+
+			return editor.destroy();
+		} ).then( () => {
+			editorElement.remove();
+		} );
+	} );
+
 	it( 'should fire internal `_selectionChangeDebounced` event 200 ms after last selection change', () => {
 		const clock = testUtils.sinon.useFakeTimers();
 		const spy = testUtils.sinon.spy();
@@ -208,9 +252,13 @@ describe( 'BalloonToolbar', () => {
 						defaultPositions.southEastArrowNorth,
 						defaultPositions.southEastArrowNorthEast,
 						defaultPositions.southEastArrowNorthWest,
+						defaultPositions.southEastArrowNorthMiddleEast,
+						defaultPositions.southEastArrowNorthMiddleWest,
 						defaultPositions.northEastArrowSouth,
 						defaultPositions.northEastArrowSouthEast,
-						defaultPositions.northEastArrowSouthWest
+						defaultPositions.northEastArrowSouthWest,
+						defaultPositions.northEastArrowSouthMiddleEast,
+						defaultPositions.northEastArrowSouthMiddleWest
 					]
 				}
 			} );
@@ -267,9 +315,13 @@ describe( 'BalloonToolbar', () => {
 						defaultPositions.northWestArrowSouth,
 						defaultPositions.northWestArrowSouthWest,
 						defaultPositions.northWestArrowSouthEast,
+						defaultPositions.northWestArrowSouthMiddleEast,
+						defaultPositions.northWestArrowSouthMiddleWest,
 						defaultPositions.southWestArrowNorth,
 						defaultPositions.southWestArrowNorthWest,
-						defaultPositions.southWestArrowNorthEast
+						defaultPositions.southWestArrowNorthEast,
+						defaultPositions.southWestArrowNorthMiddleWest,
+						defaultPositions.southWestArrowNorthMiddleEast
 					]
 				}
 			} );
@@ -344,6 +396,25 @@ describe( 'BalloonToolbar', () => {
 			balloonToolbar.show();
 			sinon.assert.calledOnce( balloonAddSpy );
 		} );
+
+		it( 'should set the toolbar max-width to 90% of the editable width', () => {
+			const viewElement = editor.ui.view.editable.element;
+
+			setData( model, '<paragraph>b[ar]</paragraph>' );
+
+			expect( global.document.body.contains( viewElement ) ).to.be.true;
+			viewElement.style.width = '400px';
+
+			resizeCallback( [ {
+				target: viewElement,
+				contentRect: new Rect( viewElement )
+			} ] );
+
+			// The expected width should be 90% of the editor's editable element's width.
+			const expectedWidth = toPx( new Rect( viewElement ).width * 0.9 );
+
+			expect( balloonToolbar.toolbarView.maxWidth ).to.be.equal( expectedWidth );
+		} );
 	} );
 
 	describe( 'hide()', () => {
@@ -403,6 +474,18 @@ describe( 'BalloonToolbar', () => {
 			clock.tick( 200 );
 			sinon.assert.notCalled( spy );
 		} );
+
+		it( 'should destroy #resizeObserver if is available', () => {
+			const editable = editor.ui.getEditableElement();
+			const resizeObserver = new ResizeObserver( editable, () => {} );
+			const destroySpy = sinon.spy( resizeObserver, 'destroy' );
+
+			balloonToolbar._resizeObserver = resizeObserver;
+
+			balloonToolbar.destroy();
+
+			sinon.assert.calledOnce( destroySpy );
+		} );
 	} );
 
 	describe( 'show and hide triggers', () => {

+ 16 - 0
packages/ckeditor5-ui/tests/toolbar/toolbarview.js

@@ -180,6 +180,22 @@ describe( 'ToolbarView', () => {
 				expect( view.element.classList.contains( 'ck-toolbar_compact' ) ).to.be.true;
 			} );
 		} );
+
+		describe( 'style', () => {
+			it( 'reacts on view#maxWidth', () => {
+				view.maxWidth = '100px';
+				expect( view.element.style.maxWidth ).to.equal( '100px' );
+
+				view.maxWidth = undefined;
+				expect( view.element.style.maxWidth ).to.equal( '' );
+
+				view.maxWidth = null;
+				expect( view.element.style.maxWidth ).to.equal( '' );
+
+				view.maxWidth = '200px';
+				expect( view.element.style.maxWidth ).to.equal( '200px' );
+			} );
+		} );
 	} );
 
 	describe( 'render()', () => {

+ 1 - 1
packages/ckeditor5-ui/theme/icons/color-tile-check.svg

@@ -1 +1 @@
-<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path class="ck-icon__fill" d="M16.935 5.328a2 2 0 010 2.829l-7.778 7.778a2 2 0 01-2.829 0L3.5 13.107a1.999 1.999 0 112.828-2.829l.707.707a1 1 0 001.414 0l5.658-5.657a2 2 0 012.828 0z"/><path d="M14.814 6.035L8.448 12.4a1 1 0 01-1.414 0l-1.413-1.415A1 1 0 104.207 12.4l2.829 2.829a1 1 0 001.414 0l7.778-7.778a1 1 0 10-1.414-1.415z"/></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path class="ck-icon__fill" d="M16.935 5.328a2 2 0 0 1 0 2.829l-7.778 7.778a2 2 0 0 1-2.829 0L3.5 13.107a1.999 1.999 0 1 1 2.828-2.829l.707.707a1 1 0 0 0 1.414 0l5.658-5.657a2 2 0 0 1 2.828 0z"/><path d="M14.814 6.035L8.448 12.4a1 1 0 0 1-1.414 0l-1.413-1.415A1 1 0 1 0 4.207 12.4l2.829 2.829a1 1 0 0 0 1.414 0l7.778-7.778a1 1 0 1 0-1.414-1.415z"/></svg>

+ 1 - 1
packages/ckeditor5-ui/theme/icons/dropdown-arrow.svg

@@ -1 +1 @@
-<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"><path d="M.941 4.523a.75.75 0 111.06-1.06l3.006 3.005 3.005-3.005a.75.75 0 111.06 1.06l-3.549 3.55a.75.75 0 01-1.168-.136L.941 4.523z"/></svg>
+<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"><path d="M.941 4.523a.75.75 0 1 1 1.06-1.06l3.006 3.005 3.005-3.005a.75.75 0 1 1 1.06 1.06l-3.549 3.55a.75.75 0 0 1-1.168-.136L.941 4.523z"/></svg>

+ 1 - 1
packages/ckeditor5-ui/theme/icons/next-arrow.svg

@@ -1 +1 @@
-<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M8.537 14.813a.888.888 0 11-1.254-1.255L10.84 10 7.283 6.442a.888.888 0 111.254-1.255L12.74 9.39a.888.888 0 01-.16 1.382l-4.043 4.042z"/></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M8.537 14.813a.888.888 0 1 1-1.254-1.255L10.84 10 7.283 6.442a.888.888 0 1 1 1.254-1.255L12.74 9.39a.888.888 0 0 1-.16 1.382l-4.043 4.042z"/></svg>

+ 1 - 1
packages/ckeditor5-ui/theme/icons/previous-arrow.svg

@@ -1 +1 @@
-<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M11.463 5.187a.888.888 0 111.254 1.255L9.16 10l3.557 3.557a.888.888 0 11-1.254 1.255L7.26 10.61a.888.888 0 01.16-1.382l4.043-4.042z"/></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M11.463 5.187a.888.888 0 1 1 1.254 1.255L9.16 10l3.557 3.557a.888.888 0 1 1-1.254 1.255L7.26 10.61a.888.888 0 0 1 .16-1.382l4.043-4.042z"/></svg>