import { css, cx } from '@emotion/css'; import React, { FC } from 'react'; import { useAsync, useLocalStorage } from 'react-use'; import { GrafanaTheme } from '@grafana/data'; import { Card, Checkbox, CollapsableSection, Icon, Spinner, stylesFactory, useTheme } from '@grafana/ui'; import { getSectionStorageKey } from 'app/features/search/utils'; import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId'; import { SearchItem } from '../..'; import { getGrafanaSearcher, SearchQuery } from '../../service'; import { DashboardSearchItemType, DashboardSectionItem } from '../../types'; import { SelectionChecker, SelectionToggle } from '../selection'; export interface DashboardSection { kind: string; // folder | query! uid: string; title: string; selected?: boolean; // not used ? keyboard url?: string; icon?: string; itemsUIDs?: string[]; // for pseudo folders } interface SectionHeaderProps { selection?: SelectionChecker; selectionToggle?: SelectionToggle; onTagSelected: (tag: string) => void; section: DashboardSection; renderStandaloneBody?: boolean; // render the body on its own tags?: string[]; } export const FolderSection: FC = ({ section, selectionToggle, onTagSelected, selection, renderStandaloneBody, tags, }) => { const editable = selectionToggle != null; const theme = useTheme(); const styles = getSectionHeaderStyles(theme, section.selected, editable); const [sectionExpanded, setSectionExpanded] = useLocalStorage(getSectionStorageKey(section.title), false); const results = useAsync(async () => { if (!sectionExpanded && !renderStandaloneBody) { return Promise.resolve([] as DashboardSectionItem[]); } let folderUid: string | undefined = section.uid; let folderTitle: string | undefined = section.title; let query: SearchQuery = { query: '*', kind: ['dashboard'], location: section.uid, sort: 'name_sort', }; if (section.itemsUIDs) { query = { uid: section.itemsUIDs, // array of UIDs }; folderUid = undefined; folderTitle = undefined; } const raw = await getGrafanaSearcher().search({ ...query, tags }); const v = raw.view.map( (item) => ({ uid: item.uid, title: item.name, url: item.url, uri: item.url, type: item.kind === 'folder' ? DashboardSearchItemType.DashFolder : DashboardSearchItemType.DashDB, id: 666, // do not use me! isStarred: false, tags: item.tags ?? [], folderUid, folderTitle, } as DashboardSectionItem) ); return v; }, [sectionExpanded, section, tags]); const onSectionExpand = () => { setSectionExpanded(!sectionExpanded); }; const onToggleFolder = (evt: React.FormEvent) => { evt.preventDefault(); evt.stopPropagation(); if (selectionToggle && selection) { const checked = !selection(section.kind, section.uid); selectionToggle(section.kind, section.uid); const sub = results.value ?? []; for (const item of sub) { if (selection('dashboard', item.uid!) !== checked) { selectionToggle('dashboard', item.uid!); } } } }; const onToggleChecked = (item: DashboardSectionItem) => { if (selectionToggle) { selectionToggle('dashboard', item.uid!); } }; const id = useUniqueId(); const labelId = `section-header-label-${id}`; let icon = section.icon; if (!icon) { icon = sectionExpanded ? 'folder-open' : 'folder'; } const renderResults = () => { if (!results.value?.length) { if (results.loading) { return ; } return ( No results found ); } return results.value.map((v) => { if (selection && selectionToggle) { const type = v.type === DashboardSearchItemType.DashFolder ? 'folder' : 'dashboard'; v = { ...v, checked: selection(type, v.uid!), }; } return ( ); }); }; // Skip the folder wrapper if (renderStandaloneBody) { return
{renderResults()}
; } return ( {selectionToggle && selection && (
)}
{section.title} {section.url && section.uid !== 'general' && ( | Go to folder )}
} > {results.value &&
    {renderResults()}
}
); }; const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = false, editable: boolean) => { const { sm } = theme.spacing; return { wrapper: cx( css` align-items: center; font-size: ${theme.typography.size.base}; padding: 12px; border-bottom: none; color: ${theme.colors.textWeak}; z-index: 1; &:hover, &.selected { color: ${theme.colors.text}; } &:hover, &:focus-visible, &:focus-within { a { opacity: 1; } } `, 'pointer', { selected } ), sectionItems: css` margin: 0 24px 0 32px; `, checkbox: css` padding: 0 ${sm} 0 0; `, icon: css` padding: 0 ${sm} 0 ${editable ? 0 : sm}; `, folderViewResults: css` overflow: auto; `, text: css` flex-grow: 1; line-height: 24px; `, link: css` padding: 2px 10px 0; color: ${theme.colors.textWeak}; opacity: 0; transition: opacity 150ms ease-in-out; `, separator: css` margin-right: 6px; `, content: css` padding-top: 0px; padding-bottom: 0px; `, }; });