/* @oracle/oraclejet-preact: undefined */
'use strict';

var hooks = require('preact/hooks');
var clientHints = require('./clientHints-89c01238.js');
var textSelectionUtils = require('./textSelectionUtils-432ab66b.js');

/**
 * `useContextMenuGesture` hook to handle contextMenuGesture. Pure onContextMenu event is not enough to handle this since:
   1.- onContextMenu event is not supported on mobile safari.
   2.- onContextMenu can't be triggered using keyboard natively on Mac

   What event is running depends on the gesture:
   -> Mouse
      We use native onContextMenu event to support it. Nothing fancy about it, prevent default event to suppress the default browser context menu.
   -> Keyboard
      We rely on onKeyDown event, since mac doesn't run onContextMenu when pressing Shift-F10. We preventDefault
      if Shift-F10 is pressed so onContextMenu native event is not run in platforms that support it.
   -> Touching
      - For non-visualization components, the default touch gesture is used:
          This is platform specific.
          On machines that supports onContextMenu event and also touching (android, hybrid laptops) the native context menu
          is used.
          On platforms where onContextMenu doesn't exist(IOS), the steps are the next:
          1.- We check if only one finger was used and also if the platform is IOS.
          2.- If there is a touch move we clear the timeout.
          3.- If user doesn't move its finger and user holds enough we trigger contextMenu handler.
          4.- We clearTimeout on touchEnd.
      - For visualization components:
          The gesture to launch context menu is touch + hold + release. The 'isTouchHoldReleaseGesture' option should be set to 'true' when using this hook.
 *
 */
