@ckeditor/ckeditor5-ui 35.2.0 → 35.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/package.json +31 -23
  2. package/src/bindings/addkeyboardhandlingforgrid.js +45 -57
  3. package/src/bindings/clickoutsidehandler.js +15 -21
  4. package/src/bindings/injectcsstransitiondisabler.js +16 -20
  5. package/src/bindings/preventdefault.js +6 -8
  6. package/src/bindings/submithandler.js +5 -7
  7. package/src/button/button.js +5 -0
  8. package/src/button/buttonview.js +220 -259
  9. package/src/button/switchbuttonview.js +56 -71
  10. package/src/colorgrid/colorgridview.js +135 -197
  11. package/src/colorgrid/colortileview.js +37 -47
  12. package/src/colorgrid/utils.js +57 -66
  13. package/src/componentfactory.js +79 -93
  14. package/src/dropdown/button/dropdownbutton.js +5 -0
  15. package/src/dropdown/button/dropdownbuttonview.js +44 -57
  16. package/src/dropdown/button/splitbuttonview.js +159 -207
  17. package/src/dropdown/dropdownpanelfocusable.js +5 -0
  18. package/src/dropdown/dropdownpanelview.js +101 -112
  19. package/src/dropdown/dropdownview.js +396 -438
  20. package/src/dropdown/utils.js +164 -213
  21. package/src/editableui/editableuiview.js +125 -141
  22. package/src/editableui/inline/inlineeditableuiview.js +44 -54
  23. package/src/editorui/bodycollection.js +61 -75
  24. package/src/editorui/boxed/boxededitoruiview.js +91 -104
  25. package/src/editorui/editoruiview.js +30 -39
  26. package/src/focuscycler.js +214 -245
  27. package/src/formheader/formheaderview.js +58 -70
  28. package/src/icon/iconview.js +145 -111
  29. package/src/iframe/iframeview.js +37 -49
  30. package/src/index.js +0 -17
  31. package/src/input/inputview.js +170 -198
  32. package/src/inputnumber/inputnumberview.js +48 -56
  33. package/src/inputtext/inputtextview.js +14 -18
  34. package/src/label/labelview.js +44 -53
  35. package/src/labeledfield/labeledfieldview.js +212 -235
  36. package/src/labeledfield/utils.js +39 -57
  37. package/src/labeledinput/labeledinputview.js +190 -221
  38. package/src/list/listitemview.js +40 -50
  39. package/src/list/listseparatorview.js +15 -19
  40. package/src/list/listview.js +94 -115
  41. package/src/model.js +19 -25
  42. package/src/notification/notification.js +151 -202
  43. package/src/panel/balloon/balloonpanelview.js +535 -628
  44. package/src/panel/balloon/contextualballoon.js +611 -732
  45. package/src/panel/sticky/stickypanelview.js +238 -270
  46. package/src/template.js +1049 -1479
  47. package/src/toolbar/balloon/balloontoolbar.js +337 -424
  48. package/src/toolbar/block/blockbuttonview.js +32 -42
  49. package/src/toolbar/block/blocktoolbar.js +375 -477
  50. package/src/toolbar/normalizetoolbarconfig.js +17 -21
  51. package/src/toolbar/toolbarlinebreakview.js +15 -19
  52. package/src/toolbar/toolbarseparatorview.js +15 -19
  53. package/src/toolbar/toolbarview.js +866 -1053
  54. package/src/tooltipmanager.js +324 -353
  55. package/src/view.js +389 -430
  56. package/src/viewcollection.js +147 -178
  57. package/src/button/button.jsdoc +0 -165
  58. package/src/dropdown/button/dropdownbutton.jsdoc +0 -22
  59. package/src/dropdown/dropdownpanelfocusable.jsdoc +0 -27
@@ -2,23 +2,18 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module ui/panel/balloon/balloonpanelview
8
7
  */
9
-
10
8
  import View from '../../view';
11
9
  import { getOptimalPosition } from '@ckeditor/ckeditor5-utils/src/dom/position';
12
10
  import isRange from '@ckeditor/ckeditor5-utils/src/dom/isrange';
13
11
  import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
14
12
  import global from '@ckeditor/ckeditor5-utils/src/dom/global';
15
13
  import { isElement } from 'lodash-es';
16
-
17
14
  import '../../../theme/components/panel/balloonpanel.css';
18
-
19
- const toPx = toUnit( 'px' );
15
+ const toPx = toUnit('px');
20
16
  const defaultLimiterElement = global.document.body;
21
-
22
17
  /**
23
18
  * The balloon panel view class.
24
19
  *
@@ -59,338 +54,285 @@ const defaultLimiterElement = global.document.body;
59
54
  * @extends module:ui/view~View
60
55
  */
