drag and drop panels, lots of visual tweaks

This commit is contained in:
Rashid Khan 2013-09-26 15:42:43 -07:00
parent 3cd1177068
commit f2e6d4409b
20 changed files with 175 additions and 109 deletions

View file

@ -234,7 +234,8 @@ module.exports = function (grunt) {
'angular-strap',
'directives/all',
'jquery.flot.pie',
'angular-sanitize'
'angular-sanitize',
'angular-dragdrop'
]
}
];

View file

@ -11,6 +11,7 @@ define([
'bootstrap',
'angular-sanitize',
'angular-strap',
'angular-dragdrop',
'extend-jquery'
],
function (angular, $, _, appLevelRequire) {
@ -83,7 +84,8 @@ function (angular, $, _, appLevelRequire) {
'elasticjs.service',
'$strap.directives',
'ngSanitize',
'kibana',
'ngDragDrop',
'kibana'
];
_.each('controllers directives factories services filters'.split(' '),

View file

@ -15,6 +15,7 @@ require.config({
filesaver: '../vendor/filesaver',
angular: '../vendor/angular/angular',
'angular-dragdrop': '../vendor/angular/angular-dragdrop',
'angular-strap': '../vendor/angular/angular-strap',
'angular-sanitize': '../vendor/angular/angular-sanitize',
timepicker: '../vendor/angular/timepicker',
@ -25,6 +26,7 @@ require.config({
bootstrap: '../vendor/bootstrap/bootstrap',
jquery: '../vendor/jquery/jquery-1.8.0',
'jquery-ui': '../vendor/jquery/jquery-ui-1.10.3',
'extend-jquery': 'components/extend-jquery',
@ -61,6 +63,7 @@ require.config({
},
// simple dependency declaration
'jquery-ui': ['jquery'],
'jquery.flot': ['jquery'],
'jquery.flot.pie': ['jquery', 'jquery.flot'],
'jquery.flot.selection':['jquery', 'jquery.flot'],
@ -70,6 +73,7 @@ require.config({
'angular-sanitize': ['angular'],
'angular-cookies': ['angular'],
'angular-dragdrop': ['jquery','jquery-ui','angular'],
'angular-loader': ['angular'],
'angular-mocks': ['angular'],
'angular-resource': ['angular'],

View file

@ -1,5 +1,5 @@
define([
'./dash',
'./dashLoader',
'./row'
'./row',
], function () {});

View file

@ -2,7 +2,6 @@ define([
'angular',
'config',
'underscore',
'services/all'
],
function (angular, config, _) {
@ -10,11 +9,22 @@ function (angular, config, _) {
var module = angular.module('kibana.controllers');
module.controller('DashCtrl', function($scope, $route, ejsResource, fields, dashboard, alertSrv) {
module.controller('DashCtrl', function(
$scope, $route, ejsResource, fields, dashboard, alertSrv, panelMove) {
$scope.editor = {
index: 0
};
// For moving stuff around the dashboard. Needs better names
$scope.panelMove = panelMove;
$scope.panelMoveDrop = panelMove.onDrop;
$scope.panelMoveStart = panelMove.onStart;
$scope.panelMoveStop = panelMove.onStop;
$scope.panelMoveOver = panelMove.onOver;
$scope.panelMoveOut = panelMove.onOut;
$scope.init = function() {
$scope.config = config;
// Make underscore.js available to views
@ -30,6 +40,14 @@ function (angular, config, _) {
$scope.ejs = ejsResource(config.elasticsearch);
};
$scope.isPanel = function(obj) {
if(!_.isNull(obj) && !_.isUndefined(obj) && !_.isUndefined(obj.type)) {
return true;
} else {
return false;
}
};
$scope.add_row = function(dash,row) {
dash.rows.push(row);
};

View file

@ -38,7 +38,10 @@ function (angular, app, _) {
};
$scope.rowSpan = function(row) {
return _.reduce(_.pluck(row.panels,'span'), function(p,v) {
var panels = _.filter(row.panels, function(p) {
return $scope.isPanel(p);
});
return _.reduce(_.pluck(panels,'span'), function(p,v) {
return p+v;
},0);
};

View file

@ -7,36 +7,47 @@ function (angular) {
angular
.module('kibana.directives')
.directive('kibanaPanel', function($compile) {
var container = '<div class="panelCont"></div>';
var editorTemplate =
'<i class="icon-spinner small icon-spin icon-large panel-loading"' +
'ng-show="panelMeta.loading == true && !panel.title"></i>' +
'<span class="panelextra">' +
'<div class="row-fluid panel-extra"><div class="panel-extra-container">' +
'<span ng-repeat="task in panelMeta.modals" ng-show="task.show">' +
'<span bs-modal="task.partial" class="pointer"><i ' +
'bs-tooltip="task.description" ng-class="task.icon" class="pointer"></i></span>'+
' / </span>' +
'<span ng-show="panel.editable != false">' +
'<span bs-modal="\'app/partials/paneleditor.html\'" class="pointer">'+
'<i class="icon-cog pointer" bs-tooltip="\'Configure\'"></i></span>'+
' / </span>' +
'<span ng-show="panel.editable != false">' +
'<span confirm-click="row.panels = _.without(row.panels,panel)" '+
'confirmation="Are you sure you want to remove this {{panel.type}} panel?" class="pointer">'+
'<i class="icon-remove-sign pointer" bs-tooltip="\'Remove\'"></i></span>'+
' / </span>' +
'<span class="small strong">{{panel.type}}</span> ' +
'<span class="extra row-button " ng-show="panel.editable != false">' +
'<span class="row-text pointer" bs-tooltip="\'Drag title to move\'" data-drag=true '+
'data-jqyoui-options="{revert: \'invalid\',helper:\'clone\'}"'+
' jqyoui-draggable="'+
'{'+
'animate:false,'+
'index:{{$index}},'+
'onStart:\'panelMoveStart\','+
'onStop:\'panelMoveStop\''+
'}" ng-model="row.panels">{{panel.type}}</span>'+
'</span>' +
'<h4 ng-show="panel.title" style="margin:0px;">' +
'<span class="extra row-button" ng-show="panel.editable != false">' +
'<span confirm-click="row.panels = _.without(row.panels,panel)" '+
'confirmation="Are you sure you want to remove this {{panel.type}} panel?" class="pointer">'+
'<i class="icon-remove pointer" bs-tooltip="\'Remove\'"></i></span>'+
'</span>' +
'<span class="row-button extra" ng-show="panel.editable != false">' +
'<span bs-modal="\'app/partials/paneleditor.html\'" class="pointer">'+
'<i class="icon-cog pointer" bs-tooltip="\'Configure\'"></i></span>'+
'</span>' +
'<span ng-repeat="task in panelMeta.modals" class="row-button extra" ng-show="task.show">' +
'<span bs-modal="task.partial" class="pointer"><i ' +
'bs-tooltip="task.description" ng-class="task.icon" class="pointer"></i></span>'+
'</span>' +
'<span class="row-button row-text panel-title" ng-show="panel.title">' +
'{{panel.title}}&nbsp' +
'<i class="icon-spinner smaller icon-spin icon-large"' +
'ng-show="panelMeta.loading == true && panel.title"></i>' +
'</h4>';
'</span>'+
'</div></div>';
return {
restrict: 'E',
link: function($scope, elem, attr) {
@ -46,6 +57,7 @@ function (angular) {
// compile the module and uncloack. We're done
function loadModule($module) {
$module.appendTo(elem);
elem.wrap(container);
/* jshint indent:false */
$compile(elem.contents())($scope);
elem.removeClass("ng-cloak");

View file

@ -1,5 +1,4 @@
<div ng-controller='query' ng-init="init()" class="query-panel">
<label class="small">{{panel.label}}</label>
<div ng-repeat="id in (unPinnedQueries = (querySrv.ids|pinnedQuery:false))" ng-class="{'short-query': unPinnedQueries.length>1}">
<form class="form-search" style="position:relative;margin-bottom:5px;" ng-submit="refresh()">
<span class="begin-query">

View file

@ -3,7 +3,6 @@
## query
### Parameters
* label :: The label to stick over the field
* query :: A string or an array of querys. String if multi is off, array if it is on
This should be fixed, it should always be an array even if its only
one element
@ -29,7 +28,6 @@ define([
// Set and populate defaults
var _d = {
label : "Search",
query : "*",
pinned : true,
history : [],

View file

@ -243,7 +243,11 @@ function (angular, app, _, kbn, moment) {
// Sort the data
$scope.data = _.sortBy($scope.data, function(v){
if(!_.isUndefined(v.sort)) {
return v.sort[0];
} else {
return 0;
}
});
// Reverse if needed

View file

@ -8,16 +8,15 @@
<div ng-switch-when="absolute" >
<div class="timepicker-block">
<form class="nomargin">
<label><small>From</small></label>
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.from.date" data-date-format="mm/dd/yyyy" bs-datepicker>
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.from.time" bs-timepicker>
</form>
</div>
<div class="timepicker-block" style="margin-left:10px">
to
<div class="timepicker-block" style="margin-left:5px">
<form class="nomargin">
<label style="margin-left:5px"><small>To (<a ng-click="to_now()">now</a>)</small></label>
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.to.date" data-date-format="mm/dd/yyyy" bs-datepicker>
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.to.time" bs-timepicker>
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.to.time" bs-timepicker> <!--(<a ng-click="to_now()">now</a>)-->
</form>
</div>
<div class="timepicker-block">
@ -29,14 +28,12 @@
<div ng-switch-when="since">
<div class="timepicker-block">
<form class="nomargin">
<label><small>Since</small></label>
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.from.date" data-date-format="mm/dd/yyyy" bs-datepicker>
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.from.time" bs-timepicker>
</form>
</div>
<div class="timepicker-block" style="margin-left:10px">
<div class="timepicker-block" style="margin-left:5px">
<form class="nomargin">
<label><small><br></small></label>
<button class="btn" ng-click="time_apply()" ><i class="icon-ok"></i></button>
</form>
</div>
@ -44,7 +41,6 @@
<div ng-switch-when="relative">
<div class="timepicker-block">
<form class="nomargin input-append">
<label><small>The last</small></label>
<button class="btn btn" ng-repeat='timespan in panel.time_options' ng-class="{'btn-success': (panel.timespan == timespan)}" ng-click="set_timespan(timespan)">{{timespan}}</button>
<!--<select ng-model="panel.sort[0]" ng-options="f for f in fields"></select>-->
</form>

View file

@ -1,36 +1,37 @@
<div class="row-fluid container" style="margin-top:10px">
<style>
.row-panel-filler {
opacity: 0;
}
.row-panel-filler:hover {
opacity: 0.3;
}
.row-name {
}
</style>
<div class="row-fluid container" style="margin-top:10px; width:98%">
<!-- Rows -->
<div class="row-fluid kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboard.current.rows" ng-style="row_style(row)">
<div class="row-control">
<div class="row-fluid" style="padding:0px;margin:0px;position:relative;">
<div class="row-close" ng-show="row.collapse">
<span bs-tooltip="'Show row'" data-placement="right" ng-class='{pointer:row.collapsable}' class="row-name label label-inverse" ng-click="toggle_row(row)">{{row.title || 'Row '+$index}}</span>
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer editlink" bs-modal="'app/partials/roweditor.html'"></i>
<div class="clear"></div>
<div class="row-close span12" ng-show="row.collapse" data-placement="bottom" >
<span class="row-button" ng-click="toggle_row(row)" ng-show="row.collapsable}">
<i bs-tooltip="'Expand row'" data-placement="right" ng-show="row.editable" class="icon-expand pointer" ></i>
</span>
<span class="row-button" bs-modal="'app/partials/roweditor.html'" class="pointer">
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
</span>
<span class="row-button row-text">{{row.title || 'Row '+$index}}</span>
</div>
<div class="row-open" ng-show="!row.collapse">
<i bs-tooltip="'Hide row'" data-placement="right" ng-class='{pointer:row.collapsable}' class="icon-chevron-up" ng-click="toggle_row(row)"></i><br>
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer editlink" bs-modal="'app/partials/roweditor.html'"></i>
<div style="text-align:left" class="row-open" ng-show="!row.collapse">
<span>
<i bs-tooltip="'Hide row'" data-placement="right" ng-show="row.collapsable" class="icon-collapse-top" ng-click="toggle_row(row)"></i>
</span><br>
<span bs-modal="'app/partials/roweditor.html'">
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
</span><br>
<span bs-modal="'app/partials/roweditor.html'">
<i ng-click="editor.index = 2" bs-tooltip="'Add panel'" data-placement="right" ng-show="row.editable" class="icon-plus pointer"></i>
</span>
</div>
</div>
<div class="row-fluid" style="padding-top:0px" ng-hide="row.collapse">
<!-- Panels -->
<div ng-repeat="(name, panel) in row.panels" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel" style="min-height:{{row.height}}; position:relative">
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel nospace" style="min-height:{{row.height}}; position:relative" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}">
<!-- Error Panel -->
<div class="row-fluid">
<div class="span12 alert alert-error panel-error" ng-hide="!panel.error">
@ -39,16 +40,13 @@
</div>
</div>
<!-- Content Panel -->
<div class="row-fluid">
<div class="row-fluid" style="position:relative" ng-class="{'dragInProgress':dashboard.panelDragging}" >
<kibana-panel type="panel.type" ng-cloak></kibana-panel>
</div>
</div>
<div ng-hide="(12-rowSpan(row)) < 1 || !dashboard.current.panel_hints" ng-class="'span'+(12-rowSpan(row))" class="bordered row-panel-filler" style="height:{{row.height}}; position:relative; border: 1px dashed; border-radius: 10px;text-align:center;vertical-align:middle;">
<span class="pointer addLink" ng-click="editor.index=2" bs-modal="'app/partials/roweditor.html'">
Add a panel here<br>
<i style="font-size:20px" class="icon-plus-sign" ></i>
</span>
</div>
<div ng-hide="(12-rowSpan(row)) < 1 || !dashboard.current.panel_hints" class="panel span{{(12-rowSpan(row))}}" ng-class="{'dragInProgress':dashboard.panelDragging}" style="height:{{row.height}};" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,onDrop:'panelMoveDrop({{(12-rowSpan(row))}})',onOver:'panelMoveOver(false)',onOut:'panelMoveOut'}"></div>
</div>
</div>
</div>

View file

@ -35,6 +35,7 @@ function (angular, _) {
self.list = _.without(self.list,_a);
}, timeout);
}
return(_a);
};
this.clear = function(alert) {

View file

@ -5,6 +5,7 @@ define([
'./filterSrv',
'./kbnIndex',
'./querySrv',
'./timer'
'./timer',
'./panelMove'
],
function () {});

View file

@ -406,7 +406,6 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
return false;
});
};
});
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<link rel="stylesheet" href="css/bootstrap-responsive.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} dashboard-notice" ng-show="$last">
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} dashboard-notice" ng-show="$last" style="position:fixed">
<button type="button" class="close" ng-click="dashAlerts.clear(alert)" style="padding-right:50px">&times;</button>
<strong>{{alert.title}}</strong> <span ng-bind-html-unsafe='alert.text'></span> <div style="padding-right:10px" class='pull-right small'> {{$index + 1}} alert(s) </div>
</div>

View file

@ -10,8 +10,6 @@
color: @black;
}
.editor-title {
margin-right: 10px;
font-size: 1.7em;
@ -42,23 +40,35 @@
}
.kibana-row {
margin-left: 15px;
margin-bottom: 10px;
}
.row-button {
border-left: 1px solid lighten(@bodyBackground, 10%);
border-right: 1px solid darken(@bodyBackground, 10%);
padding: 2px 7px 0px 7px;
float: left;
}
.row-text {
text-transform: uppercase;
font-weight: bold;
font-size: 0.8em;
}
.row-close {
color: #bbb;
margin-left: -35px;
font-size: 9pt;
font-weight: 200;
padding-left: 35px;
padding-top:0px;
outline: 1px solid darken(@bodyBackground, 10%);
border-top: 1px solid lighten(@bodyBackground, 10%);
padding: 0px;
margin: 0px;
min-height: 24px !important;
line-height: 24px;
background: darken(@bodyBackground, 3%);
}
.row-open {
text-align: right;
color: #bbb;
left:-30px;
left:-18px;
position: absolute;
font-size: 13pt;
font-weight: 200;
@ -81,7 +91,7 @@
display: none !important;
}
.table tbody + tbody{
.table tbody + tbody {
border-top: 0px;
}
@ -98,26 +108,43 @@
z-index: 800;
}
.panel span.panelextra {
position: absolute;
z-index: 800;
display: none;
opacity: 0.7;
top:-13px;
width: 98%;
text-align:right;
}
.panel span.panelextra .link {
margin-right: 5px;
}
.panel:hover span.panelextra {
.ui-draggable-dragging {
display: block;
visibility: visible;
opacity: 1;
z-index: 9999;
}
.panel span.panelextra .editlink:hover {
opacity: 1;
.panelCont {
outline: 1px solid darken(@bodyBackground, 10%);
border-top: 1px solid lighten(@bodyBackground, 10%);
padding: 0px 10px 10px 10px;
background: darken(@bodyBackground, 3%);
margin: 0px;
}
.panel-title {
border: 0px;
margin-left: -11px;
}
.panel div.panel-extra div.panel-extra-container {
margin-right: -11px;
}
.panel div.panel-extra {
font-size: 0.9em;
margin-bottom: 10px;
}
.panel div.panel-extra .extra {
float:right !important;
border-bottom: 1px solid lighten(@bodyBackground, 5%);
}
.dragInProgress {
background-color: darken(@bodyBackground,1%);
border: 1px solid @tableBorder;
}
.link {

View file

@ -272,29 +272,32 @@
// -------------------------
@gridColumns: 12;
@gridColumnWidth: 60px;
@gridGutterWidth: 20px;
@gridGutterWidth: 10px;
@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
// 1200px min
@gridColumnWidth1200: 70px;
@gridGutterWidth1200: 30px;
@gridGutterWidth1200: 10px;
@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
// 768px-979px
@gridColumnWidth768: 42px;
@gridGutterWidth768: 20px;
@gridGutterWidth768: 10px;
@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
// Fluid grid
// -------------------------
@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth);
@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth);
@gridGutterWidth: 10px;
//@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth);
// 1200px min
@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200);
@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200);
@gridGutterWidth: 10px;
//@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200);
// 768px-979px
@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768);
@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768);
@gridGutterWidth: 10px;
//@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768);