@trail-ui/react
Version:
185 lines (183 loc) • 5.29 kB
JavaScript
// 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
};