61
56
  export default class BalloonPanelView extends View {
62
- /**
63
- * @inheritDoc
64
- */
65
- constructor( locale ) {
66
- super( locale );
67
-
68
- const bind = this.bindTemplate;
69
-
70
- /**
71
- * The absolute top position of the balloon panel in pixels.
72
- *
73
- * @observable
74
- * @default 0
75
- * @member {Number} #top
76
- */
77
- this.set( 'top', 0 );
78
-
79
- /**
80
- * The absolute left position of the balloon panel in pixels.
81
- *
82
- * @observable
83
- * @default 0
84
- * @member {Number} #left
85
- */
86
- this.set( 'left', 0 );
87
-
88
- /**
89
- * The balloon panel's current position. The position name is reflected in the CSS class set
90
- * to the balloon, i.e. `.ck-balloon-panel_arrow_nw` for the "arrow_nw" position. The class
91
- * controls the minor aspects of the balloon's visual appearance like the placement
92
- * of an {@link #withArrow arrow}. To support a new position, an additional CSS must be created.
93
- *
94
- * Default position names correspond with
95
- * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
96
- *
97
- * See the {@link #attachTo} and {@link #pin} methods to learn about custom balloon positions.
98
- *
99
- * @observable
100
- * @default 'arrow_nw'
101
- * @member {'arrow_nw'|'arrow_ne'|'arrow_sw'|'arrow_se'} #position
102
- */
103
- this.set( 'position', 'arrow_nw' );
104
-
105
- /**
106
- * Controls whether the balloon panel is visible or not.
107
- *
108
- * @observable
109
- * @default false
110
- * @member {Boolean} #isVisible
111
- */
112
- this.set( 'isVisible', false );
113
-
114
- /**
115
- * Controls whether the balloon panel has an arrow. The presence of the arrow
116
- * is reflected in the `ck-balloon-panel_with-arrow` CSS class.
117
- *
118
- * @observable
119
- * @default true
120
- * @member {Boolean} #withArrow
121
- */
122
- this.set( 'withArrow', true );
123
-
124
- /**
125
- * An additional CSS class added to the {@link #element}.
126
- *
127
- * @observable
128
- * @member {String} #class
129
- */
130
- this.set( 'class' );
131
-
132
- /**
133
- * A callback that starts pinning the panel when {@link #isVisible} gets
134
- * `true`. Used by {@link #pin}.
135
- *
136
- * @private
137
- * @member {Function} #_pinWhenIsVisibleCallback
138
- */
139
-
140
- /**
141
- * A collection of the child views that creates the balloon panel contents.
142
- *
143
- * @readonly
144
- * @member {module:ui/viewcollection~ViewCollection}
145
- */
146
- this.content = this.createCollection();
147
-
148
- this.setTemplate( {
149
- tag: 'div',
150
- attributes: {
151
- class: [
152
- 'ck',
153
- 'ck-balloon-panel',
154
- bind.to( 'position', value => `ck-balloon-panel_${ value }` ),
155
- bind.if( 'isVisible', 'ck-balloon-panel_visible' ),
156
- bind.if( 'withArrow', 'ck-balloon-panel_with-arrow' ),
157
- bind.to( 'class' )
158
- ],
159
-
160
- style: {
161
- top: bind.to( 'top', toPx ),
162
- left: bind.to( 'left', toPx )
163
- }
164
- },
165
-
166
- children: this.content
167
- } );
168
- }
169
-
170
- /**
171
- * Shows the panel.
172
- *
173
- * See {@link #isVisible}.
174
- */
175
- show() {
176
- this.isVisible = true;
177
- }
178
-
179
- /**
180
- * Hides the panel.
181
- *
182
- * See {@link #isVisible}.
183
- */
184
- hide() {
185
- this.isVisible = false;
186
- }
187
-
188
- /**
189
- * Attaches the panel to a specified {@link module:utils/dom/position~Options#target} with a
190
- * smart positioning heuristics that chooses from available positions to make sure the panel
191
- * is visible to the user i.e. within the limits of the viewport.
192
- *
193
- * This method accepts configuration {@link module:utils/dom/position~Options options}
194
- * to set the `target`, optional `limiter` and `positions` the balloon should choose from.
195
- *
196
- * const panel = new BalloonPanelView( locale );
197
- * const positions = BalloonPanelView.defaultPositions;
198
- *
199
- * panel.render();
200
- *
201
- * // Attach the panel to an element with the "target" id DOM.
202
- * panel.attachTo( {
203
- * target: document.querySelector( '#target' ),
204
- * positions: [
205
- * positions.northArrowSouth,
206
- * positions.southArrowNorth
207
- * ]
208
- * } );
209
- *
210
- * **Note**: Attaching the panel will also automatically {@link #show} it.
211
- *
212
- * **Note**: An attached panel will not follow its target when the window is scrolled or resized.
213
- * See the {@link #pin} method for a more permanent positioning strategy.
214
- *
215
- * @param {module:utils/dom/position~Options} options Positioning options compatible with
216
- * {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
217
- * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
218
- */
219
- attachTo( options ) {
220
- this.show();
221
-
222
- const defaultPositions = BalloonPanelView.defaultPositions;
223
- const positionOptions = Object.assign( {}, {
224
- element: this.element,
225
- positions: [
226
- defaultPositions.southArrowNorth,
227
- defaultPositions.southArrowNorthMiddleWest,
228
- defaultPositions.southArrowNorthMiddleEast,
229
- defaultPositions.southArrowNorthWest,
230
- defaultPositions.southArrowNorthEast,
231
- defaultPositions.northArrowSouth,
232
- defaultPositions.northArrowSouthMiddleWest,
233
- defaultPositions.northArrowSouthMiddleEast,
234
- defaultPositions.northArrowSouthWest,
235
- defaultPositions.northArrowSouthEast,
236
- defaultPositions.viewportStickyNorth
237
- ],
238
- limiter: defaultLimiterElement,
239
- fitInViewport: true
240
- }, options );
241
-
242
- const optimalPosition = BalloonPanelView._getOptimalPosition( positionOptions );
243
-
244
- // Usually browsers make some problems with super accurate values like 104.345px
245
- // so it is better to use int values.
246
- const left = parseInt( optimalPosition.left );
247
- const top = parseInt( optimalPosition.top );
248
-
249
- const { name: position, config = {} } = optimalPosition;
250
- const { withArrow = true } = config;
251
-
252
- Object.assign( this, { top, left, position, withArrow } );
253
- }
254
-
255
- /**
256
- * Works the same way as the {@link #attachTo} method except that the position of the panel is
257
- * continuously updated when:
258
- *
259
- * * any ancestor of the {@link module:utils/dom/position~Options#target}
260
- * or {@link module:utils/dom/position~Options#limiter} is scrolled,
261
- * * the browser window gets resized or scrolled.
262
- *
263
- * Thanks to that, the panel always sticks to the {@link module:utils/dom/position~Options#target}
264
- * and is immune to the changing environment.
265
- *
266
- * const panel = new BalloonPanelView( locale );
267
- * const positions = BalloonPanelView.defaultPositions;
268
- *
269
- * panel.render();
270
- *
271
- * // Pin the panel to an element with the "target" id DOM.
272
- * panel.pin( {
273
- * target: document.querySelector( '#target' ),
274
- * positions: [
275
- * positions.northArrowSouth,
276
- * positions.southArrowNorth
277
- * ]
278
- * } );
279
- *
280
- * To leave the pinned state, use the {@link #unpin} method.
281
- *
282
- * **Note**: Pinning the panel will also automatically {@link #show} it.
283
- *
284
- * @param {module:utils/dom/position~Options} options Positioning options compatible with
285
- * {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
286
- * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
287
- */
288
- pin( options ) {
289
- this.unpin();
290
-
291
- this._pinWhenIsVisibleCallback = () => {
292
- if ( this.isVisible ) {
293
- this._startPinning( options );
294
- } else {
295
- this._stopPinning();
296
- }
297
- };
298
-
299
- this._startPinning( options );
300
-
301
- // Control the state of the listeners depending on whether the panel is visible
302
- // or not.
303
- // TODO: Use on() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
304
- this.listenTo( this, 'change:isVisible', this._pinWhenIsVisibleCallback );
305
- }
306
-
307
- /**
308
- * Stops pinning the panel, as set up by {@link #pin}.
309
- */
310
- unpin() {
311
- if ( this._pinWhenIsVisibleCallback ) {
312
- // Deactivate listeners attached by pin().
313
- this._stopPinning();
314
-
315
- // Deactivate the panel pin() control logic.
316
- // TODO: Use off() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
317
- this.stopListening( this, 'change:isVisible', this._pinWhenIsVisibleCallback );
318
-
319
- this._pinWhenIsVisibleCallback = null;
320
-
321
- this.hide();
322
- }
323
- }
324
-
325
- /**
326
- * Starts managing the pinned state of the panel. See {@link #pin}.
327
- *
328
- * @private
329
- * @param {module:utils/dom/position~Options} options Positioning options compatible with
330
- * {@link module:utils/dom/position~getOptimalPosition}.
331
- */
332
- _startPinning( options ) {
333
- this.attachTo( options );
334
-
335
- const targetElement = getDomElement( options.target );
336
- const limiterElement = options.limiter ? getDomElement( options.limiter ) : defaultLimiterElement;
337
-
338
- // Then we need to listen on scroll event of eny element in the document.
339
- this.listenTo( global.document, 'scroll', ( evt, domEvt ) => {
340
- const scrollTarget = domEvt.target;
341
-
342
- // The position needs to be updated if the positioning target is within the scrolled element.
343
- const isWithinScrollTarget = targetElement && scrollTarget.contains( targetElement );
344
-
345
- // The position needs to be updated if the positioning limiter is within the scrolled element.
346
- const isLimiterWithinScrollTarget = limiterElement && scrollTarget.contains( limiterElement );
347
-
348
- // The positioning target and/or limiter can be a Rect, object etc..
349
- // There's no way to optimize the listener then.
350
- if ( isWithinScrollTarget || isLimiterWithinScrollTarget || !targetElement || !limiterElement ) {
351
- this.attachTo( options );
352
- }
353
- }, { useCapture: true } );
354
-
355
- // We need to listen on window resize event and update position.
356
- this.listenTo( global.window, 'resize', () => {
357
- this.attachTo( options );
358
- } );
359
- }
360
-
361
- /**
362
- * Stops managing the pinned state of the panel. See {@link #pin}.
363
- *
364
- * @private
365
- */
366
- _stopPinning() {
367
- this.stopListening( global.document, 'scroll' );
368
- this.stopListening( global.window, 'resize' );
369
- }
57
+ /**
58
+ * @inheritDoc
59
+ */
60
+ constructor(locale) {
61
+ super(locale);
62
+ const bind = this.bindTemplate;
63
+ /**
64
+ * The absolute top position of the balloon panel in pixels.
65
+ *
66
+ * @observable
67
+ * @default 0
68
+ * @member {Number} #top
69
+ */
70
+ this.set('top', 0);
71
+ /**
72
+ * The absolute left position of the balloon panel in pixels.
73
+ *
74
+ * @observable
75
+ * @default 0
76
+ * @member {Number} #left
77
+ */
78
+ this.set('left', 0);
79
+ /**
80
+ * The balloon panel's current position. The position name is reflected in the CSS class set
81
+ * to the balloon, i.e. `.ck-balloon-panel_arrow_nw` for the "arrow_nw" position. The class
82
+ * controls the minor aspects of the balloon's visual appearance like the placement
83
+ * of an {@link #withArrow arrow}. To support a new position, an additional CSS must be created.
84
+ *
85
+ * Default position names correspond with
86
+ * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
87
+ *
88
+ * See the {@link #attachTo} and {@link #pin} methods to learn about custom balloon positions.
89
+ *
90
+ * @observable
91
+ * @default 'arrow_nw'
92
+ * @member {'arrow_nw'|'arrow_ne'|'arrow_sw'|'arrow_se'} #position
93
+ */
94
+ this.set('position', 'arrow_nw');
95
+ /**
96
+ * Controls whether the balloon panel is visible or not.
97
+ *
98
+ * @observable
99
+ * @default false
100
+ * @member {Boolean} #isVisible
101
+ */
102
+ this.set('isVisible', false);
103
+ /**
104
+ * Controls whether the balloon panel has an arrow. The presence of the arrow
105
+ * is reflected in the `ck-balloon-panel_with-arrow` CSS class.
106
+ *
107
+ * @observable
108
+ * @default true
109
+ * @member {Boolean} #withArrow
110
+ */
111
+ this.set('withArrow', true);
112
+ /**
113
+ * An additional CSS class added to the {@link #element}.
114
+ *
115
+ * @observable
116
+ * @member {String} #class
117
+ */
118
+ this.set('class', undefined);
119
+ /**
120
+ * A callback that starts pinning the panel when {@link #isVisible} gets
121
+ * `true`. Used by {@link #pin}.
122
+ *
123
+ * @private
124
+ * @member {Function} #_pinWhenIsVisibleCallback
125
+ */
126
+ this._pinWhenIsVisibleCallback = null;
127
+ /**
128
+ * A collection of the child views that creates the balloon panel contents.
129
+ *
130
+ * @readonly
131
+ * @member {module:ui/viewcollection~ViewCollection}
132
+ */
133
+ this.content = this.createCollection();
134
+ this.setTemplate({
135
+ tag: 'div',
136
+ attributes: {
137
+ class: [
138
+ 'ck',
139
+ 'ck-balloon-panel',
140
+ bind.to('position', value => `ck-balloon-panel_${value}`),
141
+ bind.if('isVisible', 'ck-balloon-panel_visible'),
142
+ bind.if('withArrow', 'ck-balloon-panel_with-arrow'),
143
+ bind.to('class')
144
+ ],
145
+ style: {
146
+ top: bind.to('top', toPx),
147
+ left: bind.to('left', toPx)
148
+ }
149
+ },
150
+ children: this.content
151
+ });
152
+ }
153
+ /**
154
+ * Shows the panel.
155
+ *
156
+ * See {@link #isVisible}.
157
+ */
158
+ show() {
159
+ this.isVisible = true;
160
+ }
161
+ /**
162
+ * Hides the panel.
163
+ *
164
+ * See {@link #isVisible}.
165
+ */
166
+ hide() {
167
+ this.isVisible = false;
168
+ }
169
+ /**
170
+ * Attaches the panel to a specified {@link module:utils/dom/position~Options#target} with a
171
+ * smart positioning heuristics that chooses from available positions to make sure the panel
172
+ * is visible to the user i.e. within the limits of the viewport.
173
+ *
174
+ * This method accepts configuration {@link module:utils/dom/position~Options options}
175
+ * to set the `target`, optional `limiter` and `positions` the balloon should choose from.
176
+ *
177
+ * const panel = new BalloonPanelView( locale );
178
+ * const positions = BalloonPanelView.defaultPositions;
179
+ *
180
+ * panel.render();
181
+ *
182
+ * // Attach the panel to an element with the "target" id DOM.
183
+ * panel.attachTo( {
184
+ * target: document.querySelector( '#target' ),
185
+ * positions: [
186
+ * positions.northArrowSouth,
187
+ * positions.southArrowNorth
188
+ * ]
189
+ * } );
190
+ *
191
+ * **Note**: Attaching the panel will also automatically {@link #show} it.
192
+ *
193
+ * **Note**: An attached panel will not follow its target when the window is scrolled or resized.
194
+ * See the {@link #pin} method for a more permanent positioning strategy.
195
+ *
196
+ * @param {module:utils/dom/position~Options} options Positioning options compatible with
197
+ * {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
198
+ * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
199
+ */
200
+ attachTo(options) {
201
+ this.show();
202
+ const defaultPositions = BalloonPanelView.defaultPositions;
203
+ const positionOptions = Object.assign({}, {
204
+ element: this.element,
205
+ positions: [
206
+ defaultPositions.southArrowNorth,
207
+ defaultPositions.southArrowNorthMiddleWest,
208
+ defaultPositions.southArrowNorthMiddleEast,
209
+ defaultPositions.southArrowNorthWest,
210
+ defaultPositions.southArrowNorthEast,
211
+ defaultPositions.northArrowSouth,
212
+ defaultPositions.northArrowSouthMiddleWest,
213
+ defaultPositions.northArrowSouthMiddleEast,
214
+ defaultPositions.northArrowSouthWest,
215
+ defaultPositions.northArrowSouthEast,
216
+ defaultPositions.viewportStickyNorth
217
+ ],
218
+ limiter: defaultLimiterElement,
219
+ fitInViewport: true
220
+ }, options);
221
+ const optimalPosition = BalloonPanelView._getOptimalPosition(positionOptions);
222
+ // Usually browsers make some problems with super accurate values like 104.345px
223
+ // so it is better to use int values.
224
+ const left = parseInt(optimalPosition.left);
225
+ const top = parseInt(optimalPosition.top);
226
+ const position = optimalPosition.name;
227
+ const config = optimalPosition.config || {};
228
+ const { withArrow = true } = config;
229
+ this.top = top;
230
+ this.left = left;
231
+ this.position = position;
232
+ this.withArrow = withArrow;
233
+ }
234
+ /**
235
+ * Works the same way as the {@link #attachTo} method except that the position of the panel is
236
+ * continuously updated when:
237
+ *
238
+ * * any ancestor of the {@link module:utils/dom/position~Options#target}
239
+ * or {@link module:utils/dom/position~Options#limiter} is scrolled,
240
+ * * the browser window gets resized or scrolled.
241
+ *
242
+ * Thanks to that, the panel always sticks to the {@link module:utils/dom/position~Options#target}
243
+ * and is immune to the changing environment.
244
+ *
245
+ * const panel = new BalloonPanelView( locale );
246
+ * const positions = BalloonPanelView.defaultPositions;
247
+ *
248
+ * panel.render();
249
+ *
250
+ * // Pin the panel to an element with the "target" id DOM.
251
+ * panel.pin( {
252
+ * target: document.querySelector( '#target' ),
253
+ * positions: [
254
+ * positions.northArrowSouth,
255
+ * positions.southArrowNorth
256
+ * ]
257
+ * } );
258
+ *
259
+ * To leave the pinned state, use the {@link #unpin} method.
260
+ *
261
+ * **Note**: Pinning the panel will also automatically {@link #show} it.
262
+ *
263
+ * @param {module:utils/dom/position~Options} options Positioning options compatible with
264
+ * {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
265
+ * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
266
+ */
267
+ pin(options) {
268
+ this.unpin();
269
+ this._pinWhenIsVisibleCallback = () => {
270
+ if (this.isVisible) {
271
+ this._startPinning(options);
272
+ }
273
+ else {
274
+ this._stopPinning();
275
+ }
276
+ };
277
+ this._startPinning(options);
278
+ // Control the state of the listeners depending on whether the panel is visible
279
+ // or not.
280
+ // TODO: Use on() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
281
+ this.listenTo(this, 'change:isVisible', this._pinWhenIsVisibleCallback);
282
+ }
283
+ /**
284
+ * Stops pinning the panel, as set up by {@link #pin}.
285
+ */
286
+ unpin() {
287
+ if (this._pinWhenIsVisibleCallback) {
288
+ // Deactivate listeners attached by pin().
289
+ this._stopPinning();
290
+ // Deactivate the panel pin() control logic.
291
+ // TODO: Use off() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
292
+ this.stopListening(this, 'change:isVisible', this._pinWhenIsVisibleCallback);
293
+ this._pinWhenIsVisibleCallback = null;
294
+ this.hide();
295
+ }
296
+ }
297
+ /**
298
+ * Starts managing the pinned state of the panel. See {@link #pin}.
299
+ *
300
+ * @private
301
+ * @param {module:utils/dom/position~Options} options Positioning options compatible with
302
+ * {@link module:utils/dom/position~getOptimalPosition}.
303
+ */
304
+ _startPinning(options) {
305
+ this.attachTo(options);
306
+ const targetElement = getDomElement(options.target);
307
+ const limiterElement = options.limiter ? getDomElement(options.limiter) : defaultLimiterElement;
308
+ // Then we need to listen on scroll event of eny element in the document.
309
+ this.listenTo(global.document, 'scroll', (evt, domEvt) => {
310
+ const scrollTarget = domEvt.target;
311
+ // The position needs to be updated if the positioning target is within the scrolled element.
312
+ const isWithinScrollTarget = targetElement && scrollTarget.contains(targetElement);
313
+ // The position needs to be updated if the positioning limiter is within the scrolled element.
314
+ const isLimiterWithinScrollTarget = limiterElement && scrollTarget.contains(limiterElement);
315
+ // The positioning target and/or limiter can be a Rect, object etc..
316
+ // There's no way to optimize the listener then.
317
+ if (isWithinScrollTarget || isLimiterWithinScrollTarget || !targetElement || !limiterElement) {
318
+ this.attachTo(options);
319
+ }
320
+ }, { useCapture: true });
321
+ // We need to listen on window resize event and update position.
322
+ this.listenTo(global.window, 'resize', () => {
323
+ this.attachTo(options);
324
+ });
325
+ }
326
+ /**
327
+ * Stops managing the pinned state of the panel. See {@link #pin}.
328
+ *
329
+ * @private
330
+ */
331
+ _stopPinning() {
332
+ this.stopListening(global.document, 'scroll');
333
+ this.stopListening(global.window, 'resize');
334
+ }
370
335
  }
