import React__default, { useState, useId, useEffect, useRef } from 'react';
import { g as getDefaultExportFromCjs } from './_commonjsHelpers-es.js';
import { _ as _baseFlatten } from './_baseFlatten-es.js';
import { a as _arrayMap, b as _baseGet } from './_getAllKeys-es.js';
import { _ as _baseEach, a as _baseIteratee } from './_baseEach-es.js';
import { b as isArrayLike_1, k as _baseUnary, a as isArray_1 } from './isTypedArray-es.js';
import { i as isSymbol_1 } from './isSymbol-es.js';
import { i as identity_1 } from './identity-es.js';
import { _ as _baseRest, a as _isIterateeCall } from './_isIterateeCall-es.js';
import { useLiveAnnounce } from '@jobber/hooks';
import { a as debounce } from './debounce-es.js';
import { I as Icon } from './Icon-es.js';
import { u as useFloating, o as offset, f as flip, c as size, b as autoUpdate } from './floating-ui.react-es.js';
import { c as calculateMaxHeight } from './maxHeight-es.js';

var baseEach = _baseEach,
    isArrayLike = isArrayLike_1;

/**
 * The base implementation of `_.map` without support for iteratee shorthands.
 *
 * @private
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 */
function baseMap$1(collection, iteratee) {
  var index = -1,
      result = isArrayLike(collection) ? Array(collection.length) : [];

  baseEach(collection, function(value, key, collection) {
    result[++index] = iteratee(value, key, collection);
  });
  return result;
}

var _baseMap = baseMap$1;

/**
 * The base implementation of `_.sortBy` which uses `comparer` to define the
 * sort order of `array` and replaces criteria objects with their corresponding
 * values.
 *
 * @private
 * @param {Array} array The array to sort.
 * @param {Function} comparer The function to define sort order.
 * @returns {Array} Returns `array`.
 */

function baseSortBy$1(array, comparer) {
  var length = array.length;

  array.sort(comparer);
  while (length--) {
    array[length] = array[length].value;
  }
  return array;
}

var _baseSortBy = baseSortBy$1;

var isSymbol = isSymbol_1;

/**
 * Compares values to sort them in ascending order.
 *
 * @private
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {number} Returns the sort order indicator for `value`.
 */
function compareAscending$1(value, other) {
  if (value !== other) {
    var valIsDefined = value !== undefined,
        valIsNull = value === null,
        valIsReflexive = value === value,
        valIsSymbol = isSymbol(value);

    var othIsDefined = other !== undefined,
        othIsNull = other === null,
        othIsReflexive = other === other,
        othIsSymbol = isSymbol(other);

    if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
        (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
        (valIsNull && othIsDefined && othIsReflexive) ||
        (!valIsDefined && othIsReflexive) ||
        !valIsReflexive) {
      return 1;
    }
    if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
        (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
        (othIsNull && valIsDefined && valIsReflexive) ||
        (!othIsDefined && valIsReflexive) ||
        !othIsReflexive) {
      return -1;
    }
  }
  return 0;
}

var _compareAscending = compareAscending$1;

var compareAscending = _compareAscending;

/**
 * Used by `_.orderBy` to compare multiple properties of a value to another
 * and stable sort them.
 *
 * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
 * specify an order of "desc" for descending or "asc" for ascending sort order
 * of corresponding values.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {boolean[]|string[]} orders The order to sort by for each property.
 * @returns {number} Returns the sort order indicator for `object`.
 */
function compareMultiple$1(object, other, orders) {
  var index = -1,
      objCriteria = object.criteria,
      othCriteria = other.criteria,
      length = objCriteria.length,
      ordersLength = orders.length;

  while (++index < length) {
    var result = compareAscending(objCriteria[index], othCriteria[index]);
    if (result) {
      if (index >= ordersLength) {
        return result;
      }
      var order = orders[index];
      return result * (order == 'desc' ? -1 : 1);
    }
  }
  // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
  // that causes it, under certain circumstances, to provide the same value for
  // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
  // for more details.
  //
  // This also ensures a stable sort in V8 and other engines.
  // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
  return object.index - other.index;
}

var _compareMultiple = compareMultiple$1;

var arrayMap = _arrayMap,
    baseGet = _baseGet,
    baseIteratee = _baseIteratee,
    baseMap = _baseMap,
    baseSortBy = _baseSortBy,
    baseUnary = _baseUnary,
    compareMultiple = _compareMultiple,
    identity = identity_1,
    isArray = isArray_1;

/**
 * The base implementation of `_.orderBy` without param guards.
 *
 * @private
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
 * @param {string[]} orders The sort orders of `iteratees`.
 * @returns {Array} Returns the new sorted array.
 */
function baseOrderBy$1(collection, iteratees, orders) {
  if (iteratees.length) {
    iteratees = arrayMap(iteratees, function(iteratee) {
      if (isArray(iteratee)) {
        return function(value) {
          return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);
        }
      }
      return iteratee;
    });
  } else {
    iteratees = [identity];
  }

  var index = -1;
  iteratees = arrayMap(iteratees, baseUnary(baseIteratee));

  var result = baseMap(collection, function(value, key, collection) {
    var criteria = arrayMap(iteratees, function(iteratee) {
      return iteratee(value);
    });
    return { 'criteria': criteria, 'index': ++index, 'value': value };
  });

  return baseSortBy(result, function(object, other) {
    return compareMultiple(object, other, orders);
  });
}

var _baseOrderBy = baseOrderBy$1;

var baseFlatten = _baseFlatten,
    baseOrderBy = _baseOrderBy,
    baseRest = _baseRest,
    isIterateeCall = _isIterateeCall;

/**
 * Creates an array of elements, sorted in ascending order by the results of
 * running each element in a collection thru each iteratee. This method
 * performs a stable sort, that is, it preserves the original sort order of
 * equal elements. The iteratees are invoked with one argument: (value).
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Collection
 * @param {Array|Object} collection The collection to iterate over.
 * @param {...(Function|Function[])} [iteratees=[_.identity]]
 *  The iteratees to sort by.
 * @returns {Array} Returns the new sorted array.
 * @example
 *
 * var users = [
 *   { 'user': 'fred',   'age': 48 },
 *   { 'user': 'barney', 'age': 36 },
 *   { 'user': 'fred',   'age': 30 },
 *   { 'user': 'barney', 'age': 34 }
 * ];
 *
 * _.sortBy(users, [function(o) { return o.user; }]);
 * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]
 *
 * _.sortBy(users, ['user', 'age']);
 * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]
 */
var sortBy = baseRest(function(collection, iteratees) {
  if (collection == null) {
    return [];
  }
  var length = iteratees.length;
  if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
    iteratees = [];
  } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
    iteratees = [iteratees[0]];
  }
  return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
});

var sortBy_1 = sortBy;

var sortBy$1 = /*@__PURE__*/getDefaultExportFromCjs(sortBy_1);

/**
 * Recursively finds a focusable element (div or button) in the specified direction
 */
function findFocusableElement(element, direction) {
    if (!element)
        return null;
    const nextElement = direction === "previous"
        ? element.previousElementSibling
        : element.nextElementSibling;
    if (!nextElement)
        return null;
    // Check if element is a div or button
    if (nextElement instanceof HTMLElement &&
        (nextElement.tagName === "DIV" || nextElement.tagName === "BUTTON")) {
        return nextElement;
    }
    // Recursively continue search
    return findFocusableElement(nextElement, direction);
}
function useInternalChipDismissible({ children, selected, onChange, onClick, onCustomAdd, }) {
    const [wrapperElement, setWrapperElement] = useState(null);
    const chipOptions = children.map(chip => chip.props);
    const visibleChipOptions = chipOptions.filter(chip => selected.includes(chip.value));
    const sortedVisibleChipOptions = sortBy$1(visibleChipOptions, chip => selected.indexOf(chip.value));
    const availableChipOptions = chipOptions.filter(chip => !selected.includes(chip.value));
    const { liveAnnounce } = useLiveAnnounce();
    const actions = {
        handleChipRemove: (value) => {
            return () => {
                liveAnnounce(`${value} Removed`);
                onChange(selected.filter(val => val !== value));
            };
        },
        handleChipAdd: (value) => onChange([...selected, value]),
        handleCustomAdd: onCustomAdd,
        handleChipClick: (value) => {
            if (onClick === undefined)
                return;
            return (event) => onClick(event, value);
        },
        handleWrapperKeyDown: (event) => {
            const target = event.target;
            const isInputAndHasValue = target instanceof HTMLInputElement && target.value;
            if (isInputAndHasValue)
                return;
            if (event.key === "ArrowLeft") {
                const prevFocusable = findFocusableElement(target, "previous");
                if (prevFocusable) {
                    prevFocusable.focus();
                }
            }
            if (event.key === "ArrowRight") {
                const nextFocusable = findFocusableElement(target, "next");
                if (nextFocusable) {
                    nextFocusable.focus();
                }
            }
        },
        handleChipKeyDown: (value) => {
            return (event) => {
                if (event.key === "Backspace" || event.key === "Delete") {
                    const target = event.target;
                    if (target instanceof HTMLElement) {
                        const prevFocusable = findFocusableElement(target, "previous");
                        const nextFocusable = findFocusableElement(target, "next");
                        if (prevFocusable) {
                            prevFocusable.focus();
                        }
                        else if (nextFocusable) {
                            nextFocusable.focus();
                        }
                    }
                    actions.handleChipRemove(value)();
                }
            };
        },
    };
    return Object.assign(Object.assign({}, actions), { ref: setWrapperElement, wrapperElement,
        sortedVisibleChipOptions,
        availableChipOptions });
}

