D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
share
/
grafana
/
public
/
app
/
features
/
search
/
page
/
components
/
Filename :
SearchView.tsx
back
Copy
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 { Trans } from 'app/core/internationalization'; 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<React.KeyboardEvent>; }; 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 <Spinner />; } return ( <div className={styles.noResults}> <div> <Trans i18nKey="search-view.no-results.text">No results found for your query.</Trans> </div> <br /> <Button variant="secondary" onClick={stateManager.onClearSearchAndFilters}> <Trans i18nKey="search-view.no-results.clear">Clear search and filters</Trans> </Button> </div> ); } const selection = showManage ? searchSelection.isSelected : undefined; if (layout === SearchLayout.Folders) { if (folderDTO) { return ( <FolderSection section={sectionForFolderView(folderDTO)} selection={selection} selectionToggle={toggleSelection} onTagSelected={stateManager.onAddTag} renderStandaloneBody={true} tags={state.tag} key={listKey} onClickItem={stateManager.onSearchItemClicked} /> ); } return ( <RootFolderView key={listKey} selection={selection} selectionToggle={toggleSelection} tags={state.tag} onTagSelected={stateManager.onAddTag} hidePseudoFolders={hidePseudoFolders} onClickItem={stateManager.onSearchItemClicked} /> ); } return ( <div style={{ height: '100%', width: '100%' }}> <AutoSizer> {({ 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 <SearchResultsCards {...props} />; } return <SearchResultsTable {...props} />; }} </AutoSizer> </div> ); }; 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 ( <EmptyListCTA title="This folder doesn't have any dashboards yet" buttonIcon="plus" buttonTitle="Create Dashboard" buttonLink={`dashboard/new?folderUid=${folderDTO.uid}`} proTip="Add/move dashboards to your folder at ->" proTipLink="dashboards" proTipLinkTitle="Manage dashboards" proTipTarget="" /> ); } return ( <> {Boolean(searchSelection.items.size > 0) ? ( <ManageActions items={searchSelection.items} onChange={onChangeItemsList} clearSelection={clearSelection} /> ) : ( <ActionRow onLayoutChange={stateManager.onLayoutChange} showStarredFilter={hidePseudoFolders} onStarredFilterChange={!hidePseudoFolders ? undefined : stateManager.onStarredFilterChange} onSortChange={stateManager.onSortChange} onTagFilterChange={stateManager.onTagFilterChange} getTagOptions={stateManager.getTagOptions} getSortOptions={getGrafanaSearcher().getSortOptions} sortPlaceholder={getGrafanaSearcher().sortPlaceholder} onDatasourceChange={stateManager.onDatasourceChange} onPanelTypeChange={stateManager.onPanelTypeChange} state={state} includePanels={state.includePanels!} onSetIncludePanels={stateManager.onSetIncludePanels} /> )} {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 }; }