371
-
372
- // Returns the DOM element for given object or null, if there is none,
373
- // e.g. when the passed object is a Rect instance or so.
374
- //
375
- // @private
376
- // @param {*} object
377
- // @returns {HTMLElement|null}
378
- function getDomElement( object ) {
379
- if ( isElement( object ) ) {
380
- return object;
381
- }
382
-
383
- if ( isRange( object ) ) {
384
- return object.commonAncestorContainer;
385
- }
386
-
387
- if ( typeof object == 'function' ) {
388
- return getDomElement( object() );
389
- }
390
-
391
- return null;
392
- }
393
-
394
336
  /**
395
337
  * A side offset of the arrow tip from the edge of the balloon. Controlled by CSS.
396
338
  *
@@ -409,7 +351,6 @@ function getDomElement( object ) {
409
351
  * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowSideOffset
410
352
  */
411
353
  BalloonPanelView.arrowSideOffset = 25;
412
-
413
354
  /**
414
355
  * A height offset of the arrow from the edge of the balloon. Controlled by CSS.
415
356
  *
@@ -439,9 +380,8 @@ BalloonPanelView.arrowSideOffset = 25;
439
380
  *
440
381
  * @default 10
441
382
  * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowHeightOffset
442
- */
383
+ */
443
384
  BalloonPanelView.arrowHeightOffset = 10;
