diff options
Diffstat (limited to 'src/components/ui/select')
-rw-r--r-- | src/components/ui/select/index.tsx | 151 |
1 files changed, 76 insertions, 75 deletions
diff --git a/src/components/ui/select/index.tsx b/src/components/ui/select/index.tsx index 805836130..b22c15320 100644 --- a/src/components/ui/select/index.tsx +++ b/src/components/ui/select/index.tsx | |||
@@ -5,47 +5,15 @@ import { | |||
5 | } from '@mdi/js'; | 5 | } from '@mdi/js'; |
6 | import Icon from '@mdi/react'; | 6 | import Icon from '@mdi/react'; |
7 | import classnames from 'classnames'; | 7 | import classnames from 'classnames'; |
8 | import { ChangeEvent, Component, createRef } from 'react'; | 8 | import { ChangeEvent, Component, createRef, ReactElement } from 'react'; |
9 | import injectStyle, { WithStylesProps } from 'react-jss'; | 9 | import withStyles, { WithStylesProps } from 'react-jss'; |
10 | 10 | import { noop } from 'lodash'; | |
11 | import { Theme } from '../../../themes'; | 11 | import { Theme } from '../../../themes'; |
12 | import { IFormField } from '../typings/generic'; | 12 | import { IFormField } from '../typings/generic'; |
13 | |||
14 | import Error from '../error'; | 13 | import Error from '../error'; |
15 | import Label from '../label'; | 14 | import Label from '../label'; |
16 | import Wrapper from '../wrapper'; | 15 | import Wrapper from '../wrapper'; |
17 | 16 | ||
18 | interface IOptions { | ||
19 | [index: string]: string; | ||
20 | } | ||
21 | |||
22 | interface IData { | ||
23 | [index: string]: string; | ||
24 | } | ||
25 | |||
26 | interface IProps extends IFormField, WithStylesProps<typeof styles> { | ||
27 | actionText: string; | ||
28 | className?: string; | ||
29 | inputClassName?: string; | ||
30 | defaultValue?: string; | ||
31 | disabled?: boolean; | ||
32 | id?: string; | ||
33 | name: string; | ||
34 | options: IOptions; | ||
35 | value: string; | ||
36 | onChange: (event: ChangeEvent<HTMLInputElement>) => void; | ||
37 | showSearch: boolean; | ||
38 | data: IData; | ||
39 | } | ||
40 | |||
41 | interface IState { | ||
42 | open: boolean; | ||
43 | value: string; | ||
44 | needle: string; | ||
45 | selected: number; | ||
46 | options: IOptions; | ||
47 | } | ||
48 | |||
49 | let popupTransition: string = 'none'; | 17 | let popupTransition: string = 'none'; |
50 | let toggleTransition: string = 'none'; | 18 | let toggleTransition: string = 'none'; |
51 | 19 | ||
@@ -149,22 +117,38 @@ const styles = (theme: Theme) => ({ | |||
149 | input: {}, | 117 | input: {}, |
150 | }); | 118 | }); |
151 | 119 | ||
152 | class SelectComponent extends Component<IProps> { | 120 | interface IOptions { |
153 | public static defaultProps = { | 121 | [index: string]: string; |
154 | onChange: () => {}, | 122 | } |
155 | showLabel: true, | ||
156 | disabled: false, | ||
157 | error: '', | ||
158 | }; | ||
159 | |||
160 | state = { | ||
161 | open: false, | ||
162 | value: '', | ||
163 | needle: '', | ||
164 | selected: 0, | ||
165 | options: null, | ||
166 | }; | ||
167 | 123 | ||
124 | interface IData { | ||
125 | [index: string]: string; | ||
126 | } | ||
127 | |||
128 | interface IProps extends IFormField, WithStylesProps<typeof styles> { | ||
129 | actionText: string; | ||
130 | className?: string; | ||
131 | inputClassName?: string; | ||
132 | defaultValue?: string; | ||
133 | disabled?: boolean; | ||
134 | id?: string; | ||
135 | name: string; | ||
136 | options: IOptions; | ||
137 | value: string; | ||
138 | onChange: (event: ChangeEvent<HTMLInputElement> | string) => void; | ||
139 | showSearch: boolean; | ||
140 | data: IData; | ||
141 | } | ||
142 | |||
143 | interface IState { | ||
144 | open: boolean; | ||
145 | value: string; | ||
146 | needle: string; | ||
147 | selected: number; | ||
148 | options: IOptions | null; | ||
149 | } | ||
150 | |||
151 | class SelectComponent extends Component<IProps, IState> { | ||
168 | private componentRef = createRef<HTMLDivElement>(); | 152 | private componentRef = createRef<HTMLDivElement>(); |
169 | 153 | ||
170 | private inputRef = createRef<HTMLInputElement>(); | 154 | private inputRef = createRef<HTMLInputElement>(); |
@@ -175,9 +159,12 @@ class SelectComponent extends Component<IProps> { | |||
175 | 159 | ||
176 | private activeOptionRef = createRef<HTMLDivElement>(); | 160 | private activeOptionRef = createRef<HTMLDivElement>(); |
177 | 161 | ||
178 | private keyListener: any; | 162 | private keyListener: (e: KeyboardEvent) => void; |
179 | 163 | ||
180 | static getDerivedStateFromProps(nextProps: IProps, prevState: IProps) { | 164 | static getDerivedStateFromProps( |
165 | nextProps: IProps, | ||
166 | prevState: IProps, | ||
167 | ): Partial<IState> { | ||
181 | if (nextProps.value && nextProps.value !== prevState.value) { | 168 | if (nextProps.value && nextProps.value !== prevState.value) { |
182 | return { | 169 | return { |
183 | value: nextProps.value, | 170 | value: nextProps.value, |
@@ -189,7 +176,22 @@ class SelectComponent extends Component<IProps> { | |||
189 | }; | 176 | }; |
190 | } | 177 | } |
191 | 178 | ||
192 | componentDidUpdate() { | 179 | constructor(props: IProps) { |
180 | super(props); | ||
181 | |||
182 | this.state = { | ||
183 | open: false, | ||
184 | value: '', | ||
185 | needle: '', | ||
186 | selected: 0, | ||
187 | options: null, | ||
188 | }; | ||
189 | |||
190 | this.keyListener = noop; | ||
191 | this.arrowKeysHandler = this.arrowKeysHandler.bind(this); | ||
192 | } | ||
193 | |||
194 | componentDidUpdate(): void { | ||
193 | const { open } = this.state; | 195 | const { open } = this.state; |
194 | 196 | ||
195 | if (this.searchInputRef && this.searchInputRef.current && open) { | 197 | if (this.searchInputRef && this.searchInputRef.current && open) { |
@@ -197,22 +199,20 @@ class SelectComponent extends Component<IProps> { | |||
197 | } | 199 | } |
198 | } | 200 | } |
199 | 201 | ||
200 | componentDidMount() { | 202 | componentDidMount(): void { |
201 | if (this.inputRef && this.inputRef.current) { | 203 | if (this.inputRef && this.inputRef.current) { |
202 | const { data } = this.props; | 204 | const { data } = this.props; |
203 | 205 | ||
204 | if (data) { | 206 | if (data) { |
205 | Object.keys(data).map( | 207 | for (const key of Object.keys(data)) |
206 | // eslint-disable-next-line no-return-assign | 208 | this.inputRef.current!.dataset[key] = data[key]; |
207 | key => (this.inputRef.current!.dataset[key] = data[key]), | ||
208 | ); | ||
209 | } | 209 | } |
210 | } | 210 | } |
211 | 211 | ||
212 | window.addEventListener('keydown', this.arrowKeysHandler.bind(this), false); | 212 | window.addEventListener('keydown', this.arrowKeysHandler, false); |
213 | } | 213 | } |
214 | 214 | ||
215 | componentWillMount() { | 215 | UNSAFE_componentWillMount(): void { |
216 | const { value } = this.props; | 216 | const { value } = this.props; |
217 | 217 | ||
218 | if (this.componentRef && this.componentRef.current) { | 218 | if (this.componentRef && this.componentRef.current) { |
@@ -231,17 +231,16 @@ class SelectComponent extends Component<IProps> { | |||
231 | this.setFilter(); | 231 | this.setFilter(); |
232 | } | 232 | } |
233 | 233 | ||
234 | componentWillUnmount() { | 234 | componentWillUnmount(): void { |
235 | // eslint-disable-next-line unicorn/no-invalid-remove-event-listener | 235 | window.removeEventListener('keydown', this.arrowKeysHandler); |
236 | window.removeEventListener('keydown', this.arrowKeysHandler.bind(this)); | ||
237 | } | 236 | } |
238 | 237 | ||
239 | setFilter(needle = '') { | 238 | setFilter(needle = ''): void { |
240 | const { options } = this.props; | 239 | const { options } = this.props; |
241 | 240 | ||
242 | let filteredOptions = {}; | 241 | let filteredOptions = {}; |
243 | if (needle) { | 242 | if (needle) { |
244 | Object.keys(options).map(key => { | 243 | for (const key of Object.keys(options)) { |
245 | if ( | 244 | if ( |
246 | key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || | 245 | key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || |
247 | options[key] | 246 | options[key] |
@@ -252,7 +251,7 @@ class SelectComponent extends Component<IProps> { | |||
252 | [`${key}`]: options[key], | 251 | [`${key}`]: options[key], |
253 | }); | 252 | }); |
254 | } | 253 | } |
255 | }); | 254 | } |
256 | } else { | 255 | } else { |
257 | filteredOptions = options; | 256 | filteredOptions = options; |
258 | } | 257 | } |
@@ -264,7 +263,7 @@ class SelectComponent extends Component<IProps> { | |||
264 | }); | 263 | }); |
265 | } | 264 | } |
266 | 265 | ||
267 | select(key: string) { | 266 | select(key: string): void { |
268 | this.setState(() => ({ | 267 | this.setState(() => ({ |
269 | value: key, | 268 | value: key, |
270 | open: false, | 269 | open: false, |
@@ -273,11 +272,11 @@ class SelectComponent extends Component<IProps> { | |||
273 | this.setFilter(); | 272 | this.setFilter(); |
274 | 273 | ||
275 | if (this.props.onChange) { | 274 | if (this.props.onChange) { |
276 | this.props.onChange(key as any); | 275 | this.props.onChange(key); |
277 | } | 276 | } |
278 | } | 277 | } |
279 | 278 | ||
280 | arrowKeysHandler(e: KeyboardEvent) { | 279 | arrowKeysHandler(e: KeyboardEvent): void { |
281 | const { selected, open, options } = this.state; | 280 | const { selected, open, options } = this.state; |
282 | 281 | ||
283 | if (!open) return; | 282 | if (!open) return; |
@@ -329,22 +328,22 @@ class SelectComponent extends Component<IProps> { | |||
329 | } | 328 | } |
330 | } | 329 | } |
331 | 330 | ||
332 | render() { | 331 | render(): ReactElement { |
333 | const { | 332 | const { |
334 | actionText, | 333 | actionText, |
335 | classes, | 334 | classes, |
336 | className, | 335 | className, |
337 | defaultValue, | 336 | defaultValue, |
338 | disabled, | ||
339 | error, | ||
340 | id, | 337 | id, |
341 | inputClassName, | 338 | inputClassName, |
342 | name, | 339 | name, |
343 | label, | 340 | label, |
344 | showLabel, | ||
345 | showSearch, | 341 | showSearch, |
346 | onChange, | ||
347 | required, | 342 | required, |
343 | onChange = noop, | ||
344 | showLabel = true, | ||
345 | disabled = false, | ||
346 | error = '', | ||
348 | } = this.props; | 347 | } = this.props; |
349 | 348 | ||
350 | const { open, needle, value, selected, options } = this.state; | 349 | const { open, needle, value, selected, options } = this.state; |
@@ -440,6 +439,8 @@ class SelectComponent extends Component<IProps> { | |||
440 | })} | 439 | })} |
441 | onMouseOver={() => this.setState({ selected: i })} | 440 | onMouseOver={() => this.setState({ selected: i })} |
442 | ref={selected === i ? this.activeOptionRef : null} | 441 | ref={selected === i ? this.activeOptionRef : null} |
442 | onKeyUp={noop} | ||
443 | onFocus={noop} | ||
443 | > | 444 | > |
444 | {options![key]} | 445 | {options![key]} |
445 | </div> | 446 | </div> |
@@ -463,4 +464,4 @@ class SelectComponent extends Component<IProps> { | |||
463 | } | 464 | } |
464 | } | 465 | } |
465 | 466 | ||
466 | export default injectStyle(styles, { injectTheme: true })(SelectComponent); | 467 | export default withStyles(styles, { injectTheme: true })(SelectComponent); |