"use client";
import { jsx as _jsx } from "react/jsx-runtime";
import { useCallback, useReducer, useRef } from "react";
import { RippleContainer } from "./RippleContainer.js";
import { useUserInteractionMode } from "./UserInteractionModeProvider.js";
import { INTERACTION_CONFIG } from "./config.js";
import { getRippleStyle, releaseRipple, updateRipplesState } from "./utils.js";
/** @since 6.0.0 */ export const PRESSED_CLASS_NAME = "rmd-pressed";
const noop = ()=>{
// do nothing
};
/**
 * This hook is used to apply the required element interaction based on the
 * {@link ElementInteractionMode} and should generally be used internally only.
 *
 * @example Providing Element Interaction
 * ```tsx
 * import { useElementInteraction } from "@react-md/core/interaction/useElementInteraction";
 * import { cnb } from "cnbuilder";
 * import { type ReactElement } from "react";
 *
 * import styles from "./CustomComponent.module.scss";
 *
 * interface Props extends HTMLAttributes<HTMLDivElement> {
 *   disabled?: boolean;
 * }
 *
 * function CustomComponent(props: Props): ReactElement {
 *   const {
 *     disabled = false,
 *     className,
 *     onBlur,
 *     onClick,
 *     onKeyDown,
 *     onKeyUp,
 *     onMouseDown,
 *     onMouseUp,
 *     onMouseLeave,
 *     onTouchStart,
 *     onTouchMove,
 *     onTouchEnd,
 *     ...remaining
 *   } = props;
 *
 *   const { handlers, pressed, ripples } = useElementInteraction({
 *     disabled,
 *     // pass remaining props so that if any event handlers were provided to
 *     // the component, they will be merged with the element interaction
 *     // handlers
 *     onBlur,
 *     onClick,
 *     onKeyDown,
 *     onKeyUp,
 *     onMouseDown,
 *     onMouseUp,
 *     onMouseLeave,
 *     onTouchStart,
 *     onTouchMove,
 *     onTouchEnd,
 *   });
 *
 *   return (
 *     <div
 *       {...remaining}
 *       {...handlers}
 *       aria-disabled={disabled}
 *       role="button"
 *       className={cnb(styles.button, pressed && styles.pressed)}
 *       tabIndex={disabled ? undefined : 0}
 *     >
 *       {children}
 *       {ripples}
 *     </div>
 *   );
 * }
 * ```
 *
 * @param options - An object of {@link ElementInteractionOptions} that is used
 * to merge event handlers or disable the interactions.
 * @returns the {@link ElementInteractionImplementation}
 * @since 6.0.0 Touch interactions were removed since it never looked
 * good if the user touched a clickable element right before scrolling. The
 * ripple effect will only be fired on click now for touch devices.
 */ export function useElementInteraction(options = {}) {
    const { onBlur = noop, onClick = noop, onMouseDown = noop, onMouseUp = noop, onMouseLeave = noop, onKeyUp = noop, onKeyDown = noop, onTouchStart = noop, onTouchEnd = noop, onTouchMove = noop, onDragStart = noop, mode = INTERACTION_CONFIG.mode, disabled = false } = options;
    const holding = useRef(false);
    const disableClick = useRef(false);
    const userMode = useUserInteractionMode();
    const isInteractionDisabled = disabled || mode === "none";
    const [state, dispatch] = useReducer(function reducer(state, action) {
        switch(action.type){
            case "press":
                {
                    const { style } = action;
                    let { ripples } = state;
                    if (style) {
                        ripples = [
                            ...ripples,
                            {
                                style,
                                entered: false,
                                exiting: false,
                                startTime: Date.now()
                            }
                        ];
                    }
                    return {
                        pressed: true,
                        ripples
                    };
                }
            case "cancel":
                // Note: unlike previous react-md versions, this will immediately
                // remove ALL ripple effects instead of trying to fade out. this seems
                // much nicer for touch devices when they are trying to scroll
                return {
                    pressed: false,
                    ripples: []
                };
            case "release":
                {
                    if (mode === "press") {
                        return {
                            ...state,
                            pressed: false
                        };
                    }
                    return releaseRipple(state);
                }
            case "entered":
                return updateRipplesState({
                    type: "entered",
                    state,
                    ripple: action.ripple,
                    holding: holding.current
                });
            case "exited":
                return updateRipplesState({
                    type: "exited",
                    state,
                    ripple: action.ripple,
                    holding: holding.current
                });
            default:
                return state;
        }
    }, {
        pressed: false,
        ripples: []
    });
    const { pressed } = state;
    let ripples;
    if (mode === "ripple") {
        ripples = /*#__PURE__*/ _jsx(RippleContainer, {
            ripples: state.ripples,
            onEntered: (ripple)=>{
                dispatch({
                    type: "entered",
                    ripple
                });
            },
            onExited: (ripple)=>{
                dispatch({
                    type: "exited",
                    ripple
                });
            }
        });
    }
    return {
        pressed,
        pressedClassName: pressed && mode === "press" ? PRESSED_CLASS_NAME : undefined,
        ripples,
        handlers: {
            onBlur: useCallback((event)=>{
                onBlur(event);
                if (holding.current) {
                    holding.current = false;
                    dispatch({
                        type: "release"
                    });
                }
            }, [
                onBlur
            ]),
            onClick: useCallback((event)=>{
                if (disabled) {
                    return;
                }
                onClick(event);
                if (event.isPropagationStopped() || userMode === "touch" || mode !== "ripple" || disableClick.current || holding.current || document.activeElement === event.currentTarget) {
                    disableClick.current = false;
                    return;
                }
                dispatch({
                    type: "press",
                    style: getRippleStyle(event, true)
                });
            }, [
                disabled,
                mode,
                onClick,
                userMode
            ]),
            onMouseDown: useCallback((event)=>{
                onMouseDown(event);
                if (event.isPropagationStopped() || isInteractionDisabled || userMode !== "mouse" || event.shiftKey || event.ctrlKey || event.metaKey || event.altKey || event.button !== 0) {
                    return;
                }
                // prevent text selection on double click
                // https://stackoverflow.com/a/43321596
                if (event.detail > 1) {
                    event.preventDefault();
                }
                holding.current = true;
                disableClick.current = true;
                let style;
                if (mode === "ripple") {
                    style = getRippleStyle(event, false);
                }
                dispatch({
                    type: "press",
                    style
                });
            }, [
                onMouseDown,
                isInteractionDisabled,
                userMode,
                mode
            ]),
            onMouseUp: useCallback((event)=>{
                onMouseUp(event);
                if (event.isPropagationStopped() || isInteractionDisabled) {
                    return;
                }
                holding.current = false;
                dispatch({
                    type: "release"
                });
            }, [
                isInteractionDisabled,
                onMouseUp
            ]),
            onMouseLeave: useCallback((event)=>{
                onMouseLeave(event);
                if (event.isPropagationStopped() || isInteractionDisabled || userMode !== "mouse" || !holding.current) {
                    return;
                }
                holding.current = false;
                dispatch({
                    type: "cancel"
                });
            }, [
                isInteractionDisabled,
                onMouseLeave,
                userMode
            ]),
            onDragStart (event) {
                onDragStart(event);
                if (event.isPropagationStopped() || !holding.current || userMode !== "mouse") {
                    return;
                }
                holding.current = false;
                dispatch({
                    type: "cancel"
                });
            },
            onKeyDown: useCallback((event)=>{
                onKeyDown(event);
                const { key } = event;
                const { tagName } = event.currentTarget;
                if (event.isPropagationStopped() || userMode !== "keyboard" || disabled || key !== " " && key !== "Enter" || // links do not support clicking on space
                key === " " && tagName === "A" || // inputs submit a form instead of clicking on enter
                key === "Enter" && tagName === "INPUT") {
                    return;
                }
                // stop propagation since we're starting to do custom event behavior
                // to click the element for everything except elements that the
                // browser clicks natively
                event.stopPropagation();
                if (tagName !== "BUTTON" && tagName !== "A") {
                    if (key === " ") {
                        // prevent the pager from scrolling
                        event.preventDefault();
                    }
                    event.currentTarget.click();
                }
                if (holding.current || isInteractionDisabled) {
                    return;
                }
                holding.current = true;
                dispatch({
                    type: "press",
                    style: getRippleStyle(event, false)
                });
            }, [
                disabled,
                isInteractionDisabled,
                onKeyDown,
                userMode
            ]),
            onKeyUp: useCallback((event)=>{
                onKeyUp(event);
                if (event.isPropagationStopped() || isInteractionDisabled || userMode !== "keyboard" || !holding.current) {
                    return;
                }
                holding.current = false;
                dispatch({
                    type: "release"
                });
            }, [
                isInteractionDisabled,
                onKeyUp,
                userMode
            ]),
            onTouchStart: useCallback((event)=>{
                onTouchStart(event);
                if (event.isPropagationStopped() || isInteractionDisabled) {
                    return;
                }
                holding.current = true;
                let style;
                if (mode === "ripple") {
                    style = getRippleStyle(event, false);
                }
                dispatch({
                    type: "press",
                    style
                });
            }, [
                mode,
                isInteractionDisabled,
                onTouchStart
            ]),
            onTouchEnd: useCallback((event)=>{
                onTouchEnd(event);
                if (event.isPropagationStopped() || isInteractionDisabled) {
                    return;
                }
                holding.current = false;
                dispatch({
                    type: "release"
                });
            }, [
                isInteractionDisabled,
                onTouchEnd
            ]),
            onTouchMove: useCallback((event)=>{
                onTouchMove(event);
                if (event.isPropagationStopped() || isInteractionDisabled) {
                    return;
                }
                holding.current = false;
                dispatch({
                    type: "cancel"
                });
            }, [
                isInteractionDisabled,
                onTouchMove
            ])
        }
    };
}

//# sourceMappingURL=useElementInteraction.js.map