refs #13598 Merge branch 'origin/#13598-Left-side-panel'
[arvados.git] / src / components / search-bar / search-bar.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, Paper, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core';
7 import SearchIcon from '@material-ui/icons/Search';
8
9 interface SearchBarDataProps {
10     value: string;
11 }
12
13 interface SearchBarActionProps {
14     onSearch: (value: string) => any;
15     debounce?: number;
16 }
17
18 type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
19
20 interface SearchBarState {
21     value: string;
22 }
23
24 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
25
26 class SearchBar extends React.Component<SearchBarProps> {
27
28     state: SearchBarState = {
29         value: ""
30     };
31
32     timeout: number;
33
34     render() {
35         const { classes } = this.props;
36         return <Paper className={classes.container}>
37             <form onSubmit={this.handleSubmit}>
38                 <input
39                     className={classes.input}
40                     onChange={this.handleChange}
41                     placeholder="Search"
42                     value={this.state.value}
43                 />
44                 <IconButton className={classes.button}>
45                     <SearchIcon />
46                 </IconButton>
47             </form>
48         </Paper>;
49     }
50
51     componentDidMount() {
52         this.setState({value: this.props.value});
53     }
54
55     componentWillReceiveProps(nextProps: SearchBarProps) {
56         if (nextProps.value !== this.props.value) {
57             this.setState({ value: nextProps.value });
58         }
59     }
60
61     componentWillUnmount() {
62         clearTimeout(this.timeout);
63     }
64
65     handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
66         event.preventDefault();
67         clearTimeout(this.timeout);
68         this.props.onSearch(this.state.value);
69     }
70
71     handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
72         clearTimeout(this.timeout);
73         this.setState({ value: event.target.value });
74         this.timeout = window.setTimeout(
75             () => this.props.onSearch(this.state.value),
76             this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
77         );
78
79     }
80
81 }
82
83 type CssRules = 'container' | 'input' | 'button';
84
85 const styles: StyleRulesCallback<CssRules> = theme => {
86     return {
87         container: {
88             position: 'relative',
89             width: '100%'
90         },
91         input: {
92             border: 'none',
93             borderRadius: theme.spacing.unit / 4,
94             boxSizing: 'border-box',
95             padding: theme.spacing.unit,
96             paddingRight: theme.spacing.unit * 4,
97             width: '100%',
98         },
99         button: {
100             position: 'absolute',
101             top: theme.spacing.unit / 2,
102             right: theme.spacing.unit / 2,
103             width: theme.spacing.unit * 3,
104             height: theme.spacing.unit * 3
105         }
106     };
107 };
108
109 export default withStyles(styles)(SearchBar);