Spaces:
Runtime error
Runtime error
| ; | |
| "use client"; | |
| var __create = Object.create; | |
| var __defProp = Object.defineProperty; | |
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | |
| var __getOwnPropNames = Object.getOwnPropertyNames; | |
| var __getProtoOf = Object.getPrototypeOf; | |
| var __hasOwnProp = Object.prototype.hasOwnProperty; | |
| var __export = (target, all) => { | |
| for (var name in all) | |
| __defProp(target, name, { get: all[name], enumerable: true }); | |
| }; | |
| var __copyProps = (to, from, except, desc) => { | |
| if (from && typeof from === "object" || typeof from === "function") { | |
| for (let key of __getOwnPropNames(from)) | |
| if (!__hasOwnProp.call(to, key) && key !== except) | |
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | |
| } | |
| return to; | |
| }; | |
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | |
| // If the importer is in node compatibility mode or this is not an ESM | |
| // file that has been converted to a CommonJS file using a Babel- | |
| // compatible transform (i.e. "__esModule" has not been set), then set | |
| // "default" to the CommonJS "module.exports" for node compatibility. | |
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | |
| mod | |
| )); | |
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | |
| // src/index.ts | |
| var index_exports = {}; | |
| __export(index_exports, { | |
| Arrow: () => Arrow2, | |
| Content: () => Content2, | |
| Group: () => Group, | |
| Icon: () => Icon, | |
| Item: () => Item, | |
| ItemIndicator: () => ItemIndicator, | |
| ItemText: () => ItemText, | |
| Label: () => Label, | |
| Portal: () => Portal, | |
| Root: () => Root2, | |
| ScrollDownButton: () => ScrollDownButton, | |
| ScrollUpButton: () => ScrollUpButton, | |
| Select: () => Select, | |
| SelectArrow: () => SelectArrow, | |
| SelectContent: () => SelectContent, | |
| SelectGroup: () => SelectGroup, | |
| SelectIcon: () => SelectIcon, | |
| SelectItem: () => SelectItem, | |
| SelectItemIndicator: () => SelectItemIndicator, | |
| SelectItemText: () => SelectItemText, | |
| SelectLabel: () => SelectLabel, | |
| SelectPortal: () => SelectPortal, | |
| SelectScrollDownButton: () => SelectScrollDownButton, | |
| SelectScrollUpButton: () => SelectScrollUpButton, | |
| SelectSeparator: () => SelectSeparator, | |
| SelectTrigger: () => SelectTrigger, | |
| SelectValue: () => SelectValue, | |
| SelectViewport: () => SelectViewport, | |
| Separator: () => Separator, | |
| Trigger: () => Trigger, | |
| Value: () => Value, | |
| Viewport: () => Viewport, | |
| createSelectScope: () => createSelectScope | |
| }); | |
| module.exports = __toCommonJS(index_exports); | |
| // src/select.tsx | |
| var React = __toESM(require("react")); | |
| var ReactDOM = __toESM(require("react-dom")); | |
| var import_number = require("@radix-ui/number"); | |
| var import_primitive = require("@radix-ui/primitive"); | |
| var import_react_collection = require("@radix-ui/react-collection"); | |
| var import_react_compose_refs = require("@radix-ui/react-compose-refs"); | |
| var import_react_context = require("@radix-ui/react-context"); | |
| var import_react_direction = require("@radix-ui/react-direction"); | |
| var import_react_dismissable_layer = require("@radix-ui/react-dismissable-layer"); | |
| var import_react_focus_guards = require("@radix-ui/react-focus-guards"); | |
| var import_react_focus_scope = require("@radix-ui/react-focus-scope"); | |
| var import_react_id = require("@radix-ui/react-id"); | |
| var PopperPrimitive = __toESM(require("@radix-ui/react-popper")); | |
| var import_react_popper = require("@radix-ui/react-popper"); | |
| var import_react_portal = require("@radix-ui/react-portal"); | |
| var import_react_primitive = require("@radix-ui/react-primitive"); | |
| var import_react_slot = require("@radix-ui/react-slot"); | |
| var import_react_use_callback_ref = require("@radix-ui/react-use-callback-ref"); | |
| var import_react_use_controllable_state = require("@radix-ui/react-use-controllable-state"); | |
| var import_react_use_layout_effect = require("@radix-ui/react-use-layout-effect"); | |
| var import_react_use_previous = require("@radix-ui/react-use-previous"); | |
| var import_react_visually_hidden = require("@radix-ui/react-visually-hidden"); | |
| var import_aria_hidden = require("aria-hidden"); | |
| var import_react_remove_scroll = require("react-remove-scroll"); | |
| var import_jsx_runtime = require("react/jsx-runtime"); | |
| var OPEN_KEYS = [" ", "Enter", "ArrowUp", "ArrowDown"]; | |
| var SELECTION_KEYS = [" ", "Enter"]; | |
| var SELECT_NAME = "Select"; | |
| var [Collection, useCollection, createCollectionScope] = (0, import_react_collection.createCollection)(SELECT_NAME); | |
| var [createSelectContext, createSelectScope] = (0, import_react_context.createContextScope)(SELECT_NAME, [ | |
| createCollectionScope, | |
| import_react_popper.createPopperScope | |
| ]); | |
| var usePopperScope = (0, import_react_popper.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 = (0, import_react_direction.useDirection)(dir); | |
| const [open, setOpen] = (0, import_react_use_controllable_state.useControllableState)({ | |
| prop: openProp, | |
| defaultProp: defaultOpen ?? false, | |
| onChange: onOpenChange, | |
| caller: SELECT_NAME | |
| }); | |
| const [value, setValue] = (0, import_react_use_controllable_state.useControllableState)({ | |
| prop: valueProp, | |
| defaultProp: defaultValue, | |
| onChange: onValueChange, | |
| caller: SELECT_NAME | |
| }); | |
| const triggerPointerDownPosRef = React.useRef(null); | |
| const isFormControl = trigger ? form || !!trigger.closest("form") : true; | |
| const [nativeOptionsSet, setNativeOptionsSet] = React.useState(/* @__PURE__ */ new Set()); | |
| const nativeSelectKey = Array.from(nativeOptionsSet).map((option) => option.props.value).join(";"); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( | |
| SelectProvider, | |
| { | |
| required, | |
| scope: __scopeSelect, | |
| trigger, | |
| onTriggerChange: setTrigger, | |
| valueNode, | |
| onValueNodeChange: setValueNode, | |
| valueNodeHasChildren, | |
| onValueNodeHasChildrenChange: setValueNodeHasChildren, | |
| contentId: (0, import_react_id.useId)(), | |
| value, | |
| onValueChange: setValue, | |
| open, | |
| onOpenChange: setOpen, | |
| dir: direction, | |
| triggerPointerDownPosRef, | |
| disabled, | |
| children: [ | |
| /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Provider, { scope: __scopeSelect, children: /* @__PURE__ */ (0, import_jsx_runtime.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 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( | |
| SelectBubbleInput, | |
| { | |
| "aria-hidden": true, | |
| required, | |
| tabIndex: -1, | |
| name, | |
| autoComplete, | |
| value, | |
| onChange: (event) => setValue(event.target.value), | |
| disabled, | |
| form, | |
| children: [ | |
| value === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.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 = (0, import_react_compose_refs.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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Anchor, { asChild: true, ...popperScope, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.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: (0, import_primitive.composeEventHandlers)(triggerProps.onClick, (event) => { | |
| event.currentTarget.focus(); | |
| if (pointerTypeRef.current !== "mouse") { | |
| handleOpen(event); | |
| } | |
| }), | |
| onPointerDown: (0, import_primitive.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: (0, import_primitive.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 = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, context.onValueNodeChange); | |
| (0, import_react_use_layout_effect.useLayoutEffect)(() => { | |
| onValueNodeHasChildrenChange(hasChildren); | |
| }, [onValueNodeHasChildrenChange, hasChildren]); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.span, | |
| { | |
| ...valueProps, | |
| ref: composedRefs, | |
| style: { pointerEvents: "none" }, | |
| children: shouldShowPlaceholder(context.value) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: placeholder }) : children | |
| } | |
| ); | |
| } | |
| ); | |
| SelectValue.displayName = VALUE_NAME; | |
| var ICON_NAME = "SelectIcon"; | |
| var SelectIcon = React.forwardRef( | |
| (props, forwardedRef) => { | |
| const { __scopeSelect, children, ...iconProps } = props; | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.Primitive.span, { "aria-hidden": true, ...iconProps, ref: forwardedRef, children: children || "\u25BC" }); | |
| } | |
| ); | |
| SelectIcon.displayName = ICON_NAME; | |
| var PORTAL_NAME = "SelectPortal"; | |
| var SelectPortal = (props) => { | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_portal.Portal, { 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(); | |
| (0, import_react_use_layout_effect.useLayoutEffect)(() => { | |
| setFragment(new DocumentFragment()); | |
| }, []); | |
| if (!context.open) { | |
| const frag = fragment; | |
| return frag ? ReactDOM.createPortal( | |
| /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContentProvider, { scope: props.__scopeSelect, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Slot, { scope: props.__scopeSelect, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: props.children }) }) }), | |
| frag | |
| ) : null; | |
| } | |
| return /* @__PURE__ */ (0, import_jsx_runtime.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 Slot = (0, import_react_slot.createSlot)("SelectContent.RemoveScroll"); | |
| var SelectContentImpl = React.forwardRef( | |
| (props, forwardedRef) => { | |
| const { | |
| __scopeSelect, | |
| position = "item-aligned", | |
| onCloseAutoFocus, | |
| onEscapeKeyDown, | |
| onPointerDownOutside, | |
| // | |
| // PopperContent props | |
| 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 = (0, import_react_compose_refs.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 (0, import_aria_hidden.hideOthers)(content); | |
| }, [content]); | |
| (0, import_react_focus_guards.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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| SelectContentProvider, | |
| { | |
| scope: __scopeSelect, | |
| content, | |
| viewport, | |
| onViewportChange: setViewport, | |
| itemRefCallback, | |
| selectedItem, | |
| onItemLeave: handleItemLeave, | |
| itemTextRefCallback, | |
| focusSelectedItem, | |
| selectedItemText, | |
| position, | |
| isPositioned, | |
| searchRef, | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_remove_scroll.RemoveScroll, { as: Slot, allowPinchZoom: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_focus_scope.FocusScope, | |
| { | |
| asChild: true, | |
| trapped: context.open, | |
| onMountAutoFocus: (event) => { | |
| event.preventDefault(); | |
| }, | |
| onUnmountAutoFocus: (0, import_primitive.composeEventHandlers)(onCloseAutoFocus, (event) => { | |
| context.trigger?.focus({ preventScroll: true }); | |
| event.preventDefault(); | |
| }), | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_dismissable_layer.DismissableLayer, | |
| { | |
| asChild: true, | |
| disableOutsidePointerEvents: true, | |
| onEscapeKeyDown, | |
| onPointerDownOutside, | |
| onFocusOutside: (event) => event.preventDefault(), | |
| onDismiss: () => context.onOpenChange(false), | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.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: { | |
| // flex layout so we can place the scroll buttons properly | |
| display: "flex", | |
| flexDirection: "column", | |
| // reset the outline by default as the content MAY get focused | |
| outline: "none", | |
| ...contentProps.style | |
| }, | |
| onKeyDown: (0, import_primitive.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 = (0, import_react_compose_refs.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 = (0, import_number.clamp)(left, [ | |
| CONTENT_MARGIN, | |
| // Prevents the content from going off the starting edge of the | |
| // viewport. It may still go off the ending edge, but this can be | |
| // controlled by the user since they may want to manage overflow in a | |
| // specific way. | |
| // https://github.com/radix-ui/primitives/issues/2049 | |
| 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 = (0, import_number.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 + // viewport might have padding bottom, include it to avoid a scrollable viewport | |
| (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 + // viewport might have padding top, include it to avoid a scrollable viewport | |
| (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 | |
| ]); | |
| (0, import_react_use_layout_effect.useLayoutEffect)(() => position(), [position]); | |
| const [contentZIndex, setContentZIndex] = React.useState(); | |
| (0, import_react_use_layout_effect.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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| SelectViewportProvider, | |
| { | |
| scope: __scopeSelect, | |
| contentWrapper, | |
| shouldExpandOnScrollRef, | |
| onScrollButtonChange: handleScrollButtonChange, | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| "div", | |
| { | |
| ref: setContentWrapper, | |
| style: { | |
| display: "flex", | |
| flexDirection: "column", | |
| position: "fixed", | |
| zIndex: contentZIndex | |
| }, | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.div, | |
| { | |
| ...popperProps, | |
| ref: composedRefs, | |
| style: { | |
| // When we get the height of the content, it includes borders. If we were to set | |
| // the height without having `boxSizing: 'border-box'` it would be too big. | |
| boxSizing: "border-box", | |
| // We need to ensure the content doesn't get taller than the wrapper | |
| 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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| PopperPrimitive.Content, | |
| { | |
| ...popperScope, | |
| ...popperProps, | |
| ref: forwardedRef, | |
| align, | |
| collisionPadding, | |
| style: { | |
| // Ensure border-box for floating-ui calculations | |
| boxSizing: "border-box", | |
| ...popperProps.style, | |
| // re-namespace exposed content custom properties | |
| ...{ | |
| "--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 = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, contentContext.onViewportChange); | |
| const prevScrollTopRef = React.useRef(0); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | |
| /* @__PURE__ */ (0, import_jsx_runtime.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 | |
| } | |
| ), | |
| /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Slot, { scope: __scopeSelect, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.div, | |
| { | |
| "data-radix-select-viewport": "", | |
| role: "presentation", | |
| ...viewportProps, | |
| ref: composedRefs, | |
| style: { | |
| // we use position: 'relative' here on the `viewport` so that when we call | |
| // `selectedItem.offsetTop` in calculations, the offset is relative to the viewport | |
| // (independent of the scrollUpButton). | |
| position: "relative", | |
| flex: 1, | |
| // Viewport should only be scrollable in the vertical direction. | |
| // This won't work in vertical writing modes, so we'll need to | |
| // revisit this if/when that is supported | |
| // https://developer.chrome.com/blog/vertical-form-controls | |
| overflow: "hidden auto", | |
| ...viewportProps.style | |
| }, | |
| onScroll: (0, import_primitive.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 = (0, import_react_id.useId)(); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectGroupContextProvider, { scope: __scopeSelect, id: groupId, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.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 = (0, import_react_compose_refs.useComposedRefs)( | |
| forwardedRef, | |
| (node) => contentContext.itemRefCallback?.(node, value, disabled) | |
| ); | |
| const textId = (0, import_react_id.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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| SelectItemContextProvider, | |
| { | |
| scope: __scopeSelect, | |
| value, | |
| disabled, | |
| textId, | |
| isSelected, | |
| onItemTextChange: React.useCallback((node) => { | |
| setTextValue((prevTextValue) => prevTextValue || (node?.textContent ?? "").trim()); | |
| }, []), | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| Collection.ItemSlot, | |
| { | |
| scope: __scopeSelect, | |
| value, | |
| disabled, | |
| textValue, | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.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: (0, import_primitive.composeEventHandlers)(itemProps.onFocus, () => setIsFocused(true)), | |
| onBlur: (0, import_primitive.composeEventHandlers)(itemProps.onBlur, () => setIsFocused(false)), | |
| onClick: (0, import_primitive.composeEventHandlers)(itemProps.onClick, () => { | |
| if (pointerTypeRef.current !== "mouse") handleSelect(); | |
| }), | |
| onPointerUp: (0, import_primitive.composeEventHandlers)(itemProps.onPointerUp, () => { | |
| if (pointerTypeRef.current === "mouse") handleSelect(); | |
| }), | |
| onPointerDown: (0, import_primitive.composeEventHandlers)(itemProps.onPointerDown, (event) => { | |
| pointerTypeRef.current = event.pointerType; | |
| }), | |
| onPointerMove: (0, import_primitive.composeEventHandlers)(itemProps.onPointerMove, (event) => { | |
| pointerTypeRef.current = event.pointerType; | |
| if (disabled) { | |
| contentContext.onItemLeave?.(); | |
| } else if (pointerTypeRef.current === "mouse") { | |
| event.currentTarget.focus({ preventScroll: true }); | |
| } | |
| }), | |
| onPointerLeave: (0, import_primitive.composeEventHandlers)(itemProps.onPointerLeave, (event) => { | |
| if (event.currentTarget === document.activeElement) { | |
| contentContext.onItemLeave?.(); | |
| } | |
| }), | |
| onKeyDown: (0, import_primitive.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 = (0, import_react_compose_refs.useComposedRefs)( | |
| forwardedRef, | |
| (node) => setItemTextNode(node), | |
| itemContext.onItemTextChange, | |
| (node) => contentContext.itemTextRefCallback?.(node, itemContext.value, itemContext.disabled) | |
| ); | |
| const textContent = itemTextNode?.textContent; | |
| const nativeOption = React.useMemo( | |
| () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: itemContext.value, disabled: itemContext.disabled, children: textContent }, itemContext.value), | |
| [itemContext.disabled, itemContext.value, textContent] | |
| ); | |
| const { onNativeOptionAdd, onNativeOptionRemove } = nativeOptionsContext; | |
| (0, import_react_use_layout_effect.useLayoutEffect)(() => { | |
| onNativeOptionAdd(nativeOption); | |
| return () => onNativeOptionRemove(nativeOption); | |
| }, [onNativeOptionAdd, onNativeOptionRemove, nativeOption]); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | |
| /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.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 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.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 = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, viewportContext.onScrollButtonChange); | |
| (0, import_react_use_layout_effect.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 ? /* @__PURE__ */ (0, import_jsx_runtime.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 = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, viewportContext.onScrollButtonChange); | |
| (0, import_react_use_layout_effect.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 ? /* @__PURE__ */ (0, import_jsx_runtime.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]); | |
| (0, import_react_use_layout_effect.useLayoutEffect)(() => { | |
| const activeItem = getItems().find((item) => item.ref.current === document.activeElement); | |
| activeItem?.ref.current?.scrollIntoView({ block: "nearest" }); | |
| }, [getItems]); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.div, | |
| { | |
| "aria-hidden": true, | |
| ...scrollIndicatorProps, | |
| ref: forwardedRef, | |
| style: { flexShrink: 0, ...scrollIndicatorProps.style }, | |
| onPointerDown: (0, import_primitive.composeEventHandlers)(scrollIndicatorProps.onPointerDown, () => { | |
| if (autoScrollTimerRef.current === null) { | |
| autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50); | |
| } | |
| }), | |
| onPointerMove: (0, import_primitive.composeEventHandlers)(scrollIndicatorProps.onPointerMove, () => { | |
| contentContext.onItemLeave?.(); | |
| if (autoScrollTimerRef.current === null) { | |
| autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50); | |
| } | |
| }), | |
| onPointerLeave: (0, import_primitive.composeEventHandlers)(scrollIndicatorProps.onPointerLeave, () => { | |
| clearAutoScrollTimer(); | |
| }) | |
| } | |
| ); | |
| }); | |
| var SEPARATOR_NAME = "SelectSeparator"; | |
| var SelectSeparator = React.forwardRef( | |
| (props, forwardedRef) => { | |
| const { __scopeSelect, ...separatorProps } = props; | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.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" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }) : null; | |
| } | |
| ); | |
| SelectArrow.displayName = ARROW_NAME; | |
| var BUBBLE_INPUT_NAME = "SelectBubbleInput"; | |
| var SelectBubbleInput = React.forwardRef( | |
| ({ __scopeSelect, value, ...props }, forwardedRef) => { | |
| const ref = React.useRef(null); | |
| const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref); | |
| const prevValue = (0, import_react_use_previous.usePrevious)(value); | |
| React.useEffect(() => { | |
| const select = ref.current; | |
| if (!select) return; | |
| 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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.select, | |
| { | |
| ...props, | |
| style: { ...import_react_visually_hidden.VISUALLY_HIDDEN_STYLES, ...props.style }, | |
| ref: composedRefs, | |
| defaultValue: value | |
| } | |
| ); | |
| } | |
| ); | |
| SelectBubbleInput.displayName = BUBBLE_INPUT_NAME; | |
| function shouldShowPlaceholder(value) { | |
| return value === "" || value === void 0; | |
| } | |
| function useTypeaheadSearch(onSearchChange) { | |
| const handleSearchChange = (0, import_react_use_callback_ref.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; | |
| //# sourceMappingURL=index.js.map | |