'use strict';

var React = require('react');
var useButton = require('./useButton-cjs.js');
var useRenderElement = require('./useRenderElement-cjs.js');
var useScrollLock = require('./useScrollLock-cjs.js');
var isElementDisabled = require('./isElementDisabled-cjs.js');
var useLabelableId = require('./useLabelableId-cjs.js');
var resolveAriaLabelledBy = require('./resolveAriaLabelledBy-cjs.js');
var scrollEdges = require('./scrollEdges-cjs.js');
var jsxRuntime = require('react/jsx-runtime');
var useCompositeListItem = require('./useCompositeListItem-cjs.js');
var floatingUi_utils = require('./floating-ui.utils-cjs.js');
var clamp = require('./clamp-cjs.js');
var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
var floatingUi_reactDom = require('./floating-ui.react-dom-cjs.js');

function _interopNamespaceDefault(e) {
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n.default = e;
  return Object.freeze(n);
}

var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);

/**
 * Returns a previous value of its argument.
 * @param value Current value.
 * @returns Previous value, or null if there is no previous value.
 */
function usePreviousValue(value) {
  const [state, setState] = React__namespace.useState({
    current: value,
    previous: null
  });
  if (value !== state.current) {
    setState({
      current: value,
      previous: state.current
    });
  }
  return state.previous;
}

const SelectRootContext = /*#__PURE__*/React__namespace.createContext(null);
if (process.env.NODE_ENV !== "production") SelectRootContext.displayName = "SelectRootContext";
const SelectFloatingContext = /*#__PURE__*/React__namespace.createContext(null);
if (process.env.NODE_ENV !== "production") SelectFloatingContext.displayName = "SelectFloatingContext";
function useSelectRootContext() {
  const context = React__namespace.useContext(SelectRootContext);
  if (context === null) {
    throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectRootContext is missing. Select parts must be placed within <Select.Root>.' : useRenderElement.formatErrorMessage(60));
  }
  return context;
}
function useSelectFloatingContext() {
  const context = React__namespace.useContext(SelectFloatingContext);
  if (context === null) {
    throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectFloatingContext is missing. Select parts must be placed within <Select.Root>.' : useRenderElement.formatErrorMessage(61));
  }
  return context;
}

const selectors = {
  id: useScrollLock.createSelector(state => state.id),
  labelId: useScrollLock.createSelector(state => state.labelId),
  modal: useScrollLock.createSelector(state => state.modal),
  multiple: useScrollLock.createSelector(state => state.multiple),
  items: useScrollLock.createSelector(state => state.items),
  itemToStringLabel: useScrollLock.createSelector(state => state.itemToStringLabel),
  itemToStringValue: useScrollLock.createSelector(state => state.itemToStringValue),
  isItemEqualToValue: useScrollLock.createSelector(state => state.isItemEqualToValue),
  value: useScrollLock.createSelector(state => state.value),
  hasSelectedValue: useScrollLock.createSelector(state => {
    const {
      value,
      multiple,
      itemToStringValue
    } = state;
    if (value == null) {
      return false;
    }
    if (multiple && Array.isArray(value)) {
      return value.length > 0;
    }
    return resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue) !== '';
  }),
  hasNullItemLabel: useScrollLock.createSelector((state, enabled) => {
    return enabled ? resolveAriaLabelledBy.hasNullItemLabel(state.items) : false;
  }),
  open: useScrollLock.createSelector(state => state.open),
  mounted: useScrollLock.createSelector(state => state.mounted),
  forceMount: useScrollLock.createSelector(state => state.forceMount),
  transitionStatus: useScrollLock.createSelector(state => state.transitionStatus),
  openMethod: useScrollLock.createSelector(state => state.openMethod),
  activeIndex: useScrollLock.createSelector(state => state.activeIndex),
  selectedIndex: useScrollLock.createSelector(state => state.selectedIndex),
  isActive: useScrollLock.createSelector((state, index) => state.activeIndex === index),
  isSelected: useScrollLock.createSelector((state, itemValue) => {
    const comparer = state.isItemEqualToValue;
    const storeValue = state.value;
    if (state.multiple) {
      return Array.isArray(storeValue) && storeValue.some(selectedItem => resolveAriaLabelledBy.compareItemEquality(itemValue, selectedItem, comparer));
    }

    // The value is the source of truth: a stale `selectedIndex` (e.g. the controlled
    // value changes while the popup is open, where the index sync is deferred) must not
    // keep a previously selected item marked as selected.
    return resolveAriaLabelledBy.compareItemEquality(itemValue, storeValue, comparer);
  }),
  isSelectedByFocus: useScrollLock.createSelector((state, index) => {
    return state.selectedIndex === index;
  }),
  popupProps: useScrollLock.createSelector(state => state.popupProps),
  triggerProps: useScrollLock.createSelector(state => state.triggerProps),
  triggerElement: useScrollLock.createSelector(state => state.triggerElement),
  positionerElement: useScrollLock.createSelector(state => state.positionerElement),
  listElement: useScrollLock.createSelector(state => state.listElement),
  popupSide: useScrollLock.createSelector(state => state.popupSide),
  scrollUpArrowVisible: useScrollLock.createSelector(state => state.scrollUpArrowVisible),
  scrollDownArrowVisible: useScrollLock.createSelector(state => state.scrollDownArrowVisible),
  hasScrollArrows: useScrollLock.createSelector(state => state.hasScrollArrows)
};

