diff options
Diffstat (limited to 'src/components/ui')
-rw-r--r-- | src/components/ui/SearchInput.tsx | 89 | ||||
-rw-r--r-- | src/components/ui/effects/Appear.tsx | 14 |
2 files changed, 45 insertions, 58 deletions
diff --git a/src/components/ui/SearchInput.tsx b/src/components/ui/SearchInput.tsx index 6a8c4de8f..39b8f95bf 100644 --- a/src/components/ui/SearchInput.tsx +++ b/src/components/ui/SearchInput.tsx | |||
@@ -1,43 +1,35 @@ | |||
1 | import { ChangeEvent, Component } from 'react'; | 1 | import { ChangeEvent, Component, ReactElement } from 'react'; |
2 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
4 | import { debounce } from 'lodash'; | 4 | import { debounce, noop } from 'lodash'; |
5 | import { mdiCloseCircleOutline, mdiMagnify } from '@mdi/js'; | 5 | import { mdiCloseCircleOutline, mdiMagnify } from '@mdi/js'; |
6 | import Icon from './icon'; | 6 | import Icon from './icon'; |
7 | 7 | ||
8 | type Props = { | 8 | interface IProps { |
9 | value: string; | 9 | value?: string; |
10 | placeholder: string; | 10 | placeholder: string; |
11 | className: string; | 11 | className?: string; |
12 | onChange: (e: ChangeEvent<HTMLInputElement>) => void; | 12 | onChange?: (e: string) => void; |
13 | onReset: () => void; | 13 | onReset: () => void; |
14 | name: string; | 14 | name?: string; |
15 | throttle: boolean; | 15 | throttle?: boolean; |
16 | throttleDelay: number; | 16 | throttleDelay?: number; |
17 | autoFocus: boolean; | 17 | autoFocus?: boolean; |
18 | }; | 18 | } |
19 | 19 | ||
20 | // Should this file be converted into the coding style similar to './toggle/index.tsx'? | 20 | interface IState { |
21 | class SearchInput extends Component<Props> { | 21 | value: string; |
22 | static defaultProps = { | 22 | } |
23 | value: '', | 23 | |
24 | placeholder: '', | 24 | @observer |
25 | className: '', | 25 | class SearchInput extends Component<IProps, IState> { |
26 | name: 'searchInput', | 26 | input: HTMLInputElement | null = null; |
27 | throttle: false, | 27 | |
28 | throttleDelay: 250, | 28 | constructor(props: IProps) { |
29 | onChange: () => null, | ||
30 | onReset: () => null, | ||
31 | autoFocus: false, | ||
32 | }; | ||
33 | |||
34 | input = null; | ||
35 | |||
36 | constructor(props: Props) { | ||
37 | super(props); | 29 | super(props); |
38 | 30 | ||
39 | this.state = { | 31 | this.state = { |
40 | value: props.value, | 32 | value: props.value || '', |
41 | }; | 33 | }; |
42 | 34 | ||
43 | this.throttledOnChange = debounce( | 35 | this.throttledOnChange = debounce( |
@@ -46,47 +38,47 @@ class SearchInput extends Component<Props> { | |||
46 | ); | 38 | ); |
47 | } | 39 | } |
48 | 40 | ||
49 | componentDidMount() { | 41 | componentDidMount(): void { |
50 | const { autoFocus } = this.props; | 42 | const { autoFocus = false } = this.props; |
51 | 43 | ||
52 | if (autoFocus) { | 44 | if (autoFocus && this.input) { |
53 | // @ts-expect-error Object is possibly 'null'. | ||
54 | this.input.focus(); | 45 | this.input.focus(); |
55 | } | 46 | } |
56 | } | 47 | } |
57 | 48 | ||
58 | onChange(e: ChangeEvent<HTMLInputElement>) { | 49 | onChange(e: ChangeEvent<HTMLInputElement>): void { |
59 | const { throttle, onChange } = this.props; | 50 | const { throttle = false, onChange = noop } = this.props; |
60 | const { value } = e.target; | 51 | const { value } = e.target; |
61 | this.setState({ value }); | 52 | this.setState({ value }); |
62 | 53 | ||
63 | if (throttle) { | 54 | if (throttle) { |
64 | e.persist(); | 55 | e.persist(); |
65 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'. | ||
66 | this.throttledOnChange(value); | 56 | this.throttledOnChange(value); |
67 | } else { | 57 | } else { |
68 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'. | ||
69 | onChange(value); | 58 | onChange(value); |
70 | } | 59 | } |
71 | } | 60 | } |
72 | 61 | ||
73 | throttledOnChange(e: ChangeEvent<HTMLInputElement>) { | 62 | throttledOnChange(e: string): void { |
74 | const { onChange } = this.props; | 63 | const { onChange = noop } = this.props; |
75 | 64 | ||
76 | onChange(e); | 65 | onChange(e); |
77 | } | 66 | } |
78 | 67 | ||
79 | reset() { | 68 | reset(): void { |
80 | const { onReset } = this.props; | 69 | const { onReset = noop } = this.props; |
81 | this.setState({ value: '' }); | 70 | this.setState({ value: '' }); |
82 | 71 | ||
83 | onReset(); | 72 | onReset(); |
84 | } | 73 | } |
85 | 74 | ||
86 | render() { | 75 | render(): ReactElement { |
87 | const { className, name, placeholder } = this.props; | 76 | const { |
88 | // @ts-expect-error Property 'value' does not exist on type 'Readonly<{}>'. | 77 | className = '', |
89 | const { value } = this.state; | 78 | name = 'searchInput', |
79 | placeholder = '', | ||
80 | } = this.props; | ||
81 | const { value = '' } = this.state; | ||
90 | 82 | ||
91 | return ( | 83 | return ( |
92 | <div className={classnames([className, 'search-input'])}> | 84 | <div className={classnames([className, 'search-input'])}> |
@@ -100,13 +92,12 @@ class SearchInput extends Component<Props> { | |||
100 | value={value} | 92 | value={value} |
101 | onChange={e => this.onChange(e)} | 93 | onChange={e => this.onChange(e)} |
102 | ref={ref => { | 94 | ref={ref => { |
103 | // @ts-expect-error Type 'HTMLInputElement | null' is not assignable to type 'null'. | ||
104 | this.input = ref; | 95 | this.input = ref; |
105 | }} | 96 | }} |
106 | /> | 97 | /> |
107 | </label> | 98 | </label> |
108 | {value.length > 0 && ( | 99 | {value.length > 0 && ( |
109 | <span onClick={() => this.reset()}> | 100 | <span onClick={() => this.reset()} onKeyDown={noop}> |
110 | <Icon icon={mdiCloseCircleOutline} /> | 101 | <Icon icon={mdiCloseCircleOutline} /> |
111 | </span> | 102 | </span> |
112 | )} | 103 | )} |
@@ -115,4 +106,4 @@ class SearchInput extends Component<Props> { | |||
115 | } | 106 | } |
116 | } | 107 | } |
117 | 108 | ||
118 | export default observer(SearchInput); | 109 | export default SearchInput; |
diff --git a/src/components/ui/effects/Appear.tsx b/src/components/ui/effects/Appear.tsx index 416017c83..bf097b6a6 100644 --- a/src/components/ui/effects/Appear.tsx +++ b/src/components/ui/effects/Appear.tsx | |||
@@ -1,16 +1,16 @@ | |||
1 | import { ReactNode, useEffect, useState } from 'react'; | 1 | import { ReactElement, ReactNode, useEffect, useState } from 'react'; |
2 | import { CSSTransitionGroup } from 'react-transition-group'; | 2 | import { CSSTransitionGroup } from 'react-transition-group'; |
3 | 3 | ||
4 | type Props = { | 4 | interface IProps { |
5 | children: ReactNode; | 5 | children: ReactNode; |
6 | transitionName: string; | 6 | transitionName?: string; |
7 | className?: string; | 7 | className?: string; |
8 | }; | 8 | } |
9 | const Appear = ({ | 9 | const Appear = ({ |
10 | children, | 10 | children, |
11 | transitionName = 'fadeIn', | 11 | transitionName = 'fadeIn', |
12 | className = '', | 12 | className = '', |
13 | }: Props) => { | 13 | }: IProps): ReactElement | null => { |
14 | const [mounted, setMounted] = useState(false); | 14 | const [mounted, setMounted] = useState(false); |
15 | 15 | ||
16 | useEffect(() => { | 16 | useEffect(() => { |
@@ -36,8 +36,4 @@ const Appear = ({ | |||
36 | ); | 36 | ); |
37 | }; | 37 | }; |
38 | 38 | ||
39 | Appear.defaultProps = { | ||
40 | className: '', | ||
41 | }; | ||
42 | |||
43 | export default Appear; | 39 | export default Appear; |