diff --git a/web/gui/src/main/webapp/app/common.css b/web/gui/src/main/webapp/app/common.css index d0d878e7d2..1aef4fbab0 100644 --- a/web/gui/src/main/webapp/app/common.css +++ b/web/gui/src/main/webapp/app/common.css @@ -24,3 +24,63 @@ padding-top: 20px; padding-bottom: 20px; } + +/* Tabular view upper right control buttons */ + +div.ctrl-btns { + display: inline-block; + float: right; + height: 44px; + margin-right: 24px; + margin-top: 7px; +} + + +div.ctrl-btns div { + display: inline-block; + padding: 4px; + cursor: pointer; +} + +/* Inactive */ +.light .ctrl-btns div g.icon rect, +.light .ctrl-btns div:hover g.icon rect { + fill: #eee; +} +.dark .ctrl-btns div g.icon rect, +.dark .ctrl-btns div:hover g.icon rect { + fill: #222; +} + +.light .ctrl-btns div g.icon use { + fill: #ddd; +} +.dark .ctrl-btns div g.icon use { + fill: #333; +} + +/* Active hover */ +.light .ctrl-btns div.active:hover g.icon rect { + fill: #800; +} + +.dark .ctrl-btns div.active:hover g.icon rect { + fill: #CE5650; +} + +/* Active */ +.light .ctrl-btns div.active g.icon use { + fill: #fff; +} +.dark .ctrl-btns div.active g.icon use { + fill: #eee; +} + +.light .ctrl-btns div.active g.icon rect { + fill: #bbb; +} +.dark .ctrl-btns div.active g.icon rect { + fill: #444; +} + + diff --git a/web/gui/src/main/webapp/app/fw/svg/icon.js b/web/gui/src/main/webapp/app/fw/svg/icon.js index 737a8a86fb..2dfd0bb29a 100644 --- a/web/gui/src/main/webapp/app/fw/svg/icon.js +++ b/web/gui/src/main/webapp/app/fw/svg/icon.js @@ -37,6 +37,8 @@ play: 'play', stop: 'stop', + crown: 'crown', + upArrow: 'triangleUp', downArrow: 'triangleDown', @@ -191,7 +193,7 @@ return g; } - function createSortIcon() { + function sortIcons() { function sortAsc(div) { div.style('display', 'inline-block'); loadEmbeddedIcon(div, 'upArrow', 10); @@ -236,7 +238,7 @@ addDeviceIcon: addDeviceIcon, addHostIcon: addHostIcon, iconConfig: function () { return config; }, - createSortIcon: createSortIcon + sortIcons: sortIcons }; }]); diff --git a/web/gui/src/main/webapp/app/fw/widget/table.js b/web/gui/src/main/webapp/app/fw/widget/table.js index 6b94dd28cc..d9fdddadc8 100644 --- a/web/gui/src/main/webapp/app/fw/widget/table.js +++ b/web/gui/src/main/webapp/app/fw/widget/table.js @@ -27,11 +27,15 @@ var tableIconTdSize = 33, pdg = 12, colWidth = 'col-width', - tableIcon = 'table-icon'; + tableIcon = 'table-icon', + asc = 'asc', + desc = 'desc', + none = 'none'; // internal state var currCol = {}, - prevCol = {}; + prevCol = {}, + sortIconAPI; // Functions for creating a fixed header on a table (Angular Directive) @@ -115,48 +119,49 @@ setTableHeight(th, tb); } - // Functions for sorting table rows by header and choosing appropriate icon + // Functions for sorting table rows by header - function updateSortingIcons(thElem, api) { - var div; + function updateSortDirection(thElem) { + sortIconAPI.sortNone(thElem.select('div')); + currCol.div = thElem.append('div'); currCol.colId = thElem.attr('colId'); if (currCol.colId === prevCol.colId) { - (currCol.icon === 'downArrow') ? - currCol.icon = 'upArrow' : - currCol.icon = 'downArrow'; - prevCol.icon = currCol.icon; + (currCol.dir === desc) ? currCol.dir = asc : currCol.dir = desc; + prevCol.dir = currCol.dir; } else { - currCol.icon = 'upArrow'; - prevCol.icon = 'tableColSortNone'; + currCol.dir = asc; + prevCol.dir = none; } + (currCol.dir === asc) ? + sortIconAPI.sortAsc(currCol.div) : sortIconAPI.sortDesc(currCol.div); - div = thElem.select('div'); - api.sortNone(div); - div = thElem.append('div'); - - if (currCol.icon === 'upArrow') { - api.sortAsc(div); - } else { - api.sortDesc(div); - } - - if (prevCol.colId !== undefined && - prevCol.icon === 'tableColSortNone') { - api.sortNone(prevCol.elem.select('div')); + if (prevCol.colId && prevCol.dir === none) { + sortIconAPI.sortNone(prevCol.div); } prevCol.colId = currCol.colId; - prevCol.elem = thElem; + prevCol.div = currCol.div; } function sortRequestParams() { return { sortCol: currCol.colId, - sortDir: (currCol.icon === 'upArrow' ? 'asc' : 'desc') + sortDir: currCol.dir }; } + function resetSortIcons() { + if (currCol.div) { + sortIconAPI.sortNone(currCol.div); + } + if (prevCol.div) { + sortIconAPI.sortNone(prevCol.div); + } + currCol = {}; + prevCol = {}; + } + angular.module('onosWidget') .directive('onosFixedHeader', ['$window', 'FnService', 'MastService', function (_$window_, _fs_, _mast_) { @@ -210,8 +215,8 @@ link: function (scope, element) { $log = _$log_; is = _is_; - var table = d3.select(element[0]), - sortIconAPI = is.createSortIcon(); + var table = d3.select(element[0]); + sortIconAPI = is.sortIcons(); // when a header is clicked, change its icon tag // and get sorting order to send to the server. @@ -219,7 +224,7 @@ var thElem = d3.select(this); if (thElem.attr('sortable') === '') { - updateSortingIcons(thElem, sortIconAPI); + updateSortDirection(thElem); scope.ctrlCallback({ requestParams: sortRequestParams() }); @@ -227,6 +232,16 @@ }); } }; + }]) + + .factory('TableService', ['$log', 'IconService', + + function ($log, is) { + sortIconAPI = is.sortIcons(); + + return { + resetSortIcons: resetSortIcons + }; }]); }()); diff --git a/web/gui/src/main/webapp/app/view/app/app.css b/web/gui/src/main/webapp/app/view/app/app.css index a44b219c6c..c2d61cc5ec 100644 --- a/web/gui/src/main/webapp/app/view/app/app.css +++ b/web/gui/src/main/webapp/app/view/app/app.css @@ -23,59 +23,11 @@ } #ov-app div.ctrl-btns { - display:inline-block; - float: right; - width: 200px; - height: 44px; - margin-right: 24px; - margin-top: 7px; + width: 290px; } -div.ctrl-btns div { - display: inline-block; - padding: 4px; - cursor: pointer; +#ov-app div.ctrl-btns div.separator { + cursor: auto; + width: 24px; + border: none; } - - -/* Inactive */ -.light .ctrl-btns div g.icon rect, -.light .ctrl-btns div:hover g.icon rect { - fill: #eee; -} -.dark .ctrl-btns div g.icon rect, -.dark .ctrl-btns div:hover g.icon rect { - fill: #222; -} - -.light .ctrl-btns div g.icon use { - fill: #ddd; -} -.dark .ctrl-btns div g.icon use { - fill: #333; -} - -/* Active hover */ -.light .ctrl-btns div.active:hover g.icon rect { - fill: #800; -} - -.dark .ctrl-btns div.active:hover g.icon rect { - fill: #CE5650; -} - -/* Active */ -.light .ctrl-btns div.active g.icon use { - fill: #fff; -} -.dark .ctrl-btns div.active g.icon use { - fill: #eee; -} - -.light .ctrl-btns div.active g.icon rect { - fill: #bbb; -} -.dark .ctrl-btns div.active g.icon rect { - fill: #444; -} - diff --git a/web/gui/src/main/webapp/app/view/app/app.html b/web/gui/src/main/webapp/app/view/app/app.html index e1e164795b..01ab4bc519 100644 --- a/web/gui/src/main/webapp/app/view/app/app.html +++ b/web/gui/src/main/webapp/app/view/app/app.html @@ -3,6 +3,10 @@

Applications ({{ctrl.tableData.length}} total)

+
+
diff --git a/web/gui/src/main/webapp/app/view/app/app.js b/web/gui/src/main/webapp/app/view/app/app.js index 236099dcd1..d0b561b356 100644 --- a/web/gui/src/main/webapp/app/view/app/app.js +++ b/web/gui/src/main/webapp/app/view/app/app.js @@ -25,9 +25,9 @@ angular.module('ovApp', []) .controller('OvAppCtrl', - ['$log', '$scope', 'TableBuilderService', 'WebSocketService', + ['$log', '$scope', 'TableService', 'TableBuilderService', 'WebSocketService', - function ($log, $scope, tbs, wss) { + function ($log, $scope, ts, tbs, wss) { function selCb($event, row) { selRow = angular.element($event.currentTarget); selection = row; @@ -45,6 +45,12 @@ document.getElementById('file').dispatchEvent(evt); }); + $scope.refresh = function () { + $log.debug('Refreshing application page'); + ts.resetSortIcons(); + $scope.sortCallback(); + }; + function appAction(action) { if (selection) { $log.debug('Initiating uninstall of', selection); diff --git a/web/gui/src/main/webapp/app/view/cluster/cluster.css b/web/gui/src/main/webapp/app/view/cluster/cluster.css index 502506249e..83ba8d87ca 100644 --- a/web/gui/src/main/webapp/app/view/cluster/cluster.css +++ b/web/gui/src/main/webapp/app/view/cluster/cluster.css @@ -18,5 +18,10 @@ ONOS GUI -- Cluster View -- CSS file */ -#ov-cluster td { +#ov-cluster h2 { + display: inline-block; +} + +#ov-cluster div.ctrl-btns { + width: 45px; } \ No newline at end of file diff --git a/web/gui/src/main/webapp/app/view/cluster/cluster.html b/web/gui/src/main/webapp/app/view/cluster/cluster.html index b08f4aced6..f418601950 100644 --- a/web/gui/src/main/webapp/app/view/cluster/cluster.html +++ b/web/gui/src/main/webapp/app/view/cluster/cluster.html @@ -18,6 +18,11 @@

Cluster Nodes ({{ctrl.tableData.length}} total)

+
+
+

Devices ({{ctrl.tableData.length}} total)

+
+
+

- Flows for Device {{ctrl.devId || "none"}} + Flows for Device {{ctrl.devId || "(No device selected)"}} ({{ctrl.tableData.length}} total)

+
+
+

Hosts ({{ctrl.tableData.length}} total)

+
+
+

Intents ({{ctrl.tableData.length}} total)

+
+
+

Links ({{ctrl.tableData.length}} total)

+
+
+
' + - '' + - '' + - '' + - '' + - ''); + expect(div.html()).toBe( + '' + + '' + ); thElems[1].click(); div = currentTh.find('div'); - expect(div.html()).toBe('' + - '' + - '' + - '' + - '' + - ''); + expect(div.html()).toBe( + '' + + '' + ); thElems[2].click(); div = currentTh.children(); @@ -233,15 +240,13 @@ describe('factory: fw/widget/table.js', function () { // the new element should have the ascending icon currentTh = angular.element(thElems[2]); div = currentTh.children(); - expect(div.html()).toBe('' + - '' + - '' + - '' + - '' + - ''); + expect(div.html()).toBe( + '' + + '' + ); } it('should affirm that onos-fixed-header is working', function () { @@ -264,8 +269,7 @@ describe('factory: fw/widget/table.js', function () { compileTable(); verifyGivenTags('onos-sortable-header'); - // ctrlCallback functionality is tested in device-spec - // only checking that it has been called correctly in the directive + scope.sortCallback = jasmine.createSpy('sortCallback'); thElems = thead.find('th'); @@ -273,4 +277,8 @@ describe('factory: fw/widget/table.js', function () { verifyIcons(thElems); }); + // Note: testing resetSortIcons isn't feasible because due to the nature of + // directive compilation: they are jQuery elements, not d3 elements, + // so the function doesn't work. + });