@stratakit/foundations
Version:
Foundational pieces of StrataKit
106 lines (105 loc) • 2.62 kB
JavaScript
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
}) {
React.useEffect(
function syncPopoverWithOpenState() {
if (element?.popover && element?.isConnected) {
element?.togglePopover?.(open);
}
},
[open, element]
);
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
};