mirror of https://github.com/grafana/grafana
Merge pull request #13984 from grafana/12759-panel-header-standard-menu-only
12759 panel header menu in React (standard options only)pull/13994/head
commit
3b4d8c9b9b
@ -1,83 +0,0 @@ |
||||
import React from 'react'; |
||||
import classNames from 'classnames'; |
||||
import { PanelModel } from '../panel_model'; |
||||
import { DashboardModel } from '../dashboard_model'; |
||||
import { store } from 'app/store/configureStore'; |
||||
import { updateLocation } from 'app/core/actions'; |
||||
|
||||
interface PanelHeaderProps { |
||||
panel: PanelModel; |
||||
dashboard: DashboardModel; |
||||
} |
||||
|
||||
export class PanelHeader extends React.Component<PanelHeaderProps, any> { |
||||
onEditPanel = () => { |
||||
store.dispatch( |
||||
updateLocation({ |
||||
query: { |
||||
panelId: this.props.panel.id, |
||||
edit: true, |
||||
fullscreen: true, |
||||
}, |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
onViewPanel = () => { |
||||
store.dispatch( |
||||
updateLocation({ |
||||
query: { |
||||
panelId: this.props.panel.id, |
||||
edit: false, |
||||
fullscreen: true, |
||||
}, |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const isFullscreen = false; |
||||
const isLoading = false; |
||||
const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); |
||||
|
||||
return ( |
||||
<div className={panelHeaderClass}> |
||||
<span className="panel-info-corner"> |
||||
<i className="fa" /> |
||||
<span className="panel-info-corner-inner" /> |
||||
</span> |
||||
|
||||
{isLoading && ( |
||||
<span className="panel-loading"> |
||||
<i className="fa fa-spinner fa-spin" /> |
||||
</span> |
||||
)} |
||||
|
||||
<div className="panel-title-container"> |
||||
<span className="panel-title"> |
||||
<span className="icon-gf panel-alert-icon" /> |
||||
<span className="panel-title-text">{this.props.panel.title}</span> |
||||
<span className="panel-menu-container dropdown"> |
||||
<span className="fa fa-caret-down panel-menu-toggle" data-toggle="dropdown" /> |
||||
<ul className="dropdown-menu dropdown-menu--menu panel-menu" role="menu"> |
||||
<li> |
||||
<a onClick={this.onEditPanel}> |
||||
<i className="fa fa-fw fa-edit" /> Edit |
||||
</a> |
||||
</li> |
||||
<li> |
||||
<a onClick={this.onViewPanel}> |
||||
<i className="fa fa-fw fa-eye" /> View |
||||
</a> |
||||
</li> |
||||
</ul> |
||||
</span> |
||||
<span className="panel-time-info"> |
||||
<i className="fa fa-clock-o" /> 4m |
||||
</span> |
||||
</span> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
import React, { PureComponent } from 'react'; |
||||
import classNames from 'classnames'; |
||||
|
||||
import { PanelHeaderMenu } from './PanelHeaderMenu'; |
||||
|
||||
import { DashboardModel } from 'app/features/dashboard/dashboard_model'; |
||||
import { PanelModel } from 'app/features/dashboard/panel_model'; |
||||
|
||||
export interface Props { |
||||
panel: PanelModel; |
||||
dashboard: DashboardModel; |
||||
} |
||||
|
||||
export class PanelHeader extends PureComponent<Props> { |
||||
render() { |
||||
const isFullscreen = false; |
||||
const isLoading = false; |
||||
const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); |
||||
const { panel, dashboard } = this.props; |
||||
|
||||
return ( |
||||
<div className={panelHeaderClass}> |
||||
<span className="panel-info-corner"> |
||||
<i className="fa" /> |
||||
<span className="panel-info-corner-inner" /> |
||||
</span> |
||||
|
||||
{isLoading && ( |
||||
<span className="panel-loading"> |
||||
<i className="fa fa-spinner fa-spin" /> |
||||
</span> |
||||
)} |
||||
|
||||
<div className="panel-title-container"> |
||||
<div className="panel-title"> |
||||
<span className="icon-gf panel-alert-icon" /> |
||||
<span className="panel-title-text" data-toggle="dropdown"> |
||||
{panel.title} <span className="fa fa-caret-down panel-menu-toggle" /> |
||||
</span> |
||||
|
||||
<PanelHeaderMenu panel={panel} dashboard={dashboard} /> |
||||
|
||||
<span className="panel-time-info"> |
||||
<i className="fa fa-clock-o" /> 4m |
||||
</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,40 @@ |
||||
import React, { PureComponent } from 'react'; |
||||
import { DashboardModel } from 'app/features/dashboard/dashboard_model'; |
||||
import { PanelModel } from 'app/features/dashboard/panel_model'; |
||||
import { PanelHeaderMenuItem } from './PanelHeaderMenuItem'; |
||||
import { getPanelMenu } from 'app/features/dashboard/utils/getPanelMenu'; |
||||
import { PanelMenuItem } from 'app/types/panel'; |
||||
|
||||
export interface Props { |
||||
panel: PanelModel; |
||||
dashboard: DashboardModel; |
||||
} |
||||
|
||||
export class PanelHeaderMenu extends PureComponent<Props> { |
||||
renderItems = (menu: PanelMenuItem[], isSubMenu = false) => { |
||||
return ( |
||||
<ul className="dropdown-menu dropdown-menu--menu panel-menu" role={isSubMenu ? '' : 'menu'}> |
||||
{menu.map((menuItem, idx: number) => { |
||||
return ( |
||||
<PanelHeaderMenuItem |
||||
key={`${menuItem.text}${idx}`} |
||||
type={menuItem.type} |
||||
text={menuItem.text} |
||||
iconClassName={menuItem.iconClassName} |
||||
onClick={menuItem.onClick} |
||||
shortcut={menuItem.shortcut} |
||||
> |
||||
{menuItem.subMenu && this.renderItems(menuItem.subMenu, true)} |
||||
</PanelHeaderMenuItem> |
||||
); |
||||
})} |
||||
</ul> |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { dashboard, panel } = this.props; |
||||
const menu = getPanelMenu(dashboard, panel); |
||||
return <div className="panel-menu-container dropdown">{this.renderItems(menu)}</div>; |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
import React, { SFC } from 'react'; |
||||
import { PanelMenuItem } from 'app/types/panel'; |
||||
|
||||
interface Props { |
||||
children: any; |
||||
} |
||||
|
||||
export const PanelHeaderMenuItem: SFC<Props & PanelMenuItem> = props => { |
||||
const isSubMenu = props.type === 'submenu'; |
||||
const isDivider = props.type === 'divider'; |
||||
return isDivider ? ( |
||||
<li className="divider" /> |
||||
) : ( |
||||
<li className={isSubMenu ? 'dropdown-submenu' : null}> |
||||
<a onClick={props.onClick}> |
||||
{props.iconClassName && <i className={props.iconClassName} />} |
||||
<span className="dropdown-item-text">{props.text}</span> |
||||
{props.shortcut && <span className="dropdown-menu-item-shortcut">{props.shortcut}</span>} |
||||
</a> |
||||
{props.children} |
||||
</li> |
||||
); |
||||
}; |
@ -0,0 +1,120 @@ |
||||
import { updateLocation } from 'app/core/actions'; |
||||
import { store } from 'app/store/configureStore'; |
||||
|
||||
import { removePanel, duplicatePanel, copyPanel, editPanelJson, sharePanel } from 'app/features/dashboard/utils/panel'; |
||||
import { PanelModel } from 'app/features/dashboard/panel_model'; |
||||
import { DashboardModel } from 'app/features/dashboard/dashboard_model'; |
||||
import { PanelMenuItem } from 'app/types/panel'; |
||||
|
||||
export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => { |
||||
const onViewPanel = () => { |
||||
store.dispatch( |
||||
updateLocation({ |
||||
query: { |
||||
panelId: panel.id, |
||||
edit: false, |
||||
fullscreen: true, |
||||
}, |
||||
partial: true, |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
const onEditPanel = () => { |
||||
store.dispatch( |
||||
updateLocation({ |
||||
query: { |
||||
panelId: panel.id, |
||||
edit: true, |
||||
fullscreen: true, |
||||
}, |
||||
partial: true, |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
const onSharePanel = () => { |
||||
sharePanel(dashboard, panel); |
||||
}; |
||||
|
||||
const onDuplicatePanel = () => { |
||||
duplicatePanel(dashboard, panel); |
||||
}; |
||||
|
||||
const onCopyPanel = () => { |
||||
copyPanel(panel); |
||||
}; |
||||
|
||||
const onEditPanelJson = () => { |
||||
editPanelJson(dashboard, panel); |
||||
}; |
||||
|
||||
const onRemovePanel = () => { |
||||
removePanel(dashboard, panel, true); |
||||
}; |
||||
|
||||
const menu: PanelMenuItem[] = []; |
||||
|
||||
menu.push({ |
||||
text: 'View', |
||||
iconClassName: 'fa fa-fw fa-eye', |
||||
onClick: onViewPanel, |
||||
shortcut: 'v', |
||||
}); |
||||
|
||||
if (dashboard.meta.canEdit) { |
||||
menu.push({ |
||||
text: 'Edit', |
||||
iconClassName: 'fa fa-fw fa-edit', |
||||
onClick: onEditPanel, |
||||
shortcut: 'e', |
||||
}); |
||||
} |
||||
|
||||
menu.push({ |
||||
text: 'Share', |
||||
iconClassName: 'fa fa-fw fa-share', |
||||
onClick: onSharePanel, |
||||
shortcut: 'p s', |
||||
}); |
||||
|
||||
const subMenu: PanelMenuItem[] = []; |
||||
|
||||
if (!panel.fullscreen && dashboard.meta.canEdit) { |
||||
subMenu.push({ |
||||
text: 'Duplicate', |
||||
onClick: onDuplicatePanel, |
||||
shortcut: 'p d', |
||||
}); |
||||
|
||||
subMenu.push({ |
||||
text: 'Copy', |
||||
onClick: onCopyPanel, |
||||
}); |
||||
} |
||||
|
||||
subMenu.push({ |
||||
text: 'Panel JSON', |
||||
onClick: onEditPanelJson, |
||||
}); |
||||
|
||||
menu.push({ |
||||
type: 'submenu', |
||||
text: 'More...', |
||||
iconClassName: 'fa fa-fw fa-cube', |
||||
subMenu: subMenu, |
||||
}); |
||||
|
||||
if (dashboard.meta.canEdit) { |
||||
menu.push({ type: 'divider' }); |
||||
|
||||
menu.push({ |
||||
text: 'Remove', |
||||
iconClassName: 'fa fa-fw fa-trash', |
||||
onClick: onRemovePanel, |
||||
shortcut: 'p r', |
||||
}); |
||||
} |
||||
|
||||
return menu; |
||||
}; |
Loading…
Reference in new issue