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