UNPKG

@i-novus/ui-core

Version:

Базовые UI компоненты

120 lines (119 loc) 4.31 kB
import { useLayoutEffect, useEffect } from 'react'; import { computePosition, offset, shift, arrow, flip } from '@floating-ui/dom'; const isScrollable = (node) => { if (!(node instanceof HTMLElement || node instanceof SVGElement)) { return false; } const style = getComputedStyle(node); return ['overflow', 'overflow-x', 'overflow-y'].some((propertyName) => { const value = style.getPropertyValue(propertyName); return value === 'auto' || value === 'scroll'; }); }; export const getScrollParent = (node) => { if (!node) { return null; } let currentParent = node.parentElement; while (currentParent) { if (isScrollable(currentParent)) { return currentParent; } currentParent = currentParent.parentElement; } return document.scrollingElement || document.documentElement; }; export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; export const debounce = (func, wait, immediate) => { let timeout = null; return function debounced(...args) { const later = () => { timeout = null; if (!immediate) { func.apply(this, args); } }; if (immediate && !timeout) { /** * there's no need to clear the timeout * since we expect it to resolve and set `timeout = null` */ func.apply(this, args); timeout = setTimeout(later, wait); } if (!immediate) { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(later, wait); } }; }; const getBorderWidth = (border) => { let borderWidth = 0; if (border) { const match = `${border}`.match(/(\d+)px/); if (match?.[1]) { borderWidth = Number(match[1]); } else { borderWidth = 1; } } return borderWidth; }; export const computeTooltipPosition = async ({ elementReference = null, tooltipReference = null, tooltipArrowReference = null, place = 'top', offset: offsetValue = 10, strategy = 'absolute', middlewares = [ offset(Array.isArray(offsetValue) ? { mainAxis: offsetValue[0], crossAxis: offsetValue[1] } : Number(offsetValue)), flip(), shift({ padding: 5 }), ], border, }) => { if (!elementReference) { return { tooltipStyles: {}, tooltipArrowStyles: {}, place }; } if (tooltipReference === null) { return { tooltipStyles: {}, tooltipArrowStyles: {}, place }; } const middleware = middlewares; if (tooltipArrowReference) { middleware.push(arrow({ element: tooltipArrowReference, padding: 5 })); return computePosition(elementReference, tooltipReference, { placement: place, strategy, middleware, }).then(({ x, y, placement, middlewareData }) => { const styles = { left: `${x}px`, top: `${y}px`, border }; const { x: arrowX, y: arrowY } = middlewareData.arrow ?? { x: 0, y: 0 }; const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right', // @ts-ignore }[placement.split('-')[0]] ?? 'bottom'; const borderSide = border && { borderBottom: border, borderRight: border, }; const borderWidth = getBorderWidth(border); const arrowStyle = { left: arrowX != null ? `${arrowX}px` : '', top: arrowY != null ? `${arrowY}px` : '', right: '', bottom: '', ...borderSide, [staticSide]: `-${4 + borderWidth}px`, }; return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement }; }); } return computePosition(elementReference, tooltipReference, { placement: 'bottom', strategy, middleware, }).then(({ x, y, placement }) => { const styles = { left: `${x}px`, top: `${y}px` }; return { tooltipStyles: styles, tooltipArrowStyles: {}, place: placement }; }); };