function SelectRoot(props) {
  const {
    id,
    value: valueProp,
    defaultValue = null,
    onValueChange,
    open: openProp,
    defaultOpen = false,
    onOpenChange,
    name: nameProp,
    form,
    autoComplete,
    disabled: disabledProp = false,
    readOnly = false,
    required = false,
    modal = true,
    actionsRef,
    inputRef,
    onOpenChangeComplete,
    items,
    multiple = false,
    itemToStringLabel,
    itemToStringValue,
    isItemEqualToValue = resolveAriaLabelledBy.defaultItemEquality,
    highlightItemOnHover = true,
    children
  } = props;
  const {
    clearErrors
  } = useLabelableId.useFormContext();
  const {
    setDirty,
    setTouched,
    setFocused,
    validityData,
    setFilled,
    name: fieldName,
    disabled: fieldDisabled,
    validation,
    validationMode
  } = useLabelableId.useFieldRootContext();
  const generatedId = useLabelableId.useLabelableId({
    id
  });
  const disabled = fieldDisabled || disabledProp;
  const name = fieldName ?? nameProp;
  const [value, setValueUnwrapped] = useButton.useControlled({
    controlled: valueProp,
    default: multiple ? defaultValue ?? useRenderElement.EMPTY_ARRAY : defaultValue,
    name: 'Select',
    state: 'value'
  });
  const [open, setOpenUnwrapped] = useButton.useControlled({
    controlled: openProp,
    default: defaultOpen,
    name: 'Select',
    state: 'open'
  });
  const listRef = React__namespace.useRef([]);
  const labelsRef = React__namespace.useRef([]);
  const popupRef = React__namespace.useRef(null);
  const scrollHandlerRef = React__namespace.useRef(null);
  const scrollArrowsMountedCountRef = React__namespace.useRef(0);
  const valueRef = React__namespace.useRef(null);
  const valuesRef = React__namespace.useRef([]);
  const typingRef = React__namespace.useRef(false);
  const firstItemTextRef = React__namespace.useRef(null);
  const selectedItemTextRef = React__namespace.useRef(null);
  const selectionRef = React__namespace.useRef({
    allowSelectedMouseUp: false,
    allowUnselectedMouseUp: false,
    dragY: 0
  });
  const alignItemWithTriggerActiveRef = React__namespace.useRef(false);
  const {
    mounted,
    setMounted,
    transitionStatus
  } = useButton.useTransitionStatus(open);
  const {
    openMethod,
    triggerProps: interactionTypeProps
  } = useScrollLock.useOpenInteractionType(open);
  const store = useRenderElement.useRefWithInit(() => new useScrollLock.Store({
    id: generatedId,
    labelId: undefined,
    modal,
    multiple,
    itemToStringLabel,
    itemToStringValue,
    isItemEqualToValue,
    value,
    open,
    mounted,
    transitionStatus,
    items,
    forceMount: false,
    openMethod: null,
    activeIndex: null,
    selectedIndex: null,
    popupProps: {},
    triggerProps: {},
    triggerElement: null,
    positionerElement: null,
    listElement: null,
    popupSide: null,
    scrollUpArrowVisible: false,
    scrollDownArrowVisible: false,
    hasScrollArrows: false
  })).current;
  const activeIndex = useScrollLock.useStore(store, selectors.activeIndex);
  const selectedIndex = useScrollLock.useStore(store, selectors.selectedIndex);
  const triggerElement = useScrollLock.useStore(store, selectors.triggerElement);
  const positionerElement = useScrollLock.useStore(store, selectors.positionerElement);
  const previousOpenMethod = usePreviousValue(openMethod);
  const renderedOpenMethod = openMethod ?? previousOpenMethod ?? null;
  const serializedValue = React__namespace.useMemo(() => {
    // In multiple mode the shared input is nameless; per-value entries are submitted via
    // `hiddenInputs`. Its value is therefore irrelevant, and passing the whole array to
    // `stringifyAsValue` would invoke a user `itemToStringValue` with an array it doesn't expect.
    if (multiple) {
      return '';
    }
    return resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue);
  }, [multiple, value, itemToStringValue]);
  const fieldStringValue = React__namespace.useMemo(() => {
    if (multiple && Array.isArray(value)) {
      return value.map(currentValue => resolveAriaLabelledBy.stringifyAsValue(currentValue, itemToStringValue));
    }
    return resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue);
  }, [multiple, value, itemToStringValue]);
  const controlRef = useButton.useValueAsRef(store.state.triggerElement);
  const getStringifiedValueForForm = useButton.useStableCallback(() => fieldStringValue);
  useLabelableId.useRegisterFieldControl(controlRef, generatedId, value, getStringifiedValueForForm, !disabled, nameProp);
  const initialValueRef = React__namespace.useRef(value);
  // Mirror the `hasSelectedValue` store selector so the Field's filled state agrees with the
  // trigger/value placeholder semantics (a value serializing to `''` counts as empty).
  const hasSelectedValue = multiple ? Array.isArray(value) && value.length > 0 : value != null && resolveAriaLabelledBy.stringifyAsValue(value, itemToStringValue) !== '';
  useButton.useIsoLayoutEffect(() => {
    // Ensure the values and labels are registered for programmatic value changes.
    if (value !== initialValueRef.current) {
      store.set('forceMount', true);
    }
  }, [store, value]);
  useButton.useIsoLayoutEffect(() => {
    setFilled(hasSelectedValue);
  }, [hasSelectedValue, setFilled]);
  useButton.useIsoLayoutEffect(function syncSelectedIndex() {
    const registry = valuesRef.current;
    let nextIndex;
    if (multiple) {
      const currentValue = Array.isArray(value) ? value : [];
      if (currentValue.length === 0) {
        nextIndex = null;
      } else {
        const lastValue = currentValue[currentValue.length - 1];
        const lastIndex = resolveAriaLabelledBy.findItemIndex(registry, lastValue, isItemEqualToValue);
        nextIndex = lastIndex === -1 ? null : lastIndex;
      }
    } else {
      const index = resolveAriaLabelledBy.findItemIndex(registry, value, isItemEqualToValue);
      nextIndex = index === -1 ? null : index;
    }
    if (nextIndex === null) {
      selectedItemTextRef.current = null;
    }
    if (open) {
      return;
    }
    store.set('selectedIndex', nextIndex);
  }, [hasSelectedValue, multiple, open, value, valuesRef, isItemEqualToValue, store, selectedItemTextRef]);
  function isSelectedValueDirty(currentValue) {
    const initialValue = validityData.initialValue;
    if (Array.isArray(currentValue) && Array.isArray(initialValue)) {
      return !resolveAriaLabelledBy.areArraysEqual(currentValue, initialValue, (itemValue, initialItemValue) => resolveAriaLabelledBy.compareItemEquality(itemValue, initialItemValue, isItemEqualToValue));
    }
    return currentValue !== initialValue;
  }
  useButton.useValueChanged(value, () => {
    clearErrors(name);
    setDirty(isSelectedValueDirty(value));
    validation.change(value);
  });
  const setOpen = useButton.useStableCallback((nextOpen, eventDetails) => {
    onOpenChange?.(nextOpen, eventDetails);
    if (eventDetails.isCanceled) {
      return;
    }
    setOpenUnwrapped(nextOpen);
    if (!nextOpen && (eventDetails.reason === useButton.focusOut || eventDetails.reason === useButton.outsidePress)) {
      setTouched(true);
      setFocused(false);
      if (validationMode === 'onBlur') {
        validation.commit(value);
      }
    }
  });
  const handleUnmount = useButton.useStableCallback(() => {
    setMounted(false);
    store.update({
      activeIndex: null,
      openMethod: null
    });
    onOpenChangeComplete?.(false);
  });
  useButton.useOpenChangeComplete({
    enabled: !actionsRef,
    open,
    ref: popupRef,
    onComplete() {
      if (!open) {
        handleUnmount();
      }
    }
  });
  React__namespace.useImperativeHandle(actionsRef, () => ({
    unmount: handleUnmount
  }), [handleUnmount]);
  const setValue = useButton.useStableCallback((nextValue, eventDetails) => {
    onValueChange?.(nextValue, eventDetails);
    if (eventDetails.isCanceled) {
      return;
    }
    setValueUnwrapped(nextValue);
  });
  const handleScrollArrowVisibility = useButton.useStableCallback(() => {
    const scroller = store.state.listElement || popupRef.current;
    if (!scroller) {
      return;
    }
    const maxScrollTop = scrollEdges.getMaxScrollOffset(scroller.scrollHeight, scroller.clientHeight);
    const scrollTop = scrollEdges.normalizeScrollOffset(scroller.scrollTop, maxScrollTop);
    const shouldShowUp = scrollTop > 0;
    const shouldShowDown = scrollTop < maxScrollTop;
    if (store.state.scrollUpArrowVisible !== shouldShowUp) {
      store.set('scrollUpArrowVisible', shouldShowUp);
    }
    if (store.state.scrollDownArrowVisible !== shouldShowDown) {
      store.set('scrollDownArrowVisible', shouldShowDown);
    }
  });
  const floatingContext = useCompositeListItem.useFloatingRootContext({
    open,
    onOpenChange: setOpen,
    elements: {
      reference: triggerElement,
      floating: positionerElement
    }
  });
  const click = useScrollLock.useClick(floatingContext, {
    enabled: !readOnly && !disabled,
    event: 'mousedown'
  });
  const dismiss = useScrollLock.useDismiss(floatingContext);
  const listNavigation = useCompositeListItem.useListNavigation(floatingContext, {
    enabled: !readOnly && !disabled,
    listRef,
    activeIndex,
    selectedIndex,
    disabledIndices: useRenderElement.EMPTY_ARRAY,
    onNavigate(nextActiveIndex) {
      // Retain the highlight while transitioning out.
      if (nextActiveIndex === null && !open) {
        return;
      }
      store.set('activeIndex', nextActiveIndex);
    },
    focusItemOnHover: highlightItemOnHover
  });
  const typeahead = useCompositeListItem.useTypeahead(floatingContext, {
    enabled: !readOnly && !disabled && (open || !multiple),
    listRef: labelsRef,
    activeIndex,
    selectedIndex,
    // Skip disabled items while matching so typeahead advances to the next selectable item
    // (a click can never select a disabled item and native `<select>` skips them too). Resolve
    // the disabled state from the element via the attribute-only `isElementDisabled` so the
    // hidden, force-mounted items used for closed-trigger typeahead aren't dropped by the
    // `elementsRef`/visibility filter that `disabledIndices` deliberately sidesteps.
    disabledIndices: index => isElementDisabled.isElementDisabled(listRef.current[index]),
    onMatch(index) {
      if (open) {
        store.set('activeIndex', index);
      } else {
        setValue(valuesRef.current[index], useButton.createChangeEventDetails('none'));
      }
    },
    onTyping(typing) {
      typingRef.current = typing;
    }
  });
  const mergedTriggerProps = React__namespace.useMemo(() => {
    const triggerInteractionProps = useRenderElement.mergeProps(typeahead.reference, listNavigation.reference, dismiss.reference, click.reference, interactionTypeProps);
    if (generatedId) {
      triggerInteractionProps.id = generatedId;
    }
    return triggerInteractionProps;
  }, [click.reference, typeahead.reference, listNavigation.reference, dismiss.reference, interactionTypeProps, generatedId]);
  const popupProps = React__namespace.useMemo(() => useRenderElement.mergeProps(useScrollLock.FOCUSABLE_POPUP_PROPS, typeahead.floating, listNavigation.floating, dismiss.floating), [typeahead.floating, listNavigation.floating, dismiss.floating]);
  const itemProps = listNavigation.item ?? useRenderElement.EMPTY_OBJECT;
  useScrollLock.useOnFirstRender(() => {
    store.update({
      popupProps,
      triggerProps: mergedTriggerProps
    });
  });
  useButton.useIsoLayoutEffect(() => {
    store.update({
      id: generatedId,
      modal,
      multiple,
      value,
      open,
      mounted,
      transitionStatus,
      popupProps,
      triggerProps: mergedTriggerProps,
      items,
      itemToStringLabel,
      itemToStringValue,
      isItemEqualToValue,
      openMethod: renderedOpenMethod
    });
  }, [store, generatedId, modal, multiple, value, open, mounted, transitionStatus, popupProps, mergedTriggerProps, items, itemToStringLabel, itemToStringValue, isItemEqualToValue, renderedOpenMethod]);
  const contextValue = React__namespace.useMemo(() => ({
    store,
    name,
    required,
    disabled,
    readOnly,
    multiple,
    highlightItemOnHover,
    setValue,
    setOpen,
    listRef,
    popupRef,
    scrollHandlerRef,
    handleScrollArrowVisibility,
    scrollArrowsMountedCountRef,
    itemProps,
    valueRef,
    valuesRef,
    labelsRef,
    typingRef,
    selectionRef,
    firstItemTextRef,
    selectedItemTextRef,
    validation,
    onOpenChangeComplete,
    alignItemWithTriggerActiveRef,
    initialValueRef
  }), [store, name, required, disabled, readOnly, multiple, highlightItemOnHover, setValue, setOpen, itemProps, validation, onOpenChangeComplete, handleScrollArrowVisibility]);
  const ref = useRenderElement.useMergedRefs(inputRef, validation.inputRef);
  const hasMultipleSelection = multiple && Array.isArray(value) && value.length > 0;
  const hiddenInputName = multiple ? undefined : name;
  const hiddenInputs = React__namespace.useMemo(() => {
    if (!multiple || !Array.isArray(value) || !name) {
      return null;
    }
    return value.map(v => {
      const currentSerializedValue = resolveAriaLabelledBy.stringifyAsValue(v, itemToStringValue);
      return /*#__PURE__*/jsxRuntime.jsx("input", {
        type: "hidden",
        form: form,
        name: name,
        value: currentSerializedValue,
        disabled: disabled
      }, currentSerializedValue);
    });
  }, [multiple, value, form, name, itemToStringValue, disabled]);
  return /*#__PURE__*/jsxRuntime.jsx(SelectRootContext.Provider, {
    value: contextValue,
    children: /*#__PURE__*/jsxRuntime.jsxs(SelectFloatingContext.Provider, {
      value: floatingContext,
      children: [children, /*#__PURE__*/jsxRuntime.jsx("input", {
        ...validation.getValidationProps(disabled, {
          onFocus() {
            // Move focus to the trigger element when the hidden input is focused.
            store.state.triggerElement?.focus({
              // Supported in Chrome from 144 (January 2026)
              focusVisible: true
            });
          },
          // Handle browser autofill.
          onChange(event) {
            // Workaround for https://github.com/facebook/react/issues/9023
            if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
              return;
            }
            const nextValue = event.currentTarget.value;
            const details = useButton.createChangeEventDetails(useButton.none, event.nativeEvent);
            function handleChange() {
              if (multiple) {
                // Browser autofill only writes a single scalar value.
                return;
              }

              // Preserve the original serialized matching, then fall back to rendered text,
              // which browsers can autofill for primitive values like `value="US">United States`.
              const nextValueLower = nextValue.toLowerCase();
              let matchingIndex = valuesRef.current.findIndex(candidate => resolveAriaLabelledBy.stringifyAsValue(candidate, itemToStringValue).toLowerCase() === nextValueLower || resolveAriaLabelledBy.stringifyAsLabel(candidate, itemToStringLabel).toLowerCase() === nextValueLower);
              if (matchingIndex === -1) {
                matchingIndex = valuesRef.current.findIndex((_, index) => {
                  const renderedLabel = labelsRef.current[index];
                  return renderedLabel != null && renderedLabel.toLowerCase() === nextValueLower;
                });
              }
              const matchingValue = matchingIndex === -1 ? undefined : valuesRef.current[matchingIndex];
              if (matchingValue != null) {
                // `setValue` may be canceled by `onValueChange`; rely on `useValueChanged` to
                // mark the field dirty and run validation only when the value actually changes.
                setValue(matchingValue, details);
              }
            }
            store.set('forceMount', true);
            queueMicrotask(handleChange);
          }
        }),
        id: generatedId && hiddenInputName == null ? `${generatedId}-hidden-input` : undefined,
        form: form,
        name: hiddenInputName,
        autoComplete: autoComplete,
        value: serializedValue,
        disabled: disabled,
        required: required && !hasMultipleSelection,
        readOnly: readOnly,
        ref: ref,
        style: name ? useButton.visuallyHiddenInput : useButton.visuallyHidden,
        tabIndex: -1,
        "aria-hidden": true,
        suppressHydrationWarning: true
      }), hiddenInputs]
    })
  });
}

