@ckeditor/ckeditor5-utils 29.2.0 → 32.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE.md +2 -2
  2. package/package.json +6 -8
  3. package/src/areconnectedthroughproperties.js +1 -1
  4. package/src/ckeditorerror.js +40 -6
  5. package/src/collection.js +2 -2
  6. package/src/comparearrays.js +1 -1
  7. package/src/config.js +1 -1
  8. package/src/count.js +1 -1
  9. package/src/diff.js +1 -1
  10. package/src/difftochanges.js +1 -1
  11. package/src/dom/createelement.js +1 -1
  12. package/src/dom/emittermixin.js +118 -52
  13. package/src/dom/getancestors.js +1 -1
  14. package/src/dom/getborderwidths.js +1 -1
  15. package/src/dom/getcommonancestor.js +1 -1
  16. package/src/dom/getdatafromelement.js +1 -1
  17. package/src/dom/getpositionedancestor.js +1 -1
  18. package/src/dom/global.js +1 -1
  19. package/src/dom/indexof.js +1 -1
  20. package/src/dom/insertat.js +1 -1
  21. package/src/dom/iscomment.js +20 -0
  22. package/src/dom/isnode.js +1 -1
  23. package/src/dom/isrange.js +1 -1
  24. package/src/dom/istext.js +1 -1
  25. package/src/dom/isvisible.js +25 -0
  26. package/src/dom/iswindow.js +1 -1
  27. package/src/dom/position.js +293 -190
  28. package/src/dom/rect.js +1 -1
  29. package/src/dom/remove.js +1 -1
  30. package/src/dom/resizeobserver.js +1 -1
  31. package/src/dom/scroll.js +1 -1
  32. package/src/dom/setdatainelement.js +1 -1
  33. package/src/dom/tounit.js +1 -1
  34. package/src/elementreplacer.js +1 -1
  35. package/src/emittermixin.js +1 -1
  36. package/src/env.js +38 -1
  37. package/src/eventinfo.js +1 -1
  38. package/src/fastdiff.js +1 -1
  39. package/src/first.js +1 -1
  40. package/src/focustracker.js +1 -1
  41. package/src/index.js +2 -1
  42. package/src/isiterable.js +1 -1
  43. package/src/keyboard.js +7 -1
  44. package/src/keystrokehandler.js +1 -1
  45. package/src/language.js +1 -1
  46. package/src/locale.js +1 -1
  47. package/src/mapsequal.js +1 -1
  48. package/src/mix.js +1 -1
  49. package/src/nth.js +1 -1
  50. package/src/objecttomap.js +1 -1
  51. package/src/observablemixin.js +1 -1
  52. package/src/priorities.js +1 -1
  53. package/src/spy.js +1 -1
  54. package/src/toarray.js +1 -1
  55. package/src/tomap.js +1 -1
  56. package/src/translation-service.js +2 -2
  57. package/src/uid.js +1 -1
  58. package/src/unicode.js +1 -1
  59. package/src/version.js +2 -2
  60. package/CHANGELOG.md +0 -324
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
@@ -13,6 +13,8 @@ import getPositionedAncestor from './getpositionedancestor';
13
13
  import getBorderWidths from './getborderwidths';
14
14
  import { isFunction } from 'lodash-es';
15
15
 
16
+ // @if CK_DEBUG_POSITION // import { RectDrawer } from '@ckeditor/ckeditor5-minimap/src/utils';
17
+
16
18
  /**
17
19
  * Calculates the `position: absolute` coordinates of a given element so it can be positioned with respect to the
18
20
  * target in the visually most efficient way, taking various restrictions like viewport or limiter geometry
@@ -74,10 +76,10 @@ import { isFunction } from 'lodash-es';
74
76
  * element.style.top = top;
75
77
  * element.style.left = left;
76
78
  *
77
- * @param {module:utils/dom/position~Options} options Positioning options object.
79
+ * @param {module:utils/dom/position~Options} options The input data and configuration of the helper.
78
80
  * @returns {module:utils/dom/position~Position}
79
81
  */