const SEARCH_DEBOUNCE_TIME = 300;
// eslint-disable-next-line max-statements
function useInternalChipDismissibleInput({ options, isLoadingMore = false, onCustomOptionSelect, onOptionSelect, onSearch, onlyShowMenuOnSearch = false, autoSelectOnClickOutside = false, }) {
    const menuId = useId();
    const [allOptions, setAllOptions] = useState([]);
    const [searchValue, setSearchValue] = useState("");
    const [menuOpen, setMenuOpen] = useState(false);
    const [showInput, setShowInput] = useState(false);
    const [activeIndex, setActiveIndex] = useState(0);
    const [shouldCancelBlur, setShouldCancelBlur] = useState(false);
    const [shouldCancelEnter, setShouldCancelEnter] = useState(false);
    const canAddCustomOption = onCustomOptionSelect !== undefined && !isLoadingMore;
    const maxOptionIndex = allOptions.length - 1;
    const { liveAnnounce } = useLiveAnnounce();
    useEffect(() => {
        setAllOptions(generateOptions(options, searchValue, canAddCustomOption));
    }, [options]);
    const computed = {
        menuId,
        inputRef: useRef(null),
        activeOption: allOptions[activeIndex],
        hasAvailableOptions: allOptions.length > 0,
        nextOptionIndex: activeIndex < maxOptionIndex ? activeIndex + 1 : 0,
        previousOptionIndex: activeIndex > 0 ? activeIndex - 1 : maxOptionIndex,
    };
    function handleSearch(newSearchValue, newOptions = []) {
        onSearch && onSearch(newSearchValue);
        setAllOptions(generateOptions(newOptions, newSearchValue, canAddCustomOption));
        setShouldCancelEnter(false);
    }
    const handleDebouncedSearch = debounce(handleSearch, SEARCH_DEBOUNCE_TIME);
    const actions = {
        generateDescendantId: (index) => `${computed.menuId}-${index}`,
        handleReset: () => {
            setActiveIndex(activeIndex === 0 ? activeIndex : activeIndex - 1);
            setSearchValue("");
            if (onlyShowMenuOnSearch) {
                actions.handleCloseMenu();
            }
        },
        handleOpenMenu: () => setMenuOpen(true),
        handleCloseMenu: () => {
            setMenuOpen(false);
            setActiveIndex(0);
        },
        handleCancelBlur: () => setShouldCancelBlur(true),
        handleEnableBlur: () => setShouldCancelBlur(false),
        handleBlur: () => {
            if (shouldCancelBlur)
                return;
            if (autoSelectOnClickOutside &&
                searchValue.length > 0 &&
                allOptions.length > 0) {
                // If there's a custom option, select it. Otherwise select the best match
                const optionToSelect = canAddCustomOption
                    ? generateCustomOptionObject(searchValue)
                    : allOptions[0];
                actions.handleSelectOption(optionToSelect);
            }
            actions.handleReset();
            actions.handleCloseMenu();
            setShowInput(false);
        },
        handleFocus: () => {
            if (!onlyShowMenuOnSearch) {
                actions.handleOpenMenu();
            }
        },
        handleSearchChange: (event) => {
            setActiveIndex(0);
            const newSearchValue = event.currentTarget.value;
            setSearchValue(newSearchValue);
            setShouldCancelEnter(true);
            if (onlyShowMenuOnSearch && newSearchValue.length > 0 && !menuOpen) {
                setTimeout(() => {
                    actions.handleOpenMenu();
                }, SEARCH_DEBOUNCE_TIME);
            }
            if (onlyShowMenuOnSearch && newSearchValue.length === 0 && menuOpen) {
                actions.handleCloseMenu();
            }
        },
        handleSetActiveOnMouseOver: (index) => {
            return () => setActiveIndex(index);
        },
        handleSelectOption: (selected) => {
            var _a;
            if (allOptions.length === 0)
                return;
            const setValue = selected.custom ? onCustomOptionSelect : onOptionSelect;
            if (setValue) {
                setValue(selected.value);
                liveAnnounce(`${selected.label} Added`);
                actions.handleReset();
                (_a = computed.inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
            }
        },
        handleShowInput: () => {
            setShowInput(true);
            if (!onlyShowMenuOnSearch) {
                actions.handleOpenMenu();
            }
        },
        handleKeyDown: (event) => {
            const callbacks = {};
            if (!onlyShowMenuOnSearch || searchValue.length > 0) {
                callbacks.Enter = () => {
                    if (shouldCancelEnter)
                        return;
                    actions.handleSelectOption(computed.activeOption);
                };
                callbacks.Tab = () => actions.handleSelectOption(computed.activeOption);
                callbacks[","] = () => {
                    if (searchValue.length === 0)
                        return;
                    actions.handleSelectOption(generateCustomOptionObject(searchValue));
                };
                callbacks.ArrowDown = () => {
                    if (isLoadingMore && activeIndex === maxOptionIndex)
                        return;
                    setActiveIndex(computed.nextOptionIndex);
                };
                callbacks.ArrowUp = () => setActiveIndex(computed.previousOptionIndex);
            }
            if (searchValue.length === 0) {
                callbacks.Backspace = () => {
                    var _a;
                    // If there's no text left to delete,
                    // and delete is pressed again, focus on a chip instead.
                    const target = (_a = computed.inputRef.current) === null || _a === void 0 ? void 0 : _a.previousElementSibling;
                    if (target instanceof HTMLElement) {
                        target.focus();
                    }
                };
            }
            handleKeydownEvents(callbacks, event);
        },
        handleDebouncedSearch,
    };
    return Object.assign(Object.assign(Object.assign(Object.assign({}, actions), { activeIndex,
        allOptions }), computed), { menuOpen,
        showInput,
        searchValue,
        shouldCancelBlur });
}
function generateOptions(options, searchValue, canAddCustomOption) {
    const allOptions = options
        .filter(option => option.label.toLowerCase().match(searchValue.trim().toLowerCase()))
        .map(opt => (Object.assign(Object.assign({}, opt), { custom: false })));
    const { shouldAddCustomOption, customOption } = generateCustomOption(allOptions, searchValue, canAddCustomOption);
    shouldAddCustomOption && allOptions.push(customOption);
    return allOptions;
}
function generateCustomOption(options, searchValue, canAddCustomOption) {
    function shouldAddCustomOption() {
        const isMatchingOption = options.some(option => option.label.toLowerCase() === searchValue.trim().toLowerCase());
        return !!searchValue && !isMatchingOption;
    }
    return {
        shouldAddCustomOption: canAddCustomOption && shouldAddCustomOption(),
        customOption: generateCustomOptionObject(searchValue),
    };
}
function generateCustomOptionObject(searchValue) {
    return {
        value: searchValue,
        label: searchValue,
        prefix: React__default.createElement(Icon, { name: "add" }),
        custom: true,
    };
}
function handleKeydownEvents(callbacks, event) {
    if (event.shiftKey)
        return;
    Object.entries(callbacks).forEach(([key, callback]) => {
        if (event.key === key) {
            event.preventDefault();
            callback(event);
        }
    });
}

function useInView() {
    const ref = useRef(null);
    const [isInView, setIsInView] = useState(false);
    if (window.IntersectionObserver) {
        const observer = new IntersectionObserver(entries => {
            setIsInView(entries[0].isIntersecting);
        });
        useEffect(() => {
            ref.current && observer.observe(ref.current);
            return () => {
                if (!ref.current)
                    return;
                observer.unobserve(ref.current);
            };
        });
    }
    return { ref, isInView };
}

const ROUNDED_BORDER_ARROW_EDGE_OFFSET = 8;
const PREFERRED_MAX_HEIGHT = 320;
function useRepositionMenu(attachTo) {
    const { refs, floatingStyles } = useFloating({
        placement: "bottom",
        middleware: [
            offset(ROUNDED_BORDER_ARROW_EDGE_OFFSET),
            flip({ fallbackPlacements: ["top"] }),
            size({
                apply({ availableHeight, elements, rects }) {
                    const maxHeight = calculateMaxHeight(availableHeight, {
                        maxHeight: PREFERRED_MAX_HEIGHT,
                    });
                    Object.assign(elements.floating.style, {
                        maxHeight: `${maxHeight}px`,
                        maxWidth: `${rects.reference.width}px`,
                    });
                },
            }),
        ],
        elements: {
            reference: attachTo,
        },
        whileElementsMounted: autoUpdate,
    });
    return {
        setFloatingRef: refs.setFloating,
        styles: {
            float: floatingStyles,
        },
    };
}

function useScrollToActive(index) {
    const ref = useRef(null);
    useEffect(() => {
        var _a, _b;
        if (!window.HTMLElement.prototype.scrollIntoView)
            return;
        (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.children[index]) === null || _b === void 0 ? void 0 : _b.scrollIntoView({
            behavior: "smooth",
            block: "nearest",
            inline: "start",
        });
    }, [index]);
    return ref;
}

export { useInternalChipDismissibleInput as a, useInView as b, useRepositionMenu as c, useScrollToActive as d, useInternalChipDismissible as u };
