Added collapsable rows, error handling in table panel, ground work for new kinds of binding, refactored view a bit
This commit is contained in:
1 parent
896a6f7c10
commit
a73242cae5
13 files changed
+78
-34
No files matched your search
@@ -17,7 +17,6 @@
|
||||
.panel-error {
|
||||
opacity: 0.9;
|
||||
position:absolute;
|
||||
top:10px;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* AngularStrap - Twitter Bootstrap directives for AngularJS
|
||||
* @version v0.6.3 - 2013-01-25
|
||||
* @link http://mgcrea.github.com/angular-strap
|
||||
* @author Olivier Louvignes
|
||||
* @license MIT License, http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
angular.module("$strap.config",[]).value("$strap.config",{}),angular.module("$strap.filters",["$strap.config"]),angular.module("$strap.directives",["$strap.config"]),angular.module("$strap",["$strap.filters","$strap.directives","$strap.config"]),angular.module("$strap.directives").directive("bsAlert",["$parse","$timeout","$compile",function(e,t,n){"use strict";return{restrict:"A",link:function(r,i,s){r.$watch(s.bsAlert,function(e,s){if(typeof e=="undefined"){typeof s!="undefined"&&i.remove();return}i.html((e.title?"<strong>"+e.title+"</strong> ":"")+e.content||""),t(function(){n(i.contents())(r)});if(e.type||s.type)s.type&&i.removeClass("alert-"+s.type),e.type&&i.addClass("alert-"+e.type);e.close!==!1&&i.prepend('<button type="button" class="close" data-dismiss="alert">×</button>')},!0),!s.bsAlert&&s.close!=="0"&&i.prepend('<button type="button" class="close" data-dismiss="alert">×</button>'),i.addClass("alert").alert()}}}]),angular.module("$strap.directives").directive("bsButton",["$parse","$timeout",function(e,t){"use strict";return{restrict:"A",require:"?ngModel",link:function(n,r,i,s){s&&(r.parent('[data-toggle="buttons-checkbox"], [data-toggle="buttons-radio"]').length||r.attr("data-toggle","button"),!n.$eval(i.ngModel)||r.addClass("active"),n.$watch(i.ngModel,function(e,t){var n=!!e,r=!!t;n!==r&&$.fn.button.Constructor.prototype.toggle.call(o)})),r.hasClass("btn")||r.on("click.button.data-api",function(e){r.button("toggle")}),r.button();var o=r.data("button");o.toggle=function(){if(!s)return $.fn.button.Constructor.prototype.toggle.call(this);var t=r.parent('[data-toggle="buttons-radio"]');t.length?(r.siblings("[ng-model]").each(function(t,r){e($(r).attr("ng-model")).assign(n,!1)}),n.$digest(),s.$modelValue||(s.$setViewValue(!s.$modelValue),n.$digest())):n.$apply(function(){s.$setViewValue(!s.$modelValue)})}}}}]).directive("bsButtonsCheckbox",["$parse",function(e){"use strict";return{restrict:"A",require:"?ngModel",compile:function(t,n,r){t.attr("data-toggle","buttons-checkbox").find("a, button").each(function(e,t){$(t).attr("bs-button","")})}}}]).directive("bsButtonsRadio",["$parse",function(e){"use strict";return{restrict:"A",require:"?ngModel",compile:function(t,n,r){return t.attr("data-toggle","buttons-radio"),n.ngModel||t.find("a, button").each(function(e,t){$(t).attr("bs-button","")}),function(t,n,r,i){i&&(n.find("[value]").button().filter('[value="'+t.$eval(r.ngModel)+'"]').addClass("active"),n.on("click.button.data-api",function(e){t.$apply(function(){i.$setViewValue($(e.target).closest("button").attr("value"))})}),t.$watch(r.ngModel,function(e,i){if(e!==i){var s=n.find('[value="'+t.$eval(r.ngModel)+'"]');$.fn.button.Constructor.prototype.toggle.call(s.data("button"))}}))}}}}]),angular.module("$strap.directives").directive("bsButtonSelect",["$parse","$timeout",function(e,t){"use strict";return{restrict:"A",require:"?ngModel",link:function(n,r,i,s){var o=e(i.bsButtonSelect),u=o.assign;s&&(r.text(n.$eval(i.ngModel)),n.$watch(i.ngModel,function(e,t){r.text(e)}));var a,f,l,c;r.on("click",function(){a=o(n),f=s?n.$eval(i.ngModel):r.text(),l=a.indexOf(f),c=l>a.length-2?a[0]:a[l+1],n.$apply(function(){r.text(c),s&&s.$setViewValue(c)})})}}}]),angular.module("$strap.directives").directive("bsDatepicker",["$timeout",function(e){"use strict";var t="ontouchstart"in window&&!window.navigator.userAgent.match(/PhantomJS/i),n={"/":"[\\/]","-":"[-]",".":"[.]",dd:"(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))",d:"(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))",mm:"(?:[0]?[1-9]|[1][012])",m:"(?:[0]?[1-9]|[1][012])",yyyy:"(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])",yy:"(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])"};return{restrict:"A",require:"?ngModel",link:function(r,i,s,o){var u=function(e,t){t||(t={});var r=e,i=n;return angular.forEach(i,function(e,t){r=r.split(t).join(e)}),new RegExp("^"+r+"$",["i"])},a=t?"yyyy/mm/dd":u(s.dateFormat||"mm/dd/yyyy");o&&o.$parsers.unshift(function(e){return!e||a.test(e)?(o.$setValidity("date",!0),e):(o.$setValidity("date",!1),undefined)});var f=i.next('[data-toggle="datepicker"]');f.length&&f.on("click",function(){t?i.trigger("focus"):i.datepicker("show")});if(t&&i.prop("type")==="text")i.prop("type","date"),i.on("change",function(e){r.$apply(function(){o.$setViewValue(i.val())})});else{o&&i.on("changeDate",function(e){r.$apply(function(){o.$setViewValue(i.val())})});var l=i.closest(".popover");l&&l.on("hide",function(e){var t=i.data("datepicker");t&&(t.picker.remove(),i.data("datepicker",null))}),i.attr("data-toggle","datepicker"),i.datepicker({autoclose:!0})}}}}]),angular.module("$strap.directives").directive("bsDropdown",["$parse","$compile",function(e,t){"use strict";var n=Array.prototype.slice,r='<ul class="dropdown-menu" role="menu" aria-labelledby="drop1"><li ng-repeat="item in items" ng-class="{divider: !!item.divider, \'dropdown-submenu\': !!item.submenu && item.submenu.length}"><a ng-hide="!!item.divider" tabindex="-1" ng-href="{{item.href}}" ng-clickLine truncated
|
||||
+12
-4
@@ -3,7 +3,8 @@ var dashboards =
|
||||
title: "Infinite Monkey Dashboard",
|
||||
rows: [
|
||||
{
|
||||
height: "20px",
|
||||
title: "Query Control",
|
||||
height: "30px",
|
||||
panels: [
|
||||
{
|
||||
type : "stringquery",
|
||||
@@ -18,15 +19,21 @@ var dashboards =
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Monkey Monitoring",
|
||||
collapse: false,
|
||||
height: "300px",
|
||||
panels: [
|
||||
{
|
||||
title : "Monkey Shakespeare Lines",
|
||||
type : "histogram",
|
||||
span : 6,
|
||||
show : ['lines','stack'],
|
||||
fill : 1,
|
||||
query : [{ label : "lines", query : "*", color: '#86B32D' } ],
|
||||
show : ['lines','points'],
|
||||
fill : 0,
|
||||
query : [
|
||||
{ label : "Query", query : "*", color: '#86B32D' },
|
||||
{ label : "Hamlet", query : "play_name:Hamlet" },
|
||||
{ label : "Macbeth", query : "play_name:macbeth" },
|
||||
],
|
||||
group : "main"
|
||||
},
|
||||
{
|
||||
@@ -43,6 +50,7 @@ var dashboards =
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Lines of Plays",
|
||||
height: "300px",
|
||||
panels: [
|
||||
{
|
||||
|
||||
+4
-2
@@ -31,12 +31,14 @@
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<span class="brand">Kibana Dashboard</span>
|
||||
<span class="brand"><small>Real time metrics</small></span>
|
||||
<span class="brand">{{dashboards.title}}</span>
|
||||
<span class="brand"><small><small>Kibana Preview</small></small></span>
|
||||
<div class='pull-right' style="padding-top: 5px; padding-left: 10px"><input type="file" id="upload" upload /></div>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn" ng-click="pause()"><i ng-class="{'icon-pause': playing,'icon-play': !playing}"></i></button>
|
||||
<button class="btn" ng-repeat='timespan in time_options' ng-click="set_timespan(timespan)">{{timespan}}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ var labjs = $LAB
|
||||
.script("common/lib/modernizr-2.6.1.min.js")
|
||||
.script("common/lib/underscore.min.js")
|
||||
.script("common/lib/angular.min.js")
|
||||
.script("common/lib/angular-strap.min.js")
|
||||
.script("common/lib/elastic.min.js")
|
||||
.script("common/lib/elastic-angular-client.min.js")
|
||||
.script("common/lib/dateformat.js")
|
||||
|
||||
@@ -47,6 +47,11 @@ angular.module('kibana.controllers', [])
|
||||
// point to your ElasticSearch server
|
||||
var ejs = $scope.ejs = ejsResource(config.elasticsearch);
|
||||
|
||||
$scope.toggle_row = function(row) {
|
||||
$scope.$broadcast('toggle_row',row)
|
||||
row.collapse = row.collapse ? false : true;
|
||||
}
|
||||
|
||||
$scope.set_timespan = function(timespan) {
|
||||
$scope.timespan = timespan;
|
||||
$scope.from = time_ago($scope.timespan);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div ng-controller='histogram'>
|
||||
<div ng-controller='histogram' style="height:{{row.height}}">
|
||||
<h4>{{panel.title}}</h4>
|
||||
<div histogram params="{{panel}}" style="height:{{row.height}}"></div>
|
||||
</div>
|
||||
@@ -87,12 +87,15 @@ angular.module('kibana.histogram', [])
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
|
||||
scope.$watch('data', function() {
|
||||
// If the data or row state changes, re-render
|
||||
scope.$watch(function () {
|
||||
return angular.toJson([scope.data, scope.row])
|
||||
}, function() {
|
||||
if(!(_.isUndefined(scope.data)))
|
||||
render_panel(scope,elem,attrs);
|
||||
});
|
||||
|
||||
// Re-render if the window is
|
||||
// Re-render if the window is resized
|
||||
angular.element(window).bind('resize', function(){
|
||||
render_panel(scope,elem,attrs);
|
||||
});
|
||||
|
||||
@@ -63,9 +63,12 @@ angular.module('kibana.map', [])
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
|
||||
// Re-rending panel if data changes
|
||||
scope.$watch('data', function() {
|
||||
render_panel(scope,elem,attrs);
|
||||
// If the data or row state changes, re-render
|
||||
scope.$watch(function () {
|
||||
return angular.toJson([scope.data, scope.row])
|
||||
}, function() {
|
||||
if(!(_.isUndefined(scope.data)))
|
||||
render_panel(scope,elem,attrs);
|
||||
});
|
||||
|
||||
// Or if the window is resized
|
||||
|
||||
@@ -112,13 +112,15 @@ angular.module('kibana.pie', [])
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
|
||||
// Re-rending the panel if it is resized,
|
||||
scope.$watch('data', function() {
|
||||
// Watch if data or row state changes
|
||||
scope.$watch(function () {
|
||||
return angular.toJson([scope.data, scope.row])
|
||||
}, function() {
|
||||
if(!(_.isUndefined(scope.data)))
|
||||
render_panel(scope,elem,attrs);
|
||||
});
|
||||
|
||||
// Or if the model changes
|
||||
// Or if the window is resized
|
||||
angular.element(window).bind('resize', function(){
|
||||
render_panel(scope,elem,attrs);
|
||||
});
|
||||
@@ -152,7 +154,9 @@ angular.module('kibana.pie', [])
|
||||
};
|
||||
|
||||
// Populate element
|
||||
$.plot(elem, scope.data, pie);
|
||||
if(elem.is(":visible")){
|
||||
$.plot(elem, scope.data, pie);
|
||||
}
|
||||
//elem.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div ng-controller='sort'>
|
||||
<h4 ng-hide="_.isUndefined(panel.title)">{{panel.title}}</h4>
|
||||
<select ng-model="panel.sort[0]" ng-options="f for f in fields"></select>
|
||||
<div ng-controller='sort' style="white-space: nowrap;">
|
||||
<h4 ng-class="{'ng-cloak': !panel.title}">{{panel.title}}</h4>
|
||||
<i ng-click="toggle_sort()" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
|
||||
<select ng-model="panel.sort[0]" ng-options="f for f in fields"></select>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<div ng-controller='stringquery'>
|
||||
<h4 ng-hide="_.isUndefined(panel.title)">{{panel.title}}</h4>
|
||||
<form class="form-search">
|
||||
<h4 ng-class="{'ng-cloak': !panel.title}">{{panel.title}}</h4>
|
||||
<form class="form-search" style="margin-bottom:0px">
|
||||
<input type="text" class="input-medium search-query" ng-model="query" style="width:85%">
|
||||
<button type="submit" class="btn" ng-click="send_query(query)">Search</button>
|
||||
</form>
|
||||
|
||||
+23
-12
@@ -1,17 +1,28 @@
|
||||
|
||||
<div class="row-fluid" style="margin-top:50px">
|
||||
<div class="row-fluid">
|
||||
<div class="span8"><h2>{{dashboards.title}} <small>Last {{timespan}}</small></h2></div>
|
||||
<div class="span4"><div><input type="file" id="upload" upload /></div></div>
|
||||
</div>
|
||||
<div class="row-fluid" ng-repeat="(row_name, row) in dashboards.rows" style="height:{{row.height}}">
|
||||
<div ng-repeat="(name, panel) in row.panels">
|
||||
<div class="span{{panel.span}}" style="position:relative">
|
||||
<div class="span12 alert alert-error panel-error" ng-class="{'ng-cloak': !panel.error}">
|
||||
<a class="close" ng-click="panel.error=false">×</a>
|
||||
<i class="icon-exclamation-sign"></i> <strong>Oops!</strong> {{panel.error}}
|
||||
<div class="row-fluid container" style="margin-top:50px">
|
||||
<!-- Rows -->
|
||||
<div class="row-fluid" ng-repeat="(row_name, row) in dashboards.rows">
|
||||
<div class="span12">
|
||||
<div class="row-fluid" style="padding:0px;margin:0px;height:0px">
|
||||
<div class="span12" style="min-height:5px;vertical-align:bottom">
|
||||
<i class="pointer" ng-class="{'icon-minus': !row.collapse,'icon-plus': row.collapse}" ng-click="toggle_row(row)"></i>
|
||||
<span ng-click="toggle_row(row)" class="pointer"><small>{{row.title}}</small></span>
|
||||
</div>
|
||||
<div class="span12" style="padding: 10px;height={{row.height}}" ng-include="'panels/'+panel.type+'/module.html'">
|
||||
</div>
|
||||
<div class="row-fluid" style="padding-top:10px" ng-hide="row.collapse">
|
||||
<!-- Panels -->
|
||||
<div ng-repeat="(name, panel) in row.panels" class="span{{panel.span}}" style="min-height:{{row.height}}; position:relative">
|
||||
<!-- Error Panel -->
|
||||
<div class="row-fluid">
|
||||
<div class="span12 alert alert-error panel-error" ng-class="{'ng-cloak': !panel.error}">
|
||||
<a class="close" ng-click="panel.error=false">×</a>
|
||||
<i class="icon-exclamation-sign"></i> <strong>Oops!</strong> {{panel.error}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content Panel -->
|
||||
<div class="row-fluid">
|
||||
<div class="span12" style="margin-top:0px" ng-include="'panels/'+panel.type+'/module.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user