Simon Hunt

GUI -- Application View Details Panel -

  - simplified DOM structure
  - refactored code to reduce boilerplate
  - cleaned up CSS

Change-Id: Iff443d7f038f1f770e7b3e9ed383c65b96ba6886
...@@ -58,7 +58,7 @@ public class ApplicationViewMessageHandler extends UiMessageHandler { ...@@ -58,7 +58,7 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
58 private static final String URL = "url"; 58 private static final String URL = "url";
59 private static final String README = "readme"; 59 private static final String README = "readme";
60 private static final String ROLE = "role"; 60 private static final String ROLE = "role";
61 - private static final String REQUIRED_APPS = "_required_apps"; 61 + private static final String REQUIRED_APPS = "required_apps";
62 private static final String FEATURES = "features"; 62 private static final String FEATURES = "features";
63 private static final String PERMISSIONS = "permissions"; 63 private static final String PERMISSIONS = "permissions";
64 64
...@@ -175,13 +175,13 @@ public class ApplicationViewMessageHandler extends UiMessageHandler { ...@@ -175,13 +175,13 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
175 175
176 // process required applications 176 // process required applications
177 ArrayNode requiredApps = arrayNode(); 177 ArrayNode requiredApps = arrayNode();
178 - app.requiredApps().forEach(s -> requiredApps.add(s)); 178 + app.requiredApps().forEach(requiredApps::add);
179 179
180 data.set(REQUIRED_APPS, requiredApps); 180 data.set(REQUIRED_APPS, requiredApps);
181 181
182 // process features 182 // process features
183 ArrayNode features = arrayNode(); 183 ArrayNode features = arrayNode();
184 - app.features().forEach(f -> features.add(f)); 184 + app.features().forEach(features::add);
185 185
186 data.set(FEATURES, features); 186 data.set(FEATURES, features);
187 187
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
15 */ 15 */
16 16
17 /* 17 /*
18 - ONOS GUI -- Host View -- CSS file 18 + ONOS GUI -- Applications View -- CSS file
19 */ 19 */
20 20
21 #ov-app h2 { 21 #ov-app h2 {
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
75 } 75 }
76 76
77 #application-details-panel .container { 77 #application-details-panel .container {
78 - padding: 0 12px; 78 + padding: 0 10px;
79 } 79 }
80 80
81 #application-details-panel .close-btn { 81 #application-details-panel .close-btn {
...@@ -91,60 +91,48 @@ ...@@ -91,60 +91,48 @@
91 fill: #ccc; 91 fill: #ccc;
92 } 92 }
93 93
94 -#application-details-panel .dev-icon { 94 +#application-details-panel .app-icon {
95 display: inline-block; 95 display: inline-block;
96 padding: 0 6px 0 0; 96 padding: 0 6px 0 0;
97 vertical-align: middle; 97 vertical-align: middle;
98 } 98 }
99 -.light .dev-icon svg.embeddedIcon .glyph {
100 - fill: rgb(0, 172, 229);
101 -}
102 -.dark .dev-icon svg.embeddedIcon .glyph {
103 - fill: #486D91;
104 -}
105 99
106 #application-details-panel h2 { 100 #application-details-panel h2 {
107 display: inline-block; 101 display: inline-block;
108 margin: 8px 0; 102 margin: 8px 0;
103 + font-size: 12pt;
109 } 104 }
110 105
111 #application-details-panel .top div.left { 106 #application-details-panel .top div.left {
112 float: left; 107 float: left;
113 - padding: 0 18px 0 0; 108 + padding: 0 12px 0 0;
114 } 109 }
115 #application-details-panel .top div.right { 110 #application-details-panel .top div.right {
116 display: inline-block; 111 display: inline-block;
112 + overflow: hidden;
113 + width: 320px;
117 } 114 }
118 115
119 -#application-details-panel td.label { 116 +#application-details-panel td.label,
117 +#application-details-panel .app-url i {
120 font-style: italic; 118 font-style: italic;
121 padding-right: 12px; 119 padding-right: 12px;
122 /* works for both light and dark themes ... */ 120 /* works for both light and dark themes ... */
123 color: #777; 121 color: #777;
124 } 122 }
125 123
126 -#application-details-panel .actionBtns div { 124 +#application-details-panel td.value-bold {
127 - padding: 12px 6px; 125 + font-weight: bold;
128 } 126 }
129 127
130 -#application-details-panel .top hr { 128 +#application-details-panel .app-url {
131 - width: 95%; 129 + padding: 10px 6px 6px;
132 - margin: 0 auto;
133 } 130 }
134 131
135 -.light #application-details-panel hr { 132 +#application-details-panel hr {
136 - opacity: .5;
137 - border-color: #FFF;
138 -}
139 -.dark #application-details-panel hr {
140 - border-color: #666;
141 -}
142 -
143 -#application-details-panel .middle hr {
144 width: 95%; 133 width: 95%;
145 - margin: 0 auto; 134 + margin: 10px auto;
146 } 135 }
147 -
148 .light #application-details-panel hr { 136 .light #application-details-panel hr {
149 opacity: .5; 137 opacity: .5;
150 border-color: #FFF; 138 border-color: #FFF;
...@@ -155,25 +143,12 @@ ...@@ -155,25 +143,12 @@
155 143
156 #application-details-panel .bottom table { 144 #application-details-panel .bottom table {
157 border-spacing: 0; 145 border-spacing: 0;
146 + width: 100%;
158 } 147 }
159 148
160 -#application-details-panel .bottom th {
161 - letter-spacing: 0.02em;
162 -}
163 -
164 -.light #application-details-panel .bottom th {
165 - background-color: #CCC;
166 - /* default text color */
167 -}
168 -.dark #application-details-panel .bottom th {
169 - background-color: #131313;
170 - color: #ccc;
171 -}
172 -
173 -#application-details-panel .bottom th,
174 #application-details-panel .bottom td { 149 #application-details-panel .bottom td {
175 padding: 6px 12px; 150 padding: 6px 12px;
176 - text-align: center; 151 + text-align: left;
177 } 152 }
178 153
179 .light #application-details-panel .bottom tr:nth-child(odd) { 154 .light #application-details-panel .bottom tr:nth-child(odd) {
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
22 'use strict'; 22 'use strict';
23 23
24 // injected refs 24 // injected refs
25 - var $log, $scope, $loc, fs, ps, wss, is, ns, ks, is; 25 + var $log, $scope, wss, fs, ks, ps, is;
26 26
27 // internal state 27 // internal state
28 var detailsPanel, 28 var detailsPanel,
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
31 top, 31 top,
32 middle, 32 middle,
33 bottom, 33 bottom,
34 - iconDiv,
35 wSize = false; 34 wSize = false;
36 35
37 // constants 36 // constants
...@@ -39,9 +38,7 @@ ...@@ -39,9 +38,7 @@
39 ACTIVE = 'ACTIVE', 38 ACTIVE = 'ACTIVE',
40 appMgmtReq = 'appManagementRequest', 39 appMgmtReq = 'appManagementRequest',
41 topPdg = 50, 40 topPdg = 50,
42 - ctnrPdg = 24, 41 + panelWidth = 500,
43 - tbWidth = 470,
44 - scrollSize = 17,
45 pName = 'application-details-panel', 42 pName = 'application-details-panel',
46 detailsReq = 'appDetailsRequest', 43 detailsReq = 'appDetailsRequest',
47 detailsResp = 'appDetailsResponse', 44 detailsResp = 'appDetailsResponse',
...@@ -57,8 +54,9 @@ ...@@ -57,8 +54,9 @@
57 }, 54 },
58 discouragement = 'Deactivating or uninstalling this component can' + 55 discouragement = 'Deactivating or uninstalling this component can' +
59 ' have serious negative consequences! Do so at your own risk!!', 56 ' have serious negative consequences! Do so at your own risk!!',
60 - propOrder = ['id', 'state', 'category', 'version', 'origin', 'role', 'url'], 57 + propOrder = ['id', 'state', 'category', 'version', 'origin', 'role'],
61 - friendlyProps = ['App ID', 'State', 'Category', 'Version', 'Origin', 'Role', 'URL']; 58 + friendlyProps = ['App ID', 'State', 'Category', 'Version', 'Origin', 'Role'];
59 + // note: url is handled separately
62 60
63 function createDetailsPane() { 61 function createDetailsPane() {
64 detailsPanel = ps.createPanel(pName, { 62 detailsPanel = ps.createPanel(pName, {
...@@ -90,156 +88,111 @@ ...@@ -90,156 +88,111 @@
90 } 88 }
91 89
92 function setUpPanel() { 90 function setUpPanel() {
93 - var container, closeBtn, tblDiv; 91 + var container, closeBtn, div;
92 +
94 detailsPanel.empty(); 93 detailsPanel.empty();
94 + detailsPanel.width(panelWidth);
95 95
96 container = detailsPanel.append('div').classed('container', true); 96 container = detailsPanel.append('div').classed('container', true);
97 97
98 top = container.append('div').classed('top', true); 98 top = container.append('div').classed('top', true);
99 closeBtn = top.append('div').classed('close-btn', true); 99 closeBtn = top.append('div').classed('close-btn', true);
100 addCloseBtn(closeBtn); 100 addCloseBtn(closeBtn);
101 - iconDiv = top.append('div').classed('dev-icon', true);
102 101
103 - tblDiv = top.append('div').classed('top-tables', true); 102 + div = top.append('div').classed('top-content', true);
104 - tblDiv.append('div').classed('left', true).append('table'); 103 +
105 - tblDiv.append('div').classed('right', true).append('table'); 104 + function ndiv(cls, tcls) {
106 - tblDiv.append('div').classed('description', true).append('table'); 105 + var d = div.append('div').classed(cls, true);
106 + if (tcls) {
107 + d.append('table').classed(tcls, true);
108 + }
109 + }
110 +
111 + ndiv('left app-icon');
112 + ndiv('right', 'app-props');
113 + ndiv('app-url');
107 114
108 - top.append('hr'); 115 + container.append('hr');
109 116
110 middle = container.append('div').classed('middle', true); 117 middle = container.append('div').classed('middle', true);
111 - tblDiv = middle.append('div').classed('middle-tables', true); 118 + middle.append('div').classed('app-readme', true);
112 - tblDiv.append('div').classed('readme', true).append('table');
113 119
114 - middle.append('hr'); 120 + container.append('hr');
115 121
122 + // TODO: make bottom container scrollable
116 bottom = container.append('div').classed('bottom', true); 123 bottom = container.append('div').classed('bottom', true);
117 - tblDiv = bottom.append('div').classed('bottom-tables', true).append('table'); 124 +
118 - tblDiv.append('h2').html('Features'); 125 + function nTable(hdr, cls) {
119 - tblDiv.append('div').classed('features', true).append('table'); 126 + bottom.append('h2').html(hdr);
120 - tblDiv.append('h2').html('Required Apps'); 127 + bottom.append('div').classed(cls, true).append('table');
121 - tblDiv.append('div').classed('required-apps', true).append('table'); 128 + }
122 - tblDiv.append('h2').html('Permissions'); 129 +
123 - tblDiv.append('div').classed('permissions', true).append('table'); 130 + nTable('Features', 'features');
131 + nTable('Required Apps', 'required-apps');
132 + nTable('Permissions', 'permissions');
124 } 133 }
125 134
126 function addProp(tbody, index, value) { 135 function addProp(tbody, index, value) {
127 - var tr = tbody.append('tr'); 136 + var tr = tbody.append('tr'),
137 + vcls = index ? 'value' : 'value-bold';
128 138
129 function addCell(cls, txt) { 139 function addCell(cls, txt) {
130 tr.append('td').attr('class', cls).html(txt); 140 tr.append('td').attr('class', cls).html(txt);
131 } 141 }
132 - addCell('label', friendlyProps[index] + ' :');
133 - addCell('value', value);
134 - }
135 142
136 - function addUrl(tbody, index, value) { 143 + addCell('label', friendlyProps[index] + ':');
137 - var href = '<a href="' + value + '" target="_blank">' + value + '</a>'; 144 + addCell(vcls, value);
138 - addProp(tbody, index, href);
139 } 145 }
140 146
141 - function addIcon(tbody, value) { 147 + function urlize(u) {
142 - var tr = tbody.append('tr'); 148 + return '<i>URL:</i> <a href="' + u + '" target="_blank">' + u + '</a>';
143 - var td = tr.append('td');
144 - td.append('img').attr('src', iconUrlPrefix + value + iconUrlSuffix);
145 } 149 }
146 150
147 - function addContent(tbody, value) { 151 + function addIcon(elem, value) {
148 - var tr = tbody.append('tr'); 152 + elem.append('img').attr('src', iconUrlPrefix + value + iconUrlSuffix);
149 - tr.append('td').html(value);
150 } 153 }
151 154
152 - function populateTop(tblDiv, details) { 155 + function populateTop(details) {
153 - var leftTbl = tblDiv.select('.left') 156 + var propsBody = top.select('.app-props').append('tbody'),
154 - .select('table') 157 + url = details.url;
155 - .append('tbody'),
156 - rightTbl = tblDiv.select('.right')
157 - .select('table')
158 - .append('tbody'),
159 - descriptionTbl = tblDiv.select('.description')
160 - .select('table')
161 - .append('tbody');
162 -
163 - top.select('h2').html(details.name);
164 158
165 - // place application icon to the left table 159 + addIcon(top.select('.app-icon'), details.id);
166 - addIcon(leftTbl, details.id);
167 160
168 - // place rest of the fields to the right table
169 propOrder.forEach(function (prop, i) { 161 propOrder.forEach(function (prop, i) {
170 - var fn = prop === 'url' ? addUrl : addProp; 162 + addProp(propsBody, i, details[prop]);
171 - fn(rightTbl, i, details[prop]);
172 }); 163 });
173 164
174 - // place description field to the description table 165 + if (url) {
175 - addContent(descriptionTbl, details.desc); 166 + top.select('.app-url').html(urlize(url));
176 - } 167 + }
177 -
178 - function populateMiddle(tblDiv, details) {
179 - var readmeTbl = tblDiv.select('.readme')
180 - .select('table')
181 - .append('tbody');
182 -
183 - // place readme field to the readme table
184 - addContent(readmeTbl, details.readme);
185 } 168 }
186 169
187 - function populateName(div, name) { 170 + function populateMiddle(details) {
188 - var lab = div.select('.label'), 171 + middle.select('.app-readme').text(details.readme);
189 - val = div.select('.value');
190 - lab.html('Friendly Name:');
191 - val.html(name);
192 } 172 }
193 173
194 - function populateDetails(details) { 174 + function populateBottom(details) {
195 - var nameDiv, topTbs, middleTbs, bottomTbs;
196 - setUpPanel();
197 -
198 - nameDiv = top.select('.name-div');
199 - topTbs = top.select('.top-tables');
200 - middleTbs = middle.select('.middle-tables');
201 - bottomTbs = bottom.select('.bottom-tables');
202 175
203 - populateName(nameDiv, details.name); 176 + function addItems(cls, items) {
204 - populateTop(topTbs, details); 177 + var table = bottom.select('.' + cls).select('table'),
205 - populateMiddle(middleTbs, details); 178 + tbody = table.append('tbody');
206 - populateBottom(bottomTbs, details);
207 -
208 - detailsPanel.height(pHeight);
209 - }
210 179
211 - function addItem(tbody, item) { 180 + items.forEach(function (item) {
212 - var tr = tbody.append('tr').attr('width', tbWidth + 'px'); 181 + tbody.append('tr').append('td').html(item);
213 - tr.append('td').attr('width', tbWidth + 'px') 182 + });
214 - .attr('style', 'text-align:left').html(item); 183 + }
215 - }
216 184
217 - function addItems(table, items) { 185 + addItems('features', details.features);
218 - var tbody = table.append('tbody'); 186 + addItems('required-apps', details.required_apps);
219 - items.forEach(function (item) { 187 + addItems('permissions', details.permissions);
220 - addItem(tbody, item);
221 - });
222 } 188 }
223 189
224 - function populateBottom(tblDiv, details) { 190 + function populateDetails(details) {
225 - var featuresTbl = tblDiv.select('.features') 191 + setUpPanel();
226 - .select('table'), 192 + populateTop(details);
227 - permissionsTbl = tblDiv.select('.permissions') 193 + populateMiddle(details);
228 - .select('table'), 194 + populateBottom(details);
229 - requiredAppsTbl = tblDiv.select('.required-apps') 195 + detailsPanel.height(pHeight);
230 - .select('table');
231 -
232 - addItems(featuresTbl, details.features);
233 - addItems(requiredAppsTbl, details._required_apps);
234 - addItems(permissionsTbl, details.permissions);
235 -
236 - featuresTbl.style({
237 - width: tbWidth + 'px',
238 - overflow: 'auto',
239 - display: 'block'
240 - });
241 -
242 - detailsPanel.width(tbWidth + ctnrPdg);
243 } 196 }
244 197
245 function respDetailsCb(data) { 198 function respDetailsCb(data) {
...@@ -250,15 +203,15 @@ ...@@ -250,15 +203,15 @@
250 angular.module('ovApp', []) 203 angular.module('ovApp', [])
251 .controller('OvAppCtrl', 204 .controller('OvAppCtrl',
252 ['$log', '$scope', '$http', 205 ['$log', '$scope', '$http',
253 - 'FnService', 'TableBuilderService', 'PanelService', 'WebSocketService', 206 + 'WebSocketService', 'FnService', 'KeyService', 'PanelService',
254 - 'IconService', 'UrlFnService', 'KeyService', 'DialogService', 207 + 'IconService', 'UrlFnService', 'DialogService', 'TableBuilderService',
255 208
256 - function (_$log_, _$scope_, $http, _fs_, tbs, _ps_, _wss_, _is_, ufs, _ks_, ds) { 209 + function (_$log_, _$scope_, $http, _wss_, _fs_, _ks_, _ps_, _is_, ufs, ds, tbs) {
257 $log = _$log_; 210 $log = _$log_;
258 $scope = _$scope_; 211 $scope = _$scope_;
259 wss = _wss_; 212 wss = _wss_;
260 - ks = _ks_;
261 fs = _fs_; 213 fs = _fs_;
214 + ks = _ks_;
262 ps = _ps_; 215 ps = _ps_;
263 is = _is_; 216 is = _is_;
264 $scope.panelData = {}; 217 $scope.panelData = {};
...@@ -285,7 +238,6 @@ ...@@ -285,7 +238,6 @@
285 } else { 238 } else {
286 $scope.hidePanel(); 239 $scope.hidePanel();
287 } 240 }
288 - $log.debug('Got a click on:', row);
289 } 241 }
290 242
291 function refreshCtrls() { 243 function refreshCtrls() {
...@@ -318,12 +270,12 @@ ...@@ -318,12 +270,12 @@
318 270
319 // TODO: reexamine where keybindings should be - directive or controller? 271 // TODO: reexamine where keybindings should be - directive or controller?
320 ks.keyBindings({ 272 ks.keyBindings({
321 - esc: [$scope.selectCallback, 'Deselect app'], 273 + esc: [$scope.selectCallback, 'Deselect application'],
322 _helpFormat: ['esc'] 274 _helpFormat: ['esc']
323 }); 275 });
324 ks.gestureNotes([ 276 ks.gestureNotes([
325 - ['click row', 'Select / deselect app'], 277 + ['click row', 'Select / deselect application'],
326 - ['scroll down', 'See more apps'] 278 + ['scroll down', 'See more applications']
327 ]); 279 ]);
328 280
329 function createConfirmationText(action, itemId) { 281 function createConfirmationText(action, itemId) {
......