merge conflicts
[arvados-workbench2.git] / src / components / search-input / search-input.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { IconButton, StyleRulesCallback, withStyles, WithStyles, FormControl, InputLabel, Input, InputAdornment } from '@material-ui/core';
7 import SearchIcon from '@material-ui/icons/Search';
8
9 type CssRules = 'container' | 'input' | 'button';
10
11 const styles: StyleRulesCallback<CssRules> = theme => {
12     return {
13         container: {
14             position: 'relative',
15             width: '100%'
16         },
17         input: {
18             border: 'none',
19             borderRadius: theme.spacing.unit / 4,
20             boxSizing: 'border-box',
21             padding: theme.spacing.unit,
22             paddingRight: theme.spacing.unit * 4,
23             width: '100%',
24         },
25         button: {
26             position: 'absolute',
27             top: theme.spacing.unit / 2,
28             right: theme.spacing.unit / 2,
29             width: theme.spacing.unit * 3,
30             height: theme.spacing.unit * 3
31         }
32     };
33 };
34
35 interface SearchInputDataProps {
36     value: string;
37 }
38
39 interface SearchInputActionProps {
40     onSearch: (value: string) => any;
41     debounce?: number;
42 }
43
44 type SearchInputProps = SearchInputDataProps & SearchInputActionProps & WithStyles<CssRules>;
45
46 interface SearchInputState {
47     value: string;
48 }
49
50 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
51
52 export const SearchInput = withStyles(styles)(
53     class extends React.Component<SearchInputProps> {
54         state: SearchInputState = {
55             value: ""
56         };
57
58         timeout: number;
59
60         render() {
61             const { classes } = this.props;
62             return <form onSubmit={this.handleSubmit}>
63                 <FormControl>
64                     <InputLabel>Search</InputLabel>
65                     <Input
66                         type="text"
67                         value={this.state.value}
68                         onChange={this.handleChange}
69                         endAdornment={
70                             <InputAdornment position="end">
71                                 <IconButton
72                                     onClick={this.handleSubmit}>
73                                     <SearchIcon/>
74                                 </IconButton>
75                             </InputAdornment>
76                         }/>
77                 </FormControl>
78             </form>;
79         }
80
81         componentDidMount() {
82             this.setState({ value: this.props.value });
83         }
84
85         componentWillReceiveProps(nextProps: SearchInputProps) {
86             if (nextProps.value !== this.props.value) {
87                 this.setState({ value: nextProps.value });
88             }
89         }
90
91         componentWillUnmount() {
92             clearTimeout(this.timeout);
93         }
94
95         handleSubmit = (event: React.FormEvent<HTMLElement>) => {
96             event.preventDefault();
97             clearTimeout(this.timeout);
98             this.props.onSearch(this.state.value);
99         }
100
101         handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
102             clearTimeout(this.timeout);
103             this.setState({ value: event.target.value });
104             this.timeout = window.setTimeout(
105                 () => this.props.onSearch(this.state.value),
106                 this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
107             );
108
109         }
110     }
111 );