1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import * as React from 'react';
13 InputAdornment, Input,
14 ListItem, ListItemText, ListItemSecondaryAction,
16 } from '@material-ui/core';
17 import SearchIcon from '@material-ui/icons/Search';
18 import { RemoveIcon, EditSavedQueryIcon } 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, SearchBarAutocompleteViewDataProps } from '~/views-components/search-bar/search-bar-autocomplete-view';
23 import { ArvadosTheme } from '~/common/custom-theme';
24 import { SearchBarAdvanceFormData } from '~/models/search-bar';
26 type CssRules = 'container' | 'containerSearchViewOpened' | 'input' | 'view';
28 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => {
33 borderRadius: theme.spacing.unit / 2
35 containerSearchViewOpened: {
38 borderRadius: `${theme.spacing.unit / 2}px ${theme.spacing.unit / 2}px 0 0`
42 padding: `0px ${theme.spacing.unit}px`
52 type SearchBarDataProps = {
55 isPopoverOpen: boolean;
56 savedQueries: SearchBarAdvanceFormData[];
58 } & SearchBarAutocompleteViewDataProps;
60 interface SearchBarActionProps {
61 onSearch: (value: string) => any;
62 searchDataOnEnter: (value: string) => void;
64 onSetView: (currentView: string) => void;
65 closeView: () => void;
66 saveRecentQuery: (query: string) => void;
67 loadRecentQueries: () => string[];
68 saveQuery: (data: SearchBarAdvanceFormData) => void;
69 deleteSavedQuery: (id: number) => void;
70 openSearchView: () => void;
71 navigateTo: (uuid: string) => void;
72 editSavedQuery: (data: SearchBarAdvanceFormData, id: number) => void;
75 type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
77 interface SearchBarState {
81 interface RenderRecentQueriesProps {
83 onSearch: (searchValue: string) => void;
86 export const RenderRecentQueries = (props: RenderRecentQueriesProps) => {
87 return <ListItem button>
88 <ListItemText secondary={props.text} onClick={() => props.onSearch(props.text)} />
92 interface RenderAutocompleteItemsProps {
93 text: string | JSX.Element;
94 navigateTo: (uuid: string) => void;
98 export const RenderAutocompleteItems = (props: RenderAutocompleteItemsProps) => {
99 return <ListItem button>
100 <ListItemText secondary={props.text} onClick={() => props.navigateTo(props.uuid)} />
104 interface RenderSavedQueriesProps {
107 deleteSavedQuery: (id: number) => void;
108 onSearch: (searchValue: string) => void;
109 editSavedQuery: (data: SearchBarAdvanceFormData, id: number) => void;
110 data: SearchBarAdvanceFormData;
113 export const RenderSavedQueries = (props: RenderSavedQueriesProps) => {
114 return <ListItem button>
115 <ListItemText secondary={props.text} onClick={() => props.onSearch(props.text)} />
116 <ListItemSecondaryAction>
117 <Tooltip title="Edit">
118 <IconButton aria-label="Edit" onClick={() => props.editSavedQuery(props.data, props.id)}>
119 <EditSavedQueryIcon />
122 <Tooltip title="Remove">
123 <IconButton aria-label="Remove" onClick={() => props.deleteSavedQuery(props.id)}>
127 </ListItemSecondaryAction>
131 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
133 export const SearchBarView = withStyles(styles)(
134 class extends React.Component<SearchBarProps> {
135 state: SearchBarState = {
142 const { classes, currentView, openSearchView, closeView, isPopoverOpen } = this.props;
143 return <ClickAwayListener onClickAway={closeView}>
144 <Paper className={isPopoverOpen ? classes.containerSearchViewOpened : classes.container} >
145 <form onSubmit={this.handleSubmit}>
147 className={classes.input}
148 onChange={this.handleChange}
150 value={this.state.value}
152 disableUnderline={true}
153 onClick={openSearchView}
155 <InputAdornment position="end">
156 <Tooltip title='Search'>
164 <div className={classes.view}>
165 {isPopoverOpen && this.getView(currentView)}
168 </ClickAwayListener>;
171 componentDidMount() {
172 this.setState({ value: this.props.searchValue });
175 componentWillReceiveProps(nextProps: SearchBarProps) {
176 if (nextProps.searchValue !== this.props.searchValue) {
177 this.setState({ value: nextProps.searchValue });
181 componentWillUnmount() {
182 clearTimeout(this.timeout);
185 getView = (currentView: string) => {
186 const { onSetView, loadRecentQueries, savedQueries, deleteSavedQuery, searchValue, searchResults, saveQuery, onSearch, navigateTo, editSavedQuery, tags } = this.props;
187 switch (currentView) {
188 case SearchView.BASIC:
189 return <SearchBarBasicView setView={onSetView} recentQueries={loadRecentQueries} savedQueries={savedQueries} deleteSavedQuery={deleteSavedQuery} onSearch={onSearch} editSavedQuery={editSavedQuery} />;
190 case SearchView.ADVANCED:
191 return <SearchBarAdvancedView setView={onSetView} saveQuery={saveQuery} tags={tags} />;
192 case SearchView.AUTOCOMPLETE:
193 return <SearchBarAutocompleteView
194 navigateTo={navigateTo}
195 searchResults={searchResults}
196 searchValue={searchValue} />;
198 return <SearchBarBasicView setView={onSetView} recentQueries={loadRecentQueries} savedQueries={savedQueries} deleteSavedQuery={deleteSavedQuery} onSearch={onSearch} editSavedQuery={editSavedQuery} />;
202 handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
203 event.preventDefault();
204 clearTimeout(this.timeout);
205 this.props.saveRecentQuery(this.state.value);
206 this.props.searchDataOnEnter(this.state.value);
207 this.props.loadRecentQueries();
210 handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
211 clearTimeout(this.timeout);
212 this.setState({ value: event.target.value });
213 this.timeout = window.setTimeout(
214 () => this.props.onSearch(this.state.value),
215 this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
217 if (event.target.value.length > 0) {
218 this.props.onSetView(SearchView.AUTOCOMPLETE);
220 this.props.onSetView(SearchView.BASIC);