UNPKG

@1771technologies/lytenyte-pro

Version:

1,474 lines 76.3 kB
import { jsx, jsxs } from "react/jsx-runtime"; import { clsx } from "@1771technologies/js-utils"; import { A as ArrowDownIcon } from "./arrow-down-icon-B-3ldODD.js"; import { c as CheckMark } from "./drag-store-BF4ad65L.js"; import "@1771technologies/react-sizer"; import "@1771technologies/react-utils"; import "@1771technologies/grid-provider"; import * as React from "react"; import "@1771technologies/react-dragon"; import "@1771technologies/grid-core"; import { j as useEventCallback, m as mergeProps, d as useEnhancedEffect, n as useOpenChangeComplete, p as useFloatingRootContext, v as useClick, w as useDismiss, x as useRole, y as useListNavigation, z as useTypeahead, A as useInteractions, P as PropTypes, E as contains, F as FloatingPortal, H as HTMLElementType, r as refType, D as FloatingFocusManager, u as useLatestRef } from "./proptypes-BjYr2nFr.js"; import { b as useBaseUiId, u as useForkRef, c as pressableTriggerOpenStateMapping, a as useComponentRenderer, p as popupStateMapping, I as InternalBackdrop, t as transitionStatusMapping } from "./InternalBackdrop-C4RACVzs.js"; import { u as useControlled, a as useTransitionStatus, o as ownerDocument, b as useScrollLock, d as ownerWindow, i as isWebKit } from "./useScrollLock-D4UY33Sb.js"; import { w as warn, u as useButton } from "./useButton-DWXzFgcr.js"; import { g as getPseudoElementBounds } from "./getPseudoElementBounds-BSHt6WYm.js"; import { C as CompositeList, u as useCompositeListItem } from "./CompositeList-CGRaS6LQ.js"; import { u as useAnchorPositioning } from "./useAnchorPositioning-52zK7jlD.js"; let FieldControlDataAttributes = /* @__PURE__ */ function(FieldControlDataAttributes2) { FieldControlDataAttributes2["disabled"] = "data-disabled"; FieldControlDataAttributes2["valid"] = "data-valid"; FieldControlDataAttributes2["invalid"] = "data-invalid"; FieldControlDataAttributes2["touched"] = "data-touched"; FieldControlDataAttributes2["dirty"] = "data-dirty"; FieldControlDataAttributes2["filled"] = "data-filled"; FieldControlDataAttributes2["focused"] = "data-focused"; return FieldControlDataAttributes2; }({}); const DEFAULT_VALIDITY_STATE = { badInput: false, customError: false, patternMismatch: false, rangeOverflow: false, rangeUnderflow: false, stepMismatch: false, tooLong: false, tooShort: false, typeMismatch: false, valid: null, valueMissing: false }; const fieldValidityMapping = { valid(value) { if (value === null) { return null; } if (value) { return { [FieldControlDataAttributes.valid]: "" }; } return { [FieldControlDataAttributes.invalid]: "" }; } }; const NOOP = () => { }; const FieldRootContext = /* @__PURE__ */ React.createContext({ invalid: void 0, controlId: void 0, setControlId: NOOP, labelId: void 0, setLabelId: NOOP, messageIds: [], setMessageIds: NOOP, name: void 0, validityData: { state: DEFAULT_VALIDITY_STATE, errors: [], error: "", value: "", initialValue: null }, setValidityData: NOOP, disabled: void 0, touched: false, setTouched: NOOP, dirty: false, setDirty: NOOP, filled: false, setFilled: NOOP, focused: false, setFocused: NOOP, validate: () => null, validationMode: "onBlur", validationDebounceTime: 0, state: { disabled: false, valid: null, touched: false, dirty: false, filled: false, focused: false }, markedDirtyRef: { current: false } }); if (process.env.NODE_ENV !== "production") { FieldRootContext.displayName = "FieldRootContext"; } function useFieldRootContext(optional = true) { const context = React.useContext(FieldRootContext); if (context.setControlId === NOOP && !optional) { throw new Error("Base UI: FieldRootContext is missing. Field parts must be placed within <Field.Root>."); } return context; } const FormContext = /* @__PURE__ */ React.createContext({ formRef: { current: { fields: /* @__PURE__ */ new Map() } }, errors: {}, onClearErrors: () => { } }); if (process.env.NODE_ENV !== "production") { FormContext.displayName = "FormContext"; } function useFormContext() { return React.useContext(FormContext); } function getCombinedFieldValidityData(validityData, invalid) { return { ...validityData, state: { ...validityData.state, valid: !invalid && validityData.state.valid } }; } const validityKeys = Object.keys(DEFAULT_VALIDITY_STATE); function useFieldControlValidation() { const { setValidityData, validate, messageIds, validityData, validationMode, validationDebounceTime, invalid, markedDirtyRef, controlId, state } = useFieldRootContext(); const { formRef } = useFormContext(); const timeoutRef = React.useRef(-1); const inputRef = React.useRef(null); React.useEffect(() => { return () => { window.clearTimeout(timeoutRef.current); }; }, []); const commitValidation = useEventCallback(async (value) => { const element = inputRef.current; if (!element) { return; } function getState(el) { return validityKeys.reduce((acc, key) => { acc[key] = el.validity[key]; if (!el.validity.customError && !markedDirtyRef.current) { acc[key] = key === "valid"; } return acc; }, {}); } window.clearTimeout(timeoutRef.current); const resultOrPromise = validate(value); let result = null; if (typeof resultOrPromise === "object" && resultOrPromise !== null && "then" in resultOrPromise) { result = await resultOrPromise; } else { result = resultOrPromise; } let errorMessage = ""; if (result !== null) { errorMessage = Array.isArray(result) ? result.join("\n") : result; } element.setCustomValidity(errorMessage); const nextState = getState(element); let validationErrors = []; if (Array.isArray(result)) { validationErrors = result; } else if (result) { validationErrors = [result]; } else if (element.validationMessage) { validationErrors = [element.validationMessage]; } const nextValidityData = { value, state: nextState, error: Array.isArray(result) ? result[0] : result ?? element.validationMessage, errors: validationErrors, initialValue: validityData.initialValue }; if (controlId) { const currentFieldData = formRef.current.fields.get(controlId); if (currentFieldData) { formRef.current.fields.set(controlId, { ...currentFieldData, ...getCombinedFieldValidityData(nextValidityData, invalid) }); } } setValidityData(nextValidityData); }); const getValidationProps = React.useCallback((externalProps = {}) => mergeProps({ ...messageIds.length && { "aria-describedby": messageIds.join(" ") }, ...state.valid === false && { "aria-invalid": true } }, externalProps), [messageIds, state.valid]); const getInputValidationProps = React.useCallback((externalProps = {}) => mergeProps({ onChange(event) { if (event.nativeEvent.defaultPrevented) { return; } if (invalid || validationMode !== "onChange") { return; } const element = event.currentTarget; if (element.value === "") { commitValidation(element.value); return; } window.clearTimeout(timeoutRef.current); if (validationDebounceTime) { timeoutRef.current = window.setTimeout(() => { commitValidation(element.value); }, validationDebounceTime); } else { commitValidation(element.value); } } }, getValidationProps(externalProps)), [getValidationProps, invalid, validationMode, validationDebounceTime, commitValidation]); return React.useMemo(() => ({ getValidationProps, getInputValidationProps, inputRef, commitValidation }), [getValidationProps, getInputValidationProps, commitValidation]); } const visuallyHidden = { clip: "rect(0 0 0 0)", overflow: "hidden", whiteSpace: "nowrap", position: "fixed", top: 0, left: 0, border: 0, padding: 0, width: 1, height: 1, margin: -1 }; const EMPTY_ARRAY = []; function useSelectRoot(params) { const { id: idProp, disabled = false, readOnly = false, required = false, alignItemToTrigger: alignItemToTriggerParam = true, modal = false, onOpenChangeComplete } = params; const { setDirty, validityData, validationMode, setControlId, setFilled } = useFieldRootContext(); const fieldControlValidation = useFieldControlValidation(); const id = useBaseUiId(idProp); useEnhancedEffect(() => { setControlId(id); return () => { setControlId(void 0); }; }, [id, setControlId]); const [value, setValueUnwrapped] = useControlled({ controlled: params.value, default: params.defaultValue, name: "Select", state: "value" }); const [open, setOpenUnwrapped] = useControlled({ controlled: params.open, default: params.defaultOpen, name: "Select", state: "open" }); useEnhancedEffect(() => { setFilled(value !== null); }, [setFilled, value]); const [controlledAlignItemToTrigger, setControlledAlignItemToTrigger] = React.useState(alignItemToTriggerParam); const listRef = React.useRef([]); const labelsRef = React.useRef([]); const popupRef = React.useRef(null); const valueRef = React.useRef(null); const valuesRef = React.useRef([]); const typingRef = React.useRef(false); const keyboardActiveRef = React.useRef(false); const selectedItemTextRef = React.useRef(null); const selectionRef = React.useRef({ allowSelectedMouseUp: false, allowUnselectedMouseUp: false, allowSelect: false }); const [triggerElement, setTriggerElement] = React.useState(null); const [positionerElement, setPositionerElement] = React.useState(null); const [activeIndex, setActiveIndex] = React.useState(null); const [selectedIndex, setSelectedIndex] = React.useState(null); const [label, setLabel] = React.useState(""); const [touchModality, setTouchModality] = React.useState(false); const [scrollUpArrowVisible, setScrollUpArrowVisible] = React.useState(false); const [scrollDownArrowVisible, setScrollDownArrowVisible] = React.useState(false); const { mounted, setMounted, transitionStatus } = useTransitionStatus(open); const alignItemToTrigger = Boolean(mounted && controlledAlignItemToTrigger && !touchModality); if (!mounted && controlledAlignItemToTrigger !== alignItemToTriggerParam) { setControlledAlignItemToTrigger(alignItemToTriggerParam); } if (!alignItemToTriggerParam || !mounted) { if (scrollUpArrowVisible) { setScrollUpArrowVisible(false); } if (scrollDownArrowVisible) { setScrollDownArrowVisible(false); } } const setOpen = useEventCallback((nextOpen, event) => { params.onOpenChange?.(nextOpen, event); setOpenUnwrapped(nextOpen); if (!nextOpen && activeIndex !== null) { const activeOption = listRef.current[activeIndex]; queueMicrotask(() => { activeOption?.setAttribute("tabindex", "-1"); }); } }); const handleUnmount = useEventCallback(() => { setMounted(false); setActiveIndex(null); onOpenChangeComplete?.(false); }); useOpenChangeComplete({ enabled: !params.actionsRef, open, ref: popupRef, onComplete() { if (!open) { handleUnmount(); } } }); React.useImperativeHandle(params.actionsRef, () => ({ unmount: handleUnmount }), [handleUnmount]); const setValue = useEventCallback((nextValue, event) => { params.onValueChange?.(nextValue, event); setValueUnwrapped(nextValue); setDirty(nextValue !== validityData.initialValue); if (validationMode === "onChange") { fieldControlValidation.commitValidation(nextValue); } const index = valuesRef.current.indexOf(nextValue); setSelectedIndex(index); setLabel(labelsRef.current[index] ?? ""); }); const hasRegisteredRef = React.useRef(false); const registerSelectedItem = useEventCallback((suppliedIndex) => { if (suppliedIndex !== void 0) { hasRegisteredRef.current = true; } const stringValue = typeof value === "string" || value === null ? value : JSON.stringify(value); const index = suppliedIndex ?? valuesRef.current.indexOf(stringValue); const hasIndex = index !== -1; if (hasIndex || value === null) { if (hasIndex) { setSelectedIndex(index); } setLabel(hasIndex ? labelsRef.current[index] ?? "" : ""); } else if (value) { warn(`The value \`${stringValue}\` is not present in the select items.`); } }); useEnhancedEffect(() => { if (!hasRegisteredRef.current) { return; } registerSelectedItem(void 0); }, [value, registerSelectedItem]); const floatingRootContext = useFloatingRootContext({ open, onOpenChange: setOpen, elements: { reference: triggerElement, floating: positionerElement } }); const click = useClick(floatingRootContext, { enabled: !readOnly && !disabled, event: "mousedown" }); const dismiss = useDismiss(floatingRootContext, { bubbles: false, outsidePressEvent: "mousedown" }); const role = useRole(floatingRootContext, { role: "select" }); const listNavigation = useListNavigation(floatingRootContext, { enabled: !readOnly && !disabled, listRef, activeIndex, selectedIndex, disabledIndices: EMPTY_ARRAY, onNavigate(nextActiveIndex) { if (nextActiveIndex === null && !open) { return; } setActiveIndex(nextActiveIndex); }, // Implement our own listeners since `onPointerLeave` on each option fires while scrolling with // the `alignItemToTrigger` prop enabled, causing a performance issue on Chrome. focusItemOnHover: false }); const typeahead = useTypeahead(floatingRootContext, { enabled: !readOnly && !disabled, listRef: labelsRef, activeIndex, selectedIndex, onMatch(index) { if (open) { setActiveIndex(index); } else { setValue(valuesRef.current[index]); } }, onTypingChange(typing) { typingRef.current = typing; } }); const { getReferenceProps: getRootTriggerProps, getFloatingProps: getRootPositionerProps, getItemProps } = useInteractions([click, dismiss, role, listNavigation, typeahead]); const rootContext = React.useMemo(() => ({ id, name: params.name, required, disabled, readOnly, triggerElement, setTriggerElement, positionerElement, setPositionerElement, scrollUpArrowVisible, setScrollUpArrowVisible, scrollDownArrowVisible, setScrollDownArrowVisible, setControlledAlignItemToTrigger, value, setValue, open, setOpen, mounted, setMounted, label, setLabel, valueRef, valuesRef, labelsRef, typingRef, selectionRef, getRootPositionerProps, getRootTriggerProps, getItemProps, listRef, popupRef, selectedItemTextRef, floatingRootContext, touchModality, setTouchModality, alignItemToTrigger, transitionStatus, fieldControlValidation, modal, registerSelectedItem, onOpenChangeComplete, keyboardActiveRef }), [id, params.name, required, disabled, readOnly, triggerElement, positionerElement, scrollUpArrowVisible, scrollDownArrowVisible, value, setValue, open, setOpen, mounted, setMounted, label, getRootPositionerProps, getRootTriggerProps, getItemProps, floatingRootContext, touchModality, alignItemToTrigger, transitionStatus, fieldControlValidation, modal, registerSelectedItem, onOpenChangeComplete, keyboardActiveRef]); const indexContext = React.useMemo(() => ({ activeIndex, setActiveIndex, selectedIndex, setSelectedIndex }), [activeIndex, selectedIndex, setActiveIndex]); return React.useMemo(() => ({ rootContext, indexContext }), [rootContext, indexContext]); } const SelectRootContext = /* @__PURE__ */ React.createContext(null); function useSelectRootContext() { const context = React.useContext(SelectRootContext); if (context === null) { throw new Error("useSelectRootContext must be used within a SelectRoot"); } return context; } const SelectIndexContext = /* @__PURE__ */ React.createContext(void 0); function useSelectIndexContext() { const context = React.useContext(SelectIndexContext); if (context === void 0) { throw new Error("Base UI: SelectIndexContext is missing. Select parts must be placed within <Select.Root>."); } return context; } const SelectRoot = function SelectRoot2(props) { const { value: valueProp, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, alignItemToTrigger = true, name, disabled = false, readOnly = false, required = false, modal = true, actionsRef, onOpenChangeComplete } = props; const selectRoot = useSelectRoot({ value: valueProp, defaultValue, onValueChange, open, defaultOpen, onOpenChange, alignItemToTrigger, name, disabled, readOnly, required, modal, actionsRef, onOpenChangeComplete }); const { setDirty, validityData, validationMode } = useFieldRootContext(); const { rootContext } = selectRoot; const value = rootContext.value; const serializedValue = React.useMemo(() => { if (value == null) { return ""; } if (typeof value === "string") { return value; } return JSON.stringify(value); }, [value]); return /* @__PURE__ */ jsx(SelectRootContext.Provider, { value: selectRoot.rootContext, children: /* @__PURE__ */ jsxs(SelectIndexContext.Provider, { value: selectRoot.indexContext, children: [props.children, /* @__PURE__ */ jsx("input", { ...rootContext.fieldControlValidation.getInputValidationProps({ onFocus() { rootContext.triggerElement?.focus(); }, // Handle browser autofill. onChange(event) { if (event.nativeEvent.defaultPrevented) { return; } const nextValue = event.target.value; const exactValue = rootContext.valuesRef.current.find((v) => v === nextValue || typeof value === "string" && nextValue.toLowerCase() === v.toLowerCase()); if (exactValue != null) { setDirty(exactValue !== validityData.initialValue); rootContext.setValue?.(exactValue, event.nativeEvent); if (validationMode === "onChange") { selectRoot.rootContext.fieldControlValidation.commitValidation(exactValue); } } }, id: rootContext.id, name: rootContext.name, disabled: rootContext.disabled, required: rootContext.required, readOnly: rootContext.readOnly, value: serializedValue, ref: rootContext.fieldControlValidation.inputRef, style: visuallyHidden, tabIndex: -1, "aria-hidden": true }) })] }) }); }; process.env.NODE_ENV !== "production" ? SelectRoot.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * A ref to imperative actions. */ actionsRef: PropTypes.shape({ current: PropTypes.shape({ unmount: PropTypes.func.isRequired }).isRequired }), /** * Determines if the selected item inside the popup should align to the trigger element. * @default true */ alignItemToTrigger: PropTypes.bool, /** * @ignore */ children: PropTypes.node, /** * Whether the select menu is initially open. * * To render a controlled select menu, use the `open` prop instead. * @default false */ defaultOpen: PropTypes.bool, /** * The uncontrolled value of the select when it’s initially rendered. * * To render a controlled select, use the `value` prop instead. * @default null */ defaultValue: PropTypes.any, /** * Whether the component should ignore user interaction. * @default false */ disabled: PropTypes.bool, /** * Whether the select should prevent outside clicks and lock page scroll when open. * @default true */ modal: PropTypes.bool, /** * Identifies the field when a form is submitted. */ name: PropTypes.string, /** * Event handler called when the select menu is opened or closed. */ onOpenChange: PropTypes.func, /** * Event handler called after any animations complete when the select menu is opened or closed. */ onOpenChangeComplete: PropTypes.func, /** * Callback fired when the value of the select changes. Use when controlled. */ onValueChange: PropTypes.func, /** * Whether the select menu is currently open. */ open: PropTypes.bool, /** * Whether the user should be unable to choose a different option from the select menu. * @default false */ readOnly: PropTypes.bool, /** * Whether the user must choose a value before submitting a form. * @default false */ required: PropTypes.bool, /** * The value of the select. */ value: PropTypes.any } : void 0; function useSelectTrigger(parameters) { const BOUNDARY_OFFSET = 2; const { disabled = false, rootRef: externalRef } = parameters; const { open, setOpen, setTriggerElement, selectionRef, value, fieldControlValidation, setTouchModality, positionerElement, alignItemToTrigger, readOnly } = useSelectRootContext(); const { labelId, setTouched, setFocused, validationMode } = useFieldRootContext(); const triggerRef = React.useRef(null); const timeoutRef = React.useRef(-1); const mergedRef = useForkRef(externalRef, triggerRef); const { getButtonProps, buttonRef } = useButton({ disabled, buttonRef: mergedRef }); const handleRef = useForkRef(buttonRef, setTriggerElement); React.useEffect(() => { if (open) { const timeoutId1 = window.setTimeout(() => { selectionRef.current.allowSelectedMouseUp = true; }, 400); const timeoutId2 = window.setTimeout(() => { selectionRef.current.allowUnselectedMouseUp = true; }, 200); return () => { clearTimeout(timeoutId1); clearTimeout(timeoutId2); }; } selectionRef.current = { allowSelectedMouseUp: false, allowUnselectedMouseUp: false, allowSelect: true }; clearTimeout(timeoutRef.current); return void 0; }, [open, selectionRef]); const getTriggerProps = React.useCallback((externalProps) => { return mergeProps( { "aria-labelledby": labelId, "aria-readonly": readOnly || void 0, tabIndex: disabled ? -1 : 0, // this is needed to make the button focused after click in Safari ref: handleRef, onFocus() { setFocused(true); if (open && alignItemToTrigger) { setOpen(false); } }, onBlur() { setTouched(true); setFocused(false); if (validationMode === "onBlur") { fieldControlValidation.commitValidation(value); } }, onPointerMove({ pointerType }) { setTouchModality(pointerType === "touch"); }, onPointerDown({ pointerType }) { setTouchModality(pointerType === "touch"); }, onMouseDown(event) { if (open) { return; } const doc = ownerDocument(event.currentTarget); function handleMouseUp(mouseEvent) { if (!triggerRef.current) { return; } const mouseUpTarget = mouseEvent.target; if (contains(triggerRef.current, mouseUpTarget) || contains(positionerElement, mouseUpTarget) || mouseUpTarget === triggerRef.current) { return; } const bounds = 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, mouseEvent); } timeoutRef.current = window.setTimeout(() => { doc.addEventListener("mouseup", handleMouseUp, { once: true }); }); } }, fieldControlValidation.getValidationProps(externalProps), getButtonProps, // ensure nested useButton does not overwrite the combobox role: // <Toolbar.Button render={<Select.Trigger />} /> { role: "combobox" } ); }, [getButtonProps, fieldControlValidation, labelId, readOnly, disabled, handleRef, setFocused, open, alignItemToTrigger, setOpen, setTouched, setTouchModality, validationMode, value, positionerElement]); return React.useMemo(() => ({ getTriggerProps, rootRef: handleRef }), [getTriggerProps, handleRef]); } const SelectTrigger = /* @__PURE__ */ React.forwardRef(function SelectTrigger2(props, forwardedRef) { const { render, className, disabled: disabledProp = false, ...otherProps } = props; const { state: fieldState, disabled: fieldDisabled } = useFieldRootContext(); const { getRootTriggerProps, disabled: selectDisabled, mounted } = useSelectRootContext(); const disabled = fieldDisabled || selectDisabled || disabledProp; const { getTriggerProps } = useSelectTrigger({ disabled, rootRef: forwardedRef }); const state = React.useMemo(() => ({ ...fieldState, open: mounted, disabled }), [fieldState, mounted, disabled]); const styleHookMapping = React.useMemo(() => ({ ...pressableTriggerOpenStateMapping, ...fieldValidityMapping }), []); const { renderElement } = useComponentRenderer({ render: render ?? "div", className, state, propGetter: (externalProps) => getTriggerProps(getRootTriggerProps(externalProps)), customStyleHookMapping: styleHookMapping, extraProps: otherProps }); return renderElement(); }); process.env.NODE_ENV !== "production" ? SelectTrigger.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * @ignore */ children: PropTypes.node, /** * CSS class applied to the element, or a function that * returns a class based on the component’s state. */ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** * Whether the component should ignore user interaction. * @default false */ disabled: PropTypes.bool, /** * Allows you to replace the component’s HTML element * with a different tag, or compose it with another component. * * Accepts a `ReactElement` or a function that returns the element to render. */ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) } : void 0; const SelectValue = /* @__PURE__ */ React.forwardRef(function SelectValue2(props, forwardedRef) { const { className, render, children, placeholder, ...otherProps } = props; const { value, label, valueRef } = useSelectRootContext(); const mergedRef = useForkRef(forwardedRef, valueRef); const state = React.useMemo(() => ({}), []); const getValueProps = React.useCallback((externalProps = {}) => mergeProps({ children: typeof children === "function" ? children(!label && placeholder ? placeholder : label, value) : label || placeholder }, externalProps), [children, label, placeholder, value]); const { renderElement } = useComponentRenderer({ propGetter: getValueProps, render: render ?? "span", className, state, ref: mergedRef, extraProps: otherProps }); return renderElement(); }); process.env.NODE_ENV !== "production" ? SelectValue.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * @ignore */ children: PropTypes.func, /** * CSS class applied to the element, or a function that * returns a class based on the component’s state. */ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** * A placeholder value to display when no value is selected. * * You can use this prop to pre-render the displayed text * during SSR in order to avoid the hydration flash. */ placeholder: PropTypes.string, /** * Allows you to replace the component’s HTML element * with a different tag, or compose it with another component. * * Accepts a `ReactElement` or a function that returns the element to render. */ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) } : void 0; const SelectIcon = /* @__PURE__ */ React.forwardRef(function SelectIcon2(props, forwardedRef) { const { className, render, ...otherProps } = props; const state = React.useMemo(() => ({}), []); const getIconProps = React.useCallback((externalProps) => { return mergeProps({ "aria-hidden": true, children: "▼" }, externalProps); }, []); const { renderElement } = useComponentRenderer({ propGetter: getIconProps, render: render ?? "span", ref: forwardedRef, className, state, extraProps: otherProps }); return renderElement(); }); process.env.NODE_ENV !== "production" ? SelectIcon.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * @ignore */ children: PropTypes.node, /** * CSS class applied to the element, or a function that * returns a class based on the component’s state. */ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** * Allows you to replace the component’s HTML element * with a different tag, or compose it with another component. * * Accepts a `ReactElement` or a function that returns the element to render. */ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) } : void 0; const SelectPortalContext = /* @__PURE__ */ React.createContext(void 0); function SelectPortal(props) { const { children, container } = props; return /* @__PURE__ */ jsx(SelectPortalContext.Provider, { value: true, children: /* @__PURE__ */ jsx(FloatingPortal, { root: container, children }) }); } process.env.NODE_ENV !== "production" ? SelectPortal.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * @ignore */ children: PropTypes.node, /** * A parent element to render the portal element into. */ container: PropTypes.oneOfType([HTMLElementType, refType]) } : void 0; function useSelectPositioner(params) { const { open, alignItemToTrigger, mounted, triggerElement, modal } = useSelectRootContext(); useScrollLock((alignItemToTrigger || modal) && open, triggerElement); const positioning = useAnchorPositioning({ ...params, trackAnchor: params.trackAnchor ?? !alignItemToTrigger }); const positionerStyles = React.useMemo(() => alignItemToTrigger ? { position: "fixed" } : positioning.positionerStyles, [alignItemToTrigger, positioning.positionerStyles]); const getPositionerProps = React.useCallback((externalProps = {}) => { const hiddenStyles = {}; if (!open) { hiddenStyles.pointerEvents = "none"; } return mergeProps({ role: "presentation", hidden: !mounted, style: { ...positionerStyles, ...hiddenStyles } }, externalProps); }, [open, mounted, positionerStyles]); return React.useMemo(() => ({ ...positioning, side: alignItemToTrigger ? "none" : positioning.side, getPositionerProps }), [alignItemToTrigger, getPositionerProps, positioning]); } const SelectPositionerContext = /* @__PURE__ */ React.createContext(null); function useSelectPositionerContext() { const context = React.useContext(SelectPositionerContext); if (context === null) { throw new Error("Base UI: SelectPositionerContext is missing. SelectPositioner parts must be placed within <Select.Positioner>."); } return context; } var _InternalBackdrop; const SelectPositioner = /* @__PURE__ */ React.forwardRef(function SelectPositioner2(props, ref) { const { anchor, positionMethod = "absolute", className, render, side = "bottom", align = "center", sideOffset = 0, alignOffset = 0, collisionBoundary = "clipping-ancestors", collisionPadding, arrowPadding = 5, sticky = false, trackAnchor = true, ...otherProps } = props; const { open, mounted, setPositionerElement, listRef, labelsRef, floatingRootContext, modal } = useSelectRootContext(); const positioner = useSelectPositioner({ anchor, floatingRootContext, positionMethod, mounted, side, sideOffset, align, alignOffset, arrowPadding, collisionBoundary, collisionPadding, sticky, trackAnchor, keepMounted: true }); const mergedRef = useForkRef(ref, setPositionerElement); const state = React.useMemo(() => ({ open, side: positioner.side, align: positioner.align, anchorHidden: positioner.anchorHidden }), [open, positioner.side, positioner.align, positioner.anchorHidden]); const { renderElement } = useComponentRenderer({ propGetter: positioner.getPositionerProps, render: render ?? "div", ref: mergedRef, className, state, customStyleHookMapping: popupStateMapping, extraProps: otherProps }); return /* @__PURE__ */ jsx(CompositeList, { elementsRef: listRef, labelsRef, children: /* @__PURE__ */ jsxs(SelectPositionerContext.Provider, { value: positioner, children: [mounted && modal && (_InternalBackdrop || (_InternalBackdrop = /* @__PURE__ */ jsx(InternalBackdrop, {}))), renderElement()] }) }); }); process.env.NODE_ENV !== "production" ? SelectPositioner.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * How to align the popup relative to the specified side. * @default 'center' */ align: PropTypes.oneOf(["center", "end", "start"]), /** * Additional offset along the alignment axis in pixels. * Also accepts a function that returns the offset to read the dimensions of the anchor * and positioner elements, along with its side and alignment. * * - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. * - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. * - `data.side`: which side of the anchor element the positioner is aligned against. * - `data.align`: how the positioner is aligned relative to the specified side. * @default 0 */ alignOffset: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), /** * An element to position the popup against. * By default, the popup will be positioned against the trigger. */ anchor: PropTypes.oneOfType([HTMLElementType, refType, PropTypes.object, PropTypes.func]), /** * Minimum distance to maintain between the arrow and the edges of the popup. * * Use it to prevent the arrow element from hanging out of the rounded corners of a popup. * @default 5 */ arrowPadding: PropTypes.number, /** * @ignore */ children: PropTypes.node, /** * CSS class applied to the element, or a function that * returns a class based on the component’s state. */ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** * An element or a rectangle that delimits the area that the popup is confined to. * @default 'clipping-ancestors' */ collisionBoundary: PropTypes.oneOfType([HTMLElementType, PropTypes.arrayOf(HTMLElementType), PropTypes.string, PropTypes.shape({ height: PropTypes.number, width: PropTypes.number, x: PropTypes.number, y: PropTypes.number })]), /** * Additional space to maintain from the edge of the collision boundary. * @default 5 */ collisionPadding: PropTypes.oneOfType([PropTypes.number, PropTypes.shape({ bottom: PropTypes.number, left: PropTypes.number, right: PropTypes.number, top: PropTypes.number })]), /** * Determines which CSS `position` property to use. * @default 'absolute' */ positionMethod: PropTypes.oneOf(["absolute", "fixed"]), /** * Allows you to replace the component’s HTML element * with a different tag, or compose it with another component. * * Accepts a `ReactElement` or a function that returns the element to render. */ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), /** * Which side of the anchor element to align the popup against. * May automatically change to avoid collisions. * @default 'bottom' */ side: PropTypes.oneOf(["bottom", "inline-end", "inline-start", "left", "right", "top"]), /** * Distance between the anchor and the popup in pixels. * Also accepts a function that returns the distance to read the dimensions of the anchor * and positioner elements, along with its side and alignment. * * - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. * - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. * - `data.side`: which side of the anchor element the positioner is aligned against. * - `data.align`: how the positioner is aligned relative to the specified side. * @default 0 */ sideOffset: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), /** * Whether to maintain the popup in the viewport after * the anchor element was scrolled out of view. * @default false */ sticky: PropTypes.bool, /** * Whether the popup tracks any layout shift of its positioning anchor. * @default true */ trackAnchor: PropTypes.bool } : void 0; function clearPositionerStyles(positionerElement, originalPositionerStyles) { Object.assign(positionerElement.style, originalPositionerStyles); } function useSelectPopup() { const { mounted, id, setOpen, getRootPositionerProps, alignItemToTrigger, triggerElement, positionerElement, valueRef, selectedItemTextRef, popupRef, scrollUpArrowVisible, scrollDownArrowVisible, setScrollUpArrowVisible, setScrollDownArrowVisible, setControlledAlignItemToTrigger, keyboardActiveRef } = useSelectRootContext(); const initialHeightRef = React.useRef(0); const reachedMaxHeightRef = React.useRef(false); const maxHeightRef = React.useRef(0); const initialPlacedRef = React.useRef(false); const originalPositionerStylesRef = React.useRef({}); const handleScrollArrowVisibility = useEventCallback(() => { if (!alignItemToTrigger || !popupRef.current) { return; } const isScrolledToTop = popupRef.current.scrollTop < 1; const isScrolledToBottom = popupRef.current.scrollTop + popupRef.current.clientHeight >= popupRef.current.scrollHeight - 1; if (scrollUpArrowVisible !== !isScrolledToTop) { setScrollUpArrowVisible(!isScrolledToTop); } if (scrollDownArrowVisible !== !isScrolledToBottom) { setScrollDownArrowVisible(!isScrolledToBottom); } }); useEnhancedEffect(() => { if (alignItemToTrigger || !positionerElement || 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 }; }, [alignItemToTrigger, positionerElement]); useEnhancedEffect(() => { if (mounted || alignItemToTrigger) { return; } initialPlacedRef.current = false; reachedMaxHeightRef.current = false; initialHeightRef.current = 0; maxHeightRef.current = 0; if (positionerElement) { clearPositionerStyles(positionerElement, originalPositionerStylesRef.current); } }, [mounted, alignItemToTrigger, positionerElement]); useEnhancedEffect(() => { if (!mounted || !alignItemToTrigger || !triggerElement || !positionerElement || !popupRef.current) { return; } const positionerStyles = getComputedStyle(positionerElement); const popupStyles = getComputedStyle(popupRef.current); const doc = ownerDocument(triggerElement); const win = ownerWindow(positionerElement); const triggerRect = triggerElement.getBoundingClientRect(); const positionerRect = positionerElement.getBoundingClientRect(); const triggerX = triggerRect.left; const triggerHeight = triggerRect.height; const scrollHeight = popupRef.current.scrollHeight; const borderBottom = parseFloat(popupStyles.borderBottomWidth); const marginTop = parseFloat(positionerStyles.marginTop) || 10; const marginBottom = parseFloat(positionerStyles.marginBottom) || 10; const minHeight = parseFloat(positionerStyles.minHeight) || 100; 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; const textElement = selectedItemTextRef.current; const valueElement = valueRef.current; let offsetX = 0; let offsetY = 0; if (textElement && valueElement) { const valueRect = valueElement.getBoundingClientRect(); const textRect = textElement.getBoundingClientRect(); const valueLeftFromTriggerLeft = valueRect.left - triggerX; const textLeftFromPositionerLeft = textRect.left - positionerRect.left; const valueCenterFromPositionerTop = valueRect.top - triggerRect.top + valueRect.height / 2; const textCenterFromTriggerTop = textRect.top - positionerRect.top + textRect.height / 2; offsetX = valueLeftFromTriggerLeft - textLeftFromPositionerLeft; offsetY = textCenterFromTriggerTop - valueCenterFromPositionerTop; } const idealHeight = availableSpaceBeneathTrigger + offsetY + marginBottom + borderBottom; let height = Math.min(viewportHeight, idealHeight); const maxHeight = viewportHeight - marginTop - marginBottom; const scrollTop = idealHeight - height; const left = Math.max(paddingLeft, triggerX + offsetX); const maxRight = viewportWidth - paddingRight; const rightOverflow = Math.max(0, left + positionerRect.width - maxRight); positionerElement.style.left = `${left - rightOverflow}px`; positionerElement.style.height = `${height}px`; positionerElement.style.maxHeight = "auto"; positionerElement.style.marginTop = `${marginTop}px`; positionerElement.style.marginBottom = `${marginBottom}px`; const maxScrollTop = popupRef.current.scrollHeight - popupRef.current.clientHeight; const isTopPositioned = scrollTop >= maxScrollTop; if (isTopPositioned) { height = Math.min(viewportHeight, positionerRect.height) - (scrollTop - maxScrollTop); } const fallbackToAlignPopupToTrigger = triggerRect.top < triggerCollisionThreshold || triggerRect.bottom > viewportHeight - triggerCollisionThreshold || height < Math.min(scrollHeight, minHeight); const isPinchZoomed = (win.visualViewport?.scale ?? 1) !== 1 && isWebKit(); if (fallbackToAlignPopupToTrigger || isPinchZoomed) { initialPlacedRef.current = true; clearPositionerStyles(positionerElement, originalPositionerStylesRef.current); setControlledAlignItemToTrigger(false); return; } if (isTopPositioned) { const topOffset = Math.max(0, viewportHeight - idealHeight); positionerElement.style.top = positionerRect.height >= maxHeight ? "0" : `${topOffset}px`; positionerElement.style.height = `${height}px`; popupRef.current.scrollTop = popupRef.current.scrollHeight - popupRef.current.clientHeight; initialHeightRef.current = Math.max(minHeight, height); } else { positionerElement.style.bottom = "0"; initialHeightRef.current = Math.max(minHeight, height); popupRef.current.scrollTop = scrollTop; } if (initialHeightRef.current === viewportHeight) { reachedMaxHeightRef.current = true; } handleScrollArrowVisibility(); setTimeout(() => { initialPlacedRef.current = true; }); }, [mounted, alignItemToTrigger, positionerElement, triggerElement, valueRef, selectedItemTextRef, popupRef, setScrollUpArrowVisible, setScrollDownArrowVisible, handleScrollArrowVisibility, setControlledAlignItemToTrigger]); React.useEffect(() => { if (!alignItemToTrigger || !positionerElement || !mounted) { return void 0; } const win = ownerWindow(positionerElement); function handleResize() { setOpen(false); } win.addEventListener("resize", handleResize); return () => { win.removeEventListener("resize", handleResize); }; }, [setOpen, alignItemToTrigger, positionerElement, mounted]); const getPopupProps = React.useCallback((externalProps = {}) => { return mergeProps({ ["data-id"]: `${id}-popup`, onKeyDown() { keyboardActiveRef.current = true; }, onMouseMove() { keyboardActiveRef.current = false; }, onScroll(event) { if (!alignItemToTrigger || !positionerElement || !popupRef.current || !initialPlacedRef.current) { return; } if (reachedMaxHeightRef.current || !alignItemToTrigger) { handleScrollArrowVisibility(); return; } const isTopPositioned = positionerElement.style.top === "0px"; const isBottomPositioned = positionerElement.style.bottom === "0px"; const currentHeight = positionerElement.getBoundingClientRect().height; const doc = ownerDocument(positionerElement); const positionerStyles = getComputedStyle(positionerElement); const marginTop = parseFloat(positionerStyles.marginTop); const marginBottom = parseFloat(positionerStyles.marginBottom); const viewportHeight = doc.documentElement.clientHeight - marginTop - marginBottom; if (isTopPositioned) { const scrollTop = event.currentTarget.scrollTop; const maxScrollTop = event.currentTarget.scrollHeight - event.currentTarget.clientHeight; const diff = maxScrollTop - scrollTop; const nextHeight = Math.min(currentHeight + diff, viewportHeight); positionerElement.style.height = `${Math.min(currentHeight + diff, viewportHeight)}px`; if (nextHeight !== viewportHeight) { event.currentTarget.scrollTop = maxScrollTop; } else { reachedMaxHeightRef.current = true; } } else if (isBottomPositioned) { const scrollTop = event.currentTarget.scrollTop; const minScrollTop = 0; const diff = scrollTop - minScrollTop; const nextHeight = Math.min(currentHeight + diff, viewportHeight); const idealHeight = currentHeight + diff; const overshoot = idealHeight - viewportHeight; positionerElement.style.height = `${Math.min(idealHeight, viewportHeight)}px`; if (nextHeight !== viewportHeight) { event.currentTarget.scrollTop = 0; } else { reachedMaxHeightRef.current = true; if (event.currentTarget.scrollTop < event.currentTarget.scrollHeight - event.currentTarget.clientHeight) { event.currentTarget.scrollTop -= diff - overshoot; } } } handleScrollArrowVisibility(); }, ...alignItemToTrigger && { style: { position: