UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

170 lines • 4.81 kB
import React, { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react'; const isSameRect = (a, b) => { if (!a || !b) return false; return a.width === b.width && a.height === b.height && a.x === b.x && a.y === b.y; }; export const useElementSize = (ref, { shouldUseChildElement = false, shouldUseParentElement = false } = {}) => { const [size, setSize] = useState(); useEffect(() => { let target = ref.current; if (shouldUseParentElement) { target = ref.current?.parentElement ?? null; } if (shouldUseChildElement) { target = ref.current?.firstElementChild; } if (!target) return undefined; let frameId; const updateSize = nextSize => { if (frameId) { window.cancelAnimationFrame(frameId); } frameId = window.requestAnimationFrame(() => { setSize(currentSize => isSameRect(currentSize, nextSize) ? currentSize : nextSize); }); }; updateSize(target.getBoundingClientRect()); const observer = new ResizeObserver(([entry]) => { if (!entry || entry.target !== target) return; updateSize(entry.contentRect); }); observer.observe(target); return () => { if (frameId) { window.cancelAnimationFrame(frameId); } observer.disconnect(); }; }, [ref, shouldUseChildElement, shouldUseParentElement]); return size; }; const cloneWithTabIndex = node => { if (! /*#__PURE__*/isValidElement(node)) return node; const element = node; if (element.type === React.Fragment) { const children = Children.map(element.props.children, cloneWithTabIndex); return /*#__PURE__*/cloneElement(element, { ...element.props, children }); } const children = element.props.children ? Children.map(element.props.children, cloneWithTabIndex) : element.props.children; if (element.type.displayName === 'Button') { return ( /*#__PURE__*/ // eslint-disable-next-line react/button-has-type React.createElement("button", { tabIndex: -1 }, children) ); } return /*#__PURE__*/cloneElement(element, { ...element.props, tabIndex: -1, children }); }; const getClonedElement = content => { const preventEvents = { onClick: e => e.stopPropagation(), onMouseDown: e => e.stopPropagation(), onMouseUp: e => e.stopPropagation(), onKeyDown: e => e.stopPropagation(), onKeyUp: e => e.stopPropagation(), onFocus: e => e.stopPropagation(), onBlur: e => e.stopPropagation() }; if (typeof content === 'string') { return /*#__PURE__*/React.createElement("span", { tabIndex: -1, "data-measured-clone": true }, content); } if (/*#__PURE__*/isValidElement(content)) { return cloneWithTabIndex(/*#__PURE__*/cloneElement(content, { ...preventEvents, 'data-measured-clone': true })); } return content; }; export const useMeasuredClone = ({ content, shouldPreventTextWrapping = true }) => { const ref = useRef(null); const [size, setSize] = useState({ width: 0, height: 0 }); const clonedElement = getClonedElement(content); useEffect(() => { const measure = () => { if (!ref.current) return; const { offsetWidth: width, offsetHeight: height } = ref.current; setSize({ width: width + (shouldPreventTextWrapping ? 10 : 0), height }); }; measure(); const observer = new ResizeObserver(measure); if (ref.current) observer.observe(ref.current); return () => observer.disconnect(); }, [shouldPreventTextWrapping]); const measuredElement = /*#__PURE__*/React.createElement("div", { "data-measured-clone": "true", ref: ref, style: { position: 'fixed', opacity: 0, pointerEvents: 'none', userSelect: 'none', zIndex: -1, height: 'auto', width: 'auto', visibility: 'hidden' }, inert: "true", tabIndex: -1 }, clonedElement); return { measuredElement, width: size.width, height: size.height }; }; export const useIsMeasuredClone = () => { const ref = useRef(null); const [isClone, setIsClone] = useState(false); useEffect(() => { if (!ref.current) return; let el = ref.current; while (el) { if (el.hasAttribute('data-measured-clone')) { setIsClone(true); return; } el = el.parentElement; } setIsClone(false); }, []); return [isClone, ref]; }; export const useIsInsideDialog = ref => { const [isInsideDialog, setIsInsideDialog] = useState(false); useEffect(() => { if (!ref.current) { return; } setIsInsideDialog(ref.current.closest('.dialog-inner') !== null); }, [ref]); return isInsideDialog; }; //# sourceMappingURL=element.js.map