Skip to content

Commit

Permalink
fix: The Enter key lock cannot be released correctly when the custom …
Browse files Browse the repository at this point in the history
…input calls blur().
  • Loading branch information
LeeSSHH authored and 黎书行 committed Jan 13, 2025
1 parent 4779fb8 commit d69b841
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 159 deletions.
171 changes: 19 additions & 152 deletions docs/examples/combobox.tsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,22 @@
/* eslint-disable no-console */
import React from 'react';
import Select, { Option } from 'rc-select';
import '../../assets/index.less';
import Select from '@/Select';
import { useRef } from 'react';

class Combobox extends React.Component {
state = {
disabled: false,
value: '',
options: [],
const Demo: React.FC = () => {
const ref = useRef<HTMLInputElement>(null);
const onSelect = () => {
ref.current!.blur();
};

textareaRef = React.createRef<HTMLTextAreaElement>();

timeoutId: number;

componentDidMount() {
console.log('Ref:', this.textareaRef);
}

onChange = (value, option) => {
console.log('onChange', value, option);
this.setState({
value,
});
};

onKeyDown = (e) => {
const { value } = this.state;
if (e.keyCode === 13) {
console.log('onEnter', value);
}
};

onSelect = (v, option) => {
console.log('onSelect', v, option);
};

onSearch = (text: string) => {
console.log('onSearch:', text);
const getInputElement = () => {
return <input ref={ref} />;
};

onAsyncChange = (value) => {
window.clearTimeout(this.timeoutId);

this.setState({
options: [],
});

this.timeoutId = window.setTimeout(() => {
this.setState({
options: [{ value }, { value: `${value}-${value}` }],
});
}, 1000);
};

toggleDisabled = () => {
const { disabled } = this.state;
this.setState({
disabled: !disabled,
});
};

render() {
const { value, disabled } = this.state;
return (
<div>
<h2>combobox</h2>
<p>
<button type="button" onClick={this.toggleDisabled}>
toggle disabled
</button>
<button
type="button"
onClick={() => {
this.setState({ value: '' });
}}
>
reset
</button>
</p>
<Select
value={value}
mode="combobox"
onChange={this.onChange}
filterOption={(inputValue, option) => {
if (!inputValue) {
return true;
}
return (option.value as string).includes(inputValue);
}}
>
{['1', '2', '3'].map((i) => (
<Option value={i} key={i}>
{i}
</Option>
))}
</Select>
<div>
<Select
disabled={disabled}
style={{ width: 500 }}
onChange={this.onChange}
onSelect={this.onSelect}
onSearch={this.onSearch}
onInputKeyDown={this.onKeyDown}
notFoundContent=""
allowClear
placeholder="please input, max len: 10"
value={value}
maxLength={10}
mode="combobox"
backfill
onFocus={() => console.log('focus')}
onBlur={() => console.log('blur')}
>
<Option value="jack">
<b style={{ color: 'red' }}>jack</b>
</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>
disabled
</Option>
<Option value="yiminghe">yiminghe</Option>
<Option value="竹林星光">竹林星光</Option>
</Select>

<h3>Customize Input Element</h3>
<Select
mode="combobox"
style={{ width: 200 }}
getInputElement={() => (
<textarea style={{ background: 'red' }} rows={3} ref={this.textareaRef} />
)}
options={[{ value: 'light' }, { value: 'bamboo' }]}
allowClear
placeholder="2333"
/>

<h3>Async Input Element</h3>
<Select
mode="combobox"
notFoundContent={null}
style={{ width: 200 }}
options={this.state.options}
onChange={this.onAsyncChange}
/>
</div>
</div>
);
}
}

export default Combobox;
/* eslint-enable */
return (
<Select
options={[{ value: 'aa' }, { value: 'bb' }]}
onSelect={onSelect}
mode="combobox"
getInputElement={getInputElement}
/>
);
};

export default Demo;
14 changes: 10 additions & 4 deletions src/BaseSelect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -535,13 +535,13 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
}

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

if (isEnterKey) {
keyLockRef.current = true;
}

onKeyDown?.(event, ...rest);
};

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

const onSelectorBlur = () => {
// Unlock the Enter key after the selector blur; otherwise, the Enter key needs to be pressed twice to trigger the correct effect.
keyLockRef.current = false;
};

