// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. /*----------------------------------------------------------------------------- | Copyright (c) 2014-2017, PhosphorJS Contributors | | Distributed under the terms of the BSD 3-Clause License. | | The full license is in the file LICENSE, distributed with this software. |----------------------------------------------------------------------------*/ /** * The namespace for element related utilities. */ export namespace ElementExt { /** * An object which holds the border and padding data for an element. */ export interface IBoxSizing { /** * The top border width, in pixels. */ borderTop: number; /** * The left border width, in pixels. */ borderLeft: number; /** * The right border width, in pixels. */ borderRight: number; /** * The bottom border width, in pixels. */ borderBottom: number; /** * The top padding width, in pixels. */ paddingTop: number; /** * The left padding width, in pixels. */ paddingLeft: number; /** * The right padding width, in pixels. */ paddingRight: number; /** * The bottom padding width, in pixels. */ paddingBottom: number; /** * The sum of horizontal border and padding. */ horizontalSum: number; /** * The sum of vertical border and padding. */ verticalSum: number; } /** * Compute the box sizing for an element. * * @param element - The element of interest. * * @returns The box sizing data for the specified element. */ export function boxSizing(element: Element): IBoxSizing { let style = window.getComputedStyle(element); let bt = parseFloat(style.borderTopWidth!) || 0; let bl = parseFloat(style.borderLeftWidth!) || 0; let br = parseFloat(style.borderRightWidth!) || 0; let bb = parseFloat(style.borderBottomWidth!) || 0; let pt = parseFloat(style.paddingTop!) || 0; let pl = parseFloat(style.paddingLeft!) || 0; let pr = parseFloat(style.paddingRight!) || 0; let pb = parseFloat(style.paddingBottom!) || 0; let hs = bl + pl + pr + br; let vs = bt + pt + pb + bb; return { borderTop: bt, borderLeft: bl, borderRight: br, borderBottom: bb, paddingTop: pt, paddingLeft: pl, paddingRight: pr, paddingBottom: pb, horizontalSum: hs, verticalSum: vs }; } /** * An object which holds the min and max size data for an element. */ export interface ISizeLimits { /** * The minimum width, in pixels. */ minWidth: number; /** * The minimum height, in pixels. */ minHeight: number; /** * The maximum width, in pixels. */ maxWidth: number; /** * The maximum height, in pixels. */ maxHeight: number; } /** * Compute the size limits for an element. * * @param element - The element of interest. * * @returns The size limit data for the specified element. */ export function sizeLimits(element: Element): ISizeLimits { let style = window.getComputedStyle(element); let minWidth = parseFloat(style.minWidth!) || 0; let minHeight = parseFloat(style.minHeight!) || 0; let maxWidth = parseFloat(style.maxWidth!) || Infinity; let maxHeight = parseFloat(style.maxHeight!) || Infinity; maxWidth = Math.max(minWidth, maxWidth); maxHeight = Math.max(minHeight, maxHeight); return { minWidth, minHeight, maxWidth, maxHeight }; } /** * Test whether a client position lies within an element. * * @param element - The DOM element of interest. * * @param clientX - The client X coordinate of interest. * * @param clientY - The client Y coordinate of interest. * * @returns Whether the point is within the given element. */ export function hitTest( element: Element, clientX: number, clientY: number ): boolean { let rect = element.getBoundingClientRect(); return ( clientX >= rect.left && clientX < rect.right && clientY >= rect.top && clientY < rect.bottom ); } /** * Vertically scroll an element into view if needed. * * @param area - The scroll area element. * * @param element - The element of interest. * * #### Notes * This follows the "nearest" behavior of the native `scrollIntoView` * method, which is not supported by all browsers. * https://drafts.csswg.org/cssom-view/#element-scrolling-members * * If the element fully covers the visible area or is fully contained * within the visible area, no scrolling will take place. Otherwise, * the nearest edges of the area and element are aligned. */ export function scrollIntoViewIfNeeded( area: Element, element: Element ): void { let ar = area.getBoundingClientRect(); let er = element.getBoundingClientRect(); if (er.top <= ar.top && er.bottom >= ar.bottom) { return; } if (er.top < ar.top && er.height <= ar.height) { area.scrollTop -= ar.top - er.top; return; } if (er.bottom > ar.bottom && er.height >= ar.height) { area.scrollTop -= ar.top - er.top; return; } if (er.top < ar.top && er.height > ar.height) { area.scrollTop -= ar.bottom - er.bottom; return; } if (er.bottom > ar.bottom && er.height < ar.height) { area.scrollTop -= ar.bottom - er.bottom; return; } } }