@ckeditor/ckeditor5-utils 47.4.0-alpha.6 → 47.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-utils",
3
- "version": "47.4.0-alpha.6",
3
+ "version": "47.5.0-alpha.0",
4
4
  "description": "Miscellaneous utilities used by CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -12,7 +12,7 @@
12
12
  "type": "module",
13
13
  "main": "src/index.js",
14
14
  "dependencies": {
15
- "@ckeditor/ckeditor5-ui": "47.4.0-alpha.6",
15
+ "@ckeditor/ckeditor5-ui": "47.5.0-alpha.0",
16
16
  "es-toolkit": "1.39.5"
17
17
  },
18
18
  "author": "CKSource (http://cksource.com/)",
@@ -51,4 +51,4 @@
51
51
  },
52
52
  "./package.json": "./package.json"
53
53
  }
54
- }
54
+ }
package/src/dom/rect.js CHANGED
@@ -11,7 +11,8 @@ import { getBorderWidths } from './getborderwidths.js';
11
11
  import { isText } from './istext.js';
12
12
  import { getPositionedAncestor } from './getpositionedancestor.js';
13
13
  import { global } from './global.js';
14
- const rectProperties = ['top', 'right', 'bottom', 'left', 'width', 'height'];
14
+ const RECT_PROPERTIES = ['top', 'right', 'bottom', 'left', 'width', 'height'];
15
+ const POSITIONING_VALUES = new Set(['relative', 'absolute', 'fixed', 'sticky']);
15
16
  /**
16
17
  * A helper class representing a `ClientRect` object, e.g. value returned by
17
18
  * the native `object.getBoundingClientRect()` method. Provides a set of methods
@@ -236,50 +237,21 @@ export class Rect {
236
237
  }
237
238
  let child = source;
238
239
  let parent = source.parentNode || source.commonAncestorContainer;
239
- let absolutelyPositionedChildElement;
240
+ let lastPositionedChildElement;
240
241
  // Check the ancestors all the way up to the <body>.
241
242
  while (parent && !isBody(parent)) {
242
- const isParentOverflowVisible = getElementOverflow(parent) === 'visible';
243
- if (child instanceof HTMLElement && getElementPosition(child) === 'absolute') {
244
- absolutelyPositionedChildElement = child;
243
+ const isNonClippingParent = getElementOverflow(parent) === 'visible';
244
+ if (isPositioned(child)) {
245
+ lastPositionedChildElement = child;
245
246
  }
246
- const parentElementPosition = getElementPosition(parent);
247
- // The child will be cropped only if it has `position: absolute` and the parent has `position: relative` + some overflow.
248
- // Otherwise there's no chance of visual clipping and the parent can be skipped
249
- // https://github.com/ckeditor/ckeditor5/issues/14107.
250
- //
251
- // condition: isParentOverflowVisible
252
- // +---------------------------+
253
- // | #parent |
254
- // | (overflow: visible) |
255
- // | +-----------+---------------+
256
- // | | child |
257
- // | +-----------+---------------+
258
- // +---------------------------+
247
+ // 1. If a parent has overflow: visible, it can be safely skipped in consideration for any parent-child configuration.
248
+ // 2. If a parent has any other overflow (it clips), for the actual clipping to happen the following must be true:
249
+ // * the last positioned child must have `position: absolute`,
250
+ // * the parent must have a position other than `position: static`.
259
251
  //
260
- // condition: absolutelyPositionedChildElement && parentElementPosition === 'relative' && isParentOverflowVisible
261
- // +---------------------------+
262
- // | parent |
263
- // | (position: relative;) |
264
- // | (overflow: visible;) |
265
- // | +-----------+---------------+
266
- // | | child |
267
- // | | (position: absolute;) |
268
- // | +-----------+---------------+
269
- // +---------------------------+
270
- //
271
- // condition: absolutelyPositionedChildElement && parentElementPosition !== 'relative'
272
- // +---------------------------+
273
- // | parent |
274
- // | (position: static;) |
275
- // | +-----------+---------------+
276
- // | | child |
277
- // | | (position: absolute;) |
278
- // | +-----------+---------------+
279
- // +---------------------------+
280
- if (isParentOverflowVisible ||
281
- absolutelyPositionedChildElement && ((parentElementPosition === 'relative' && isParentOverflowVisible) ||
282
- parentElementPosition !== 'relative')) {
252
+ // https://github.com/ckeditor/ckeditor5/issues/14107.
253
+ if (isNonClippingParent ||
254
+ (lastPositionedChildElement && getElementPosition(lastPositionedChildElement) === 'absolute' && !isPositioned(parent))) {
283
255
  child = parent;
284
256
  parent = parent.parentNode;
285
257
  continue;
@@ -310,7 +282,7 @@ export class Rect {
310
282
  * @returns `true` when Rects are equal. `false` otherwise.
311
283
  */
