Skip to content

Commit d69b841

Browse files
LeeSSHH黎书行
authored andcommitted
fix: The Enter key lock cannot be released correctly when the custom input calls blur().
1 parent 4779fb8 commit d69b841

File tree

8 files changed

+128
-159
lines changed

8 files changed

+128
-159
lines changed

docs/examples/combobox.tsx

Lines changed: 19 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,155 +1,22 @@
1-
/* eslint-disable no-console */
2-
import React from 'react';
3-
import Select, { Option } from 'rc-select';
4-
import '../../assets/index.less';
1+
import Select from '@/Select';
2+
import { useRef } from 'react';
53

6-
class Combobox extends React.Component {
7-
state = {
8-
disabled: false,
9-
value: '',
10-
options: [],
4+
const Demo: React.FC = () => {
5+
const ref = useRef<HTMLInputElement>(null);
6+
const onSelect = () => {
7+
ref.current!.blur();
118
};
12-
13-
textareaRef = React.createRef<HTMLTextAreaElement>();
14-
15-
timeoutId: number;
16-
17-
componentDidMount() {
18-
console.log('Ref:', this.textareaRef);
19-
}
20-
21-
onChange = (value, option) => {
22-
console.log('onChange', value, option);
23-
this.setState({
24-
value,
25-
});
26-
};
27-
28-
onKeyDown = (e) => {
29-
const { value } = this.state;
30-
if (e.keyCode === 13) {
31-
console.log('onEnter', value);
32-
}
33-
};
34-
35-
onSelect = (v, option) => {
36-
console.log('onSelect', v, option);
37-
};
38-
39-
onSearch = (text: string) => {
40-
console.log('onSearch:', text);
9+
const getInputElement = () => {
10+
return <input ref={ref} />;
4111
};
42-
43-
onAsyncChange = (value) => {
44-
window.clearTimeout(this.timeoutId);
45-
46-
this.setState({
47-
options: [],
48-
});
49-
50-
this.timeoutId = window.setTimeout(() => {
51-
this.setState({
52-
options: [{ value }, { value: `${value}-${value}` }],
53-
});
54-
}, 1000);
55-
};
56-
57-
toggleDisabled = () => {
58-
const { disabled } = this.state;
59-
this.setState({
60-
disabled: !disabled,
61-
});
62-
};
63-
64-
render() {
65-
const { value, disabled } = this.state;
66-
return (
67-
<div>
68-
<h2>combobox</h2>
69-
<p>
70-
<button type="button" onClick={this.toggleDisabled}>
71-
toggle disabled
72-
</button>
73-
<button
74-
type="button"
75-
onClick={() => {
76-
this.setState({ value: '' });
77-
}}
78-
>
79-
reset
80-
</button>
81-
</p>
82-
<Select
83-
value={value}
84-
mode="combobox"
85-
onChange={this.onChange}
86-
filterOption={(inputValue, option) => {
87-
if (!inputValue) {
88-
return true;
89-
}
90-
return (option.value as string).includes(inputValue);
91-
}}
92-
>
93-
{['1', '2', '3'].map((i) => (
94-
<Option value={i} key={i}>
95-
{i}
96-
</Option>
97-
))}
98-
</Select>
99-
<div>
100-
<Select
101-
disabled={disabled}
102-
style={{ width: 500 }}
103-
onChange={this.onChange}
104-
onSelect={this.onSelect}
105-
onSearch={this.onSearch}
106-
onInputKeyDown={this.onKeyDown}
107-
notFoundContent=""
108-
allowClear
109-
placeholder="please input, max len: 10"
110-
value={value}
111-
maxLength={10}
112-
mode="combobox"
113-
backfill
114-
onFocus={() => console.log('focus')}
115-
onBlur={() => console.log('blur')}
116-
>
117-
<Option value="jack">
118-
<b style={{ color: 'red' }}>jack</b>
119-
</Option>
120-
<Option value="lucy">lucy</Option>
121-
<Option value="disabled" disabled>
122-
disabled
123-
</Option>
124-
<Option value="yiminghe">yiminghe</Option>
125-
<Option value="竹林星光">竹林星光</Option>
126-
</Select>
127-
128-
<h3>Customize Input Element</h3>
129-
<Select
130-
mode="combobox"
131-
style={{ width: 200 }}
132-
getInputElement={() => (
133-
<textarea style={{ background: 'red' }} rows={3} ref={this.textareaRef} />
134-
)}
135-
options={[{ value: 'light' }, { value: 'bamboo' }]}
136-
allowClear
137-
placeholder="2333"
138-
/>
139-
140-
<h3>Async Input Element</h3>
141-
<Select
142-
mode="combobox"
143-
notFoundContent={null}
144-
style={{ width: 200 }}
145-
options={this.state.options}
146-
onChange={this.onAsyncChange}
147-
/>
148-
</div>
149-
</div>
150-
);
151-
}
152-
}
153-
154-
export default Combobox;
155-
/* eslint-enable */
12+
return (
13+
<Select
14+
options={[{ value: 'aa' }, { value: 'bb' }]}
15+
onSelect={onSelect}
16+
mode="combobox"
17+
getInputElement={getInputElement}
18+
/>
19+
);
20+
};
21+
22+
export default Demo;

src/BaseSelect/index.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -535,13 +535,13 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
535535
}
536536

537537
if (mergedOpen && (!isEnterKey || !keyLockRef.current)) {
538+
// Lock the Enter key after it is pressed to avoid repeated triggering of the onChange event.
539+
if (isEnterKey) {
540+
keyLockRef.current = true;
541+
}
538542
listRef.current?.onKeyDown(event, ...rest);
539543
}
540544

541-
if (isEnterKey) {
542-
keyLockRef.current = true;
543-
}
544-
545545
onKeyDown?.(event, ...rest);
546546
};
547547

