@lobehub/ui 5.10.5 → 5.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/Menu/baseItem.d.mts +32 -4
- package/es/base-ui/ContextMenu/renderItems.mjs +32 -22
- package/es/base-ui/ContextMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/DropdownMenu/renderItems.mjs +29 -18
- package/es/base-ui/DropdownMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/ScrollArea/atoms.d.mts +9 -2
- package/es/base-ui/ScrollArea/atoms.mjs +8 -1
- package/es/base-ui/ScrollArea/atoms.mjs.map +1 -1
- package/es/base-ui/ScrollArea/style.mjs +167 -5
- package/es/base-ui/ScrollArea/style.mjs.map +1 -1
- package/es/base-ui/ScrollArea/type.d.mts +10 -2
- package/es/base-ui/Select/Select.mjs +67 -435
- package/es/base-ui/Select/Select.mjs.map +1 -1
- package/es/base-ui/Select/helpers.mjs +32 -0
- package/es/base-ui/Select/helpers.mjs.map +1 -0
- package/es/base-ui/Select/hooks.mjs +293 -0
- package/es/base-ui/Select/hooks.mjs.map +1 -0
- package/es/base-ui/Select/parts.mjs +136 -0
- package/es/base-ui/Select/parts.mjs.map +1 -0
- package/es/base-ui/Select/renderOptions.mjs +52 -0
- package/es/base-ui/Select/renderOptions.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.mjs","names":["Select","styles","menuStyles","BaseSelect"],"sources":["../../../src/base-ui/Select/Select.tsx"],"sourcesContent":["'use client';\n\nimport { Select as BaseSelect } from '@base-ui/react/select';\nimport { cx, useThemeMode } from 'antd-style';\nimport { Check, ChevronDown, Loader2, X } from 'lucide-react';\nimport {\n type ChangeEvent,\n type HTMLAttributes,\n type KeyboardEvent,\n type MouseEvent,\n type MutableRefObject,\n type Ref,\n} from 'react';\nimport { isValidElement, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Virtualizer } from 'virtua';\n\nimport Icon from '@/Icon';\nimport { styles as menuStyles } from '@/Menu/sharedStyle';\nimport { useAppElement } from '@/ThemeProvider';\n\nimport { styles, triggerVariants } from './style';\nimport {\n type SelectOption,\n type SelectOptionGroup,\n type SelectOptions,\n type SelectProps,\n type SelectRootChangeEventDetails,\n} from './type';\n\nconst isGroupOption = <Value,>(\n option: SelectOption<Value> | SelectOptionGroup<Value>,\n): option is SelectOptionGroup<Value> => Boolean((option as SelectOptionGroup<Value>).options);\n\nconst getOptionSearchText = <Value,>(option: SelectOption<Value>) => {\n if (typeof option.label === 'string' || typeof option.label === 'number') {\n return String(option.label);\n }\n if (typeof option.value === 'string' || typeof option.value === 'number') {\n return String(option.value);\n }\n if (option.title) return option.title;\n return '';\n};\n\nconst escapeRegExp = (value: string) => value.replaceAll(/[$()*+.?[\\\\\\]^{|}]/g, '\\\\$&');\n\nconst splitBySeparators = (value: string, separators?: string[]) => {\n if (!separators || separators.length === 0) return [value];\n const pattern = separators.map(escapeRegExp).join('|');\n return value.split(new RegExp(pattern, 'g'));\n};\n\nconst countVirtualItems = (items: SelectOptions) =>\n items.reduce((count, item) => {\n if (isGroupOption(item)) {\n return count + item.options.length + 1;\n }\n return count + 1;\n }, 0);\n\nconst isValueEmpty = (value: unknown) => value === null || value === undefined || value === '';\n\nconst Select = memo<SelectProps<any>>(\n ({\n allowClear,\n autoFocus,\n className,\n classNames,\n defaultOpen,\n defaultValue,\n disabled,\n id,\n labelRender,\n listHeight = 512,\n listItemHeight,\n loading,\n mode,\n name,\n onChange,\n onOpenChange,\n onSelect,\n open,\n optionRender,\n options,\n placeholder,\n popupClassName,\n popupMatchSelectWidth,\n prefix,\n readOnly,\n required,\n behaviorVariant = 'default',\n selectedIndicatorVariant = 'check',\n shadow,\n showSearch,\n size = 'middle',\n style,\n suffixIcon,\n suffixIconProps,\n tokenSeparators,\n value,\n variant,\n virtual,\n }) => {\n const { isDarkMode } = useThemeMode();\n const resolvedVariant = variant ?? (isDarkMode ? 'filled' : 'outlined');\n const isMultiple = mode === 'multiple' || mode === 'tags';\n const isItemAligned = behaviorVariant === 'item-aligned';\n\n const [uncontrolledValue, setUncontrolledValue] = useState<any>(() => {\n if (defaultValue !== undefined) return defaultValue;\n return isMultiple ? [] : null;\n });\n\n const normalizeValue = useCallback(\n (nextValue: any) => {\n if (isMultiple) {\n if (Array.isArray(nextValue)) return nextValue;\n if (nextValue === null || nextValue === undefined) return [];\n return [nextValue];\n }\n if (Array.isArray(nextValue)) return nextValue[0] ?? null;\n return nextValue === undefined ? null : nextValue;\n },\n [isMultiple],\n );\n\n const mergedValue = value !== undefined ? value : uncontrolledValue;\n const normalizedValue = useMemo(\n () => normalizeValue(mergedValue),\n [mergedValue, normalizeValue],\n );\n const valueArray = useMemo(\n () =>\n isMultiple\n ? (normalizedValue as any[])\n : isValueEmpty(normalizedValue)\n ? []\n : [normalizedValue],\n [isMultiple, normalizedValue],\n );\n\n const [extraOptions, setExtraOptions] = useState<SelectOption<any>[]>([]);\n\n useEffect(() => {\n if (mode !== 'tags' && extraOptions.length > 0) {\n setExtraOptions([]);\n }\n }, [mode, extraOptions.length]);\n\n const { resolvedOptions, optionMap } = useMemo(() => {\n const baseOptions = options ?? [];\n const optionValueMap = new Map<any, SelectOption<any>>();\n\n const addOption = (item: SelectOption<any>) => {\n if (!optionValueMap.has(item.value)) {\n optionValueMap.set(item.value, item);\n }\n };\n\n const walkOptions = (items: SelectOptions) => {\n items.forEach((item) => {\n if (isGroupOption(item)) {\n item.options.forEach(addOption);\n } else {\n addOption(item);\n }\n });\n };\n\n walkOptions(baseOptions);\n\n const filteredExtraOptions = extraOptions.filter((item) => !optionValueMap.has(item.value));\n filteredExtraOptions.forEach(addOption);\n\n const mergedOptions: SelectOptions = [...baseOptions, ...filteredExtraOptions];\n\n const missingValueOptions: SelectOption<any>[] = valueArray\n .filter((val) => !optionValueMap.has(val))\n .map((val) => ({\n label: String(val),\n value: val,\n }));\n missingValueOptions.forEach(addOption);\n\n return {\n optionMap: optionValueMap,\n resolvedOptions: missingValueOptions.length\n ? [...mergedOptions, ...missingValueOptions]\n : mergedOptions,\n };\n }, [extraOptions, options, valueArray]);\n\n const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));\n\n useEffect(() => {\n if (open !== undefined) {\n setUncontrolledOpen(open);\n }\n }, [open]);\n\n const mergedOpen = open ?? uncontrolledOpen;\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean, eventDetails?: SelectRootChangeEventDetails) => {\n onOpenChange?.(nextOpen, eventDetails);\n if (open === undefined) {\n setUncontrolledOpen(nextOpen);\n }\n },\n [onOpenChange, open],\n );\n\n const [searchValue, setSearchValue] = useState('');\n const shouldShowSearch = Boolean(showSearch || mode === 'tags');\n\n useEffect(() => {\n if (!mergedOpen) setSearchValue('');\n }, [mergedOpen]);\n\n const getOption = useCallback(\n (optionValue: any): SelectOption<any> => {\n const matched = optionMap.get(optionValue);\n if (matched) return matched;\n if (optionValue && typeof optionValue === 'object' && 'label' in optionValue) {\n return {\n label: (optionValue as any).label,\n value: optionValue,\n };\n }\n return {\n label: String(optionValue),\n value: optionValue,\n };\n },\n [optionMap],\n );\n\n const previousValueRef = useRef<any>(normalizedValue);\n\n useEffect(() => {\n previousValueRef.current = normalizedValue;\n }, [normalizedValue]);\n\n const handleValueChange = useCallback(\n (nextValue: any) => {\n const normalizedNextValue = normalizeValue(nextValue);\n const previousValue = previousValueRef.current;\n\n if (isMultiple) {\n const prevValues = Array.isArray(previousValue) ? previousValue : [];\n const nextValues = Array.isArray(normalizedNextValue) ? normalizedNextValue : [];\n const addedValues = nextValues.filter(\n (val) => !prevValues.some((prev) => Object.is(prev, val)),\n );\n\n addedValues.forEach((val) => {\n onSelect?.(val, getOption(val));\n });\n\n if (value === undefined) {\n setUncontrolledValue(nextValues);\n }\n onChange?.(\n nextValues,\n nextValues.map((val) => getOption(val)),\n );\n } else {\n if (\n !isValueEmpty(normalizedNextValue) &&\n !Object.is(previousValue, normalizedNextValue)\n ) {\n onSelect?.(normalizedNextValue, getOption(normalizedNextValue));\n }\n if (value === undefined) {\n setUncontrolledValue(normalizedNextValue);\n }\n onChange?.(\n normalizedNextValue,\n isValueEmpty(normalizedNextValue) ? undefined : getOption(normalizedNextValue),\n );\n }\n\n previousValueRef.current = normalizedNextValue;\n },\n [getOption, isMultiple, normalizeValue, onChange, onSelect, value],\n );\n\n const appendTagValues = useCallback(\n (rawValues: string[]) => {\n const valuesToAdd = rawValues.map((val) => val.trim()).filter(Boolean);\n if (!valuesToAdd.length) return;\n\n const nextValues = [...valueArray];\n const newOptionValues = valuesToAdd.filter((val) => !optionMap.has(val));\n\n if (newOptionValues.length > 0) {\n setExtraOptions((prev) => {\n const existingValues = new Set(prev.map((item) => item.value));\n const merged = [...prev];\n newOptionValues.forEach((val) => {\n if (!existingValues.has(val)) {\n merged.push({ label: val, value: val });\n }\n });\n return merged;\n });\n }\n\n valuesToAdd.forEach((val) => {\n if (!nextValues.some((item) => Object.is(item, val))) {\n nextValues.push(val);\n }\n });\n\n if (nextValues.length !== valueArray.length) {\n handleValueChange(nextValues);\n }\n },\n [handleValueChange, optionMap, valueArray],\n );\n\n const handleSearchChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const nextValue = event.target.value;\n if (mode === 'tags') {\n const parts = splitBySeparators(nextValue, tokenSeparators);\n if (parts.length > 1) {\n const pending = parts.pop() ?? '';\n appendTagValues(parts.filter(Boolean));\n setSearchValue(pending);\n return;\n }\n }\n setSearchValue(nextValue);\n },\n [appendTagValues, mode, tokenSeparators],\n );\n\n const handleSearchKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n event.stopPropagation();\n\n if (event.key === 'Escape') {\n handleOpenChange(false);\n return;\n }\n\n if (mode !== 'tags') return;\n\n if (event.key === 'Enter') {\n event.preventDefault();\n event.stopPropagation();\n appendTagValues([searchValue]);\n setSearchValue('');\n return;\n }\n\n if (tokenSeparators?.includes(event.key)) {\n event.preventDefault();\n event.stopPropagation();\n appendTagValues([searchValue]);\n setSearchValue('');\n }\n },\n [appendTagValues, handleOpenChange, mode, searchValue, tokenSeparators],\n );\n\n const filteredOptions = useMemo(() => {\n if (!shouldShowSearch || !searchValue.trim()) return resolvedOptions;\n const query = searchValue.trim().toLowerCase();\n\n const filterItems = (items: SelectOptions): SelectOptions => {\n const filtered = items\n .map((item) => {\n if (isGroupOption(item)) {\n const groupItems = item.options.filter((option) =>\n getOptionSearchText(option).toLowerCase().includes(query),\n );\n if (!groupItems.length) return null;\n return { ...item, options: groupItems };\n }\n return getOptionSearchText(item).toLowerCase().includes(query) ? item : null;\n })\n .filter(Boolean) as SelectOptions;\n\n return filtered;\n };\n\n return filterItems(resolvedOptions);\n }, [resolvedOptions, searchValue, shouldShowSearch]);\n\n const renderValue = useCallback(\n (currentValue: any) => {\n const resolved = normalizeValue(currentValue);\n const placeholderNode =\n placeholder === undefined ? null : (\n <span className={styles.valueText}>{placeholder}</span>\n );\n\n if (isMultiple) {\n const values = Array.isArray(resolved) ? resolved : [];\n if (values.length === 0) return placeholderNode;\n return (\n <span className={styles.tags}>\n {values.map((val, index) => {\n const option = getOption(val);\n const content = labelRender ? labelRender(option) : (option.label ?? String(val));\n return (\n <span className={styles.tag} key={`${String(val)}-${index}`}>\n {content}\n </span>\n );\n })}\n </span>\n );\n }\n\n if (isValueEmpty(resolved)) return placeholderNode;\n const option = getOption(resolved);\n const content = labelRender ? labelRender(option) : (option.label ?? String(resolved));\n return <span className={styles.valueText}>{content}</span>;\n },\n [getOption, isMultiple, labelRender, normalizeValue, placeholder],\n );\n\n const hasValue = isMultiple ? valueArray.length > 0 : !isValueEmpty(normalizedValue);\n const showClear = Boolean(allowClear && hasValue && !disabled && !readOnly);\n\n const handleClear = useCallback(\n (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n handleValueChange(isMultiple ? [] : null);\n },\n [handleValueChange, isMultiple],\n );\n\n const prefixNode = useMemo(() => {\n if (prefix === undefined || prefix === null) return null;\n if (isValidElement(prefix) || typeof prefix === 'string' || typeof prefix === 'number') {\n return prefix;\n }\n return <Icon icon={prefix as any} size={'small'} />;\n }, [prefix]);\n\n const suffixIconNode = useMemo(() => {\n if (loading) {\n return <Icon spin icon={Loader2} size={'small'} />;\n }\n if (suffixIcon === null) return null;\n if (\n isValidElement(suffixIcon) ||\n typeof suffixIcon === 'string' ||\n typeof suffixIcon === 'number'\n ) {\n return suffixIcon;\n }\n return (\n <Icon\n icon={(suffixIcon as any) || ChevronDown}\n size={'small'}\n {...suffixIconProps}\n style={{\n pointerEvents: 'none',\n ...suffixIconProps?.style,\n }}\n />\n );\n }, [loading, suffixIcon, suffixIconProps]);\n\n const popupStyle = useMemo(() => {\n const maxHeight = isItemAligned ? '80vh' : `${listHeight}px`;\n const baseStyle: React.CSSProperties = {\n maxHeight,\n maxWidth: 'var(--available-width)',\n minWidth: 'var(--anchor-width)',\n ['--lobe-select-popup-max-height' as any]: maxHeight,\n };\n\n if (popupMatchSelectWidth === undefined || popupMatchSelectWidth === true) {\n return baseStyle;\n }\n if (typeof popupMatchSelectWidth === 'number') {\n return {\n ...baseStyle,\n minWidth: popupMatchSelectWidth,\n width: popupMatchSelectWidth,\n };\n }\n return {\n ...baseStyle,\n minWidth: 'max-content',\n };\n }, [isItemAligned, listHeight, popupMatchSelectWidth]);\n\n const triggerClassName = cx(\n triggerVariants({ shadow, size, variant: resolvedVariant }),\n className,\n classNames?.root,\n classNames?.trigger,\n );\n\n const listRef = useRef<HTMLDivElement | null>(null);\n const pointerScrollRef = useRef(false);\n const pointerScrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const renderVirtualItem = useCallback((props: HTMLAttributes<HTMLDivElement>) => {\n const { ref, ...rest } = props as HTMLAttributes<HTMLDivElement> & {\n ref?: Ref<HTMLDivElement>;\n };\n\n return (\n <div\n {...rest}\n ref={(node) => {\n if (node) {\n node.scrollIntoView = (...args) => {\n if (!pointerScrollRef.current) {\n HTMLElement.prototype.scrollIntoView.call(node, ...args);\n }\n };\n }\n\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref && 'current' in ref) {\n (ref as MutableRefObject<HTMLDivElement | null>).current = node;\n }\n }}\n />\n );\n }, []);\n\n const markPointerScroll = useCallback(() => {\n pointerScrollRef.current = true;\n if (pointerScrollTimeoutRef.current) {\n clearTimeout(pointerScrollTimeoutRef.current);\n }\n pointerScrollTimeoutRef.current = setTimeout(() => {\n pointerScrollRef.current = false;\n }, 120);\n }, []);\n\n const handleListScroll = useCallback(() => {\n if (!virtual || !pointerScrollRef.current) return;\n const listElement = listRef.current;\n const activeElement = document.activeElement;\n if (listElement && activeElement && listElement.contains(activeElement)) {\n listElement.focus({ preventScroll: true });\n }\n }, [virtual]);\n\n useEffect(() => {\n return () => {\n if (pointerScrollTimeoutRef.current) {\n clearTimeout(pointerScrollTimeoutRef.current);\n }\n };\n }, []);\n const virtualListStyle = useMemo(() => {\n if (!virtual) return undefined;\n const rowCount = countVirtualItems(filteredOptions);\n const maxVisibleRows = 6;\n const estimatedRowHeight =\n listItemHeight ?? (size === 'large' ? 40 : size === 'small' ? 28 : 32);\n const visibleRows = Math.min(Math.max(rowCount, 1), maxVisibleRows);\n const estimatedHeight = visibleRows * estimatedRowHeight + 8;\n\n return {\n height: `min(${estimatedHeight}px, var(--lobe-select-available-height, var(--available-height)))`,\n };\n }, [filteredOptions, listItemHeight, size, virtual]);\n\n const keepMountedIndices = useMemo(() => {\n if (!virtual || valueArray.length === 0) return undefined;\n const selectedSet = new Set(valueArray);\n const indices: number[] = [];\n let index = 0;\n\n filteredOptions.forEach((item) => {\n if (isGroupOption(item)) {\n if (item.options.some((option) => selectedSet.has(option.value))) {\n indices.push(index);\n }\n index += 1;\n return;\n }\n\n if (selectedSet.has(item.value)) {\n indices.push(index);\n }\n index += 1;\n });\n\n return indices.length ? indices : undefined;\n }, [filteredOptions, valueArray, virtual]);\n\n const itemTextClassName = cx(\n optionRender ? menuStyles.itemContent : menuStyles.label,\n styles.itemText,\n classNames?.itemText,\n );\n\n const isBoldIndicator = selectedIndicatorVariant === 'bold';\n let optionIndex = 0;\n const renderOptions = (items: SelectOptions) =>\n items.map((item, index) => {\n if (isGroupOption(item)) {\n return (\n <BaseSelect.Group\n className={cx(styles.group, classNames?.group)}\n key={`group-${index}`}\n >\n <BaseSelect.GroupLabel\n className={cx(menuStyles.groupLabel, styles.groupLabel, classNames?.groupLabel)}\n >\n {item.label}\n </BaseSelect.GroupLabel>\n {item.options.map((option) => {\n const currentIndex = optionIndex++;\n return (\n <BaseSelect.Item\n disabled={option.disabled}\n key={`${String(option.value)}-${currentIndex}`}\n label={getOptionSearchText(option)}\n render={virtual ? renderVirtualItem : undefined}\n value={option.value}\n className={cx(\n menuStyles.item,\n styles.item,\n isBoldIndicator && styles.itemBoldSelected,\n classNames?.item,\n classNames?.option,\n option.className,\n )}\n style={{\n minHeight: listItemHeight,\n ...option.style,\n }}\n >\n <BaseSelect.ItemText className={itemTextClassName}>\n {optionRender ? optionRender(option, { index: currentIndex }) : option.label}\n </BaseSelect.ItemText>\n {!isBoldIndicator && (\n <BaseSelect.ItemIndicator\n className={cx(styles.itemIndicator, classNames?.itemIndicator)}\n >\n <Icon icon={Check} size={'small'} />\n </BaseSelect.ItemIndicator>\n )}\n </BaseSelect.Item>\n );\n })}\n </BaseSelect.Group>\n );\n }\n\n const currentIndex = optionIndex++;\n return (\n <BaseSelect.Item\n disabled={item.disabled}\n key={`${String(item.value)}-${currentIndex}`}\n label={getOptionSearchText(item)}\n render={virtual ? renderVirtualItem : undefined}\n value={item.value}\n className={cx(\n menuStyles.item,\n styles.item,\n isBoldIndicator && styles.itemBoldSelected,\n classNames?.item,\n classNames?.option,\n item.className,\n )}\n style={{\n minHeight: listItemHeight,\n ...item.style,\n }}\n >\n <BaseSelect.ItemText className={itemTextClassName}>\n {optionRender ? optionRender(item, { index: currentIndex }) : item.label}\n </BaseSelect.ItemText>\n {!isBoldIndicator && (\n <BaseSelect.ItemIndicator\n className={cx(styles.itemIndicator, classNames?.itemIndicator)}\n >\n <Icon icon={Check} size={'small'} />\n </BaseSelect.ItemIndicator>\n )}\n </BaseSelect.Item>\n );\n });\n\n const appElement = useAppElement();\n // `appElement` is the ThemeProvider wrapper div, which uses\n // `display: contents` so it has no layout box. `@base-ui/react/select`\n // fails to mount its Popup into a `display: contents` container in\n // certain hosts (editor/chat inputs with focus traps). Fall back to\n // `document.body` in that case; keep the original behavior when the\n // wrapper has a real layout (older themes, SSR snapshot, etc.).\n const portalContainer = useMemo(() => {\n if (typeof window === 'undefined') return appElement;\n if (!(appElement instanceof HTMLElement)) return undefined;\n const display = window.getComputedStyle(appElement).display;\n return display === 'contents' ? document.body : appElement;\n }, [appElement]);\n return (\n <BaseSelect.Root\n disabled={disabled}\n id={id}\n modal={isItemAligned}\n multiple={isMultiple}\n name={name}\n open={mergedOpen}\n readOnly={readOnly}\n required={required}\n value={normalizedValue}\n onOpenChange={handleOpenChange}\n onValueChange={handleValueChange}\n >\n <BaseSelect.Trigger\n autoFocus={autoFocus}\n className={triggerClassName}\n disabled={disabled}\n style={style}\n >\n {prefixNode !== null && prefixNode !== undefined && (\n <span className={cx(styles.prefix, classNames?.prefix)}>{prefixNode}</span>\n )}\n <BaseSelect.Value className={cx(styles.value, classNames?.value)}>\n {renderValue}\n </BaseSelect.Value>\n <span className={cx(styles.suffix, classNames?.suffix)}>\n {showClear && (\n <span\n className={cx(styles.clear, classNames?.clear)}\n data-role=\"lobe-select-clear\"\n onClick={handleClear}\n >\n <Icon icon={X} size={'small'} />\n </span>\n )}\n {suffixIconNode !== null && suffixIconNode !== undefined && (\n <BaseSelect.Icon className={cx(styles.icon, classNames?.icon)}>\n {suffixIconNode}\n </BaseSelect.Icon>\n )}\n </span>\n </BaseSelect.Trigger>\n\n <BaseSelect.Portal container={portalContainer}>\n <BaseSelect.Positioner\n align=\"start\"\n alignItemWithTrigger={isItemAligned}\n className={styles.positioner}\n side=\"bottom\"\n sideOffset={6}\n >\n <BaseSelect.Popup\n style={popupStyle}\n className={cx(\n menuStyles.popup,\n styles.popup,\n popupClassName,\n classNames?.popup,\n classNames?.dropdown,\n )}\n >\n {shouldShowSearch && (\n <div className={cx(styles.search, classNames?.search)}>\n <input\n className={styles.searchInput}\n placeholder={typeof placeholder === 'string' ? placeholder : undefined}\n value={searchValue}\n onChange={handleSearchChange}\n onKeyDown={handleSearchKeyDown}\n />\n </div>\n )}\n {(() => {\n const content =\n filteredOptions.length > 0 ? (\n renderOptions(filteredOptions)\n ) : (\n <div\n className={cx(\n menuStyles.item,\n menuStyles.empty,\n styles.empty,\n classNames?.empty,\n )}\n >\n No data\n </div>\n );\n\n if (!virtual || filteredOptions.length === 0) {\n return (\n <BaseSelect.List\n className={cx(styles.list, classNames?.list)}\n data-virtual={virtual || undefined}\n >\n {content}\n </BaseSelect.List>\n );\n }\n\n return (\n <BaseSelect.List\n className={cx(styles.list, classNames?.list)}\n data-virtual={virtual || undefined}\n ref={listRef}\n style={virtualListStyle}\n tabIndex={virtual ? -1 : undefined}\n onPointerDown={virtual ? markPointerScroll : undefined}\n onScroll={virtual ? handleListScroll : undefined}\n onTouchMove={virtual ? markPointerScroll : undefined}\n onWheel={virtual ? markPointerScroll : undefined}\n >\n <Virtualizer itemSize={listItemHeight} keepMounted={keepMountedIndices}>\n {content}\n </Virtualizer>\n </BaseSelect.List>\n );\n })()}\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n );\n },\n);\n\nSelect.displayName = 'Select';\n\nexport default Select;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Select.mjs","names":["Select","menuStyles","styles","BaseSelect"],"sources":["../../../src/base-ui/Select/Select.tsx"],"sourcesContent":["'use client';\n\nimport { Select as BaseSelect } from '@base-ui/react/select';\nimport { cx, useThemeMode } from 'antd-style';\nimport { type MouseEvent } from 'react';\nimport { memo, useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { styles as menuStyles } from '@/Menu/sharedStyle';\n\nimport { isValueEmpty } from './helpers';\nimport {\n usePortalContainer,\n useSelectOpen,\n useSelectSearch,\n useSelectValue,\n useSelectVirtual,\n} from './hooks';\nimport {\n createTriggerValueRenderer,\n EmptyContent,\n resolveIconNode,\n resolveSuffixIcon,\n SelectListSection,\n SelectSearchInput,\n SelectTriggerSuffix,\n} from './parts';\nimport { renderOptions } from './renderOptions';\nimport { styles, triggerVariants } from './style';\nimport { type SelectOption, type SelectProps } from './type';\n\nconst Select = memo<SelectProps<any>>(\n ({\n allowClear,\n autoFocus,\n className,\n classNames,\n defaultOpen,\n defaultValue,\n disabled,\n id,\n labelRender,\n listHeight = 512,\n listItemHeight,\n loading,\n mode,\n name,\n onChange,\n onOpenChange,\n onSelect,\n open,\n optionRender,\n options,\n placeholder,\n popupClassName,\n popupMatchSelectWidth,\n prefix,\n readOnly,\n required,\n behaviorVariant = 'default',\n selectedIndicatorVariant = 'check',\n shadow,\n showSearch,\n size = 'middle',\n style,\n suffixIcon,\n suffixIconProps,\n tokenSeparators,\n value,\n variant,\n virtual,\n }) => {\n const { isDarkMode } = useThemeMode();\n const resolvedVariant = variant ?? (isDarkMode ? 'filled' : 'outlined');\n const isMultiple = mode === 'multiple' || mode === 'tags';\n const isItemAligned = behaviorVariant === 'item-aligned';\n\n const [extraOptions, setExtraOptions] = useState<SelectOption<any>[]>([]);\n\n useEffect(() => {\n if (mode !== 'tags' && extraOptions.length > 0) {\n setExtraOptions([]);\n }\n }, [mode, extraOptions.length]);\n\n const {\n appendTagValues,\n getOption,\n handleValueChange,\n normalizedValue,\n normalizeValue,\n resolvedOptions,\n valueArray,\n } = useSelectValue({\n defaultValue,\n extraOptions,\n isMultiple,\n onChange,\n onSelect,\n options,\n setExtraOptions,\n value,\n });\n\n const { handleOpenChange, mergedOpen } = useSelectOpen({ defaultOpen, onOpenChange, open });\n\n const {\n filteredOptions,\n handleSearchChange,\n handleSearchKeyDown,\n searchValue,\n shouldShowSearch,\n stopSearchPropagation,\n } = useSelectSearch({\n appendTagValues,\n handleOpenChange,\n mergedOpen,\n mode,\n resolvedOptions,\n showSearch,\n tokenSeparators,\n });\n\n const virtualState = useSelectVirtual({\n filteredOptions,\n listItemHeight,\n size,\n valueArray,\n virtual,\n });\n\n const portalContainer = usePortalContainer();\n\n const renderValue = useMemo(\n () =>\n createTriggerValueRenderer({\n getOption,\n isMultiple,\n labelRender,\n normalizeValue,\n placeholder,\n }),\n [getOption, isMultiple, labelRender, normalizeValue, placeholder],\n );\n\n const hasValue = isMultiple ? valueArray.length > 0 : !isValueEmpty(normalizedValue);\n const showClear = Boolean(allowClear && hasValue && !disabled && !readOnly);\n\n const handleClear = useCallback(\n (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n handleValueChange(isMultiple ? [] : null);\n },\n [handleValueChange, isMultiple],\n );\n\n const prefixNode = useMemo(() => resolveIconNode(prefix), [prefix]);\n const suffixIconNode = useMemo(\n () => resolveSuffixIcon(suffixIcon, suffixIconProps, loading),\n [loading, suffixIcon, suffixIconProps],\n );\n\n const popupStyle = useMemo(() => {\n const maxHeight = isItemAligned ? '80vh' : `${listHeight}px`;\n const baseStyle: React.CSSProperties = {\n maxHeight,\n maxWidth: 'var(--available-width)',\n minWidth: 'var(--anchor-width)',\n ['--lobe-select-popup-max-height' as any]: maxHeight,\n };\n\n if (popupMatchSelectWidth === undefined || popupMatchSelectWidth === true) {\n return baseStyle;\n }\n if (typeof popupMatchSelectWidth === 'number') {\n return {\n ...baseStyle,\n minWidth: popupMatchSelectWidth,\n width: popupMatchSelectWidth,\n };\n }\n return { ...baseStyle, minWidth: 'max-content' };\n }, [isItemAligned, listHeight, popupMatchSelectWidth]);\n\n const triggerClassName = cx(\n triggerVariants({ shadow, size, variant: resolvedVariant }),\n className,\n classNames?.root,\n classNames?.trigger,\n );\n\n const isBoldIndicator = selectedIndicatorVariant === 'bold';\n const itemTextClassName = cx(\n optionRender ? menuStyles.itemContent : menuStyles.label,\n styles.itemText,\n classNames?.itemText,\n );\n\n const isEmpty = filteredOptions.length === 0;\n const listContent = isEmpty ? (\n <EmptyContent classNames={classNames} />\n ) : (\n renderOptions({\n classNames,\n isBoldIndicator,\n items: filteredOptions,\n itemTextClassName,\n listItemHeight,\n optionRender,\n renderVirtualItem: virtualState.renderVirtualItem,\n virtual,\n })\n );\n\n return (\n <BaseSelect.Root\n disabled={disabled}\n id={id}\n modal={isItemAligned}\n multiple={isMultiple}\n name={name}\n open={mergedOpen}\n readOnly={readOnly}\n required={required}\n value={normalizedValue}\n onOpenChange={handleOpenChange}\n onValueChange={handleValueChange}\n >\n <BaseSelect.Trigger\n autoFocus={autoFocus}\n className={triggerClassName}\n disabled={disabled}\n style={style}\n >\n {prefixNode !== null && prefixNode !== undefined && (\n <span className={cx(styles.prefix, classNames?.prefix)}>{prefixNode}</span>\n )}\n <BaseSelect.Value className={cx(styles.value, classNames?.value)}>\n {renderValue}\n </BaseSelect.Value>\n <SelectTriggerSuffix\n classNames={classNames}\n showClear={showClear}\n suffixIconNode={suffixIconNode}\n onClear={handleClear}\n />\n </BaseSelect.Trigger>\n\n <BaseSelect.Portal container={portalContainer}>\n <BaseSelect.Positioner\n align=\"start\"\n alignItemWithTrigger={isItemAligned}\n className={styles.positioner}\n side=\"bottom\"\n sideOffset={6}\n >\n <BaseSelect.Popup\n style={popupStyle}\n className={cx(\n menuStyles.popup,\n styles.popup,\n popupClassName,\n classNames?.popup,\n classNames?.dropdown,\n )}\n >\n {shouldShowSearch && (\n <SelectSearchInput\n classNames={classNames}\n placeholder={placeholder}\n stopPropagation={stopSearchPropagation}\n value={searchValue}\n onChange={handleSearchChange}\n onKeyDown={handleSearchKeyDown}\n />\n )}\n <SelectListSection\n classNames={classNames}\n isEmpty={isEmpty}\n listContent={listContent}\n listItemHeight={listItemHeight}\n virtual={virtual}\n virtualState={virtualState}\n />\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n );\n },\n);\n\nSelect.displayName = 'Select';\n\nexport default Select;\n"],"mappings":";;;;;;;;;;;;AA8BA,MAAMA,WAAS,MACZ,EACC,YACA,WACA,WACA,YACA,aACA,cACA,UACA,IACA,aACA,aAAa,KACb,gBACA,SACA,MACA,MACA,UACA,cACA,UACA,MACA,cACA,SACA,aACA,gBACA,uBACA,QACA,UACA,UACA,kBAAkB,WAClB,2BAA2B,SAC3B,QACA,YACA,OAAO,UACP,OACA,YACA,iBACA,iBACA,OACA,SACA,cACI;CACJ,MAAM,EAAE,eAAe,cAAc;CACrC,MAAM,kBAAkB,YAAY,aAAa,WAAW;CAC5D,MAAM,aAAa,SAAS,cAAc,SAAS;CACnD,MAAM,gBAAgB,oBAAoB;CAE1C,MAAM,CAAC,cAAc,mBAAmB,SAA8B,EAAE,CAAC;AAEzE,iBAAgB;AACd,MAAI,SAAS,UAAU,aAAa,SAAS,EAC3C,iBAAgB,EAAE,CAAC;IAEpB,CAAC,MAAM,aAAa,OAAO,CAAC;CAE/B,MAAM,EACJ,iBACA,WACA,mBACA,iBACA,gBACA,iBACA,eACE,eAAe;EACjB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,MAAM,EAAE,kBAAkB,eAAe,cAAc;EAAE;EAAa;EAAc;EAAM,CAAC;CAE3F,MAAM,EACJ,iBACA,oBACA,qBACA,aACA,kBACA,0BACE,gBAAgB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,MAAM,eAAe,iBAAiB;EACpC;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,MAAM,kBAAkB,oBAAoB;CAE5C,MAAM,cAAc,cAEhB,2BAA2B;EACzB;EACA;EACA;EACA;EACA;EACD,CAAC,EACJ;EAAC;EAAW;EAAY;EAAa;EAAgB;EAAY,CAClE;CAED,MAAM,WAAW,aAAa,WAAW,SAAS,IAAI,CAAC,aAAa,gBAAgB;CACpF,MAAM,YAAY,QAAQ,cAAc,YAAY,CAAC,YAAY,CAAC,SAAS;CAE3E,MAAM,cAAc,aACjB,UAAsB;AACrB,QAAM,gBAAgB;AACtB,QAAM,iBAAiB;AACvB,oBAAkB,aAAa,EAAE,GAAG,KAAK;IAE3C,CAAC,mBAAmB,WAAW,CAChC;CAED,MAAM,aAAa,cAAc,gBAAgB,OAAO,EAAE,CAAC,OAAO,CAAC;CACnE,MAAM,iBAAiB,cACf,kBAAkB,YAAY,iBAAiB,QAAQ,EAC7D;EAAC;EAAS;EAAY;EAAgB,CACvC;CAED,MAAM,aAAa,cAAc;EAC/B,MAAM,YAAY,gBAAgB,SAAS,GAAG,WAAW;EACzD,MAAM,YAAiC;GACrC;GACA,UAAU;GACV,UAAU;IACT,mCAA0C;GAC5C;AAED,MAAI,0BAA0B,KAAA,KAAa,0BAA0B,KACnE,QAAO;AAET,MAAI,OAAO,0BAA0B,SACnC,QAAO;GACL,GAAG;GACH,UAAU;GACV,OAAO;GACR;AAEH,SAAO;GAAE,GAAG;GAAW,UAAU;GAAe;IAC/C;EAAC;EAAe;EAAY;EAAsB,CAAC;CAEtD,MAAM,mBAAmB,GACvB,gBAAgB;EAAE;EAAQ;EAAM,SAAS;EAAiB,CAAC,EAC3D,WACA,YAAY,MACZ,YAAY,QACb;CAED,MAAM,kBAAkB,6BAA6B;CACrD,MAAM,oBAAoB,GACxB,eAAeC,OAAW,cAAcA,OAAW,OACnDC,SAAO,UACP,YAAY,SACb;CAED,MAAM,UAAU,gBAAgB,WAAW;CAC3C,MAAM,cAAc,UAClB,oBAAC,cAAD,EAA0B,YAAc,CAAA,GAExC,cAAc;EACZ;EACA;EACA,OAAO;EACP;EACA;EACA;EACA,mBAAmB,aAAa;EAChC;EACD,CAAC;AAGJ,QACE,qBAACC,OAAW,MAAZ;EACY;EACN;EACJ,OAAO;EACP,UAAU;EACJ;EACN,MAAM;EACI;EACA;EACV,OAAO;EACP,cAAc;EACd,eAAe;YAXjB,CAaE,qBAACA,OAAW,SAAZ;GACa;GACX,WAAW;GACD;GACH;aAJT;IAMG,eAAe,QAAQ,eAAe,KAAA,KACrC,oBAAC,QAAD;KAAM,WAAW,GAAGD,SAAO,QAAQ,YAAY,OAAO;eAAG;KAAkB,CAAA;IAE7E,oBAACC,OAAW,OAAZ;KAAkB,WAAW,GAAGD,SAAO,OAAO,YAAY,MAAM;eAC7D;KACgB,CAAA;IACnB,oBAAC,qBAAD;KACc;KACD;KACK;KAChB,SAAS;KACT,CAAA;IACiB;MAErB,oBAACC,OAAW,QAAZ;GAAmB,WAAW;aAC5B,oBAACA,OAAW,YAAZ;IACE,OAAM;IACN,sBAAsB;IACtB,WAAWD,SAAO;IAClB,MAAK;IACL,YAAY;cAEZ,qBAACC,OAAW,OAAZ;KACE,OAAO;KACP,WAAW,GACTF,OAAW,OACXC,SAAO,OACP,gBACA,YAAY,OACZ,YAAY,SACb;eARH,CAUG,oBACC,oBAAC,mBAAD;MACc;MACC;MACb,iBAAiB;MACjB,OAAO;MACP,UAAU;MACV,WAAW;MACX,CAAA,EAEJ,oBAAC,mBAAD;MACc;MACH;MACI;MACG;MACP;MACK;MACd,CAAA,CACe;;IACG,CAAA;GACN,CAAA,CACJ;;EAGvB;AAED,SAAO,cAAc"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/base-ui/Select/helpers.ts
|
|
2
|
+
const isGroupOption = (option) => Boolean(option.options);
|
|
3
|
+
const getOptionSearchText = (option) => {
|
|
4
|
+
if (typeof option.label === "string" || typeof option.label === "number") return String(option.label);
|
|
5
|
+
if (typeof option.value === "string" || typeof option.value === "number") return String(option.value);
|
|
6
|
+
if (option.title) return option.title;
|
|
7
|
+
return "";
|
|
8
|
+
};
|
|
9
|
+
const escapeRegExp = (value) => value.replaceAll(/[$()*+.?[\\\]^{|}]/g, "\\$&");
|
|
10
|
+
const splitBySeparators = (value, separators) => {
|
|
11
|
+
if (!separators || separators.length === 0) return [value];
|
|
12
|
+
const pattern = separators.map(escapeRegExp).join("|");
|
|
13
|
+
return value.split(new RegExp(pattern, "g"));
|
|
14
|
+
};
|
|
15
|
+
const countVirtualItems = (items) => items.reduce((count, item) => {
|
|
16
|
+
if (isGroupOption(item)) return count + item.options.length + 1;
|
|
17
|
+
return count + 1;
|
|
18
|
+
}, 0);
|
|
19
|
+
const isValueEmpty = (value) => value === null || value === void 0 || value === "";
|
|
20
|
+
const normalizeValueFor = (isMultiple) => (nextValue) => {
|
|
21
|
+
if (isMultiple) {
|
|
22
|
+
if (Array.isArray(nextValue)) return nextValue;
|
|
23
|
+
if (nextValue === null || nextValue === void 0) return [];
|
|
24
|
+
return [nextValue];
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(nextValue)) return nextValue[0] ?? null;
|
|
27
|
+
return nextValue === void 0 ? null : nextValue;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { countVirtualItems, getOptionSearchText, isGroupOption, isValueEmpty, normalizeValueFor, splitBySeparators };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=helpers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.mjs","names":[],"sources":["../../../src/base-ui/Select/helpers.ts"],"sourcesContent":["import { type SelectOption, type SelectOptionGroup, type SelectOptions } from './type';\n\nexport const isGroupOption = <Value>(\n option: SelectOption<Value> | SelectOptionGroup<Value>,\n): option is SelectOptionGroup<Value> => Boolean((option as SelectOptionGroup<Value>).options);\n\nexport const getOptionSearchText = <Value>(option: SelectOption<Value>) => {\n if (typeof option.label === 'string' || typeof option.label === 'number') {\n return String(option.label);\n }\n if (typeof option.value === 'string' || typeof option.value === 'number') {\n return String(option.value);\n }\n if (option.title) return option.title;\n return '';\n};\n\nconst escapeRegExp = (value: string) => value.replaceAll(/[$()*+.?[\\\\\\]^{|}]/g, '\\\\$&');\n\nexport const splitBySeparators = (value: string, separators?: string[]) => {\n if (!separators || separators.length === 0) return [value];\n const pattern = separators.map(escapeRegExp).join('|');\n return value.split(new RegExp(pattern, 'g'));\n};\n\nexport const countVirtualItems = (items: SelectOptions) =>\n items.reduce((count, item) => {\n if (isGroupOption(item)) {\n return count + item.options.length + 1;\n }\n return count + 1;\n }, 0);\n\nexport const isValueEmpty = (value: unknown) =>\n value === null || value === undefined || value === '';\n\nexport const normalizeValueFor = (isMultiple: boolean) => (nextValue: any) => {\n if (isMultiple) {\n if (Array.isArray(nextValue)) return nextValue;\n if (nextValue === null || nextValue === undefined) return [];\n return [nextValue];\n }\n if (Array.isArray(nextValue)) return nextValue[0] ?? null;\n return nextValue === undefined ? null : nextValue;\n};\n"],"mappings":";AAEA,MAAa,iBACX,WACuC,QAAS,OAAoC,QAAQ;AAE9F,MAAa,uBAA8B,WAAgC;AACzE,KAAI,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,UAAU,SAC9D,QAAO,OAAO,OAAO,MAAM;AAE7B,KAAI,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,UAAU,SAC9D,QAAO,OAAO,OAAO,MAAM;AAE7B,KAAI,OAAO,MAAO,QAAO,OAAO;AAChC,QAAO;;AAGT,MAAM,gBAAgB,UAAkB,MAAM,WAAW,uBAAuB,OAAO;AAEvF,MAAa,qBAAqB,OAAe,eAA0B;AACzE,KAAI,CAAC,cAAc,WAAW,WAAW,EAAG,QAAO,CAAC,MAAM;CAC1D,MAAM,UAAU,WAAW,IAAI,aAAa,CAAC,KAAK,IAAI;AACtD,QAAO,MAAM,MAAM,IAAI,OAAO,SAAS,IAAI,CAAC;;AAG9C,MAAa,qBAAqB,UAChC,MAAM,QAAQ,OAAO,SAAS;AAC5B,KAAI,cAAc,KAAK,CACrB,QAAO,QAAQ,KAAK,QAAQ,SAAS;AAEvC,QAAO,QAAQ;GACd,EAAE;AAEP,MAAa,gBAAgB,UAC3B,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU;AAErD,MAAa,qBAAqB,gBAAyB,cAAmB;AAC5E,KAAI,YAAY;AACd,MAAI,MAAM,QAAQ,UAAU,CAAE,QAAO;AACrC,MAAI,cAAc,QAAQ,cAAc,KAAA,EAAW,QAAO,EAAE;AAC5D,SAAO,CAAC,UAAU;;AAEpB,KAAI,MAAM,QAAQ,UAAU,CAAE,QAAO,UAAU,MAAM;AACrD,QAAO,cAAc,KAAA,IAAY,OAAO"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useAppElement } from "../../ThemeProvider/AppElementContext.mjs";
|
|
3
|
+
import { countVirtualItems, getOptionSearchText, isGroupOption, isValueEmpty, normalizeValueFor, splitBySeparators } from "./helpers.mjs";
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/base-ui/Select/hooks.tsx
|
|
7
|
+
function useSelectValue({ defaultValue, extraOptions, isMultiple, onChange, onSelect, options, setExtraOptions, value }) {
|
|
8
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(() => {
|
|
9
|
+
if (defaultValue !== void 0) return defaultValue;
|
|
10
|
+
return isMultiple ? [] : null;
|
|
11
|
+
});
|
|
12
|
+
const normalizeValue = useMemo(() => normalizeValueFor(isMultiple), [isMultiple]);
|
|
13
|
+
const mergedValue = value !== void 0 ? value : uncontrolledValue;
|
|
14
|
+
const normalizedValue = useMemo(() => normalizeValue(mergedValue), [mergedValue, normalizeValue]);
|
|
15
|
+
const valueArray = useMemo(() => {
|
|
16
|
+
if (isMultiple) return normalizedValue;
|
|
17
|
+
return isValueEmpty(normalizedValue) ? [] : [normalizedValue];
|
|
18
|
+
}, [isMultiple, normalizedValue]);
|
|
19
|
+
const { optionMap, resolvedOptions } = useMemo(() => {
|
|
20
|
+
const baseOptions = options ?? [];
|
|
21
|
+
const optionValueMap = /* @__PURE__ */ new Map();
|
|
22
|
+
const addOption = (item) => {
|
|
23
|
+
if (!optionValueMap.has(item.value)) optionValueMap.set(item.value, item);
|
|
24
|
+
};
|
|
25
|
+
baseOptions.forEach((item) => {
|
|
26
|
+
if (isGroupOption(item)) item.options.forEach(addOption);
|
|
27
|
+
else addOption(item);
|
|
28
|
+
});
|
|
29
|
+
const filteredExtraOptions = extraOptions.filter((item) => !optionValueMap.has(item.value));
|
|
30
|
+
filteredExtraOptions.forEach(addOption);
|
|
31
|
+
const mergedOptions = [...baseOptions, ...filteredExtraOptions];
|
|
32
|
+
const missingValueOptions = valueArray.filter((val) => !optionValueMap.has(val)).map((val) => ({
|
|
33
|
+
label: String(val),
|
|
34
|
+
value: val
|
|
35
|
+
}));
|
|
36
|
+
missingValueOptions.forEach(addOption);
|
|
37
|
+
return {
|
|
38
|
+
optionMap: optionValueMap,
|
|
39
|
+
resolvedOptions: missingValueOptions.length ? [...mergedOptions, ...missingValueOptions] : mergedOptions
|
|
40
|
+
};
|
|
41
|
+
}, [
|
|
42
|
+
extraOptions,
|
|
43
|
+
options,
|
|
44
|
+
valueArray
|
|
45
|
+
]);
|
|
46
|
+
const getOption = useCallback((optionValue) => {
|
|
47
|
+
const matched = optionMap.get(optionValue);
|
|
48
|
+
if (matched) return matched;
|
|
49
|
+
if (optionValue && typeof optionValue === "object" && "label" in optionValue) return {
|
|
50
|
+
label: optionValue.label,
|
|
51
|
+
value: optionValue
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
label: String(optionValue),
|
|
55
|
+
value: optionValue
|
|
56
|
+
};
|
|
57
|
+
}, [optionMap]);
|
|
58
|
+
const previousValueRef = useRef(normalizedValue);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
previousValueRef.current = normalizedValue;
|
|
61
|
+
}, [normalizedValue]);
|
|
62
|
+
const handleValueChange = useCallback((nextValue) => {
|
|
63
|
+
const normalizedNextValue = normalizeValue(nextValue);
|
|
64
|
+
const previousValue = previousValueRef.current;
|
|
65
|
+
if (isMultiple) {
|
|
66
|
+
const prevValues = Array.isArray(previousValue) ? previousValue : [];
|
|
67
|
+
const nextValues = Array.isArray(normalizedNextValue) ? normalizedNextValue : [];
|
|
68
|
+
nextValues.filter((val) => !prevValues.some((prev) => Object.is(prev, val))).forEach((val) => onSelect?.(val, getOption(val)));
|
|
69
|
+
if (value === void 0) setUncontrolledValue(nextValues);
|
|
70
|
+
onChange?.(nextValues, nextValues.map((val) => getOption(val)));
|
|
71
|
+
} else {
|
|
72
|
+
if (!isValueEmpty(normalizedNextValue) && !Object.is(previousValue, normalizedNextValue)) onSelect?.(normalizedNextValue, getOption(normalizedNextValue));
|
|
73
|
+
if (value === void 0) setUncontrolledValue(normalizedNextValue);
|
|
74
|
+
onChange?.(normalizedNextValue, isValueEmpty(normalizedNextValue) ? void 0 : getOption(normalizedNextValue));
|
|
75
|
+
}
|
|
76
|
+
previousValueRef.current = normalizedNextValue;
|
|
77
|
+
}, [
|
|
78
|
+
getOption,
|
|
79
|
+
isMultiple,
|
|
80
|
+
normalizeValue,
|
|
81
|
+
onChange,
|
|
82
|
+
onSelect,
|
|
83
|
+
value
|
|
84
|
+
]);
|
|
85
|
+
return {
|
|
86
|
+
appendTagValues: useCallback((rawValues) => {
|
|
87
|
+
const valuesToAdd = rawValues.map((val) => val.trim()).filter(Boolean);
|
|
88
|
+
if (!valuesToAdd.length) return;
|
|
89
|
+
const nextValues = [...valueArray];
|
|
90
|
+
const newOptionValues = valuesToAdd.filter((val) => !optionMap.has(val));
|
|
91
|
+
if (newOptionValues.length > 0) setExtraOptions((prev) => {
|
|
92
|
+
const existingValues = new Set(prev.map((item) => item.value));
|
|
93
|
+
const merged = [...prev];
|
|
94
|
+
newOptionValues.forEach((val) => {
|
|
95
|
+
if (!existingValues.has(val)) merged.push({
|
|
96
|
+
label: val,
|
|
97
|
+
value: val
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
return merged;
|
|
101
|
+
});
|
|
102
|
+
valuesToAdd.forEach((val) => {
|
|
103
|
+
if (!nextValues.some((item) => Object.is(item, val))) nextValues.push(val);
|
|
104
|
+
});
|
|
105
|
+
if (nextValues.length !== valueArray.length) handleValueChange(nextValues);
|
|
106
|
+
}, [
|
|
107
|
+
handleValueChange,
|
|
108
|
+
optionMap,
|
|
109
|
+
setExtraOptions,
|
|
110
|
+
valueArray
|
|
111
|
+
]),
|
|
112
|
+
getOption,
|
|
113
|
+
handleValueChange,
|
|
114
|
+
normalizedValue,
|
|
115
|
+
normalizeValue,
|
|
116
|
+
optionMap,
|
|
117
|
+
resolvedOptions,
|
|
118
|
+
valueArray
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function useSelectOpen({ defaultOpen, onOpenChange, open }) {
|
|
122
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (open !== void 0) setUncontrolledOpen(open);
|
|
125
|
+
}, [open]);
|
|
126
|
+
const mergedOpen = open ?? uncontrolledOpen;
|
|
127
|
+
return {
|
|
128
|
+
handleOpenChange: useCallback((nextOpen, eventDetails) => {
|
|
129
|
+
onOpenChange?.(nextOpen, eventDetails);
|
|
130
|
+
if (open === void 0) setUncontrolledOpen(nextOpen);
|
|
131
|
+
}, [onOpenChange, open]),
|
|
132
|
+
mergedOpen
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function useSelectSearch({ appendTagValues, handleOpenChange, mergedOpen, mode, resolvedOptions, showSearch, tokenSeparators }) {
|
|
136
|
+
const [searchValue, setSearchValue] = useState("");
|
|
137
|
+
const shouldShowSearch = Boolean(showSearch || mode === "tags");
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (!mergedOpen) setSearchValue("");
|
|
140
|
+
}, [mergedOpen]);
|
|
141
|
+
const handleSearchChange = useCallback((event) => {
|
|
142
|
+
const nextValue = event.target.value;
|
|
143
|
+
if (mode === "tags") {
|
|
144
|
+
const parts = splitBySeparators(nextValue, tokenSeparators);
|
|
145
|
+
if (parts.length > 1) {
|
|
146
|
+
const pending = parts.pop() ?? "";
|
|
147
|
+
appendTagValues(parts.filter(Boolean));
|
|
148
|
+
setSearchValue(pending);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
setSearchValue(nextValue);
|
|
153
|
+
}, [
|
|
154
|
+
appendTagValues,
|
|
155
|
+
mode,
|
|
156
|
+
tokenSeparators
|
|
157
|
+
]);
|
|
158
|
+
const handleSearchKeyDown = useCallback((event) => {
|
|
159
|
+
event.stopPropagation();
|
|
160
|
+
if (event.key === "Escape") {
|
|
161
|
+
handleOpenChange(false);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (mode !== "tags") return;
|
|
165
|
+
const isSeparator = tokenSeparators?.includes(event.key);
|
|
166
|
+
if (event.key === "Enter" || isSeparator) {
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
event.stopPropagation();
|
|
169
|
+
appendTagValues([searchValue]);
|
|
170
|
+
setSearchValue("");
|
|
171
|
+
}
|
|
172
|
+
}, [
|
|
173
|
+
appendTagValues,
|
|
174
|
+
handleOpenChange,
|
|
175
|
+
mode,
|
|
176
|
+
searchValue,
|
|
177
|
+
tokenSeparators
|
|
178
|
+
]);
|
|
179
|
+
const stopSearchPropagation = useCallback((event) => {
|
|
180
|
+
event.stopPropagation();
|
|
181
|
+
}, []);
|
|
182
|
+
return {
|
|
183
|
+
filteredOptions: useMemo(() => {
|
|
184
|
+
if (!shouldShowSearch || !searchValue.trim()) return resolvedOptions;
|
|
185
|
+
const query = searchValue.trim().toLowerCase();
|
|
186
|
+
return resolvedOptions.map((item) => {
|
|
187
|
+
if (isGroupOption(item)) {
|
|
188
|
+
const groupItems = item.options.filter((option) => getOptionSearchText(option).toLowerCase().includes(query));
|
|
189
|
+
if (!groupItems.length) return null;
|
|
190
|
+
return {
|
|
191
|
+
...item,
|
|
192
|
+
options: groupItems
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return getOptionSearchText(item).toLowerCase().includes(query) ? item : null;
|
|
196
|
+
}).filter(Boolean);
|
|
197
|
+
}, [
|
|
198
|
+
resolvedOptions,
|
|
199
|
+
searchValue,
|
|
200
|
+
shouldShowSearch
|
|
201
|
+
]),
|
|
202
|
+
handleSearchChange,
|
|
203
|
+
handleSearchKeyDown,
|
|
204
|
+
searchValue,
|
|
205
|
+
shouldShowSearch,
|
|
206
|
+
stopSearchPropagation
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function useSelectVirtual({ filteredOptions, listItemHeight, size, valueArray, virtual }) {
|
|
210
|
+
const listRef = useRef(null);
|
|
211
|
+
const pointerScrollRef = useRef(false);
|
|
212
|
+
const pointerScrollTimeoutRef = useRef(null);
|
|
213
|
+
const renderVirtualItem = useCallback((props) => {
|
|
214
|
+
const { ref, ...rest } = props;
|
|
215
|
+
return /* @__PURE__ */ jsx("div", {
|
|
216
|
+
...rest,
|
|
217
|
+
ref: (node) => {
|
|
218
|
+
if (node) node.scrollIntoView = (...args) => {
|
|
219
|
+
if (!pointerScrollRef.current) HTMLElement.prototype.scrollIntoView.call(node, ...args);
|
|
220
|
+
};
|
|
221
|
+
if (typeof ref === "function") ref(node);
|
|
222
|
+
else if (ref && "current" in ref) ref.current = node;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}, []);
|
|
226
|
+
const markPointerScroll = useCallback(() => {
|
|
227
|
+
pointerScrollRef.current = true;
|
|
228
|
+
if (pointerScrollTimeoutRef.current) clearTimeout(pointerScrollTimeoutRef.current);
|
|
229
|
+
pointerScrollTimeoutRef.current = setTimeout(() => {
|
|
230
|
+
pointerScrollRef.current = false;
|
|
231
|
+
}, 120);
|
|
232
|
+
}, []);
|
|
233
|
+
const handleListScroll = useCallback(() => {
|
|
234
|
+
if (!virtual || !pointerScrollRef.current) return;
|
|
235
|
+
const listElement = listRef.current;
|
|
236
|
+
const activeElement = document.activeElement;
|
|
237
|
+
if (listElement && activeElement && listElement.contains(activeElement)) listElement.focus({ preventScroll: true });
|
|
238
|
+
}, [virtual]);
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
return () => {
|
|
241
|
+
if (pointerScrollTimeoutRef.current) clearTimeout(pointerScrollTimeoutRef.current);
|
|
242
|
+
};
|
|
243
|
+
}, []);
|
|
244
|
+
const virtualListStyle = useMemo(() => {
|
|
245
|
+
if (!virtual) return void 0;
|
|
246
|
+
const rowCount = countVirtualItems(filteredOptions);
|
|
247
|
+
return { height: `min(${Math.min(Math.max(rowCount, 1), 6) * (listItemHeight ?? (size === "large" ? 40 : size === "small" ? 28 : 32)) + 8}px, var(--lobe-select-available-height, var(--available-height)))` };
|
|
248
|
+
}, [
|
|
249
|
+
filteredOptions,
|
|
250
|
+
listItemHeight,
|
|
251
|
+
size,
|
|
252
|
+
virtual
|
|
253
|
+
]);
|
|
254
|
+
return {
|
|
255
|
+
handleListScroll,
|
|
256
|
+
keepMountedIndices: useMemo(() => {
|
|
257
|
+
if (!virtual || valueArray.length === 0) return void 0;
|
|
258
|
+
const selectedSet = new Set(valueArray);
|
|
259
|
+
const indices = [];
|
|
260
|
+
let index = 0;
|
|
261
|
+
filteredOptions.forEach((item) => {
|
|
262
|
+
if (isGroupOption(item)) {
|
|
263
|
+
if (item.options.some((option) => selectedSet.has(option.value))) indices.push(index);
|
|
264
|
+
index += 1;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (selectedSet.has(item.value)) indices.push(index);
|
|
268
|
+
index += 1;
|
|
269
|
+
});
|
|
270
|
+
return indices.length ? indices : void 0;
|
|
271
|
+
}, [
|
|
272
|
+
filteredOptions,
|
|
273
|
+
valueArray,
|
|
274
|
+
virtual
|
|
275
|
+
]),
|
|
276
|
+
listRef,
|
|
277
|
+
markPointerScroll,
|
|
278
|
+
renderVirtualItem,
|
|
279
|
+
virtualListStyle
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function usePortalContainer() {
|
|
283
|
+
const appElement = useAppElement();
|
|
284
|
+
return useMemo(() => {
|
|
285
|
+
if (typeof window === "undefined") return appElement;
|
|
286
|
+
if (!(appElement instanceof HTMLElement)) return void 0;
|
|
287
|
+
return window.getComputedStyle(appElement).display === "contents" ? document.body : appElement;
|
|
288
|
+
}, [appElement]);
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
export { usePortalContainer, useSelectOpen, useSelectSearch, useSelectValue, useSelectVirtual };
|
|
292
|
+
|
|
293
|
+
//# sourceMappingURL=hooks.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.mjs","names":[],"sources":["../../../src/base-ui/Select/hooks.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ChangeEvent,\n type HTMLAttributes,\n type KeyboardEvent,\n type MutableRefObject,\n type Ref,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { useAppElement } from '@/ThemeProvider';\n\nimport {\n countVirtualItems,\n getOptionSearchText,\n isGroupOption,\n isValueEmpty,\n normalizeValueFor,\n splitBySeparators,\n} from './helpers';\nimport {\n type SelectOption,\n type SelectOptions,\n type SelectProps,\n type SelectRootChangeEventDetails,\n type SelectSize,\n} from './type';\n\ninterface UseSelectValueParams {\n defaultValue: SelectProps['defaultValue'];\n extraOptions: SelectOption<any>[];\n isMultiple: boolean;\n onChange: SelectProps['onChange'];\n onSelect: SelectProps['onSelect'];\n options: SelectOptions<any> | undefined;\n setExtraOptions: React.Dispatch<React.SetStateAction<SelectOption<any>[]>>;\n value: SelectProps['value'];\n}\n\nexport function useSelectValue({\n defaultValue,\n extraOptions,\n isMultiple,\n onChange,\n onSelect,\n options,\n setExtraOptions,\n value,\n}: UseSelectValueParams) {\n const [uncontrolledValue, setUncontrolledValue] = useState<any>(() => {\n if (defaultValue !== undefined) return defaultValue;\n return isMultiple ? [] : null;\n });\n\n const normalizeValue = useMemo(() => normalizeValueFor(isMultiple), [isMultiple]);\n\n const mergedValue = value !== undefined ? value : uncontrolledValue;\n const normalizedValue = useMemo(() => normalizeValue(mergedValue), [mergedValue, normalizeValue]);\n\n const valueArray = useMemo(() => {\n if (isMultiple) return normalizedValue as any[];\n return isValueEmpty(normalizedValue) ? [] : [normalizedValue];\n }, [isMultiple, normalizedValue]);\n\n const { optionMap, resolvedOptions } = useMemo(() => {\n const baseOptions = options ?? [];\n const optionValueMap = new Map<any, SelectOption<any>>();\n\n const addOption = (item: SelectOption<any>) => {\n if (!optionValueMap.has(item.value)) {\n optionValueMap.set(item.value, item);\n }\n };\n\n baseOptions.forEach((item) => {\n if (isGroupOption(item)) {\n item.options.forEach(addOption);\n } else {\n addOption(item);\n }\n });\n\n const filteredExtraOptions = extraOptions.filter((item) => !optionValueMap.has(item.value));\n filteredExtraOptions.forEach(addOption);\n const mergedOptions: SelectOptions<any> = [...baseOptions, ...filteredExtraOptions];\n\n const missingValueOptions: SelectOption<any>[] = valueArray\n .filter((val) => !optionValueMap.has(val))\n .map((val) => ({ label: String(val), value: val }));\n missingValueOptions.forEach(addOption);\n\n return {\n optionMap: optionValueMap,\n resolvedOptions: missingValueOptions.length\n ? [...mergedOptions, ...missingValueOptions]\n : mergedOptions,\n };\n }, [extraOptions, options, valueArray]);\n\n const getOption = useCallback(\n (optionValue: any): SelectOption<any> => {\n const matched = optionMap.get(optionValue);\n if (matched) return matched;\n if (optionValue && typeof optionValue === 'object' && 'label' in optionValue) {\n return { label: (optionValue as any).label, value: optionValue };\n }\n return { label: String(optionValue), value: optionValue };\n },\n [optionMap],\n );\n\n const previousValueRef = useRef<any>(normalizedValue);\n useEffect(() => {\n previousValueRef.current = normalizedValue;\n }, [normalizedValue]);\n\n const handleValueChange = useCallback(\n (nextValue: any) => {\n const normalizedNextValue = normalizeValue(nextValue);\n const previousValue = previousValueRef.current;\n\n if (isMultiple) {\n const prevValues = Array.isArray(previousValue) ? previousValue : [];\n const nextValues = Array.isArray(normalizedNextValue) ? normalizedNextValue : [];\n const addedValues = nextValues.filter(\n (val) => !prevValues.some((prev) => Object.is(prev, val)),\n );\n addedValues.forEach((val) => onSelect?.(val, getOption(val)));\n\n if (value === undefined) setUncontrolledValue(nextValues);\n onChange?.(\n nextValues,\n nextValues.map((val) => getOption(val)),\n );\n } else {\n if (!isValueEmpty(normalizedNextValue) && !Object.is(previousValue, normalizedNextValue)) {\n onSelect?.(normalizedNextValue, getOption(normalizedNextValue));\n }\n if (value === undefined) setUncontrolledValue(normalizedNextValue);\n onChange?.(\n normalizedNextValue,\n isValueEmpty(normalizedNextValue) ? undefined : getOption(normalizedNextValue),\n );\n }\n\n previousValueRef.current = normalizedNextValue;\n },\n [getOption, isMultiple, normalizeValue, onChange, onSelect, value],\n );\n\n const appendTagValues = useCallback(\n (rawValues: string[]) => {\n const valuesToAdd = rawValues.map((val) => val.trim()).filter(Boolean);\n if (!valuesToAdd.length) return;\n\n const nextValues = [...valueArray];\n const newOptionValues = valuesToAdd.filter((val) => !optionMap.has(val));\n\n if (newOptionValues.length > 0) {\n setExtraOptions((prev) => {\n const existingValues = new Set(prev.map((item) => item.value));\n const merged = [...prev];\n newOptionValues.forEach((val) => {\n if (!existingValues.has(val)) {\n merged.push({ label: val, value: val });\n }\n });\n return merged;\n });\n }\n\n valuesToAdd.forEach((val) => {\n if (!nextValues.some((item) => Object.is(item, val))) {\n nextValues.push(val);\n }\n });\n\n if (nextValues.length !== valueArray.length) {\n handleValueChange(nextValues);\n }\n },\n [handleValueChange, optionMap, setExtraOptions, valueArray],\n );\n\n return {\n appendTagValues,\n getOption,\n handleValueChange,\n normalizedValue,\n normalizeValue,\n optionMap,\n resolvedOptions,\n valueArray,\n };\n}\n\ninterface UseSelectOpenParams {\n defaultOpen: boolean | undefined;\n onOpenChange: SelectProps['onOpenChange'];\n open: boolean | undefined;\n}\n\nexport function useSelectOpen({ defaultOpen, onOpenChange, open }: UseSelectOpenParams) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));\n\n useEffect(() => {\n if (open !== undefined) setUncontrolledOpen(open);\n }, [open]);\n\n const mergedOpen = open ?? uncontrolledOpen;\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean, eventDetails?: SelectRootChangeEventDetails) => {\n onOpenChange?.(nextOpen, eventDetails);\n if (open === undefined) setUncontrolledOpen(nextOpen);\n },\n [onOpenChange, open],\n );\n\n return { handleOpenChange, mergedOpen };\n}\n\ninterface UseSelectSearchParams {\n appendTagValues: (values: string[]) => void;\n handleOpenChange: (nextOpen: boolean) => void;\n mergedOpen: boolean;\n mode: SelectProps['mode'];\n resolvedOptions: SelectOptions;\n showSearch: boolean | undefined;\n tokenSeparators: string[] | undefined;\n}\n\nexport function useSelectSearch({\n appendTagValues,\n handleOpenChange,\n mergedOpen,\n mode,\n resolvedOptions,\n showSearch,\n tokenSeparators,\n}: UseSelectSearchParams) {\n const [searchValue, setSearchValue] = useState('');\n const shouldShowSearch = Boolean(showSearch || mode === 'tags');\n\n useEffect(() => {\n if (!mergedOpen) setSearchValue('');\n }, [mergedOpen]);\n\n const handleSearchChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const nextValue = event.target.value;\n if (mode === 'tags') {\n const parts = splitBySeparators(nextValue, tokenSeparators);\n if (parts.length > 1) {\n const pending = parts.pop() ?? '';\n appendTagValues(parts.filter(Boolean));\n setSearchValue(pending);\n return;\n }\n }\n setSearchValue(nextValue);\n },\n [appendTagValues, mode, tokenSeparators],\n );\n\n const handleSearchKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n event.stopPropagation();\n\n if (event.key === 'Escape') {\n handleOpenChange(false);\n return;\n }\n if (mode !== 'tags') return;\n\n const isSeparator = tokenSeparators?.includes(event.key);\n if (event.key === 'Enter' || isSeparator) {\n event.preventDefault();\n event.stopPropagation();\n appendTagValues([searchValue]);\n setSearchValue('');\n }\n },\n [appendTagValues, handleOpenChange, mode, searchValue, tokenSeparators],\n );\n\n // Stop propagation in capture phase + on keyup to block base-ui Popup-level\n // `useTypeahead` (and any ancestor typeahead) from hijacking the search\n // input's first-letter focus behavior. Do not remove.\n const stopSearchPropagation = useCallback((event: KeyboardEvent<HTMLInputElement>) => {\n event.stopPropagation();\n }, []);\n\n const filteredOptions = useMemo(() => {\n if (!shouldShowSearch || !searchValue.trim()) return resolvedOptions;\n const query = searchValue.trim().toLowerCase();\n\n return resolvedOptions\n .map((item) => {\n if (isGroupOption(item)) {\n const groupItems = item.options.filter((option) =>\n getOptionSearchText(option).toLowerCase().includes(query),\n );\n if (!groupItems.length) return null;\n return { ...item, options: groupItems };\n }\n return getOptionSearchText(item).toLowerCase().includes(query) ? item : null;\n })\n .filter(Boolean) as SelectOptions;\n }, [resolvedOptions, searchValue, shouldShowSearch]);\n\n return {\n filteredOptions,\n handleSearchChange,\n handleSearchKeyDown,\n searchValue,\n shouldShowSearch,\n stopSearchPropagation,\n };\n}\n\ninterface UseSelectVirtualParams {\n filteredOptions: SelectOptions;\n listItemHeight: number | undefined;\n size: SelectSize;\n valueArray: any[];\n virtual: boolean | undefined;\n}\n\nexport function useSelectVirtual({\n filteredOptions,\n listItemHeight,\n size,\n valueArray,\n virtual,\n}: UseSelectVirtualParams) {\n const listRef = useRef<HTMLDivElement | null>(null);\n const pointerScrollRef = useRef(false);\n const pointerScrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const renderVirtualItem = useCallback((props: HTMLAttributes<HTMLDivElement>) => {\n const { ref, ...rest } = props as HTMLAttributes<HTMLDivElement> & {\n ref?: Ref<HTMLDivElement>;\n };\n\n return (\n <div\n {...rest}\n ref={(node) => {\n if (node) {\n node.scrollIntoView = (...args) => {\n if (!pointerScrollRef.current) {\n HTMLElement.prototype.scrollIntoView.call(node, ...args);\n }\n };\n }\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref && 'current' in ref) {\n (ref as MutableRefObject<HTMLDivElement | null>).current = node;\n }\n }}\n />\n );\n }, []);\n\n const markPointerScroll = useCallback(() => {\n pointerScrollRef.current = true;\n if (pointerScrollTimeoutRef.current) {\n clearTimeout(pointerScrollTimeoutRef.current);\n }\n pointerScrollTimeoutRef.current = setTimeout(() => {\n pointerScrollRef.current = false;\n }, 120);\n }, []);\n\n const handleListScroll = useCallback(() => {\n if (!virtual || !pointerScrollRef.current) return;\n const listElement = listRef.current;\n const activeElement = document.activeElement;\n if (listElement && activeElement && listElement.contains(activeElement)) {\n listElement.focus({ preventScroll: true });\n }\n }, [virtual]);\n\n useEffect(() => {\n return () => {\n if (pointerScrollTimeoutRef.current) {\n clearTimeout(pointerScrollTimeoutRef.current);\n }\n };\n }, []);\n\n const virtualListStyle = useMemo(() => {\n if (!virtual) return undefined;\n const rowCount = countVirtualItems(filteredOptions);\n const maxVisibleRows = 6;\n const estimatedRowHeight =\n listItemHeight ?? (size === 'large' ? 40 : size === 'small' ? 28 : 32);\n const visibleRows = Math.min(Math.max(rowCount, 1), maxVisibleRows);\n const estimatedHeight = visibleRows * estimatedRowHeight + 8;\n\n return {\n height: `min(${estimatedHeight}px, var(--lobe-select-available-height, var(--available-height)))`,\n };\n }, [filteredOptions, listItemHeight, size, virtual]);\n\n const keepMountedIndices = useMemo(() => {\n if (!virtual || valueArray.length === 0) return undefined;\n const selectedSet = new Set(valueArray);\n const indices: number[] = [];\n let index = 0;\n\n filteredOptions.forEach((item) => {\n if (isGroupOption(item)) {\n if (item.options.some((option) => selectedSet.has(option.value))) {\n indices.push(index);\n }\n index += 1;\n return;\n }\n if (selectedSet.has(item.value)) indices.push(index);\n index += 1;\n });\n\n return indices.length ? indices : undefined;\n }, [filteredOptions, valueArray, virtual]);\n\n return {\n handleListScroll,\n keepMountedIndices,\n listRef,\n markPointerScroll,\n renderVirtualItem,\n virtualListStyle,\n };\n}\n\nexport function usePortalContainer() {\n const appElement = useAppElement();\n // `appElement` is the ThemeProvider wrapper div, which uses `display: contents`\n // so it has no layout box. `@base-ui/react/select` fails to mount its Popup\n // into a `display: contents` container in certain hosts (editor/chat inputs\n // with focus traps). Fall back to `document.body` in that case; keep the\n // original behavior when the wrapper has a real layout (older themes, SSR\n // snapshot, etc.).\n return useMemo(() => {\n if (typeof window === 'undefined') return appElement;\n if (!(appElement instanceof HTMLElement)) return undefined;\n const display = window.getComputedStyle(appElement).display;\n return display === 'contents' ? document.body : appElement;\n }, [appElement]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import Icon from "../../Icon/Icon.mjs";
|
|
3
|
+
import { styles } from "../../Menu/sharedStyle.mjs";
|
|
4
|
+
import { styles as styles$1 } from "./style.mjs";
|
|
5
|
+
import { isValueEmpty } from "./helpers.mjs";
|
|
6
|
+
import { isValidElement } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { cx } from "antd-style";
|
|
9
|
+
import { ChevronDown, Loader2, X } from "lucide-react";
|
|
10
|
+
import { Select } from "@base-ui/react/select";
|
|
11
|
+
import { Virtualizer } from "virtua";
|
|
12
|
+
//#region src/base-ui/Select/parts.tsx
|
|
13
|
+
function resolveIconNode(node) {
|
|
14
|
+
if (node === void 0 || node === null) return null;
|
|
15
|
+
if (isValidElement(node) || typeof node === "string" || typeof node === "number") return node;
|
|
16
|
+
return /* @__PURE__ */ jsx(Icon, {
|
|
17
|
+
icon: node,
|
|
18
|
+
size: "small"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function resolveSuffixIcon(suffixIcon, suffixIconProps, loading) {
|
|
22
|
+
if (loading) return /* @__PURE__ */ jsx(Icon, {
|
|
23
|
+
spin: true,
|
|
24
|
+
icon: Loader2,
|
|
25
|
+
size: "small"
|
|
26
|
+
});
|
|
27
|
+
if (suffixIcon === null) return null;
|
|
28
|
+
if (isValidElement(suffixIcon) || typeof suffixIcon === "string" || typeof suffixIcon === "number") return suffixIcon;
|
|
29
|
+
return /* @__PURE__ */ jsx(Icon, {
|
|
30
|
+
icon: suffixIcon || ChevronDown,
|
|
31
|
+
size: "small",
|
|
32
|
+
...suffixIconProps,
|
|
33
|
+
style: {
|
|
34
|
+
pointerEvents: "none",
|
|
35
|
+
...suffixIconProps?.style
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function createTriggerValueRenderer({ getOption, isMultiple, labelRender, normalizeValue, placeholder }) {
|
|
40
|
+
return function renderValue(currentValue) {
|
|
41
|
+
const resolved = normalizeValue(currentValue);
|
|
42
|
+
const placeholderNode = placeholder === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
43
|
+
className: styles$1.valueText,
|
|
44
|
+
children: placeholder
|
|
45
|
+
});
|
|
46
|
+
if (isMultiple) {
|
|
47
|
+
const values = Array.isArray(resolved) ? resolved : [];
|
|
48
|
+
if (values.length === 0) return placeholderNode;
|
|
49
|
+
return /* @__PURE__ */ jsx("span", {
|
|
50
|
+
className: styles$1.tags,
|
|
51
|
+
children: values.map((val, index) => {
|
|
52
|
+
const option = getOption(val);
|
|
53
|
+
const content = labelRender ? labelRender(option) : option.label ?? String(val);
|
|
54
|
+
return /* @__PURE__ */ jsx("span", {
|
|
55
|
+
className: styles$1.tag,
|
|
56
|
+
children: content
|
|
57
|
+
}, `${String(val)}-${index}`);
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (isValueEmpty(resolved)) return placeholderNode;
|
|
62
|
+
const option = getOption(resolved);
|
|
63
|
+
const content = labelRender ? labelRender(option) : option.label ?? String(resolved);
|
|
64
|
+
return /* @__PURE__ */ jsx("span", {
|
|
65
|
+
className: styles$1.valueText,
|
|
66
|
+
children: content
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function SelectListSection({ classNames, isEmpty, listContent, listItemHeight, virtual, virtualState }) {
|
|
71
|
+
const listClassName = cx(styles$1.list, classNames?.list);
|
|
72
|
+
if (!virtual || isEmpty) return /* @__PURE__ */ jsx(Select.List, {
|
|
73
|
+
className: listClassName,
|
|
74
|
+
"data-virtual": virtual || void 0,
|
|
75
|
+
children: listContent
|
|
76
|
+
});
|
|
77
|
+
const { handleListScroll, keepMountedIndices, listRef, markPointerScroll, virtualListStyle } = virtualState;
|
|
78
|
+
return /* @__PURE__ */ jsx(Select.List, {
|
|
79
|
+
"data-virtual": true,
|
|
80
|
+
className: listClassName,
|
|
81
|
+
ref: listRef,
|
|
82
|
+
style: virtualListStyle,
|
|
83
|
+
tabIndex: -1,
|
|
84
|
+
onPointerDown: markPointerScroll,
|
|
85
|
+
onScroll: handleListScroll,
|
|
86
|
+
onTouchMove: markPointerScroll,
|
|
87
|
+
onWheel: markPointerScroll,
|
|
88
|
+
children: /* @__PURE__ */ jsx(Virtualizer, {
|
|
89
|
+
itemSize: listItemHeight,
|
|
90
|
+
keepMounted: keepMountedIndices,
|
|
91
|
+
children: listContent
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function EmptyContent({ classNames }) {
|
|
96
|
+
return /* @__PURE__ */ jsx("div", {
|
|
97
|
+
className: cx(styles.item, styles.empty, styles$1.empty, classNames?.empty),
|
|
98
|
+
children: "No data"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function SelectSearchInput({ classNames, onChange, onKeyDown, placeholder, stopPropagation, value }) {
|
|
102
|
+
return /* @__PURE__ */ jsx("div", {
|
|
103
|
+
className: cx(styles$1.search, classNames?.search),
|
|
104
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
105
|
+
className: styles$1.searchInput,
|
|
106
|
+
placeholder: typeof placeholder === "string" ? placeholder : void 0,
|
|
107
|
+
value,
|
|
108
|
+
onChange,
|
|
109
|
+
onKeyDown,
|
|
110
|
+
onKeyDownCapture: stopPropagation,
|
|
111
|
+
onKeyUp: stopPropagation,
|
|
112
|
+
onKeyUpCapture: stopPropagation
|
|
113
|
+
})
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function SelectTriggerSuffix({ classNames, onClear, showClear, suffixIconNode }) {
|
|
117
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
118
|
+
className: cx(styles$1.suffix, classNames?.suffix),
|
|
119
|
+
children: [showClear && /* @__PURE__ */ jsx("span", {
|
|
120
|
+
className: cx(styles$1.clear, classNames?.clear),
|
|
121
|
+
"data-role": "lobe-select-clear",
|
|
122
|
+
onClick: onClear,
|
|
123
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
124
|
+
icon: X,
|
|
125
|
+
size: "small"
|
|
126
|
+
})
|
|
127
|
+
}), suffixIconNode !== null && suffixIconNode !== void 0 && /* @__PURE__ */ jsx(Select.Icon, {
|
|
128
|
+
className: cx(styles$1.icon, classNames?.icon),
|
|
129
|
+
children: suffixIconNode
|
|
130
|
+
})]
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
export { EmptyContent, SelectListSection, SelectSearchInput, SelectTriggerSuffix, createTriggerValueRenderer, resolveIconNode, resolveSuffixIcon };
|
|
135
|
+
|
|
136
|
+
//# sourceMappingURL=parts.mjs.map
|