@rc-component/select 1.1.4 → 1.2.0-alpha.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/assets/index.css +63 -0
- package/assets/index.less +1 -0
- package/assets/patch.less +83 -0
- package/es/BaseSelect/index.d.ts +14 -3
- package/es/BaseSelect/index.js +137 -200
- package/es/OptionList.js +3 -3
- package/es/Select.d.ts +1 -1
- package/es/Select.js +5 -9
- package/es/SelectInput/Affix.d.ts +5 -0
- package/es/SelectInput/Affix.js +12 -0
- package/es/SelectInput/Content/MultipleContent.d.ts +4 -0
- package/es/SelectInput/Content/MultipleContent.js +152 -0
- package/es/SelectInput/Content/Placeholder.d.ts +5 -0
- package/es/SelectInput/Content/Placeholder.js +21 -0
- package/es/SelectInput/Content/SingleContent.d.ts +4 -0
- package/es/SelectInput/Content/SingleContent.js +98 -0
- package/es/SelectInput/Content/index.d.ts +6 -0
- package/es/SelectInput/Content/index.js +37 -0
- package/es/SelectInput/Input.d.ts +20 -0
- package/es/SelectInput/Input.js +214 -0
- package/es/SelectInput/context.d.ts +6 -0
- package/es/SelectInput/context.js +6 -0
- package/es/SelectInput/index.d.ts +39 -0
- package/es/SelectInput/index.js +189 -0
- package/es/SelectTrigger.d.ts +1 -0
- package/es/SelectTrigger.js +5 -3
- package/es/TransBtn.d.ts +10 -0
- package/es/TransBtn.js +12 -2
- package/es/hooks/useAllowClear.d.ts +8 -7
- package/es/hooks/useAllowClear.js +21 -23
- package/es/hooks/useBaseProps.d.ts +1 -0
- package/es/hooks/useComponents.d.ts +12 -0
- package/es/hooks/useComponents.js +23 -0
- package/es/hooks/useOpen.d.ts +15 -0
- package/es/hooks/useOpen.js +76 -0
- package/es/hooks/useSearchConfig.d.ts +2 -2
- package/es/hooks/useSearchConfig.js +3 -3
- package/es/hooks/useSelectTriggerControl.d.ts +1 -1
- package/es/hooks/useSelectTriggerControl.js +16 -21
- package/es/utils/keyUtil.js +4 -0
- package/lib/BaseSelect/index.d.ts +14 -3
- package/lib/BaseSelect/index.js +137 -201
- package/lib/OptionList.js +3 -3
- package/lib/Select.d.ts +1 -1
- package/lib/Select.js +5 -9
- package/lib/SelectInput/Affix.d.ts +5 -0
- package/lib/{hooks/useLayoutEffect.js → SelectInput/Affix.js} +11 -16
- package/lib/SelectInput/Content/MultipleContent.d.ts +4 -0
- package/lib/{Selector/MultipleSelector.js → SelectInput/Content/MultipleContent.js} +71 -104
- package/lib/SelectInput/Content/Placeholder.d.ts +5 -0
- package/lib/SelectInput/Content/Placeholder.js +29 -0
- package/lib/SelectInput/Content/SingleContent.d.ts +4 -0
- package/lib/SelectInput/Content/SingleContent.js +107 -0
- package/lib/SelectInput/Content/index.d.ts +6 -0
- package/lib/SelectInput/Content/index.js +46 -0
- package/lib/SelectInput/Input.d.ts +20 -0
- package/lib/SelectInput/Input.js +223 -0
- package/lib/SelectInput/context.d.ts +6 -0
- package/lib/SelectInput/context.js +15 -0
- package/lib/SelectInput/index.d.ts +39 -0
- package/lib/SelectInput/index.js +198 -0
- package/lib/SelectTrigger.d.ts +1 -0
- package/lib/SelectTrigger.js +5 -3
- package/lib/TransBtn.d.ts +10 -0
- package/lib/TransBtn.js +12 -3
- package/lib/hooks/useAllowClear.d.ts +8 -7
- package/lib/hooks/useAllowClear.js +21 -24
- package/lib/hooks/useBaseProps.d.ts +1 -0
- package/lib/hooks/useComponents.d.ts +12 -0
- package/lib/hooks/{useDelayReset.js → useComponents.js} +21 -30
- package/lib/hooks/useOpen.d.ts +15 -0
- package/lib/hooks/useOpen.js +82 -0
- package/lib/hooks/useSearchConfig.d.ts +2 -2
- package/lib/hooks/useSearchConfig.js +3 -3
- package/lib/hooks/useSelectTriggerControl.d.ts +1 -1
- package/lib/hooks/useSelectTriggerControl.js +16 -21
- package/lib/utils/keyUtil.js +4 -0
- package/package.json +5 -4
- package/es/Selector/Input.d.ts +0 -27
- package/es/Selector/Input.js +0 -61
- package/es/Selector/MultipleSelector.d.ts +0 -16
- package/es/Selector/MultipleSelector.js +0 -185
- package/es/Selector/SingleSelector.d.ts +0 -8
- package/es/Selector/SingleSelector.js +0 -104
- package/es/Selector/index.d.ts +0 -83
- package/es/Selector/index.js +0 -189
- package/es/hooks/useDelayReset.d.ts +0 -5
- package/es/hooks/useDelayReset.js +0 -33
- package/es/hooks/useLayoutEffect.d.ts +0 -5
- package/es/hooks/useLayoutEffect.js +0 -17
- package/lib/Selector/Input.d.ts +0 -27
- package/lib/Selector/Input.js +0 -70
- package/lib/Selector/MultipleSelector.d.ts +0 -16
- package/lib/Selector/SingleSelector.d.ts +0 -8
- package/lib/Selector/SingleSelector.js +0 -113
- package/lib/Selector/index.d.ts +0 -83
- package/lib/Selector/index.js +0 -196
- package/lib/hooks/useDelayReset.d.ts +0 -5
- package/lib/hooks/useLayoutEffect.d.ts +0 -5
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import Overflow from 'rc-overflow';
|
|
5
|
+
import Input from "../Input";
|
|
6
|
+
import { useSelectInputContext } from "../context";
|
|
7
|
+
import TransBtn from "../../TransBtn";
|
|
8
|
+
import { getTitle } from "../../utils/commonUtil";
|
|
9
|
+
import useBaseProps from "../../hooks/useBaseProps";
|
|
10
|
+
import Placeholder from "./Placeholder";
|
|
11
|
+
function itemKey(value) {
|
|
12
|
+
return value.key ?? value.value;
|
|
13
|
+
}
|
|
14
|
+
const onPreventMouseDown = event => {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
event.stopPropagation();
|
|
17
|
+
};
|
|
18
|
+
export default /*#__PURE__*/React.forwardRef(function MultipleContent({
|
|
19
|
+
inputProps
|
|
20
|
+
}, ref) {
|
|
21
|
+
const {
|
|
22
|
+
prefixCls,
|
|
23
|
+
displayValues,
|
|
24
|
+
searchValue,
|
|
25
|
+
mode,
|
|
26
|
+
onSelectorRemove,
|
|
27
|
+
removeIcon: removeIconFromContext
|
|
28
|
+
} = useSelectInputContext();
|
|
29
|
+
const {
|
|
30
|
+
disabled,
|
|
31
|
+
showSearch,
|
|
32
|
+
triggerOpen,
|
|
33
|
+
toggleOpen,
|
|
34
|
+
autoClearSearchValue,
|
|
35
|
+
tagRender: tagRenderFromContext,
|
|
36
|
+
maxTagPlaceholder: maxTagPlaceholderFromContext,
|
|
37
|
+
maxTagTextLength,
|
|
38
|
+
maxTagCount
|
|
39
|
+
} = useBaseProps();
|
|
40
|
+
const selectionItemPrefixCls = `${prefixCls}-selection-item`;
|
|
41
|
+
|
|
42
|
+
// ===================== Search ======================
|
|
43
|
+
// Apply autoClearSearchValue logic: when dropdown is closed and autoClearSearchValue is not false (default true), clear search value
|
|
44
|
+
let computedSearchValue = searchValue;
|
|
45
|
+
if (!triggerOpen && mode === 'multiple' && autoClearSearchValue !== false) {
|
|
46
|
+
computedSearchValue = '';
|
|
47
|
+
}
|
|
48
|
+
const inputValue = showSearch ? computedSearchValue || '' : '';
|
|
49
|
+
const inputEditable = showSearch && !disabled;
|
|
50
|
+
|
|
51
|
+
// Props from context with safe defaults
|
|
52
|
+
const removeIcon = removeIconFromContext ?? '×';
|
|
53
|
+
const maxTagPlaceholder = maxTagPlaceholderFromContext ?? (omittedValues => `+ ${omittedValues.length} ...`);
|
|
54
|
+
const tagRender = tagRenderFromContext;
|
|
55
|
+
const onToggleOpen = newOpen => {
|
|
56
|
+
toggleOpen(newOpen);
|
|
57
|
+
};
|
|
58
|
+
const onRemove = value => {
|
|
59
|
+
onSelectorRemove?.(value);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ======================== Item ========================
|
|
63
|
+
// >>> Render Selector Node. Includes Item & Rest
|
|
64
|
+
const defaultRenderSelector = (item, content, itemDisabled, closable, onClose) => /*#__PURE__*/React.createElement("span", {
|
|
65
|
+
title: getTitle(item),
|
|
66
|
+
className: clsx(selectionItemPrefixCls, {
|
|
67
|
+
[`${selectionItemPrefixCls}-disabled`]: itemDisabled
|
|
68
|
+
})
|
|
69
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
70
|
+
className: `${selectionItemPrefixCls}-content`
|
|
71
|
+
}, content), closable && /*#__PURE__*/React.createElement(TransBtn, {
|
|
72
|
+
className: `${selectionItemPrefixCls}-remove`,
|
|
73
|
+
onMouseDown: onPreventMouseDown,
|
|
74
|
+
onClick: onClose,
|
|
75
|
+
customizeIcon: removeIcon
|
|
76
|
+
}, "\xD7"));
|
|
77
|
+
const customizeRenderSelector = (value, content, itemDisabled, closable, onClose, isMaxTag, info) => {
|
|
78
|
+
const onMouseDown = e => {
|
|
79
|
+
onPreventMouseDown(e);
|
|
80
|
+
onToggleOpen(!triggerOpen);
|
|
81
|
+
};
|
|
82
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
83
|
+
onMouseDown: onMouseDown
|
|
84
|
+
}, tagRender({
|
|
85
|
+
label: content,
|
|
86
|
+
value,
|
|
87
|
+
index: info?.index,
|
|
88
|
+
disabled: itemDisabled,
|
|
89
|
+
closable,
|
|
90
|
+
onClose,
|
|
91
|
+
isMaxTag: !!isMaxTag
|
|
92
|
+
}));
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// ====================== Overflow ======================
|
|
96
|
+
const renderItem = (valueItem, info) => {
|
|
97
|
+
const {
|
|
98
|
+
disabled: itemDisabled,
|
|
99
|
+
label,
|
|
100
|
+
value
|
|
101
|
+
} = valueItem;
|
|
102
|
+
const closable = !disabled && !itemDisabled;
|
|
103
|
+
let displayLabel = label;
|
|
104
|
+
if (typeof maxTagTextLength === 'number') {
|
|
105
|
+
if (typeof label === 'string' || typeof label === 'number') {
|
|
106
|
+
const strLabel = String(displayLabel);
|
|
107
|
+
if (strLabel.length > maxTagTextLength) {
|
|
108
|
+
displayLabel = `${strLabel.slice(0, maxTagTextLength)}...`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const onClose = event => {
|
|
113
|
+
if (event) {
|
|
114
|
+
event.stopPropagation();
|
|
115
|
+
}
|
|
116
|
+
onRemove(valueItem);
|
|
117
|
+
};
|
|
118
|
+
return typeof tagRender === 'function' ? customizeRenderSelector(value, displayLabel, itemDisabled, closable, onClose, undefined, info) : defaultRenderSelector(valueItem, displayLabel, itemDisabled, closable, onClose);
|
|
119
|
+
};
|
|
120
|
+
const renderRest = omittedValues => {
|
|
121
|
+
// https://github.com/ant-design/ant-design/issues/48930
|
|
122
|
+
if (!displayValues.length) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const content = typeof maxTagPlaceholder === 'function' ? maxTagPlaceholder(omittedValues) : maxTagPlaceholder;
|
|
126
|
+
return typeof tagRender === 'function' ? customizeRenderSelector(undefined, content, false, false, undefined, true) : defaultRenderSelector({
|
|
127
|
+
title: content
|
|
128
|
+
}, content, false);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// ======================= Render =======================
|
|
132
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Placeholder, {
|
|
133
|
+
show: !searchValue || !triggerOpen
|
|
134
|
+
}), /*#__PURE__*/React.createElement(Overflow, {
|
|
135
|
+
prefixCls: `${prefixCls}-content`,
|
|
136
|
+
data: displayValues,
|
|
137
|
+
renderItem: renderItem,
|
|
138
|
+
renderRest: renderRest
|
|
139
|
+
// suffix={inputNode}
|
|
140
|
+
,
|
|
141
|
+
suffix: /*#__PURE__*/React.createElement(Input, _extends({
|
|
142
|
+
ref: ref,
|
|
143
|
+
disabled: disabled,
|
|
144
|
+
readOnly: !inputEditable
|
|
145
|
+
}, inputProps, {
|
|
146
|
+
value: inputValue || '',
|
|
147
|
+
syncWidth: true
|
|
148
|
+
})),
|
|
149
|
+
itemKey: itemKey,
|
|
150
|
+
maxCount: maxTagCount
|
|
151
|
+
}));
|
|
152
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useSelectInputContext } from "../context";
|
|
3
|
+
export default function Placeholder(props) {
|
|
4
|
+
const {
|
|
5
|
+
prefixCls,
|
|
6
|
+
placeholder,
|
|
7
|
+
displayValues
|
|
8
|
+
} = useSelectInputContext();
|
|
9
|
+
const {
|
|
10
|
+
show
|
|
11
|
+
} = props;
|
|
12
|
+
if (displayValues.length) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
16
|
+
className: `${prefixCls}-placeholder`,
|
|
17
|
+
style: {
|
|
18
|
+
visibility: show ? 'visible' : 'hidden'
|
|
19
|
+
}
|
|
20
|
+
}, placeholder);
|
|
21
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import Input from "../Input";
|
|
5
|
+
import { useSelectInputContext } from "../context";
|
|
6
|
+
import useBaseProps from "../../hooks/useBaseProps";
|
|
7
|
+
import Placeholder from "./Placeholder";
|
|
8
|
+
import SelectContext from "../../SelectContext";
|
|
9
|
+
import { getTitle } from "../../utils/commonUtil";
|
|
10
|
+
const SingleContent = /*#__PURE__*/React.forwardRef(({
|
|
11
|
+
inputProps
|
|
12
|
+
}, ref) => {
|
|
13
|
+
const {
|
|
14
|
+
prefixCls,
|
|
15
|
+
searchValue,
|
|
16
|
+
activeValue,
|
|
17
|
+
displayValues,
|
|
18
|
+
maxLength,
|
|
19
|
+
mode
|
|
20
|
+
} = useSelectInputContext();
|
|
21
|
+
const {
|
|
22
|
+
triggerOpen,
|
|
23
|
+
title: rootTitle,
|
|
24
|
+
showSearch
|
|
25
|
+
} = useBaseProps();
|
|
26
|
+
const selectContext = React.useContext(SelectContext);
|
|
27
|
+
const [inputChanged, setInputChanged] = React.useState(false);
|
|
28
|
+
const combobox = mode === 'combobox';
|
|
29
|
+
const displayValue = displayValues[0];
|
|
30
|
+
|
|
31
|
+
// Implement the same logic as the old SingleSelector
|
|
32
|
+
const mergedSearchValue = React.useMemo(() => {
|
|
33
|
+
if (combobox && activeValue && !inputChanged && triggerOpen) {
|
|
34
|
+
return activeValue;
|
|
35
|
+
}
|
|
36
|
+
return showSearch ? searchValue : '';
|
|
37
|
+
}, [combobox, activeValue, inputChanged, triggerOpen, searchValue, showSearch]);
|
|
38
|
+
|
|
39
|
+
// Extract option props, excluding label and value, and handle className/style merging
|
|
40
|
+
const optionProps = React.useMemo(() => {
|
|
41
|
+
let restProps = {
|
|
42
|
+
className: `${prefixCls}-content-value`,
|
|
43
|
+
style: {
|
|
44
|
+
visibility: mergedSearchValue ? 'hidden' : 'visible'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
if (displayValue && selectContext?.flattenOptions) {
|
|
48
|
+
const option = selectContext.flattenOptions.find(opt => opt.value === displayValue.value);
|
|
49
|
+
if (option?.data) {
|
|
50
|
+
const {
|
|
51
|
+
label,
|
|
52
|
+
value,
|
|
53
|
+
className,
|
|
54
|
+
style,
|
|
55
|
+
key,
|
|
56
|
+
...rest
|
|
57
|
+
} = option.data;
|
|
58
|
+
restProps = {
|
|
59
|
+
...restProps,
|
|
60
|
+
...rest,
|
|
61
|
+
title: getTitle(option.data),
|
|
62
|
+
className: clsx(restProps.className, className),
|
|
63
|
+
style: {
|
|
64
|
+
...restProps.style,
|
|
65
|
+
...style
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (displayValue && !restProps.title) {
|
|
71
|
+
restProps.title = getTitle(displayValue);
|
|
72
|
+
}
|
|
73
|
+
if (rootTitle !== undefined) {
|
|
74
|
+
restProps.title = rootTitle;
|
|
75
|
+
}
|
|
76
|
+
return restProps;
|
|
77
|
+
}, [displayValue, selectContext?.flattenOptions, prefixCls, mergedSearchValue, rootTitle]);
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
if (combobox) {
|
|
80
|
+
setInputChanged(false);
|
|
81
|
+
}
|
|
82
|
+
}, [combobox, activeValue]);
|
|
83
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
84
|
+
className: `${prefixCls}-content`
|
|
85
|
+
}, displayValue ? /*#__PURE__*/React.createElement("div", optionProps, displayValue.label) : /*#__PURE__*/React.createElement(Placeholder, {
|
|
86
|
+
show: !mergedSearchValue
|
|
87
|
+
}), /*#__PURE__*/React.createElement(Input, _extends({
|
|
88
|
+
ref: ref
|
|
89
|
+
}, inputProps, {
|
|
90
|
+
value: mergedSearchValue,
|
|
91
|
+
maxLength: mode === 'combobox' ? maxLength : undefined,
|
|
92
|
+
onChange: e => {
|
|
93
|
+
setInputChanged(true);
|
|
94
|
+
inputProps.onChange?.(e);
|
|
95
|
+
}
|
|
96
|
+
})));
|
|
97
|
+
});
|
|
98
|
+
export default SingleContent;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
|
3
|
+
import SingleContent from "./SingleContent";
|
|
4
|
+
import MultipleContent from "./MultipleContent";
|
|
5
|
+
import { useSelectInputContext } from "../context";
|
|
6
|
+
import useBaseProps from "../../hooks/useBaseProps";
|
|
7
|
+
const SelectContent = /*#__PURE__*/React.forwardRef(function SelectContent(_, ref) {
|
|
8
|
+
const {
|
|
9
|
+
multiple,
|
|
10
|
+
onInputKeyDown,
|
|
11
|
+
tabIndex
|
|
12
|
+
} = useSelectInputContext();
|
|
13
|
+
const baseProps = useBaseProps();
|
|
14
|
+
const {
|
|
15
|
+
showSearch
|
|
16
|
+
} = baseProps;
|
|
17
|
+
const ariaProps = pickAttrs(baseProps, {
|
|
18
|
+
aria: true
|
|
19
|
+
});
|
|
20
|
+
const sharedInputProps = {
|
|
21
|
+
...ariaProps,
|
|
22
|
+
onKeyDown: onInputKeyDown,
|
|
23
|
+
readOnly: !showSearch,
|
|
24
|
+
tabIndex
|
|
25
|
+
};
|
|
26
|
+
if (multiple) {
|
|
27
|
+
return /*#__PURE__*/React.createElement(MultipleContent, {
|
|
28
|
+
ref: ref,
|
|
29
|
+
inputProps: sharedInputProps
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return /*#__PURE__*/React.createElement(SingleContent, {
|
|
33
|
+
ref: ref,
|
|
34
|
+
inputProps: sharedInputProps
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
export default SelectContent;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface InputProps {
|
|
3
|
+
id?: string;
|
|
4
|
+
readOnly?: boolean;
|
|
5
|
+
value?: string;
|
|
6
|
+
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
|
7
|
+
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
|
|
8
|
+
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
|
9
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
maxLength?: number;
|
|
14
|
+
/** width always match content width */
|
|
15
|
+
syncWidth?: boolean;
|
|
16
|
+
/** autoComplete for input */
|
|
17
|
+
autoComplete?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
20
|
+
export default Input;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { useSelectInputContext } from "./context";
|
|
4
|
+
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
|
|
5
|
+
import useBaseProps from "../hooks/useBaseProps";
|
|
6
|
+
import { composeRef } from "@rc-component/util/es/ref";
|
|
7
|
+
const Input = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
8
|
+
const {
|
|
9
|
+
onChange,
|
|
10
|
+
onKeyDown,
|
|
11
|
+
onBlur,
|
|
12
|
+
style,
|
|
13
|
+
syncWidth,
|
|
14
|
+
value,
|
|
15
|
+
className,
|
|
16
|
+
autoComplete,
|
|
17
|
+
...restProps
|
|
18
|
+
} = props;
|
|
19
|
+
const {
|
|
20
|
+
prefixCls,
|
|
21
|
+
mode,
|
|
22
|
+
onSearch,
|
|
23
|
+
onSearchSubmit,
|
|
24
|
+
onInputBlur,
|
|
25
|
+
autoFocus,
|
|
26
|
+
tokenWithEnter,
|
|
27
|
+
components: {
|
|
28
|
+
input: InputComponent = 'input'
|
|
29
|
+
}
|
|
30
|
+
} = useSelectInputContext();
|
|
31
|
+
const {
|
|
32
|
+
id,
|
|
33
|
+
classNames,
|
|
34
|
+
styles,
|
|
35
|
+
open,
|
|
36
|
+
activeDescendantId,
|
|
37
|
+
role,
|
|
38
|
+
disabled
|
|
39
|
+
} = useBaseProps() || {};
|
|
40
|
+
const inputCls = clsx(`${prefixCls}-input`, classNames?.input, className);
|
|
41
|
+
|
|
42
|
+
// Used to handle input method composition status
|
|
43
|
+
const compositionStatusRef = React.useRef(false);
|
|
44
|
+
|
|
45
|
+
// Used to handle paste content, similar to original Selector implementation
|
|
46
|
+
const pastedTextRef = React.useRef(null);
|
|
47
|
+
|
|
48
|
+
// ============================== Refs ==============================
|
|
49
|
+
const inputRef = React.useRef(null);
|
|
50
|
+
React.useImperativeHandle(ref, () => inputRef.current);
|
|
51
|
+
|
|
52
|
+
// ============================== Data ==============================
|
|
53
|
+
// Handle input changes
|
|
54
|
+
const handleChange = event => {
|
|
55
|
+
let {
|
|
56
|
+
value: nextVal
|
|
57
|
+
} = event.target;
|
|
58
|
+
|
|
59
|
+
// Handle pasted text with tokenWithEnter, similar to original Selector implementation
|
|
60
|
+
if (tokenWithEnter && pastedTextRef.current && /[\r\n]/.test(pastedTextRef.current)) {
|
|
61
|
+
// CRLF will be treated as a single space for input element
|
|
62
|
+
const replacedText = pastedTextRef.current.replace(/[\r\n]+$/, '').replace(/\r\n/g, ' ').replace(/[\r\n]/g, ' ');
|
|
63
|
+
nextVal = nextVal.replace(replacedText, pastedTextRef.current);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Reset pasted text reference
|
|
67
|
+
pastedTextRef.current = null;
|
|
68
|
+
|
|
69
|
+
// Call onSearch callback
|
|
70
|
+
if (onSearch) {
|
|
71
|
+
onSearch(nextVal, true, compositionStatusRef.current);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Call original onChange callback
|
|
75
|
+
onChange?.(event);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ============================ Keyboard ============================
|
|
79
|
+
// Handle keyboard events
|
|
80
|
+
const handleKeyDown = event => {
|
|
81
|
+
const {
|
|
82
|
+
key
|
|
83
|
+
} = event;
|
|
84
|
+
const {
|
|
85
|
+
value: nextVal
|
|
86
|
+
} = event.currentTarget;
|
|
87
|
+
|
|
88
|
+
// Handle Enter key submission - referencing Selector implementation
|
|
89
|
+
if (key === 'Enter' && mode === 'tags' && !compositionStatusRef.current && onSearchSubmit) {
|
|
90
|
+
onSearchSubmit(nextVal);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Call original onKeyDown callback
|
|
94
|
+
onKeyDown?.(event);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Handle blur events
|
|
98
|
+
const handleBlur = event => {
|
|
99
|
+
// Call onInputBlur callback
|
|
100
|
+
onInputBlur?.();
|
|
101
|
+
|
|
102
|
+
// Call original onBlur callback
|
|
103
|
+
onBlur?.(event);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Handle input method composition start
|
|
107
|
+
const handleCompositionStart = () => {
|
|
108
|
+
compositionStatusRef.current = true;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Handle input method composition end
|
|
112
|
+
const handleCompositionEnd = event => {
|
|
113
|
+
compositionStatusRef.current = false;
|
|
114
|
+
|
|
115
|
+
// Trigger search when input method composition ends, similar to original Selector
|
|
116
|
+
if (mode !== 'combobox') {
|
|
117
|
+
const {
|
|
118
|
+
value: nextVal
|
|
119
|
+
} = event.currentTarget;
|
|
120
|
+
onSearch?.(nextVal, true, false);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Handle paste events to track pasted content
|
|
125
|
+
const handlePaste = event => {
|
|
126
|
+
const {
|
|
127
|
+
clipboardData
|
|
128
|
+
} = event;
|
|
129
|
+
const pastedValue = clipboardData?.getData('text');
|
|
130
|
+
pastedTextRef.current = pastedValue || '';
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// ============================= Width ==============================
|
|
134
|
+
const [widthCssVar, setWidthCssVar] = React.useState(undefined);
|
|
135
|
+
|
|
136
|
+
// When syncWidth is enabled, adjust input width based on content
|
|
137
|
+
useLayoutEffect(() => {
|
|
138
|
+
const input = inputRef.current;
|
|
139
|
+
if (syncWidth && input) {
|
|
140
|
+
input.style.width = '0px';
|
|
141
|
+
const scrollWidth = input.scrollWidth;
|
|
142
|
+
setWidthCssVar(scrollWidth);
|
|
143
|
+
|
|
144
|
+
// Reset input style
|
|
145
|
+
input.style.width = '';
|
|
146
|
+
}
|
|
147
|
+
}, [syncWidth, value]);
|
|
148
|
+
|
|
149
|
+
// ============================= Render =============================
|
|
150
|
+
// Extract shared input props
|
|
151
|
+
const sharedInputProps = {
|
|
152
|
+
id,
|
|
153
|
+
type: mode === 'combobox' ? 'text' : 'search',
|
|
154
|
+
...restProps,
|
|
155
|
+
ref: inputRef,
|
|
156
|
+
style: {
|
|
157
|
+
...styles?.input,
|
|
158
|
+
...style,
|
|
159
|
+
'--select-input-width': widthCssVar
|
|
160
|
+
},
|
|
161
|
+
autoFocus,
|
|
162
|
+
autoComplete: autoComplete || 'off',
|
|
163
|
+
className: inputCls,
|
|
164
|
+
disabled,
|
|
165
|
+
value: value || '',
|
|
166
|
+
onChange: handleChange,
|
|
167
|
+
onKeyDown: handleKeyDown,
|
|
168
|
+
onBlur: handleBlur,
|
|
169
|
+
onPaste: handlePaste,
|
|
170
|
+
onCompositionStart: handleCompositionStart,
|
|
171
|
+
onCompositionEnd: handleCompositionEnd,
|
|
172
|
+
// Accessibility attributes
|
|
173
|
+
role: role || 'combobox',
|
|
174
|
+
'aria-expanded': open || false,
|
|
175
|
+
'aria-haspopup': 'listbox',
|
|
176
|
+
'aria-owns': `${id}_list`,
|
|
177
|
+
'aria-autocomplete': 'list',
|
|
178
|
+
'aria-controls': `${id}_list`,
|
|
179
|
+
'aria-activedescendant': open ? activeDescendantId : undefined
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Handle different InputComponent types
|
|
183
|
+
if ( /*#__PURE__*/React.isValidElement(InputComponent)) {
|
|
184
|
+
// If InputComponent is a ReactElement, use cloneElement with merged props
|
|
185
|
+
const existingProps = InputComponent.props || {};
|
|
186
|
+
|
|
187
|
+
// Start with shared props as base
|
|
188
|
+
const mergedProps = {
|
|
189
|
+
...sharedInputProps,
|
|
190
|
+
...existingProps
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Batch update function calls
|
|
194
|
+
Object.keys(existingProps).forEach(key => {
|
|
195
|
+
const existingValue = existingProps[key];
|
|
196
|
+
if (typeof existingValue === 'function') {
|
|
197
|
+
// Merge event handlers
|
|
198
|
+
mergedProps[key] = (...args) => {
|
|
199
|
+
existingValue(...args);
|
|
200
|
+
sharedInputProps[key]?.(...args);
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Update ref
|
|
206
|
+
mergedProps.ref = composeRef(InputComponent.ref, sharedInputProps.ref);
|
|
207
|
+
return /*#__PURE__*/React.cloneElement(InputComponent, mergedProps);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// If InputComponent is a component type, render normally
|
|
211
|
+
const Component = InputComponent;
|
|
212
|
+
return /*#__PURE__*/React.createElement(Component, sharedInputProps);
|
|
213
|
+
});
|
|
214
|
+
export default Input;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { SelectInputProps } from '.';
|
|
3
|
+
export type ContentContextProps = SelectInputProps;
|
|
4
|
+
declare const SelectInputContext: React.Context<SelectInputProps>;
|
|
5
|
+
export declare function useSelectInputContext(): SelectInputProps;
|
|
6
|
+
export default SelectInputContext;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { DisplayValueType, Mode, RenderNode } from '../interface';
|
|
3
|
+
import type { ComponentsConfig } from '../hooks/useComponents';
|
|
4
|
+
export interface SelectInputRef {
|
|
5
|
+
focus: (options?: FocusOptions) => void;
|
|
6
|
+
blur: () => void;
|
|
7
|
+
nativeElement: HTMLDivElement;
|
|
8
|
+
}
|
|
9
|
+
export interface SelectInputProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'prefix'> {
|
|
10
|
+
prefixCls: string;
|
|
11
|
+
prefix?: React.ReactNode;
|
|
12
|
+
suffix?: React.ReactNode;
|
|
13
|
+
clearIcon?: React.ReactNode;
|
|
14
|
+
removeIcon?: RenderNode;
|
|
15
|
+
multiple?: boolean;
|
|
16
|
+
displayValues: DisplayValueType[];
|
|
17
|
+
placeholder?: React.ReactNode;
|
|
18
|
+
searchValue?: string;
|
|
19
|
+
activeValue?: string;
|
|
20
|
+
mode?: Mode;
|
|
21
|
+
autoClearSearchValue?: boolean;
|
|
22
|
+
onSearch?: (searchText: string, fromTyping: boolean, isCompositing: boolean) => void;
|
|
23
|
+
onSearchSubmit?: (searchText: string) => void;
|
|
24
|
+
onInputBlur?: () => void;
|
|
25
|
+
onClearMouseDown?: React.MouseEventHandler<HTMLElement>;
|
|
26
|
+
onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
|
27
|
+
onSelectorRemove?: (value: DisplayValueType) => void;
|
|
28
|
+
maxLength?: number;
|
|
29
|
+
autoFocus?: boolean;
|
|
30
|
+
/** Check if `tokenSeparators` contains `\n` or `\r\n` */
|
|
31
|
+
tokenWithEnter?: boolean;
|
|
32
|
+
className?: string;
|
|
33
|
+
style?: React.CSSProperties;
|
|
34
|
+
focused?: boolean;
|
|
35
|
+
components: ComponentsConfig;
|
|
36
|
+
children?: React.ReactElement;
|
|
37
|
+
}
|
|
38
|
+
declare const _default: React.ForwardRefExoticComponent<SelectInputProps & React.RefAttributes<SelectInputRef>>;
|
|
39
|
+
export default _default;
|