@ckeditor/ckeditor5-utils
Version:
Miscellaneous utilities used by CKEditor 5.
212 lines (211 loc) • 8.01 kB
TypeScript
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import Rect, { type RectSource } from './rect.js';
/**
* Calculates the `position: absolute` coordinates of a given element so it can be positioned with respect to the
* target in the visually most efficient way, taking various restrictions like viewport or limiter geometry
* into consideration.
*
* **Note**: If there are no position coordinates found that meet the requirements (arguments of this helper),
* `null` is returned.
*
* ```ts
* // The element which is to be positioned.
* const element = document.body.querySelector( '#toolbar' );
*
* // A target to which the element is positioned relatively.
* const target = document.body.querySelector( '#container' );
*
* // Finding the optimal coordinates for the positioning.
* const { left, top, name } = getOptimalPosition( {
* element: element,
* target: target,
*
* // The algorithm will chose among these positions to meet the requirements such
* // as "limiter" element or "fitInViewport", set below. The positions are considered
* // in the order of the array.
* positions: [
* //
* // [ Target ]
* // +-----------------+
* // | Element |
* // +-----------------+
* //
* targetRect => ( {
* top: targetRect.bottom,
* left: targetRect.left,
* name: 'mySouthEastPosition'
* } ),
*
* //
* // +-----------------+
* // | Element |
* // +-----------------+
* // [ Target ]
* //
* ( targetRect, elementRect ) => ( {
* top: targetRect.top - elementRect.height,
* left: targetRect.left,
* name: 'myNorthEastPosition'
* } )
* ],
*
* // Find a position such guarantees the element remains within visible boundaries of <body>.
* limiter: document.body,
*
* // Find a position such guarantees the element remains within visible boundaries of the browser viewport.
* fitInViewport: true
* } );
*
* // The best position which fits into document.body and the viewport. May be useful
* // to set proper class on the `element`.
* console.log( name ); // -> "myNorthEastPosition"
*
* // Using the absolute coordinates which has been found to position the element
* // as in the diagram depicting the "myNorthEastPosition" position.
* element.style.top = top;
* element.style.left = left;
* ```
*
* @param options The input data and configuration of the helper.
*/
export declare function getOptimalPosition({ element, target, positions, limiter, fitInViewport, viewportOffsetConfig }: Options): DomPoint | null;
/**
* A position object which instances are created and used by the {@link module:utils/dom/position~getOptimalPosition} helper.
*
* {@link module:utils/dom/position~DomPoint#top} and {@link module:utils/dom/position~DomPoint#left} properties of the position instance
* translate directly to the `top` and `left` properties in CSS "`position: absolute` coordinate system". If set on the positioned element
* in DOM, they will make it display it in the right place in the viewport.
*/
export interface DomPoint {
/**
* Position name.
*/
readonly name?: string;
/**
* Additional position configuration, as passed from the {@link module:utils/dom/position~PositioningFunction positioning function}.
*
* This object can be use, for instance, to pass through presentation options used by the consumer of the
* {@link module:utils/dom/position~getOptimalPosition} helper.
*/
readonly config?: object;
/**
* The left value in pixels in the CSS `position: absolute` coordinate system.
* Set it on the positioned element in DOM to move it to the position.
*/
readonly left: number;
/**
* The top value in pixels in the CSS `position: absolute` coordinate system.
* Set it on the positioned element in DOM to move it to the position.
*/
readonly top: number;
}
/**
* The `getOptimalPosition()` helper options.
*/
export interface Options {
/**
* Element that is to be positioned.
*/
readonly element: HTMLElement;
/**
* Target with respect to which the `element` is to be positioned.
*/
readonly target: RectSource | (() => RectSource);
/**
* An array of positioning functions.
*
* **Note**: Positioning functions are processed in the order of preference. The first function that works
* in the current environment (e.g. offers the complete fit in the viewport geometry) will be picked by
* `getOptimalPosition()`.
*
* **Note**: Any positioning function returning `null` is ignored.
*/
readonly positions: ReadonlyArray<PositioningFunction>;
/**
* When set, the algorithm will chose position which fits the most in the
* limiter's bounding rect.
*/
readonly limiter?: RectSource | (() => (RectSource | null)) | null;
/**
* When set, the algorithm will chose such a position which fits `element`
* the most inside visible viewport.
*/
readonly fitInViewport?: boolean;
/**
* Viewport offset config object. It restricts the visible viewport available to the `getOptimalPosition()` from each side.
*
* ```ts
* {
* top: 50,
* right: 50,
* bottom: 50,
* left: 50
* }
* ```
*/
readonly viewportOffsetConfig?: {
readonly top?: number;
readonly right?: number;
readonly bottom?: number;
readonly left?: number;
};
}
/**
* A positioning function which, based on positioned element and target {@link module:utils/dom/rect~Rect Rects}, returns rect coordinates
* representing the geometrical relation between them. Used by the {@link module:utils/dom/position~getOptimalPosition} helper.
*
* ```ts
* // This simple position will place the element directly under the target, in the middle:
* //
* // [ Target ]
* // +-----------------+
* // | Element |
* // +-----------------+
* //
* const position = ( targetRect, elementRect, [ viewportRect ] ) => ( {
* top: targetRect.bottom,
* left: targetRect.left + targetRect.width / 2 - elementRect.width / 2,
* name: 'bottomMiddle',
*
* // Note: The config is optional.
* config: {
* zIndex: '999'
* }
* } );
* ```
*
* @param elementRect The rect of the element to be positioned.
* @param targetRect The rect of the target the element (its rect) is relatively positioned to.
* @param viewportRect The rect of the visual browser viewport.
* @returns When the function returns `null`, it will not be considered by {@link module:utils/dom/position~getOptimalPosition}.
*/
export type PositioningFunction = (elementRect: Rect, targetRect: Rect, viewportRect: Rect, limiterRect?: Rect) => PositioningFunctionResult | null;
/**
* The result of {@link module:utils/dom/position~PositioningFunction}.
*/
export interface PositioningFunctionResult {
/**
* The `top` value of the element rect that would represent the position.
*/
top: number;
/**
* The `left` value of the element rect that would represent the position.
*/
left: number;
/**
* The name of the position. It helps the user of the {@link module:utils/dom/position~getOptimalPosition}
* helper to recognize different positioning function results. It will pass through to the {@link module:utils/dom/position~DomPoint}
* returned by the helper.
*/
name?: string;
/**
* An optional configuration that will pass-through the {@link module:utils/dom/position~getOptimalPosition} helper
* to the {@link module:utils/dom/position~DomPoint} returned by this helper.
* This configuration may, for instance, let the user of {@link module:utils/dom/position~getOptimalPosition} know that this particular
* position comes with a certain presentation.
*/
config?: object;
}