80
- export function getOptimalPosition( { element, target, positions, limiter, fitInViewport } ) {
82
+ export function getOptimalPosition( { element, target, positions, limiter, fitInViewport, viewportOffsetConfig } ) {
81
83
  // If the {@link module:utils/dom/position~Options#target} is a function, use what it returns.
82
84
  // https://github.com/ckeditor/ckeditor5-utils/issues/157
83
85
  if ( isFunction( target ) ) {
@@ -94,52 +96,54 @@ export function getOptimalPosition( { element, target, positions, limiter, fitIn
94
96
  const elementRect = new Rect( element );
95
97
  const targetRect = new Rect( target );
96
98
 
97
- let bestPositionRect;
98
- let bestPositionName;
99
+ let bestPosition;
100
+
101
+ // @if CK_DEBUG_POSITION // RectDrawer.clear();
102
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( targetRect, { outlineWidth: '5px' }, 'Target' );
103
+
104
+ const positionOptions = { targetRect, elementRect, positionedElementAncestor };
99
105
 
100
106
  // If there are no limits, just grab the very first position and be done with that drama.
101
107
  if ( !limiter && !fitInViewport ) {
102
- [ bestPositionName, bestPositionRect ] = getPositionNameAndRect( positions[ 0 ], targetRect, elementRect );
108
+ bestPosition = new Position( positions[ 0 ], positionOptions );
103
109
  } else {
104
110
  const limiterRect = limiter && new Rect( limiter ).getVisible();
105
- const viewportRect = fitInViewport && new Rect( global.window );
106
- const bestPosition = getBestPositionNameAndRect( positions, { targetRect, elementRect, limiterRect, viewportRect } );
111
+ const viewportRect = fitInViewport && getConstrainedViewportRect( viewportOffsetConfig );
107
112
 
108
- // If there's no best position found, i.e. when all intersections have no area because
109
- // rects have no width or height, then just use the first available position.
110
- [ bestPositionName, bestPositionRect ] = bestPosition || getPositionNameAndRect( positions[ 0 ], targetRect, elementRect );
111
- }
113
+ // @if CK_DEBUG_POSITION // if ( viewportRect ) {
114
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( viewportRect, { outlineWidth: '5px' }, 'Viewport' );
115
+ // @if CK_DEBUG_POSITION // }
112
116
 
113
- let absoluteRectCoordinates = getAbsoluteRectCoordinates( bestPositionRect );
117
+ // @if CK_DEBUG_POSITION // if ( limiter ) {
118
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( limiterRect, { outlineWidth: '5px', outlineColor: 'green' }, 'Visible limiter' );
119
+ // @if CK_DEBUG_POSITION // }
114
120
 
115
- if ( positionedElementAncestor ) {
116
- absoluteRectCoordinates = shiftRectCoordinatesDueToPositionedAncestor( absoluteRectCoordinates, positionedElementAncestor );
121
+ Object.assign( positionOptions, { limiterRect, viewportRect } );
122
+
123
+ // If there's no best position found, i.e. when all intersections have no area because
124
+ // rects have no width or height, then just use the first available position.
125
+ bestPosition = getBestPosition( positions, positionOptions ) || new Position( positions[ 0 ], positionOptions );
117
126
  }
118
127
 
119
- return {
120
- left: absoluteRectCoordinates.left,
121
- top: absoluteRectCoordinates.top,
122
- name: bestPositionName
123
- };
128
+ return bestPosition;
124
129
  }
125
130
 
126
- // For given position function, returns a corresponding `Rect` instance.
131
+ // Returns a viewport `Rect` shrunk by the viewport offset config from all sides.
127
132
  //
128
133
  // @private
129
- // @param {Function} position A function returning {@link module:utils/dom/position~Position}.
130
- // @param {utils/dom/rect~Rect} targetRect A rect of the target.
131
- // @param {utils/dom/rect~Rect} elementRect A rect of positioned element.
132
- // @returns {Array|null} An array containing position name and its Rect (or null if position should be ignored).
133
- function getPositionNameAndRect( position, targetRect, elementRect ) {
134
- const positionData = position( targetRect, elementRect );
135
-
136
- if ( !positionData ) {
137
- return null;
138
- }
134
+ // @param {Object} An object containing viewportOffset config.
135
+ // @returns {utils/dom/rect~Rect} A shrunken rect of the viewport.
136
+ function getConstrainedViewportRect( viewportOffsetConfig ) {
137
+ viewportOffsetConfig = Object.assign( { top: 0, bottom: 0, left: 0, right: 0 }, viewportOffsetConfig );
139
138
 
140
- const { left, top, name } = positionData;
139
+ const viewportRect = new Rect( global.window );
141
140
 
142
- return [ name, elementRect.clone().moveTo( left, top ) ];
141
+ viewportRect.top += viewportOffsetConfig.top;
142
+ viewportRect.height -= viewportOffsetConfig.top;
143
+ viewportRect.bottom -= viewportOffsetConfig.bottom;
144
+ viewportRect.height -= viewportOffsetConfig.bottom;
145
+
146
+ return viewportRect;
143
147
  }
144
148
 
145
149
  // For a given array of positioning functions, returns such that provides the best
@@ -148,157 +152,50 @@ function getPositionNameAndRect( position, targetRect, elementRect ) {
148
152
  // @private
149
153
  //
150
154
  // @param {Object} options
151
- // @param {module:utils/dom/position~Options#positions} positions Functions returning {@link module:utils/dom/position~Position}
152
- // to be checked, in the order of preference.
155
+ // @param {module:utils/dom/position~Options#positions} positions Functions returning
156
+ // {@link module:utils/dom/position~Position}to be checked, in the order of preference.
153
157
  // @param {Object} options
154
158
  // @param {utils/dom/rect~Rect} options.targetRect A rect of the {@link module:utils/dom/position~Options#target}.
155
- // @param {utils/dom/rect~Rect} options.elementRect A rect of positioned {@link module:utils/dom/position~Options#element}.
159
+ // @param {utils/dom/rect~Rect} options.elementRect A rect of positioned
160
+ // {@link module:utils/dom/position~Options#element}.
156
161
  // @param {utils/dom/rect~Rect} options.limiterRect A rect of the {@link module:utils/dom/position~Options#limiter}.
157
- // @param {utils/dom/rect~Rect} options.viewportRect A rect of the viewport.
162
+ // @param {utils/dom/rect~Rect} options.viewportRect A rect of the {@link module:utils/dom/position~Options#viewport}.
158
163
  //
159
164
  // @returns {Array} An array containing the name of the position and it's rect.
160
- function getBestPositionNameAndRect( positions, options ) {
161
- const { elementRect, viewportRect } = options;
162
-
163
- // This is when element is fully visible.
164
- const elementRectArea = elementRect.getArea();
165
-
166
- // Let's calculate intersection areas for positions. It will end early if best match is found.
167
- const processedPositions = processPositionsToAreas( positions, options );
168
-
169
- // First let's check all positions that fully fit in the viewport.
170
- if ( viewportRect ) {
171
- const processedPositionsInViewport = processedPositions.filter( ( { viewportIntersectArea } ) => {
172
- return viewportIntersectArea === elementRectArea;
173
- } );
174
-
175
- // Try to find best position from those which fit completely in viewport.
176
- const bestPositionData = getBestOfProcessedPositions( processedPositionsInViewport, elementRectArea );
177
-
178
- if ( bestPositionData ) {
179
- return bestPositionData;
180
- }
181
- }
182
-
183
- // Either there is no viewportRect or there is no position that fits completely in the viewport.
184
- return getBestOfProcessedPositions( processedPositions, elementRectArea );
185
- }
186
-
187
- // For a given array of positioning functions, calculates intersection areas for them.
188
- //
189
- // Note: If some position fully fits into the `limiterRect`, it will be returned early, without further consideration
190
- // of other positions.
191
- //
192
- // @private
193
- //
194
- // @param {module:utils/dom/position~Options#positions} positions Functions returning {@link module:utils/dom/position~Position}
195
- // to be checked, in the order of preference.
196
- // @param {Object} options
197
- // @param {utils/dom/rect~Rect} options.targetRect A rect of the {@link module:utils/dom/position~Options#target}.
198
- // @param {utils/dom/rect~Rect} options.elementRect A rect of positioned {@link module:utils/dom/position~Options#element}.
199
- // @param {utils/dom/rect~Rect} options.limiterRect A rect of the {@link module:utils/dom/position~Options#limiter}.
200
- // @param {utils/dom/rect~Rect} options.viewportRect A rect of the viewport.
201
- //
202
- // @returns {Array.<Object>} Array of positions with calculated intersection areas. Each item is an object containing:
203
- // * {String} positionName Name of position.
204
- // * {utils/dom/rect~Rect} positionRect Rect of position.
205
- // * {Number} limiterIntersectArea Area of intersection of the position with limiter part that is in the viewport.
206
- // * {Number} viewportIntersectArea Area of intersection of the position with viewport.
207
- function processPositionsToAreas( positions, { targetRect, elementRect, limiterRect, viewportRect } ) {
208
- const processedPositions = [];
165
+ function getBestPosition( positions, options ) {
166
+ const { elementRect } = options;
209
167
 
210
168
  // This is when element is fully visible.
211
169
  const elementRectArea = elementRect.getArea();
212
170
 
213
- for ( const position of positions ) {
214
- const positionData = getPositionNameAndRect( position, targetRect, elementRect );
215
-
216
- if ( !positionData ) {
217
- continue;
218
- }
219
-
220
- const [ positionName, positionRect ] = positionData;
221
- let limiterIntersectArea = 0;
222
- let viewportIntersectArea = 0;
223
-
224
- if ( limiterRect ) {
225
- if ( viewportRect ) {
226
- // Consider only the part of the limiter which is visible in the viewport. So the limiter is getting limited.
227
- const limiterViewportIntersectRect = limiterRect.getIntersection( viewportRect );
228
-
229
- if ( limiterViewportIntersectRect ) {
230
- // If the limiter is within the viewport, then check the intersection between that part of the
231
- // limiter and actual position.
232
- limiterIntersectArea = limiterViewportIntersectRect.getIntersectionArea( positionRect );
233
- }
234
- } else {
235
- limiterIntersectArea = limiterRect.getIntersectionArea( positionRect );
236
- }
237
- }
171
+ const positionInstances = positions
172
+ .map( positioningFunction => new Position( positioningFunction, options ) )
173
+ // Some positioning functions may return `null` if they don't want to participate.
174
+ .filter( position => !!position.name );
238
175
 
239
- if ( viewportRect ) {
240
- viewportIntersectArea = viewportRect.getIntersectionArea( positionRect );
241
- }
176
+ let maxFitFactor = 0;
177
+ let bestPosition = null;
242
178
 
243
- const processedPosition = {
244
- positionName,
245
- positionRect,
246
- limiterIntersectArea,
247
- viewportIntersectArea
248
- };
179
+ for ( const position of positionInstances ) {
180
+ const { _limiterIntersectionArea, _viewportIntersectionArea } = position;
249
181
 
250
182
  // If a such position is found that element is fully contained by the limiter then, obviously,
251
183
  // there will be no better one, so finishing.
252
- if ( limiterIntersectArea === elementRectArea ) {
253
- return [ processedPosition ];
254
- }
255
-
256
- processedPositions.push( processedPosition );
257
- }
258
-
259
- return processedPositions;
260
- }
261
-
262
- // For a given array of processed position data (with calculated Rects for positions and intersection areas)
263
- // returns such that provides the best fit of the `elementRect` into the `limiterRect` and `viewportRect` at the same time.
264
- //
265
- // **Note**: It will return early if some position fully fits into the `limiterRect`.
266
- //
267
- // @private
268
- // @param {Array.<Object>} Array of positions with calculated intersection areas (in order of preference).
269
- // Each item is an object containing:
270
- //
271
- // * {String} positionName Name of position.
272
- // * {utils/dom/rect~Rect} positionRect Rect of position.
273
- // * {Number} limiterIntersectArea Area of intersection of the position with limiter part that is in the viewport.
274
- // * {Number} viewportIntersectArea Area of intersection of the position with viewport.
275
- //
276
- // @param {Number} elementRectArea Area of positioned {@link module:utils/dom/position~Options#element}.
277
- // @returns {Array|null} An array containing the name of the position and it's rect, or null if not found.
278
- function getBestOfProcessedPositions( processedPositions, elementRectArea ) {
279
- let maxFitFactor = 0;
280
- let bestPositionRect;
281
- let bestPositionName;
282
-
283
- for ( const { positionName, positionRect, limiterIntersectArea, viewportIntersectArea } of processedPositions ) {
284
- // If a such position is found that element is fully container by the limiter then, obviously,
285
- // there will be no better one, so finishing.
286
- if ( limiterIntersectArea === elementRectArea ) {
287
- return [ positionName, positionRect ];
184
+ if ( _limiterIntersectionArea === elementRectArea ) {
185
+ return position;
288
186
  }
289
187
 
290
- // To maximize both viewport and limiter intersection areas we use distance on viewportIntersectArea
291
- // and limiterIntersectArea plane (without sqrt because we are looking for max value).
292
- const fitFactor = viewportIntersectArea ** 2 + limiterIntersectArea ** 2;
188
+ // To maximize both viewport and limiter intersection areas we use distance on _viewportIntersectionArea
189
+ // and _limiterIntersectionArea plane (without sqrt because we are looking for max value).
190
+ const fitFactor = _viewportIntersectionArea ** 2 + _limiterIntersectionArea ** 2;
293
191
 
294
192
  if ( fitFactor > maxFitFactor ) {
295
193
  maxFitFactor = fitFactor;
296
- bestPositionRect = positionRect;
297
- bestPositionName = positionName;
194
+ bestPosition = position;
298
195
  }
299
196
  }
300
197
 
301
- return bestPositionRect ? [ bestPositionName, bestPositionRect ] : null;
198
+ return bestPosition;
302
199
  }
303
200
 
304
201
  // For a given absolute Rect coordinates object and a positioned element ancestor, it returns an object with
@@ -310,41 +207,44 @@ function getBestOfProcessedPositions( processedPositions, elementRectArea ) {
310
207
  //
311
208
  // @private
312
209
  //
313
- // @param {Object} absoluteRectCoordinates An object with absolute rect coordinates.
314
- // @param {Object} absoluteRectCoordinates.top
315
- // @param {Object} absoluteRectCoordinates.left
210
+ // @param {utils/dom/rect~Rect} rect A rect with absolute rect coordinates.
211
+ // @param {Number} rect.top
212
+ // @param {Number} rect.left
316
213
  // @param {HTMLElement} positionedElementAncestor An ancestor element that should be considered.
317
214
  //
318
- // @returns {Object} An object corresponding to `absoluteRectCoordinates` input but with values shifted
215
+ // @returns {utils/dom/rect~Rect} A rect corresponding to `absoluteRect` input but with values shifted
319
216
  // to make up for the positioned element ancestor.
320
- function shiftRectCoordinatesDueToPositionedAncestor( { left, top }, positionedElementAncestor ) {
321
- const ancestorPosition = getAbsoluteRectCoordinates( new Rect( positionedElementAncestor ) );
217
+ function shiftRectToCompensatePositionedAncestor( rect, positionedElementAncestor ) {
218
+ const ancestorPosition = getRectForAbsolutePositioning( new Rect( positionedElementAncestor ) );
322
219
  const ancestorBorderWidths = getBorderWidths( positionedElementAncestor );
323
220
 
221
+ let moveX = 0;
222
+ let moveY = 0;
223
+
324
224
  // (https://github.com/ckeditor/ckeditor5-ui-default/issues/126)
325
225
  // If there's some positioned ancestor of the panel, then its `Rect` must be taken into
326
226
  // consideration. `Rect` is always relative to the viewport while `position: absolute` works
327
227
  // with respect to that positioned ancestor.
328
- left -= ancestorPosition.left;
329
- top -= ancestorPosition.top;
228
+ moveX -= ancestorPosition.left;
229
+ moveY -= ancestorPosition.top;
330
230
 
331
231
  // (https://github.com/ckeditor/ckeditor5-utils/issues/139)
332
232
  // If there's some positioned ancestor of the panel, not only its position must be taken into
333
233
  // consideration (see above) but also its internal scrolls. Scroll have an impact here because `Rect`
334
234
  // is relative to the viewport (it doesn't care about scrolling), while `position: absolute`
335
235
  // must compensate that scrolling.
336
- left += positionedElementAncestor.scrollLeft;
337
- top += positionedElementAncestor.scrollTop;
236
+ moveX += positionedElementAncestor.scrollLeft;
237
+ moveY += positionedElementAncestor.scrollTop;
338
238
 
339
239
  // (https://github.com/ckeditor/ckeditor5-utils/issues/139)
340
240
  // If there's some positioned ancestor of the panel, then its `Rect` includes its CSS `borderWidth`
341
241
  // while `position: absolute` positioning does not consider it.
342
242
  // E.g. `{ position: absolute, top: 0, left: 0 }` means upper left corner of the element,
343
243
  // not upper-left corner of its border.
344
- left -= ancestorBorderWidths.left;
345
- top -= ancestorBorderWidths.top;
244
+ moveX -= ancestorBorderWidths.left;
245
+ moveY -= ancestorBorderWidths.top;
346
246
 
347
- return { left, top };
247
+ rect.moveBy( moveX, moveY );
348
248
  }
349
249
 
350
250
  // DOMRect (also Rect) works in a scroll–independent geometry but `position: absolute` doesn't.
@@ -353,13 +253,172 @@ function shiftRectCoordinatesDueToPositionedAncestor( { left, top }, positionedE
353
253
  // @private
354
254
  // @param {utils/dom/rect~Rect} rect A rect to be converted.
355
255
  // @returns {Object} Object containing `left` and `top` properties, in absolute coordinates.
356
- function getAbsoluteRectCoordinates( { left, top } ) {
256
+ function getRectForAbsolutePositioning( rect ) {
357
257
  const { scrollX, scrollY } = global.window;
358
258
 
359
- return {
360
- left: left + scrollX,
361
- top: top + scrollY
362
- };
259
+ return rect.clone().moveBy( scrollX, scrollY );
260
+ }
261
+
262
+ /**
263
+ * A position class which instances are created and used by the {@link module:utils/dom/position~getOptimalPosition} helper.
264
+ *
265
+ * {@link module:utils/dom/position~Position#top} and {@link module:utils/dom/position~Position#left} properties of the position instance
266
+ * translate directly to the `top` and `left` properties in CSS "`position: absolute` coordinate system". If set on the positioned element
267
+ * in DOM, they will make it display it in the right place in the viewport.
268
+ */
269
+ export class Position {
270
+ /**
271
+ * Creates an instance of the {@link module:utils/dom/position~Position} class.
272
+ *
273
+ * @param {module:utils/dom/position~positioningFunction} [positioningFunction] function The function that defines the expected
274
+ * coordinates the positioned element should move to.
275
+ * @param {Object} [options] options object.
276
+ * @param {module:utils/dom/rect~Rect} options.elementRect The positioned element rect.
277
+ * @param {module:utils/dom/rect~Rect} options.targetRect The target element rect.
278
+ * @param {module:utils/dom/rect~Rect} options.viewportRect The viewport rect.
279
+ * @param {HTMLElement|null} [options.positionedElementAncestor] Nearest element ancestor element which CSS position is not "static".
280
+ */
281
+ constructor( positioningFunction, options ) {
282
+ const positioningFunctionOutput = positioningFunction( options.targetRect, options.elementRect, options.viewportRect );
283
+
284
+ // Nameless position for a function that didn't participate.
285
+ if ( !positioningFunctionOutput ) {
286
+ return;
287
+ }
288
+
289
+ const { left, top, name, config } = positioningFunctionOutput;
290
+
291
+ Object.assign( this, { name, config } );
292
+
293
+ this._positioningFunctionCorrdinates = { left, top };
294
+ this._options = options;
295
+
296
+ /**
297
+ * Position name.
298
+ *
299
+ * @readonly
300
+ * @member {String} #name
301
+ */
302
+
303
+ /**
304
+ * Additional position configuration, as passed from the {@link module:utils/dom/position~positioningFunction positioning function}.
305
+ *
306
+ * This object can be use, for instance, to pass through presentation options used by the consumer of the
307
+ * {@link module:utils/dom/position~getOptimalPosition} helper.
308
+ *
309
+ * @readonly
310
+ * @member {Object} #config
311
+ */
312
+ }
313
+
314
+ /**
315
+ * The left value in pixels in the CSS `position: absolute` coordinate system.
316
+ * Set it on the positioned element in DOM to move it to the position.
317
+ *
318
+ * @readonly
319
+ * @type {Number}
320
+ */
321
+ get left() {
322
+ return this._absoluteRect.left;
323
+ }
324
+
325
+ /**
326
+ * The top value in pixels in the CSS `position: absolute` coordinate system.
327
+ * Set it on the positioned element in DOM to move it to the position.
328
+ *
329
+ * @readonly
330
+ * @type {Number}
331
+ */
332
+ get top() {
333
+ return this._absoluteRect.top;
334
+ }
335
+
336
+ /**
337
+ * An intersection area between positioned element and limiter within viewport constraints.
338
+ *
339
+ * @readonly
340
+ * @private
341
+ * @type {Number}
342
+ */
343
+ get _limiterIntersectionArea() {
344
+ const limiterRect = this._options.limiterRect;
345
+
346
+ if ( limiterRect ) {
347
+ const viewportRect = this._options.viewportRect;
348
+
349
+ if ( viewportRect ) {
350
+ // Consider only the part of the limiter which is visible in the viewport. So the limiter is getting limited.
351
+ const limiterViewportIntersectRect = limiterRect.getIntersection( viewportRect );
352
+
353
+ if ( limiterViewportIntersectRect ) {
354
+ // If the limiter is within the viewport, then check the intersection between that part of the
355
+ // limiter and actual position.
356
+ return limiterViewportIntersectRect.getIntersectionArea( this._rect );
357
+ }
358
+ } else {
359
+ return limiterRect.getIntersectionArea( this._rect );
360
+ }
361
+ }
362
+
363
+ return 0;
364
+ }
365
+
366
+ /**
367
+ * An intersection area between positioned element and viewport.
368
+ *
369
+ * @readonly
370
+ * @private
371
+ * @type {Number}
372
+ */
373
+ get _viewportIntersectionArea() {
374
+ const viewportRect = this._options.viewportRect;
375
+
376
+ if ( viewportRect ) {
377
+ return viewportRect.getIntersectionArea( this._rect );
378
+ }
379
+
380
+ return 0;
381
+ }
382
+
383
+ /**
384
+ * An already positioned element rect. A clone of the element rect passed to the constructor
385
+ * but placed in the viewport according to the positioning function.
386
+ *
387
+ * @private
388
+ * @type {module:utils/dom/rect~Rect}
389
+ */
390
+ get _rect() {
391
+ if ( this._cachedRect ) {
392
+ return this._cachedRect;
393
+ }
394
+
395
+ this._cachedRect = this._options.elementRect.clone().moveTo(
396
+ this._positioningFunctionCorrdinates.left,
397
+ this._positioningFunctionCorrdinates.top
398
+ );
399
+
400
+ return this._cachedRect;
401
+ }
402
+
403
+ /**
404
+ * An already absolutely positioned element rect. See ({@link #_rect}).
405
+ *
406
+ * @private
407
+ * @type {module:utils/dom/rect~Rect}
408
+ */
409
+ get _absoluteRect() {
410
+ if ( this._cachedAbsoluteRect ) {
411
+ return this._cachedAbsoluteRect;
412
+ }
413
+
414
+ this._cachedAbsoluteRect = getRectForAbsolutePositioning( this._rect );
415
+
416
+ if ( this._options.positionedElementAncestor ) {
417
+ shiftRectToCompensatePositionedAncestor( this._cachedAbsoluteRect, this._options.positionedElementAncestor );
418
+ }
419
+
420
+ return this._cachedAbsoluteRect;
421
+ }
363
422
  }
364
423
 
365
424
  /**
@@ -381,12 +440,15 @@ function getAbsoluteRectCoordinates( { left, top } ) {
381
440
  */
382
441
 
383
442
  /**
384
- * An array of functions which return {@link module:utils/dom/position~Position} relative
385
- * to the `target`, in the order of preference.
443
+ * An array of positioning functions.
444
+ *
445
+ * **Note**: Positioning functions are processed in the order of preference. The first function that works
446
+ * in the current environment (e.g. offers the complete fit in the viewport geometry) will be picked by
447
+ * `getOptimalPosition()`.
386
448
  *
387
- * **Note**: If a function returns `null`, it is ignored by the `getOptimalPosition()`.
449
+ * **Note**: Any positioning function returning `null` is ignored.
388
450
  *
389
- * @member {Array.<Function>} #positions
451
+ * @member {Array.<module:utils/dom/position~positioningFunction>} #positions
390
452
  */
391
453
 
392
454
  /**
@@ -404,12 +466,53 @@ function getAbsoluteRectCoordinates( { left, top } ) {
404
466
  */
405
467
 
406
468
  /**
407
- * An object describing a position in `position: absolute` coordinate
408
- * system, along with position name.
469
+ * Viewport offset config object. It restricts the visible viewport available to the `getOptimalPosition()` from each side.
409
470
  *
410
- * @typedef {Object} module:utils/dom/position~Position
471
+ * {
472
+ * top: 50,
473
+ * right: 50,
474
+ * bottom: 50,
475
+ * left: 50
476
+ * }
477
+ *
478
+ * @member {Object} #viewportOffsetConfig
479
+ */
480
+
481
+ /**
482
+ * A positioning function which, based on positioned element and target {@link module:utils/dom/rect~Rect Rects}, returns rect coordinates
483
+ * representing the geometrical relation between them. Used by the {@link module:utils/dom/position~getOptimalPosition} helper.
484
+ *
485
+ * // This simple position will place the element directly under the target, in the middle:
486
+ * //
487
+ * // [ Target ]
488
+ * // +-----------------+
489
+ * // | Element |
490
+ * // +-----------------+
491
+ * //
492
+ * const position = ( targetRect, elementRect, [ viewportRect ] ) => ( {
493
+ * top: targetRect.bottom,
494
+ * left: targetRect.left + targetRect.width / 2 - elementRect.width / 2,
495
+ * name: 'bottomMiddle',
496
+ *
497
+ * // Note: The config is optional.
498
+ * config: {
499
+ * zIndex: '999'
500
+ * }
501
+ * } );
411
502
  *
412
- * @property {Number} top Top position offset.
413
- * @property {Number} left Left position offset.
414
- * @property {String} name Name of the position.
503
+ * @callback module:utils/dom/position~positioningFunction
504
+ * @param {module:utils/dom/rect~Rect} elementRect The rect of the element to be positioned.
505
+ * @param {module:utils/dom/rect~Rect} targetRect The rect of the target the element (its rect) is relatively positioned to.
506
+ * @param {module:utils/dom/rect~Rect} viewportRect The rect of the visual browser viewport.
507
+ * @returns {Object|null} return When the function returns `null`, it will not be considered by
508
+ * {@link module:utils/dom/position~getOptimalPosition}.
509
+ * @returns {Number} return.top The `top` value of the element rect that would represent the position.
510
+ * @returns {Number} return.left The `left` value of the element rect that would represent the position.
511
+ * @returns {Number} return.name The name of the position. It helps the user of the {@link module:utils/dom/position~getOptimalPosition}
512
+ * helper to recognize different positioning function results. It will pass through to the {@link module:utils/dom/position~Position}
513
+ * returned by the helper.
514
+ * @returns {Number} [return.config] An optional configuration that will pass-through the
515
+ * {@link module:utils/dom/position~getOptimalPosition} helper to the {@link module:utils/dom/position~Position} returned by this helper.
516
+ * This configuration may, for instance, let the user of {@link module:utils/dom/position~getOptimalPosition} know that this particular
517
+ * position comes with a certain presentation.
415
518
  */
package/src/dom/rect.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
package/src/dom/remove.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
package/src/dom/scroll.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
package/src/dom/tounit.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
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
5