This commit is contained in:
2026-05-25 17:08:18 +08:00
parent df501f6151
commit f1259b71b5
9178 changed files with 1626125 additions and 0 deletions
@@ -0,0 +1,5 @@
import * as React from 'react';
export interface AffixProps extends React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
}
export default function Affix(props: AffixProps): React.JSX.Element;
@@ -0,0 +1,12 @@
import * as React from 'react';
// Affix is a simple wrapper which should not read context or logical props
export default function Affix(props) {
const {
children,
...restProps
} = props;
if (!children) {
return null;
}
return /*#__PURE__*/React.createElement("div", restProps, children);
}
@@ -0,0 +1,4 @@
import * as React from 'react';
import type { SharedContentProps } from '.';
declare const _default: React.ForwardRefExoticComponent<SharedContentProps & React.RefAttributes<HTMLInputElement>>;
export default _default;
@@ -0,0 +1,158 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import { clsx } from 'clsx';
import Overflow from '@rc-component/overflow';
import Input from "../Input";
import { useSelectInputContext } from "../context";
import TransBtn from "../../TransBtn";
import { getTitle } from "../../utils/commonUtil";
import useBaseProps from "../../hooks/useBaseProps";
import Placeholder from "./Placeholder";
function itemKey(value) {
return value.key ?? value.value;
}
const onPreventMouseDown = event => {
event.preventDefault();
event.stopPropagation();
};
export default /*#__PURE__*/React.forwardRef(function MultipleContent({
inputProps
}, ref) {
const {
prefixCls,
displayValues,
searchValue,
mode,
onSelectorRemove,
removeIcon: removeIconFromContext
} = useSelectInputContext();
const {
disabled,
showSearch,
triggerOpen,
rawOpen,
toggleOpen,
autoClearSearchValue,
tagRender: tagRenderFromContext,
maxTagPlaceholder: maxTagPlaceholderFromContext,
maxTagTextLength,
maxTagCount,
classNames,
styles
} = useBaseProps();
const selectionItemPrefixCls = `${prefixCls}-selection-item`;
// ===================== Search ======================
// Apply autoClearSearchValue logic: when dropdown is closed and autoClearSearchValue is not false (default true), clear search value
// Use rawOpen to avoid clearing search when emptyListContent blocks open
let computedSearchValue = searchValue;
if (!rawOpen && mode === 'multiple' && autoClearSearchValue !== false) {
computedSearchValue = '';
}
const inputValue = showSearch ? computedSearchValue || '' : '';
const inputEditable = showSearch && !disabled;
// Props from context with safe defaults
const removeIcon = removeIconFromContext ?? '×';
const maxTagPlaceholder = maxTagPlaceholderFromContext ?? (omittedValues => `+ ${omittedValues.length} ...`);
const tagRender = tagRenderFromContext;
const onToggleOpen = newOpen => {
toggleOpen(newOpen);
};
const onRemove = value => {
onSelectorRemove?.(value);
};
// ======================== Item ========================
// >>> Render Selector Node. Includes Item & Rest
const defaultRenderSelector = (item, content, itemDisabled, closable, onClose) => /*#__PURE__*/React.createElement("span", {
title: getTitle(item),
className: clsx(selectionItemPrefixCls, {
[`${selectionItemPrefixCls}-disabled`]: itemDisabled
}, classNames?.item),
style: styles?.item
}, /*#__PURE__*/React.createElement("span", {
className: clsx(`${selectionItemPrefixCls}-content`, classNames?.itemContent),
style: styles?.itemContent
}, content), closable && /*#__PURE__*/React.createElement(TransBtn, {
className: clsx(`${selectionItemPrefixCls}-remove`, classNames?.itemRemove),
style: styles?.itemRemove,
onMouseDown: onPreventMouseDown,
onClick: onClose,
customizeIcon: removeIcon
}, "\xD7"));
const customizeRenderSelector = (value, content, itemDisabled, closable, onClose, isMaxTag, info) => {
const onMouseDown = e => {
onPreventMouseDown(e);
onToggleOpen(!triggerOpen);
};
return /*#__PURE__*/React.createElement("span", {
onMouseDown: onMouseDown
}, tagRender({
label: content,
value,
index: info?.index,
disabled: itemDisabled,
closable,
onClose,
isMaxTag: !!isMaxTag
}));
};
// ====================== Overflow ======================
const renderItem = (valueItem, info) => {
const {
disabled: itemDisabled,
label,
value
} = valueItem;
const closable = !disabled && !itemDisabled;
let displayLabel = label;
if (typeof maxTagTextLength === 'number') {
if (typeof label === 'string' || typeof label === 'number') {
const strLabel = String(displayLabel);
if (strLabel.length > maxTagTextLength) {
displayLabel = `${strLabel.slice(0, maxTagTextLength)}...`;
}
}
}
const onClose = event => {
if (event) {
event.stopPropagation();
}
onRemove(valueItem);
};
return typeof tagRender === 'function' ? customizeRenderSelector(value, displayLabel, itemDisabled, closable, onClose, undefined, info) : defaultRenderSelector(valueItem, displayLabel, itemDisabled, closable, onClose);
};
const renderRest = omittedValues => {
// https://github.com/ant-design/ant-design/issues/48930
if (!displayValues.length) {
return null;
}
const content = typeof maxTagPlaceholder === 'function' ? maxTagPlaceholder(omittedValues) : maxTagPlaceholder;
return typeof tagRender === 'function' ? customizeRenderSelector(undefined, content, false, false, undefined, true) : defaultRenderSelector({
title: content
}, content, false);
};
// ======================= Render =======================
return /*#__PURE__*/React.createElement(Overflow, {
prefixCls: `${prefixCls}-content`,
className: classNames?.content,
style: styles?.content,
prefix: !displayValues.length && !inputValue && /*#__PURE__*/React.createElement(Placeholder, null),
data: displayValues,
renderItem: renderItem,
renderRest: renderRest,
suffix: /*#__PURE__*/React.createElement(Input, _extends({
ref: ref,
disabled: disabled,
readOnly: !inputEditable
}, inputProps, {
value: inputValue || '',
syncWidth: true
})),
itemKey: itemKey,
maxCount: maxTagCount
});
});
@@ -0,0 +1,5 @@
import * as React from 'react';
export interface PlaceholderProps {
show?: boolean;
}
export default function Placeholder(props: PlaceholderProps): React.JSX.Element;
@@ -0,0 +1,28 @@
import * as React from 'react';
import { clsx } from 'clsx';
import { useSelectInputContext } from "../context";
import useBaseProps from "../../hooks/useBaseProps";
export default function Placeholder(props) {
const {
prefixCls,
placeholder,
displayValues
} = useSelectInputContext();
const {
classNames,
styles
} = useBaseProps();
const {
show = true
} = props;
if (displayValues.length) {
return null;
}
return /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-placeholder`, classNames?.placeholder),
style: {
visibility: show ? 'visible' : 'hidden',
...styles?.placeholder
}
}, placeholder);
}
@@ -0,0 +1,4 @@
import * as React from 'react';
import type { SharedContentProps } from '.';
declare const SingleContent: React.ForwardRefExoticComponent<SharedContentProps & React.RefAttributes<HTMLInputElement>>;
export default SingleContent;
@@ -0,0 +1,102 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import { clsx } from 'clsx';
import Input from "../Input";
import { useSelectInputContext } from "../context";
import useBaseProps from "../../hooks/useBaseProps";
import Placeholder from "./Placeholder";
import SelectContext from "../../SelectContext";
import { getTitle } from "../../utils/commonUtil";
const SingleContent = /*#__PURE__*/React.forwardRef(({
inputProps
}, ref) => {
const {
prefixCls,
searchValue,
activeValue,
displayValues,
maxLength,
mode,
components
} = useSelectInputContext();
const {
triggerOpen,
title: rootTitle,
showSearch,
classNames,
styles
} = useBaseProps();
const selectContext = React.useContext(SelectContext);
const [inputChanged, setInputChanged] = React.useState(false);
const combobox = mode === 'combobox';
const displayValue = displayValues[0];
// Implement the same logic as the old SingleSelector
const mergedSearchValue = React.useMemo(() => {
if (combobox && activeValue && !inputChanged && triggerOpen) {
return activeValue;
}
return showSearch ? searchValue : '';
}, [combobox, activeValue, inputChanged, triggerOpen, searchValue, showSearch]);
const [optionClassName, optionStyle, optionTitle, hasOptionStyle] = React.useMemo(() => {
let className;
let style;
let titleValue;
if (displayValue && selectContext?.flattenOptions) {
const option = selectContext.flattenOptions.find(opt => opt.value === displayValue.value);
if (option?.data) {
className = option.data.className;
style = option.data.style;
titleValue = getTitle(option.data);
}
}
if (displayValue && !titleValue) {
titleValue = getTitle(displayValue);
}
if (rootTitle !== undefined) {
titleValue = rootTitle;
}
const nextHasStyle = !!className || !!style;
return [className, style, titleValue, nextHasStyle];
}, [displayValue, selectContext?.flattenOptions, rootTitle]);
React.useEffect(() => {
if (combobox) {
setInputChanged(false);
}
}, [combobox, activeValue]);
// ========================== Render ==========================
const showHasValueCls = displayValue && displayValue.label !== null && displayValue.label !== undefined && String(displayValue.label).trim() !== '';
// Render value
// Only render value when not using custom input in combobox mode
const shouldRenderValue = !(combobox && components?.input);
const renderValue = shouldRenderValue ? displayValue ? hasOptionStyle ? /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-content-value`, optionClassName),
style: {
...(mergedSearchValue ? {
visibility: 'hidden'
} : {}),
...optionStyle
},
title: optionTitle
}, displayValue.label) : displayValue.label : /*#__PURE__*/React.createElement(Placeholder, {
show: !mergedSearchValue
}) : null;
// Render
return /*#__PURE__*/React.createElement("div", {
className: clsx(`${prefixCls}-content`, showHasValueCls && `${prefixCls}-content-has-value`, mergedSearchValue && `${prefixCls}-content-has-search-value`, hasOptionStyle && `${prefixCls}-content-has-option-style`, classNames?.content),
style: styles?.content,
title: hasOptionStyle ? undefined : optionTitle
}, renderValue, /*#__PURE__*/React.createElement(Input, _extends({
ref: ref
}, inputProps, {
value: mergedSearchValue,
maxLength: mode === 'combobox' ? maxLength : undefined,
onChange: e => {
setInputChanged(true);
inputProps.onChange?.(e);
}
})));
});
export default SingleContent;
@@ -0,0 +1,6 @@
import * as React from 'react';
export interface SharedContentProps {
inputProps: React.InputHTMLAttributes<HTMLInputElement>;
}
declare const SelectContent: React.ForwardRefExoticComponent<React.RefAttributes<HTMLInputElement>>;
export default SelectContent;
@@ -0,0 +1,37 @@
import * as React from 'react';
import pickAttrs from "@rc-component/util/es/pickAttrs";
import SingleContent from "./SingleContent";
import MultipleContent from "./MultipleContent";
import { useSelectInputContext } from "../context";
import useBaseProps from "../../hooks/useBaseProps";
const SelectContent = /*#__PURE__*/React.forwardRef(function SelectContent(_, ref) {
const {
multiple,
onInputKeyDown,
tabIndex
} = useSelectInputContext();
const baseProps = useBaseProps();
const {
showSearch
} = baseProps;
const ariaProps = pickAttrs(baseProps, {
aria: true
});
const sharedInputProps = {
...ariaProps,
onKeyDown: onInputKeyDown,
readOnly: !showSearch,
tabIndex
};
if (multiple) {
return /*#__PURE__*/React.createElement(MultipleContent, {
ref: ref,
inputProps: sharedInputProps
});
}
return /*#__PURE__*/React.createElement(SingleContent, {
ref: ref,
inputProps: sharedInputProps
});
});
export default SelectContent;
@@ -0,0 +1,20 @@
import * as React from 'react';
export interface InputProps {
id?: string;
readOnly?: boolean;
value?: string;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
onFocus?: React.FocusEventHandler<HTMLInputElement>;
onBlur?: React.FocusEventHandler<HTMLInputElement>;
placeholder?: string;
className?: string;
style?: React.CSSProperties;
maxLength?: number;
/** width always match content width */
syncWidth?: boolean;
/** autoComplete for input */
autoComplete?: string;
}
declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
export default Input;
@@ -0,0 +1,216 @@
import * as React from 'react';
import { clsx } from 'clsx';
import { useSelectInputContext } from "./context";
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
import useBaseProps from "../hooks/useBaseProps";
import { composeRef } from "@rc-component/util/es/ref";
const Input = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
onChange,
onKeyDown,
onBlur,
style,
syncWidth,
value,
className,
autoComplete,
...restProps
} = props;
const {
prefixCls,
mode,
onSearch,
onSearchSubmit,
onInputBlur,
autoFocus,
tokenWithEnter,
placeholder,
components: {
input: InputComponent = 'input'
}
} = useSelectInputContext();
const {
id,
classNames,
styles,
open,
activeDescendantId,
role,
disabled
} = useBaseProps() || {};
const inputCls = clsx(`${prefixCls}-input`, classNames?.input, className);
// Used to handle input method composition status
const compositionStatusRef = React.useRef(false);
// Used to handle paste content, similar to original Selector implementation
const pastedTextRef = React.useRef(null);
// ============================== Refs ==============================
const inputRef = React.useRef(null);
React.useImperativeHandle(ref, () => inputRef.current);
// ============================== Data ==============================
// Handle input changes
const handleChange = event => {
let {
value: nextVal
} = event.target;
// Handle pasted text with tokenWithEnter, similar to original Selector implementation
if (tokenWithEnter && pastedTextRef.current && /[\r\n]/.test(pastedTextRef.current)) {
// CRLF will be treated as a single space for input element
const replacedText = pastedTextRef.current.replace(/[\r\n]+$/, '').replace(/\r\n/g, ' ').replace(/[\r\n]/g, ' ');
nextVal = nextVal.replace(replacedText, pastedTextRef.current);
}
// Reset pasted text reference
pastedTextRef.current = null;
// Call onSearch callback
if (onSearch) {
onSearch(nextVal, true, compositionStatusRef.current);
}
// Call original onChange callback
onChange?.(event);
};
// ============================ Keyboard ============================
// Handle keyboard events
const handleKeyDown = event => {
const {
key
} = event;
const {
value: nextVal
} = event.currentTarget;
// Handle Enter key submission - referencing Selector implementation
if (key === 'Enter' && mode === 'tags' && !open && !compositionStatusRef.current && onSearchSubmit) {
onSearchSubmit(nextVal);
}
// Call original onKeyDown callback
onKeyDown?.(event);
};
// Handle blur events
const handleBlur = event => {
// Call onInputBlur callback
onInputBlur?.();
// Call original onBlur callback
onBlur?.(event);
};
// Handle input method composition start
const handleCompositionStart = () => {
compositionStatusRef.current = true;
};
// Handle input method composition end
const handleCompositionEnd = event => {
compositionStatusRef.current = false;
// Trigger search when input method composition ends, similar to original Selector
if (mode !== 'combobox') {
const {
value: nextVal
} = event.currentTarget;
onSearch?.(nextVal, true, false);
}
};
// Handle paste events to track pasted content
const handlePaste = event => {
const {
clipboardData
} = event;
const pastedValue = clipboardData?.getData('text');
pastedTextRef.current = pastedValue || '';
};
// ============================= Width ==============================
const [widthCssVar, setWidthCssVar] = React.useState(undefined);
// When syncWidth is enabled, adjust input width based on content
useLayoutEffect(() => {
const input = inputRef.current;
if (syncWidth && input) {
input.style.width = '0px';
const scrollWidth = input.scrollWidth;
setWidthCssVar(scrollWidth);
// Reset input style
input.style.width = '';
}
}, [syncWidth, value]);
// ============================= Render =============================
// Extract shared input props
const sharedInputProps = {
id,
type: mode === 'combobox' ? 'text' : 'search',
...restProps,
ref: inputRef,
style: {
...styles?.input,
...style,
'--select-input-width': widthCssVar
},
autoFocus,
autoComplete: autoComplete || 'off',
className: inputCls,
disabled,
value: value || '',
onChange: handleChange,
onKeyDown: handleKeyDown,
onBlur: handleBlur,
onPaste: handlePaste,
onCompositionStart: handleCompositionStart,
onCompositionEnd: handleCompositionEnd,
// Accessibility attributes
role: role || 'combobox',
'aria-expanded': open || false,
'aria-haspopup': 'listbox',
'aria-owns': open ? `${id}_list` : undefined,
'aria-autocomplete': 'list',
'aria-controls': open ? `${id}_list` : undefined,
'aria-activedescendant': open ? activeDescendantId : undefined
};
// Handle different InputComponent types
if ( /*#__PURE__*/React.isValidElement(InputComponent)) {
// If InputComponent is a ReactElement, use cloneElement with merged props
const existingProps = InputComponent.props || {};
// Start with shared props as base
const mergedProps = {
placeholder: props.placeholder || placeholder,
...sharedInputProps,
...existingProps
};
// Batch update function calls
Object.keys(existingProps).forEach(key => {
const existingValue = existingProps[key];
if (typeof existingValue === 'function') {
// Merge event handlers
mergedProps[key] = (...args) => {
existingValue(...args);
sharedInputProps[key]?.(...args);
};
}
});
// Update ref
mergedProps.ref = composeRef(InputComponent.ref, sharedInputProps.ref);
return /*#__PURE__*/React.cloneElement(InputComponent, mergedProps);
}
// If InputComponent is a component type, render normally
const Component = InputComponent;
return /*#__PURE__*/React.createElement(Component, sharedInputProps);
});
export default Input;
@@ -0,0 +1,6 @@
import * as React from 'react';
import type { SelectInputProps } from '.';
export type ContentContextProps = SelectInputProps;
declare const SelectInputContext: React.Context<SelectInputProps>;
export declare function useSelectInputContext(): SelectInputProps;
export default SelectInputContext;
@@ -0,0 +1,6 @@
import * as React from 'react';
const SelectInputContext = /*#__PURE__*/React.createContext(null);
export function useSelectInputContext() {
return React.useContext(SelectInputContext);
}
export default SelectInputContext;
@@ -0,0 +1,39 @@
import * as React from 'react';
import type { DisplayValueType, Mode, RenderNode } from '../interface';
import type { ComponentsConfig } from '../hooks/useComponents';
export interface SelectInputRef {
focus: (options?: FocusOptions) => void;
blur: () => void;
nativeElement: HTMLDivElement;
}
export interface SelectInputProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'prefix'> {
prefixCls: string;
prefix?: React.ReactNode;
suffix?: React.ReactNode;
clearIcon?: React.ReactNode;
removeIcon?: RenderNode;
multiple?: boolean;
displayValues: DisplayValueType[];
placeholder?: React.ReactNode;
searchValue?: string;
activeValue?: string;
mode?: Mode;
autoClearSearchValue?: boolean;
onSearch?: (searchText: string, fromTyping: boolean, isCompositing: boolean) => void;
onSearchSubmit?: (searchText: string) => void;
onInputBlur?: () => void;
onClearMouseDown?: React.MouseEventHandler<HTMLElement>;
onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
onSelectorRemove?: (value: DisplayValueType) => void;
maxLength?: number;
autoFocus?: boolean;
/** Check if `tokenSeparators` contains `\n` or `\r\n` */
tokenWithEnter?: boolean;
className?: string;
style?: React.CSSProperties;
focused?: boolean;
components: ComponentsConfig;
children?: React.ReactElement;
}
declare const _default: React.ForwardRefExoticComponent<SelectInputProps & React.RefAttributes<SelectInputRef>>;
export default _default;
@@ -0,0 +1,217 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import Affix from "./Affix";
import SelectContent from "./Content";
import SelectInputContext from "./context";
import useBaseProps from "../hooks/useBaseProps";
import { omit, useEvent } from '@rc-component/util';
import KeyCode from "@rc-component/util/es/KeyCode";
import { isValidateOpenKey } from "../utils/keyUtil";
import { clsx } from 'clsx';
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
import { composeRef } from "@rc-component/util/es/ref";
import pickAttrs from "@rc-component/util/es/pickAttrs";
const DEFAULT_OMIT_PROPS = ['value', 'onChange', 'removeIcon', 'placeholder', 'maxTagCount', 'maxTagTextLength', 'maxTagPlaceholder', 'choiceTransitionName', 'onInputKeyDown', 'onPopupScroll', 'tabIndex', 'activeValue', 'onSelectorRemove', 'focused'];
export default /*#__PURE__*/React.forwardRef(function SelectInput(props, ref) {
const {
// Style
prefixCls,
className,
style,
// UI
prefix,
suffix,
clearIcon,
children,
// Data
multiple,
displayValues,
placeholder,
mode,
// Search
searchValue,
onSearch,
onSearchSubmit,
onInputBlur,
// Input
maxLength,
autoFocus,
// Events
onMouseDown,
onClearMouseDown,
onInputKeyDown,
onSelectorRemove,
// Token handling
tokenWithEnter,
// Components
components,
...restProps
} = props;
const {
triggerOpen,
toggleOpen,
showSearch,
disabled,
loading,
classNames,
styles
} = useBaseProps();
const rootRef = React.useRef(null);
const inputRef = React.useRef(null);
// Handle keyboard events similar to original Selector
const onInternalInputKeyDown = useEvent(event => {
const {
which
} = event;
// Compatible with multiple lines in TextArea
const isTextAreaElement = inputRef.current instanceof HTMLTextAreaElement;
// Prevent default behavior for up/down arrows when dropdown is open
if (!isTextAreaElement && triggerOpen && (which === KeyCode.UP || which === KeyCode.DOWN)) {
event.preventDefault();
}
// Call the original onInputKeyDown callback
if (onInputKeyDown) {
onInputKeyDown(event);
}
// Move within the text box for TextArea
if (isTextAreaElement && !triggerOpen && ~[KeyCode.UP, KeyCode.DOWN, KeyCode.LEFT, KeyCode.RIGHT].indexOf(which)) {
return;
}
// Open dropdown when a valid open key is pressed
const isModifier = event.ctrlKey || event.altKey || event.metaKey;
if (!isModifier && isValidateOpenKey(which)) {
toggleOpen(true);
}
});
// ====================== Refs ======================
React.useImperativeHandle(ref, () => {
return {
focus: options => {
// Focus the inner input if available, otherwise fall back to root div.
(inputRef.current || rootRef.current).focus?.(options);
},
blur: () => {
(inputRef.current || rootRef.current).blur?.();
},
// Use getDOM to handle nested nativeElement structure (e.g., when RootComponent is antd Input)
nativeElement: getDOM(rootRef.current)
};
});
// ====================== Open ======================
const onInternalMouseDown = useEvent(event => {
if (!disabled) {
const inputDOM = getDOM(inputRef.current);
// https://github.com/ant-design/ant-design/issues/56002
// Tell `useSelectTriggerControl` to ignore this event
// When icon is dynamic render, the parentNode will miss
// so we need to mark the event directly
event.nativeEvent._ori_target = inputDOM;
const isClickOnInput = inputDOM === event.target || inputDOM?.contains(event.target);
if (inputDOM && !isClickOnInput) {
event.preventDefault();
}
// Check if we should prevent closing when clicking on selector
// Don't close if: open && not multiple && (combobox mode || showSearch)
const shouldPreventCloseOnSingle = triggerOpen && !multiple && (mode === 'combobox' || showSearch);
// Don't close if: open && multiple && click on input
const shouldPreventCloseOnMultipleInput = triggerOpen && multiple && isClickOnInput;
const shouldPreventClose = shouldPreventCloseOnSingle || shouldPreventCloseOnMultipleInput;
if (!event.nativeEvent._select_lazy) {
inputRef.current?.focus();
// Only toggle open if we should not prevent close
if (!shouldPreventClose) {
toggleOpen();
}
} else if (triggerOpen) {
// Lazy should also close when click clear icon
toggleOpen(false);
}
}
onMouseDown?.(event);
});
// =================== Components ===================
const {
root: RootComponent
} = components;
// ===================== Render =====================
const domProps = omit(restProps, DEFAULT_OMIT_PROPS);
const ariaProps = pickAttrs(domProps, {
aria: true
});
const ariaKeys = Object.keys(ariaProps);
// Create context value with wrapped callbacks
const contextValue = {
...props,
onInputKeyDown: onInternalInputKeyDown
};
if (RootComponent) {
const originProps = RootComponent.props || {};
const mergedProps = {
...originProps,
...domProps
};
Object.keys(originProps).forEach(key => {
const originVal = originProps[key];
const domVal = domProps[key];
if (typeof originVal === 'function' && typeof domVal === 'function') {
mergedProps[key] = (...args) => {
domVal(...args);
originVal(...args);
};
}
});
if ( /*#__PURE__*/React.isValidElement(RootComponent)) {
return /*#__PURE__*/React.cloneElement(RootComponent, {
...mergedProps,
ref: composeRef(RootComponent.ref, rootRef)
});
}
return /*#__PURE__*/React.createElement(RootComponent, _extends({}, mergedProps, {
ref: rootRef
}));
}
return /*#__PURE__*/React.createElement(SelectInputContext.Provider, {
value: contextValue
}, /*#__PURE__*/React.createElement("div", _extends({}, omit(domProps, ariaKeys), {
// Style
ref: rootRef,
className: className,
style: style
// Mouse Events
,
onMouseDown: onInternalMouseDown
}), /*#__PURE__*/React.createElement(Affix, {
className: clsx(`${prefixCls}-prefix`, classNames?.prefix),
style: styles?.prefix
}, prefix), /*#__PURE__*/React.createElement(SelectContent, {
ref: inputRef
}), /*#__PURE__*/React.createElement(Affix, {
className: clsx(`${prefixCls}-suffix`, {
[`${prefixCls}-suffix-loading`]: loading
}, classNames?.suffix),
style: styles?.suffix
}, suffix), clearIcon && /*#__PURE__*/React.createElement(Affix, {
className: clsx(`${prefixCls}-clear`, classNames?.clear),
style: styles?.clear,
onMouseDown: e => {
// Mark to tell not trigger open or focus
e.nativeEvent._select_lazy = true;
onClearMouseDown?.(e);
}
}, clearIcon), children));
});