You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eagle.apache.org by ji...@apache.org on 2016/03/10 04:34:41 UTC
incubator-eagle git commit: EAGLE-193 UI metric dashboard support
sortable For metrics -> Dashboard,
support sort-able to easily adjust charts position.
Repository: incubator-eagle
Updated Branches:
refs/heads/master cab2e42d0 -> 625f7c19a
EAGLE-193 UI metric dashboard support sortable
For metrics -> Dashboard, support sort-able to easily adjust charts position.
https://issues.apache.org/jira/browse/EAGLE-193
Author: @zombiej
Reviewer: @qingwen220
Closes #119
Project: http://git-wip-us.apache.org/repos/asf/incubator-eagle/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-eagle/commit/625f7c19
Tree: http://git-wip-us.apache.org/repos/asf/incubator-eagle/tree/625f7c19
Diff: http://git-wip-us.apache.org/repos/asf/incubator-eagle/diff/625f7c19
Branch: refs/heads/master
Commit: 625f7c19a8827c1cae762b0d4ab7e0e5a9b23d4f
Parents: cab2e42
Author: jiljiang <ji...@ebay.com>
Authored: Thu Mar 10 11:30:37 2016 +0800
Committer: jiljiang <ji...@ebay.com>
Committed: Thu Mar 10 11:34:21 2016 +0800
----------------------------------------------------------------------
eagle-webservice/src/main/webapp/app/index.html | 1 +
.../src/main/webapp/app/public/css/main.css | 56 ++++++-
.../app/public/feature/metrics/controller.js | 45 +++++-
.../public/feature/metrics/page/dashboard.html | 23 ++-
.../src/main/webapp/app/public/js/app.js | 5 +-
.../src/main/webapp/app/public/js/common.js | 11 +-
.../webapp/app/public/js/components/sortable.js | 152 +++++++++++++++++++
7 files changed, 272 insertions(+), 21 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/index.html b/eagle-webservice/src/main/webapp/app/index.html
index cd58877..3311d8c 100644
--- a/eagle-webservice/src/main/webapp/app/index.html
+++ b/eagle-webservice/src/main/webapp/app/index.html
@@ -235,6 +235,7 @@
<script src="public/js/components/file.js" type="text/javascript" charset="utf-8"></script>
<script src="public/js/components/charts/line3d.js" type="text/javascript" charset="utf-8"></script>
<script src="public/js/components/nvd3.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/sortable.js" type="text/javascript" charset="utf-8"></script>
<!-- Controllers -->
<script src="public/js/ctrl/main.js" type="text/javascript" charset="utf-8"></script>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/public/css/main.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/css/main.css b/eagle-webservice/src/main/webapp/app/public/css/main.css
index b32c4fe..b6e848e 100644
--- a/eagle-webservice/src/main/webapp/app/public/css/main.css
+++ b/eagle-webservice/src/main/webapp/app/public/css/main.css
@@ -446,10 +446,40 @@ ul.tree.tree-bordered > li > ul > li > a {
color: #FFFFFF;
}
+/* Row */
+.row.narrow {
+ margin-left: -5px;
+ margin-right: -5px;
+ margin-bottom: -10px;
+}
+
+.row.narrow>.col-xs-1, .row.narrow>.col-sm-1, .row.narrow>.col-md-1, .row.narrow>.col-lg-1, .row.narrow>.col-xs-2, .row.narrow>.col-sm-2, .row.narrow>.col-md-2, .row.narrow>.col-lg-2, .row.narrow>.col-xs-3, .row.narrow>.col-sm-3, .row.narrow>.col-md-3, .row.narrow>.col-lg-3, .row.narrow>.col-xs-4, .row.narrow>.col-sm-4, .row.narrow>.col-md-4, .row.narrow>.col-lg-4, .row.narrow>.col-xs-5, .row.narrow>.col-sm-5, .row.narrow>.col-md-5, .row.narrow>.col-lg-5, .row.narrow>.col-xs-6, .row.narrow>.col-sm-6, .row.narrow>.col-md-6, .row.narrow>.col-lg-6, .row.narrow>.col-xs-7, .row.narrow>.col-sm-7, .row.narrow>.col-md-7, .row.narrow>.col-lg-7, .row.narrow>.col-xs-8, .row.narrow>.col-sm-8, .row.narrow>.col-md-8, .row.narrow>.col-lg-8, .row.narrow>.col-xs-9, .row.narrow>.col-sm-9, .row.narrow>.col-md-9, .row.narrow>.col-lg-9, .row.narrow>.col-xs-10, .row.narrow>.col-sm-10, .row.narrow>.col-md-10, .row.narrow>.col-lg-10, .row.narrow>.col-xs-11, .row.narrow>.col-sm-11, .row.narrow>.col-md-11, .
row.narrow>.col-lg-11, .row.narrow>.col-xs-12, .row.narrow>.col-sm-12, .row.narrow>.col-md-12, .row.narrow>.col-lg-12 {
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.row.narrow > [class^="col-"],
+.row.narrow > [class*=" col-"] {
+ margin-bottom: 10px;
+}
+
/* Chart */
+.sortable-mock-element .nvd3-chart-wrapper {
+ background: #FFFFFF;
+ opacity: 0.8;
+}
+
+.sortable-enter .nvd3-chart-wrapper {
+ border-color: #3c8dbc;
+ pointer-events: none;
+}
+.sortable-enter .nvd3-chart-wrapper .nvtooltip {
+ display: none;
+}
+
.nvd3-chart-wrapper {
position: relative;
- //border: 1px solid rgba(0,0,0,0);
+ border: 1px solid rgba(0,0,0,0.1);
}
.nvd3-chart-wrapper:hover {
//border-color: #F4F4F4;
@@ -457,10 +487,10 @@ ul.tree.tree-bordered > li > ul > li > a {
.nvd3-chart-wrapper .nvd3-chart-config {
position: absolute;
- top: 5px;
- right: 10px;
+ top: 1px;
+ right: 1px;
display: none;
- border-radius: 3px;
+ border-radius: 0;
padding: 0 5px;
background: rgba(0,0,0,0.7);
}
@@ -470,7 +500,7 @@ ul.tree.tree-bordered > li > ul > li > a {
.nvd3-chart-wrapper .nvd3-chart-config a {
color: rgba(255,255,255, 0.9);
- padding: 5px 5px 4px 5px;
+ padding: 5px 2px 4px 2px;
font-size: 16px;
}
.nvd3-chart-wrapper .nvd3-chart-config a:hover {
@@ -478,7 +508,7 @@ ul.tree.tree-bordered > li > ul > li > a {
}
.nvd3-chart-cntr {
- padding-top: 10px;
+ padding: 5px;
}
.nvd3-chart-cntr > h3 {
@@ -486,6 +516,11 @@ ul.tree.tree-bordered > li > ul > li > a {
font-size: 16px;
font-weight: bolder;
margin: 0;
+ padding: 5px 0;
+
+ overflow:hidden;
+ text-overflow:ellipsis;
+
}
.nvd3-chart-cntr > svg.nvd3-svg {
@@ -677,6 +712,13 @@ td.text-ellipsis {
padding: 2px 6px;
}
+.form-control.input-xs {
+ height: 24px;
+ padding: 2px 8px;
+ font-size: 12px;
+ line-height: 100%;
+}
+
.noSelect {
-khtml-user-select: none;
-moz-user-select: none;
@@ -684,4 +726,4 @@ td.text-ellipsis {
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
index 27faf07..95e6638 100644
--- a/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
+++ b/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
@@ -288,7 +288,6 @@
$scope.configPreviewChartMinimumCheck = function() {
$scope.configPreviewChart.min = $scope.configPreviewChart.min === 0 ? undefined : 0;
- window.ccc1 = $scope.getChartConfig($scope.configPreviewChart);
};
$scope.seriesChecked = function(chart, series) {
@@ -322,7 +321,7 @@
$scope.configChart = function(chart) {
$scope.configTargetChart = chart;
- $scope.configPreviewChart = $.extend({}, chart);
+ $scope.configPreviewChart = $.extend({}, chart, {aggregations: (chart.aggregations || []).slice()});
delete $scope.configPreviewChart._config;
$("#chartMDL").modal();
setTimeout(function() {
@@ -341,11 +340,11 @@
UI.deleteConfirm(chart.metric).then(null, null, function(holder) {
common.array.remove(chart, group.charts);
holder.closeFunc();
- $scope.chartRefresh();
+ $scope.chartRefresh(false, true);
});
};
- $scope.chartRefresh = function(forceRefresh) {
+ $scope.chartRefresh = function(forceRefresh, refreshAll) {
setTimeout(function() {
$scope.endTime = app.time.now();
$scope.startTime = $scope.autoRefreshSelect.getStartTime($scope.endTime);
@@ -380,21 +379,55 @@
$http.post(_druidConfig.broker + "/druid/v2", _data, {withCredentials: false}).then(function (response) {
chart._oriData = nvd3.convert.druid([response.data]);
$scope.chartSeriesUpdate(chart);
- if(chart._holder) chart._holder.refresh();
+ if(chart._holder) {
+ if(refreshAll) {
+ chart._holder.refreshAll();
+ } else {
+ chart._holder.refresh();
+ }
+ }
});
} else {
- if(chart._holder) chart._holder.refresh();
+ if(chart._holder) {
+ if(refreshAll) {
+ chart._holder.refreshAll();
+ } else {
+ chart._holder.refresh();
+ }
+ }
}
});
});
+
+ $(window).resize();
}, 0);
};
+ $scope.chartSwitchRefresh = function(source, target) {
+ var _oriSize = source.size;
+ source.size = target.size;
+ target.size = _oriSize;
+
+ if(source._holder) source._holder.refreshAll();
+ if(target._holder) target._holder.refreshAll();
+
+ };
+
_refreshInterval = setInterval(function() {
if(!$scope.dashboardReady) return;
$scope.chartRefresh(true);
}, 1000 * 30);
+ // > Chart UI
+ $scope.configChartSize = function(chart, sizeOffset) {
+ chart.size = (chart.size || 6) + sizeOffset;
+ if(chart.size <= 0) chart.size = 1;
+ if(chart.size > 12) chart.size = 12;
+ setTimeout(function() {
+ $(window).resize();
+ }, 1);
+ };
+
// ====================== Clean Up ======================
$scope.$on('$destroy', function() {
clearInterval(_refreshInterval);
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html b/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
index df85c10..48fe738 100644
--- a/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
+++ b/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
@@ -57,12 +57,14 @@
<div tabs menu="menu" holder="tabHolder" ng-show="dashboard.groups.length">
<pane ng-repeat="group in dashboard.groups" data-data="group" data-title="{{group.name}}">
- <div class="row">
- <div ng-repeat="chart in group.charts track by $index" class="col-md-6">
+ <div sortable class="row narrow" ng-model="group.charts" update-func="chartSwitchRefresh" ng-show="group.charts.length">
+ <div ng-repeat="chart in group.charts track by $index" class="col-md-{{chart.size || 6}}">
<div class="nvd3-chart-wrapper">
- <div nvd3="chart._data" data-holder="chart._holder" data-title="{{chart.metric}}" data-watching="false"
+ <div nvd3="chart._data" data-holder="chart._holder" data-title="{{chart.title || chart.metric}}" data-watching="false"
data-chart="{{chart.chart || 'line'}}" data-config="getChartConfig(chart)" class="nvd3-chart-cntr"></div>
<div class="nvd3-chart-config" ng-if="Auth.isRole('ROLE_ADMIN')">
+ <a class="fa fa-minus" ng-click="configChartSize(chart, -1)"></a>
+ <a class="fa fa-plus" ng-click="configChartSize(chart, 1)"></a>
<a class="fa fa-cog" ng-click="configChart(chart)"></a>
<a class="fa fa-trash" ng-click="deleteChart(group, chart)"></a>
</div>
@@ -72,7 +74,12 @@
<p ng-if="!group.charts.length">
Empty group.
- <span ng-if="Auth.isRole('ROLE_ADMIN')">Click <a ng-click="newChart()">here</a> to add metric.</span>
+ <span ng-if="Auth.isRole('ROLE_ADMIN')">
+ Click
+ <span ng-hide="dataSourceListReady" class="fa fa-refresh fa-spin"></span>
+ <a ng-show="dataSourceListReady" ng-click="newChart()">here</a>
+ to add metric.
+ </span>
</p>
</pane>
</div>
@@ -145,7 +152,7 @@
<div class="row">
<div class="col-md-6">
<div class="nvd3-chart-wrapper">
- <div nvd3="configPreviewChart._data" data-title="{{configPreviewChart.metric}}" data-holder="configPreviewChart._holder"
+ <div nvd3="configPreviewChart._data" data-title="{{configPreviewChart.title || configPreviewChart.metric}}"
data-watching="true" data-chart="{{configPreviewChart.chart || 'line'}}" data-config="getChartConfig(configPreviewChart)" class="nvd3-chart-cntr"></div>
</div>
</div>
@@ -154,7 +161,11 @@
<table class="table">
<tbody>
<tr>
- <th width="100">Chart Type</th>
+ <th width="100">Name</th>
+ <td><input type="text" class="form-control input-xs" ng-model="configPreviewChart.title" placeholder="Default: {{configPreviewChart.metric}}" /></td>
+ </tr>
+ <tr>
+ <th>Chart Type</th>
<td>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default btn-xs" ng-class="{active: (configPreviewChart.chart || 'line') === type.chart}"
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/public/js/app.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/app.js b/eagle-webservice/src/main/webapp/app/public/js/app.js
index dc3f538..80b6aa0 100644
--- a/eagle-webservice/src/main/webapp/app/public/js/app.js
+++ b/eagle-webservice/src/main/webapp/app/public/js/app.js
@@ -226,7 +226,7 @@ var app = {};
// ======================================================================================
// = Router config =
// ======================================================================================
- eagleApp.config(function ($stateProvider, $urlRouterProvider) {
+ eagleApp.config(function ($stateProvider, $urlRouterProvider, $animateProvider) {
// Resolve
function _resolve(config) {
config = config || {};
@@ -367,6 +367,9 @@ var app = {};
.state('page', $.extend({url: "/:feature/:page"}, _featureBase))
.state('pageFilter', $.extend({url: "/:feature/:page/:filter"}, _featureBase))
;
+
+ // Animation
+ $animateProvider.classNameFilter(/^((?!(fa-spin)).)*$/);
});
eagleApp.filter('parseJSON', function () {
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/public/js/common.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/common.js b/eagle-webservice/src/main/webapp/app/public/js/common.js
index 92c4f86..0e238b8 100644
--- a/eagle-webservice/src/main/webapp/app/public/js/common.js
+++ b/eagle-webservice/src/main/webapp/app/public/js/common.js
@@ -247,4 +247,13 @@ common.map.toArray = function(map) {
return $.map(map, function(unit) {
return unit;
});
-};
\ No newline at end of file
+};
+
+// ======================= Math =======================
+common.math = {};
+
+common.math.distance = function(x1,y1,x2,y2) {
+ var a = x1 - x2;
+ var b = y1 - y2;
+ return Math.sqrt(a * a + b * b);
+};
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/625f7c19/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js b/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js
new file mode 100644
index 0000000..be2fb16
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+eagleComponents.directive('sortable', function($rootScope) {
+ 'use strict';
+
+ var _move = false;
+ var _selectElement;
+ var _overElement;
+ var _mockElement;
+ var _offsetX, _offsetY;
+ var _mouseDownPageX, _mouseDownPageY;
+
+ function doMock(element, event) {
+ var _offset = element.offset();
+ if(_mockElement) _mockElement.remove();
+
+ // Create mock element
+ _mockElement = element.clone(false).appendTo("body");
+ _mockElement.addClass("sortable-mock-element");
+ _mockElement.css({
+ display: "block",
+ position: "absolute",
+ "pointer-events": "none",
+ "z-index": 10001,
+ padding: element.css("padding"),
+ margin: element.css("margin")
+ });
+ _mockElement.width(element.width());
+ _mockElement.height(element.height());
+
+ _mockElement.offset(_offset);
+ _offsetX = event.pageX - _offset.left;
+ _offsetY = event.pageY - _offset.top;
+ }
+
+ $(window).on("mousemove", function(event) {
+ if(!_move) return;
+ event.preventDefault();
+
+ _mockElement.offset({
+ left: event.pageX - _offsetX,
+ top: event.pageY - _offsetY
+ });
+ });
+
+ $(window).on("mouseup", function() {
+ if(!_move) {
+ _overElement = null;
+ _selectElement = null;
+ _mockElement = null;
+ return;
+ }
+ _move = false;
+
+ if(_overElement) {
+ _overElement.removeClass("sortable-enter");
+
+ if(_overElement[0] !== _selectElement[0]) {
+ // Process switch
+ var _oriIndex = angular.element(_selectElement).scope().$index;
+ var _tgtIndex = angular.element(_overElement).scope().$index;
+ var _oriHolder = _selectElement.holder;
+ var _tgtHolder = _overElement.holder;
+ var _oriScope = _oriHolder.scope;
+ var _tgtScope = _tgtHolder.scope;
+
+ var _oriUnit = _oriScope.ngModel[_oriIndex];
+ var _tgtUnit = _tgtScope.ngModel[_tgtIndex];
+ _oriScope.ngModel[_oriIndex] = _tgtUnit;
+ _tgtScope.ngModel[_tgtIndex] = _oriUnit;
+
+ // Trigger event
+ _oriHolder.change(_oriUnit, _tgtUnit);
+ if (_oriHolder !== _tgtHolder) _tgtHolder.change(_oriUnit, _tgtUnit);
+
+ $rootScope.$apply();
+ }
+ }
+
+ if(_mockElement) _mockElement.remove();
+
+ _overElement = null;
+ _selectElement = null;
+ _mockElement = null;
+ });
+
+ return {
+ require: 'ngModel',
+ restrict : 'AE',
+ scope: {
+ ngModel: "=",
+ updateFunc: "=?updateFunc"
+ },
+ link: function($scope, $element, $attrs, $ctrl) {
+ var _holder = {
+ scope: $scope,
+ change: function(source, target) {
+ if($scope.updateFunc) $scope.updateFunc(source, target);
+ }
+ };
+
+ $element.on("mousedown", ">", function(event) {
+ _selectElement = $(this);
+ _selectElement.holder = _holder;
+
+ _mouseDownPageX = event.pageX;
+ _mouseDownPageY = event.pageY;
+
+ event.preventDefault();
+ });
+
+ $element.on("mousemove", ">", function(event) {
+ if(_selectElement && !_move && common.math.distance(_mouseDownPageX, _mouseDownPageY, event.pageX, event.pageY) > 10) {
+ _move = true;
+ _overElement = _selectElement;
+ _overElement.addClass("sortable-enter");
+
+ doMock(_selectElement, event);
+ }
+ });
+
+ $element.on("mouseenter", ">", function() {
+ if(!_move) return;
+ _overElement = $(this);
+ _overElement.holder = _holder;
+ _overElement.addClass("sortable-enter");
+ });
+ $element.on("mouseleave", ">", function() {
+ if(!_move) return;
+ $(this).removeClass("sortable-enter");
+ _overElement = null;
+ });
+ },
+ replace: false
+ };
+});
\ No newline at end of file