|
|
|
|
@ -1,6 +1,6 @@ |
|
|
|
|
import React, { FC, memo } from 'react'; |
|
|
|
|
import { css } from 'emotion'; |
|
|
|
|
import { useTheme, CustomScrollbar, stylesFactory, Button } from '@grafana/ui'; |
|
|
|
|
import { useTheme, CustomScrollbar, stylesFactory, IconButton } from '@grafana/ui'; |
|
|
|
|
import { GrafanaTheme } from '@grafana/data'; |
|
|
|
|
import { useSearchQuery } from '../hooks/useSearchQuery'; |
|
|
|
|
import { useDashboardSearch } from '../hooks/useDashboardSearch'; |
|
|
|
|
@ -24,11 +24,8 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch, folder }) => { |
|
|
|
|
|
|
|
|
|
// The main search input has own keydown handler, also TagFilter uses input, so
|
|
|
|
|
// clicking Esc when tagFilter is active shouldn't close the whole search overlay
|
|
|
|
|
const onClose = (e: React.KeyboardEvent<HTMLElement>) => { |
|
|
|
|
const target = e.target as HTMLElement; |
|
|
|
|
if ((target.tagName as any) !== 'INPUT' && ['Escape', 'ArrowLeft'].includes(e.key)) { |
|
|
|
|
onCloseSearch(); |
|
|
|
|
} |
|
|
|
|
const onClose = () => { |
|
|
|
|
onCloseSearch(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const onLayoutChange = (layout: string) => { |
|
|
|
|
@ -39,59 +36,80 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch, folder }) => { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<div tabIndex={0} className="search-container" onKeyDown={onClose}> |
|
|
|
|
<SearchField |
|
|
|
|
query={query} |
|
|
|
|
onChange={onQueryChange} |
|
|
|
|
onKeyDown={onKeyDown} |
|
|
|
|
autoFocus |
|
|
|
|
clearable |
|
|
|
|
className={styles.searchField} |
|
|
|
|
/> |
|
|
|
|
<div className={styles.search}> |
|
|
|
|
<ActionRow |
|
|
|
|
{...{ |
|
|
|
|
layout, |
|
|
|
|
onLayoutChange, |
|
|
|
|
onSortChange, |
|
|
|
|
onTagFilterChange, |
|
|
|
|
query, |
|
|
|
|
}} |
|
|
|
|
/> |
|
|
|
|
<CustomScrollbar> |
|
|
|
|
<SearchResults |
|
|
|
|
results={results} |
|
|
|
|
loading={loading} |
|
|
|
|
onTagSelected={onTagAdd} |
|
|
|
|
editable={false} |
|
|
|
|
onToggleSection={onToggleSection} |
|
|
|
|
layout={layout} |
|
|
|
|
<div tabIndex={0} className={styles.overlay}> |
|
|
|
|
<div className={styles.container}> |
|
|
|
|
<div className={styles.searchField}> |
|
|
|
|
<SearchField query={query} onChange={onQueryChange} onKeyDown={onKeyDown} autoFocus clearable /> |
|
|
|
|
<div className={styles.closeBtn}> |
|
|
|
|
<IconButton name="times" surface="panel" onClick={onClose} size="xxl" tooltip="Close search" /> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<div className={styles.search}> |
|
|
|
|
<ActionRow |
|
|
|
|
{...{ |
|
|
|
|
layout, |
|
|
|
|
onLayoutChange, |
|
|
|
|
onSortChange, |
|
|
|
|
onTagFilterChange, |
|
|
|
|
query, |
|
|
|
|
}} |
|
|
|
|
/> |
|
|
|
|
</CustomScrollbar> |
|
|
|
|
<CustomScrollbar> |
|
|
|
|
<SearchResults |
|
|
|
|
results={results} |
|
|
|
|
loading={loading} |
|
|
|
|
onTagSelected={onTagAdd} |
|
|
|
|
editable={false} |
|
|
|
|
onToggleSection={onToggleSection} |
|
|
|
|
layout={layout} |
|
|
|
|
/> |
|
|
|
|
</CustomScrollbar> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<Button icon="times" className={styles.closeBtn} onClick={onCloseSearch} variant="secondary"> |
|
|
|
|
Close |
|
|
|
|
</Button> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
const getStyles = stylesFactory((theme: GrafanaTheme) => { |
|
|
|
|
return { |
|
|
|
|
overlay: css` |
|
|
|
|
left: 0; |
|
|
|
|
top: 0; |
|
|
|
|
right: 0; |
|
|
|
|
bottom: 0; |
|
|
|
|
z-index: ${theme.zIndex.sidemenu}; |
|
|
|
|
position: fixed; |
|
|
|
|
background: ${theme.colors.dashboardBg}; |
|
|
|
|
|
|
|
|
|
@media only screen and (min-width: ${theme.breakpoints.md}) { |
|
|
|
|
left: 60px; |
|
|
|
|
z-index: ${theme.zIndex.navbarFixed + 1}; |
|
|
|
|
} |
|
|
|
|
`,
|
|
|
|
|
container: css` |
|
|
|
|
max-width: 1400px; |
|
|
|
|
margin: 0 auto; |
|
|
|
|
padding: ${theme.spacing.md}; |
|
|
|
|
|
|
|
|
|
height: 100%; |
|
|
|
|
|
|
|
|
|
@media only screen and (min-width: ${theme.breakpoints.md}) { |
|
|
|
|
padding: 32px; |
|
|
|
|
} |
|
|
|
|
`,
|
|
|
|
|
closeBtn: css` |
|
|
|
|
top: 10px; |
|
|
|
|
right: 8px; |
|
|
|
|
right: -5px; |
|
|
|
|
top: 2px; |
|
|
|
|
z-index: 1; |
|
|
|
|
position: absolute; |
|
|
|
|
`,
|
|
|
|
|
searchField: css` |
|
|
|
|
padding-left: ${theme.spacing.md}; |
|
|
|
|
position: relative; |
|
|
|
|
`,
|
|
|
|
|
search: css` |
|
|
|
|
display: flex; |
|
|
|
|
flex-direction: column; |
|
|
|
|
padding: ${theme.spacing.xl}; |
|
|
|
|
height: 100%; |
|
|
|
|
max-width: 1400px; |
|
|
|
|
`,
|
|
|
|
|
}; |
|
|
|
|
}); |
|
|
|
|
|