312
284
  isEqual(anotherRect) {
313
- for (const prop of rectProperties) {
285
+ for (const prop of RECT_PROPERTIES) {
314
286
  if (this[prop] !== anotherRect[prop]) {
315
287
  return false;
316
288
  }
@@ -392,7 +364,11 @@ export class Rect {
392
364
  const clientRects = Array.from(range.getClientRects());
393
365
  if (clientRects.length) {
394
366
  for (const rect of clientRects) {
395
- rects.push(new Rect(rect));
367
+ const r = new Rect(rect);
368
+ // Point the rect source to the DOM range instead of of the DOM client rect to allow proper clipping,
369
+ // in `Rect#getVisible()` method.
370
+ r._source = range;
371
+ rects.push(r);
396
372
  }
397
373
  }
398
374
  // If there's no client rects for the Range, use parent container's bounding rect
@@ -446,7 +422,7 @@ export class Rect {
446
422
  * Acquires all the rect properties from the passed source.
447
423
  */
448
424
  function copyRectProperties(rect, source) {
449
- for (const p of rectProperties) {
425
+ for (const p of RECT_PROPERTIES) {
450
426
  rect[p] = source[p];
451
427
  }
452
428
  }
@@ -471,7 +447,7 @@ function isDomElement(value) {
471
447
  * Returns the value of the `position` style of an `HTMLElement`.
472
448
  */
473
449
  function getElementPosition(element) {
474
- return element instanceof HTMLElement ? element.ownerDocument.defaultView.getComputedStyle(element).position : 'static';
450
+ return element.ownerDocument.defaultView.getComputedStyle(element).position;
475
451
  }
476
452
  /**
477
453
  * Returns the value of the `overflow` style of an `HTMLElement` or a `Range`.
@@ -479,6 +455,12 @@ function getElementPosition(element) {
479
455
  function getElementOverflow(element) {
480
456
  return element instanceof HTMLElement ? element.ownerDocument.defaultView.getComputedStyle(element).overflow : 'visible';
481
457
  }
458
+ /**
459
+ * Checks if the given node is positioned in any other way than `position: static`.
460
+ */
461
+ function isPositioned(node) {
462
+ return node instanceof HTMLElement && POSITIONING_VALUES.has(getElementPosition(node));
463
+ }
482
464
  /**
483
465
  * For a given absolute Rect coordinates object and a positioned element ancestor, it updates its
484
466
  * coordinates that make up for the position and the scroll of the ancestor.
@@ -68,5 +68,11 @@ export declare function scrollViewportToShowTarget<T extends boolean, U extends
68
68
  * to be maintained while scrolling.
69
69
  * @param limiterElement The outermost ancestor that should be scrolled. If specified, it can prevent
70
70
  * scrolling the whole page.
71
+ * @param alignToTop When set `true`, the function will make sure the `target` is scrolled up
72
+ * to the top boundary of the scrollable ancestors if scrolled up. When not set (default), the `target`
73
+ * will be revealed by scrolling as little as possible. This option will not affect target elements that must be
74
+ * scrolled down because they will appear at the top of the boundary anyway.
75
+ * @param forceScroll When set `true`, the `target` will be aligned to the top of scrollable ancestors
76
+ * whether it is already visible or not. This option will only work when `alignToTop` is `true`
71
77
  */
72
- export declare function scrollAncestorsToShowTarget(target: HTMLElement | Range, ancestorOffset?: number, limiterElement?: HTMLElement): void;
78
+ export declare function scrollAncestorsToShowTarget(target: HTMLElement | Range, ancestorOffset?: number, limiterElement?: HTMLElement, alignToTop?: boolean, forceScroll?: true): void;
package/src/dom/scroll.js CHANGED
@@ -141,14 +141,22 @@ export function scrollViewportToShowTarget({ target, viewportOffset = 0, ancesto
141
141
  * to be maintained while scrolling.
142
142
  * @param limiterElement The outermost ancestor that should be scrolled. If specified, it can prevent
143
143
  * scrolling the whole page.
144
+ * @param alignToTop When set `true`, the function will make sure the `target` is scrolled up
145
+ * to the top boundary of the scrollable ancestors if scrolled up. When not set (default), the `target`
146
+ * will be revealed by scrolling as little as possible. This option will not affect target elements that must be
147
+ * scrolled down because they will appear at the top of the boundary anyway.
148
+ * @param forceScroll When set `true`, the `target` will be aligned to the top of scrollable ancestors
149
+ * whether it is already visible or not. This option will only work when `alignToTop` is `true`
144
150
  */
145
- export function scrollAncestorsToShowTarget(target, ancestorOffset, limiterElement) {
151
+ export function scrollAncestorsToShowTarget(target, ancestorOffset, limiterElement, alignToTop, forceScroll) {
146
152
  const targetParent = getParentElement(target);
147
153
  scrollAncestorsToShowRect({
148
154
  parent: targetParent,
149
155
  getRect: () => new Rect(target),
150
156
  ancestorOffset,
151
- limiterElement
157
+ limiterElement,
158
+ alignToTop,
159
+ forceScroll
152
160
  });
153
161
  }
154
162
  /**
package/src/version.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
4
  */
5
- export declare const version = "47.4.0-alpha.6";
5
+ export declare const version = "47.5.0-alpha.0";
6
6
  export declare const releaseDate: Date;
7
7
  declare global {
8
8
  var CKEDITOR_VERSION: string;
package/src/version.js CHANGED
@@ -6,9 +6,9 @@
6
6
  * @module utils/version
7
7
  */
8
8
  import { CKEditorError } from './ckeditorerror.js';
9
- export const version = '47.4.0-alpha.6';
9
+ export const version = '47.5.0-alpha.0';
10
10
  // The second argument is not a month. It is `monthIndex` and starts from `0`.
11
- export const releaseDate = new Date(2026, 0, 14);
11
+ export const releaseDate = new Date(2026, 1, 4);
12
12
  /* istanbul ignore next -- @preserve */
13
13
  if (globalThis.CKEDITOR_VERSION) {
14
14
  /**