Commit 65b96e11 by Torkel Ödegaard

Merge branch 'react-panels' of github.com:grafana/grafana into react-panels

parents c74a1c18 13bc9f6f
...@@ -5,14 +5,10 @@ coreModule.directive('dashClass', function($timeout) { ...@@ -5,14 +5,10 @@ coreModule.directive('dashClass', function($timeout) {
return { return {
link: function($scope, elem) { link: function($scope, elem) {
$scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) { $scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) {
$timeout(() => { elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true);
elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true);
});
}); });
$scope.onAppEvent('panel-fullscreen-exit', function() { elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true);
elem.toggleClass('panel-in-fullscreen', false);
});
$scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) { $scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) {
elem.toggleClass('playlist-active', newValue === true); elem.toggleClass('playlist-active', newValue === true);
......
...@@ -85,7 +85,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> { ...@@ -85,7 +85,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
this.dashboard.on('panel-added', this.triggerForceUpdate.bind(this)); this.dashboard.on('panel-added', this.triggerForceUpdate.bind(this));
this.dashboard.on('panel-removed', this.triggerForceUpdate.bind(this)); this.dashboard.on('panel-removed', this.triggerForceUpdate.bind(this));
this.dashboard.on('repeats-processed', this.triggerForceUpdate.bind(this)); this.dashboard.on('repeats-processed', this.triggerForceUpdate.bind(this));
this.dashboard.on('view-mode-changed', this.triggerForceUpdate.bind(this)); this.dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this));
this.dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this)); this.dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this));
this.dashboard.on('row-expanded', this.triggerForceUpdate.bind(this)); this.dashboard.on('row-expanded', this.triggerForceUpdate.bind(this));
} }
...@@ -142,6 +142,10 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> { ...@@ -142,6 +142,10 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
} }
} }
onViewModeChanged(payload) {
this.setState({ animated: payload.fullscreen });
}
updateGridPos(item, layout) { updateGridPos(item, layout) {
this.panelMap[item.i].updateGridPos(item); this.panelMap[item.i].updateGridPos(item);
...@@ -165,9 +169,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> { ...@@ -165,9 +169,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
componentDidMount() { componentDidMount() {
setTimeout(() => { setTimeout(() => {
this.setState(() => { this.setState({ animated: true });
return { animated: true };
});
}); });
} }
...@@ -178,7 +180,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> { ...@@ -178,7 +180,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen });
panelElements.push( panelElements.push(
<div key={panel.id.toString()} className={panelClasses}> <div key={panel.id.toString()} className={panelClasses}>
<DashboardPanel panel={panel} dashboard={this.dashboard} /> <DashboardPanel panel={panel} dashboard={this.dashboard} panelContainer={this.panelContainer} />
</div> </div>
); );
} }
......
import React from 'react'; import React from 'react';
import config from 'app/core/config'; import config from 'app/core/config';
import classNames from 'classnames';
import { PanelModel } from '../panel_model'; import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model'; import { DashboardModel } from '../dashboard_model';
import { AttachedPanel } from './PanelLoader'; import { AttachedPanel } from './PanelLoader';
import { DashboardRow } from './DashboardRow'; import { DashboardRow } from './DashboardRow';
import { PanelContainer } from './PanelContainer';
import { AddPanelPanel } from './AddPanelPanel'; import { AddPanelPanel } from './AddPanelPanel';
import { importPluginModule } from 'app/features/plugins/plugin_loader'; import { importPluginModule } from 'app/features/plugins/plugin_loader';
import { PanelChrome } from './PanelChrome';
export interface DashboardPanelProps { export interface DashboardPanelProps {
panel: PanelModel; panel: PanelModel;
dashboard: DashboardModel; dashboard: DashboardModel;
panelContainer: PanelContainer;
} }
export class DashboardPanel extends React.Component<DashboardPanelProps, any> { export class DashboardPanel extends React.Component<DashboardPanelProps, any> {
...@@ -50,77 +52,43 @@ export class DashboardPanel extends React.Component<DashboardPanelProps, any> { ...@@ -50,77 +52,43 @@ export class DashboardPanel extends React.Component<DashboardPanelProps, any> {
return <AddPanelPanel panel={this.props.panel} dashboard={this.props.dashboard} />; return <AddPanelPanel panel={this.props.panel} dashboard={this.props.dashboard} />;
} }
render() { componentDidUpdate() {
if (this.isSpecial()) { // skip loading angular component if we have no element
return this.specialPanels[this.props.panel.type](); // or we have already loaded it
if (!this.element || this.attachedPanel) {
return;
} }
let PanelComponent = null; const loader = this.props.panelContainer.getPanelLoader();
this.attachedPanel = loader.load(this.element, this.props.panel, this.props.dashboard);
}
if (this.pluginExports && this.pluginExports.PanelComponent) { componentWillUnmount() {
PanelComponent = this.pluginExports.PanelComponent; if (this.attachedPanel) {
this.attachedPanel.destroy();
} }
return (
<div className="panel-container">
<PanelHeader panel={this.props.panel} dashboard={this.props.dashboard} />
<div className="panel-content">{PanelComponent && <PanelComponent />}</div>
</div>
);
} }
}
interface PanelHeaderProps { render() {
panel: PanelModel; if (this.isSpecial()) {
dashboard: DashboardModel; return this.specialPanels[this.props.panel.type]();
} }
export class PanelHeader extends React.Component<PanelHeaderProps, any> { if (!this.pluginExports) {
onEditPanel = () => { return null;
this.props.dashboard.setViewMode(this.props.panel, true, true); }
};
render() { if (this.pluginExports.PanelComponent) {
let isFullscreen = false; return (
let isLoading = false; <PanelChrome
let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); component={this.pluginExports.PanelComponent}
panel={this.props.panel}
return ( dashboard={this.props.dashboard}
<div className={panelHeaderClass}> />
<span className="panel-info-corner"> );
<i className="fa" /> }
<span className="panel-info-corner-inner" />
</span> // legacy angular rendering
return <div ref={element => (this.element = element)} className="panel-height-helper" />;
{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 href="asd">asd</a>
</li>
</ul>
</span>
<span className="panel-time-info">
<i className="fa fa-clock-o" /> 4m
</span>
</span>
</div>
</div>
);
} }
} }
import React from 'react';
import $ from 'jquery';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
import { PanelHeader } from './PanelHeader';
import { PanelEditor } from './PanelEditor';
const TITLE_HEIGHT = 27;
const PANEL_BORDER = 2;
export interface PanelChromeProps {
panel: PanelModel;
dashboard: DashboardModel;
component: any;
}
export class PanelChrome extends React.Component<PanelChromeProps, any> {
constructor(props) {
super(props);
this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this));
}
triggerForceUpdate() {
this.forceUpdate();
}
render() {
let panelContentStyle = {
height: this.getPanelHeight(),
};
let PanelComponent = this.props.component;
console.log('PanelChrome render');
return (
<div className="panel-height-helper">
<div className="panel-container">
<PanelHeader panel={this.props.panel} dashboard={this.props.dashboard} />
<div className="panel-content" style={panelContentStyle}>
{<PanelComponent />}
</div>
</div>
<div>
{this.props.panel.isEditing && <PanelEditor panel={this.props.panel} dashboard={this.props.dashboard} />}
</div>
</div>
);
}
getPanelHeight() {
const panel = this.props.panel;
let height = 0;
if (panel.fullscreen) {
var docHeight = $(window).height();
var editHeight = Math.floor(docHeight * 0.4);
var fullscreenHeight = Math.floor(docHeight * 0.8);
height = panel.isEditing ? editHeight : fullscreenHeight;
} else {
height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN;
}
return height - PANEL_BORDER + TITLE_HEIGHT;
}
}
import React from 'react';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
interface PanelEditorProps {
panel: PanelModel;
dashboard: DashboardModel;
}
export class PanelEditor extends React.Component<PanelEditorProps, any> {
render() {
return (
<div className="tabbed-view tabbed-view--panel-edit">
<div className="tabbed-view-header">
<ul className="gf-tabs">
<li className="gf-tabs-item">
<a className="gf-tabs-link active">Queries</a>
</li>
<li className="gf-tabs-item">
<a className="gf-tabs-link">Visualization</a>
</li>
</ul>
<button className="tabbed-view-close-btn" ng-click="ctrl.exitFullscreen();">
<i className="fa fa-remove" />
</button>
</div>
<div className="tabbed-view-body">testing</div>
</div>
);
}
}
import React from 'react';
import classNames from 'classnames';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { store } from 'app/stores/store';
interface PanelHeaderProps {
panel: PanelModel;
dashboard: DashboardModel;
}
export class PanelHeader extends React.Component<PanelHeaderProps, any> {
onEditPanel = () => {
store.view.updateQuery({
panelId: this.props.panel.id,
edit: true,
fullscreen: true,
});
};
render() {
let isFullscreen = false;
let isLoading = false;
let 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 href="asd">asd</a>
</li>
</ul>
</span>
<span className="panel-time-info">
<i className="fa fa-clock-o" /> 4m
</span>
</span>
</div>
</div>
);
}
}
...@@ -82,7 +82,6 @@ export class PanelModel { ...@@ -82,7 +82,6 @@ export class PanelModel {
this.gridPos.h = newPos.h; this.gridPos.h = newPos.h;
if (sizeChanged) { if (sizeChanged) {
console.log('PanelModel sizeChanged event and render events fired');
this.events.emit('panel-size-changed'); this.events.emit('panel-size-changed');
} }
} }
...@@ -91,6 +90,10 @@ export class PanelModel { ...@@ -91,6 +90,10 @@ export class PanelModel {
this.events.emit('panel-size-changed'); this.events.emit('panel-size-changed');
} }
initEditMode() {
this.events.emit('panel-init-edit-mode');
}
destroy() { destroy() {
this.events.removeAllListeners(); this.events.removeAllListeners();
} }
......
...@@ -14,7 +14,7 @@ jest.mock('app/core/store', () => ({ ...@@ -14,7 +14,7 @@ jest.mock('app/core/store', () => ({
})); }));
describe('AddPanelPanel', () => { describe('AddPanelPanel', () => {
let wrapper, dashboardMock, getPanelContainer, panel; let wrapper, dashboardMock, panel;
beforeEach(() => { beforeEach(() => {
config.panels = [ config.panels = [
...@@ -77,13 +77,8 @@ describe('AddPanelPanel', () => { ...@@ -77,13 +77,8 @@ describe('AddPanelPanel', () => {
dashboardMock = { toggleRow: jest.fn() }; dashboardMock = { toggleRow: jest.fn() };
getPanelContainer = jest.fn().mockReturnValue({
getDashboard: jest.fn().mockReturnValue(dashboardMock),
getPanelLoader: jest.fn(),
});
panel = new PanelModel({ collapsed: false }); panel = new PanelModel({ collapsed: false });
wrapper = shallow(<AddPanelPanel panel={panel} getPanelContainer={getPanelContainer} />); wrapper = shallow(<AddPanelPanel panel={panel} dashboard={dashboardMock} />);
}); });
it('should fetch all panels sorted with core plugins first', () => { it('should fetch all panels sorted with core plugins first', () => {
......
...@@ -33,10 +33,6 @@ export class DashboardViewState { ...@@ -33,10 +33,6 @@ export class DashboardViewState {
self.update(payload); self.update(payload);
}); });
$scope.onAppEvent('panel-initialized', function(evt, payload) {
self.registerPanel(payload.scope);
});
// this marks changes to location during this digest cycle as not to add history item // this marks changes to location during this digest cycle as not to add history item
// don't want url changes like adding orgId to add browser history // don't want url changes like adding orgId to add browser history
$location.replace(); $location.replace();
...@@ -124,102 +120,61 @@ export class DashboardViewState { ...@@ -124,102 +120,61 @@ export class DashboardViewState {
} }
syncState() { syncState() {
if (this.panelScopes.length === 0) {
return;
}
if (this.dashboard.meta.fullscreen) { if (this.dashboard.meta.fullscreen) {
var panelScope = this.getPanelScope(this.state.panelId); var panel = this.dashboard.getPanelById(this.state.panelId);
if (!panelScope) {
if (!panel) {
return; return;
} }
if (this.fullscreenPanel) { if (this.fullscreenPanel) {
// if already fullscreen // if already fullscreen
if (this.fullscreenPanel === panelScope && this.editStateChanged === false) { if (this.fullscreenPanel === panel && this.editStateChanged === false) {
return; return;
} else { } else {
this.leaveFullscreen(false); this.leaveFullscreen(false);
} }
} }
if (!panelScope.ctrl.editModeInitiated) { if (!panel.fullscreen) {
panelScope.ctrl.initEditMode(); this.enterFullscreen(panel);
}
if (!panelScope.ctrl.fullscreen) {
this.enterFullscreen(panelScope);
} }
} else if (this.fullscreenPanel) { } else if (this.fullscreenPanel) {
this.leaveFullscreen(true); this.leaveFullscreen(true);
} }
} }
getPanelScope(id) {
return _.find(this.panelScopes, function(panelScope) {
return panelScope.ctrl.panel.id === id;
});
}
leaveFullscreen(render) { leaveFullscreen(render) {
var self = this; var panel = this.fullscreenPanel;
var ctrl = self.fullscreenPanel.ctrl;
ctrl.editMode = false;
ctrl.fullscreen = false;
this.dashboard.setViewMode(ctrl.panel, false, false); this.dashboard.setViewMode(panel, false, false);
this.$scope.appEvent('panel-fullscreen-exit', { panelId: ctrl.panel.id });
this.$scope.appEvent('dash-scroll', { restore: true }); this.$scope.appEvent('dash-scroll', { restore: true });
if (!render) { if (!render) {
return false; return false;
} }
this.$timeout(function() { this.$timeout(() => {
if (self.oldTimeRange !== ctrl.range) { if (this.oldTimeRange !== this.dashboard.time) {
self.$rootScope.$broadcast('refresh'); this.$rootScope.$broadcast('refresh');
} else { } else {
self.$rootScope.$broadcast('render'); this.$rootScope.$broadcast('render');
} }
delete self.fullscreenPanel; delete this.fullscreenPanel;
}); });
return true; return true;
} }
enterFullscreen(panelScope) { enterFullscreen(panel) {
var ctrl = panelScope.ctrl; const isEditing = this.state.edit && this.dashboard.meta.canEdit;
ctrl.editMode = this.state.edit && this.dashboard.meta.canEdit;
ctrl.fullscreen = true;
this.oldTimeRange = ctrl.range; this.oldTimeRange = this.dashboard.time;
this.fullscreenPanel = panelScope; this.fullscreenPanel = panel;
// Firefox doesn't return scrollTop position properly if 'dash-scroll' is emitted after setViewMode() // Firefox doesn't return scrollTop position properly if 'dash-scroll' is emitted after setViewMode()
this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 }); this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 });
this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode); this.dashboard.setViewMode(panel, true, isEditing);
this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id });
}
registerPanel(panelScope) {
var self = this;
self.panelScopes.push(panelScope);
if (!self.dashboard.meta.soloMode) {
if (self.state.panelId === panelScope.ctrl.panel.id) {
if (self.state.edit) {
panelScope.ctrl.editPanel();
} else {
panelScope.ctrl.viewPanel();
}
}
}
var unbind = panelScope.$on('$destroy', function() {
self.panelScopes = _.without(self.panelScopes, panelScope);
unbind();
});
} }
} }
......
...@@ -24,10 +24,8 @@ export class PanelCtrl { ...@@ -24,10 +24,8 @@ export class PanelCtrl {
$injector: any; $injector: any;
$location: any; $location: any;
$timeout: any; $timeout: any;
fullscreen: boolean;
inspector: any; inspector: any;
editModeInitiated: boolean; editModeInitiated: boolean;
editMode: any;
height: any; height: any;
containerHeight: any; containerHeight: any;
events: Emitter; events: Emitter;
...@@ -130,6 +128,7 @@ export class PanelCtrl { ...@@ -130,6 +128,7 @@ export class PanelCtrl {
return { templateUrl: directiveFn }; return { templateUrl: directiveFn };
}; };
} }
if (index) { if (index) {
this.editorTabs.splice(index, 0, editorTab); this.editorTabs.splice(index, 0, editorTab);
} else { } else {
...@@ -190,7 +189,7 @@ export class PanelCtrl { ...@@ -190,7 +189,7 @@ export class PanelCtrl {
getExtendedMenu() { getExtendedMenu() {
let menu = []; let menu = [];
if (!this.fullscreen && this.dashboard.meta.canEdit) { if (!this.panel.fullscreen && this.dashboard.meta.canEdit) {
menu.push({ menu.push({
text: 'Duplicate', text: 'Duplicate',
click: 'ctrl.duplicate()', click: 'ctrl.duplicate()',
...@@ -220,15 +219,15 @@ export class PanelCtrl { ...@@ -220,15 +219,15 @@ export class PanelCtrl {
} }
otherPanelInFullscreenMode() { otherPanelInFullscreenMode() {
return this.dashboard.meta.fullscreen && !this.fullscreen; return this.dashboard.meta.fullscreen && !this.panel.fullscreen;
} }
calculatePanelHeight() { calculatePanelHeight() {
if (this.fullscreen) { if (this.panel.fullscreen) {
var docHeight = $(window).height(); var docHeight = $(window).height();
var editHeight = Math.floor(docHeight * 0.4); var editHeight = Math.floor(docHeight * 0.4);
var fullscreenHeight = Math.floor(docHeight * 0.8); var fullscreenHeight = Math.floor(docHeight * 0.8);
this.containerHeight = this.editMode ? editHeight : fullscreenHeight; this.containerHeight = this.panel.isEditing ? editHeight : fullscreenHeight;
} else { } else {
this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + (this.panel.gridPos.h - 1) * GRID_CELL_VMARGIN; this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + (this.panel.gridPos.h - 1) * GRID_CELL_VMARGIN;
} }
...@@ -237,6 +236,11 @@ export class PanelCtrl { ...@@ -237,6 +236,11 @@ export class PanelCtrl {
this.containerHeight = $(window).height(); this.containerHeight = $(window).height();
} }
// hacky solution
if (this.panel.isEditing && !this.editModeInitiated) {
this.initEditMode();
}
this.height = this.containerHeight - (PANEL_BORDER + TITLE_HEIGHT); this.height = this.containerHeight - (PANEL_BORDER + TITLE_HEIGHT);
} }
...@@ -247,9 +251,6 @@ export class PanelCtrl { ...@@ -247,9 +251,6 @@ export class PanelCtrl {
duplicate() { duplicate() {
this.dashboard.duplicatePanel(this.panel); this.dashboard.duplicatePanel(this.panel);
this.$timeout(() => {
this.$scope.$root.$broadcast('render');
});
} }
removePanel() { removePanel() {
......
...@@ -7,7 +7,7 @@ var module = angular.module('grafana.directives'); ...@@ -7,7 +7,7 @@ var module = angular.module('grafana.directives');
var panelTemplate = ` var panelTemplate = `
<div class="panel-container"> <div class="panel-container">
<div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.fullscreen}"> <div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.panel.fullscreen}">
<span class="panel-info-corner"> <span class="panel-info-corner">
<i class="fa"></i> <i class="fa"></i>
<span class="panel-info-corner-inner"></span> <span class="panel-info-corner-inner"></span>
...@@ -25,7 +25,7 @@ var panelTemplate = ` ...@@ -25,7 +25,7 @@ var panelTemplate = `
</div> </div>
</div> </div>
<div class="panel-full-edit" ng-if="ctrl.editMode"> <div class="panel-full-edit" ng-if="ctrl.panel.isEditing">
<div class="tabbed-view tabbed-view--panel-edit"> <div class="tabbed-view tabbed-view--panel-edit">
<div class="tabbed-view-header"> <div class="tabbed-view-header">
<h3 class="tabbed-view-panel-title"> <h3 class="tabbed-view-panel-title">
......
...@@ -197,6 +197,7 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { ...@@ -197,6 +197,7 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
// Function for rendering panel // Function for rendering panel
function render_panel() { function render_panel() {
panelWidth = elem.width(); panelWidth = elem.width();
if (shouldAbortRender()) { if (shouldAbortRender()) {
return; return;
} }
......
...@@ -6,7 +6,7 @@ export class ReactTestPanel extends React.Component<any, any> { ...@@ -6,7 +6,7 @@ export class ReactTestPanel extends React.Component<any, any> {
} }
render() { render() {
return <h2>Panel content</h2>; return <h2>I am a react panel, haha!</h2>;
} }
} }
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
} }
// Disable grid interaction indicators in fullscreen panels // Disable grid interaction indicators in fullscreen panels
.panel-header:hover { .panel-header:hover {
background-color: inherit; background-color: inherit;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment