mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Panel Header: Add an "untitled" placeholder, and change the dragging icon in the hover header (#64700)
Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
This commit is contained in:
parent
a996344e14
commit
bc5400d8f5
@ -29,6 +29,7 @@ export const availableIconsIndex = {
|
||||
'arrow-up': true,
|
||||
'arrows-h': true,
|
||||
'arrows-v': true,
|
||||
'expand-arrows': true,
|
||||
at: true,
|
||||
backward: true,
|
||||
bars: true,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { css } from '@emotion/css';
|
||||
import classnames from 'classnames';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { ReactElement, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
@ -38,18 +37,19 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(styles.container, { 'show-on-hover': !menuOpen })}
|
||||
className={cx(styles.container, { 'show-on-hover': !menuOpen })}
|
||||
style={{ top: `${offset}px` }}
|
||||
data-testid="hover-header-container"
|
||||
>
|
||||
<div
|
||||
className={classnames(styles.square, styles.draggable, dragClass)}
|
||||
className={cx(styles.square, styles.draggable, dragClass)}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerUp={onPointerUp}
|
||||
ref={draggableRef}
|
||||
>
|
||||
<Icon name="draggabledots" />
|
||||
<Icon name="expand-arrows" className={styles.draggableIcon} />
|
||||
</div>
|
||||
{!title && <h6 className={cx(styles.untitled, styles.draggable, dragClass)}>Untitled</h6>}
|
||||
{children}
|
||||
<div className={styles.square}>
|
||||
<PanelMenu
|
||||
@ -77,7 +77,7 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
right: 0,
|
||||
boxSizing: 'border-box',
|
||||
boxSizing: 'content-box',
|
||||
alignItems: 'center',
|
||||
background: theme.colors.background.secondary,
|
||||
color: theme.colors.text.primary,
|
||||
@ -92,18 +92,37 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
alignItems: 'center',
|
||||
width: theme.spacing(4),
|
||||
height: '100%',
|
||||
paddingRight: theme.spacing(0.5),
|
||||
}),
|
||||
draggable: css({
|
||||
cursor: 'move',
|
||||
// mobile do not support draggable panels
|
||||
[theme.breakpoints.down('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
menuButton: css({
|
||||
color: theme.colors.text.primary,
|
||||
// Background and border are overriden when topnav toggle is disabled
|
||||
background: 'inherit',
|
||||
border: 'none',
|
||||
'&:hover': {
|
||||
background: 'inherit',
|
||||
background: theme.colors.secondary.main,
|
||||
},
|
||||
}),
|
||||
title: css({
|
||||
padding: theme.spacing(0.75),
|
||||
}),
|
||||
untitled: css({
|
||||
color: theme.colors.text.disabled,
|
||||
fontStyle: 'italic',
|
||||
marginBottom: 0,
|
||||
}),
|
||||
draggableIcon: css({
|
||||
transform: 'rotate(45deg)',
|
||||
color: theme.colors.text.secondary,
|
||||
'&:hover': {
|
||||
color: theme.colors.text.primary,
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { CSSProperties, ReactElement, ReactNode } from 'react';
|
||||
import { useMedia } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -84,7 +85,14 @@ export function PanelChrome({
|
||||
}: PanelChromeProps) {
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
const hasHeader = !hoverHeader;
|
||||
|
||||
const pointerQuery = '(pointer: coarse)';
|
||||
// detect if we are on touch devices
|
||||
const isTouchDevice = useMedia(pointerQuery);
|
||||
const hasHeader = !hoverHeader || isTouchDevice;
|
||||
|
||||
// hover menu is only shown on hover when not on touch devices
|
||||
const showOnHoverClass = !isTouchDevice ? 'show-on-hover' : '';
|
||||
|
||||
const headerHeight = getHeaderHeight(theme, hasHeader);
|
||||
const { contentStyle, innerWidth, innerHeight } = getContentStyle(padding, theme, width, headerHeight, height);
|
||||
@ -131,7 +139,7 @@ export function PanelChrome({
|
||||
{loadingState === LoadingState.Loading ? <LoadingBar width={width} ariaLabel="Panel loading bar" /> : null}
|
||||
</div>
|
||||
|
||||
{hoverHeader && (
|
||||
{hoverHeader && !isTouchDevice && (
|
||||
<>
|
||||
{menu && (
|
||||
<HoverWidget menu={menu} title={title} offset={hoverHeaderOffset} dragClass={dragClass}>
|
||||
@ -162,7 +170,12 @@ export function PanelChrome({
|
||||
menu={menu}
|
||||
title={title}
|
||||
placement="bottom-end"
|
||||
menuButtonClass={cx(styles.menuItem, dragClassCancel, 'show-on-hover')}
|
||||
menuButtonClass={cx(
|
||||
{ [styles.hiddenMenu]: !isTouchDevice },
|
||||
styles.menuItem,
|
||||
dragClassCancel,
|
||||
showOnHoverClass
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -296,9 +309,11 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}),
|
||||
hiddenMenu: css({
|
||||
visibility: 'hidden',
|
||||
}),
|
||||
menuItem: css({
|
||||
label: 'panel-menu',
|
||||
visibility: 'hidden',
|
||||
border: 'none',
|
||||
background: theme.colors.secondary.main,
|
||||
'&:hover': {
|
||||
|
@ -27,7 +27,7 @@ export const TitleItem = forwardRef<HTMLAnchorElement, TitleItemProps>(
|
||||
onClick={onClick}
|
||||
target={target}
|
||||
title={title}
|
||||
className={cx(styles.item, className)}
|
||||
className={cx(styles.linkItem, className)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
@ -46,29 +46,33 @@ export const TitleItem = forwardRef<HTMLAnchorElement, TitleItemProps>(
|
||||
TitleItem.displayName = 'TitleItem';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
const item = css({
|
||||
color: `${theme.colors.text.secondary}`,
|
||||
label: 'panel-header-item',
|
||||
cursor: 'auto',
|
||||
border: 'none',
|
||||
borderRadius: `${theme.shape.borderRadius()}`,
|
||||
padding: `${theme.spacing(0, 1)}`,
|
||||
height: `${theme.spacing(theme.components.panel.headerHeight)}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
'&:focus, &:focus-visible': {
|
||||
...getFocusStyles(theme),
|
||||
zIndex: 1,
|
||||
},
|
||||
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
|
||||
|
||||
'&:hover ': {
|
||||
boxShadow: `${theme.shadows.z1}`,
|
||||
background: `${theme.colors.background.secondary}`,
|
||||
color: `${theme.colors.text.primary}`,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
item: css({
|
||||
color: `${theme.colors.text.secondary}`,
|
||||
label: 'panel-header-item',
|
||||
cursor: 'auto',
|
||||
border: 'none',
|
||||
borderRadius: `${theme.shape.borderRadius()}`,
|
||||
padding: `${theme.spacing(0, 1)}`,
|
||||
height: `${theme.spacing(theme.components.panel.headerHeight)}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
'&:focus, &:focus-visible': {
|
||||
...getFocusStyles(theme),
|
||||
zIndex: 1,
|
||||
},
|
||||
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
|
||||
|
||||
'&:hover ': {
|
||||
boxShadow: `${theme.shadows.z1}`,
|
||||
background: `${theme.colors.background.secondary}`,
|
||||
},
|
||||
}),
|
||||
item,
|
||||
linkItem: cx(item, css({ cursor: 'pointer' })),
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user