ONOS-6327: WIP - added code for details panel in Hosts view.
- still need to write back-end code for providing data.
Change-Id: Id79a1c9b52d19f2899ce7c7fdc6d217870b1f8b3
diff --git a/web/gui/src/main/webapp/app/view/host/host.css b/web/gui/src/main/webapp/app/view/host/host.css
index 71e0ff0..377918a 100644
--- a/web/gui/src/main/webapp/app/view/host/host.css
+++ b/web/gui/src/main/webapp/app/view/host/host.css
@@ -24,4 +24,80 @@
#ov-host div.ctrl-btns {
width: 45px;
-}
\ No newline at end of file
+}
+
+/* More in generic panel.css */
+
+#host-details-panel.floatpanel {
+ z-index: 0;
+}
+
+
+#host-details-panel .container {
+ padding: 8px 12px;
+}
+
+#host-details-panel .close-btn {
+ position: absolute;
+ right: 12px;
+ top: 12px;
+ cursor: pointer;
+}
+
+#host-details-panel .host-icon {
+ display: inline-block;
+ padding: 0 6px 0 0;
+ vertical-align: middle;
+}
+
+#host-details-panel h2 {
+ display: inline-block;
+ margin: 8px 0;
+}
+
+
+#host-details-panel h2 input {
+ font-size: 0.90em;
+ width: 106%;
+}
+
+#host-details-panel .top-tables {
+ font-size: 12pt;
+}
+
+#host-details-panel .top div.left {
+ float: left;
+ padding: 0 18px 0 0;
+}
+#host-details-panel .top div.right {
+ display: inline-block;
+}
+
+#host-details-panel td.label {
+ font-weight: bold;
+ text-align: right;
+ padding-right: 6px;
+}
+
+#host-details-panel .actionBtns div {
+ padding: 12px 6px;
+}
+
+#host-details-panel hr {
+ width: 100%;
+ margin: 2px auto;
+}
+
+#host-details-panel .bottom table {
+ border-spacing: 0;
+}
+
+#host-details-panel .bottom th {
+ letter-spacing: 0.02em;
+}
+
+#host-details-panel .bottom th,
+#host-details-panel .bottom td {
+ padding: 6px 12px;
+ text-align: center;
+}
diff --git a/web/gui/src/main/webapp/app/view/host/host.html b/web/gui/src/main/webapp/app/view/host/host.html
index 654f263..e88efa3 100644
--- a/web/gui/src/main/webapp/app/view/host/host.html
+++ b/web/gui/src/main/webapp/app/view/host/host.html
@@ -33,6 +33,8 @@
</tr>
<tr ng-repeat="host in tableData track by $index"
+ ng-click="selectCallback($event, host)"
+ ng-class="{selected: host.id === selId}"
ng-repeat-complete row-id="{{host.id}}">
<td class="table-icon">
<div icon icon-id="{{host._iconid_type}}"></div>
@@ -48,4 +50,6 @@
</div>
+ <host-details-panel></host-details-panel>
+
</div>
diff --git a/web/gui/src/main/webapp/app/view/host/host.js b/web/gui/src/main/webapp/app/view/host/host.js
index 6e4f71b..537bc13 100644
--- a/web/gui/src/main/webapp/app/view/host/host.js
+++ b/web/gui/src/main/webapp/app/view/host/host.js
@@ -21,16 +21,298 @@
(function () {
'use strict';
+ // injected refs
+ var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks;
+
+ // internal state
+ var detailsPanel,
+ pStartY,
+ pHeight,
+ top,
+ bottom,
+ iconDiv,
+ wSize,
+ editingName = false,
+ host;
+
+ // constants
+ var topPdg = 28,
+ ctnrPdg = 24,
+ scrollSize = 17,
+
+ pName = 'host-details-panel',
+ detailsReq = 'hostDetailsRequest',
+ detailsResp = 'hostDetailsResponse',
+ nameChangeReq = 'hostNameChangeRequest',
+ nameChangeResp = 'hostNameChangeResponse';
+
+
+ function closePanel() {
+ if (detailsPanel.isVisible()) {
+ $scope.selId = null;
+ detailsPanel.hide();
+ return true;
+ }
+ return false;
+ }
+
+ function addCloseBtn(div) {
+ is.loadEmbeddedIcon(div, 'close', 20);
+ div.on('click', closePanel);
+ }
+
+ function exitEditMode(nameH2, name) {
+ nameH2.text(name);
+ nameH2.classed('editable clickable', true);
+ editingName = false;
+ ks.enableGlobalKeys(true);
+ }
+
+ function editNameSave() {
+ var nameH2 = top.select('h2'),
+ id = $scope.panelData.id,
+ val,
+ newVal;
+
+ if (editingName) {
+ val = nameH2.select('input').property('value').trim();
+ newVal = val || id;
+
+ exitEditMode(nameH2, newVal);
+ $scope.panelData.name = newVal;
+ wss.sendEvent(nameChangeReq, { id: id, name: val });
+ }
+ }
+
+ function editNameCancel() {
+ if (editingName) {
+ exitEditMode(top.select('h2'), $scope.panelData.name);
+ return true;
+ }
+ return false;
+ }
+
+ function editName() {
+ var nameH2 = top.select('h2'),
+ tf, el;
+
+ if (!editingName) {
+ nameH2.classed('editable clickable', false);
+ nameH2.text('');
+ tf = nameH2.append('input').classed('name-input', true)
+ .attr('type', 'text')
+ .attr('value', $scope.panelData.name);
+ el = tf[0][0];
+ el.focus();
+ el.select();
+ editingName = true;
+ ks.enableGlobalKeys(false);
+ }
+ }
+
+ function handleEscape() {
+ return editNameCancel() || closePanel();
+ }
+
+ function setUpPanel() {
+ var container, closeBtn, tblDiv;
+ detailsPanel.empty();
+
+ container = detailsPanel.append('div').classed('container', true);
+
+ top = container.append('div').classed('top', true);
+ closeBtn = top.append('div').classed('close-btn', true);
+ addCloseBtn(closeBtn);
+ iconDiv = top.append('div').classed('host-icon', true);
+ top.append('h2').classed('editable clickable', true).on('click', editName);
+
+ // tblDiv = top.append('div').classed('top-tables', true);
+ // tblDiv.append('div').classed('left', true).append('table');
+ // tblDiv.append('div').classed('right', true).append('table');
+
+ top.append('hr');
+
+ // bottom = container.append('div').classed('bottom', true);
+ // bottom.append('h2').classed('ports-title', true).text('Ports');
+ // bottom.append('table');
+ }
+
+ function populateTop(details) {
+ is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
+ top.select('h2').text(details.name);
+
+ // TODO: still need to add host properties (one per line)
+ }
+
+ function populateDetails(details) {
+ setUpPanel();
+ populateTop(details);
+ detailsPanel.height(pHeight);
+ }
+
+ function respDetailsCb(data) {
+ $scope.panelData = data.details;
+ host = data.host;
+ $scope.$apply();
+ }
+
+ function respNameCb(data) {
+ if (data.warn) {
+ $log.warn(data.warn, data.id);
+ top.select('h2').text(data.id);
+ }
+ }
+
+ function createDetailsPane() {
+ detailsPanel = ps.createPanel(pName, {
+ width: wSize.width,
+ margin: 0,
+ hideMargin: 0
+ });
+ detailsPanel.el().style({
+ position: 'absolute',
+ top: pStartY + 'px'
+ });
+ $scope.hidePanel = function () { detailsPanel.hide(); };
+ detailsPanel.hide();
+ }
+
+
+ // Defines the Host View controller...
angular.module('ovHost', [])
.controller('OvHostCtrl',
- ['$log', '$scope', 'TableBuilderService',
+ ['$log', '$scope',
+ '$location',
+ 'TableBuilderService',
+ 'FnService', 'MastService', 'PanelService', 'WebSocketService',
+ 'IconService', 'NavService', 'KeyService',
- function ($log, $scope, tbs) {
+ function (_$log_, _$scope_, _$location_,
+ tbs,
+ _fs_, _mast_, _ps_, _wss_,
+ _is_, _ns_, _ks_) {
+
+ var params,
+ handlers = {};
+
+ $log = _$log_;
+ $scope = _$scope_;
+ $loc = _$location_;
+ fs = _fs_;
+ mast = _mast_;
+ ps = _ps_;
+ wss = _wss_;
+ is = _is_;
+ ns = _ns_;
+ ks = _ks_;
+
+ params = $loc.search();
+
+ $scope.panelData = {};
+
+ // details panel handlers
+ handlers[detailsResp] = respDetailsCb;
+ handlers[nameChangeResp] = respNameCb;
+ wss.bindHandlers(handlers);
+
+ // query for if a certain host needs to be highlighted
+ if (params.hasOwnProperty('hostId')) {
+ $scope.selId = params['hostId'];
+ wss.sendEvent(detailsReq, { id: $scope.selId });
+ }
+
+ function selCb($event, row) {
+ if ($scope.selId) {
+ wss.sendEvent(detailsReq, { id: row.id });
+ } else {
+ $scope.hidePanel();
+ }
+ $log.debug('Got a click on:', row);
+ }
+
tbs.buildTable({
scope: $scope,
- tag: 'host'
+ tag: 'host',
+ selCb: selCb
+ });
+
+ $scope.nav = function (path) {
+ if ($scope.selId) {
+ ns.navTo(path, { hostId: $scope.selId });
+ }
+ };
+
+ $scope.$on('$destroy', function () {
+ wss.unbindHandlers(handlers);
});
$log.log('OvHostCtrl has been created');
- }]);
+ }])
+
+ .directive('hostDetailsPanel',
+ ['$rootScope', '$window', '$timeout', 'KeyService',
+ function ($rootScope, $window, $timeout, ks) {
+ return function (scope) {
+ var unbindWatch;
+
+ function heightCalc() {
+ pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height')
+ + mast.mastHeight() + topPdg;
+ wSize = fs.windowSize(pStartY);
+ pHeight = wSize.height;
+ }
+
+ function initPanel() {
+ heightCalc();
+ createDetailsPane();
+ }
+
+ // Safari has a bug where it renders the fixed-layout table wrong
+ // if you ask for the window's size too early
+ if (scope.onos.browser === 'safari') {
+ $timeout(initPanel);
+ } else {
+ initPanel();
+ }
+ // create key bindings to handle panel
+ ks.keyBindings({
+ enter: editNameSave,
+ esc: [handleEscape, 'Close the details panel'],
+ _helpFormat: ['esc']
+ });
+ ks.gestureNotes([
+ ['click', 'Select a row to show device details'],
+ ['scroll down', 'See more devices']
+ ]);
+
+ // if the panelData changes
+ scope.$watch('panelData', function () {
+ if (!fs.isEmptyObject(scope.panelData)) {
+ populateDetails(scope.panelData);
+ detailsPanel.show();
+ }
+ });
+
+ // if the window size changes
+ unbindWatch = $rootScope.$watchCollection(
+ function () {
+ return {
+ h: $window.innerHeight,
+ w: $window.innerWidth
+ };
+ }, function () {
+ if (!fs.isEmptyObject(scope.panelData)) {
+ heightCalc();
+ populateDetails(scope.panelData);
+ }
+ }
+ );
+
+ scope.$on('$destroy', function () {
+ unbindWatch();
+ ks.unbindKeys();
+ ps.destroyPanel(pName);
+ });
+ };
+ }]);
}());