From 33ff5d7922a79d06a428454d2e7c0013500ddbaa Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 16 Mar 2020 23:11:34 +0800 Subject: [PATCH 1/6] Use ref --- src/Selector/Input.tsx | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index 3c76ed4eb..db012fe5d 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { composeRef } from 'rc-util/lib/ref'; type InputRef = HTMLInputElement | HTMLTextAreaElement; @@ -14,26 +15,9 @@ interface InputProps { open: boolean; tabIndex: number; - onKeyDown: React.KeyboardEventHandler< - HTMLInputElement | HTMLTextAreaElement | HTMLElement - >; - onMouseDown: React.MouseEventHandler< - HTMLInputElement | HTMLTextAreaElement | HTMLElement - >; - onChange: React.ChangeEventHandler< - HTMLInputElement | HTMLTextAreaElement | HTMLElement - >; -} - -function fillRef( - node: InputRef, - ref: React.LegacyRef | React.Ref, -) { - if (typeof ref === 'function') { - ref(node); - } else if (ref && typeof ref === 'object') { - (ref as any).current = node; - } + onKeyDown: React.KeyboardEventHandler; + onMouseDown: React.MouseEventHandler; + onChange: React.ChangeEventHandler; } const Input: React.RefForwardingComponent = ( @@ -66,14 +50,9 @@ const Input: React.RefForwardingComponent = ( }, } = inputNode; - function inputRef(node: InputRef) { - fillRef(node, ref); - fillRef(node, originRef); - } - inputNode = React.cloneElement(inputNode, { id, - ref: inputRef, + ref: composeRef(ref, originRef as any), disabled, tabIndex, autoComplete: 'off', From 99e505c3302f00e3cfa85ef0564c6059ccd6450d Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 16 Mar 2020 23:20:13 +0800 Subject: [PATCH 2/6] Use class --- src/Selector/Input.tsx | 142 +++++++++++++++++++++-------------------- src/Selector/index.tsx | 62 ++++++------------ 2 files changed, 90 insertions(+), 114 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index db012fe5d..585f6cb0a 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -20,78 +20,80 @@ interface InputProps { onChange: React.ChangeEventHandler; } -const Input: React.RefForwardingComponent = ( - { - prefixCls, - id, - inputElement, - disabled, - tabIndex, - autoFocus, - editable, - accessibilityIndex, - value, - onKeyDown, - onMouseDown, - onChange, - open, - }, - ref, -) => { - let inputNode: React.ComponentElement = inputElement || ; +class Input extends React.Component { + inputRef = React.createRef(); - const { - ref: originRef, - props: { - onKeyDown: onOriginKeyDown, - onChange: onOriginChange, - onMouseDown: onOriginMouseDown, - style, - }, - } = inputNode; + getInput = () => this.inputRef.current; - inputNode = React.cloneElement(inputNode, { - id, - ref: composeRef(ref, originRef as any), - disabled, - tabIndex, - autoComplete: 'off', - autoFocus, - className: `${prefixCls}-selection-search-input`, - style: { ...style, opacity: editable ? null : 0 }, - role: 'combobox', - 'aria-expanded': open, - 'aria-haspopup': 'listbox', - 'aria-owns': `${id}_list`, - 'aria-autocomplete': 'list', - 'aria-controls': `${id}_list`, - 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, - value: editable ? value : '', - readOnly: !editable, - onKeyDown: (event: React.KeyboardEvent) => { - onKeyDown(event); - if (onOriginKeyDown) { - onOriginKeyDown(event); - } - }, - onMouseDown: (event: React.MouseEvent) => { - onMouseDown(event); - if (onOriginMouseDown) { - onOriginMouseDown(event); - } - }, - onChange: (event: React.ChangeEvent) => { - onChange(event); - if (onOriginChange) { - onOriginChange(event); - } - }, - }); + render() { + const { + prefixCls, + id, + inputElement, + disabled, + tabIndex, + autoFocus, + editable, + accessibilityIndex, + value, + onKeyDown, + onMouseDown, + onChange, + open, + } = this.props; - return inputNode; -}; + let inputNode: React.ReactElement = inputElement || ; -const RefInput = React.forwardRef(Input); -RefInput.displayName = 'Input'; + const { + ref: originRef, + props: { + onKeyDown: onOriginKeyDown, + onChange: onOriginChange, + onMouseDown: onOriginMouseDown, + style, + }, + } = inputNode as any; -export default RefInput; + inputNode = React.cloneElement(inputNode, { + id, + ref: composeRef(this.inputRef, originRef as any), + disabled, + tabIndex, + autoComplete: 'off', + autoFocus, + className: `${prefixCls}-selection-search-input`, + style: { ...style, opacity: editable ? null : 0 }, + role: 'combobox', + 'aria-expanded': open, + 'aria-haspopup': 'listbox', + 'aria-owns': `${id}_list`, + 'aria-autocomplete': 'list', + 'aria-controls': `${id}_list`, + 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, + value: editable ? value : '', + readOnly: !editable, + onKeyDown: (event: React.KeyboardEvent) => { + onKeyDown(event); + if (onOriginKeyDown) { + onOriginKeyDown(event); + } + }, + onMouseDown: (event: React.MouseEvent) => { + onMouseDown(event); + if (onOriginMouseDown) { + onOriginMouseDown(event); + } + }, + onChange: (event: React.ChangeEvent) => { + onChange(event); + if (onOriginChange) { + onOriginChange(event); + } + }, + }); + + return inputNode; + } +} + +export default Input; diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index f84db265f..ba986de12 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -12,20 +12,17 @@ import * as React from 'react'; import KeyCode from 'rc-util/lib/KeyCode'; import MultipleSelector from './MultipleSelector'; import SingleSelector from './SingleSelector'; -import { - LabelValueType, - RawValueType, - CustomTagProps, -} from '../interface/generator'; +import { LabelValueType, RawValueType, CustomTagProps } from '../interface/generator'; import { RenderNode, Mode } from '../interface'; import useLock from '../hooks/useLock'; +import Input from './Input'; export interface InnerSelectorProps { prefixCls: string; id: string; mode: Mode; - inputRef: React.Ref; + inputRef: React.Ref; placeholder?: React.ReactNode; disabled?: boolean; autoFocus?: boolean; @@ -36,15 +33,9 @@ export interface InnerSelectorProps { open: boolean; tabIndex?: number; - onInputKeyDown: React.KeyboardEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; - onInputMouseDown: React.MouseEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; - onInputChange: React.ChangeEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; + onInputKeyDown: React.KeyboardEventHandler; + onInputMouseDown: React.MouseEventHandler; + onInputChange: React.ChangeEventHandler; } export interface RefSelectorProps { @@ -75,9 +66,7 @@ export interface SelectorProps { // Tags maxTagCount?: number; maxTagTextLength?: number; - maxTagPlaceholder?: - | React.ReactNode - | ((omittedValues: LabelValueType[]) => React.ReactNode); + maxTagPlaceholder?: React.ReactNode | ((omittedValues: LabelValueType[]) => React.ReactNode); tagRender?: (props: CustomTagProps) => React.ReactElement; // Motion @@ -87,9 +76,7 @@ export interface SelectorProps { /** `onSearch` returns go next step boolean to check if need do toggle open */ onSearch: (searchValue: string) => boolean; onSelect: (value: RawValueType, option: { selected: boolean }) => void; - onInputKeyDown?: React.KeyboardEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; + onInputKeyDown?: React.KeyboardEventHandler; /** * @private get real dom for trigger align. @@ -98,11 +85,8 @@ export interface SelectorProps { domRef: React.Ref; } -const Selector: React.RefForwardingComponent< - RefSelectorProps, - SelectorProps -> = (props, ref) => { - const inputRef = React.useRef(null); +const Selector: React.RefForwardingComponent = (props, ref) => { + const inputRef = React.useRef(null); const { prefixCls, @@ -120,19 +104,17 @@ const Selector: React.RefForwardingComponent< // ======================= Ref ======================= React.useImperativeHandle(ref, () => ({ focus: () => { - inputRef.current.focus(); + inputRef.current.getInput().focus(); }, blur: () => { - inputRef.current.blur(); + inputRef.current.getInput().blur(); }, })); // ====================== Input ====================== const [getInputMouseDown, setInputMouseDown] = useLock(0); - const onInternalInputKeyDown: React.KeyboardEventHandler< - HTMLInputElement - > = event => { + const onInternalInputKeyDown: React.KeyboardEventHandler = event => { const { which } = event; if (which === KeyCode.UP || which === KeyCode.DOWN) { @@ -143,11 +125,7 @@ const Selector: React.RefForwardingComponent< onInputKeyDown(event); } - if ( - ![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes( - which, - ) - ) { + if (![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes(which)) { onToggleOpen(true); } }; @@ -156,9 +134,7 @@ const Selector: React.RefForwardingComponent< * We can not use `findDOMNode` sine it will get warning, * have to use timer to check if is input element. */ - const onInternalInputMouseDown: React.MouseEventHandler< - HTMLInputElement - > = () => { + const onInternalInputMouseDown: React.MouseEventHandler = () => { setInputMouseDown(true); }; @@ -172,12 +148,12 @@ const Selector: React.RefForwardingComponent< // Should focus input if click the selector const onClick = ({ target }) => { if (target !== inputRef.current) { - inputRef.current.focus(); + inputRef.current.getInput().focus(); } }; const onMouseDown: React.MouseEventHandler = event => { - if (event.target !== inputRef.current && !getInputMouseDown()) { + if (event.target !== inputRef.current.getInput() && !getInputMouseDown()) { event.preventDefault(); } @@ -212,9 +188,7 @@ const Selector: React.RefForwardingComponent< ); }; -const ForwardSelector = React.forwardRef( - Selector, -); +const ForwardSelector = React.forwardRef(Selector); ForwardSelector.displayName = 'Selector'; export default ForwardSelector; From 1e72c1fb8bdf091e7e38c30ddedc5a2fb75ef8bf Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 16 Mar 2020 23:28:13 +0800 Subject: [PATCH 3/6] Revert "Use class" This reverts commit 99e505c3302f00e3cfa85ef0564c6059ccd6450d. --- src/Selector/Input.tsx | 142 ++++++++++++++++++++--------------------- src/Selector/index.tsx | 62 ++++++++++++------ 2 files changed, 114 insertions(+), 90 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index 585f6cb0a..db012fe5d 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -20,80 +20,78 @@ interface InputProps { onChange: React.ChangeEventHandler; } -class Input extends React.Component { - inputRef = React.createRef(); +const Input: React.RefForwardingComponent = ( + { + prefixCls, + id, + inputElement, + disabled, + tabIndex, + autoFocus, + editable, + accessibilityIndex, + value, + onKeyDown, + onMouseDown, + onChange, + open, + }, + ref, +) => { + let inputNode: React.ComponentElement = inputElement || ; - getInput = () => this.inputRef.current; + const { + ref: originRef, + props: { + onKeyDown: onOriginKeyDown, + onChange: onOriginChange, + onMouseDown: onOriginMouseDown, + style, + }, + } = inputNode; - render() { - const { - prefixCls, - id, - inputElement, - disabled, - tabIndex, - autoFocus, - editable, - accessibilityIndex, - value, - onKeyDown, - onMouseDown, - onChange, - open, - } = this.props; + inputNode = React.cloneElement(inputNode, { + id, + ref: composeRef(ref, originRef as any), + disabled, + tabIndex, + autoComplete: 'off', + autoFocus, + className: `${prefixCls}-selection-search-input`, + style: { ...style, opacity: editable ? null : 0 }, + role: 'combobox', + 'aria-expanded': open, + 'aria-haspopup': 'listbox', + 'aria-owns': `${id}_list`, + 'aria-autocomplete': 'list', + 'aria-controls': `${id}_list`, + 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, + value: editable ? value : '', + readOnly: !editable, + onKeyDown: (event: React.KeyboardEvent) => { + onKeyDown(event); + if (onOriginKeyDown) { + onOriginKeyDown(event); + } + }, + onMouseDown: (event: React.MouseEvent) => { + onMouseDown(event); + if (onOriginMouseDown) { + onOriginMouseDown(event); + } + }, + onChange: (event: React.ChangeEvent) => { + onChange(event); + if (onOriginChange) { + onOriginChange(event); + } + }, + }); - let inputNode: React.ReactElement = inputElement || ; + return inputNode; +}; - const { - ref: originRef, - props: { - onKeyDown: onOriginKeyDown, - onChange: onOriginChange, - onMouseDown: onOriginMouseDown, - style, - }, - } = inputNode as any; +const RefInput = React.forwardRef(Input); +RefInput.displayName = 'Input'; - inputNode = React.cloneElement(inputNode, { - id, - ref: composeRef(this.inputRef, originRef as any), - disabled, - tabIndex, - autoComplete: 'off', - autoFocus, - className: `${prefixCls}-selection-search-input`, - style: { ...style, opacity: editable ? null : 0 }, - role: 'combobox', - 'aria-expanded': open, - 'aria-haspopup': 'listbox', - 'aria-owns': `${id}_list`, - 'aria-autocomplete': 'list', - 'aria-controls': `${id}_list`, - 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, - value: editable ? value : '', - readOnly: !editable, - onKeyDown: (event: React.KeyboardEvent) => { - onKeyDown(event); - if (onOriginKeyDown) { - onOriginKeyDown(event); - } - }, - onMouseDown: (event: React.MouseEvent) => { - onMouseDown(event); - if (onOriginMouseDown) { - onOriginMouseDown(event); - } - }, - onChange: (event: React.ChangeEvent) => { - onChange(event); - if (onOriginChange) { - onOriginChange(event); - } - }, - }); - - return inputNode; - } -} - -export default Input; +export default RefInput; diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index ba986de12..f84db265f 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -12,17 +12,20 @@ import * as React from 'react'; import KeyCode from 'rc-util/lib/KeyCode'; import MultipleSelector from './MultipleSelector'; import SingleSelector from './SingleSelector'; -import { LabelValueType, RawValueType, CustomTagProps } from '../interface/generator'; +import { + LabelValueType, + RawValueType, + CustomTagProps, +} from '../interface/generator'; import { RenderNode, Mode } from '../interface'; import useLock from '../hooks/useLock'; -import Input from './Input'; export interface InnerSelectorProps { prefixCls: string; id: string; mode: Mode; - inputRef: React.Ref; + inputRef: React.Ref; placeholder?: React.ReactNode; disabled?: boolean; autoFocus?: boolean; @@ -33,9 +36,15 @@ export interface InnerSelectorProps { open: boolean; tabIndex?: number; - onInputKeyDown: React.KeyboardEventHandler; - onInputMouseDown: React.MouseEventHandler; - onInputChange: React.ChangeEventHandler; + onInputKeyDown: React.KeyboardEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; + onInputMouseDown: React.MouseEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; + onInputChange: React.ChangeEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; } export interface RefSelectorProps { @@ -66,7 +75,9 @@ export interface SelectorProps { // Tags maxTagCount?: number; maxTagTextLength?: number; - maxTagPlaceholder?: React.ReactNode | ((omittedValues: LabelValueType[]) => React.ReactNode); + maxTagPlaceholder?: + | React.ReactNode + | ((omittedValues: LabelValueType[]) => React.ReactNode); tagRender?: (props: CustomTagProps) => React.ReactElement; // Motion @@ -76,7 +87,9 @@ export interface SelectorProps { /** `onSearch` returns go next step boolean to check if need do toggle open */ onSearch: (searchValue: string) => boolean; onSelect: (value: RawValueType, option: { selected: boolean }) => void; - onInputKeyDown?: React.KeyboardEventHandler; + onInputKeyDown?: React.KeyboardEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; /** * @private get real dom for trigger align. @@ -85,8 +98,11 @@ export interface SelectorProps { domRef: React.Ref; } -const Selector: React.RefForwardingComponent = (props, ref) => { - const inputRef = React.useRef(null); +const Selector: React.RefForwardingComponent< + RefSelectorProps, + SelectorProps +> = (props, ref) => { + const inputRef = React.useRef(null); const { prefixCls, @@ -104,17 +120,19 @@ const Selector: React.RefForwardingComponent = // ======================= Ref ======================= React.useImperativeHandle(ref, () => ({ focus: () => { - inputRef.current.getInput().focus(); + inputRef.current.focus(); }, blur: () => { - inputRef.current.getInput().blur(); + inputRef.current.blur(); }, })); // ====================== Input ====================== const [getInputMouseDown, setInputMouseDown] = useLock(0); - const onInternalInputKeyDown: React.KeyboardEventHandler = event => { + const onInternalInputKeyDown: React.KeyboardEventHandler< + HTMLInputElement + > = event => { const { which } = event; if (which === KeyCode.UP || which === KeyCode.DOWN) { @@ -125,7 +143,11 @@ const Selector: React.RefForwardingComponent = onInputKeyDown(event); } - if (![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes(which)) { + if ( + ![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes( + which, + ) + ) { onToggleOpen(true); } }; @@ -134,7 +156,9 @@ const Selector: React.RefForwardingComponent = * We can not use `findDOMNode` sine it will get warning, * have to use timer to check if is input element. */ - const onInternalInputMouseDown: React.MouseEventHandler = () => { + const onInternalInputMouseDown: React.MouseEventHandler< + HTMLInputElement + > = () => { setInputMouseDown(true); }; @@ -148,12 +172,12 @@ const Selector: React.RefForwardingComponent = // Should focus input if click the selector const onClick = ({ target }) => { if (target !== inputRef.current) { - inputRef.current.getInput().focus(); + inputRef.current.focus(); } }; const onMouseDown: React.MouseEventHandler = event => { - if (event.target !== inputRef.current.getInput() && !getInputMouseDown()) { + if (event.target !== inputRef.current && !getInputMouseDown()) { event.preventDefault(); } @@ -188,7 +212,9 @@ const Selector: React.RefForwardingComponent = ); }; -const ForwardSelector = React.forwardRef(Selector); +const ForwardSelector = React.forwardRef( + Selector, +); ForwardSelector.displayName = 'Selector'; export default ForwardSelector; From ba14e482da19607f90fe399aeab66bf6ebed1798 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 16 Mar 2020 23:31:09 +0800 Subject: [PATCH 4/6] Revert "Revert "Use class"" This reverts commit 1e72c1fb8bdf091e7e38c30ddedc5a2fb75ef8bf. --- src/Selector/Input.tsx | 142 +++++++++++++++++++++-------------------- src/Selector/index.tsx | 62 ++++++------------ 2 files changed, 90 insertions(+), 114 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index db012fe5d..585f6cb0a 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -20,78 +20,80 @@ interface InputProps { onChange: React.ChangeEventHandler; } -const Input: React.RefForwardingComponent = ( - { - prefixCls, - id, - inputElement, - disabled, - tabIndex, - autoFocus, - editable, - accessibilityIndex, - value, - onKeyDown, - onMouseDown, - onChange, - open, - }, - ref, -) => { - let inputNode: React.ComponentElement = inputElement || ; +class Input extends React.Component { + inputRef = React.createRef(); - const { - ref: originRef, - props: { - onKeyDown: onOriginKeyDown, - onChange: onOriginChange, - onMouseDown: onOriginMouseDown, - style, - }, - } = inputNode; + getInput = () => this.inputRef.current; - inputNode = React.cloneElement(inputNode, { - id, - ref: composeRef(ref, originRef as any), - disabled, - tabIndex, - autoComplete: 'off', - autoFocus, - className: `${prefixCls}-selection-search-input`, - style: { ...style, opacity: editable ? null : 0 }, - role: 'combobox', - 'aria-expanded': open, - 'aria-haspopup': 'listbox', - 'aria-owns': `${id}_list`, - 'aria-autocomplete': 'list', - 'aria-controls': `${id}_list`, - 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, - value: editable ? value : '', - readOnly: !editable, - onKeyDown: (event: React.KeyboardEvent) => { - onKeyDown(event); - if (onOriginKeyDown) { - onOriginKeyDown(event); - } - }, - onMouseDown: (event: React.MouseEvent) => { - onMouseDown(event); - if (onOriginMouseDown) { - onOriginMouseDown(event); - } - }, - onChange: (event: React.ChangeEvent) => { - onChange(event); - if (onOriginChange) { - onOriginChange(event); - } - }, - }); + render() { + const { + prefixCls, + id, + inputElement, + disabled, + tabIndex, + autoFocus, + editable, + accessibilityIndex, + value, + onKeyDown, + onMouseDown, + onChange, + open, + } = this.props; - return inputNode; -}; + let inputNode: React.ReactElement = inputElement || ; -const RefInput = React.forwardRef(Input); -RefInput.displayName = 'Input'; + const { + ref: originRef, + props: { + onKeyDown: onOriginKeyDown, + onChange: onOriginChange, + onMouseDown: onOriginMouseDown, + style, + }, + } = inputNode as any; -export default RefInput; + inputNode = React.cloneElement(inputNode, { + id, + ref: composeRef(this.inputRef, originRef as any), + disabled, + tabIndex, + autoComplete: 'off', + autoFocus, + className: `${prefixCls}-selection-search-input`, + style: { ...style, opacity: editable ? null : 0 }, + role: 'combobox', + 'aria-expanded': open, + 'aria-haspopup': 'listbox', + 'aria-owns': `${id}_list`, + 'aria-autocomplete': 'list', + 'aria-controls': `${id}_list`, + 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, + value: editable ? value : '', + readOnly: !editable, + onKeyDown: (event: React.KeyboardEvent) => { + onKeyDown(event); + if (onOriginKeyDown) { + onOriginKeyDown(event); + } + }, + onMouseDown: (event: React.MouseEvent) => { + onMouseDown(event); + if (onOriginMouseDown) { + onOriginMouseDown(event); + } + }, + onChange: (event: React.ChangeEvent) => { + onChange(event); + if (onOriginChange) { + onOriginChange(event); + } + }, + }); + + return inputNode; + } +} + +export default Input; diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index f84db265f..ba986de12 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -12,20 +12,17 @@ import * as React from 'react'; import KeyCode from 'rc-util/lib/KeyCode'; import MultipleSelector from './MultipleSelector'; import SingleSelector from './SingleSelector'; -import { - LabelValueType, - RawValueType, - CustomTagProps, -} from '../interface/generator'; +import { LabelValueType, RawValueType, CustomTagProps } from '../interface/generator'; import { RenderNode, Mode } from '../interface'; import useLock from '../hooks/useLock'; +import Input from './Input'; export interface InnerSelectorProps { prefixCls: string; id: string; mode: Mode; - inputRef: React.Ref; + inputRef: React.Ref; placeholder?: React.ReactNode; disabled?: boolean; autoFocus?: boolean; @@ -36,15 +33,9 @@ export interface InnerSelectorProps { open: boolean; tabIndex?: number; - onInputKeyDown: React.KeyboardEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; - onInputMouseDown: React.MouseEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; - onInputChange: React.ChangeEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; + onInputKeyDown: React.KeyboardEventHandler; + onInputMouseDown: React.MouseEventHandler; + onInputChange: React.ChangeEventHandler; } export interface RefSelectorProps { @@ -75,9 +66,7 @@ export interface SelectorProps { // Tags maxTagCount?: number; maxTagTextLength?: number; - maxTagPlaceholder?: - | React.ReactNode - | ((omittedValues: LabelValueType[]) => React.ReactNode); + maxTagPlaceholder?: React.ReactNode | ((omittedValues: LabelValueType[]) => React.ReactNode); tagRender?: (props: CustomTagProps) => React.ReactElement; // Motion @@ -87,9 +76,7 @@ export interface SelectorProps { /** `onSearch` returns go next step boolean to check if need do toggle open */ onSearch: (searchValue: string) => boolean; onSelect: (value: RawValueType, option: { selected: boolean }) => void; - onInputKeyDown?: React.KeyboardEventHandler< - HTMLInputElement | HTMLTextAreaElement - >; + onInputKeyDown?: React.KeyboardEventHandler; /** * @private get real dom for trigger align. @@ -98,11 +85,8 @@ export interface SelectorProps { domRef: React.Ref; } -const Selector: React.RefForwardingComponent< - RefSelectorProps, - SelectorProps -> = (props, ref) => { - const inputRef = React.useRef(null); +const Selector: React.RefForwardingComponent = (props, ref) => { + const inputRef = React.useRef(null); const { prefixCls, @@ -120,19 +104,17 @@ const Selector: React.RefForwardingComponent< // ======================= Ref ======================= React.useImperativeHandle(ref, () => ({ focus: () => { - inputRef.current.focus(); + inputRef.current.getInput().focus(); }, blur: () => { - inputRef.current.blur(); + inputRef.current.getInput().blur(); }, })); // ====================== Input ====================== const [getInputMouseDown, setInputMouseDown] = useLock(0); - const onInternalInputKeyDown: React.KeyboardEventHandler< - HTMLInputElement - > = event => { + const onInternalInputKeyDown: React.KeyboardEventHandler = event => { const { which } = event; if (which === KeyCode.UP || which === KeyCode.DOWN) { @@ -143,11 +125,7 @@ const Selector: React.RefForwardingComponent< onInputKeyDown(event); } - if ( - ![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes( - which, - ) - ) { + if (![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes(which)) { onToggleOpen(true); } }; @@ -156,9 +134,7 @@ const Selector: React.RefForwardingComponent< * We can not use `findDOMNode` sine it will get warning, * have to use timer to check if is input element. */ - const onInternalInputMouseDown: React.MouseEventHandler< - HTMLInputElement - > = () => { + const onInternalInputMouseDown: React.MouseEventHandler = () => { setInputMouseDown(true); }; @@ -172,12 +148,12 @@ const Selector: React.RefForwardingComponent< // Should focus input if click the selector const onClick = ({ target }) => { if (target !== inputRef.current) { - inputRef.current.focus(); + inputRef.current.getInput().focus(); } }; const onMouseDown: React.MouseEventHandler = event => { - if (event.target !== inputRef.current && !getInputMouseDown()) { + if (event.target !== inputRef.current.getInput() && !getInputMouseDown()) { event.preventDefault(); } @@ -212,9 +188,7 @@ const Selector: React.RefForwardingComponent< ); }; -const ForwardSelector = React.forwardRef( - Selector, -); +const ForwardSelector = React.forwardRef(Selector); ForwardSelector.displayName = 'Selector'; export default ForwardSelector; From be574bbd15edec57e84b7e427eb5ce032379c638 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 16 Mar 2020 23:36:34 +0800 Subject: [PATCH 5/6] Revert "Revert "Revert "Use class""" This reverts commit ba14e482da19607f90fe399aeab66bf6ebed1798. --- src/Selector/Input.tsx | 142 ++++++++++++++++++++--------------------- src/Selector/index.tsx | 62 ++++++++++++------ 2 files changed, 114 insertions(+), 90 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index 585f6cb0a..db012fe5d 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -20,80 +20,78 @@ interface InputProps { onChange: React.ChangeEventHandler; } -class Input extends React.Component { - inputRef = React.createRef(); +const Input: React.RefForwardingComponent = ( + { + prefixCls, + id, + inputElement, + disabled, + tabIndex, + autoFocus, + editable, + accessibilityIndex, + value, + onKeyDown, + onMouseDown, + onChange, + open, + }, + ref, +) => { + let inputNode: React.ComponentElement = inputElement || ; - getInput = () => this.inputRef.current; + const { + ref: originRef, + props: { + onKeyDown: onOriginKeyDown, + onChange: onOriginChange, + onMouseDown: onOriginMouseDown, + style, + }, + } = inputNode; - render() { - const { - prefixCls, - id, - inputElement, - disabled, - tabIndex, - autoFocus, - editable, - accessibilityIndex, - value, - onKeyDown, - onMouseDown, - onChange, - open, - } = this.props; + inputNode = React.cloneElement(inputNode, { + id, + ref: composeRef(ref, originRef as any), + disabled, + tabIndex, + autoComplete: 'off', + autoFocus, + className: `${prefixCls}-selection-search-input`, + style: { ...style, opacity: editable ? null : 0 }, + role: 'combobox', + 'aria-expanded': open, + 'aria-haspopup': 'listbox', + 'aria-owns': `${id}_list`, + 'aria-autocomplete': 'list', + 'aria-controls': `${id}_list`, + 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, + value: editable ? value : '', + readOnly: !editable, + onKeyDown: (event: React.KeyboardEvent) => { + onKeyDown(event); + if (onOriginKeyDown) { + onOriginKeyDown(event); + } + }, + onMouseDown: (event: React.MouseEvent) => { + onMouseDown(event); + if (onOriginMouseDown) { + onOriginMouseDown(event); + } + }, + onChange: (event: React.ChangeEvent) => { + onChange(event); + if (onOriginChange) { + onOriginChange(event); + } + }, + }); - let inputNode: React.ReactElement = inputElement || ; + return inputNode; +}; - const { - ref: originRef, - props: { - onKeyDown: onOriginKeyDown, - onChange: onOriginChange, - onMouseDown: onOriginMouseDown, - style, - }, - } = inputNode as any; +const RefInput = React.forwardRef(Input); +RefInput.displayName = 'Input'; - inputNode = React.cloneElement(inputNode, { - id, - ref: composeRef(this.inputRef, originRef as any), - disabled, - tabIndex, - autoComplete: 'off', - autoFocus, - className: `${prefixCls}-selection-search-input`, - style: { ...style, opacity: editable ? null : 0 }, - role: 'combobox', - 'aria-expanded': open, - 'aria-haspopup': 'listbox', - 'aria-owns': `${id}_list`, - 'aria-autocomplete': 'list', - 'aria-controls': `${id}_list`, - 'aria-activedescendant': `${id}_list_${accessibilityIndex}`, - value: editable ? value : '', - readOnly: !editable, - onKeyDown: (event: React.KeyboardEvent) => { - onKeyDown(event); - if (onOriginKeyDown) { - onOriginKeyDown(event); - } - }, - onMouseDown: (event: React.MouseEvent) => { - onMouseDown(event); - if (onOriginMouseDown) { - onOriginMouseDown(event); - } - }, - onChange: (event: React.ChangeEvent) => { - onChange(event); - if (onOriginChange) { - onOriginChange(event); - } - }, - }); - - return inputNode; - } -} - -export default Input; +export default RefInput; diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index ba986de12..f84db265f 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -12,17 +12,20 @@ import * as React from 'react'; import KeyCode from 'rc-util/lib/KeyCode'; import MultipleSelector from './MultipleSelector'; import SingleSelector from './SingleSelector'; -import { LabelValueType, RawValueType, CustomTagProps } from '../interface/generator'; +import { + LabelValueType, + RawValueType, + CustomTagProps, +} from '../interface/generator'; import { RenderNode, Mode } from '../interface'; import useLock from '../hooks/useLock'; -import Input from './Input'; export interface InnerSelectorProps { prefixCls: string; id: string; mode: Mode; - inputRef: React.Ref; + inputRef: React.Ref; placeholder?: React.ReactNode; disabled?: boolean; autoFocus?: boolean; @@ -33,9 +36,15 @@ export interface InnerSelectorProps { open: boolean; tabIndex?: number; - onInputKeyDown: React.KeyboardEventHandler; - onInputMouseDown: React.MouseEventHandler; - onInputChange: React.ChangeEventHandler; + onInputKeyDown: React.KeyboardEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; + onInputMouseDown: React.MouseEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; + onInputChange: React.ChangeEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; } export interface RefSelectorProps { @@ -66,7 +75,9 @@ export interface SelectorProps { // Tags maxTagCount?: number; maxTagTextLength?: number; - maxTagPlaceholder?: React.ReactNode | ((omittedValues: LabelValueType[]) => React.ReactNode); + maxTagPlaceholder?: + | React.ReactNode + | ((omittedValues: LabelValueType[]) => React.ReactNode); tagRender?: (props: CustomTagProps) => React.ReactElement; // Motion @@ -76,7 +87,9 @@ export interface SelectorProps { /** `onSearch` returns go next step boolean to check if need do toggle open */ onSearch: (searchValue: string) => boolean; onSelect: (value: RawValueType, option: { selected: boolean }) => void; - onInputKeyDown?: React.KeyboardEventHandler; + onInputKeyDown?: React.KeyboardEventHandler< + HTMLInputElement | HTMLTextAreaElement + >; /** * @private get real dom for trigger align. @@ -85,8 +98,11 @@ export interface SelectorProps { domRef: React.Ref; } -const Selector: React.RefForwardingComponent = (props, ref) => { - const inputRef = React.useRef(null); +const Selector: React.RefForwardingComponent< + RefSelectorProps, + SelectorProps +> = (props, ref) => { + const inputRef = React.useRef(null); const { prefixCls, @@ -104,17 +120,19 @@ const Selector: React.RefForwardingComponent = // ======================= Ref ======================= React.useImperativeHandle(ref, () => ({ focus: () => { - inputRef.current.getInput().focus(); + inputRef.current.focus(); }, blur: () => { - inputRef.current.getInput().blur(); + inputRef.current.blur(); }, })); // ====================== Input ====================== const [getInputMouseDown, setInputMouseDown] = useLock(0); - const onInternalInputKeyDown: React.KeyboardEventHandler = event => { + const onInternalInputKeyDown: React.KeyboardEventHandler< + HTMLInputElement + > = event => { const { which } = event; if (which === KeyCode.UP || which === KeyCode.DOWN) { @@ -125,7 +143,11 @@ const Selector: React.RefForwardingComponent = onInputKeyDown(event); } - if (![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes(which)) { + if ( + ![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes( + which, + ) + ) { onToggleOpen(true); } }; @@ -134,7 +156,9 @@ const Selector: React.RefForwardingComponent = * We can not use `findDOMNode` sine it will get warning, * have to use timer to check if is input element. */ - const onInternalInputMouseDown: React.MouseEventHandler = () => { + const onInternalInputMouseDown: React.MouseEventHandler< + HTMLInputElement + > = () => { setInputMouseDown(true); }; @@ -148,12 +172,12 @@ const Selector: React.RefForwardingComponent = // Should focus input if click the selector const onClick = ({ target }) => { if (target !== inputRef.current) { - inputRef.current.getInput().focus(); + inputRef.current.focus(); } }; const onMouseDown: React.MouseEventHandler = event => { - if (event.target !== inputRef.current.getInput() && !getInputMouseDown()) { + if (event.target !== inputRef.current && !getInputMouseDown()) { event.preventDefault(); } @@ -188,7 +212,9 @@ const Selector: React.RefForwardingComponent = ); }; -const ForwardSelector = React.forwardRef(Selector); +const ForwardSelector = React.forwardRef( + Selector, +); ForwardSelector.displayName = 'Selector'; export default ForwardSelector; From 8962d6a50194d916a752fbb216a122a5ea40f5a0 Mon Sep 17 00:00:00 2001 From: zombiej Date: Tue, 17 Mar 2020 00:12:28 +0800 Subject: [PATCH 6/6] delay for update status --- src/OptionList.tsx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/OptionList.tsx b/src/OptionList.tsx index ece78413b..a8637c7f3 100644 --- a/src/OptionList.tsx +++ b/src/OptionList.tsx @@ -129,14 +129,21 @@ const OptionList: React.RefForwardingComponent< // Auto scroll to item position in single mode React.useEffect(() => { - if (!multiple && open && values.size === 1) { - const value: RawValueType = Array.from(values)[0]; - const index = memoFlattenOptions.findIndex( - ({ data }) => (data as OptionData).value === value, - ); - setActive(index); - scrollIntoView(index); - } + /** + * React will skip `onChange` when component update. + * `setActive` function will call root accessibility state update which makes re-render. + * So we need to delay to let Input component trigger onChange first. + */ + setTimeout(() => { + if (!multiple && open && values.size === 1) { + const value: RawValueType = Array.from(values)[0]; + const index = memoFlattenOptions.findIndex( + ({ data }) => (data as OptionData).value === value, + ); + setActive(index); + scrollIntoView(index); + } + }); }, [open]); // ========================== Values ==========================