const BOUNDARY_OFFSET = 2;
const SELECTED_DELAY = 400;
const stateAttributesMapping$2 = {
  ...useScrollLock.pressableTriggerOpenStateMapping,
  ...useLabelableId.fieldValidityMapping,
  popupSide: side => side ? {
    'data-popup-side': side
  } : null,
  value: () => null
};

/**
 * A button that opens the select popup.
 * Renders a `<button>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectTrigger = /*#__PURE__*/React__namespace.forwardRef(function SelectTrigger(componentProps, forwardedRef) {
  const {
    render,
    className,
    id: idProp,
    disabled: disabledProp = false,
    nativeButton = true,
    style,
    ...elementProps
  } = componentProps;
  const {
    setTouched,
    setFocused,
    validationMode,
    state: fieldState,
    disabled: fieldDisabled
  } = useLabelableId.useFieldRootContext();
  const {
    labelId: fieldLabelId
  } = useLabelableId.useLabelableContext();
  const {
    store,
    setOpen,
    selectionRef,
    validation,
    readOnly,
    required,
    alignItemWithTriggerActiveRef,
    disabled: selectDisabled
  } = useSelectRootContext();
  const disabled = fieldDisabled || selectDisabled || disabledProp;
  const open = useScrollLock.useStore(store, selectors.open);
  const mounted = useScrollLock.useStore(store, selectors.mounted);
  const value = useScrollLock.useStore(store, selectors.value);
  const triggerProps = useScrollLock.useStore(store, selectors.triggerProps);
  const positionerElement = useScrollLock.useStore(store, selectors.positionerElement);
  const listElement = useScrollLock.useStore(store, selectors.listElement);
  const popupSideValue = useScrollLock.useStore(store, selectors.popupSide);
  const rootId = useScrollLock.useStore(store, selectors.id);
  const selectLabelId = useScrollLock.useStore(store, selectors.labelId);
  const hasSelectedValue = useScrollLock.useStore(store, selectors.hasSelectedValue);
  const popupSide = mounted && positionerElement ? popupSideValue : null;
  const id = idProp ?? rootId;
  const ariaLabelledBy = resolveAriaLabelledBy.resolveAriaLabelledBy(fieldLabelId, selectLabelId);
  useLabelableId.useLabelableId({
    id
  });
  const positionerRef = useButton.useValueAsRef(positionerElement);
  const triggerRef = React__namespace.useRef(null);
  const {
    getButtonProps,
    buttonRef
  } = useButton.useButton({
    disabled,
    native: nativeButton
  });
  const setTriggerElement = useButton.useStableCallback(element => {
    store.set('triggerElement', element);
  });
  const timeoutFocus = useButton.useTimeout();
  const timeoutMouseDown = useButton.useTimeout();
  const selectedDelayTimeout = useButton.useTimeout();
  React__namespace.useEffect(() => {
    if (open) {
      // A mousedown on the trigger can open the popup under the cursor. Keep mouseup selection
      // disabled briefly so releasing over either the selected item or a neighboring item doesn't
      // commit an accidental selection. SelectItem can still opt into unselected mouseup sooner
      // after a real drag over the item.
      selectedDelayTimeout.start(SELECTED_DELAY, () => {
        selectionRef.current.allowUnselectedMouseUp = true;
        selectionRef.current.allowSelectedMouseUp = true;
      });
      return () => {
        selectedDelayTimeout.clear();
      };
    }
    selectionRef.current = {
      allowSelectedMouseUp: false,
      allowUnselectedMouseUp: false,
      dragY: 0
    };
    timeoutMouseDown.clear();
    return undefined;
  }, [open, selectionRef, timeoutMouseDown, selectedDelayTimeout]);
  const mergedProps = useRenderElement.mergeProps(triggerProps, {
    id,
    role: 'combobox',
    'aria-expanded': open ? 'true' : 'false',
    'aria-haspopup': 'listbox',
    'aria-controls': open ? listElement?.id ?? useScrollLock.getFloatingFocusElement(positionerElement)?.id : undefined,
    'aria-labelledby': ariaLabelledBy,
    'aria-readonly': readOnly || undefined,
    'aria-required': required || undefined,
    tabIndex: disabled ? -1 : 0,
    onFocus(event) {
      setFocused(true);

      // The popup element shouldn't obscure the focused trigger.
      if (open && alignItemWithTriggerActiveRef.current) {
        setOpen(false, useButton.createChangeEventDetails(useButton.none, event.nativeEvent));
      }

      // Saves a re-render on initial click: `forceMount === true` mounts
      // the items before `open === true`. We could sync those cycles better
      // without a timeout, but this is enough for now.
      timeoutFocus.start(0, () => {
        store.set('forceMount', true);
      });
    },
    onBlur(event) {
      // If focus is moving into the popup, don't count it as a blur.
      if (useButton.contains(positionerElement, event.relatedTarget)) {
        return;
      }
      setTouched(true);
      setFocused(false);
      if (validationMode === 'onBlur') {
        validation.commit(value);
      }
    },
    onMouseDown(event) {
      if (open) {
        return;
      }
      const doc = useButton.ownerDocument(event.currentTarget);
      function handleMouseUp(mouseEvent) {
        if (!triggerRef.current) {
          return;
        }
        const mouseUpTarget = mouseEvent.target;

        // Don't treat the release as an outside press when it lands on the trigger or inside
        // the popup positioner (or their children).
        if (useButton.contains(triggerRef.current, mouseUpTarget) || useButton.contains(positionerRef.current, mouseUpTarget)) {
          return;
        }
        const bounds = useCompositeListItem.getPseudoElementBounds(triggerRef.current);
        if (mouseEvent.clientX >= bounds.left - BOUNDARY_OFFSET && mouseEvent.clientX <= bounds.right + BOUNDARY_OFFSET && mouseEvent.clientY >= bounds.top - BOUNDARY_OFFSET && mouseEvent.clientY <= bounds.bottom + BOUNDARY_OFFSET) {
          return;
        }
        setOpen(false, useButton.createChangeEventDetails(useButton.cancelOpen, mouseEvent));
      }

      // Firefox can fire this upon mousedown
      timeoutMouseDown.start(0, () => {
        doc.addEventListener('mouseup', handleMouseUp, {
          once: true
        });
      });
    }
  }, elementProps, getButtonProps);
  const props = validation.getValidationProps(disabled, mergedProps);

  // ensure nested useButton does not overwrite the combobox role:
  // <Toolbar.Button render={<Select.Trigger />} />
  props.role = 'combobox';
  const state = {
    ...fieldState,
    open,
    disabled,
    value,
    readOnly,
    popupSide,
    placeholder: !hasSelectedValue
  };
  return useRenderElement.useRenderElement('button', componentProps, {
    ref: [forwardedRef, triggerRef, buttonRef, setTriggerElement],
    state,
    stateAttributesMapping: stateAttributesMapping$2,
    props
  });
});
if (process.env.NODE_ENV !== "production") SelectTrigger.displayName = "SelectTrigger";

const stateAttributesMapping$1 = {
  value: () => null
};

/**
 * A text label of the currently selected item.
 * Renders a `<span>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectValue = /*#__PURE__*/React__namespace.forwardRef(function SelectValue(componentProps, forwardedRef) {
  const {
    className,
    render,
    children: childrenProp,
    placeholder,
    style,
    ...elementProps
  } = componentProps;
  const {
    store,
    valueRef
  } = useSelectRootContext();
  const value = useScrollLock.useStore(store, selectors.value);
  const items = useScrollLock.useStore(store, selectors.items);
  const itemToStringLabel = useScrollLock.useStore(store, selectors.itemToStringLabel);
  const hasSelectedValue = useScrollLock.useStore(store, selectors.hasSelectedValue);
  const shouldCheckNullItemLabel = !hasSelectedValue && placeholder != null && childrenProp == null;
  const hasNullLabel = useScrollLock.useStore(store, selectors.hasNullItemLabel, shouldCheckNullItemLabel);
  const state = {
    value,
    placeholder: !hasSelectedValue
  };
  let children = null;
  if (typeof childrenProp === 'function') {
    children = childrenProp(value);
  } else if (childrenProp != null) {
    children = childrenProp;
  } else if (!hasSelectedValue && placeholder != null && !hasNullLabel) {
    children = placeholder;
  } else if (Array.isArray(value)) {
    children = resolveAriaLabelledBy.resolveMultipleLabels(value, items, itemToStringLabel);
  } else {
    children = resolveAriaLabelledBy.resolveSelectedLabel(value, items, itemToStringLabel);
  }
  const element = useRenderElement.useRenderElement('span', componentProps, {
    state,
    ref: [forwardedRef, valueRef],
    props: [{
      children
    }, elementProps],
    stateAttributesMapping: stateAttributesMapping$1
  });
  return element;
});
if (process.env.NODE_ENV !== "production") SelectValue.displayName = "SelectValue";

const SelectPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
if (process.env.NODE_ENV !== "production") SelectPortalContext.displayName = "SelectPortalContext";

const SelectPortal = /*#__PURE__*/React__namespace.forwardRef(function SelectPortal(portalProps, forwardedRef) {
  const {
    store
  } = useSelectRootContext();
  const mounted = useScrollLock.useStore(store, selectors.mounted);
  const forceMount = useScrollLock.useStore(store, selectors.forceMount);
  const shouldRender = mounted || forceMount;
  if (!shouldRender) {
    return null;
  }
  return /*#__PURE__*/jsxRuntime.jsx(SelectPortalContext.Provider, {
    value: true,
    children: /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingPortal, {
      ref: forwardedRef,
      ...portalProps
    })
  });
});
if (process.env.NODE_ENV !== "production") SelectPortal.displayName = "SelectPortal";

const SelectPositionerContext = /*#__PURE__*/React__namespace.createContext(undefined);
if (process.env.NODE_ENV !== "production") SelectPositionerContext.displayName = "SelectPositionerContext";
function useSelectPositionerContext() {
  const context = React__namespace.useContext(SelectPositionerContext);
  if (!context) {
    throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectPositionerContext is missing. SelectPositioner parts must be placed within <Select.Positioner>.' : useRenderElement.formatErrorMessage(59));
  }
  return context;
}

function clearStyles(element, originalStyles) {
  if (element) {
    Object.assign(element.style, originalStyles);
  }
}
const LIST_FUNCTIONAL_STYLES = {
  position: 'relative',
  maxHeight: '100%',
  overflowX: 'hidden',
  overflowY: 'auto'
};

const FIXED = {
  position: 'fixed'
};

/**
 * Positions the select popup.
 * Renders a `<div>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectPositioner = /*#__PURE__*/React__namespace.forwardRef(function SelectPositioner(componentProps, forwardedRef) {
  const {
    anchor,
    positionMethod = 'absolute',
    className,
    render,
    side = 'bottom',
    align = 'center',
    sideOffset = 0,
    alignOffset = 0,
    collisionBoundary = 'clipping-ancestors',
    collisionPadding,
    arrowPadding = 5,
    sticky = false,
    disableAnchorTracking,
    alignItemWithTrigger = true,
    collisionAvoidance = useScrollLock.DROPDOWN_COLLISION_AVOIDANCE,
    style,
    ...elementProps
  } = componentProps;
  const {
    store,
    listRef,
    labelsRef,
    alignItemWithTriggerActiveRef,
    selectedItemTextRef,
    valuesRef,
    initialValueRef,
    popupRef,
    setValue
  } = useSelectRootContext();
  const floatingRootContext = useSelectFloatingContext();
  const open = useScrollLock.useStore(store, selectors.open);
  const mounted = useScrollLock.useStore(store, selectors.mounted);
  const modal = useScrollLock.useStore(store, selectors.modal);
  const value = useScrollLock.useStore(store, selectors.value);
  const openMethod = useScrollLock.useStore(store, selectors.openMethod);
  const positionerElement = useScrollLock.useStore(store, selectors.positionerElement);
  const triggerElement = useScrollLock.useStore(store, selectors.triggerElement);
  const isItemEqualToValue = useScrollLock.useStore(store, selectors.isItemEqualToValue);
  const transitionStatus = useScrollLock.useStore(store, selectors.transitionStatus);
  const scrollUpArrowRef = React__namespace.useRef(null);
  const scrollDownArrowRef = React__namespace.useRef(null);
  const [controlledAlignItemWithTrigger, setControlledAlignItemWithTrigger] = React__namespace.useState(alignItemWithTrigger);
  const alignItemWithTriggerActive = mounted && controlledAlignItemWithTrigger && openMethod !== 'touch';
  if (!mounted && controlledAlignItemWithTrigger !== alignItemWithTrigger) {
    setControlledAlignItemWithTrigger(alignItemWithTrigger);
  }
  useButton.useIsoLayoutEffect(() => {
    if (!mounted) {
      if (selectors.scrollUpArrowVisible(store.state)) {
        store.set('scrollUpArrowVisible', false);
      }
      if (selectors.scrollDownArrowVisible(store.state)) {
        store.set('scrollDownArrowVisible', false);
      }
    }
  }, [store, mounted]);
  React__namespace.useImperativeHandle(alignItemWithTriggerActiveRef, () => alignItemWithTriggerActive);
  useCompositeListItem.useAnchoredPopupScrollLock((alignItemWithTriggerActive || modal) && open, openMethod === 'touch', positionerElement, triggerElement);
  const positioning = useCompositeListItem.useAnchorPositioning({
    anchor,
    floatingRootContext,
    positionMethod,
    mounted,
    side,
    sideOffset,
    align,
    alignOffset,
    arrowPadding,
    collisionBoundary,
    collisionPadding,
    sticky,
    disableAnchorTracking: disableAnchorTracking ?? alignItemWithTriggerActive,
    collisionAvoidance,
    keepMounted: true
  });
  const renderedSide = alignItemWithTriggerActive ? 'none' : positioning.side;
  const positionerStyles = alignItemWithTriggerActive ? FIXED : positioning.positionerStyles;
  const state = {
    open,
    side: renderedSide,
    align: positioning.align,
    anchorHidden: positioning.anchorHidden
  };
  useButton.useIsoLayoutEffect(() => {
    store.set('popupSide', positioning.side);
  }, [store, positioning.side]);
  const setPositionerElement = useButton.useStableCallback(element => {
    store.set('positionerElement', element);
  });
  const element = useCompositeListItem.usePositioner(componentProps, state, {
    styles: positionerStyles,
    transitionStatus,
    props: elementProps,
    refs: [forwardedRef, setPositionerElement],
    hidden: !mounted,
    inert: !open
  });
  const prevMapSizeRef = React__namespace.useRef(0);
  const onMapChange = useButton.useStableCallback(map => {
    if (map.size === 0 && prevMapSizeRef.current === 0) {
      return;
    }
    if (valuesRef.current.length === 0) {
      return;
    }
    const prevSize = prevMapSizeRef.current;
    prevMapSizeRef.current = map.size;
    if (map.size === prevSize) {
      return;
    }
    const eventDetails = useButton.createChangeEventDetails(useButton.none);
    if (prevSize !== 0 && !store.state.multiple && value !== null) {
      const selectedValueIndex = resolveAriaLabelledBy.findItemIndex(valuesRef.current, value, isItemEqualToValue);
      if (selectedValueIndex === -1) {
        const initialSelectedValue = initialValueRef.current;
        const hasInitial = initialSelectedValue != null && resolveAriaLabelledBy.findItemIndex(valuesRef.current, initialSelectedValue, isItemEqualToValue) !== -1;
        const nextValue = hasInitial ? initialSelectedValue : null;
        setValue(nextValue, eventDetails);
        if (nextValue === null) {
          store.set('selectedIndex', null);
          selectedItemTextRef.current = null;
        }
      }
    }
    if (prevSize !== 0 && store.state.multiple && Array.isArray(value)) {
      const hasVisibleItem = selectedItemValue => resolveAriaLabelledBy.findItemIndex(valuesRef.current, selectedItemValue, isItemEqualToValue) !== -1;
      const nextValue = value.filter(selectedItemValue => hasVisibleItem(selectedItemValue));
      if (nextValue.length !== value.length || nextValue.some(selectedItemValue => !resolveAriaLabelledBy.selectedValueIncludes(value, selectedItemValue, isItemEqualToValue))) {
        setValue(nextValue, eventDetails);
        if (nextValue.length === 0) {
          store.set('selectedIndex', null);
          selectedItemTextRef.current = null;
        }
      }
    }
    if (open && alignItemWithTriggerActive) {
      store.update({
        scrollUpArrowVisible: false,
        scrollDownArrowVisible: false
      });
      const stylesToClear = {
        height: ''
      };
      clearStyles(positionerElement, stylesToClear);
      clearStyles(popupRef.current, stylesToClear);
    }
  });
  const contextValue = React__namespace.useMemo(() => ({
    ...positioning,
    side: renderedSide,
    alignItemWithTriggerActive,
    setControlledAlignItemWithTrigger,
    scrollUpArrowRef,
    scrollDownArrowRef
  }), [positioning, renderedSide, alignItemWithTriggerActive, setControlledAlignItemWithTrigger]);
  return /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.CompositeList, {
    elementsRef: listRef,
    labelsRef: labelsRef,
    onMapChange: onMapChange,
    children: /*#__PURE__*/jsxRuntime.jsxs(SelectPositionerContext.Provider, {
      value: contextValue,
      children: [mounted && modal && /*#__PURE__*/jsxRuntime.jsx(useScrollLock.InternalBackdrop, {
        inert: useScrollLock.inertValue(!open),
        cutout: triggerElement
      }), element]
    })
  });
});
if (process.env.NODE_ENV !== "production") SelectPositioner.displayName = "SelectPositioner";

