UNPKG

@trail-ui/react

Version:
185 lines (183 loc) 5.29 kB
// src/_utils/utils.tsx import { useLayoutEffect } from "@react-aria/utils"; import React, { createContext, useCallback, useContext, useImperativeHandle, useMemo, useRef, useState } from "react"; import { useIsSSR } from "react-aria"; import ReactDOM from "react-dom"; import { Fragment, jsx } from "react/jsx-runtime"; function useRenderProps(props) { let { className, style, children, defaultClassName, defaultChildren, values } = props; return useMemo(() => { let computedClassName; let computedStyle; let computedChildren; if (typeof className === "function") { computedClassName = className(values); } else { computedClassName = className; } if (typeof style === "function") { computedStyle = style(values); } else { computedStyle = style; } if (typeof children === "function") { computedChildren = children(values); } else if (children == null) { computedChildren = defaultChildren; } else { computedChildren = children; } return { className: computedClassName != null ? computedClassName : defaultClassName, style: computedStyle, children: computedChildren, "data-rac": "" }; }, [className, style, children, defaultClassName, defaultChildren, values]); } function useSlot() { let [hasSlot, setHasSlot] = useState(true); let hasRun = useRef(false); let ref = useCallback((el) => { hasRun.current = true; setHasSlot(!!el); }, []); useLayoutEffect(() => { if (!hasRun.current) { setHasSlot(false); } }, []); return [ref, hasSlot]; } function useEnterAnimation(ref, isReady = true) { let [isEntering, setEntering] = useState(true); useAnimation( ref, isEntering && isReady, useCallback(() => setEntering(false), []) ); return isEntering && isReady; } function useExitAnimation(ref, isOpen) { let [isExiting, setExiting] = useState(false); let [exitState, setExitState] = useState("idle"); if (!isOpen && ref.current && exitState === "idle") { isExiting = true; setExiting(true); setExitState("exiting"); } if (!ref.current && exitState === "exited") { setExitState("idle"); } useAnimation( ref, isExiting, useCallback(() => { setExitState("exited"); setExiting(false); }, []) ); return isExiting; } function useAnimation(ref, isActive, onEnd) { let prevAnimation = useRef(null); if (isActive && ref.current) { prevAnimation.current = window.getComputedStyle(ref.current).animation; } useLayoutEffect(() => { if (isActive && ref.current) { let computedStyle = window.getComputedStyle(ref.current); if (computedStyle.animationName !== "none" && computedStyle.animation !== prevAnimation.current) { let onAnimationEnd = (e) => { if (e.target === ref.current) { element.removeEventListener("animationend", onAnimationEnd); ReactDOM.flushSync(() => { onEnd(); }); } }; let element = ref.current; element.addEventListener("animationend", onAnimationEnd); return () => { element.removeEventListener("animationend", onAnimationEnd); }; } else { onEnd(); } } }, [ref, isActive, onEnd]); } if (typeof HTMLTemplateElement !== "undefined") { const getFirstChild = Object.getOwnPropertyDescriptor(Node.prototype, "firstChild").get; Object.defineProperty(HTMLTemplateElement.prototype, "firstChild", { configurable: true, enumerable: true, get: function() { if (this.dataset.reactAriaHidden) { return this.content.firstChild; } else { return getFirstChild.call(this); } } }); } var HiddenContext = createContext(false); var hiddenFragment = typeof DocumentFragment !== "undefined" ? new DocumentFragment() : null; function Hidden(props) { let isHidden = useContext(HiddenContext); let isSSR = useIsSSR(); if (isHidden) { return /* @__PURE__ */ jsx(Fragment, { children: props.children }); } let children = /* @__PURE__ */ jsx(HiddenContext.Provider, { value: true, children: props.children }); return isSSR ? /* @__PURE__ */ jsx("template", { "data-react-aria-hidden": true, children }) : ReactDOM.createPortal(children, hiddenFragment); } function createHideableComponent(fn) { let Wrapper = (props, ref) => { let isHidden = useContext(HiddenContext); if (isHidden) { return null; } return fn(props, ref); }; Wrapper.displayName = fn.displayName || fn.name; return React.forwardRef(Wrapper); } function removeDataAttributes(props) { const prefix = /^(data-.*)$/; let filteredProps = {}; for (const prop in props) { if (!prefix.test(prop)) { filteredProps[prop] = props[prop]; } } return filteredProps; } function useDOMRef(ref) { const domRef = useRef(null); useImperativeHandle(ref, () => domRef.current); return domRef; } function replaceSpacesWithHyphens(str) { return str.toLowerCase().trim().replace(/\s+/g, "-"); } export { useRenderProps, useSlot, useEnterAnimation, useExitAnimation, HiddenContext, Hidden, createHideableComponent, removeDataAttributes, useDOMRef, replaceSpacesWithHyphens };