444
-
445
385
  /**
446
386
  * A vertical offset of the balloon panel from the edge of the viewport if sticky.
447
387
  * It helps in accessing toolbar buttons underneath the balloon panel.
@@ -465,7 +405,6 @@ BalloonPanelView.arrowHeightOffset = 10;
465
405
  * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.stickyVerticalOffset
466
406
  */
467
407
  BalloonPanelView.stickyVerticalOffset = 20;
468
-
469
408
  /**
470
409
  * Function used to calculate the optimal position for the balloon.
471
410
  *
@@ -473,7 +412,6 @@ BalloonPanelView.stickyVerticalOffset = 20;
473
412
  * @member {Function} module:ui/panel/balloon/balloonpanelview~BalloonPanelView._getOptimalPosition
474
413
  */
475
414
  BalloonPanelView._getOptimalPosition = getOptimalPosition;
476
-
477
415
  /**
478
416
  * A default set of positioning functions used by the balloon panel view
479
417
  * when attaching using the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#attachTo} method.
@@ -796,7 +734,24 @@ BalloonPanelView._getOptimalPosition = getOptimalPosition;
796
734
  * module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions
797
735
  */
798
736
  BalloonPanelView.defaultPositions = generatePositions();
799
-
737
+ // Returns the DOM element for given object or null, if there is none,
738
+ // e.g. when the passed object is a Rect instance or so.
739
+ //
740
+ // @private
741
+ // @param {*} object
742
+ // @returns {HTMLElement|null}
743
+ function getDomElement(object) {
744
+ if (isElement(object)) {
745
+ return object;
746
+ }
747
+ if (isRange(object)) {
748
+ return object.commonAncestorContainer;
749
+ }
750
+ if (typeof object == 'function') {
751
+ return getDomElement(object());
752
+ }
753
+ return null;
754
+ }
800
755
  /**
801
756
  * Returns available {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
802
757
  * {@link module:utils/dom/position~PositioningFunction positioning functions} adjusted by the specific offsets.
@@ -818,289 +773,241 @@ BalloonPanelView.defaultPositions = generatePositions();
818
773
  * about {@link module:utils/dom/position~PositioningFunction positioning functions}.
819
774
  * @returns {Object.<String,module:utils/dom/position~PositioningFunction>}
820
775
  */
