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,
15 } from '@material-ui/core';
16 import SearchIcon from '@material-ui/icons/Search';
17 import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
18 import { ArvadosTheme } from '~/common/custom-theme';
19 import { SearchView } from '~/store/search-bar/search-bar-reducer';
22 SearchBarBasicViewDataProps,
23 SearchBarBasicViewActionProps
24 } from '~/views-components/search-bar/search-bar-basic-view';
26 SearchBarAutocompleteView,
27 SearchBarAutocompleteViewDataProps,
28 SearchBarAutocompleteViewActionProps
29 } from '~/views-components/search-bar/search-bar-autocomplete-view';
31 SearchBarAdvancedView,
32 SearchBarAdvancedViewDataProps,
33 SearchBarAdvancedViewActionProps
34 } from '~/views-components/search-bar/search-bar-advanced-view';
35 import { KEY_CODE_DOWN, KEY_CODE_ESC, KEY_CODE_UP, KEY_ENTER } from "~/common/codes";
37 type CssRules = 'container' | 'containerSearchViewOpened' | 'input' | 'view';
39 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => {
44 borderRadius: theme.spacing.unit / 2
46 containerSearchViewOpened: {
49 borderRadius: `${theme.spacing.unit / 2}px ${theme.spacing.unit / 2}px 0 0`
63 export type SearchBarDataProps = SearchBarViewDataProps
64 & SearchBarAutocompleteViewDataProps
65 & SearchBarAdvancedViewDataProps
66 & SearchBarBasicViewDataProps;
68 interface SearchBarViewDataProps {
71 isPopoverOpen: boolean;
75 export type SearchBarActionProps = SearchBarViewActionProps
76 & SearchBarAutocompleteViewActionProps
77 & SearchBarAdvancedViewActionProps
78 & SearchBarBasicViewActionProps;
80 interface SearchBarViewActionProps {
81 onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
82 onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
83 onSetView: (currentView: string) => void;
84 closeView: () => void;
85 openSearchView: () => void;
86 loadRecentQueries: () => string[];
89 setAdvancedDataFromSearchValue: (search: string) => void;
92 type SearchBarViewProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
94 const handleKeyDown = (e: React.KeyboardEvent, props: SearchBarViewProps) => {
95 if (e.keyCode === KEY_CODE_DOWN) {
97 if (!props.isPopoverOpen) {
98 props.onSetView(SearchView.AUTOCOMPLETE);
99 props.openSearchView();
103 } else if (e.keyCode === KEY_CODE_UP) {
106 } else if (e.keyCode === KEY_CODE_ESC) {
109 } else if (e.keyCode === KEY_ENTER) {
110 if (props.currentView === SearchView.BASIC) {
112 props.onSearch(props.selectedItem.query);
113 } else if (props.currentView === SearchView.AUTOCOMPLETE) {
114 if (props.selectedItem.id !== props.searchValue) {
116 props.navigateTo(props.selectedItem.id);
122 const handleInputClick = (e: React.MouseEvent, props: SearchBarViewProps) => {
123 if (props.searchValue) {
124 props.onSetView(SearchView.AUTOCOMPLETE);
125 props.openSearchView();
131 const handleDropdownClick = (e: React.MouseEvent, props: SearchBarViewProps) => {
133 if (props.isPopoverOpen) {
134 if (props.currentView === SearchView.ADVANCED) {
137 props.setAdvancedDataFromSearchValue(props.searchValue);
138 props.onSetView(SearchView.ADVANCED);
141 props.setAdvancedDataFromSearchValue(props.searchValue);
142 props.onSetView(SearchView.ADVANCED);
146 export const SearchBarView = withStyles(styles)(
147 class SearchBarView extends React.Component<SearchBarViewProps> {
149 viewAnchorRef = React.createRef<HTMLDivElement>();
152 const { children, ...props } = this.props;
153 const { classes } = props;
155 <Paper className={classes.container}>
156 <div ref={this.viewAnchorRef}>
157 <form onSubmit={props.onSubmit}>
158 <SearchInput {...props} />
163 width={this.getViewWidth()}
164 anchorEl={this.viewAnchorRef.current}>
165 <form onSubmit={props.onSubmit}>
169 disableClickHandler />
171 {getView({ ...props })}
172 </SearchViewContainer>
178 const { current } = this.viewAnchorRef;
179 return current ? current.offsetWidth : 0;
185 const SearchInput = (props: SearchBarViewProps & { disableClickHandler?: boolean; autoFocus?: boolean }) => {
186 const { classes } = props;
188 autoFocus={props.autoFocus}
189 className={classes.input}
190 onChange={props.onChange}
192 value={props.searchValue}
194 disableUnderline={true}
195 onClick={e => !props.disableClickHandler && handleInputClick(e, props)}
196 onKeyDown={e => handleKeyDown(e, props)}
198 <InputAdornment position="start">
199 <Tooltip title='Search'>
200 <IconButton type="submit">
207 <InputAdornment position="end">
208 <Tooltip title='Advanced search'>
209 <IconButton onClick={e => handleDropdownClick(e, props)}>
210 <ArrowDropDownIcon />
217 const SearchViewContainer = (props: SearchBarViewProps & { width: number, anchorEl: HTMLElement | null, children: React.ReactNode }) =>
220 style: { width: props.width }
222 anchorEl={props.anchorEl}
225 horizontal: 'center',
229 horizontal: 'center',
232 open={props.isPopoverOpen}
233 onClose={props.closeView}>
240 const getView = (props: SearchBarViewProps) => {
241 switch (props.currentView) {
242 case SearchView.AUTOCOMPLETE:
243 return <SearchBarAutocompleteView
244 navigateTo={props.navigateTo}
245 searchResults={props.searchResults}
246 searchValue={props.searchValue}
247 selectedItem={props.selectedItem} />;
248 case SearchView.ADVANCED:
249 return <SearchBarAdvancedView
250 closeAdvanceView={props.closeAdvanceView}
252 saveQuery={props.saveQuery} />;
254 return <SearchBarBasicView
255 onSetView={props.onSetView}
256 onSearch={props.onSearch}
257 loadRecentQueries={props.loadRecentQueries}
258 savedQueries={props.savedQueries}
259 deleteSavedQuery={props.deleteSavedQuery}
260 editSavedQuery={props.editSavedQuery}
261 selectedItem={props.selectedItem} />;