mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DockedMegaMenu: Refactor and rename to simplify (#75872)
* tidy up some styles * remove NavBarMenuItemWrapper + consolidate components * lots of renaming * use object syntax in FeatureHighlight * fix a couple of missing find+replace * adjust li positioning * fix text truncation * bit more tidy up * refactor indent into it's own component * memoize styles in Indent
This commit is contained in:
parent
523d1b46d4
commit
18b237879d
@ -1185,9 +1185,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/DockedMegaMenu/NavFeatureHighlight.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
],
|
||||
"public/app/core/components/AppChrome/MegaMenu/NavFeatureHighlight.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
],
|
||||
|
@ -10,7 +10,7 @@ import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { KioskMode } from 'app/types';
|
||||
|
||||
import { DockedMegaMenu, MENU_WIDTH } from './DockedMegaMenu/DockedMegaMenu';
|
||||
import { MegaMenu, MENU_WIDTH } from './DockedMegaMenu/MegaMenu';
|
||||
import { TOGGLE_BUTTON_ID } from './NavToolbar/NavToolbar';
|
||||
import { TOP_BAR_LEVEL_HEIGHT } from './types';
|
||||
|
||||
@ -58,7 +58,7 @@ export function AppChromeMenu({}: Props) {
|
||||
timeout={{ enter: animationSpeed, exit: 0 }}
|
||||
>
|
||||
<FocusScope contain autoFocus>
|
||||
<DockedMegaMenu className={styles.menu} onClose={onClose} ref={ref} {...overlayProps} {...dialogProps} />
|
||||
<MegaMenu className={styles.menu} onClose={onClose} ref={ref} {...overlayProps} {...dialogProps} />
|
||||
</FocusScope>
|
||||
</CSSTransition>
|
||||
<CSSTransition
|
||||
|
@ -8,7 +8,7 @@ export interface Props {
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
export const NavFeatureHighlight = ({ children }: Props): JSX.Element => {
|
||||
export const FeatureHighlight = ({ children }: Props): JSX.Element => {
|
||||
const styles = useStyles2(getStyles);
|
||||
return (
|
||||
<div>
|
||||
@ -20,15 +20,15 @@ export const NavFeatureHighlight = ({ children }: Props): JSX.Element => {
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
highlight: css`
|
||||
background-color: ${theme.colors.success.main};
|
||||
border-radius: ${theme.shape.radius.circle};
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
`,
|
||||
highlight: css({
|
||||
backgroundColor: theme.colors.success.main,
|
||||
borderRadius: theme.shape.radius.circle,
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
display: 'inline-block;',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
}),
|
||||
};
|
||||
};
|
@ -9,7 +9,7 @@ import { locationService } from '@grafana/runtime';
|
||||
|
||||
import { TestProvider } from '../../../../../test/helpers/TestProvider';
|
||||
|
||||
import { DockedMegaMenu } from './DockedMegaMenu';
|
||||
import { MegaMenu } from './MegaMenu';
|
||||
|
||||
const setup = () => {
|
||||
const navBarTree: NavModelItem[] = [
|
||||
@ -40,7 +40,7 @@ const setup = () => {
|
||||
return render(
|
||||
<TestProvider storeState={{ navBarTree }} grafanaContext={grafanaContext}>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<DockedMegaMenu onClose={() => {}} />
|
||||
<MegaMenu onClose={() => {}} />
|
||||
</Router>
|
||||
</TestProvider>
|
||||
);
|
@ -8,7 +8,7 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { CustomScrollbar, Icon, IconButton, useStyles2 } from '@grafana/ui';
|
||||
import { useSelector } from 'app/types';
|
||||
|
||||
import { NavBarMenuItemWrapper } from './NavBarMenuItemWrapper';
|
||||
import { MegaMenuItem } from './MegaMenuItem';
|
||||
import { enrichWithInteractionTracking, getActiveItem } from './utils';
|
||||
|
||||
export const MENU_WIDTH = '350px';
|
||||
@ -17,7 +17,7 @@ export interface Props extends DOMAttributes {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const DockedMegaMenu = React.memo(
|
||||
export const MegaMenu = React.memo(
|
||||
forwardRef<HTMLDivElement, Props>(({ onClose, ...restProps }, ref) => {
|
||||
const navBarTree = useSelector((state) => state.navBarTree);
|
||||
const styles = useStyles2(getStyles);
|
||||
@ -49,7 +49,7 @@ export const DockedMegaMenu = React.memo(
|
||||
<CustomScrollbar showScrollIndicators hideHorizontalTrack>
|
||||
<ul className={styles.itemList}>
|
||||
{navItems.map((link) => (
|
||||
<NavBarMenuItemWrapper link={link} onClose={onClose} activeItem={activeItem} key={link.text} />
|
||||
<MegaMenuItem link={link} onClose={onClose} activeItem={activeItem} key={link.text} />
|
||||
))}
|
||||
</ul>
|
||||
</CustomScrollbar>
|
||||
@ -59,7 +59,7 @@ export const DockedMegaMenu = React.memo(
|
||||
})
|
||||
);
|
||||
|
||||
DockedMegaMenu.displayName = 'DockedMegaMenu';
|
||||
MegaMenu.displayName = 'MegaMenu';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
content: css({
|
||||
@ -82,6 +82,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
display: 'grid',
|
||||
gridAutoRows: `minmax(${theme.spacing(6)}, auto)`,
|
||||
gridTemplateColumns: `minmax(${MENU_WIDTH}, auto)`,
|
||||
listStyleType: 'none',
|
||||
minWidth: MENU_WIDTH,
|
||||
}),
|
||||
});
|
@ -3,38 +3,36 @@ import React from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { Button, Icon, useStyles2 } from '@grafana/ui';
|
||||
import { Button, Icon, useStyles2, Text } from '@grafana/ui';
|
||||
|
||||
import { NavBarItemIcon } from './NavBarItemIcon';
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
import { NavFeatureHighlight } from './NavFeatureHighlight';
|
||||
import { Indent } from '../../Indent/Indent';
|
||||
|
||||
import { FeatureHighlight } from './FeatureHighlight';
|
||||
import { MegaMenuItemIcon } from './MegaMenuItemIcon';
|
||||
import { MegaMenuItemText } from './MegaMenuItemText';
|
||||
import { hasChildMatch } from './utils';
|
||||
|
||||
export function NavBarMenuSection({
|
||||
link,
|
||||
activeItem,
|
||||
children,
|
||||
className,
|
||||
onClose,
|
||||
}: {
|
||||
interface Props {
|
||||
link: NavModelItem;
|
||||
activeItem?: NavModelItem;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
onClose?: () => void;
|
||||
}) {
|
||||
level?: number;
|
||||
}
|
||||
|
||||
export function MegaMenuItem({ link, activeItem, level = 0, onClose }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const FeatureHighlightWrapper = link.highlightText ? NavFeatureHighlight : React.Fragment;
|
||||
const FeatureHighlightWrapper = link.highlightText ? FeatureHighlight : React.Fragment;
|
||||
const isActive = link === activeItem;
|
||||
const hasActiveChild = hasChildMatch(link, activeItem);
|
||||
const [sectionExpanded, setSectionExpanded] =
|
||||
useLocalStorage(`grafana.navigation.expanded[${link.text}]`, false) ?? Boolean(hasActiveChild);
|
||||
const showExpandButton = linkHasChildren(link) || link.emptyMessage;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cx(styles.collapsibleSectionWrapper, className)}>
|
||||
<NavBarMenuItem
|
||||
isActive={link === activeItem}
|
||||
<li>
|
||||
<div className={styles.collapsibleSectionWrapper}>
|
||||
<MegaMenuItemText
|
||||
isActive={isActive}
|
||||
onClick={() => {
|
||||
link.onClick?.();
|
||||
onClose?.();
|
||||
@ -49,12 +47,13 @@ export function NavBarMenuSection({
|
||||
})}
|
||||
>
|
||||
<FeatureHighlightWrapper>
|
||||
<NavBarItemIcon link={link} />
|
||||
<div className={styles.iconWrapper}>{level === 0 && <MegaMenuItemIcon link={link} />}</div>
|
||||
</FeatureHighlightWrapper>
|
||||
{link.text}
|
||||
<Indent level={Math.max(0, level - 1)} spacing={2} />
|
||||
<Text truncate>{link.text}</Text>
|
||||
</div>
|
||||
</NavBarMenuItem>
|
||||
{children && (
|
||||
</MegaMenuItemText>
|
||||
{showExpandButton && (
|
||||
<Button
|
||||
aria-label={`${sectionExpanded ? 'Collapse' : 'Expand'} section ${link.text}`}
|
||||
variant="secondary"
|
||||
@ -66,12 +65,35 @@ export function NavBarMenuSection({
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{sectionExpanded && children}
|
||||
</>
|
||||
{showExpandButton && sectionExpanded && (
|
||||
<ul className={styles.children}>
|
||||
{linkHasChildren(link) ? (
|
||||
link.children
|
||||
.filter((childLink) => !childLink.isCreateAction)
|
||||
.map((childLink) => (
|
||||
<MegaMenuItem
|
||||
key={`${link.text}-${childLink.text}`}
|
||||
link={childLink}
|
||||
activeItem={activeItem}
|
||||
onClose={onClose}
|
||||
level={level + 1}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className={styles.emptyMessage}>{link.emptyMessage}</div>
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
children: css({
|
||||
display: 'flex',
|
||||
listStyleType: 'none',
|
||||
flexDirection: 'column',
|
||||
}),
|
||||
collapsibleSectionWrapper: css({
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
@ -81,18 +103,22 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
padding: theme.spacing(0, 0.5),
|
||||
marginRight: theme.spacing(1),
|
||||
}),
|
||||
collapseWrapperActive: css({
|
||||
backgroundColor: theme.colors.action.disabledBackground,
|
||||
emptyMessage: css({
|
||||
color: theme.colors.text.secondary,
|
||||
fontStyle: 'italic',
|
||||
padding: theme.spacing(1, 1.5, 1, 7),
|
||||
}),
|
||||
collapseContent: css({
|
||||
padding: 0,
|
||||
iconWrapper: css({
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}),
|
||||
labelWrapper: css({
|
||||
display: 'grid',
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
gridAutoFlow: 'column',
|
||||
gridTemplateColumns: `${theme.spacing(7)} auto`,
|
||||
placeItems: 'center',
|
||||
alignItems: 'center',
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
}),
|
||||
isActive: css({
|
||||
@ -115,3 +141,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
color: theme.colors.text.primary,
|
||||
}),
|
||||
});
|
||||
|
||||
function linkHasChildren(link: NavModelItem): link is NavModelItem & { children: NavModelItem[] } {
|
||||
return Boolean(link.children && link.children.length > 0);
|
||||
}
|
@ -10,7 +10,7 @@ interface NavBarItemIconProps {
|
||||
link: NavModelItem;
|
||||
}
|
||||
|
||||
export function NavBarItemIcon({ link }: NavBarItemIconProps) {
|
||||
export function MegaMenuItemIcon({ link }: NavBarItemIconProps) {
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme);
|
||||
|
@ -15,7 +15,7 @@ export interface Props {
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export function NavBarMenuItem({ children, icon, isActive, isChild, onClick, target, url }: Props) {
|
||||
export function MegaMenuItemText({ children, icon, isActive, isChild, onClick, target, url }: Props) {
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme, isActive, isChild);
|
||||
|
||||
@ -23,7 +23,7 @@ export function NavBarMenuItem({ children, icon, isActive, isChild, onClick, tar
|
||||
<div className={styles.linkContent}>
|
||||
{icon && <Icon data-testid="dropdown-child-icon" name={icon} />}
|
||||
|
||||
<div className={styles.linkText}>{children}</div>
|
||||
{children}
|
||||
|
||||
{target === '_blank' && (
|
||||
<Icon data-testid="external-link-icon" name="external-link-alt" className={styles.externalLinkIcon} />
|
||||
@ -66,10 +66,10 @@ export function NavBarMenuItem({ children, icon, isActive, isChild, onClick, tar
|
||||
);
|
||||
}
|
||||
|
||||
return <li className={styles.listItem}>{element}</li>;
|
||||
return <div className={styles.wrapper}>{element}</div>;
|
||||
}
|
||||
|
||||
NavBarMenuItem.displayName = 'NavBarMenuItem';
|
||||
MegaMenuItemText.displayName = 'MegaMenuItemText';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive'], isChild: Props['isActive']) => ({
|
||||
button: css({
|
||||
@ -83,11 +83,6 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive'], isChild: P
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}),
|
||||
linkText: css({
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
}),
|
||||
externalLinkIcon: css({
|
||||
color: theme.colors.text.secondary,
|
||||
}),
|
||||
@ -127,7 +122,7 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive'], isChild: P
|
||||
backgroundImage: theme.colors.gradients.brandVertical,
|
||||
},
|
||||
}),
|
||||
listItem: css({
|
||||
wrapper: css({
|
||||
boxSizing: 'border-box',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
@ -1,112 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
import { NavBarMenuSection } from './NavBarMenuSection';
|
||||
import { isMatchOrChildMatch } from './utils';
|
||||
|
||||
export function NavBarMenuItemWrapper({
|
||||
link,
|
||||
activeItem,
|
||||
onClose,
|
||||
}: {
|
||||
link: NavModelItem;
|
||||
activeItem?: NavModelItem;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
if (link.emptyMessage && !linkHasChildren(link)) {
|
||||
return (
|
||||
<NavBarMenuSection onClose={onClose} link={link} activeItem={activeItem}>
|
||||
<ul className={styles.children}>
|
||||
<div className={styles.emptyMessage}>{link.emptyMessage}</div>
|
||||
</ul>
|
||||
</NavBarMenuSection>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavBarMenuSection onClose={onClose} link={link} activeItem={activeItem}>
|
||||
{linkHasChildren(link) && (
|
||||
<ul className={styles.children}>
|
||||
{link.children.map((childLink) => {
|
||||
return linkHasChildren(childLink) ? (
|
||||
<NavBarMenuItemWrapper
|
||||
key={`${link.text}-${childLink.text}`}
|
||||
link={childLink}
|
||||
activeItem={activeItem}
|
||||
onClose={onClose}
|
||||
/>
|
||||
) : (
|
||||
!childLink.isCreateAction && (
|
||||
<NavBarMenuItem
|
||||
key={`${link.text}-${childLink.text}`}
|
||||
isActive={isMatchOrChildMatch(childLink, activeItem)}
|
||||
isChild
|
||||
onClick={() => {
|
||||
childLink.onClick?.();
|
||||
onClose();
|
||||
}}
|
||||
target={childLink.target}
|
||||
url={childLink.url}
|
||||
>
|
||||
{childLink.text}
|
||||
</NavBarMenuItem>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</NavBarMenuSection>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
children: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}),
|
||||
flex: css({
|
||||
display: 'flex',
|
||||
}),
|
||||
itemWithoutMenu: css({
|
||||
position: 'relative',
|
||||
placeItems: 'inherit',
|
||||
justifyContent: 'start',
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
alignItems: 'center',
|
||||
}),
|
||||
fullWidth: css({
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}),
|
||||
iconContainer: css({
|
||||
display: 'flex',
|
||||
placeContent: 'center',
|
||||
}),
|
||||
itemWithoutMenuContent: css({
|
||||
display: 'grid',
|
||||
gridAutoFlow: 'column',
|
||||
gridTemplateColumns: `${theme.spacing(7)} auto`,
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
}),
|
||||
linkText: css({
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
justifySelf: 'start',
|
||||
}),
|
||||
emptyMessage: css({
|
||||
color: theme.colors.text.secondary,
|
||||
fontStyle: 'italic',
|
||||
padding: theme.spacing(1, 1.5, 1, 7),
|
||||
}),
|
||||
});
|
||||
|
||||
function linkHasChildren(link: NavModelItem): link is NavModelItem & { children: NavModelItem[] } {
|
||||
return Boolean(link.children && link.children.length > 0);
|
||||
}
|
26
public/app/core/components/Indent/Indent.tsx
Normal file
26
public/app/core/components/Indent/Indent.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2, ThemeSpacingTokens } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { getResponsiveStyle, ResponsiveProp } from '@grafana/ui/src/components/Layout/utils/responsiveness';
|
||||
|
||||
interface IndentProps {
|
||||
children?: React.ReactNode;
|
||||
level: number;
|
||||
spacing: ResponsiveProp<ThemeSpacingTokens>;
|
||||
}
|
||||
|
||||
export function Indent({ children, spacing, level }: IndentProps) {
|
||||
const styles = useStyles2(getStyles, spacing, level);
|
||||
|
||||
return <span className={css(styles.indentor)}>{children}</span>;
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, spacing: IndentProps['spacing'], level: IndentProps['level']) => ({
|
||||
indentor: css(
|
||||
getResponsiveStyle(theme, spacing, (val) => ({
|
||||
paddingLeft: theme.spacing(val * level),
|
||||
}))
|
||||
),
|
||||
});
|
@ -8,8 +8,8 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { IconButton, useStyles2 } from '@grafana/ui';
|
||||
import { getSvgSize } from '@grafana/ui/src/components/Icon/utils';
|
||||
import { Text } from '@grafana/ui/src/components/Text/Text';
|
||||
import { Indent } from 'app/core/components/Indent/Indent';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { Indent } from 'app/features/browse-dashboards/components/Indent';
|
||||
import { childrenByParentUIDSelector, rootItemsSelector } from 'app/features/browse-dashboards/state';
|
||||
import { DashboardsTreeItem } from 'app/features/browse-dashboards/types';
|
||||
import { DashboardViewItem } from 'app/features/search/types';
|
||||
@ -153,7 +153,7 @@ function Row({ index, style: virtualStyles, data }: RowProps) {
|
||||
if (item.kind === 'ui' && item.uiKind === 'pagination-placeholder') {
|
||||
return (
|
||||
<span style={virtualStyles} className={styles.row}>
|
||||
<Indent level={level} />
|
||||
<Indent level={level} spacing={2} />
|
||||
<Skeleton width={SKELETON_WIDTHS[index % SKELETON_WIDTHS.length]} />
|
||||
</span>
|
||||
);
|
||||
@ -190,7 +190,7 @@ function Row({ index, style: virtualStyles, data }: RowProps) {
|
||||
id={getDOMId(idPrefix, item.uid)}
|
||||
>
|
||||
<div className={styles.rowBody}>
|
||||
<Indent level={level} />
|
||||
<Indent level={level} spacing={2} />
|
||||
{foldersAreOpenable ? (
|
||||
<IconButton
|
||||
size={CHEVRON_SIZE}
|
||||
|
@ -10,13 +10,7 @@ import { useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { DashboardViewItem } from 'app/features/search/types';
|
||||
|
||||
import {
|
||||
DashboardsTreeCellProps,
|
||||
DashboardsTreeColumn,
|
||||
DashboardsTreeItem,
|
||||
INDENT_AMOUNT_CSS_VAR,
|
||||
SelectionState,
|
||||
} from '../types';
|
||||
import { DashboardsTreeCellProps, DashboardsTreeColumn, DashboardsTreeItem, SelectionState } from '../types';
|
||||
|
||||
import CheckboxCell from './CheckboxCell';
|
||||
import CheckboxHeaderCell from './CheckboxHeaderCell';
|
||||
@ -126,7 +120,7 @@ export function DashboardsTree({
|
||||
);
|
||||
|
||||
return (
|
||||
<div {...getTableProps()} className={styles.tableRoot} role="table">
|
||||
<div {...getTableProps()} role="table">
|
||||
{headerGroups.map((headerGroup) => {
|
||||
const { key, ...headerGroupProps } = headerGroup.getHeaderGroupProps({
|
||||
style: { width },
|
||||
@ -213,15 +207,6 @@ function VirtualListRow({ index, style, data }: VirtualListRowProps) {
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
tableRoot: css({
|
||||
// Responsively
|
||||
[INDENT_AMOUNT_CSS_VAR]: theme.spacing(1),
|
||||
|
||||
[theme.breakpoints.up('md')]: {
|
||||
[INDENT_AMOUNT_CSS_VAR]: theme.spacing(3),
|
||||
},
|
||||
}),
|
||||
|
||||
// Column flex properties (cell sizing) are set by customFlexTableLayout.ts
|
||||
|
||||
row: css({
|
||||
|
@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { INDENT_AMOUNT_CSS_VAR } from '../types';
|
||||
|
||||
interface IndentProps {
|
||||
children?: React.ReactNode;
|
||||
level: number;
|
||||
}
|
||||
|
||||
export function Indent({ children, level }: IndentProps) {
|
||||
const theme = useTheme2();
|
||||
|
||||
// DashboardsTree responsively sets the value of INDENT_AMOUNT_CSS_VAR
|
||||
// but we also have a fallback just in case it's not set for some reason...
|
||||
const space = `var(${INDENT_AMOUNT_CSS_VAR}, ${theme.spacing(2)})`;
|
||||
|
||||
return <span style={{ paddingLeft: `calc(${space} * ${level})` }}>{children}</span>;
|
||||
}
|
@ -9,11 +9,10 @@ import { Icon, IconButton, Link, Spinner, useStyles2, Text } from '@grafana/ui';
|
||||
import { getSvgSize } from '@grafana/ui/src/components/Icon/utils';
|
||||
import { getIconForKind } from 'app/features/search/service/utils';
|
||||
|
||||
import { Indent } from '../../../core/components/Indent/Indent';
|
||||
import { useChildrenByParentUIDState } from '../state';
|
||||
import { DashboardsTreeItem } from '../types';
|
||||
|
||||
import { Indent } from './Indent';
|
||||
|
||||
const CHEVRON_SIZE = 'md';
|
||||
const ICON_SIZE = 'sm';
|
||||
|
||||
@ -31,7 +30,13 @@ export function NameCell({ row: { original: data }, onFolderClick }: NameCellPro
|
||||
if (item.kind === 'ui') {
|
||||
return (
|
||||
<>
|
||||
<Indent level={level} />
|
||||
<Indent
|
||||
level={level}
|
||||
spacing={{
|
||||
xs: 1,
|
||||
md: 3,
|
||||
}}
|
||||
/>
|
||||
<span className={styles.folderButtonSpacer} />
|
||||
{item.uiKind === 'empty-folder' ? (
|
||||
<em className={styles.emptyText}>
|
||||
@ -48,7 +53,13 @@ export function NameCell({ row: { original: data }, onFolderClick }: NameCellPro
|
||||
|
||||
return (
|
||||
<>
|
||||
<Indent level={level} />
|
||||
<Indent
|
||||
level={level}
|
||||
spacing={{
|
||||
xs: 1,
|
||||
md: 3,
|
||||
}}
|
||||
/>
|
||||
|
||||
{item.kind === 'folder' ? (
|
||||
<IconButton
|
||||
|
@ -42,8 +42,6 @@ export interface DashboardsTreeItem<T extends DashboardViewItemWithUIItems = Das
|
||||
parentUID?: string;
|
||||
}
|
||||
|
||||
export const INDENT_AMOUNT_CSS_VAR = '--dashboards-tree-indentation';
|
||||
|
||||
interface RendererUserProps {
|
||||
// Note: userProps for cell renderers (e.g. second argument in `cell.render('Cell', foo)` )
|
||||
// aren't typed, so we must be careful when accessing this
|
||||
|
Loading…
Reference in New Issue
Block a user