| "use client"; |
|
|
| |
| import * as React from "react"; |
| import * as ReactDOM from "react-dom"; |
| import { clamp } from "@radix-ui/number"; |
| import { composeEventHandlers } from "@radix-ui/primitive"; |
| import { createCollection } from "@radix-ui/react-collection"; |
| import { useComposedRefs } from "@radix-ui/react-compose-refs"; |
| import { createContextScope } from "@radix-ui/react-context"; |
| import { useDirection } from "@radix-ui/react-direction"; |
| import { DismissableLayer } from "@radix-ui/react-dismissable-layer"; |
| import { useFocusGuards } from "@radix-ui/react-focus-guards"; |
| import { FocusScope } from "@radix-ui/react-focus-scope"; |
| import { useId } from "@radix-ui/react-id"; |
| import * as PopperPrimitive from "@radix-ui/react-popper"; |
| import { createPopperScope } from "@radix-ui/react-popper"; |
| import { Portal as PortalPrimitive } from "@radix-ui/react-portal"; |
| import { Primitive } from "@radix-ui/react-primitive"; |
| import { Slot } from "@radix-ui/react-slot"; |
| import { useCallbackRef } from "@radix-ui/react-use-callback-ref"; |
| import { useControllableState } from "@radix-ui/react-use-controllable-state"; |
| import { useLayoutEffect } from "@radix-ui/react-use-layout-effect"; |
| import { usePrevious } from "@radix-ui/react-use-previous"; |
| import { VisuallyHidden } from "@radix-ui/react-visually-hidden"; |
| import { hideOthers } from "aria-hidden"; |
| import { RemoveScroll } from "react-remove-scroll"; |
| import { Fragment, jsx, jsxs } from "react/jsx-runtime"; |
| var OPEN_KEYS = [" ", "Enter", "ArrowUp", "ArrowDown"]; |
| var SELECTION_KEYS = [" ", "Enter"]; |
| var SELECT_NAME = "Select"; |
| var [Collection, useCollection, createCollectionScope] = createCollection(SELECT_NAME); |
| var [createSelectContext, createSelectScope] = createContextScope(SELECT_NAME, [ |
| createCollectionScope, |
| createPopperScope |
| ]); |
| var usePopperScope = createPopperScope(); |
| var [SelectProvider, useSelectContext] = createSelectContext(SELECT_NAME); |
| var [SelectNativeOptionsProvider, useSelectNativeOptionsContext] = createSelectContext(SELECT_NAME); |
| var Select = (props) => { |
| const { |
| __scopeSelect, |
| children, |
| open: openProp, |
| defaultOpen, |
| onOpenChange, |
| value: valueProp, |
| defaultValue, |
| onValueChange, |
| dir, |
| name, |
| autoComplete, |
| disabled, |
| required, |
| form |
| } = props; |
| const popperScope = usePopperScope(__scopeSelect); |
| const [trigger, setTrigger] = React.useState(null); |
| const [valueNode, setValueNode] = React.useState(null); |
| const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false); |
| const direction = useDirection(dir); |
| const [open = false, setOpen] = useControllableState({ |
| prop: openProp, |
| defaultProp: defaultOpen, |
| onChange: onOpenChange |
| }); |
| const [value, setValue] = useControllableState({ |
| prop: valueProp, |
| defaultProp: defaultValue, |
| onChange: onValueChange |
| }); |
| const triggerPointerDownPosRef = React.useRef(null); |
| const isFormControl = trigger ? form || !!trigger.closest("form") : true; |
| const [nativeOptionsSet, setNativeOptionsSet] = React.useState( new Set()); |
| const nativeSelectKey = Array.from(nativeOptionsSet).map((option) => option.props.value).join(";"); |
| return jsx(PopperPrimitive.Root, { ...popperScope, children: jsxs( |
| SelectProvider, |
| { |
| required, |
| scope: __scopeSelect, |
| trigger, |
| onTriggerChange: setTrigger, |
| valueNode, |
| onValueNodeChange: setValueNode, |
| valueNodeHasChildren, |
| onValueNodeHasChildrenChange: setValueNodeHasChildren, |
| contentId: useId(), |
| value, |
| onValueChange: setValue, |
| open, |
| onOpenChange: setOpen, |
| dir: direction, |
| triggerPointerDownPosRef, |
| disabled, |
| children: [ |
| jsx(Collection.Provider, { scope: __scopeSelect, children: jsx( |
| SelectNativeOptionsProvider, |
| { |
| scope: props.__scopeSelect, |
| onNativeOptionAdd: React.useCallback((option) => { |
| setNativeOptionsSet((prev) => new Set(prev).add(option)); |
| }, []), |
| onNativeOptionRemove: React.useCallback((option) => { |
| setNativeOptionsSet((prev) => { |
| const optionsSet = new Set(prev); |
| optionsSet.delete(option); |
| return optionsSet; |
| }); |
| }, []), |
| children |
| } |
| ) }), |
| isFormControl ? jsxs( |
| BubbleSelect, |
| { |
| "aria-hidden": true, |
| required, |
| tabIndex: -1, |
| name, |
| autoComplete, |
| value, |
| onChange: (event) => setValue(event.target.value), |
| disabled, |
| form, |
| children: [ |
| value === void 0 ? jsx("option", { value: "" }) : null, |
| Array.from(nativeOptionsSet) |
| ] |
| }, |
| nativeSelectKey |
| ) : null |
| ] |
| } |
| ) }); |
| }; |
| Select.displayName = SELECT_NAME; |
| var TRIGGER_NAME = "SelectTrigger"; |
| var SelectTrigger = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, disabled = false, ...triggerProps } = props; |
| const popperScope = usePopperScope(__scopeSelect); |
| const context = useSelectContext(TRIGGER_NAME, __scopeSelect); |
| const isDisabled = context.disabled || disabled; |
| const composedRefs = useComposedRefs(forwardedRef, context.onTriggerChange); |
| const getItems = useCollection(__scopeSelect); |
| const pointerTypeRef = React.useRef("touch"); |
| const [searchRef, handleTypeaheadSearch, resetTypeahead] = useTypeaheadSearch((search) => { |
| const enabledItems = getItems().filter((item) => !item.disabled); |
| const currentItem = enabledItems.find((item) => item.value === context.value); |
| const nextItem = findNextItem(enabledItems, search, currentItem); |
| if (nextItem !== void 0) { |
| context.onValueChange(nextItem.value); |
| } |
| }); |
| const handleOpen = (pointerEvent) => { |
| if (!isDisabled) { |
| context.onOpenChange(true); |
| resetTypeahead(); |
| } |
| if (pointerEvent) { |
| context.triggerPointerDownPosRef.current = { |
| x: Math.round(pointerEvent.pageX), |
| y: Math.round(pointerEvent.pageY) |
| }; |
| } |
| }; |
| return jsx(PopperPrimitive.Anchor, { asChild: true, ...popperScope, children: jsx( |
| Primitive.button, |
| { |
| type: "button", |
| role: "combobox", |
| "aria-controls": context.contentId, |
| "aria-expanded": context.open, |
| "aria-required": context.required, |
| "aria-autocomplete": "none", |
| dir: context.dir, |
| "data-state": context.open ? "open" : "closed", |
| disabled: isDisabled, |
| "data-disabled": isDisabled ? "" : void 0, |
| "data-placeholder": shouldShowPlaceholder(context.value) ? "" : void 0, |
| ...triggerProps, |
| ref: composedRefs, |
| onClick: composeEventHandlers(triggerProps.onClick, (event) => { |
| event.currentTarget.focus(); |
| if (pointerTypeRef.current !== "mouse") { |
| handleOpen(event); |
| } |
| }), |
| onPointerDown: composeEventHandlers(triggerProps.onPointerDown, (event) => { |
| pointerTypeRef.current = event.pointerType; |
| const target = event.target; |
| if (target.hasPointerCapture(event.pointerId)) { |
| target.releasePointerCapture(event.pointerId); |
| } |
| if (event.button === 0 && event.ctrlKey === false && event.pointerType === "mouse") { |
| handleOpen(event); |
| event.preventDefault(); |
| } |
| }), |
| onKeyDown: composeEventHandlers(triggerProps.onKeyDown, (event) => { |
| const isTypingAhead = searchRef.current !== ""; |
| const isModifierKey = event.ctrlKey || event.altKey || event.metaKey; |
| if (!isModifierKey && event.key.length === 1) handleTypeaheadSearch(event.key); |
| if (isTypingAhead && event.key === " ") return; |
| if (OPEN_KEYS.includes(event.key)) { |
| handleOpen(); |
| event.preventDefault(); |
| } |
| }) |
| } |
| ) }); |
| } |
| ); |
| SelectTrigger.displayName = TRIGGER_NAME; |
| var VALUE_NAME = "SelectValue"; |
| var SelectValue = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, className, style, children, placeholder = "", ...valueProps } = props; |
| const context = useSelectContext(VALUE_NAME, __scopeSelect); |
| const { onValueNodeHasChildrenChange } = context; |
| const hasChildren = children !== void 0; |
| const composedRefs = useComposedRefs(forwardedRef, context.onValueNodeChange); |
| useLayoutEffect(() => { |
| onValueNodeHasChildrenChange(hasChildren); |
| }, [onValueNodeHasChildrenChange, hasChildren]); |
| return jsx( |
| Primitive.span, |
| { |
| ...valueProps, |
| ref: composedRefs, |
| style: { pointerEvents: "none" }, |
| children: shouldShowPlaceholder(context.value) ? jsx(Fragment, { children: placeholder }) : children |
| } |
| ); |
| } |
| ); |
| SelectValue.displayName = VALUE_NAME; |
| var ICON_NAME = "SelectIcon"; |
| var SelectIcon = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, children, ...iconProps } = props; |
| return jsx(Primitive.span, { "aria-hidden": true, ...iconProps, ref: forwardedRef, children: children || "\u25BC" }); |
| } |
| ); |
| SelectIcon.displayName = ICON_NAME; |
| var PORTAL_NAME = "SelectPortal"; |
| var SelectPortal = (props) => { |
| return jsx(PortalPrimitive, { asChild: true, ...props }); |
| }; |
| SelectPortal.displayName = PORTAL_NAME; |
| var CONTENT_NAME = "SelectContent"; |
| var SelectContent = React.forwardRef( |
| (props, forwardedRef) => { |
| const context = useSelectContext(CONTENT_NAME, props.__scopeSelect); |
| const [fragment, setFragment] = React.useState(); |
| useLayoutEffect(() => { |
| setFragment(new DocumentFragment()); |
| }, []); |
| if (!context.open) { |
| const frag = fragment; |
| return frag ? ReactDOM.createPortal( |
| jsx(SelectContentProvider, { scope: props.__scopeSelect, children: jsx(Collection.Slot, { scope: props.__scopeSelect, children: jsx("div", { children: props.children }) }) }), |
| frag |
| ) : null; |
| } |
| return jsx(SelectContentImpl, { ...props, ref: forwardedRef }); |
| } |
| ); |
| SelectContent.displayName = CONTENT_NAME; |
| var CONTENT_MARGIN = 10; |
| var [SelectContentProvider, useSelectContentContext] = createSelectContext(CONTENT_NAME); |
| var CONTENT_IMPL_NAME = "SelectContentImpl"; |
| var SelectContentImpl = React.forwardRef( |
| (props, forwardedRef) => { |
| const { |
| __scopeSelect, |
| position = "item-aligned", |
| onCloseAutoFocus, |
| onEscapeKeyDown, |
| onPointerDownOutside, |
| |
| |
| side, |
| sideOffset, |
| align, |
| alignOffset, |
| arrowPadding, |
| collisionBoundary, |
| collisionPadding, |
| sticky, |
| hideWhenDetached, |
| avoidCollisions, |
| |
| ...contentProps |
| } = props; |
| const context = useSelectContext(CONTENT_NAME, __scopeSelect); |
| const [content, setContent] = React.useState(null); |
| const [viewport, setViewport] = React.useState(null); |
| const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node)); |
| const [selectedItem, setSelectedItem] = React.useState(null); |
| const [selectedItemText, setSelectedItemText] = React.useState( |
| null |
| ); |
| const getItems = useCollection(__scopeSelect); |
| const [isPositioned, setIsPositioned] = React.useState(false); |
| const firstValidItemFoundRef = React.useRef(false); |
| React.useEffect(() => { |
| if (content) return hideOthers(content); |
| }, [content]); |
| useFocusGuards(); |
| const focusFirst = React.useCallback( |
| (candidates) => { |
| const [firstItem, ...restItems] = getItems().map((item) => item.ref.current); |
| const [lastItem] = restItems.slice(-1); |
| const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement; |
| for (const candidate of candidates) { |
| if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return; |
| candidate?.scrollIntoView({ block: "nearest" }); |
| if (candidate === firstItem && viewport) viewport.scrollTop = 0; |
| if (candidate === lastItem && viewport) viewport.scrollTop = viewport.scrollHeight; |
| candidate?.focus(); |
| if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return; |
| } |
| }, |
| [getItems, viewport] |
| ); |
| const focusSelectedItem = React.useCallback( |
| () => focusFirst([selectedItem, content]), |
| [focusFirst, selectedItem, content] |
| ); |
| React.useEffect(() => { |
| if (isPositioned) { |
| focusSelectedItem(); |
| } |
| }, [isPositioned, focusSelectedItem]); |
| const { onOpenChange, triggerPointerDownPosRef } = context; |
| React.useEffect(() => { |
| if (content) { |
| let pointerMoveDelta = { x: 0, y: 0 }; |
| const handlePointerMove = (event) => { |
| pointerMoveDelta = { |
| x: Math.abs(Math.round(event.pageX) - (triggerPointerDownPosRef.current?.x ?? 0)), |
| y: Math.abs(Math.round(event.pageY) - (triggerPointerDownPosRef.current?.y ?? 0)) |
| }; |
| }; |
| const handlePointerUp = (event) => { |
| if (pointerMoveDelta.x <= 10 && pointerMoveDelta.y <= 10) { |
| event.preventDefault(); |
| } else { |
| if (!content.contains(event.target)) { |
| onOpenChange(false); |
| } |
| } |
| document.removeEventListener("pointermove", handlePointerMove); |
| triggerPointerDownPosRef.current = null; |
| }; |
| if (triggerPointerDownPosRef.current !== null) { |
| document.addEventListener("pointermove", handlePointerMove); |
| document.addEventListener("pointerup", handlePointerUp, { capture: true, once: true }); |
| } |
| return () => { |
| document.removeEventListener("pointermove", handlePointerMove); |
| document.removeEventListener("pointerup", handlePointerUp, { capture: true }); |
| }; |
| } |
| }, [content, onOpenChange, triggerPointerDownPosRef]); |
| React.useEffect(() => { |
| const close = () => onOpenChange(false); |
| window.addEventListener("blur", close); |
| window.addEventListener("resize", close); |
| return () => { |
| window.removeEventListener("blur", close); |
| window.removeEventListener("resize", close); |
| }; |
| }, [onOpenChange]); |
| const [searchRef, handleTypeaheadSearch] = useTypeaheadSearch((search) => { |
| const enabledItems = getItems().filter((item) => !item.disabled); |
| const currentItem = enabledItems.find((item) => item.ref.current === document.activeElement); |
| const nextItem = findNextItem(enabledItems, search, currentItem); |
| if (nextItem) { |
| setTimeout(() => nextItem.ref.current.focus()); |
| } |
| }); |
| const itemRefCallback = React.useCallback( |
| (node, value, disabled) => { |
| const isFirstValidItem = !firstValidItemFoundRef.current && !disabled; |
| const isSelectedItem = context.value !== void 0 && context.value === value; |
| if (isSelectedItem || isFirstValidItem) { |
| setSelectedItem(node); |
| if (isFirstValidItem) firstValidItemFoundRef.current = true; |
| } |
| }, |
| [context.value] |
| ); |
| const handleItemLeave = React.useCallback(() => content?.focus(), [content]); |
| const itemTextRefCallback = React.useCallback( |
| (node, value, disabled) => { |
| const isFirstValidItem = !firstValidItemFoundRef.current && !disabled; |
| const isSelectedItem = context.value !== void 0 && context.value === value; |
| if (isSelectedItem || isFirstValidItem) { |
| setSelectedItemText(node); |
| } |
| }, |
| [context.value] |
| ); |
| const SelectPosition = position === "popper" ? SelectPopperPosition : SelectItemAlignedPosition; |
| const popperContentProps = SelectPosition === SelectPopperPosition ? { |
| side, |
| sideOffset, |
| align, |
| alignOffset, |
| arrowPadding, |
| collisionBoundary, |
| collisionPadding, |
| sticky, |
| hideWhenDetached, |
| avoidCollisions |
| } : {}; |
| return jsx( |
| SelectContentProvider, |
| { |
| scope: __scopeSelect, |
| content, |
| viewport, |
| onViewportChange: setViewport, |
| itemRefCallback, |
| selectedItem, |
| onItemLeave: handleItemLeave, |
| itemTextRefCallback, |
| focusSelectedItem, |
| selectedItemText, |
| position, |
| isPositioned, |
| searchRef, |
| children: jsx(RemoveScroll, { as: Slot, allowPinchZoom: true, children: jsx( |
| FocusScope, |
| { |
| asChild: true, |
| trapped: context.open, |
| onMountAutoFocus: (event) => { |
| event.preventDefault(); |
| }, |
| onUnmountAutoFocus: composeEventHandlers(onCloseAutoFocus, (event) => { |
| context.trigger?.focus({ preventScroll: true }); |
| event.preventDefault(); |
| }), |
| children: jsx( |
| DismissableLayer, |
| { |
| asChild: true, |
| disableOutsidePointerEvents: true, |
| onEscapeKeyDown, |
| onPointerDownOutside, |
| onFocusOutside: (event) => event.preventDefault(), |
| onDismiss: () => context.onOpenChange(false), |
| children: jsx( |
| SelectPosition, |
| { |
| role: "listbox", |
| id: context.contentId, |
| "data-state": context.open ? "open" : "closed", |
| dir: context.dir, |
| onContextMenu: (event) => event.preventDefault(), |
| ...contentProps, |
| ...popperContentProps, |
| onPlaced: () => setIsPositioned(true), |
| ref: composedRefs, |
| style: { |
| |
| display: "flex", |
| flexDirection: "column", |
| |
| outline: "none", |
| ...contentProps.style |
| }, |
| onKeyDown: composeEventHandlers(contentProps.onKeyDown, (event) => { |
| const isModifierKey = event.ctrlKey || event.altKey || event.metaKey; |
| if (event.key === "Tab") event.preventDefault(); |
| if (!isModifierKey && event.key.length === 1) handleTypeaheadSearch(event.key); |
| if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) { |
| const items = getItems().filter((item) => !item.disabled); |
| let candidateNodes = items.map((item) => item.ref.current); |
| if (["ArrowUp", "End"].includes(event.key)) { |
| candidateNodes = candidateNodes.slice().reverse(); |
| } |
| if (["ArrowUp", "ArrowDown"].includes(event.key)) { |
| const currentElement = event.target; |
| const currentIndex = candidateNodes.indexOf(currentElement); |
| candidateNodes = candidateNodes.slice(currentIndex + 1); |
| } |
| setTimeout(() => focusFirst(candidateNodes)); |
| event.preventDefault(); |
| } |
| }) |
| } |
| ) |
| } |
| ) |
| } |
| ) }) |
| } |
| ); |
| } |
| ); |
| SelectContentImpl.displayName = CONTENT_IMPL_NAME; |
| var ITEM_ALIGNED_POSITION_NAME = "SelectItemAlignedPosition"; |
| var SelectItemAlignedPosition = React.forwardRef((props, forwardedRef) => { |
| const { __scopeSelect, onPlaced, ...popperProps } = props; |
| const context = useSelectContext(CONTENT_NAME, __scopeSelect); |
| const contentContext = useSelectContentContext(CONTENT_NAME, __scopeSelect); |
| const [contentWrapper, setContentWrapper] = React.useState(null); |
| const [content, setContent] = React.useState(null); |
| const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node)); |
| const getItems = useCollection(__scopeSelect); |
| const shouldExpandOnScrollRef = React.useRef(false); |
| const shouldRepositionRef = React.useRef(true); |
| const { viewport, selectedItem, selectedItemText, focusSelectedItem } = contentContext; |
| const position = React.useCallback(() => { |
| if (context.trigger && context.valueNode && contentWrapper && content && viewport && selectedItem && selectedItemText) { |
| const triggerRect = context.trigger.getBoundingClientRect(); |
| const contentRect = content.getBoundingClientRect(); |
| const valueNodeRect = context.valueNode.getBoundingClientRect(); |
| const itemTextRect = selectedItemText.getBoundingClientRect(); |
| if (context.dir !== "rtl") { |
| const itemTextOffset = itemTextRect.left - contentRect.left; |
| const left = valueNodeRect.left - itemTextOffset; |
| const leftDelta = triggerRect.left - left; |
| const minContentWidth = triggerRect.width + leftDelta; |
| const contentWidth = Math.max(minContentWidth, contentRect.width); |
| const rightEdge = window.innerWidth - CONTENT_MARGIN; |
| const clampedLeft = clamp(left, [ |
| CONTENT_MARGIN, |
| |
| |
| |
| |
| |
| Math.max(CONTENT_MARGIN, rightEdge - contentWidth) |
| ]); |
| contentWrapper.style.minWidth = minContentWidth + "px"; |
| contentWrapper.style.left = clampedLeft + "px"; |
| } else { |
| const itemTextOffset = contentRect.right - itemTextRect.right; |
| const right = window.innerWidth - valueNodeRect.right - itemTextOffset; |
| const rightDelta = window.innerWidth - triggerRect.right - right; |
| const minContentWidth = triggerRect.width + rightDelta; |
| const contentWidth = Math.max(minContentWidth, contentRect.width); |
| const leftEdge = window.innerWidth - CONTENT_MARGIN; |
| const clampedRight = clamp(right, [ |
| CONTENT_MARGIN, |
| Math.max(CONTENT_MARGIN, leftEdge - contentWidth) |
| ]); |
| contentWrapper.style.minWidth = minContentWidth + "px"; |
| contentWrapper.style.right = clampedRight + "px"; |
| } |
| const items = getItems(); |
| const availableHeight = window.innerHeight - CONTENT_MARGIN * 2; |
| const itemsHeight = viewport.scrollHeight; |
| const contentStyles = window.getComputedStyle(content); |
| const contentBorderTopWidth = parseInt(contentStyles.borderTopWidth, 10); |
| const contentPaddingTop = parseInt(contentStyles.paddingTop, 10); |
| const contentBorderBottomWidth = parseInt(contentStyles.borderBottomWidth, 10); |
| const contentPaddingBottom = parseInt(contentStyles.paddingBottom, 10); |
| const fullContentHeight = contentBorderTopWidth + contentPaddingTop + itemsHeight + contentPaddingBottom + contentBorderBottomWidth; |
| const minContentHeight = Math.min(selectedItem.offsetHeight * 5, fullContentHeight); |
| const viewportStyles = window.getComputedStyle(viewport); |
| const viewportPaddingTop = parseInt(viewportStyles.paddingTop, 10); |
| const viewportPaddingBottom = parseInt(viewportStyles.paddingBottom, 10); |
| const topEdgeToTriggerMiddle = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN; |
| const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle; |
| const selectedItemHalfHeight = selectedItem.offsetHeight / 2; |
| const itemOffsetMiddle = selectedItem.offsetTop + selectedItemHalfHeight; |
| const contentTopToItemMiddle = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle; |
| const itemMiddleToContentBottom = fullContentHeight - contentTopToItemMiddle; |
| const willAlignWithoutTopOverflow = contentTopToItemMiddle <= topEdgeToTriggerMiddle; |
| if (willAlignWithoutTopOverflow) { |
| const isLastItem = items.length > 0 && selectedItem === items[items.length - 1].ref.current; |
| contentWrapper.style.bottom = "0px"; |
| const viewportOffsetBottom = content.clientHeight - viewport.offsetTop - viewport.offsetHeight; |
| const clampedTriggerMiddleToBottomEdge = Math.max( |
| triggerMiddleToBottomEdge, |
| selectedItemHalfHeight + |
| (isLastItem ? viewportPaddingBottom : 0) + viewportOffsetBottom + contentBorderBottomWidth |
| ); |
| const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge; |
| contentWrapper.style.height = height + "px"; |
| } else { |
| const isFirstItem = items.length > 0 && selectedItem === items[0].ref.current; |
| contentWrapper.style.top = "0px"; |
| const clampedTopEdgeToTriggerMiddle = Math.max( |
| topEdgeToTriggerMiddle, |
| contentBorderTopWidth + viewport.offsetTop + |
| (isFirstItem ? viewportPaddingTop : 0) + selectedItemHalfHeight |
| ); |
| const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom; |
| contentWrapper.style.height = height + "px"; |
| viewport.scrollTop = contentTopToItemMiddle - topEdgeToTriggerMiddle + viewport.offsetTop; |
| } |
| contentWrapper.style.margin = `${CONTENT_MARGIN}px 0`; |
| contentWrapper.style.minHeight = minContentHeight + "px"; |
| contentWrapper.style.maxHeight = availableHeight + "px"; |
| onPlaced?.(); |
| requestAnimationFrame(() => shouldExpandOnScrollRef.current = true); |
| } |
| }, [ |
| getItems, |
| context.trigger, |
| context.valueNode, |
| contentWrapper, |
| content, |
| viewport, |
| selectedItem, |
| selectedItemText, |
| context.dir, |
| onPlaced |
| ]); |
| useLayoutEffect(() => position(), [position]); |
| const [contentZIndex, setContentZIndex] = React.useState(); |
| useLayoutEffect(() => { |
| if (content) setContentZIndex(window.getComputedStyle(content).zIndex); |
| }, [content]); |
| const handleScrollButtonChange = React.useCallback( |
| (node) => { |
| if (node && shouldRepositionRef.current === true) { |
| position(); |
| focusSelectedItem?.(); |
| shouldRepositionRef.current = false; |
| } |
| }, |
| [position, focusSelectedItem] |
| ); |
| return jsx( |
| SelectViewportProvider, |
| { |
| scope: __scopeSelect, |
| contentWrapper, |
| shouldExpandOnScrollRef, |
| onScrollButtonChange: handleScrollButtonChange, |
| children: jsx( |
| "div", |
| { |
| ref: setContentWrapper, |
| style: { |
| display: "flex", |
| flexDirection: "column", |
| position: "fixed", |
| zIndex: contentZIndex |
| }, |
| children: jsx( |
| Primitive.div, |
| { |
| ...popperProps, |
| ref: composedRefs, |
| style: { |
| |
| |
| boxSizing: "border-box", |
| |
| maxHeight: "100%", |
| ...popperProps.style |
| } |
| } |
| ) |
| } |
| ) |
| } |
| ); |
| }); |
| SelectItemAlignedPosition.displayName = ITEM_ALIGNED_POSITION_NAME; |
| var POPPER_POSITION_NAME = "SelectPopperPosition"; |
| var SelectPopperPosition = React.forwardRef((props, forwardedRef) => { |
| const { |
| __scopeSelect, |
| align = "start", |
| collisionPadding = CONTENT_MARGIN, |
| ...popperProps |
| } = props; |
| const popperScope = usePopperScope(__scopeSelect); |
| return jsx( |
| PopperPrimitive.Content, |
| { |
| ...popperScope, |
| ...popperProps, |
| ref: forwardedRef, |
| align, |
| collisionPadding, |
| style: { |
| |
| boxSizing: "border-box", |
| ...popperProps.style, |
| |
| ...{ |
| "--radix-select-content-transform-origin": "var(--radix-popper-transform-origin)", |
| "--radix-select-content-available-width": "var(--radix-popper-available-width)", |
| "--radix-select-content-available-height": "var(--radix-popper-available-height)", |
| "--radix-select-trigger-width": "var(--radix-popper-anchor-width)", |
| "--radix-select-trigger-height": "var(--radix-popper-anchor-height)" |
| } |
| } |
| } |
| ); |
| }); |
| SelectPopperPosition.displayName = POPPER_POSITION_NAME; |
| var [SelectViewportProvider, useSelectViewportContext] = createSelectContext(CONTENT_NAME, {}); |
| var VIEWPORT_NAME = "SelectViewport"; |
| var SelectViewport = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, nonce, ...viewportProps } = props; |
| const contentContext = useSelectContentContext(VIEWPORT_NAME, __scopeSelect); |
| const viewportContext = useSelectViewportContext(VIEWPORT_NAME, __scopeSelect); |
| const composedRefs = useComposedRefs(forwardedRef, contentContext.onViewportChange); |
| const prevScrollTopRef = React.useRef(0); |
| return jsxs(Fragment, { children: [ |
| jsx( |
| "style", |
| { |
| dangerouslySetInnerHTML: { |
| __html: `[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}` |
| }, |
| nonce |
| } |
| ), |
| jsx(Collection.Slot, { scope: __scopeSelect, children: jsx( |
| Primitive.div, |
| { |
| "data-radix-select-viewport": "", |
| role: "presentation", |
| ...viewportProps, |
| ref: composedRefs, |
| style: { |
| |
| |
| |
| position: "relative", |
| flex: 1, |
| |
| |
| |
| |
| overflow: "hidden auto", |
| ...viewportProps.style |
| }, |
| onScroll: composeEventHandlers(viewportProps.onScroll, (event) => { |
| const viewport = event.currentTarget; |
| const { contentWrapper, shouldExpandOnScrollRef } = viewportContext; |
| if (shouldExpandOnScrollRef?.current && contentWrapper) { |
| const scrolledBy = Math.abs(prevScrollTopRef.current - viewport.scrollTop); |
| if (scrolledBy > 0) { |
| const availableHeight = window.innerHeight - CONTENT_MARGIN * 2; |
| const cssMinHeight = parseFloat(contentWrapper.style.minHeight); |
| const cssHeight = parseFloat(contentWrapper.style.height); |
| const prevHeight = Math.max(cssMinHeight, cssHeight); |
| if (prevHeight < availableHeight) { |
| const nextHeight = prevHeight + scrolledBy; |
| const clampedNextHeight = Math.min(availableHeight, nextHeight); |
| const heightDiff = nextHeight - clampedNextHeight; |
| contentWrapper.style.height = clampedNextHeight + "px"; |
| if (contentWrapper.style.bottom === "0px") { |
| viewport.scrollTop = heightDiff > 0 ? heightDiff : 0; |
| contentWrapper.style.justifyContent = "flex-end"; |
| } |
| } |
| } |
| } |
| prevScrollTopRef.current = viewport.scrollTop; |
| }) |
| } |
| ) }) |
| ] }); |
| } |
| ); |
| SelectViewport.displayName = VIEWPORT_NAME; |
| var GROUP_NAME = "SelectGroup"; |
| var [SelectGroupContextProvider, useSelectGroupContext] = createSelectContext(GROUP_NAME); |
| var SelectGroup = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, ...groupProps } = props; |
| const groupId = useId(); |
| return jsx(SelectGroupContextProvider, { scope: __scopeSelect, id: groupId, children: jsx(Primitive.div, { role: "group", "aria-labelledby": groupId, ...groupProps, ref: forwardedRef }) }); |
| } |
| ); |
| SelectGroup.displayName = GROUP_NAME; |
| var LABEL_NAME = "SelectLabel"; |
| var SelectLabel = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, ...labelProps } = props; |
| const groupContext = useSelectGroupContext(LABEL_NAME, __scopeSelect); |
| return jsx(Primitive.div, { id: groupContext.id, ...labelProps, ref: forwardedRef }); |
| } |
| ); |
| SelectLabel.displayName = LABEL_NAME; |
| var ITEM_NAME = "SelectItem"; |
| var [SelectItemContextProvider, useSelectItemContext] = createSelectContext(ITEM_NAME); |
| var SelectItem = React.forwardRef( |
| (props, forwardedRef) => { |
| const { |
| __scopeSelect, |
| value, |
| disabled = false, |
| textValue: textValueProp, |
| ...itemProps |
| } = props; |
| const context = useSelectContext(ITEM_NAME, __scopeSelect); |
| const contentContext = useSelectContentContext(ITEM_NAME, __scopeSelect); |
| const isSelected = context.value === value; |
| const [textValue, setTextValue] = React.useState(textValueProp ?? ""); |
| const [isFocused, setIsFocused] = React.useState(false); |
| const composedRefs = useComposedRefs( |
| forwardedRef, |
| (node) => contentContext.itemRefCallback?.(node, value, disabled) |
| ); |
| const textId = useId(); |
| const pointerTypeRef = React.useRef("touch"); |
| const handleSelect = () => { |
| if (!disabled) { |
| context.onValueChange(value); |
| context.onOpenChange(false); |
| } |
| }; |
| if (value === "") { |
| throw new Error( |
| "A <Select.Item /> must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder." |
| ); |
| } |
| return jsx( |
| SelectItemContextProvider, |
| { |
| scope: __scopeSelect, |
| value, |
| disabled, |
| textId, |
| isSelected, |
| onItemTextChange: React.useCallback((node) => { |
| setTextValue((prevTextValue) => prevTextValue || (node?.textContent ?? "").trim()); |
| }, []), |
| children: jsx( |
| Collection.ItemSlot, |
| { |
| scope: __scopeSelect, |
| value, |
| disabled, |
| textValue, |
| children: jsx( |
| Primitive.div, |
| { |
| role: "option", |
| "aria-labelledby": textId, |
| "data-highlighted": isFocused ? "" : void 0, |
| "aria-selected": isSelected && isFocused, |
| "data-state": isSelected ? "checked" : "unchecked", |
| "aria-disabled": disabled || void 0, |
| "data-disabled": disabled ? "" : void 0, |
| tabIndex: disabled ? void 0 : -1, |
| ...itemProps, |
| ref: composedRefs, |
| onFocus: composeEventHandlers(itemProps.onFocus, () => setIsFocused(true)), |
| onBlur: composeEventHandlers(itemProps.onBlur, () => setIsFocused(false)), |
| onClick: composeEventHandlers(itemProps.onClick, () => { |
| if (pointerTypeRef.current !== "mouse") handleSelect(); |
| }), |
| onPointerUp: composeEventHandlers(itemProps.onPointerUp, () => { |
| if (pointerTypeRef.current === "mouse") handleSelect(); |
| }), |
| onPointerDown: composeEventHandlers(itemProps.onPointerDown, (event) => { |
| pointerTypeRef.current = event.pointerType; |
| }), |
| onPointerMove: composeEventHandlers(itemProps.onPointerMove, (event) => { |
| pointerTypeRef.current = event.pointerType; |
| if (disabled) { |
| contentContext.onItemLeave?.(); |
| } else if (pointerTypeRef.current === "mouse") { |
| event.currentTarget.focus({ preventScroll: true }); |
| } |
| }), |
| onPointerLeave: composeEventHandlers(itemProps.onPointerLeave, (event) => { |
| if (event.currentTarget === document.activeElement) { |
| contentContext.onItemLeave?.(); |
| } |
| }), |
| onKeyDown: composeEventHandlers(itemProps.onKeyDown, (event) => { |
| const isTypingAhead = contentContext.searchRef?.current !== ""; |
| if (isTypingAhead && event.key === " ") return; |
| if (SELECTION_KEYS.includes(event.key)) handleSelect(); |
| if (event.key === " ") event.preventDefault(); |
| }) |
| } |
| ) |
| } |
| ) |
| } |
| ); |
| } |
| ); |
| SelectItem.displayName = ITEM_NAME; |
| var ITEM_TEXT_NAME = "SelectItemText"; |
| var SelectItemText = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, className, style, ...itemTextProps } = props; |
| const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect); |
| const contentContext = useSelectContentContext(ITEM_TEXT_NAME, __scopeSelect); |
| const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect); |
| const nativeOptionsContext = useSelectNativeOptionsContext(ITEM_TEXT_NAME, __scopeSelect); |
| const [itemTextNode, setItemTextNode] = React.useState(null); |
| const composedRefs = useComposedRefs( |
| forwardedRef, |
| (node) => setItemTextNode(node), |
| itemContext.onItemTextChange, |
| (node) => contentContext.itemTextRefCallback?.(node, itemContext.value, itemContext.disabled) |
| ); |
| const textContent = itemTextNode?.textContent; |
| const nativeOption = React.useMemo( |
| () => jsx("option", { value: itemContext.value, disabled: itemContext.disabled, children: textContent }, itemContext.value), |
| [itemContext.disabled, itemContext.value, textContent] |
| ); |
| const { onNativeOptionAdd, onNativeOptionRemove } = nativeOptionsContext; |
| useLayoutEffect(() => { |
| onNativeOptionAdd(nativeOption); |
| return () => onNativeOptionRemove(nativeOption); |
| }, [onNativeOptionAdd, onNativeOptionRemove, nativeOption]); |
| return jsxs(Fragment, { children: [ |
| jsx(Primitive.span, { id: itemContext.textId, ...itemTextProps, ref: composedRefs }), |
| itemContext.isSelected && context.valueNode && !context.valueNodeHasChildren ? ReactDOM.createPortal(itemTextProps.children, context.valueNode) : null |
| ] }); |
| } |
| ); |
| SelectItemText.displayName = ITEM_TEXT_NAME; |
| var ITEM_INDICATOR_NAME = "SelectItemIndicator"; |
| var SelectItemIndicator = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, ...itemIndicatorProps } = props; |
| const itemContext = useSelectItemContext(ITEM_INDICATOR_NAME, __scopeSelect); |
| return itemContext.isSelected ? jsx(Primitive.span, { "aria-hidden": true, ...itemIndicatorProps, ref: forwardedRef }) : null; |
| } |
| ); |
| SelectItemIndicator.displayName = ITEM_INDICATOR_NAME; |
| var SCROLL_UP_BUTTON_NAME = "SelectScrollUpButton"; |
| var SelectScrollUpButton = React.forwardRef((props, forwardedRef) => { |
| const contentContext = useSelectContentContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect); |
| const viewportContext = useSelectViewportContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect); |
| const [canScrollUp, setCanScrollUp] = React.useState(false); |
| const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange); |
| useLayoutEffect(() => { |
| if (contentContext.viewport && contentContext.isPositioned) { |
| let handleScroll2 = function() { |
| const canScrollUp2 = viewport.scrollTop > 0; |
| setCanScrollUp(canScrollUp2); |
| }; |
| var handleScroll = handleScroll2; |
| const viewport = contentContext.viewport; |
| handleScroll2(); |
| viewport.addEventListener("scroll", handleScroll2); |
| return () => viewport.removeEventListener("scroll", handleScroll2); |
| } |
| }, [contentContext.viewport, contentContext.isPositioned]); |
| return canScrollUp ? jsx( |
| SelectScrollButtonImpl, |
| { |
| ...props, |
| ref: composedRefs, |
| onAutoScroll: () => { |
| const { viewport, selectedItem } = contentContext; |
| if (viewport && selectedItem) { |
| viewport.scrollTop = viewport.scrollTop - selectedItem.offsetHeight; |
| } |
| } |
| } |
| ) : null; |
| }); |
| SelectScrollUpButton.displayName = SCROLL_UP_BUTTON_NAME; |
| var SCROLL_DOWN_BUTTON_NAME = "SelectScrollDownButton"; |
| var SelectScrollDownButton = React.forwardRef((props, forwardedRef) => { |
| const contentContext = useSelectContentContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect); |
| const viewportContext = useSelectViewportContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect); |
| const [canScrollDown, setCanScrollDown] = React.useState(false); |
| const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange); |
| useLayoutEffect(() => { |
| if (contentContext.viewport && contentContext.isPositioned) { |
| let handleScroll2 = function() { |
| const maxScroll = viewport.scrollHeight - viewport.clientHeight; |
| const canScrollDown2 = Math.ceil(viewport.scrollTop) < maxScroll; |
| setCanScrollDown(canScrollDown2); |
| }; |
| var handleScroll = handleScroll2; |
| const viewport = contentContext.viewport; |
| handleScroll2(); |
| viewport.addEventListener("scroll", handleScroll2); |
| return () => viewport.removeEventListener("scroll", handleScroll2); |
| } |
| }, [contentContext.viewport, contentContext.isPositioned]); |
| return canScrollDown ? jsx( |
| SelectScrollButtonImpl, |
| { |
| ...props, |
| ref: composedRefs, |
| onAutoScroll: () => { |
| const { viewport, selectedItem } = contentContext; |
| if (viewport && selectedItem) { |
| viewport.scrollTop = viewport.scrollTop + selectedItem.offsetHeight; |
| } |
| } |
| } |
| ) : null; |
| }); |
| SelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME; |
| var SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => { |
| const { __scopeSelect, onAutoScroll, ...scrollIndicatorProps } = props; |
| const contentContext = useSelectContentContext("SelectScrollButton", __scopeSelect); |
| const autoScrollTimerRef = React.useRef(null); |
| const getItems = useCollection(__scopeSelect); |
| const clearAutoScrollTimer = React.useCallback(() => { |
| if (autoScrollTimerRef.current !== null) { |
| window.clearInterval(autoScrollTimerRef.current); |
| autoScrollTimerRef.current = null; |
| } |
| }, []); |
| React.useEffect(() => { |
| return () => clearAutoScrollTimer(); |
| }, [clearAutoScrollTimer]); |
| useLayoutEffect(() => { |
| const activeItem = getItems().find((item) => item.ref.current === document.activeElement); |
| activeItem?.ref.current?.scrollIntoView({ block: "nearest" }); |
| }, [getItems]); |
| return jsx( |
| Primitive.div, |
| { |
| "aria-hidden": true, |
| ...scrollIndicatorProps, |
| ref: forwardedRef, |
| style: { flexShrink: 0, ...scrollIndicatorProps.style }, |
| onPointerDown: composeEventHandlers(scrollIndicatorProps.onPointerDown, () => { |
| if (autoScrollTimerRef.current === null) { |
| autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50); |
| } |
| }), |
| onPointerMove: composeEventHandlers(scrollIndicatorProps.onPointerMove, () => { |
| contentContext.onItemLeave?.(); |
| if (autoScrollTimerRef.current === null) { |
| autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50); |
| } |
| }), |
| onPointerLeave: composeEventHandlers(scrollIndicatorProps.onPointerLeave, () => { |
| clearAutoScrollTimer(); |
| }) |
| } |
| ); |
| }); |
| var SEPARATOR_NAME = "SelectSeparator"; |
| var SelectSeparator = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, ...separatorProps } = props; |
| return jsx(Primitive.div, { "aria-hidden": true, ...separatorProps, ref: forwardedRef }); |
| } |
| ); |
| SelectSeparator.displayName = SEPARATOR_NAME; |
| var ARROW_NAME = "SelectArrow"; |
| var SelectArrow = React.forwardRef( |
| (props, forwardedRef) => { |
| const { __scopeSelect, ...arrowProps } = props; |
| const popperScope = usePopperScope(__scopeSelect); |
| const context = useSelectContext(ARROW_NAME, __scopeSelect); |
| const contentContext = useSelectContentContext(ARROW_NAME, __scopeSelect); |
| return context.open && contentContext.position === "popper" ? jsx(PopperPrimitive.Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }) : null; |
| } |
| ); |
| SelectArrow.displayName = ARROW_NAME; |
| function shouldShowPlaceholder(value) { |
| return value === "" || value === void 0; |
| } |
| var BubbleSelect = React.forwardRef( |
| (props, forwardedRef) => { |
| const { value, ...selectProps } = props; |
| const ref = React.useRef(null); |
| const composedRefs = useComposedRefs(forwardedRef, ref); |
| const prevValue = usePrevious(value); |
| React.useEffect(() => { |
| const select = ref.current; |
| const selectProto = window.HTMLSelectElement.prototype; |
| const descriptor = Object.getOwnPropertyDescriptor( |
| selectProto, |
| "value" |
| ); |
| const setValue = descriptor.set; |
| if (prevValue !== value && setValue) { |
| const event = new Event("change", { bubbles: true }); |
| setValue.call(select, value); |
| select.dispatchEvent(event); |
| } |
| }, [prevValue, value]); |
| return jsx(VisuallyHidden, { asChild: true, children: jsx("select", { ...selectProps, ref: composedRefs, defaultValue: value }) }); |
| } |
| ); |
| BubbleSelect.displayName = "BubbleSelect"; |
| function useTypeaheadSearch(onSearchChange) { |
| const handleSearchChange = useCallbackRef(onSearchChange); |
| const searchRef = React.useRef(""); |
| const timerRef = React.useRef(0); |
| const handleTypeaheadSearch = React.useCallback( |
| (key) => { |
| const search = searchRef.current + key; |
| handleSearchChange(search); |
| (function updateSearch(value) { |
| searchRef.current = value; |
| window.clearTimeout(timerRef.current); |
| if (value !== "") timerRef.current = window.setTimeout(() => updateSearch(""), 1e3); |
| })(search); |
| }, |
| [handleSearchChange] |
| ); |
| const resetTypeahead = React.useCallback(() => { |
| searchRef.current = ""; |
| window.clearTimeout(timerRef.current); |
| }, []); |
| React.useEffect(() => { |
| return () => window.clearTimeout(timerRef.current); |
| }, []); |
| return [searchRef, handleTypeaheadSearch, resetTypeahead]; |
| } |
| function findNextItem(items, search, currentItem) { |
| const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]); |
| const normalizedSearch = isRepeated ? search[0] : search; |
| const currentItemIndex = currentItem ? items.indexOf(currentItem) : -1; |
| let wrappedItems = wrapArray(items, Math.max(currentItemIndex, 0)); |
| const excludeCurrentItem = normalizedSearch.length === 1; |
| if (excludeCurrentItem) wrappedItems = wrappedItems.filter((v) => v !== currentItem); |
| const nextItem = wrappedItems.find( |
| (item) => item.textValue.toLowerCase().startsWith(normalizedSearch.toLowerCase()) |
| ); |
| return nextItem !== currentItem ? nextItem : void 0; |
| } |
| function wrapArray(array, startIndex) { |
| return array.map((_, index) => array[(startIndex + index) % array.length]); |
| } |
| var Root2 = Select; |
| var Trigger = SelectTrigger; |
| var Value = SelectValue; |
| var Icon = SelectIcon; |
| var Portal = SelectPortal; |
| var Content2 = SelectContent; |
| var Viewport = SelectViewport; |
| var Group = SelectGroup; |
| var Label = SelectLabel; |
| var Item = SelectItem; |
| var ItemText = SelectItemText; |
| var ItemIndicator = SelectItemIndicator; |
| var ScrollUpButton = SelectScrollUpButton; |
| var ScrollDownButton = SelectScrollDownButton; |
| var Separator = SelectSeparator; |
| var Arrow2 = SelectArrow; |
| export { |
| Arrow2 as Arrow, |
| Content2 as Content, |
| Group, |
| Icon, |
| Item, |
| ItemIndicator, |
| ItemText, |
| Label, |
| Portal, |
| Root2 as Root, |
| ScrollDownButton, |
| ScrollUpButton, |
| Select, |
| SelectArrow, |
| SelectContent, |
| SelectGroup, |
| SelectIcon, |
| SelectItem, |
| SelectItemIndicator, |
| SelectItemText, |
| SelectLabel, |
| SelectPortal, |
| SelectScrollDownButton, |
| SelectScrollUpButton, |
| SelectSeparator, |
| SelectTrigger, |
| SelectValue, |
| SelectViewport, |
| Separator, |
| Trigger, |
| Value, |
| Viewport, |
| createSelectScope |
| }; |
| |
|
|