const stateAttributesMapping = {
  ...useScrollLock.popupStateMapping,
  ...useButton.transitionStatusMapping
};

/**
 * A container for the select list.
 * Renders a `<div>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectPopup = /*#__PURE__*/React__namespace.forwardRef(function SelectPopup(componentProps, forwardedRef) {
  const {
    render,
    className,
    style,
    finalFocus,
    ...elementProps
  } = componentProps;
  const {
    store,
    popupRef,
    onOpenChangeComplete,
    setOpen,
    valueRef,
    firstItemTextRef,
    selectedItemTextRef,
    multiple,
    handleScrollArrowVisibility,
    scrollHandlerRef,
    listRef,
    highlightItemOnHover
  } = useSelectRootContext();
  const {
    side,
    align,
    alignItemWithTriggerActive,
    isPositioned,
    setControlledAlignItemWithTrigger
  } = useSelectPositionerContext();
  const insideToolbar = isElementDisabled.useToolbarRootContext(true) != null;
  const floatingRootContext = useSelectFloatingContext();
  const direction = useScrollLock.useDirection();
  const {
    nonce,
    disableStyleElements
  } = scrollEdges.useCSPContext();
  const id = useScrollLock.useStore(store, selectors.id);
  const open = useScrollLock.useStore(store, selectors.open);
  const openMethod = useScrollLock.useStore(store, selectors.openMethod);
  const mounted = useScrollLock.useStore(store, selectors.mounted);
  const popupProps = useScrollLock.useStore(store, selectors.popupProps);
  const transitionStatus = useScrollLock.useStore(store, selectors.transitionStatus);
  const triggerElement = useScrollLock.useStore(store, selectors.triggerElement);
  const positionerElement = useScrollLock.useStore(store, selectors.positionerElement);
  const listElement = useScrollLock.useStore(store, selectors.listElement);
  const reachedMaxHeightRef = React__namespace.useRef(false);
  const initialPlacedRef = React__namespace.useRef(false);
  const originalPositionerStylesRef = React__namespace.useRef({});
  const scrollArrowFrame = useButton.useAnimationFrame();
  const handleScroll = useButton.useStableCallback(scroller => {
    if (!positionerElement || !popupRef.current || !initialPlacedRef.current) {
      return;
    }
    if (reachedMaxHeightRef.current || !alignItemWithTriggerActive) {
      handleScrollArrowVisibility();
      return;
    }
    const isTopPositioned = positionerElement.style.top === '0px';
    const isBottomPositioned = positionerElement.style.bottom === '0px';
    if (!isTopPositioned && !isBottomPositioned) {
      handleScrollArrowVisibility();
      return;
    }
    const scale = getScale(positionerElement);
    const currentHeight = normalizeSize(positionerElement.getBoundingClientRect().height, 'y', scale);
    const doc = useButton.ownerDocument(positionerElement);
    const win = floatingUi_utils_dom.getWindow(positionerElement);
    const positionerStyles = win.getComputedStyle(positionerElement);
    const marginTop = parseFloat(positionerStyles.marginTop);
    const marginBottom = parseFloat(positionerStyles.marginBottom);
    const maxPopupHeight = getMaxPopupHeight(win.getComputedStyle(popupRef.current));
    const maxAvailableHeight = Math.min(doc.documentElement.clientHeight - marginTop - marginBottom, maxPopupHeight);
    const scrollTop = scroller.scrollTop;
    const maxScrollTop = getMaxScrollTop(scroller);
    let nextPositionerHeight = 0;
    let nextScrollTop = null;
    let setReachedMax = false;
    let scrollToMax = false;
    const setHeight = height => {
      positionerElement.style.height = `${height}px`;
    };
    const handleSmallDiff = (diff, targetScrollTop) => {
      const heightDelta = clamp.clamp(diff, 0, maxAvailableHeight - currentHeight);
      if (heightDelta > 0) {
        // Consume the remaining scroll in height.
        setHeight(currentHeight + heightDelta);
      }
      scroller.scrollTop = targetScrollTop;
      if (maxAvailableHeight - (currentHeight + heightDelta) <= scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
        reachedMaxHeightRef.current = true;
      }
      handleScrollArrowVisibility();
    };
    const diff = isTopPositioned ? maxScrollTop - scrollTop : scrollTop;
    const nextHeight = Math.min(currentHeight + diff, maxAvailableHeight);
    nextPositionerHeight = nextHeight;
    if (diff <= scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
      handleSmallDiff(diff, isTopPositioned ? maxScrollTop : 0);
      return;
    }
    if (maxAvailableHeight - nextHeight > scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
      if (isTopPositioned) {
        scrollToMax = true;
      } else {
        nextScrollTop = 0;
      }
    } else {
      setReachedMax = true;
      if (isBottomPositioned && scrollTop < maxScrollTop) {
        const overshoot = currentHeight + diff - maxAvailableHeight;
        nextScrollTop = scrollTop - (diff - overshoot);
      }
    }
    nextPositionerHeight = Math.ceil(nextPositionerHeight);
    if (nextPositionerHeight !== 0) {
      setHeight(nextPositionerHeight);
    }
    if (scrollToMax || nextScrollTop != null) {
      // Recompute bounds after resizing (clientHeight likely changed).
      const nextMaxScrollTop = getMaxScrollTop(scroller);
      const target = scrollToMax ? nextMaxScrollTop : clamp.clamp(nextScrollTop, 0, nextMaxScrollTop);

      // Avoid adjustments that re-trigger scroll events forever.
      if (Math.abs(scroller.scrollTop - target) > scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
        scroller.scrollTop = target;
      }
    }
    if (setReachedMax || nextPositionerHeight >= maxAvailableHeight - scrollEdges.SCROLL_EDGE_TOLERANCE_PX) {
      reachedMaxHeightRef.current = true;
    }
    handleScrollArrowVisibility();
  });
  React__namespace.useImperativeHandle(scrollHandlerRef, () => handleScroll, [handleScroll]);
  useButton.useOpenChangeComplete({
    open,
    ref: popupRef,
    onComplete() {
      if (open) {
        onOpenChangeComplete?.(true);
      }
    }
  });
  const state = {
    open,
    transitionStatus,
    side,
    align
  };
  useButton.useIsoLayoutEffect(() => {
    if (!positionerElement || !popupRef.current || Object.keys(originalPositionerStylesRef.current).length) {
      return;
    }
    originalPositionerStylesRef.current = {
      top: positionerElement.style.top || '0',
      left: positionerElement.style.left || '0',
      right: positionerElement.style.right,
      height: positionerElement.style.height,
      bottom: positionerElement.style.bottom,
      minHeight: positionerElement.style.minHeight,
      maxHeight: positionerElement.style.maxHeight,
      marginTop: positionerElement.style.marginTop,
      marginBottom: positionerElement.style.marginBottom
    };
  }, [popupRef, positionerElement]);
  useButton.useIsoLayoutEffect(() => {
    if (open || alignItemWithTriggerActive) {
      return;
    }
    initialPlacedRef.current = false;
    reachedMaxHeightRef.current = false;
    clearStyles(positionerElement, originalPositionerStylesRef.current);
  }, [open, alignItemWithTriggerActive, positionerElement, popupRef]);
  useButton.useIsoLayoutEffect(() => {
    const popupElement = popupRef.current;

    // Wait for Floating UI's first positioning pass before reading DOM geometry.
    // We replace the final coordinates for aligned selects, but still need middleware
    // like `size()` to set CSS variables such as `--anchor-width`.
    if (!open || !triggerElement || !positionerElement || !popupElement || alignItemWithTriggerActive && !isPositioned || store.state.transitionStatus === 'ending') {
      return;
    }
    if (!alignItemWithTriggerActive) {
      initialPlacedRef.current = true;
      scrollArrowFrame.request(handleScrollArrowVisibility);
      popupElement.style.removeProperty('--transform-origin');
      return;
    }

    // Ensure we remove any transforms that can affect the location of the popup
    // and therefore the calculations.
    const restoreTransformStyles = unsetTransformStyles(popupElement);
    popupElement.style.removeProperty('--transform-origin');
    try {
      let textElement = selectedItemTextRef.current;
      if (!textElement?.isConnected) {
        const hasSelectedValue = selectors.hasSelectedValue(store.state);
        textElement = !hasSelectedValue && firstItemTextRef.current?.isConnected ? firstItemTextRef.current : null;
      }
      const valueElement = valueRef.current;
      const win = floatingUi_utils_dom.getWindow(positionerElement);
      const positionerStyles = win.getComputedStyle(positionerElement);
      const popupStyles = win.getComputedStyle(popupElement);
      const doc = useButton.ownerDocument(triggerElement);
      const scale = getScale(triggerElement);
      const triggerRect = normalizeRect(triggerElement.getBoundingClientRect(), scale);
      const positionerRect = normalizeRect(positionerElement.getBoundingClientRect(), scale);
      const triggerHeight = triggerRect.height;
      const scroller = listElement || popupElement;
      const scrollHeight = scroller.scrollHeight;
      const borderBottom = parseFloat(popupStyles.borderBottomWidth);
      // The `|| N` fallbacks cover an unset/`auto` value (parses to `NaN`). Note a literal `0`
      // also resolves to the fallback, so an explicit `margin: 0` or `min-height: 0` still takes
      // the default below.
      const marginTop = parseFloat(positionerStyles.marginTop) || 10;
      const marginBottom = parseFloat(positionerStyles.marginBottom) || 10;
      const minHeight = parseFloat(positionerStyles.minHeight) || 100;
      const maxPopupHeight = getMaxPopupHeight(popupStyles);
      const paddingLeft = 5;
      const paddingRight = 5;
      const triggerCollisionThreshold = 20;
      const viewportHeight = doc.documentElement.clientHeight - marginTop - marginBottom;
      const viewportWidth = doc.documentElement.clientWidth;
      const availableSpaceBeneathTrigger = viewportHeight - triggerRect.bottom + triggerHeight;
      let textRect;
      let alignedLeft = direction === 'rtl' ? triggerRect.right - positionerRect.width : triggerRect.left;
      let offsetY = 0;
      if (textElement && valueElement) {
        const valueRect = normalizeRect(valueElement.getBoundingClientRect(), scale);
        textRect = normalizeRect(textElement.getBoundingClientRect(), scale);
        alignedLeft = positionerRect.left + (direction === 'rtl' ? valueRect.right - textRect.right : valueRect.left - textRect.left);
        const valueCenterFromTriggerTop = valueRect.top - triggerRect.top + valueRect.height / 2;
        const textCenterFromPositionerTop = textRect.top - positionerRect.top + textRect.height / 2;
        offsetY = textCenterFromPositionerTop - valueCenterFromTriggerTop;
      }
      const idealHeight = availableSpaceBeneathTrigger + offsetY + marginBottom + borderBottom;
      let height = Math.min(viewportHeight, idealHeight);
      const maxHeight = viewportHeight - marginTop - marginBottom;
      const scrollTop = idealHeight - height;
      const maxRight = viewportWidth - paddingRight;
      positionerElement.style.left = `${clamp.clamp(alignedLeft, paddingLeft, maxRight - positionerRect.width)}px`;
      positionerElement.style.height = `${height}px`;
      // `none` (not the invalid `auto`) so the explicit height governs in align mode and isn't
      // clamped by a `max-height` from user CSS.
      positionerElement.style.maxHeight = 'none';
      positionerElement.style.marginTop = `${marginTop}px`;
      positionerElement.style.marginBottom = `${marginBottom}px`;
      popupElement.style.height = '100%';
      const maxScrollTop = getMaxScrollTop(scroller);
      const isTopPositioned = scrollTop >= maxScrollTop - scrollEdges.SCROLL_EDGE_TOLERANCE_PX;
      if (isTopPositioned) {
        height = Math.min(viewportHeight, positionerRect.height) - (scrollTop - maxScrollTop);
      }

      // When the trigger is too close to the top or bottom of the viewport, or the minHeight is
      // reached, we fallback to aligning the popup to the trigger as the UX is poor otherwise.
      const fallbackToAlignPopupToTrigger = triggerRect.top < triggerCollisionThreshold || triggerRect.bottom > viewportHeight - triggerCollisionThreshold || Math.ceil(height) + scrollEdges.SCROLL_EDGE_TOLERANCE_PX < Math.min(scrollHeight, minHeight);

      // Safari doesn't position the popup correctly when pinch-zoomed.
      const isPinchZoomed = (win.visualViewport?.scale ?? 1) !== 1 && useScrollLock.webkit;
      if (fallbackToAlignPopupToTrigger || isPinchZoomed) {
        initialPlacedRef.current = true;
        clearStyles(positionerElement, originalPositionerStylesRef.current);
        setControlledAlignItemWithTrigger(false);
        return;
      }
      const initialHeight = Math.max(minHeight, height);
      if (isTopPositioned) {
        const topOffset = Math.max(0, viewportHeight - idealHeight);
        positionerElement.style.top = positionerRect.height >= maxHeight ? '0' : `${topOffset}px`;
        positionerElement.style.height = `${height}px`;
        scroller.scrollTop = getMaxScrollTop(scroller);
      } else {
        positionerElement.style.bottom = '0';
        scroller.scrollTop = scrollTop;
      }
      if (textRect) {
        const popupTop = positionerRect.top;
        const popupHeight = positionerRect.height;
        const textCenterY = textRect.top + textRect.height / 2;
        const transformOriginY = popupHeight > 0 ? (textCenterY - popupTop) / popupHeight * 100 : 50;
        const clampedY = clamp.clamp(transformOriginY, 0, 100);
        popupElement.style.setProperty('--transform-origin', `50% ${clampedY}%`);
      }
      if (initialHeight === viewportHeight || height >= maxPopupHeight) {
        reachedMaxHeightRef.current = true;
      }
      handleScrollArrowVisibility();
      if (highlightItemOnHover && store.state.selectedIndex === null && store.state.activeIndex === null && listRef.current[0] != null) {
        store.set('activeIndex', 0);
      }
      initialPlacedRef.current = true;
    } finally {
      restoreTransformStyles();
    }
  }, [store, open, positionerElement, triggerElement, valueRef, firstItemTextRef, selectedItemTextRef, popupRef, handleScrollArrowVisibility, alignItemWithTriggerActive, setControlledAlignItemWithTrigger, scrollArrowFrame, listElement, listRef, highlightItemOnHover, direction, isPositioned]);
  React__namespace.useEffect(() => {
    if (!alignItemWithTriggerActive || !positionerElement || !open) {
      return undefined;
    }
    const win = floatingUi_utils_dom.getWindow(positionerElement);
    function handleResize(event) {
      setOpen(false, useButton.createChangeEventDetails(useButton.windowResize, event));
    }
    return useButton.addEventListener(win, 'resize', handleResize);
  }, [setOpen, alignItemWithTriggerActive, positionerElement, open]);
  const defaultProps = {
    ...(listElement ? {
      role: 'presentation',
      'aria-orientation': undefined
    } : {
      role: 'listbox',
      'aria-multiselectable': multiple || undefined,
      id: `${id}-list`
    }),
    onKeyDown(event) {
      if (insideToolbar && scrollEdges.COMPOSITE_KEYS.has(event.key)) {
        event.stopPropagation();
      }
    },
    onScroll(event) {
      if (listElement) {
        return;
      }
      handleScroll(event.currentTarget);
    },
    ...(alignItemWithTriggerActive && {
      style: listElement ? {
        height: '100%'
      } : LIST_FUNCTIONAL_STYLES
    })
  };
  const element = useRenderElement.useRenderElement('div', componentProps, {
    ref: [forwardedRef, popupRef],
    state,
    stateAttributesMapping,
    props: [popupProps, defaultProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), {
      className: !listElement && alignItemWithTriggerActive ? scrollEdges.styleDisableScrollbar.className : undefined
    }, elementProps]
  });
  return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
    children: [!disableStyleElements && scrollEdges.styleDisableScrollbar.getElement(nonce), /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingFocusManager, {
      context: floatingRootContext,
      modal: false,
      disabled: !mounted,
      openInteractionType: openMethod,
      returnFocus: finalFocus,
      restoreFocus: true,
      children: element
    })]
  });
});
if (process.env.NODE_ENV !== "production") SelectPopup.displayName = "SelectPopup";
function getMaxPopupHeight(popupStyles) {
  const maxHeightStyle = popupStyles.maxHeight || '';
  return maxHeightStyle.endsWith('px') ? parseFloat(maxHeightStyle) || Infinity : Infinity;
}
function getMaxScrollTop(scroller) {
  return scrollEdges.getMaxScrollOffset(scroller.scrollHeight, scroller.clientHeight);
}
function getScale(element) {
  // The platform API is async-capable, but the DOM platform returns a plain scale object.
  return floatingUi_reactDom.platform.getScale(element);
}
function normalizeSize(size, axis, scale) {
  return size / scale[axis];
}
function normalizeRect(rect, scale) {
  return floatingUi_utils.rectToClientRect({
    x: normalizeSize(rect.x, 'x', scale),
    y: normalizeSize(rect.y, 'y', scale),
    width: normalizeSize(rect.width, 'x', scale),
    height: normalizeSize(rect.height, 'y', scale)
  });
}
const TRANSFORM_STYLE_RESETS = [['transform', 'none'], ['scale', '1'], ['translate', '0 0']];
function unsetTransformStyles(popupElement) {
  const {
    style
  } = popupElement;
  const originalStyles = {};
  for (const [property, value] of TRANSFORM_STYLE_RESETS) {
    originalStyles[property] = style.getPropertyValue(property);
    style.setProperty(property, value, 'important');
  }
  return () => {
    for (const [property] of TRANSFORM_STYLE_RESETS) {
      const originalValue = originalStyles[property];
      if (originalValue) {
        style.setProperty(property, originalValue);
      } else {
        style.removeProperty(property);
      }
    }
  };
}

/**
 * A container for the select items.
 * Renders a `<div>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectList = /*#__PURE__*/React__namespace.forwardRef(function SelectList(componentProps, forwardedRef) {
  const {
    render,
    className,
    style,
    ...elementProps
  } = componentProps;
  const {
    store,
    scrollHandlerRef
  } = useSelectRootContext();
  const {
    alignItemWithTriggerActive
  } = useSelectPositionerContext();
  const hasScrollArrows = useScrollLock.useStore(store, selectors.hasScrollArrows);
  const openMethod = useScrollLock.useStore(store, selectors.openMethod);
  const multiple = useScrollLock.useStore(store, selectors.multiple);
  const id = useScrollLock.useStore(store, selectors.id);
  const defaultProps = {
    id: `${id}-list`,
    role: 'listbox',
    'aria-multiselectable': multiple || undefined,
    onScroll(event) {
      scrollHandlerRef.current?.(event.currentTarget);
    },
    ...(alignItemWithTriggerActive && {
      style: LIST_FUNCTIONAL_STYLES
    }),
    className: hasScrollArrows && openMethod !== 'touch' ? scrollEdges.styleDisableScrollbar.className : undefined
  };
  const setListElement = useButton.useStableCallback(element => {
    store.set('listElement', element);
  });
  return useRenderElement.useRenderElement('div', componentProps, {
    ref: [forwardedRef, setListElement],
    props: [defaultProps, elementProps]
  });
});
if (process.env.NODE_ENV !== "production") SelectList.displayName = "SelectList";