@@ -566,6 +566,11 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
566566
});
567567
};
568568

569+
const onSelectorBlur = () => {
570+
// Unlock the Enter key after the selector blur; otherwise, the Enter key needs to be pressed twice to trigger the correct effect.
571+
keyLockRef.current = false;
572+
};
573+
569574
// ========================== Focus / Blur ==========================
570575
/** Record real focus status */
571576
const focusRef = React.useRef<boolean>(false);
@@ -813,6 +818,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
813818
onSearchSubmit={onInternalSearchSubmit}
814819
onRemove={onSelectorRemove}
815820
tokenWithEnter={tokenWithEnter}
821+
onSelectorBlur={onSelectorBlur}
816822
/>
817823
)}
818824
</SelectTrigger>

src/Selector/Input.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface InputProps {
2525
onMouseDown: React.MouseEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
2626
onChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
2727
onPaste: React.ClipboardEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
28+
onBlur: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
2829
onCompositionStart: React.CompositionEventHandler<
2930
HTMLInputElement | HTMLTextAreaElement | HTMLElement
3031
>;
@@ -52,6 +53,7 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
5253
onPaste,
5354
onCompositionStart,
5455
onCompositionEnd,
56+
onBlur,
5557
open,
5658
attrs,
5759
} = props;
@@ -66,6 +68,7 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
6668
onMouseDown: onOriginMouseDown,
6769
onCompositionStart: onOriginCompositionStart,
6870
onCompositionEnd: onOriginCompositionEnd,
71+
onBlur: onOriginBlur,
6972
style,
7073
} = originProps;
7174

@@ -134,6 +137,12 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
134137
}
135138
},
136139
onPaste,
140+
onBlur(event: React.FocusEvent<HTMLElement>) {
141+
onBlur(event);
142+
if (onOriginBlur) {
143+
onOriginBlur(event);
144+
}
145+
},
137146
});
138147

139148
return inputNode;

src/Selector/MultipleSelector.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
7272
onInputMouseDown,
7373
onInputCompositionStart,
7474
onInputCompositionEnd,
75+
onInputBlur,
7576
} = props;
7677

7778
const measureRef = React.useRef<HTMLSpanElement>(null);
@@ -231,6 +232,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
231232
onPaste={onInputPaste}
232233
onCompositionStart={onInputCompositionStart}
233234
onCompositionEnd={onInputCompositionEnd}
235+
onBlur={onInputBlur}
234236
tabIndex={tabIndex}
235237
attrs={pickAttrs(props, true)}
236238
/>

src/Selector/SingleSelector.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const SingleSelector: React.FC<SelectorProps> = (props) => {
3636
onInputPaste,
3737
onInputCompositionStart,
3838
onInputCompositionEnd,
39+
onInputBlur,
3940
title,
4041
} = props;
4142

@@ -100,6 +101,7 @@ const SingleSelector: React.FC<SelectorProps> = (props) => {
100101
onPaste={onInputPaste}
101102
onCompositionStart={onInputCompositionStart}
102103
onCompositionEnd={onInputCompositionEnd}
104+
onBlur={onInputBlur}
103105
tabIndex={tabIndex}
104106
attrs={pickAttrs(props, true)}
105107
maxLength={combobox ? maxLength : undefined}

src/Selector/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface InnerSelectorProps {
4444
onInputPaste: React.ClipboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
4545
onInputCompositionStart: React.CompositionEventHandler<HTMLInputElement | HTMLTextAreaElement>;
4646
onInputCompositionEnd: React.CompositionEventHandler<HTMLInputElement | HTMLTextAreaElement>;
47+
onInputBlur: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
4748
}
4849

4950
export interface RefSelectorProps {
@@ -92,7 +93,8 @@ export interface SelectorProps {
9293
onSearchSubmit?: (searchText: string) => void;
9394
onRemove: (value: DisplayValueType) => void;
9495
onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
95-
96+
// on selector blur
97+
onSelectorBlur?: () => void;
9698
/**
9799
* @private get real dom for trigger align.
98100
* This may be removed after React provides replacement of `findDOMNode`
@@ -119,6 +121,7 @@ const Selector: React.ForwardRefRenderFunction<RefSelectorProps, SelectorProps>
119121
onSearchSubmit,
120122
onToggleOpen,
121123
onInputKeyDown,
124+
onSelectorBlur,
122125

123126
domRef,
124127
} = props;
@@ -270,6 +273,7 @@ const Selector: React.ForwardRefRenderFunction<RefSelectorProps, SelectorProps>
270273
onInputPaste,
271274
onInputCompositionStart,
272275
onInputCompositionEnd,
276+
onInputBlur: onSelectorBlur,
273277
};
274278

275279
const selectNode =

0 commit comments

Comments
 (0)