import $4ELxY$intlStringsmodulejs from "./intlStrings.mjs";
import {useId as $4ELxY$useId, isIOS as $4ELxY$isIOS, isAndroid as $4ELxY$isAndroid, useEffectEvent as $4ELxY$useEffectEvent, getActiveElement as $4ELxY$getActiveElement, getOwnerDocument as $4ELxY$getOwnerDocument, getEventTarget as $4ELxY$getEventTarget, useLayoutEffect as $4ELxY$useLayoutEffect, useObjectRef as $4ELxY$useObjectRef, mergeRefs as $4ELxY$mergeRefs, FOCUS_EVENT as $4ELxY$FOCUS_EVENT, CLEAR_FOCUS_EVENT as $4ELxY$CLEAR_FOCUS_EVENT, useEvent as $4ELxY$useEvent, isCtrlKeyPressed as $4ELxY$isCtrlKeyPressed, useLabels as $4ELxY$useLabels, mergeProps as $4ELxY$mergeProps} from "@react-aria/utils";
import {moveVirtualFocus as $4ELxY$moveVirtualFocus, getVirtuallyFocusedElement as $4ELxY$getVirtuallyFocusedElement, dispatchVirtualFocus as $4ELxY$dispatchVirtualFocus, dispatchVirtualBlur as $4ELxY$dispatchVirtualBlur} from "@react-aria/focus";
import {getInteractionModality as $4ELxY$getInteractionModality, getPointerType as $4ELxY$getPointerType} from "@react-aria/interactions";
import {useRef as $4ELxY$useRef, useState as $4ELxY$useState, useEffect as $4ELxY$useEffect, useCallback as $4ELxY$useCallback, useMemo as $4ELxY$useMemo} from "react";
import {useLocalizedStringFormatter as $4ELxY$useLocalizedStringFormatter} from "@react-aria/i18n";


function $parcel$interopDefault(a) {
  return a && a.__esModule ? a.default : a;
}
/*
 * Copyright 2024 Adobe. All rights reserved.
 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under
 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
 * OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */ 





function $48f6ba390f8c5b59$export$1e40b3ca02d92d21(props, state) {
    let { inputRef: inputRef, collectionRef: collectionRef, filter: filter, disableAutoFocusFirst: disableAutoFocusFirst = false, disableVirtualFocus: disableVirtualFocus = false } = props;
    let collectionId = (0, $4ELxY$useId)();
    let timeout = (0, $4ELxY$useRef)(undefined);
    let delayNextActiveDescendant = (0, $4ELxY$useRef)(false);
    let queuedActiveDescendant = (0, $4ELxY$useRef)(null);
    // For mobile screen readers, we don't want virtual focus, instead opting to disable FocusScope's restoreFocus and manually
    // moving focus back to the subtriggers
    let isMobileScreenReader = (0, $4ELxY$getInteractionModality)() === 'virtual' && ((0, $4ELxY$isIOS)() || (0, $4ELxY$isAndroid)());
    let [shouldUseVirtualFocus, setShouldUseVirtualFocus] = (0, $4ELxY$useState)(!isMobileScreenReader && !disableVirtualFocus);
    // Tracks if a collection has been connected to the autocomplete. If false, we don't want to add various attributes to the autocomplete input
    // since it isn't attached to a filterable collection (e.g. Tabs)
    let [hasCollection, setHasCollection] = (0, $4ELxY$useState)(false);
    (0, $4ELxY$useEffect)(()=>{
        return ()=>clearTimeout(timeout.current);
    }, []);
    let updateActiveDescendantEvent = (0, $4ELxY$useEffectEvent)((e)=>{
        // Ensure input is focused if the user clicks on the collection directly.
        // don't trigger on touch so that mobile keyboard doesnt appear when tapping on options
        if (!e.isTrusted && shouldUseVirtualFocus && inputRef.current && (0, $4ELxY$getActiveElement)((0, $4ELxY$getOwnerDocument)(inputRef.current)) !== inputRef.current && (0, $4ELxY$getPointerType)() !== 'touch') inputRef.current.focus();
        let target = (0, $4ELxY$getEventTarget)(e);
        if (e.isTrusted || !target || queuedActiveDescendant.current === target.id) return;
        clearTimeout(timeout.current);
        if (target !== collectionRef.current) {
            if (delayNextActiveDescendant.current) {
                queuedActiveDescendant.current = target.id;
                timeout.current = setTimeout(()=>{
                    state.setFocusedNodeId(target.id);
                }, 500);
            } else {
                queuedActiveDescendant.current = target.id;
                state.setFocusedNodeId(target.id);
            }
        } else if (queuedActiveDescendant.current && !document.getElementById(queuedActiveDescendant.current)) {
            // If we recieve a focus event refocusing the collection, either we have newly refocused the input and are waiting for the
            // wrapped collection to refocus the previously focused node if any OR
            // we are in a state where we've filtered to such a point that there aren't any matching items in the collection to focus.
            // In this case we want to clear tracked item if any and clear active descendant
            queuedActiveDescendant.current = null;
            state.setFocusedNodeId(null);
        }
        delayNextActiveDescendant.current = false;
    });
    let [collectionNode, setCollectionNode] = (0, $4ELxY$useState)(null);
    let callbackRef = (0, $4ELxY$useCallback)((node)=>{
        setCollectionNode(node);
        if (node != null) {
            // If useSelectableCollection isn't passed shouldUseVirtualFocus even when useAutocomplete provides it
            // that means the collection doesn't support it (e.g. Table). If that is the case, we need to disable it here regardless
            // of what the user's provided so that the input doesn't recieve the onKeyDown and autocomplete props.
            if (node.getAttribute('tabindex') != null) setShouldUseVirtualFocus(false);
            setHasCollection(true);
        } else setHasCollection(false);
    }, []);
    (0, $4ELxY$useLayoutEffect)(()=>{
        if (collectionNode != null) // When typing forward, we want to delay the setting of active descendant to not interrupt the native screen reader announcement
        // of the letter you just typed. If we recieve another focus event then we clear the queued update
        collectionNode.addEventListener('focusin', updateActiveDescendantEvent);
        return ()=>{
            collectionNode === null || collectionNode === void 0 ? void 0 : collectionNode.removeEventListener('focusin', updateActiveDescendantEvent);
        };
    }, [
        collectionNode
    ]);
    // Make sure to memo so that React doesn't keep registering a new event listeners on every rerender of the wrapped collection
    let mergedCollectionRef = (0, $4ELxY$useObjectRef)((0, $4ELxY$useMemo)(()=>(0, $4ELxY$mergeRefs)(collectionRef, callbackRef), [
        collectionRef,
        callbackRef
    ]));
    let focusFirstItem = (0, $4ELxY$useCallback)(()=>{
        var _collectionRef_current;
        delayNextActiveDescendant.current = true;
        (_collectionRef_current = collectionRef.current) === null || _collectionRef_current === void 0 ? void 0 : _collectionRef_current.dispatchEvent(new CustomEvent((0, $4ELxY$FOCUS_EVENT), {
            cancelable: true,
            bubbles: true,
            detail: {
                focusStrategy: 'first'
            }
        }));
    }, [
        collectionRef
    ]);
    let clearVirtualFocus = (0, $4ELxY$useCallback)((clearFocusKey)=>{
        var _collectionRef_current;
        (0, $4ELxY$moveVirtualFocus)((0, $4ELxY$getActiveElement)());
        queuedActiveDescendant.current = null;
        state.setFocusedNodeId(null);
        let clearFocusEvent = new CustomEvent((0, $4ELxY$CLEAR_FOCUS_EVENT), {
            cancelable: true,
            bubbles: true,
            detail: {
                clearFocusKey: clearFocusKey
            }
        });
        clearTimeout(timeout.current);
        delayNextActiveDescendant.current = false;
        (_collectionRef_current = collectionRef.current) === null || _collectionRef_current === void 0 ? void 0 : _collectionRef_current.dispatchEvent(clearFocusEvent);
    }, [
        collectionRef,
        state
    ]);
    let lastInputType = (0, $4ELxY$useRef)('');
    (0, $4ELxY$useEvent)(inputRef, 'input', (e)=>{
        let { inputType: inputType } = e;
        lastInputType.current = inputType;
    });
    let onChange = (value)=>{
        // Tell wrapped collection to focus the first element in the list when typing forward and to clear focused key when modifying the text via
        // copy paste/backspacing/undo/redo for screen reader announcements
        if (lastInputType.current === 'insertText' && !disableAutoFocusFirst) focusFirstItem();
        else if (lastInputType.current && (lastInputType.current.includes('insert') || lastInputType.current.includes('delete') || lastInputType.current.includes('history'))) {
            clearVirtualFocus(true);
            // If onChange was triggered before the timeout actually updated the activedescendant, we need to fire
            // our own dispatchVirtualFocus so focusVisible gets reapplied on the input
            if ((0, $4ELxY$getVirtuallyFocusedElement)(document) === inputRef.current) (0, $4ELxY$dispatchVirtualFocus)(inputRef.current, null);
        }
        state.setInputValue(value);
    };
    let keyDownTarget = (0, $4ELxY$useRef)(null);
    // For textfield specific keydown operations
    let onKeyDown = (e)=>{
        keyDownTarget.current = (0, $4ELxY$getEventTarget)(e);
        if (e.nativeEvent.isComposing) return;
        let focusedNodeId = queuedActiveDescendant.current;
        switch(e.key){
            case 'a':
                if ((0, $4ELxY$isCtrlKeyPressed)(e)) return;
                break;
            case 'Escape':
                // Early return for Escape here so it doesn't leak the Escape event from the simulated collection event below and
                // close the dialog prematurely. Ideally that should be up to the discretion of the input element hence the check
                // for isPropagationStopped
                if (e.isDefaultPrevented()) return;
                break;
            case ' ':
                // Space shouldn't trigger onAction so early return.
                return;
            case 'Tab':
                // Don't propogate Tab down to the collection, otherwise we will try to focus the collection via useSelectableCollection's Tab handler (aka shift tab logic)
                // We want FocusScope to handle Tab if one exists (aka sub dialog), so special casepropogate
                if ('continuePropagation' in e) e.continuePropagation();
                return;
            case 'Home':
            case 'End':
            case 'PageDown':
            case 'PageUp':
            case 'ArrowUp':
            case 'ArrowDown':
                {
                    var _collectionRef_current;
                    if ((e.key === 'Home' || e.key === 'End') && focusedNodeId == null && e.shiftKey) return;
                    // Prevent these keys from moving the text cursor in the input
                    e.preventDefault();
                    // Move virtual focus into the wrapped collection
                    let focusCollection = new CustomEvent((0, $4ELxY$FOCUS_EVENT), {
                        cancelable: true,
                        bubbles: true
                    });
                    (_collectionRef_current = collectionRef.current) === null || _collectionRef_current === void 0 ? void 0 : _collectionRef_current.dispatchEvent(focusCollection);
                    break;
                }
        }
        // Emulate the keyboard events that happen in the input field in the wrapped collection. This is for triggering things like onAction via Enter
        // or moving focus from one item to another. Stop propagation on the input event if it isn't already stopped so it doesn't leak out. For events
        // like ESC, the dispatched event below will bubble out of the collection and be stopped if handled by useSelectableCollection, otherwise will bubble
        // as expected
        if (!e.isPropagationStopped()) e.stopPropagation();
        let shouldPerformDefaultAction = true;
        if (collectionRef.current !== null) {
            var _collectionRef_current1;
            if (focusedNodeId == null) shouldPerformDefaultAction = ((_collectionRef_current1 = collectionRef.current) === null || _collectionRef_current1 === void 0 ? void 0 : _collectionRef_current1.dispatchEvent(new KeyboardEvent(e.nativeEvent.type, e.nativeEvent))) || false;
            else {
                let item = document.getElementById(focusedNodeId);
                if (item) shouldPerformDefaultAction = (item === null || item === void 0 ? void 0 : item.dispatchEvent(new KeyboardEvent(e.nativeEvent.type, e.nativeEvent))) || false;
            }
        }
        if (shouldPerformDefaultAction) switch(e.key){
            case 'ArrowLeft':
            case 'ArrowRight':
                // Clear the activedescendant so NVDA announcements aren't interrupted but retain the focused key in the collection so the
                // user's keyboard navigation restarts from where they left off
                clearVirtualFocus();
                break;
            case 'Enter':
                // Trigger click action on item when Enter key was pressed.
                if (focusedNodeId != null) {
                    let item = document.getElementById(focusedNodeId);
                    item === null || item === void 0 ? void 0 : item.dispatchEvent(new PointerEvent('click', e.nativeEvent));
                }
                break;
        }
        else // TODO: check if we can do this, want to stop textArea from using its default Enter behavior so items are properly triggered
        e.preventDefault();
    };
    let onKeyUpCapture = (0, $4ELxY$useEffectEvent)((e)=>{
        // Dispatch simulated key up events for things like triggering links in listbox
        // Make sure to stop the propagation of the input keyup event so that the simulated keyup/down pair
        // is detected by usePress instead of the original keyup originating from the input
        if ((0, $4ELxY$getEventTarget)(e) === keyDownTarget.current) {
            var _collectionRef_current;
            e.stopImmediatePropagation();
            let focusedNodeId = queuedActiveDescendant.current;
            if (focusedNodeId == null) (_collectionRef_current = collectionRef.current) === null || _collectionRef_current === void 0 ? void 0 : _collectionRef_current.dispatchEvent(new KeyboardEvent(e.type, e));
            else {
                let item = document.getElementById(focusedNodeId);
                item === null || item === void 0 ? void 0 : item.dispatchEvent(new KeyboardEvent(e.type, e));
            }
        }
    });
    (0, $4ELxY$useEffect)(()=>{
        document.addEventListener('keyup', onKeyUpCapture, true);
        return ()=>{
            document.removeEventListener('keyup', onKeyUpCapture, true);
        };
    }, []);
    let stringFormatter = (0, $4ELxY$useLocalizedStringFormatter)((0, ($parcel$interopDefault($4ELxY$intlStringsmodulejs))), '@react-aria/autocomplete');
    let collectionProps = (0, $4ELxY$useLabels)({
        id: collectionId,
        'aria-label': stringFormatter.format('collectionLabel')
    });
    let filterFn = (0, $4ELxY$useCallback)((nodeTextValue, node)=>{
        if (filter) return filter(nodeTextValue, state.inputValue, node);
        return true;
    }, [
        state.inputValue,
        filter
    ]);
    // Be sure to clear/restore the virtual + collection focus when blurring/refocusing the field so we only show the
    // focus ring on the virtually focused collection when are actually interacting with the Autocomplete
    let onBlur = (e)=>{
        if (!e.isTrusted) return;
        let lastFocusedNode = queuedActiveDescendant.current ? document.getElementById(queuedActiveDescendant.current) : null;
        if (lastFocusedNode) (0, $4ELxY$dispatchVirtualBlur)(lastFocusedNode, e.relatedTarget);
    };
    let onFocus = (e)=>{
        if (!e.isTrusted) return;
        let curFocusedNode = queuedActiveDescendant.current ? document.getElementById(queuedActiveDescendant.current) : null;
        if (curFocusedNode) {
            let target = (0, $4ELxY$getEventTarget)(e);
            queueMicrotask(()=>{
                // instead of focusing the last focused node, just focus the collection instead and have the collection handle what item to focus via useSelectableCollection/Item
                (0, $4ELxY$dispatchVirtualBlur)(target, collectionRef.current);
                (0, $4ELxY$dispatchVirtualFocus)(collectionRef.current, target);
            });
        }
    };
    // Only apply the autocomplete specific behaviors if the collection component wrapped by it is actually
    // being filtered/allows filtering by the Autocomplete.
    let inputProps = {
        value: state.inputValue,
        onChange: onChange
    };
    var _state_focusedNodeId;
    let virtualFocusProps = {
        onKeyDown: onKeyDown,
        'aria-activedescendant': (_state_focusedNodeId = state.focusedNodeId) !== null && _state_focusedNodeId !== void 0 ? _state_focusedNodeId : undefined,
        onBlur: onBlur,
        onFocus: onFocus
    };
    if (hasCollection) inputProps = {
        ...inputProps,
        ...shouldUseVirtualFocus && virtualFocusProps,
        enterKeyHint: 'go',
        'aria-controls': collectionId,
        // TODO: readd proper logic for completionMode = complete (aria-autocomplete: both)
        'aria-autocomplete': 'list',
        // This disable's iOS's autocorrect suggestions, since the autocomplete provides its own suggestions.
        autoCorrect: 'off',
        // This disable's the macOS Safari spell check auto corrections.
        spellCheck: 'false',
        autoComplete: 'off'
    };
    return {
        inputProps: inputProps,
        collectionProps: (0, $4ELxY$mergeProps)(collectionProps, {
            shouldUseVirtualFocus: shouldUseVirtualFocus,
            disallowTypeAhead: shouldUseVirtualFocus
        }),
        collectionRef: mergedCollectionRef,
        filter: filter != null ? filterFn : undefined
    };
}


export {$48f6ba390f8c5b59$export$1e40b3ca02d92d21 as useAutocomplete};
//# sourceMappingURL=useAutocomplete.module.js.map
