mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
grid: worked on row options modal and row removal
This commit is contained in:
parent
df12cbc4a7
commit
38f97d5cc9
@ -1,5 +1,3 @@
|
||||
///<reference path="../headers/common.d.ts" />
|
||||
|
||||
import {Emitter} from './utils/emitter';
|
||||
|
||||
var appEvents = new Emitter();
|
||||
|
@ -24,7 +24,8 @@ define([
|
||||
'./repeat_option/repeat_option',
|
||||
'./dashgrid/DashboardGrid',
|
||||
'./dashgrid/PanelLoader',
|
||||
'./row/add_panel',
|
||||
'./dashgrid/RowOptions',
|
||||
'./acl/acl',
|
||||
'./acl/acl',
|
||||
'./folder_picker/picker',
|
||||
'./folder_modal/folder'
|
||||
|
@ -7,7 +7,6 @@ import {Emitter} from 'app/core/utils/emitter';
|
||||
import {contextSrv} from 'app/core/services/context_srv';
|
||||
import sortByKeys from 'app/core/utils/sort_by_keys';
|
||||
|
||||
import {DashboardRow} from './row/row_model';
|
||||
import {PanelModel} from './panel_model';
|
||||
|
||||
export class DashboardModel {
|
||||
@ -20,7 +19,6 @@ export class DashboardModel {
|
||||
timezone: any;
|
||||
editable: any;
|
||||
graphTooltip: any;
|
||||
rows: DashboardRow[];
|
||||
time: any;
|
||||
timepicker: any;
|
||||
hideControls: any;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {PanelModel} from '../panel_model';
|
||||
import {PanelContainer} from './PanelContainer';
|
||||
import { PanelModel } from '../panel_model';
|
||||
import { PanelContainer } from './PanelContainer';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export interface DashboardRowProps {
|
||||
@ -28,20 +28,49 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
||||
dashboard.toggleRow(this.props.panel);
|
||||
|
||||
this.setState(prevState => {
|
||||
return {collapsed: !prevState.collapsed};
|
||||
return { collapsed: !prevState.collapsed };
|
||||
});
|
||||
}
|
||||
|
||||
openSettings() {
|
||||
appEvents.emit('show-modal', {
|
||||
src: 'public/app/features/dashboard/partials/shareModal.html',
|
||||
scope: shareScope
|
||||
templateHtml: `<row-options row="model.row" on-updated="model.onUpdated()" on-delete="model.onDelete()" dismiss="dismiss()"></row-options>`,
|
||||
modalClass: 'modal--narrow',
|
||||
model: {
|
||||
row: this.props.panel,
|
||||
onUpdated: this.forceUpdate.bind(this),
|
||||
onDelete: this.onDelete.bind(this),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
let text2 = '';
|
||||
|
||||
if (this.props.panel.panels.length) {
|
||||
text2 = 'This will also remove ' + this.props.panel.panels.length + ' panels';
|
||||
}
|
||||
|
||||
appEvents.emit('confirm-modal', {
|
||||
title: 'Delete Row',
|
||||
text: 'Are you sure you want to remove this row?',
|
||||
text2: text2,
|
||||
icon: 'fa-trash',
|
||||
onConfirm: () => {
|
||||
const panelContainer = this.props.getPanelContainer();
|
||||
const dashboard = panelContainer.getDashboard();
|
||||
dashboard.removePanel(this.props.panel);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const classes = classNames({'dashboard-row': true, 'dashboard-row--collapsed': this.state.collapsed});
|
||||
const chevronClass = classNames({'fa': true, 'fa-chevron-down': !this.state.collapsed, 'fa-chevron-right': this.state.collapsed});
|
||||
const classes = classNames({ 'dashboard-row': true, 'dashboard-row--collapsed': this.state.collapsed });
|
||||
const chevronClass = classNames({
|
||||
fa: true,
|
||||
'fa-chevron-down': !this.state.collapsed,
|
||||
'fa-chevron-right': this.state.collapsed,
|
||||
});
|
||||
const hiddenPanels = this.props.panel.panels ? this.props.panel.panels.length : 0;
|
||||
|
||||
return (
|
||||
|
45
public/app/features/dashboard/dashgrid/RowOptions.ts
Normal file
45
public/app/features/dashboard/dashgrid/RowOptions.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import {coreModule} from 'app/core/core';
|
||||
|
||||
export class RowOptionsCtrl {
|
||||
row: any;
|
||||
source: any;
|
||||
dismiss: any;
|
||||
onUpdated: any;
|
||||
onDelete: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor() {
|
||||
this.source = this.row;
|
||||
this.row = this.row.getSaveModel();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.source.title = this.row.title;
|
||||
this.source.repeat = this.row.repeat;
|
||||
this.onUpdated();
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.onDelete();
|
||||
this.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
export function rowOptionsDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/partials/row_options.html',
|
||||
controller: RowOptionsCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
row: "=",
|
||||
dismiss: "&",
|
||||
onUpdated: "&",
|
||||
onDelete: "&"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('rowOptions', rowOptionsDirective);
|
29
public/app/features/dashboard/partials/row_options.html
Normal file
29
public/app/features/dashboard/partials/row_options.html
Normal file
@ -0,0 +1,29 @@
|
||||
<div class="modal-body">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-header-title">
|
||||
<i class="fa fa-copy"></i>
|
||||
<span class="p-l-1">Row Options</span>
|
||||
</h2>
|
||||
|
||||
<a class="modal-header-close" ng-click="ctrl.dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">Title</span>
|
||||
<input type="text" class="gf-form-input max-width-13" ng-model='ctrl.row.title'></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">Repeat for</span>
|
||||
<dash-repeat-option panel="ctrl.row"></dash-repeat-option>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<button type="button" class="btn btn-success" ng-click="ctrl.update()">Update</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="ctrl.delete()">Remove</button>
|
||||
<button type="button" class="btn btn-inverse" ng-click="ctrl.dismiss()">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -2,7 +2,7 @@ import {coreModule} from 'app/core/core';
|
||||
|
||||
var template = `
|
||||
<div class="gf-form-select-wrapper max-width-13">
|
||||
<select class="gf-form-input" ng-model="model.repeat" ng-options="f.value as f.text for f in variables" ng-change="optionChanged()">
|
||||
<select class="gf-form-input" ng-model="panel.repeat" ng-options="f.value as f.text for f in variables" ng-change="optionChanged()">
|
||||
<option value=""></option>
|
||||
</div>
|
||||
`;
|
||||
@ -13,7 +13,7 @@ function dashRepeatOptionDirective(variableSrv) {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
model: "=",
|
||||
panel: "=",
|
||||
},
|
||||
link: function(scope, element) {
|
||||
element.css({display: 'block', width: '100%'});
|
||||
|
@ -1,26 +0,0 @@
|
||||
<div class="tabbed-view-header">
|
||||
<h2 class="tabbed-view-title">
|
||||
Add Panel
|
||||
</h2>
|
||||
|
||||
<input type="text" class="gf-form-input max-width-14 pull-left" ng-model='ctrl.panelSearch' give-focus='true' ng-keydown="ctrl.keyDown($event)" ng-change="ctrl.panelSearchChanged()" placeholder="panel search filter"></input>
|
||||
|
||||
<button class="tabbed-view-close-btn" ng-click="ctrl.dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tabbed-view-body tabbed-view-body--small">
|
||||
<div class="add-panel-panels-scroll">
|
||||
<div class="add-panel-panels">
|
||||
<div class="add-panel-item"
|
||||
ng-repeat="panel in ctrl.panelHits"
|
||||
ng-class="{active: $index === ctrl.activeIndex}"
|
||||
ng-click="ctrl.addPanel(panel)"
|
||||
title="{{panel.name}}">
|
||||
<img class="add-panel-item-img" ng-src="{{panel.info.logos.small}}"></img>
|
||||
<div class="add-panel-item-name">{{panel.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,114 +0,0 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import config from 'app/core/config';
|
||||
import {coreModule} from 'app/core/core';
|
||||
|
||||
export class AddPanelCtrl {
|
||||
dashboard: any;
|
||||
allPanels: any;
|
||||
panelHits: any;
|
||||
activeIndex: any;
|
||||
panelSearch: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope, dashboardSrv) {
|
||||
this.dashboard = dashboardSrv.getCurrent();
|
||||
this.activeIndex = 0;
|
||||
|
||||
this.allPanels = _.chain(config.panels)
|
||||
.filter({hideFromList: false})
|
||||
.map(item => item)
|
||||
.orderBy('sort')
|
||||
.value();
|
||||
|
||||
this.panelHits = this.allPanels;
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.$rootScope.appEvent('hide-dash-editor');
|
||||
}
|
||||
|
||||
keyDown(evt) {
|
||||
if (evt.keyCode === 27) {
|
||||
this.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.keyCode === 40 || evt.keyCode === 39) {
|
||||
this.moveSelection(1);
|
||||
}
|
||||
|
||||
if (evt.keyCode === 38 || evt.keyCode === 37) {
|
||||
this.moveSelection(-1);
|
||||
}
|
||||
|
||||
if (evt.keyCode === 13) {
|
||||
var selectedPanel = this.panelHits[this.activeIndex];
|
||||
if (selectedPanel) {
|
||||
this.addPanel(selectedPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveSelection(direction) {
|
||||
var max = this.panelHits.length;
|
||||
var newIndex = this.activeIndex + direction;
|
||||
this.activeIndex = ((newIndex %= max) < 0) ? newIndex + max : newIndex;
|
||||
}
|
||||
|
||||
panelSearchChanged() {
|
||||
var items = this.allPanels.slice();
|
||||
var startsWith = [];
|
||||
var contains = [];
|
||||
var searchLower = this.panelSearch.toLowerCase();
|
||||
var item;
|
||||
|
||||
while (item = items.shift()) {
|
||||
var nameLower = item.name.toLowerCase();
|
||||
if (nameLower.indexOf(searchLower) === 0) {
|
||||
startsWith.push(item);
|
||||
} else if (nameLower.indexOf(searchLower) !== -1) {
|
||||
contains.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
this.panelHits = startsWith.concat(contains);
|
||||
this.activeIndex = 0;
|
||||
}
|
||||
|
||||
addPanel(panelPluginInfo) {
|
||||
let defaultHeight = 6;
|
||||
let defaultWidth = 6;
|
||||
|
||||
if (panelPluginInfo.id === "singlestat") {
|
||||
defaultWidth = 3;
|
||||
defaultHeight = 3;
|
||||
}
|
||||
|
||||
this.dashboard.addPanel({
|
||||
type: panelPluginInfo.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: defaultWidth,
|
||||
height: defaultHeight,
|
||||
title: 'New panel',
|
||||
});
|
||||
|
||||
this.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
export function addPanelDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/row/add_panel.html',
|
||||
controller: AddPanelCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('addPanel', addPanelDirective);
|
@ -1,34 +0,0 @@
|
||||
<div class="dash-row-dropview">
|
||||
<a class="dash-row-dropview-close pointer" ng-click="ctrl.rowCtrl.closeDropView();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<div class="section">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-6">Title</span>
|
||||
<input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.title'></input>
|
||||
</div>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-6">Size</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="input-small gf-form-input" ng-model="ctrl.row.titleSize" ng-options="f for f in ctrl.fontSizes"></select>
|
||||
</div>
|
||||
</div>
|
||||
<gf-form-switch class="gf-form" label="Show" checked="ctrl.row.showTitle">
|
||||
</gf-form-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">Height</span>
|
||||
<input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.height'></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">Repeat for</span>
|
||||
<dash-repeat-option model="ctrl.row"></dash-repeat-option>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,34 +0,0 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import {coreModule} from 'app/core/core';
|
||||
// import VirtualScroll from 'virtual-scroll';
|
||||
// console.log(VirtualScroll);
|
||||
|
||||
export class RowOptionsCtrl {
|
||||
row: any;
|
||||
dashboard: any;
|
||||
rowCtrl: any;
|
||||
fontSizes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
||||
|
||||
/** @ngInject */
|
||||
constructor() {
|
||||
this.row = this.rowCtrl.row;
|
||||
this.dashboard = this.rowCtrl.dashboard;
|
||||
this.row.titleSize = this.row.titleSize || 'h6';
|
||||
}
|
||||
}
|
||||
|
||||
export function rowOptionsDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/row/options.html',
|
||||
controller: RowOptionsCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
rowCtrl: "=",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('dashRowOptions', rowOptionsDirective);
|
@ -1,11 +0,0 @@
|
||||
<div class="dash-row-header">
|
||||
<a class="dash-row-header-title" ng-click="ctrl.toggleCollapse()">
|
||||
<span class="dash-row-collapse-toggle pointer">
|
||||
<i class="fa fa-chevron-down" ng-show="!ctrl.row.collapse"></i>
|
||||
<i class="fa fa-chevron-right" ng-show="ctrl.row.collapse"></i>
|
||||
</span>
|
||||
<span ng-class="ctrl.row.titleSize">{{ctrl.row.title | interpolateTemplateVars:this}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
@ -1,134 +0,0 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import {Emitter, appEvents, assignModelProperties} from 'app/core/core';
|
||||
|
||||
export class DashboardRow {
|
||||
panels: any;
|
||||
title: any;
|
||||
showTitle: any;
|
||||
titleSize: any;
|
||||
events: Emitter;
|
||||
span: number;
|
||||
height: number;
|
||||
collapse: boolean;
|
||||
|
||||
defaults = {
|
||||
title: 'Dashboard Row',
|
||||
panels: [],
|
||||
showTitle: false,
|
||||
titleSize: 'h6',
|
||||
height: 250,
|
||||
isNew: false,
|
||||
repeat: null,
|
||||
repeatRowId: null,
|
||||
repeatIteration: null,
|
||||
collapse: false,
|
||||
};
|
||||
|
||||
constructor(private model) {
|
||||
assignModelProperties(this, model, this.defaults);
|
||||
this.events = new Emitter();
|
||||
this.updateRowSpan();
|
||||
}
|
||||
|
||||
getSaveModel() {
|
||||
this.model = {};
|
||||
assignModelProperties(this.model, this, this.defaults);
|
||||
|
||||
// remove properties that dont server persisted purpose
|
||||
delete this.model.isNew;
|
||||
return this.model;
|
||||
}
|
||||
|
||||
updateRowSpan() {
|
||||
this.span = 0;
|
||||
for (let panel of this.panels) {
|
||||
this.span += panel.span;
|
||||
}
|
||||
}
|
||||
|
||||
panelSpanChanged(alwaysSendEvent?) {
|
||||
var oldSpan = this.span;
|
||||
this.updateRowSpan();
|
||||
|
||||
if (alwaysSendEvent || oldSpan !== this.span) {
|
||||
this.events.emit('span-changed');
|
||||
}
|
||||
}
|
||||
|
||||
addPanel(panel) {
|
||||
var rowSpan = this.span;
|
||||
var panelCount = this.panels.length;
|
||||
var space = (12 - rowSpan) - panel.span;
|
||||
|
||||
// try to make room of there is no space left
|
||||
if (space <= 0) {
|
||||
if (panelCount === 1) {
|
||||
this.panels[0].span = 6;
|
||||
panel.span = 6;
|
||||
} else if (panelCount === 2) {
|
||||
this.panels[0].span = 4;
|
||||
this.panels[1].span = 4;
|
||||
panel.span = 4;
|
||||
} else if (panelCount === 3) {
|
||||
this.panels[0].span = 3;
|
||||
this.panels[1].span = 3;
|
||||
this.panels[2].span = 3;
|
||||
panel.span = 3;
|
||||
}
|
||||
}
|
||||
|
||||
this.panels.push(panel);
|
||||
this.events.emit('panel-added', panel);
|
||||
this.panelSpanChanged();
|
||||
}
|
||||
|
||||
removePanel(panel, ask?) {
|
||||
if (ask !== false) {
|
||||
var text2, confirmText;
|
||||
if (panel.alert) {
|
||||
text2 = "Panel includes an alert rule, removing panel will also remove alert rule";
|
||||
confirmText = "YES";
|
||||
}
|
||||
|
||||
appEvents.emit('confirm-modal', {
|
||||
title: 'Remove Panel',
|
||||
text: 'Are you sure you want to remove this panel?',
|
||||
text2: text2,
|
||||
icon: 'fa-trash',
|
||||
confirmText: confirmText,
|
||||
yesText: 'Remove',
|
||||
onConfirm: () => {
|
||||
this.removePanel(panel, false);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var index = _.indexOf(this.panels, panel);
|
||||
this.panels.splice(index, 1);
|
||||
this.events.emit('panel-removed', panel);
|
||||
this.panelSpanChanged();
|
||||
}
|
||||
|
||||
movePanel(fromIndex, toIndex) {
|
||||
this.panels.splice(toIndex, 0, this.panels.splice(fromIndex, 1)[0]);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.events.removeAllListeners();
|
||||
}
|
||||
|
||||
copyPropertiesFromRowSource(source) {
|
||||
this.height = source.height;
|
||||
this.title = source.title;
|
||||
this.showTitle = source.showTitle;
|
||||
this.titleSize = source.titleSize;
|
||||
}
|
||||
|
||||
toggleCollapse() {
|
||||
this.collapse = !this.collapse;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<h5 class="section-heading">Repeat</h5>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-9">For each value of</span>
|
||||
<dash-repeat-option model="ctrl.panel"></dash-repeat-option>
|
||||
<dash-repeat-option panel="ctrl.panel"></dash-repeat-option>
|
||||
</div>
|
||||
<div class="gf-form" ng-show="ctrl.panel.repeat">
|
||||
<span class="gf-form-label width-9">Min width</span>
|
||||
|
@ -1,8 +1,8 @@
|
||||
.gicon {
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
width: 1.2057142857em;
|
||||
height: 1.2057142857em;
|
||||
width: 1.1057142857em;
|
||||
height: 1.1057142857em;
|
||||
text-align: center;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
@ -21,10 +21,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.react-grid-item {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.theme-dark {
|
||||
.react-grid-item > .react-resizable-handle {
|
||||
background-image: url('../img/resize-handle-white.svg');
|
||||
|
@ -14,7 +14,7 @@
|
||||
"forin": false,
|
||||
"indent": [true, "spaces", 2],
|
||||
"label-position": true,
|
||||
"max-line-length": [true, 140],
|
||||
"max-line-length": [true, 150],
|
||||
"member-access": false,
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
|
Loading…
Reference in New Issue
Block a user