mirror of https://github.com/grafana/grafana
Search/virtualize list (#23710)
* Search: Add FixedSizeList for result items * Search: Move SectionHeader to a separate file * Search: Add useListHeight hook * Search: Fix horizontal scrollbar * Search: Remove custom scrollbar * Search: Do not fetch dashboard folder on route change * Search: Update tests * Search: Remove extra checkbox renders * Search: Move wrapper ref outside search results * Search: Fix param type * Search: Fix merge conflicts * Search: Virtualize dashboard list * Search: Update layout * Search: Pass wrapper to search results * Search: Update dashboard redirect * Search: Remove unused css * Search: Revert config * Search: Use AutoSizer * Search: Remove redundant appEvents call * Search: Use List layout in folder viewpull/23845/head
parent
cf23f15a08
commit
fb8a555f19
@ -0,0 +1,99 @@ |
||||
import React, { FC, useCallback } from 'react'; |
||||
import { css, cx } from 'emotion'; |
||||
import { GrafanaTheme } from '@grafana/data'; |
||||
import { Icon, IconButton, IconName, stylesFactory, useTheme } from '@grafana/ui'; |
||||
import { DashboardSection, OnToggleChecked } from '../types'; |
||||
import { SearchCheckbox } from './SearchCheckbox'; |
||||
|
||||
interface SectionHeaderProps { |
||||
editable?: boolean; |
||||
onSectionClick: (section: DashboardSection) => void; |
||||
onToggleChecked?: OnToggleChecked; |
||||
section: DashboardSection; |
||||
} |
||||
|
||||
export const SectionHeader: FC<SectionHeaderProps> = ({ |
||||
section, |
||||
onSectionClick, |
||||
onToggleChecked, |
||||
editable = false, |
||||
}) => { |
||||
const theme = useTheme(); |
||||
const styles = getSectionHeaderStyles(theme, section.selected); |
||||
|
||||
const onSectionExpand = () => { |
||||
onSectionClick(section); |
||||
}; |
||||
|
||||
const onSectionChecked = useCallback( |
||||
(e: React.MouseEvent) => { |
||||
e.preventDefault(); |
||||
e.stopPropagation(); |
||||
if (onToggleChecked) { |
||||
onToggleChecked(section); |
||||
} |
||||
}, |
||||
[section] |
||||
); |
||||
|
||||
return !section.hideHeader ? ( |
||||
<div className={styles.wrapper} onClick={onSectionExpand}> |
||||
<SearchCheckbox editable={editable} checked={section.checked} onClick={onSectionChecked} /> |
||||
<Icon className={styles.icon} name={section.icon as IconName} /> |
||||
|
||||
<span className={styles.text}>{section.title}</span> |
||||
{section.url && ( |
||||
<a href={section.url} className={styles.link}> |
||||
<IconButton name="cog" className={styles.button} /> |
||||
</a> |
||||
)} |
||||
<Icon name={section.expanded ? 'angle-down' : 'angle-right'} /> |
||||
</div> |
||||
) : ( |
||||
<div className={styles.wrapper} /> |
||||
); |
||||
}; |
||||
|
||||
const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = false) => { |
||||
const { sm, xs } = theme.spacing; |
||||
return { |
||||
wrapper: cx( |
||||
css` |
||||
display: flex; |
||||
align-items: center; |
||||
font-size: ${theme.typography.size.base}; |
||||
padding: ${sm} ${xs} ${xs}; |
||||
color: ${theme.colors.textWeak}; |
||||
|
||||
&:hover, |
||||
&.selected { |
||||
color: ${theme.colors.text}; |
||||
} |
||||
|
||||
&:hover { |
||||
a { |
||||
opacity: 1; |
||||
} |
||||
} |
||||
`,
|
||||
'pointer', |
||||
{ selected } |
||||
), |
||||
icon: css` |
||||
width: 43px; |
||||
`,
|
||||
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; |
||||
`,
|
||||
button: css` |
||||
margin-top: 3px; |
||||
`,
|
||||
}; |
||||
}); |
||||
@ -1,2 +1,4 @@ |
||||
export const NO_ID_SECTIONS = ['Recent', 'Starred']; |
||||
// Height of the search result item
|
||||
export const ITEM_HEIGHT = 40; |
||||
export const DEFAULT_SORT = { label: 'A-Z', value: 'alpha-asc' }; |
||||
|
||||
Loading…
Reference in new issue