Bri Prebilic Cole

GUI -- Finished Device Details Panel.

- Cleaned up front and backend
- modified table row select callback to take the click $event (in app and device view)
- panel has device glyph
- closing panel deselects clicked on row

Change-Id: I42c372c74fd9fd417ceff01e424f754ea2559595
......@@ -84,11 +84,8 @@ public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler
DeviceService service = get(DeviceService.class);
MastershipService mastershipService = get(MastershipService.class);
LinkService linkService = get(LinkService.class);
TableRow[] rows = generateTableRows(service,
mastershipService,
linkService);
TableRow[] rows = generateTableRows(service, mastershipService);
RowComparator rc =
new RowComparator(sortCol, RowComparator.direction(sortDir));
Arrays.sort(rows, rc);
......@@ -111,6 +108,7 @@ public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler
data.put(ID, deviceId.toString());
data.put(TYPE, device.type().toString());
data.put(TYPE_IID, getTypeIconId(device));
data.put(MFR, device.manufacturer());
data.put(HW, device.hwVersion());
data.put(SW, device.swVersion());
......@@ -131,13 +129,11 @@ public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler
}
private TableRow[] generateTableRows(DeviceService service,
MastershipService mastershipService,
LinkService linkService) {
MastershipService mastershipService) {
List<TableRow> list = new ArrayList<>();
for (Device dev : service.getDevices()) {
list.add(new DeviceTableRow(service,
mastershipService,
linkService,
dev));
}
return list.toArray(new TableRow[list.size()]);
......@@ -166,6 +162,10 @@ public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler
return port;
}
private static String getTypeIconId(Device d) {
return DEV_ICON_PREFIX + d.type().toString();
}
/**
* TableRow implementation for {@link Device devices}.
*/
......@@ -180,20 +180,8 @@ public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler
private static final String ICON_ID_ONLINE = "deviceOnline";
private static final String ICON_ID_OFFLINE = "deviceOffline";
// TODO: use in details pane
// private String getEgressLinks(Set<Link> links) {
// String formattedString = "";
//
// for (Link l : links) {
// formattedString += l.dst().port().toString() + ", ";
// }
// return formattedString;
// }
// TODO: include "extra" backend information in device details pane
public DeviceTableRow(DeviceService service,
MastershipService ms,
LinkService ls,
Device d) {
boolean available = service.isAvailable(d.id());
String iconId = available ? ICON_ID_ONLINE : ICON_ID_OFFLINE;
......@@ -212,10 +200,6 @@ public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler
add(MASTER_ID, ms.getMasterFor(d.id()).toString());
}
private String getTypeIconId(Device d) {
return DEV_ICON_PREFIX + d.type().toString();
}
@Override
protected String[] columnIds() {
return COL_IDS;
......
......@@ -52,9 +52,9 @@
}
o.scope.sortCallback = sortCb;
function selCb(sel) {
function selCb($event, sel) {
o.scope.sel = (o.scope.sel === sel) ? null : sel;
onSel && onSel(o.scope.sel);
onSel && onSel($event, o.scope.sel);
}
o.scope.selectCallback = selCb;
......
......@@ -31,7 +31,7 @@
</tr>
<tr ng-repeat="app in ctrl.tableData"
ng-click="selectCallback(app)"
ng-click="selectCallback($event, app)"
ng-class="{selected: app === sel}"
ng-repeat-done>
<td class="table-icon">
......
......@@ -21,12 +21,15 @@
(function () {
'use strict';
var selRow;
angular.module('ovApp', [])
.controller('OvAppCtrl',
['$log', '$scope', 'TableBuilderService',
function ($log, $scope, tbs) {
function selCb(row) {
function selCb($event, row) {
selRow = angular.element($event.currentTarget);
// adjust which toolbar buttons are selected
$log.debug('Got a click on:', row);
}
......
......@@ -26,10 +26,10 @@
}
.light #device-details-panel.floatpanel {
background-color: rgb(226, 248, 255);
background-color: rgb(229, 234, 237);
}
.dark #device-details-panel.floatpanel {
background-color: #444;
background-color: #3A4042;
}
#device-details-panel .container {
......@@ -40,13 +40,29 @@
position: absolute;
right: 10px;
top: 0;
cursor: pointer;
}
.close-btn svg.embeddedIcon .icon.appPlus .glyph {
/* works for both dark and light themes */
.light .close-btn svg.embeddedIcon .icon.appPlus .glyph {
fill: #aaa;
}
.dark .close-btn svg.embeddedIcon .icon.appPlus .glyph {
fill: #ccc;
}
#device-details-panel .dev-icon {
display: inline-block;
padding: 0 6px 0 0;
vertical-align: middle;
}
.light .dev-icon svg.embeddedIcon .glyph {
fill: rgb(0, 172, 229);
}
.dark .dev-icon svg.embeddedIcon .glyph {
fill: #486D91;
}
#device-details-panel h2 {
display: inline-block;
margin: 8px 0;
}
......@@ -78,11 +94,11 @@
}
.light #device-details-panel .bottom th {
background-color: #D0E1ED;
background-color: #CCC;
/* default text color */
}
.dark #device-details-panel .bottom th {
background-color: #2b2b2b;
background-color: #131313;
color: #ccc;
}
......@@ -101,3 +117,6 @@
.dark #device-details-panel .bottom tr:nth-child(odd) {
background-color: #333;
}
.dark #device-details-panel .bottom tr:nth-child(even) {
background-color: #555;
}
......
......@@ -27,7 +27,7 @@
</tr>
<tr ng-repeat="dev in ctrl.tableData"
ng-click="selectCallback(dev)"
ng-click="selectCallback($event, dev)"
ng-class="{selected: dev === sel}"
ng-repeat-done>
<td class="table-icon">
......
......@@ -26,11 +26,13 @@
// internal state
var self,
detailsPane,
container, top, bottom, closeBtn;
detailsPanel,
container, top, bottom, iconDiv,
selRow;
// constants
// TODO: consider having a set y height that all tables start at
// to make calculations easier
var h2Pdg = 40,
mastPdg = 8,
tbodyPdg = 5,
......@@ -53,14 +55,26 @@
'Enabled', 'ID', 'Speed', 'Type', 'Egress Links'
];
function addCloseBtn(div) {
is.loadEmbeddedIcon(div, 'appPlus', 30);
div.select('g').attr('transform', 'translate(25, 0) rotate(45)');
div.on('click', function () {
detailsPanel.hide();
selRow.removeClass('selected');
});
}
function setUpPanel() {
detailsPane.empty();
var closeBtn;
detailsPanel.empty();
container = detailsPane.append('div').classed('container', true);
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('dev-icon', true);
top.append('h2');
top.append('table');
......@@ -76,61 +90,72 @@
panelTop = headerHeight + tbodyPdg + mast.mastHeight() + mastPdg,
wSize = fs.windowSize(panelTop);
detailsPane = ps.createPanel(pName, {
detailsPanel = ps.createPanel(pName, {
height: wSize.height,
width: wSize.width / 2,
margin: 0,
hideMargin: 0
});
detailsPane.el().style({
detailsPanel.el().style({
position: 'absolute',
top: panelTop + 'px'
});
setUpPanel();
detailsPane.hide();
detailsPanel.hide();
}
function addCloseBtn(div) {
is.loadEmbeddedIcon(div, 'appPlus', 30);
div.select('g').attr('transform', 'translate(25, 0) rotate(45)');
div.on('click', function () {
detailsPane.hide();
// TODO: deselect the table row when button is clicked
//$scope.sel = null;
});
function addProp(tbody, index, value) {
var tr = tbody.append('tr');
function addCell(cls, txt) {
tr.append('td').attr('class', cls).html(txt);
}
addCell('label', friendlyProps[index] + ' :');
addCell('value', value);
}
function populateTopHalf(tbody, details) {
top.select('h2').text(details['id']);
function populateTop(tbody, details) {
is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
top.select('h2').text(details.id);
propOrder.forEach(function (prop, i) {
addProp(tbody, i, details[prop]);
});
}
function populateBottomHalf(table, ports) {
function addPortRow(tbody, port) {
var tr = tbody.append('tr');
portCols.forEach(function (col) {
if (col === 'type' || col === 'id') {
port[col] = fs.cap(port[col]);
}
tr.append('td').html(port[col]);
});
}
function populateBottom(table, ports) {
var theader = table.append('thead').append('tr'),
tbody = table.append('tbody'),
tbWidth, tbHeight,
scrollSize = 20,
btmPdg = 50;
padding = 55;
friendlyPortCols.forEach(function (header) {
theader.append('th').html(header);
friendlyPortCols.forEach(function (col) {
theader.append('th').html(col);
});
ports.forEach(function (port) {
addPortRow(tbody, port);
});
tbWidth = fs.noPxStyle(tbody, 'width') + scrollSize;
tbHeight = detailsPane.height()
- (fs.noPxStyle(detailsPane.el().select('.top'), 'height')
+ fs.noPxStyle(detailsPane.el().select('hr'), 'height')
+ fs.noPxStyle(detailsPane.el().select('h2'), 'height')
+ btmPdg);
tbHeight = detailsPanel.height()
- (fs.noPxStyle(detailsPanel.el().select('.top'), 'height')
+ fs.noPxStyle(detailsPanel.el().select('hr'), 'height')
+ fs.noPxStyle(detailsPanel.el().select('h2'), 'height')
+ padding);
table.style({
height: tbHeight + 'px',
......@@ -139,45 +164,24 @@
display: 'block'
});
detailsPane.width(tbWidth + cntrPdg);
}
function addProp(tbody, index, value) {
var tr = tbody.append('tr');
function addCell(cls, txt) {
tr.append('td').attr('class', cls).html(txt);
}
addCell('label', friendlyProps[index] + ' :');
addCell('value', value);
}
function addPortRow(tbody, port) {
var tr = tbody.append('tr');
portCols.forEach(function (col) {
if (col === 'type' || col === 'id') {
port[col] = fs.cap(port[col]);
}
tr.append('td').html(port[col]);
});
detailsPanel.width(tbWidth + cntrPdg);
}
function populateDetails(details) {
setUpPanel();
var toptbody = top.select('table').append('tbody'),
btmTable = bottom.select('table'),
var topTb = top.select('table').append('tbody'),
btmTbl = bottom.select('table'),
ports = details.ports;
populateTopHalf(toptbody, details);
populateBottomHalf(btmTable, ports);
populateTop(topTb, details);
populateBottom(btmTbl, ports);
}
function respDetailsCb(data) {
self.panelData = data['details'];
self.panelData = data.details;
populateDetails(self.panelData);
detailsPane.show();
detailsPanel.show();
}
angular.module('ovDevice', [])
......@@ -197,13 +201,13 @@
var handlers = {};
self.panelData = [];
function selCb(row) {
// request the server for more information
// get the id from the row to request details with
function selCb($event, row) {
selRow = angular.element($event.currentTarget);
if ($scope.sel) {
wss.sendEvent(detailsReq, { id: row.id });
} else {
detailsPane.hide();
detailsPanel.hide();
}
$log.debug('Got a click on:', row);
}
......@@ -217,7 +221,6 @@
createDetailsPane();
// bind websocket handlers
handlers[detailsResp] = respDetailsCb;
wss.bindHandlers(handlers);
......