const SelectItemContext = /*#__PURE__*/React__namespace.createContext(undefined);
if (process.env.NODE_ENV !== "production") SelectItemContext.displayName = "SelectItemContext";
function useSelectItemContext() {
  const context = React__namespace.useContext(SelectItemContext);
  if (!context) {
    throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectItemContext is missing. SelectItem parts must be placed within <Select.Item>.' : useRenderElement.formatErrorMessage(57));
  }
  return context;
}

const SelectItem = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef(function SelectItem(componentProps, forwardedRef) {
  const {
    render,
    className,
    style,
    value: itemValue = null,
    label,
    disabled = false,
    nativeButton = false,
    ...elementProps
  } = componentProps;
  const textRef = React__namespace.useRef(null);
  const listItem = useCompositeListItem.useCompositeListItem({
    label,
    textRef,
    indexGuessBehavior: useCompositeListItem.IndexGuessBehavior.GuessFromOrder
  });
  const {
    store,
    itemProps,
    setOpen,
    setValue,
    selectionRef,
    typingRef,
    valuesRef,
    multiple,
    selectedItemTextRef,
    disabled: selectDisabled,
    readOnly
  } = useSelectRootContext();
  const highlighted = useScrollLock.useStore(store, selectors.isActive, listItem.index);
  const open = useScrollLock.useStore(store, selectors.open);
  const selected = useScrollLock.useStore(store, selectors.isSelected, itemValue);
  const selectedByFocus = useScrollLock.useStore(store, selectors.isSelectedByFocus, listItem.index);
  const isItemEqualToValue = useScrollLock.useStore(store, selectors.isItemEqualToValue);
  const index = listItem.index;
  const hasRegistered = index !== -1;
  const itemRef = React__namespace.useRef(null);
  useButton.useIsoLayoutEffect(() => {
    if (!hasRegistered) {
      return undefined;
    }
    const values = valuesRef.current;
    values[index] = itemValue;
    return () => {
      delete values[index];
    };
  }, [hasRegistered, index, itemValue, valuesRef]);
  useButton.useIsoLayoutEffect(() => {
    if (!hasRegistered) {
      return;
    }
    const selectedValue = store.state.value;
    let selectedCandidate = selectedValue;
    if (multiple && Array.isArray(selectedValue)) {
      // Compare against the last selected item, or `undefined` when nothing is selected — never
      // the raw array, which a custom `isItemEqualToValue` isn't expected to receive.
      selectedCandidate = selectedValue.length > 0 ? selectedValue[selectedValue.length - 1] : undefined;
    }
    if (selectedCandidate !== undefined && resolveAriaLabelledBy.compareItemEquality(itemValue, selectedCandidate, isItemEqualToValue)) {
      store.set('selectedIndex', index);
      // Make sure SelectPopup can measure the selected item on first open.
      // SelectItemText can still update this ref later when focus moves.
      if (textRef.current) {
        selectedItemTextRef.current = textRef.current;
      }
    }
  }, [hasRegistered, index, multiple, isItemEqualToValue, store, itemValue, selectedItemTextRef]);
  const lastKeyRef = React__namespace.useRef(null);
  const pointerTypeRef = React__namespace.useRef('mouse');
  const allowMouseSelectionRef = React__namespace.useRef(false);
  const {
    getButtonProps,
    buttonRef
  } = useButton.useButton({
    disabled,
    focusableWhenDisabled: true,
    native: nativeButton,
    composite: true
  });
  const state = {
    disabled,
    selected,
    highlighted
  };
  function commitSelection(event) {
    // A forced-open select (`open`/`defaultOpen`) can still receive item activations even
    // when the root is disabled or read-only, so guard the commit here too.
    if (selectDisabled || readOnly) {
      return;
    }
    const selectedValue = store.state.value;
    if (multiple) {
      const currentValue = Array.isArray(selectedValue) ? selectedValue : [];
      const nextValue = selected ? resolveAriaLabelledBy.removeItem(currentValue, itemValue, isItemEqualToValue) : [...currentValue, itemValue];
      setValue(nextValue, useButton.createChangeEventDetails(useButton.itemPress, event));
    } else {
      setValue(itemValue, useButton.createChangeEventDetails(useButton.itemPress, event));
      setOpen(false, useButton.createChangeEventDetails(useButton.itemPress, event));
    }
  }
  function resetDragMovement() {
    selectionRef.current.dragY = 0;
  }
  const defaultProps = {
    role: 'option',
    'aria-selected': selected,
    tabIndex: open && highlighted ? 0 : -1,
    onKeyDown(event) {
      lastKeyRef.current = event.key;
      store.set('activeIndex', index);
      if (event.key === ' ' && typingRef.current) {
        event.preventDefault();
      }
    },
    onClick(event) {
      const isMouseClick = event.type === 'click' && pointerTypeRef.current !== 'touch';
      const clickPointerType = event.nativeEvent.pointerType;
      const isVirtualMouseClick = isMouseClick && useButton.isVirtualClick(event.nativeEvent) && (
      // Generic no-pointer `detail === 0` clicks stay tied to highlight state. Virtual
      // clicks that carry browser pointer data, including an empty string from assistive
      // technology, can activate unhighlighted items.
      clickPointerType !== undefined || highlighted);
      // With alignItemWithTrigger, opening can place an item under the cursor. Real mouse
      // clicks must start on the item, while virtual clicks represent explicit keyboard or
      // assistive technology activation.
      const isInvalidMouseClick = isMouseClick && !isVirtualMouseClick && !allowMouseSelectionRef.current;
      allowMouseSelectionRef.current = false;

      // Prevent double commit on {Enter}
      if (event.type === 'keydown' && lastKeyRef.current === null) {
        return;
      }
      if (disabled || event.type === 'keydown' && lastKeyRef.current === ' ' && typingRef.current || isInvalidMouseClick) {
        return;
      }
      lastKeyRef.current = null;
      commitSelection(event.nativeEvent);
    },
    onPointerEnter(event) {
      pointerTypeRef.current = event.pointerType;
    },
    onPointerMove(event) {
      if (event.pointerType === 'mouse' && event.buttons === 1) {
        const selection = selectionRef.current;
        selection.dragY += event.movementY;
        if (selection.dragY ** 2 >= 64) {
          selection.allowUnselectedMouseUp = true;
        }
      }
    },
    onPointerDown(event) {
      pointerTypeRef.current = event.pointerType;
      allowMouseSelectionRef.current = true;
      resetDragMovement();
    },
    onMouseUp() {
      resetDragMovement();
      if (disabled || pointerTypeRef.current === 'touch') {
        return;
      }

      // Regular clicks are committed by the click event.
      if (allowMouseSelectionRef.current) {
        return;
      }
      const disallowSelectedMouseUp = !selectionRef.current.allowSelectedMouseUp && selected;
      const disallowUnselectedMouseUp = !selectionRef.current.allowUnselectedMouseUp && !selected;
      if (disallowSelectedMouseUp || disallowUnselectedMouseUp) {
        return;
      }
      allowMouseSelectionRef.current = true;
      itemRef.current?.click();
      allowMouseSelectionRef.current = false;
    }
  };
  const element = useRenderElement.useRenderElement('div', componentProps, {
    ref: [buttonRef, forwardedRef, listItem.ref, itemRef],
    state,
    props: [itemProps, defaultProps, elementProps, getButtonProps]
  });
  const contextValue = React__namespace.useMemo(() => ({
    selected,
    index,
    textRef,
    selectedByFocus,
    hasRegistered
  }), [selected, index, textRef, selectedByFocus, hasRegistered]);
  return /*#__PURE__*/jsxRuntime.jsx(SelectItemContext.Provider, {
    value: contextValue,
    children: element
  });
}));
if (process.env.NODE_ENV !== "production") SelectItem.displayName = "SelectItem";

const SelectItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function SelectItemIndicator(componentProps, forwardedRef) {
  const keepMounted = componentProps.keepMounted ?? false;
  const {
    selected
  } = useSelectItemContext();
  const shouldRender = keepMounted || selected;
  if (!shouldRender) {
    return null;
  }

  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  return /*#__PURE__*/jsxRuntime.jsx(Inner, {
    ...componentProps,
    ref: forwardedRef
  });
});

// Split the core implementation to avoid paying the hook costs unless the element needs to mount.
if (process.env.NODE_ENV !== "production") SelectItemIndicator.displayName = "SelectItemIndicator";
const Inner = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef((componentProps, forwardedRef) => {
  const {
    render,
    className,
    style,
    keepMounted,
    ...elementProps
  } = componentProps;
  const {
    selected
  } = useSelectItemContext();
  const indicatorRef = React__namespace.useRef(null);
  const {
    transitionStatus,
    setMounted
  } = useButton.useTransitionStatus(selected);
  const state = {
    selected,
    transitionStatus
  };
  const element = useRenderElement.useRenderElement('span', componentProps, {
    ref: [forwardedRef, indicatorRef],
    state,
    props: [{
      'aria-hidden': true,
      children: '✔️'
    }, elementProps],
    stateAttributesMapping: useButton.transitionStatusMapping
  });
  useButton.useOpenChangeComplete({
    open: selected,
    ref: indicatorRef,
    onComplete() {
      if (!selected) {
        setMounted(false);
      }
    }
  });
  return element;
}));
if (process.env.NODE_ENV !== "production") Inner.displayName = "Inner";

/**
 * A text label of the select item.
 * Renders a `<div>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectItemText = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__namespace.forwardRef(function SelectItemText(componentProps, forwardedRef) {
  const {
    index,
    textRef,
    selectedByFocus,
    hasRegistered
  } = useSelectItemContext();
  const {
    firstItemTextRef,
    selectedItemTextRef
  } = useSelectRootContext();
  const {
    render,
    className,
    style,
    ...elementProps
  } = componentProps;
  const localRef = React__namespace.useCallback(node => {
    if (!node) {
      return;
    }
    if (hasRegistered && index === 0) {
      firstItemTextRef.current = node;
    }
    if (hasRegistered && selectedByFocus) {
      selectedItemTextRef.current = node;
    }
  }, [firstItemTextRef, selectedItemTextRef, index, selectedByFocus, hasRegistered]);
  const element = useRenderElement.useRenderElement('div', componentProps, {
    ref: [localRef, forwardedRef, textRef],
    props: elementProps
  });
  return element;
}));
if (process.env.NODE_ENV !== "production") SelectItemText.displayName = "SelectItemText";

const SelectGroupContext = /*#__PURE__*/React__namespace.createContext(undefined);
if (process.env.NODE_ENV !== "production") SelectGroupContext.displayName = "SelectGroupContext";
function useSelectGroupContext() {
  const context = React__namespace.useContext(SelectGroupContext);
  if (context === undefined) {
    throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectGroupContext is missing. SelectGroup parts must be placed within <Select.Group>.' : useRenderElement.formatErrorMessage(56));
  }
  return context;
}

const SelectGroup = /*#__PURE__*/React__namespace.forwardRef(function SelectGroup(componentProps, forwardedRef) {
  const {
    render,
    className,
    style,
    ...elementProps
  } = componentProps;
  const [labelId, setLabelId] = React__namespace.useState();
  const contextValue = React__namespace.useMemo(() => ({
    labelId,
    setLabelId
  }), [labelId, setLabelId]);
  const element = useRenderElement.useRenderElement('div', componentProps, {
    ref: forwardedRef,
    props: [{
      role: 'group',
      'aria-labelledby': labelId
    }, elementProps]
  });
  return /*#__PURE__*/jsxRuntime.jsx(SelectGroupContext.Provider, {
    value: contextValue,
    children: element
  });
});
if (process.env.NODE_ENV !== "production") SelectGroup.displayName = "SelectGroup";

/**
 * An accessible label that is automatically associated with its parent group.
 * Renders a `<div>` element.
 *
 * Documentation: [Base UI Select](https://base-ui.com/react/components/select)
 */
const SelectGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function SelectGroupLabel(componentProps, forwardedRef) {
  const {
    render,
    className,
    style,
    id: idProp,
    ...elementProps
  } = componentProps;
  const {
    setLabelId
  } = useSelectGroupContext();
  const id = useButton.useBaseUiId(idProp);
  useButton.useIsoLayoutEffect(() => {
    setLabelId(id);
  }, [id, setLabelId]);
  const element = useRenderElement.useRenderElement('div', componentProps, {
    ref: forwardedRef,
    props: [{
      id
    }, elementProps]
  });
  return element;
});
if (process.env.NODE_ENV !== "production") SelectGroupLabel.displayName = "SelectGroupLabel";

exports.SelectGroup = SelectGroup;
exports.SelectGroupLabel = SelectGroupLabel;
exports.SelectItem = SelectItem;
exports.SelectItemIndicator = SelectItemIndicator;
exports.SelectItemText = SelectItemText;
exports.SelectList = SelectList;
exports.SelectPopup = SelectPopup;
exports.SelectPortal = SelectPortal;
exports.SelectPositioner = SelectPositioner;
exports.SelectRoot = SelectRoot;
exports.SelectTrigger = SelectTrigger;
exports.SelectValue = SelectValue;
exports.selectors = selectors;
exports.usePreviousValue = usePreviousValue;
exports.useSelectPositionerContext = useSelectPositionerContext;
exports.useSelectRootContext = useSelectRootContext;