821
- export function generatePositions( {
822
- sideOffset = BalloonPanelView.arrowSideOffset,
823
- heightOffset = BalloonPanelView.arrowHeightOffset,
824
- stickyVerticalOffset = BalloonPanelView.stickyVerticalOffset,
825
- config
826
- } = {} ) {
827
- return {
828
- // ------- North west
829
-
830
- northWestArrowSouthWest: ( targetRect, balloonRect ) => ( {
831
- top: getNorthTop( targetRect, balloonRect ),
832
- left: targetRect.left - sideOffset,
833
- name: 'arrow_sw',
834
- ...( config && { config } )
835
- } ),
836
-
837
- northWestArrowSouthMiddleWest: ( targetRect, balloonRect ) => ( {
838
- top: getNorthTop( targetRect, balloonRect ),
839
- left: targetRect.left - ( balloonRect.width * .25 ) - sideOffset,
840
- name: 'arrow_smw',
841
- ...( config && { config } )
842
- } ),
843
-
844
- northWestArrowSouth: ( targetRect, balloonRect ) => ( {
845
- top: getNorthTop( targetRect, balloonRect ),
846
- left: targetRect.left - balloonRect.width / 2,
847
- name: 'arrow_s',
848
- ...( config && { config } )
849
- } ),
850
-
851
- northWestArrowSouthMiddleEast: ( targetRect, balloonRect ) => ( {
852
- top: getNorthTop( targetRect, balloonRect ),
853
- left: targetRect.left - ( balloonRect.width * .75 ) + sideOffset,
854
- name: 'arrow_sme',
855
- ...( config && { config } )
856
- } ),
857
-
858
- northWestArrowSouthEast: ( targetRect, balloonRect ) => ( {
859
- top: getNorthTop( targetRect, balloonRect ),
860
- left: targetRect.left - balloonRect.width + sideOffset,
861
- name: 'arrow_se',
862
- ...( config && { config } )
863
- } ),
864
-
865
- // ------- North
866
-
867
- northArrowSouthWest: ( targetRect, balloonRect ) => ( {
868
- top: getNorthTop( targetRect, balloonRect ),
869
- left: targetRect.left + targetRect.width / 2 - sideOffset,
870
- name: 'arrow_sw',
871
- ...( config && { config } )
872
- } ),
873
-
874
- northArrowSouthMiddleWest: ( targetRect, balloonRect ) => ( {
875
- top: getNorthTop( targetRect, balloonRect ),
876
- left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * .25 ) - sideOffset,
877
- name: 'arrow_smw',
878
- ...( config && { config } )
879
- } ),
880
-
881
- northArrowSouth: ( targetRect, balloonRect ) => ( {
882
- top: getNorthTop( targetRect, balloonRect ),
883
- left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
884
- name: 'arrow_s',
885
- ...( config && { config } )
886
- } ),
887
-
888
- northArrowSouthMiddleEast: ( targetRect, balloonRect ) => ( {
889
- top: getNorthTop( targetRect, balloonRect ),
890
- left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * .75 ) + sideOffset,
891
- name: 'arrow_sme',
892
- ...( config && { config } )
893
- } ),
894
-
895
- northArrowSouthEast: ( targetRect, balloonRect ) => ( {
896
- top: getNorthTop( targetRect, balloonRect ),
897
- left: targetRect.left + targetRect.width / 2 - balloonRect.width + sideOffset,
898
- name: 'arrow_se',
899
- ...( config && { config } )
900
- } ),
901
-
902
- // ------- North east
903
-
904
- northEastArrowSouthWest: ( targetRect, balloonRect ) => ( {
905
- top: getNorthTop( targetRect, balloonRect ),
906
- left: targetRect.right - sideOffset,
907
- name: 'arrow_sw',
908
- ...( config && { config } )
909
- } ),
910
-
911
- northEastArrowSouthMiddleWest: ( targetRect, balloonRect ) => ( {
912
- top: getNorthTop( targetRect, balloonRect ),
913
- left: targetRect.right - ( balloonRect.width * .25 ) - sideOffset,
914
- name: 'arrow_smw',
915
- ...( config && { config } )
916
- } ),
917
-
918
- northEastArrowSouth: ( targetRect, balloonRect ) => ( {
919
- top: getNorthTop( targetRect, balloonRect ),
920
- left: targetRect.right - balloonRect.width / 2,
921
- name: 'arrow_s',
922
- ...( config && { config } )
923
- } ),
924
-
925
- northEastArrowSouthMiddleEast: ( targetRect, balloonRect ) => ( {
926
- top: getNorthTop( targetRect, balloonRect ),
927
- left: targetRect.right - ( balloonRect.width * .75 ) + sideOffset,
928
- name: 'arrow_sme',
929
- ...( config && { config } )
930
- } ),
931
-
932
- northEastArrowSouthEast: ( targetRect, balloonRect ) => ( {
933
- top: getNorthTop( targetRect, balloonRect ),
934
- left: targetRect.right - balloonRect.width + sideOffset,
935
- name: 'arrow_se',
936
- ...( config && { config } )
937
- } ),
938
-
939
- // ------- South west
940
-
941
- southWestArrowNorthWest: ( targetRect, balloonRect ) => ( {
942
- top: getSouthTop( targetRect, balloonRect ),
943
- left: targetRect.left - sideOffset,
944
- name: 'arrow_nw',
945
- ...( config && { config } )
946
- } ),
947
-
948
- southWestArrowNorthMiddleWest: ( targetRect, balloonRect ) => ( {
949
- top: getSouthTop( targetRect, balloonRect ),
950
- left: targetRect.left - ( balloonRect.width * .25 ) - sideOffset,
951
- name: 'arrow_nmw',
952
- ...( config && { config } )
953
- } ),
954
-
955
- southWestArrowNorth: ( targetRect, balloonRect ) => ( {
956
- top: getSouthTop( targetRect, balloonRect ),
957
- left: targetRect.left - balloonRect.width / 2,
958
- name: 'arrow_n',
959
- ...( config && { config } )
960
- } ),
961
-
962
- southWestArrowNorthMiddleEast: ( targetRect, balloonRect ) => ( {
963
- top: getSouthTop( targetRect, balloonRect ),
964
- left: targetRect.left - ( balloonRect.width * .75 ) + sideOffset,
965
- name: 'arrow_nme',
966
- ...( config && { config } )
967
- } ),
968
-
969
- southWestArrowNorthEast: ( targetRect, balloonRect ) => ( {
970
- top: getSouthTop( targetRect, balloonRect ),
971
- left: targetRect.left - balloonRect.width + sideOffset,
972
- name: 'arrow_ne',
973
- ...( config && { config } )
974
- } ),
975
-
976
- // ------- South
977
-
978
- southArrowNorthWest: ( targetRect, balloonRect ) => ( {
979
- top: getSouthTop( targetRect, balloonRect ),
980
- left: targetRect.left + targetRect.width / 2 - sideOffset,
981
- name: 'arrow_nw',
982
- ...( config && { config } )
983
- } ),
984
-
985
- southArrowNorthMiddleWest: ( targetRect, balloonRect ) => ( {
986
- top: getSouthTop( targetRect, balloonRect ),
987
- left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * 0.25 ) - sideOffset,
988
- name: 'arrow_nmw',
989
- ...( config && { config } )
990
- } ),
991
-
992
- southArrowNorth: ( targetRect, balloonRect ) => ( {
993
- top: getSouthTop( targetRect, balloonRect ),
994
- left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
995
- name: 'arrow_n',
996
- ...( config && { config } )
997
- } ),
998
-
999
- southArrowNorthMiddleEast: ( targetRect, balloonRect ) => ( {
1000
- top: getSouthTop( targetRect, balloonRect ),
1001
- left: targetRect.left + targetRect.width / 2 - ( balloonRect.width * 0.75 ) + sideOffset,
1002
- name: 'arrow_nme',
1003
- ...( config && { config } )
1004
- } ),
1005
-
1006
- southArrowNorthEast: ( targetRect, balloonRect ) => ( {
1007
- top: getSouthTop( targetRect, balloonRect ),
1008
- left: targetRect.left + targetRect.width / 2 - balloonRect.width + sideOffset,
1009
- name: 'arrow_ne',
1010
- ...( config && { config } )
1011
- } ),
1012
-
1013
- // ------- South east
1014
-
1015
- southEastArrowNorthWest: ( targetRect, balloonRect ) => ( {
1016
- top: getSouthTop( targetRect, balloonRect ),
1017
- left: targetRect.right - sideOffset,
1018
- name: 'arrow_nw',
1019
- ...( config && { config } )
1020
- } ),
1021
-
1022
- southEastArrowNorthMiddleWest: ( targetRect, balloonRect ) => ( {
1023
- top: getSouthTop( targetRect, balloonRect ),
1024
- left: targetRect.right - ( balloonRect.width * .25 ) - sideOffset,
1025
- name: 'arrow_nmw',
1026
- ...( config && { config } )
1027
- } ),
1028
-
1029
- southEastArrowNorth: ( targetRect, balloonRect ) => ( {
1030
- top: getSouthTop( targetRect, balloonRect ),
1031
- left: targetRect.right - balloonRect.width / 2,
1032
- name: 'arrow_n',
1033
- ...( config && { config } )
1034
- } ),
1035
-
1036
- southEastArrowNorthMiddleEast: ( targetRect, balloonRect ) => ( {
1037
- top: getSouthTop( targetRect, balloonRect ),
1038
- left: targetRect.right - ( balloonRect.width * .75 ) + sideOffset,
1039
- name: 'arrow_nme',
1040
- ...( config && { config } )
1041
- } ),
1042
-
1043
- southEastArrowNorthEast: ( targetRect, balloonRect ) => ( {
1044
- top: getSouthTop( targetRect, balloonRect ),
1045
- left: targetRect.right - balloonRect.width + sideOffset,
1046
- name: 'arrow_ne',
1047
- ...( config && { config } )
1048
- } ),
1049
-
1050
- // ------- West
1051
-
1052
- westArrowEast: ( targetRect, balloonRect ) => ( {
1053
- top: targetRect.top + targetRect.height / 2 - balloonRect.height / 2,
1054
- left: targetRect.left - balloonRect.width - heightOffset,
1055
- name: 'arrow_e',
1056
- ...( config && { config } )
1057
- } ),
1058
-
1059
- // ------- East
1060
-
1061
- eastArrowWest: ( targetRect, balloonRect ) => ( {
1062
- top: targetRect.top + targetRect.height / 2 - balloonRect.height / 2,
1063
- left: targetRect.right + heightOffset,
1064
- name: 'arrow_w',
1065
- ...( config && { config } )
1066
- } ),
1067
-
1068
- // ------- Sticky
1069
-
1070
- viewportStickyNorth: ( targetRect, balloonRect, viewportRect ) => {
1071
- if ( !targetRect.getIntersection( viewportRect ) ) {
1072
- return null;
1073
- }
1074
-
1075
- return {
1076
- top: viewportRect.top + stickyVerticalOffset,
1077
- left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
1078
- name: 'arrowless',
1079
- config: {
1080
- withArrow: false,
1081
- ...config
1082
- }
1083
- };
1084
- }
1085
- };
1086
-
1087
- // Returns the top coordinate for positions starting with `north*`.
1088
- //
1089
- // @private
1090
- // @param {utils/dom/rect~Rect} targetRect A rect of the target.
1091
- // @param {utils/dom/rect~Rect} elementRect A rect of the balloon.
1092
- // @returns {Number}
1093
- function getNorthTop( targetRect, balloonRect ) {
1094
- return targetRect.top - balloonRect.height - heightOffset;
1095
- }
1096
-
1097
- // Returns the top coordinate for positions starting with `south*`.
1098
- //
1099
- // @private
1100
- // @param {utils/dom/rect~Rect} targetRect A rect of the target.
1101
- // @param {utils/dom/rect~Rect} elementRect A rect of the balloon.
1102
- // @returns {Number}
1103
- function getSouthTop( targetRect ) {
1104
- return targetRect.bottom + heightOffset;
1105
- }
776
+ export function generatePositions(options = {}) {
777
+ const { sideOffset = BalloonPanelView.arrowSideOffset, heightOffset = BalloonPanelView.arrowHeightOffset, stickyVerticalOffset = BalloonPanelView.stickyVerticalOffset, config } = options;
778
+ return {
779
+ // ------- North west
780
+ northWestArrowSouthWest: (targetRect, balloonRect) => ({
781
+ top: getNorthTop(targetRect, balloonRect),
782
+ left: targetRect.left - sideOffset,
783
+ name: 'arrow_sw',
784
+ ...(config && { config })
785
+ }),
786
+ northWestArrowSouthMiddleWest: (targetRect, balloonRect) => ({
787
+ top: getNorthTop(targetRect, balloonRect),
788
+ left: targetRect.left - (balloonRect.width * .25) - sideOffset,
789
+ name: 'arrow_smw',
790
+ ...(config && { config })
791
+ }),
792
+ northWestArrowSouth: (targetRect, balloonRect) => ({
793
+ top: getNorthTop(targetRect, balloonRect),
794
+ left: targetRect.left - balloonRect.width / 2,
795
+ name: 'arrow_s',
796
+ ...(config && { config })
797
+ }),
798
+ northWestArrowSouthMiddleEast: (targetRect, balloonRect) => ({
799
+ top: getNorthTop(targetRect, balloonRect),
800
+ left: targetRect.left - (balloonRect.width * .75) + sideOffset,
801
+ name: 'arrow_sme',
802
+ ...(config && { config })
803
+ }),
804
+ northWestArrowSouthEast: (targetRect, balloonRect) => ({
805
+ top: getNorthTop(targetRect, balloonRect),
806
+ left: targetRect.left - balloonRect.width + sideOffset,
807
+ name: 'arrow_se',
808
+ ...(config && { config })
809
+ }),
810
+ // ------- North
811
+ northArrowSouthWest: (targetRect, balloonRect) => ({
812
+ top: getNorthTop(targetRect, balloonRect),
813
+ left: targetRect.left + targetRect.width / 2 - sideOffset,
814
+ name: 'arrow_sw',
815
+ ...(config && { config })
816
+ }),
817
+ northArrowSouthMiddleWest: (targetRect, balloonRect) => ({
818
+ top: getNorthTop(targetRect, balloonRect),
819
+ left: targetRect.left + targetRect.width / 2 - (balloonRect.width * .25) - sideOffset,
820
+ name: 'arrow_smw',
821
+ ...(config && { config })
822
+ }),
823
+ northArrowSouth: (targetRect, balloonRect) => ({
824
+ top: getNorthTop(targetRect, balloonRect),
825
+ left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
826
+ name: 'arrow_s',
827
+ ...(config && { config })
828
+ }),
829
+ northArrowSouthMiddleEast: (targetRect, balloonRect) => ({
830
+ top: getNorthTop(targetRect, balloonRect),
831
+ left: targetRect.left + targetRect.width / 2 - (balloonRect.width * .75) + sideOffset,
832
+ name: 'arrow_sme',
833
+ ...(config && { config })
834
+ }),
835
+ northArrowSouthEast: (targetRect, balloonRect) => ({
836
+ top: getNorthTop(targetRect, balloonRect),
837
+ left: targetRect.left + targetRect.width / 2 - balloonRect.width + sideOffset,
838
+ name: 'arrow_se',
839
+ ...(config && { config })
840
+ }),
841
+ // ------- North east
842
+ northEastArrowSouthWest: (targetRect, balloonRect) => ({
843
+ top: getNorthTop(targetRect, balloonRect),
844
+ left: targetRect.right - sideOffset,
845
+ name: 'arrow_sw',
846
+ ...(config && { config })
847
+ }),
848
+ northEastArrowSouthMiddleWest: (targetRect, balloonRect) => ({
849
+ top: getNorthTop(targetRect, balloonRect),
850
+ left: targetRect.right - (balloonRect.width * .25) - sideOffset,
851
+ name: 'arrow_smw',
852
+ ...(config && { config })
853
+ }),
854
+ northEastArrowSouth: (targetRect, balloonRect) => ({
855
+ top: getNorthTop(targetRect, balloonRect),
856
+ left: targetRect.right - balloonRect.width / 2,
857
+ name: 'arrow_s',
858
+ ...(config && { config })
859
+ }),
860
+ northEastArrowSouthMiddleEast: (targetRect, balloonRect) => ({
861
+ top: getNorthTop(targetRect, balloonRect),
862
+ left: targetRect.right - (balloonRect.width * .75) + sideOffset,
863
+ name: 'arrow_sme',
864
+ ...(config && { config })
865
+ }),
866
+ northEastArrowSouthEast: (targetRect, balloonRect) => ({
867
+ top: getNorthTop(targetRect, balloonRect),
868
+ left: targetRect.right - balloonRect.width + sideOffset,
869
+ name: 'arrow_se',
870
+ ...(config && { config })
871
+ }),
872
+ // ------- South west
873
+ southWestArrowNorthWest: targetRect => ({
874
+ top: getSouthTop(targetRect),
875
+ left: targetRect.left - sideOffset,
876
+ name: 'arrow_nw',
877
+ ...(config && { config })
878
+ }),
879
+ southWestArrowNorthMiddleWest: (targetRect, balloonRect) => ({
880
+ top: getSouthTop(targetRect),
881
+ left: targetRect.left - (balloonRect.width * .25) - sideOffset,
882
+ name: 'arrow_nmw',
883
+ ...(config && { config })
884
+ }),
885
+ southWestArrowNorth: (targetRect, balloonRect) => ({
886
+ top: getSouthTop(targetRect),
887
+ left: targetRect.left - balloonRect.width / 2,
888
+ name: 'arrow_n',
889
+ ...(config && { config })
890
+ }),
891
+ southWestArrowNorthMiddleEast: (targetRect, balloonRect) => ({
892
+ top: getSouthTop(targetRect),
893
+ left: targetRect.left - (balloonRect.width * .75) + sideOffset,
894
+ name: 'arrow_nme',
895
+ ...(config && { config })
896
+ }),
897
+ southWestArrowNorthEast: (targetRect, balloonRect) => ({
898
+ top: getSouthTop(targetRect),
899
+ left: targetRect.left - balloonRect.width + sideOffset,
900
+ name: 'arrow_ne',
901
+ ...(config && { config })
902
+ }),
903
+ // ------- South
904
+ southArrowNorthWest: targetRect => ({
905
+ top: getSouthTop(targetRect),
906
+ left: targetRect.left + targetRect.width / 2 - sideOffset,
907
+ name: 'arrow_nw',
908
+ ...(config && { config })
909
+ }),
910
+ southArrowNorthMiddleWest: (targetRect, balloonRect) => ({
911
+ top: getSouthTop(targetRect),
912
+ left: targetRect.left + targetRect.width / 2 - (balloonRect.width * 0.25) - sideOffset,
913
+ name: 'arrow_nmw',
914
+ ...(config && { config })
915
+ }),
916
+ southArrowNorth: (targetRect, balloonRect) => ({
917
+ top: getSouthTop(targetRect),
918
+ left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
919
+ name: 'arrow_n',
920
+ ...(config && { config })
921
+ }),
922
+ southArrowNorthMiddleEast: (targetRect, balloonRect) => ({
923
+ top: getSouthTop(targetRect),
924
+ left: targetRect.left + targetRect.width / 2 - (balloonRect.width * 0.75) + sideOffset,
925
+ name: 'arrow_nme',
926
+ ...(config && { config })
927
+ }),
928
+ southArrowNorthEast: (targetRect, balloonRect) => ({
929
+ top: getSouthTop(targetRect),
930
+ left: targetRect.left + targetRect.width / 2 - balloonRect.width + sideOffset,
931
+ name: 'arrow_ne',
932
+ ...(config && { config })
933
+ }),
934
+ // ------- South east
935
+ southEastArrowNorthWest: targetRect => ({
936
+ top: getSouthTop(targetRect),
937
+ left: targetRect.right - sideOffset,
938
+ name: 'arrow_nw',
939
+ ...(config && { config })
940
+ }),
941
+ southEastArrowNorthMiddleWest: (targetRect, balloonRect) => ({
942
+ top: getSouthTop(targetRect),
943
+ left: targetRect.right - (balloonRect.width * .25) - sideOffset,
944
+ name: 'arrow_nmw',
945
+ ...(config && { config })
946
+ }),
947
+ southEastArrowNorth: (targetRect, balloonRect) => ({
948
+ top: getSouthTop(targetRect),
949
+ left: targetRect.right - balloonRect.width / 2,
950
+ name: 'arrow_n',
951
+ ...(config && { config })
952
+ }),
953
+ southEastArrowNorthMiddleEast: (targetRect, balloonRect) => ({
954
+ top: getSouthTop(targetRect),
955
+ left: targetRect.right - (balloonRect.width * .75) + sideOffset,
956
+ name: 'arrow_nme',
957
+ ...(config && { config })
958
+ }),
959
+ southEastArrowNorthEast: (targetRect, balloonRect) => ({
960
+ top: getSouthTop(targetRect),
961
+ left: targetRect.right - balloonRect.width + sideOffset,
962
+ name: 'arrow_ne',
963
+ ...(config && { config })
964
+ }),
965
+ // ------- West
966
+ westArrowEast: (targetRect, balloonRect) => ({
967
+ top: targetRect.top + targetRect.height / 2 - balloonRect.height / 2,
968
+ left: targetRect.left - balloonRect.width - heightOffset,
969
+ name: 'arrow_e',
970
+ ...(config && { config })
971
+ }),
972
+ // ------- East
973
+ eastArrowWest: (targetRect, balloonRect) => ({
974
+ top: targetRect.top + targetRect.height / 2 - balloonRect.height / 2,
975
+ left: targetRect.right + heightOffset,
976
+ name: 'arrow_w',
977
+ ...(config && { config })
978
+ }),
979
+ // ------- Sticky
980
+ viewportStickyNorth: (targetRect, balloonRect, viewportRect) => {
981
+ if (!targetRect.getIntersection(viewportRect)) {
982
+ return null;
983
+ }
984
+ return {
985
+ top: viewportRect.top + stickyVerticalOffset,
986
+ left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
987
+ name: 'arrowless',
988
+ config: {
989
+ withArrow: false,
990
+ ...config
991
+ }
992
+ };
993
+ }
994
+ };
995
+ // Returns the top coordinate for positions starting with `north*`.
996
+ //
997
+ // @private
998
+ // @param {utils/dom/rect~Rect} targetRect A rect of the target.
999
+ // @param {utils/dom/rect~Rect} elementRect A rect of the balloon.
1000
+ // @returns {Number}
1001
+ function getNorthTop(targetRect, balloonRect) {
1002
+ return targetRect.top - balloonRect.height - heightOffset;
1003
+ }
1004
+ // Returns the top coordinate for positions starting with `south*`.
1005
+ //
1006
+ // @private
1007
+ // @param {utils/dom/rect~Rect} targetRect A rect of the target.
1008
+ // @param {utils/dom/rect~Rect} elementRect A rect of the balloon.
1009
+ // @returns {Number}
1010
+ function getSouthTop(targetRect) {
1011
+ return targetRect.bottom + heightOffset;
1012
+ }
1106
1013
  }