import { isArray, isFunction } from "./assertion.js";
import { runIfFn } from "./function.js";
import React, { useCallback, useEffect, useRef, useState } from "react";

//#region src/utils/react.ts
/**
* Assigns given value to the provided ref.
* Supports ref as function (e.g. useState) and classic ref (e.g. useRef).
*/
function assignRef(ref, value) {
	if (ref === null || ref === void 0) return;
	if (isFunction(ref)) {
		ref(value);
		return;
	}
	try {
		ref.current = value;
	} catch (error) {
		throw new Error(`Cannot assign value '${value}' to ref '${ref}'`);
	}
}
/** Generic function to create new Context and context access hook. */
function createContext$1(options = {}) {
	const { errorMessage = "Context can only be accessed by components wrapped with the Provider.", isOptional, name } = options;
	const Context = React.createContext(void 0);
	Context.displayName = name;
	function useContext$1() {
		const context = React.useContext(Context);
		if (!isOptional && context === void 0) throw new Error(errorMessage);
		return context;
	}
	return [Context.Provider, useContext$1];
}
/** Assigns a node to all provided refs. */
function mergeRefs(...refs) {
	return (node) => {
		refs.forEach((ref) => assignRef(ref, node));
	};
}
/** Returns given callback with a stable reference, but keeps it updated internally. */
function useCallbackRef(callback, deps = []) {
	const innerRef = useRef(callback);
	innerRef.current = callback;
	return useCallback(((...args) => innerRef.current?.(...args)), deps);
}
const defaultEvents = ["mousedown", "touchstart"];
/**
* Triggers given callback when an event happens outside of a given element.
* Event listeners are attached to the window object.
* @param ref - element to check click position for
* @param callback - function triggered on outside click
* @param events - array of events listened for (default: ['mousedown', 'touchstart'])
*/
function useClickOutside(ref, callback, events = defaultEvents) {
	const eventsString = JSON.stringify(events);
	const listener = useCallbackRef((ev) => {
		const refs = isArray(ref) ? ref : [ref];
		const target = ev.target;
		refs.every((ref$1) => ref$1.current && !ref$1.current.contains(target)) && callback?.(ev);
	});
	useEffect(() => {
		const events$1 = JSON.parse(eventsString);
		events$1.forEach((event) => window.addEventListener(event, listener));
		return () => {
			events$1.forEach((event) => window.removeEventListener(event, listener));
		};
	}, [eventsString, ref]);
}
/** Supports state management in components that can be controlled or uncontrolled. */
function useControlled(props) {
	const { defaultValue, onChange: onChangeProp, value: valueProp } = props;
	const [valueState, setValue] = useState(defaultValue);
	const isControlled = valueProp !== void 0;
	const value = isControlled ? valueProp : valueState;
	/** Triggers the external callback with the new value. In uncontrolled mode, updates state. */
	const onChange = useCallbackRef((next) => {
		const newValue = runIfFn(next);
		if (!isControlled) setValue(newValue);
		onChangeProp?.(newValue);
	});
	return [
		value,
		onChange,
		{
			isControlled,
			reset: useCallbackRef(() => {
				!isControlled && defaultValue && onChange(defaultValue);
			})
		}
	];
}
/** Returns an array with each prefix added to the id. Useful for aria attributes in compound components. */
function useIds(id, prefixes) {
	return prefixes.map((prefix) => `${prefix}-${id}`);
}
/**
* Provides a function, which can be called to check if component using the hook is mounted or unmounted.
*/
function useIsMounted() {
	const isMountedRef = useRef(false);
	useEffect(() => {
		isMountedRef.current = true;
		return () => {
			isMountedRef.current = false;
		};
	}, []);
	return useCallback(() => isMountedRef.current, []);
}

//#endregion
export { assignRef, createContext$1 as createContext, mergeRefs, useCallbackRef, useClickOutside, useControlled, useIds, useIsMounted };
globalThis.__vuiVersion__ = "4.0.0-beta.1"
//# sourceMappingURL=react.js.map