basic-view-recent-queries
[arvados-workbench2.git] / src / views-components / search-bar / search-bar-view.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     ListItem, ListItemText, ListItemSecondaryAction,
15     ClickAwayListener
16 } from '@material-ui/core';
17 import SearchIcon from '@material-ui/icons/Search';
18 import { RemoveIcon } from '~/components/icon/icon';
19 import { SearchView } from '~/store/search-bar/search-bar-reducer';
20 import { SearchBarBasicView } from '~/views-components/search-bar/search-bar-basic-view';
21 import { SearchBarAdvancedView } from '~/views-components/search-bar/search-bar-advanced-view';
22 import { SearchBarAutocompleteView } from '~/views-components/search-bar/search-bar-autocomplete-view';
23
24 type CssRules = 'container' | 'input' | 'searchBar';
25
26 const styles: StyleRulesCallback<CssRules> = theme => {
27     return {
28         container: {
29             position: 'relative',
30             width: '100%',
31             borderRadius: '0px'
32         },
33         input: {
34             border: 'none',
35             padding: `0px ${theme.spacing.unit}px`
36         },
37         searchBar: {
38             height: '30px'
39         }
40     };
41 };
42
43 interface SearchBarDataProps {
44     value: string;
45     currentView: string;
46     open: boolean;
47 }
48
49 interface SearchBarActionProps {
50     onSearch: (value: string) => any;
51     debounce?: number;
52     onSetView: (currentView: string) => void;
53     openView: () => void;
54     closeView: () => void;
55     saveQuery: (query: string) => void;
56     loadQueries: () => string[];
57 }
58
59 type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
60
61 interface SearchBarState {
62     value: string;
63 }
64
65 interface RenderQueriesProps {
66     text: string;
67 }
68
69 export const RenderRecentQueries = (props: RenderQueriesProps) => {
70     return <ListItem button>
71         <ListItemText secondary={props.text} />
72     </ListItem>;
73 };
74
75
76 export const RenderSavedQueries = (props: RenderQueriesProps) => {
77     return <ListItem button>
78         <ListItemText secondary={props.text} />
79         <ListItemSecondaryAction>
80             <Tooltip title="Remove">
81                 <IconButton aria-label="Remove">
82                     <RemoveIcon />
83                 </IconButton>
84             </Tooltip>
85         </ListItemSecondaryAction>
86     </ListItem>;
87 };
88
89 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
90
91 export const SearchBarView = withStyles(styles)(
92     class extends React.Component<SearchBarProps> {
93         state: SearchBarState = {
94             value: ""
95         };
96
97         timeout: number;
98
99         render() {
100             const { classes, currentView, openView, closeView, open } = this.props;
101             return <ClickAwayListener onClickAway={() => closeView()}>
102                 <Paper className={classes.container} >
103                     <form onSubmit={this.handleSubmit} className={classes.searchBar}>
104                         <Input
105                             className={classes.input}
106                             onChange={this.handleChange}
107                             placeholder="Search"
108                             value={this.state.value}
109                             fullWidth={true}
110                             disableUnderline={true}
111                             onClick={() => openView()}
112                             endAdornment={
113                                 <InputAdornment position="end">
114                                     <Tooltip title='Search'>
115                                         <IconButton>
116                                             <SearchIcon />
117                                         </IconButton>
118                                     </Tooltip>
119                                 </InputAdornment>
120                             } />
121                         {open && this.getView(currentView)}
122                     </form>
123                 </Paper >
124             </ClickAwayListener>;
125         }
126
127         componentDidMount() {
128             this.setState({ value: this.props.value });
129         }
130
131         componentWillReceiveProps(nextProps: SearchBarProps) {
132             if (nextProps.value !== this.props.value) {
133                 this.setState({ value: nextProps.value });
134             }
135         }
136
137         componentWillUnmount() {
138             clearTimeout(this.timeout);
139         }
140
141         getView = (currentView: string) => {
142             switch (currentView) {
143                 case SearchView.BASIC:
144                     return <SearchBarBasicView setView={this.props.onSetView} recentQueries={this.props.loadQueries}/>;
145                 case SearchView.ADVANCED:
146                     return <SearchBarAdvancedView setView={this.props.onSetView} />;
147                 case SearchView.AUTOCOMPLETE:
148                     return <SearchBarAutocompleteView />;
149                 default:
150                     return <SearchBarBasicView setView={this.props.onSetView} recentQueries={this.props.loadQueries}/>;
151             }
152         }
153
154         handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
155                 event.preventDefault();
156                 clearTimeout(this.timeout);
157                 this.props.saveQuery(this.state.value);
158                 this.props.onSearch(this.state.value);
159                 this.props.loadQueries();
160             }
161
162         handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
163             clearTimeout(this.timeout);
164             this.setState({ value: event.target.value });
165             this.timeout = window.setTimeout(
166                 () => this.props.onSearch(this.state.value),
167                 this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
168             );
169             if (event.target.value.length > 0) {
170                 this.props.onSetView(SearchView.AUTOCOMPLETE);
171             } else {
172                 this.props.onSetView(SearchView.BASIC);
173             }
174         }
175     }
176 );