const useContextMenuGesture = (onContextMenuHandler, contextMenuOptions = {
    isTouchHoldReleaseGesture: false,
    isDisabled: false
}) => {
    const touchGestureType = !contextMenuOptions.isTouchHoldReleaseGesture
        ? clientHints.getClientHints().platform === 'ios'
            ? 'touchHold'
            : 'native'
        : 'touchHoldRelease';
    const pressHoldTime = touchGestureType === 'touchHold' ? PRESS_HOLD_TIME : PRESS_HOLD_RELEASE_TIME;
    /*Use this ref to avoid triggering contextMenuHandler twice. This could happen due to Shift-F10 combination of keys.
     On firefox preventDefault inside keyDown event does not prevent contextMenu native event to be triggered. That's why
     we use this ref instead. */
    const ignoreNativeContextMenuEvent = hooks.useRef(false);
    //Info that we need to track when handling touch hold portion of the gesture
    const touchHoldGestureInfo = hooks.useRef({ isTouchHoldComplete: false });
    // We track if there is any selection pending so we don't trigger the context menu if that's the case
    const isSelectionPending = hooks.useRef(false);
    // TODO: We need to confirm if this type is correct or has to be changed. JET-59244
    const timerId = hooks.useRef();
    const getIsSelectionPending = hooks.useCallback(() => {
        if (isSelectionPending.current) {
            return true;
        }
        return textSelectionUtils.getIsSelectionPending();
    }, []);
    const resetUserSelect = hooks.useCallback(() => {
        document.body.style.userSelect = touchHoldGestureInfo.current.bodyUserSelectValue;
        // Implemented with the vendor prefix: -webkit- on safari
        document.body.style.webkitUserSelect = touchHoldGestureInfo.current
            .webkitBodyUserSelectValue;
        touchHoldGestureInfo.current.isMenuOpened = false;
    }, []);
    hooks.useEffect(() => {
        const onSelectionChange = () => {
            const { timeNeededForSelection, startTime } = touchHoldGestureInfo.current;
            if (startTime && !timeNeededForSelection && document.getSelection()?.type === 'Range') {
                clearTimeout(timerId.current);
                touchHoldGestureInfo.current.timeNeededForSelection = Date.now() - startTime;
                touchHoldGestureInfo.current.scheduleAndHandleTouchHold?.(pressHoldTime);
            }
        };
        const onSelectStart = (event) => {
            if ((event?.target).nodeName === '#text') {
                isSelectionPending.current = true;
                // we want to reset isSelectionPending if we don't get a contextmenu event. 10ms seems to be enough time tweak as needed.
                setTimeout(() => {
                    isSelectionPending.current = false;
                }, 10);
            }
            else {
                isSelectionPending.current = false;
            }
        };
        if (touchGestureType === 'touchHold' || touchGestureType === 'touchHoldRelease') {
            document.addEventListener('selectionchange', onSelectionChange, true);
        }
        else {
            document.addEventListener('selectstart', onSelectStart, true);
        }
        return () => {
            document.removeEventListener('selectionchange', onSelectionChange, true);
            document.removeEventListener('selectstart', onSelectStart, true);
        };
    }, [touchGestureType, pressHoldTime]);
    const onContextMenu = hooks.useCallback((event) => {
        if (event.pointerType === 'touch' && touchGestureType === 'touchHoldRelease') {
            event.preventDefault();
            return;
        }
        if (!getIsSelectionPending()) {
            //Prevent default so we don't open browser context menu
            event.preventDefault();
            if (ignoreNativeContextMenuEvent.current) {
                ignoreNativeContextMenuEvent.current = false;
            }
            else {
                onContextMenuHandler({
                    /*Pointer type can be touch/pen/mouse if its touch we passed touch if any other value we consider it a mouse
                    gesture */
                    gesture: event.pointerType === 'touch' ? 'touch' : 'mouse',
                    anchor: {
                        x: event.clientX,
                        y: event.clientY,
                        contextElement: event.currentTarget
                    },
                    currentTarget: event.currentTarget,
                    target: event.target
                });
            }
        }
    }, [onContextMenuHandler, getIsSelectionPending, touchGestureType]);
    const onKeyDown = hooks.useCallback((event) => {
        if (!getIsSelectionPending()) {
            if (event.key === 'F10' && event.shiftKey) {
                ignoreNativeContextMenuEvent.current = true;
                const currentTarget = event.currentTarget;
                onContextMenuHandler({
                    gesture: 'keyboard',
                    anchor: currentTarget,
                    currentTarget,
                    target: event.target
                });
            }
        }
    }, [onContextMenuHandler, getIsSelectionPending]);
    const onTouchStart = hooks.useCallback((event) => {
        //We check if the touch was using a single finger
        if (event.touches.length === 1) {
            const timeNeededForSelection = touchHoldGestureInfo.current.timeNeededForSelection;
            let delay = touchGestureType === 'touchHold'
                ? pressHoldTime * 2 // By default delay to launch context menu is 1500 ms
                : pressHoldTime;
            if (timeNeededForSelection === undefined) {
                // We get start time to get timeNeeded for selection inside selection change event
                touchHoldGestureInfo.current.startTime = Date.now();
            }
            else {
                // If timeNeededForSelection was already obtained we use that time + press hold time
                delay = timeNeededForSelection + pressHoldTime;
            }
            const touchobj = event.touches[0];
            touchHoldGestureInfo.current.touchPageCoords = { x: touchobj.pageX, y: touchobj.pageY };
            //According to mozilla docs: "The value of event.currentTarget is only available while the event is being handled"
            //Due to this we need to save this element on a const
            const currentTarget = event.currentTarget;
            const launchContextMenu = () => {
                touchHoldGestureInfo.current.bodyUserSelectValue = document.body.style.userSelect;
                touchHoldGestureInfo.current.webkitBodyUserSelectValue =
                    document.body.style.webkitUserSelect;
                //We have to clean selections and don't permit any until a touchEnd/touchCancel
                document.body.style.userSelect = 'none';
                document.body.style.webkitUserSelect = 'none';
                document.getSelection()?.empty();
                touchHoldGestureInfo.current.isMenuOpened = true;
                onContextMenuHandler({
                    gesture: 'touch',
                    anchor: { x: touchobj.clientX, y: touchobj.clientY, contextElement: currentTarget },
                    currentTarget: currentTarget,
                    target: event.target
                });
            };
            const scheduleAndHandleTouchHold = (delay) => {
                touchHoldGestureInfo.current.isTouchHoldComplete = false;
                timerId.current = setTimeout(() => {
                    if (touchGestureType === 'touchHold') {
                        launchContextMenu();
                    }
                    touchHoldGestureInfo.current.isTouchHoldComplete = true;
                }, delay);
            };
            touchHoldGestureInfo.current.launchContextMenu = launchContextMenu;
            touchHoldGestureInfo.current.scheduleAndHandleTouchHold = scheduleAndHandleTouchHold;
            scheduleAndHandleTouchHold(delay);
        }
    }, [onContextMenuHandler, touchGestureType, pressHoldTime]);
    const onTouchMove = hooks.useCallback((e) => {
        const firstTouch = e.touches[0];
        if (Math.abs(touchHoldGestureInfo.current.touchPageCoords?.x - firstTouch.pageX) >
            MAX_ALLOWED_MOVEMENT ||
            Math.abs(touchHoldGestureInfo.current.touchPageCoords?.y - firstTouch.pageY) >
                MAX_ALLOWED_MOVEMENT) {
            clearTimeout(timerId.current);
        }
    }, []);
    const onTouchEnd = hooks.useCallback((e) => {
        clearTimeout(timerId.current);
        if (touchGestureType === 'touchHoldRelease' &&
            touchHoldGestureInfo.current.isTouchHoldComplete) {
            touchHoldGestureInfo.current.launchContextMenu?.();
        }
        if (touchHoldGestureInfo.current.isMenuOpened) {
            //Prevent default so we don't open browser context menu
            e.preventDefault();
            // 500 comes from testing behavior on real devices, seems like enough time to allow user to lift finger. UX approved.
            setTimeout(() => {
                resetUserSelect();
            }, 500);
        }
        touchHoldGestureInfo.current.isTouchHoldComplete = false;
    }, [resetUserSelect, touchGestureType]);
    const onTouchCancel = hooks.useCallback(() => {
        clearTimeout(timerId.current);
        if (touchHoldGestureInfo.current.isMenuOpened) {
            resetUserSelect();
        }
    }, [resetUserSelect]);
    const triggerProps = contextMenuOptions.isDisabled
        ? {}
        : {
            onContextMenu,
            onKeyDown,
            ...((touchGestureType === 'touchHold' || touchGestureType === 'touchHoldRelease') && {
                onTouchStart,
                onTouchMove,
                onTouchEnd,
                onTouchCancel
            })
        };
    return { triggerProps };
};
//Time needed to hold touch to be considered context menu triggering
const PRESS_HOLD_TIME = 750;
const PRESS_HOLD_RELEASE_TIME = 200; // Same value used in DvtTouchManager._touchHoldTimer
const MAX_ALLOWED_MOVEMENT = 5;

exports.useContextMenuGesture = useContextMenuGesture;
//# sourceMappingURL=useContextMenuGesture-fb7f7df5.js.map
