@1771technologies/lytenyte-pro
Version:
1,474 lines • 76.3 kB
JavaScript
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: