import React__default, { useContext, useRef, useEffect, useMemo } from 'react';
import classnames from 'classnames';
import { i as useFloatingParentNodeId, j as useFloatingNodeId, u as useFloating, o as offset, f as flip, d as useDismiss, g as useInteractions, b as autoUpdate, k as FloatingTree, m as FloatingNode, F as FloatingPortal } from './floating-ui.react-es.js';
import { C as ComboboxContentSearch } from './ComboboxContentSearch-es.js';
import { C as ComboboxContentList } from './ComboboxContentList-es.js';
import { C as ComboboxContentHeader } from './ComboboxContentHeader-es.js';
import { a as ComboboxContext } from './ComboboxProvider-es.js';
import { useRefocusOnActivator, useFocusTrap } from '@jobber/hooks';
import { C as COMBOBOX_MENU_ID } from './constants-es.js';

var styles = {"content":"QsRc49DHSgA-","hidden":"_7Fd5XGsPpUA-","actions":"yV8-0Hs43J0-","actionPadding":"g3Gdt8jXvFk-","spinning":"dJVYYOaf04E-"};

function useComboboxContent(open, selected) {
    const { onClear, onSelectAll, shouldScroll } = useContext(ComboboxContext);
    const optionsListRef = useRef(null);
    useEffect(() => {
        var _a;
        if (open && shouldScroll.current && optionsListRef.current) {
            const firstSelected = Array.from((_a = optionsListRef === null || optionsListRef === void 0 ? void 0 : optionsListRef.current) === null || _a === void 0 ? void 0 : _a.children).find(child => {
                if (child instanceof HTMLElement) {
                    return child.dataset.selected === "true";
                }
            });
            scrollToFirstSelected(firstSelected, shouldScroll);
        }
    }, [open, selected]);
    return {
        onClear,
        onSelectAll,
        optionsListRef,
    };
}
function scrollToFirstSelected(firstSelected, shouldScroll) {
    if (firstSelected) {
        firstSelected.scrollIntoView({
            block: "nearest",
        });
        shouldScroll.current = false;
    }
}

const COMBOBOX_OFFSET = 8;
// eslint-disable-next-line max-statements
function useComboboxAccessibility(selectionCallback, filteredOptions, optionsListRef, open, wrapperRef) {
    const { handleClose } = useContext(ComboboxContext);
    const hasOptionsVisible = open && filteredOptions.length > 0;
    const focusedIndex = useRef(null);
    const parentNodeId = useFloatingParentNodeId();
    const nodeId = useFloatingNodeId();
    useRefocusOnActivator(open);
    const floatingRef = useFocusTrap(open);
    const { floatingStyles, update, context, elements } = useFloating({
        nodeId,
        elements: {
            reference: wrapperRef.current,
            floating: floatingRef.current,
        },
        placement: "bottom-start",
        open,
        onOpenChange: openState => {
            if (!openState)
                handleClose();
        },
        middleware: [
            offset(COMBOBOX_OFFSET),
            flip({ fallbackPlacements: ["top-start", "bottom-end", "top-end"] }),
        ],
    });
    const dismiss = useDismiss(context);
    const { getFloatingProps } = useInteractions([dismiss]);
    // Floating element is hidden via CSS (not conditionally rendered),
    // set up and tear down autoUpdate manually to avoid unnecessary observers.
    useEffect(() => {
        if (open && elements.reference && elements.floating) {
            const cleanup = autoUpdate(elements.reference, elements.floating, update);
            return cleanup;
        }
    }, [open, autoUpdate, elements, update]);
    useEffect(() => {
        focusedIndex.current = null;
        if (open) {
            update === null || update === void 0 ? void 0 : update();
        }
    }, [open, filteredOptions.length, update]);
    useEffect(() => {
        var _a;
        if (open) {
            (_a = floatingRef.current) === null || _a === void 0 ? void 0 : _a.addEventListener("keydown", handleContentKeydown);
        }
        return () => {
            var _a;
            (_a = floatingRef.current) === null || _a === void 0 ? void 0 : _a.removeEventListener("keydown", handleContentKeydown);
        };
    }, [open, optionsListRef, filteredOptions]);
    function handleContentKeydown(event) {
        var _a;
        if (!hasOptionsVisible)
            return;
        if (event.key === "Enter" || event.key === " ") {
            const activeElementInList = (_a = optionsListRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement);
            if (!activeElementInList)
                return;
            handleKeyboardSelection(event);
        }
        if (event.key === "ArrowDown") {
            handleKeyboardNavigation(event, 1);
        }
        if (event.key === "ArrowUp") {
            handleKeyboardNavigation(event, -1);
        }
    }
    function handleKeyboardNavigation(event, indexChange) {
        var _a;
        const newIndex = focusedIndex.current === null ? 0 : focusedIndex.current + indexChange;
        if (newIndex < 0 || newIndex >= filteredOptions.length)
            return;
        const optionElement = (_a = optionsListRef.current) === null || _a === void 0 ? void 0 : _a.children[newIndex];
        optionElement === null || optionElement === void 0 ? void 0 : optionElement.focus();
        focusedIndex.current = newIndex;
        event.preventDefault();
        event.stopPropagation();
    }
    function handleKeyboardSelection(event) {
        event.preventDefault();
        event.stopPropagation();
        if (focusedIndex.current !== null) {
            selectionCallback(filteredOptions[focusedIndex.current]);
        }
    }
    return {
        floatingRef,
        floatingStyles,
        floatingProps: getFloatingProps(),
        nodeId,
        parentNodeId,
    };
}

function ComboboxContent(props) {
    const optionsExist = props.options.length > 0;
    const { onClear, onSelectAll, optionsListRef } = useComboboxContent(props.open, props.selected);
    const { floatingRef, floatingStyles, floatingProps, nodeId, parentNodeId } = useComboboxAccessibility(props.handleSelection, props.options, optionsListRef, props.open, props.wrapperRef);
    // options that are passed back to consumers via onSelectAll callback
    // should only contain id and label
    const optionsData = useMemo(() => {
        return props.options.map(option => ({
            id: option.id,
            label: option.label,
        }));
    }, [props.options]);
    const content = (React__default.createElement("div", Object.assign({ ref: floatingRef, id: COMBOBOX_MENU_ID, "data-testid": COMBOBOX_MENU_ID, "data-elevation": "elevated", tabIndex: 0, className: classnames(styles.content, {
            [styles.hidden]: !props.open,
        }), style: floatingStyles }, floatingProps),
        React__default.createElement(ComboboxContentSearch, { open: props.open, placeholder: props.subjectNoun, searchValue: props.searchValue, setSearchValue: props.setSearchValue, handleSearchChange: props.handleSearchChange }),
        props.multiselect && (optionsExist || props.selected.length > 0) && (React__default.createElement(ComboboxContentHeader, { hasOptionsVisible: optionsExist, subjectNoun: props.subjectNoun, selectedCount: props.selected.length, onClearAll: () => {
                props.selectedStateSetter([]);
                onClear === null || onClear === void 0 ? void 0 : onClear();
            }, onSelectAll: () => {
                props.selectedStateSetter(props.options);
                onSelectAll === null || onSelectAll === void 0 ? void 0 : onSelectAll(optionsData);
            } })),
        React__default.createElement(ComboboxContentList, { multiselect: props.multiselect, options: props.options, selected: props.selected, optionsListRef: optionsListRef, searchValue: props.searchValue, subjectNoun: props.subjectNoun, loading: props.loading, onLoadMore: props.onLoadMore }),
        props.actionElements && (React__default.createElement("div", { className: styles.actions, role: "group" }, props.actionElements.map((child, index, childrenArray) => (React__default.createElement("div", { key: index, className: classnames({
                [styles.actionPadding]: index === childrenArray.length - 1,
            }) }, child)))))));
    if (parentNodeId) {
        return (React__default.createElement(FloatingTree, null,
            React__default.createElement(FloatingNode, { id: nodeId },
                React__default.createElement(FloatingPortal, null, content))));
    }
    return (globalThis === null || globalThis === void 0 ? void 0 : globalThis.document) ? (React__default.createElement(FloatingPortal, null, content)) : (content);
}

export { ComboboxContent as C };
