import { css } from '@emotion/css'; import React, { useCallback, useState } from 'react'; import { useDebounce } from 'react-use'; import AutoSizer from 'react-virtualized-auto-sizer'; import { Observable } from 'rxjs'; import { GrafanaTheme2 } from '@grafana/data'; import { config } from '@grafana/runtime'; import { useStyles2, Spinner, Button } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { FolderDTO } from 'app/types'; import { getGrafanaSearcher } from '../../service'; import { getSearchStateManager } from '../../state/SearchStateManager'; import { SearchLayout, DashboardViewItem } from '../../types'; import { newSearchSelection, updateSearchSelection } from '../selection'; import { ActionRow, getValidQueryLayout } from './ActionRow'; import { FolderSection } from './FolderSection'; import { ManageActions } from './ManageActions'; import { RootFolderView } from './RootFolderView'; import { SearchResultsCards } from './SearchResultsCards'; import { SearchResultsTable, SearchResultsProps } from './SearchResultsTable'; export type SearchViewProps = { showManage: boolean; folderDTO?: FolderDTO; hidePseudoFolders?: boolean; // Recent + starred keyboardEvents: Observable; }; export const SearchView = ({ showManage, folderDTO, hidePseudoFolders, keyboardEvents }: SearchViewProps) => { const styles = useStyles2(getStyles); const stateManager = getSearchStateManager(); // State is initialized from URL by parent component const state = stateManager.useState(); const [searchSelection, setSearchSelection] = useState(newSearchSelection()); const layout = getValidQueryLayout(state); const isFolders = layout === SearchLayout.Folders; const [listKey, setListKey] = useState(Date.now()); // Search usage reporting useDebounce(stateManager.onReportSearchUsage, 1000, []); const clearSelection = useCallback(() => { searchSelection.items.clear(); setSearchSelection({ ...searchSelection }); }, [searchSelection]); const toggleSelection = useCallback( (kind: string, uid: string) => { const current = searchSelection.isSelected(kind, uid); setSearchSelection(updateSearchSelection(searchSelection, !current, kind, [uid])); }, [searchSelection] ); // function to update items when dashboards or folders are moved or deleted const onChangeItemsList = async () => { // clean up search selection clearSelection(); setListKey(Date.now()); // trigger again the search to the backend stateManager.onQueryChange(state.query); }; const renderResults = () => { const value = state.result; if ((!value || !value.totalRows) && !isFolders) { if (state.loading && !value) { return ; } return (
No results found for your query.

); } const selection = showManage ? searchSelection.isSelected : undefined; if (layout === SearchLayout.Folders) { if (folderDTO) { return ( ); } return ( ); } return (
{({ width, height }) => { const props: SearchResultsProps = { response: value!, selection, selectionToggle: toggleSelection, clearSelection, width: width, height: height, onTagSelected: stateManager.onAddTag, keyboardEvents, onDatasourceChange: state.datasource ? stateManager.onDatasourceChange : undefined, onClickItem: stateManager.onSearchItemClicked, }; if (width < 800) { return ; } return ; }}
); }; if ( folderDTO && // With nested folders, SearchView doesn't know if it's fetched all children // of a folder so don't show empty state here. !config.featureToggles.nestedFolders && !state.loading && !state.result?.totalRows && !stateManager.hasSearchFilters() ) { return ( ); } return ( <> {Boolean(searchSelection.items.size > 0) ? ( ) : ( )} {renderResults()} ); }; const getStyles = (theme: GrafanaTheme2) => ({ searchInput: css` margin-bottom: 6px; min-height: ${theme.spacing(4)}; `, unsupported: css` padding: 10px; display: flex; align-items: center; justify-content: center; height: 100%; font-size: 18px; `, noResults: css` padding: ${theme.v1.spacing.md}; background: ${theme.v1.colors.bg2}; font-style: italic; margin-top: ${theme.v1.spacing.md}; `, }); function sectionForFolderView(folderDTO: FolderDTO): DashboardViewItem { return { uid: folderDTO.uid, kind: 'folder', title: folderDTO.title }; }