UNPKG

@stratakit/foundations

Version:

Foundational pieces of StrataKit

113 lines (112 loc) 2.86 kB
import * as React from "react"; import { isBrowser, supportsPopover } from "./~utils.js"; const useLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect; function useControlledState(initialValue, controlledState, setControlledState) { const [uncontrolledState, setUncontrolledState] = React.useState(initialValue); const state = React.useMemo( () => controlledState !== void 0 ? controlledState : uncontrolledState, [controlledState, uncontrolledState] ); const setControlledStateRef = useLatestRef(setControlledState); const setState = React.useCallback( (value) => { setUncontrolledState(value); setControlledStateRef.current?.(value); }, [setControlledStateRef] ); return [state, setState]; } function useLatestRef(value) { const valueRef = React.useRef(value); React.useInsertionEffect(() => { valueRef.current = value; }); return valueRef; } function useMergedRefs(...refs) { return React.useCallback( (instance) => { for (const ref of refs) { if (typeof ref === "function") { ref(instance); } else if (ref) { ref.current = instance; } } }, [...refs] ); } function useUnreactiveCallback(callback) { const latestCallback = useLatestRef(callback); return React.useCallback( (...args) => latestCallback.current?.(...args), [latestCallback] ); } function useEventHandlers(...handlers) { const latestHandlers = useLatestRef(handlers); return React.useCallback( (event) => { for (const handler of latestHandlers.current) { handler?.(event); if (event.defaultPrevented) return; } }, [latestHandlers] ); } function useSafeContext(context) { const value = React.useContext(context); if (value === void 0) { throw new Error(`${context.displayName || "Context"} is undefined`); } return value; } function usePopoverApi({ element, open, setOpen }) { React.useEffect( function syncPopoverWithOpenState() { if (element?.popover && element?.isConnected && open !== void 0) { element?.togglePopover?.(open); if (open && "CloseWatcher" in window) { const closeWatcher = new CloseWatcher(); closeWatcher.onclose = () => { if (open) setOpen(false); }; } } }, [open, element, setOpen] ); return React.useMemo( () => ({ style: { zIndex: supportsPopover ? void 0 : 9999 }, popover: "manual" }), [] ); } function useIsClient() { return React.useSyncExternalStore( React.useCallback(() => () => { }, []), () => true, () => false ); } export { useControlledState, useEventHandlers, useIsClient, useLatestRef, useLayoutEffect, useMergedRefs, usePopoverApi, useSafeContext, useUnreactiveCallback };