123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- import { css } from '@emotion/css';
- import React, { FC, memo, useState } from 'react';
- import { useDebounce, useLocalStorage } from 'react-use';
- import { GrafanaTheme2 } from '@grafana/data';
- import { config } from '@grafana/runtime';
- import { CustomScrollbar, IconButton, stylesFactory, useStyles2, useTheme2 } from '@grafana/ui';
- import { SEARCH_PANELS_LOCAL_STORAGE_KEY } from '../constants';
- import { useDashboardSearch } from '../hooks/useDashboardSearch';
- import { useKeyNavigationListener } from '../hooks/useSearchKeyboardSelection';
- import { useSearchQuery } from '../hooks/useSearchQuery';
- import { SearchView } from '../page/components/SearchView';
- import { ActionRow } from './ActionRow';
- import { PreviewsSystemRequirements } from './PreviewsSystemRequirements';
- import { SearchField } from './SearchField';
- import { SearchResults } from './SearchResults';
- export interface Props {
- onCloseSearch: () => void;
- }
- export default function DashboardSearch({ onCloseSearch }: Props) {
- if (config.featureToggles.panelTitleSearch) {
- // TODO: "folder:current" ????
- return <DashboardSearchNew onCloseSearch={onCloseSearch} />;
- }
- return <DashboardSearchOLD onCloseSearch={onCloseSearch} />;
- }
- function DashboardSearchNew({ onCloseSearch }: Props) {
- const styles = useStyles2(getStyles);
- const { query, onQueryChange } = useSearchQuery({});
- let [includePanels, setIncludePanels] = useLocalStorage<boolean>(SEARCH_PANELS_LOCAL_STORAGE_KEY, true);
- if (!config.featureToggles.panelTitleSearch) {
- includePanels = false;
- }
- const [inputValue, setInputValue] = useState(query.query ?? '');
- const onSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- e.preventDefault();
- setInputValue(e.currentTarget.value);
- };
- useDebounce(() => onQueryChange(inputValue), 200, [inputValue]);
- const { onKeyDown, keyboardEvents } = useKeyNavigationListener();
- return (
- <div tabIndex={0} className={styles.overlay}>
- <div className={styles.container}>
- <div className={styles.searchField}>
- <div>
- <input
- type="text"
- placeholder={includePanels ? 'Search dashboards and panels by name' : 'Search dashboards by name'}
- value={inputValue}
- onChange={onSearchQueryChange}
- onKeyDown={onKeyDown}
- tabIndex={0}
- spellCheck={false}
- className={styles.input}
- autoFocus
- />
- </div>
- <div className={styles.closeBtn}>
- <IconButton name="times" onClick={onCloseSearch} size="xxl" tooltip="Close search" />
- </div>
- </div>
- <div className={styles.search}>
- <SearchView
- showManage={false}
- queryText={query.query}
- onQueryTextChange={(newQueryText) => {
- setInputValue(newQueryText);
- }}
- includePanels={includePanels!}
- setIncludePanels={setIncludePanels}
- keyboardEvents={keyboardEvents}
- />
- </div>
- </div>
- </div>
- );
- }
- export const DashboardSearchOLD: FC<Props> = memo(({ onCloseSearch }) => {
- const { query, onQueryChange, onTagFilterChange, onTagAdd, onSortChange, onLayoutChange } = useSearchQuery({});
- const { results, loading, onToggleSection, onKeyDown, showPreviews, setShowPreviews } = useDashboardSearch(
- query,
- onCloseSearch
- );
- const theme = useTheme2();
- const styles = getStyles(theme);
- return (
- <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" onClick={onCloseSearch} size="xxl" tooltip="Close search" />
- </div>
- </div>
- <div className={styles.search}>
- <ActionRow
- {...{
- onLayoutChange,
- setShowPreviews,
- onSortChange,
- onTagFilterChange,
- query,
- showPreviews,
- }}
- />
- <PreviewsSystemRequirements
- bottomSpacing={3}
- showPreviews={showPreviews}
- onRemove={() => setShowPreviews(false)}
- />
- <CustomScrollbar>
- <SearchResults
- results={results}
- loading={loading}
- onTagSelected={onTagAdd}
- editable={false}
- onToggleSection={onToggleSection}
- layout={query.layout}
- showPreviews={showPreviews}
- />
- </CustomScrollbar>
- </div>
- </div>
- </div>
- );
- });
- DashboardSearchOLD.displayName = 'DashboardSearchOLD';
- const getStyles = stylesFactory((theme: GrafanaTheme2) => {
- return {
- overlay: css`
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- z-index: ${theme.zIndex.sidemenu};
- position: fixed;
- background: ${theme.colors.background.canvas};
- padding: ${theme.spacing(1)};
- ${theme.breakpoints.up('md')} {
- left: ${theme.components.sidemenu.width}px;
- z-index: ${theme.zIndex.navbarFixed + 1};
- padding: ${theme.spacing(2)};
- }
- `,
- container: css`
- display: flex;
- flex-direction: column;
- max-width: 1400px;
- margin: 0 auto;
- padding: ${theme.spacing(1)};
- background: ${theme.colors.background.primary};
- border: 1px solid ${theme.components.panel.borderColor};
- height: 100%;
- ${theme.breakpoints.up('md')} {
- padding: ${theme.spacing(3)};
- }
- `,
- closeBtn: css`
- right: -5px;
- top: 2px;
- z-index: 1;
- position: absolute;
- `,
- searchField: css`
- position: relative;
- `,
- search: css`
- display: flex;
- flex-direction: column;
- overflow: hidden;
- height: 100%;
- padding: ${theme.spacing(2, 0, 3, 0)};
- `,
- input: css`
- box-sizing: border-box;
- outline: none;
- background-color: transparent;
- background: transparent;
- border-bottom: 2px solid ${theme.v1.colors.border1};
- font-size: 20px;
- line-height: 38px;
- width: 100%;
- &::placeholder {
- color: ${theme.v1.colors.textWeak};
- }
- `,
- };
- });
|