// ========================== Focus / Blur ==========================
/** Record real focus status */
const focusRef = React.useRef<boolean>(false);
Expand Down Expand Up @@ -813,6 +818,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
onSearchSubmit={onInternalSearchSubmit}
onRemove={onSelectorRemove}
tokenWithEnter={tokenWithEnter}
onSelectorBlur={onSelectorBlur}
/>
)}
</SelectTrigger>
Expand Down
9 changes: 9 additions & 0 deletions src/Selector/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface InputProps {
onMouseDown: React.MouseEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
onChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
onPaste: React.ClipboardEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
onBlur: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLElement>;
onCompositionStart: React.CompositionEventHandler<
HTMLInputElement | HTMLTextAreaElement | HTMLElement
>;
Expand Down Expand Up @@ -52,6 +53,7 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
onPaste,
onCompositionStart,
onCompositionEnd,
onBlur,
open,
attrs,
} = props;
Expand All @@ -66,6 +68,7 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
onMouseDown: onOriginMouseDown,
onCompositionStart: onOriginCompositionStart,
onCompositionEnd: onOriginCompositionEnd,
onBlur: onOriginBlur,
style,
} = originProps;

Expand Down Expand Up @@ -134,6 +137,12 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
}
},
onPaste,
onBlur(event: React.FocusEvent<HTMLElement>) {
onBlur(event);
if (onOriginBlur) {
onOriginBlur(event);
}
},
});

return inputNode;
Expand Down
2 changes: 2 additions & 0 deletions src/Selector/MultipleSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
onInputMouseDown,
onInputCompositionStart,
onInputCompositionEnd,
onInputBlur,
} = props;

const measureRef = React.useRef<HTMLSpanElement>(null);
Expand Down Expand Up @@ -231,6 +232,7 @@ const SelectSelector: React.FC<SelectorProps> = (props) => {
onPaste={onInputPaste}
onCompositionStart={onInputCompositionStart}
onCompositionEnd={onInputCompositionEnd}
onBlur={onInputBlur}
tabIndex={tabIndex}
attrs={pickAttrs(props, true)}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/Selector/SingleSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const SingleSelector: React.FC<SelectorProps> = (props) => {
onInputPaste,
onInputCompositionStart,
onInputCompositionEnd,
onInputBlur,
title,
} = props;

Expand Down Expand Up @@ -100,6 +101,7 @@ const SingleSelector: React.FC<SelectorProps> = (props) => {
onPaste={onInputPaste}
onCompositionStart={onInputCompositionStart}
onCompositionEnd={onInputCompositionEnd}
onBlur={onInputBlur}
tabIndex={tabIndex}
attrs={pickAttrs(props, true)}
maxLength={combobox ? maxLength : undefined}
Expand Down
6 changes: 5 additions & 1 deletion src/Selector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface InnerSelectorProps {
onInputPaste: React.ClipboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
onInputCompositionStart: React.CompositionEventHandler<HTMLInputElement | HTMLTextAreaElement>;
onInputCompositionEnd: React.CompositionEventHandler<HTMLInputElement | HTMLTextAreaElement>;
onInputBlur: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
}

export interface RefSelectorProps {
Expand Down Expand Up @@ -92,7 +93,8 @@ export interface SelectorProps {
onSearchSubmit?: (searchText: string) => void;
onRemove: (value: DisplayValueType) => void;
onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;

// on selector blur
onSelectorBlur?: () => void;
/**
* @private get real dom for trigger align.
* This may be removed after React provides replacement of `findDOMNode`
Expand All @@ -119,6 +121,7 @@ const Selector: React.ForwardRefRenderFunction<RefSelectorProps, SelectorProps>
onSearchSubmit,
onToggleOpen,
onInputKeyDown,
onSelectorBlur,

domRef,
} = props;
Expand Down Expand Up @@ -270,6 +273,7 @@ const Selector: React.ForwardRefRenderFunction<RefSelectorProps, SelectorProps>
onInputPaste,
onInputCompositionStart,
onInputCompositionEnd,
onInputBlur: onSelectorBlur,
};

const selectNode =
Expand Down
Loading

0 comments on commit d69b841

Please sign in to comment.