Skip to content

Commit ea5b5d6

Browse files
committed
support focus/blur
1 parent ecfde32 commit ea5b5d6

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

examples/basic.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,28 @@ import '../assets/index.less';
77
const { Option } = Mentions;
88

99
class Demo extends React.Component {
10-
state = {};
10+
onSelect = (option, prefix) => {
11+
console.log('Select:', prefix, '-', option.value);
12+
};
13+
14+
onFocus = () => {
15+
console.log('onFocus');
16+
};
17+
18+
onBlur = () => {
19+
console.log('onBlur');
20+
};
1121

1222
render() {
1323
return (
1424
<div>
15-
<Mentions autoFocus defaultValue="Hello World">
25+
<Mentions
26+
autoFocus
27+
defaultValue="Hello World"
28+
onSelect={this.onSelect}
29+
onFocus={this.onFocus}
30+
onBlur={this.onBlur}
31+
>
1632
<Option value="light">Light</Option>
1733
<Option value="bamboo">Bamboo</Option>
1834
<Option value="cat">Cat</Option>

src/DropdownMenu.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ interface DropdownMenuProps {
1313
* The focus is controlled by textarea to make accessibility easy.
1414
*/
1515
class DropdownMenu extends React.Component<DropdownMenuProps, {}> {
16-
public renderDropdown = ({ activeIndex, setActiveIndex, selectOption }: MentionsContextProps) => {
16+
public renderDropdown = ({
17+
activeIndex,
18+
setActiveIndex,
19+
selectOption,
20+
onFocus,
21+
}: MentionsContextProps) => {
1722
const { prefixCls, options } = this.props;
1823
const activeOption = options[activeIndex] || {};
1924

@@ -25,6 +30,7 @@ class DropdownMenu extends React.Component<DropdownMenuProps, {}> {
2530
const option = options.find(({ value }) => value === key);
2631
selectOption(option!);
2732
}}
33+
onFocus={onFocus}
2834
>
2935
{options.map((option, index) => {
3036
const { value, disabled, children, className, style } = option;

src/Mentions.tsx

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ export interface MentionsProps {
2020
defaultValue?: string;
2121
value?: string;
2222
onChange?: (text: string) => void;
23+
onSelect?: (option: OptionProps, prefix: string) => void;
2324
onSearch?: (text: string, prefix: string) => void;
25+
onFocus?: React.FocusEventHandler<HTMLTextAreaElement>;
26+
onBlur?: React.FocusEventHandler<HTMLTextAreaElement>;
2427
prefixCls?: string;
2528
prefix?: string | string[];
2629
className?: string;
@@ -37,6 +40,7 @@ interface MentionsState {
3740
measurePrefix: string;
3841
measureLocation: number;
3942
activeIndex: number;
43+
isFocus: boolean;
4044
}
4145
class Mentions extends React.Component<MentionsProps, MentionsState> {
4246
public static Option = Option;
@@ -61,6 +65,7 @@ class Mentions extends React.Component<MentionsProps, MentionsState> {
6165

6266
public textarea?: HTMLTextAreaElement;
6367
public measure?: HTMLDivElement;
68+
public focusId: number | undefined = undefined;
6469

6570
constructor(props: MentionsProps) {
6671
super(props);
@@ -71,6 +76,7 @@ class Mentions extends React.Component<MentionsProps, MentionsState> {
7176
measureText: null,
7277
measurePrefix: '',
7378
activeIndex: 0,
79+
isFocus: false,
7480
};
7581
}
7682

@@ -186,9 +192,41 @@ class Mentions extends React.Component<MentionsProps, MentionsState> {
186192
}
187193
};
188194

195+
public onInputFocus: React.FocusEventHandler<HTMLTextAreaElement> = event => {
196+
this.onFocus(event);
197+
};
198+
199+
public onInputBlur: React.FocusEventHandler<HTMLTextAreaElement> = event => {
200+
this.onBlur(event);
201+
};
202+
203+
public onDropdownFocus = () => {
204+
this.onFocus();
205+
};
206+
207+
public onFocus = (event?: React.FocusEvent<HTMLTextAreaElement>) => {
208+
window.clearTimeout(this.focusId);
209+
const { isFocus } = this.state;
210+
const { onFocus } = this.props;
211+
if (!isFocus && event && onFocus) {
212+
onFocus(event);
213+
}
214+
this.setState({ isFocus: true });
215+
};
216+
217+
public onBlur = (event: React.FocusEvent<HTMLTextAreaElement>) => {
218+
this.focusId = window.setTimeout(() => {
219+
const { onBlur } = this.props;
220+
this.setState({ isFocus: false });
221+
if (onBlur) {
222+
onBlur(event);
223+
}
224+
}, 0);
225+
};
226+
189227
public selectOption = (option: OptionProps) => {
190228
const { value, measureLocation, measurePrefix } = this.state;
191-
const { split } = this.props;
229+
const { split, onSelect } = this.props;
192230

193231
const { value: mentionValue = '' } = option;
194232
const { text, selectionLocation } = replaceWithMeasure(value, {
@@ -203,6 +241,10 @@ class Mentions extends React.Component<MentionsProps, MentionsState> {
203241
// We need restore the selection position
204242
setInputSelection(this.textarea!, selectionLocation);
205243
});
244+
245+
if (onSelect) {
246+
onSelect(option, measurePrefix);
247+
}
206248
};
207249

208250
public setActiveIndex = (activeIndex: number) => {
@@ -257,21 +299,21 @@ class Mentions extends React.Component<MentionsProps, MentionsState> {
257299

258300
public render() {
259301
const { value, measureLocation, measurePrefix, measuring, activeIndex } = this.state;
260-
const { prefixCls, className, style, ...restProps } = this.props;
261-
262-
const props = omit(restProps, ['onChange', 'prefix']);
302+
const { prefixCls, className, style, autoFocus } = this.props;
263303

264304
const options = measuring ? this.getOptions() : [];
265305

266306
return (
267307
<div className={classNames(prefixCls, className)} style={style}>
268308
<textarea
269-
{...props}
309+
autoFocus={autoFocus}
270310
ref={this.setTextAreaRef}
271311
value={value}
272312
onChange={this.onChange}
273313
onKeyDown={this.onKeyDown}
274314
onKeyUp={this.onKeyUp}
315+
onFocus={this.onInputFocus}
316+
onBlur={this.onInputBlur}
275317
/>
276318
{measuring && (
277319
<div ref={this.setMeasureRef} className={`${prefixCls}-measure`}>
@@ -281,6 +323,7 @@ class Mentions extends React.Component<MentionsProps, MentionsState> {
281323
activeIndex,
282324
setActiveIndex: this.setActiveIndex,
283325
selectOption: this.selectOption,
326+
onFocus: this.onDropdownFocus,
284327
}}
285328
>
286329
<KeywordTrigger prefixCls={prefixCls} options={options} visible={true}>

src/MentionsContext.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface MentionsContextProps {
55
activeIndex: number;
66
setActiveIndex: (index: number) => void;
77
selectOption: (option: OptionProps) => void;
8+
onFocus: () => void;
89
}
910

1011
const MentionsContext: Context<MentionsContextProps> = createReactContext({
@@ -15,6 +16,9 @@ const MentionsContext: Context<MentionsContextProps> = createReactContext({
1516
selectOption: (_: OptionProps) => {
1617
/* Do nothing */
1718
},
19+
onFocus: () => {
20+
/* Do nothing */
21+
},
1822
});
1923

2024
export const MentionsContextProvider = MentionsContext.Provider;

0 commit comments

Comments
 (0)