GUI -- Reworked selection logic to populate fly-in detail pane with action items.
Change-Id: I1a7ba3e04d608b7ec7bc635c21658d2d55b2d5c9
Showing
2 changed files
with
86 additions
and
51 deletions
... | @@ -146,6 +146,26 @@ | ... | @@ -146,6 +146,26 @@ |
146 | } | 146 | } |
147 | 147 | ||
148 | 148 | ||
149 | +#topo-detail .actionBtn { | ||
150 | + margin: 6px 12px; | ||
151 | + padding: 2px 6px; | ||
152 | + font-size: 9pt; | ||
153 | + cursor: pointer; | ||
154 | + width: 50%; | ||
155 | + text-align: center; | ||
156 | + | ||
157 | + /* theme specific... */ | ||
158 | + border: 1px solid #ddf; | ||
159 | + color: #99f; | ||
160 | +} | ||
161 | + | ||
162 | +#topo-detail .actionBtn:hover { | ||
163 | + /* theme specific... */ | ||
164 | + border: 1px solid #ddf; | ||
165 | + background: #eef; | ||
166 | + color: #77d; | ||
167 | +} | ||
168 | + | ||
149 | 169 | ||
150 | #topo-detail hr { | 170 | #topo-detail hr { |
151 | height: 1px; | 171 | height: 1px; | ... | ... |
... | @@ -129,11 +129,7 @@ | ... | @@ -129,11 +129,7 @@ |
129 | P: togglePorts, | 129 | P: togglePorts, |
130 | U: unpin, | 130 | U: unpin, |
131 | R: resetZoomPan, | 131 | R: resetZoomPan, |
132 | - esc: deselectAll, | 132 | + esc: deselectAll |
133 | - | ||
134 | - W: requestTraffic, // bag of selections | ||
135 | - X: cancelTraffic, | ||
136 | - Z: requestPath // host-to-host intent (and monitor) | ||
137 | }; | 133 | }; |
138 | 134 | ||
139 | // state variables | 135 | // state variables |
... | @@ -355,14 +351,17 @@ | ... | @@ -355,14 +351,17 @@ |
355 | } | 351 | } |
356 | 352 | ||
357 | var eventDispatch = { | 353 | var eventDispatch = { |
354 | + addInstance: stillToImplement, | ||
358 | addDevice: addDevice, | 355 | addDevice: addDevice, |
359 | addLink: addLink, | 356 | addLink: addLink, |
360 | addHost: addHost, | 357 | addHost: addHost, |
361 | 358 | ||
359 | + updateInstance: stillToImplement, | ||
362 | updateDevice: updateDevice, | 360 | updateDevice: updateDevice, |
363 | updateLink: updateLink, | 361 | updateLink: updateLink, |
364 | updateHost: updateHost, | 362 | updateHost: updateHost, |
365 | 363 | ||
364 | + removeInstance: stillToImplement, | ||
366 | removeDevice: stillToImplement, | 365 | removeDevice: stillToImplement, |
367 | removeLink: removeLink, | 366 | removeLink: removeLink, |
368 | removeHost: removeHost, | 367 | removeHost: removeHost, |
... | @@ -482,11 +481,11 @@ | ... | @@ -482,11 +481,11 @@ |
482 | function showDetails(data) { | 481 | function showDetails(data) { |
483 | fnTrace('showDetails', data.payload.id); | 482 | fnTrace('showDetails', data.payload.id); |
484 | populateDetails(data.payload); | 483 | populateDetails(data.payload); |
485 | - // TODO: Add single-select actions ... | ||
486 | detailPane.show(); | 484 | detailPane.show(); |
487 | } | 485 | } |
488 | 486 | ||
489 | function showPath(data) { | 487 | function showPath(data) { |
488 | + // TODO: review - making sure we are handling the payload correctly. | ||
490 | fnTrace('showPath', data.payload.id); | 489 | fnTrace('showPath', data.payload.id); |
491 | var links = data.payload.links, | 490 | var links = data.payload.links, |
492 | s = [ data.event + "\n" + links.length ]; | 491 | s = [ data.event + "\n" + links.length ]; |
... | @@ -501,11 +500,11 @@ | ... | @@ -501,11 +500,11 @@ |
501 | link.el.classed('showPath', true); | 500 | link.el.classed('showPath', true); |
502 | } | 501 | } |
503 | }); | 502 | }); |
504 | - | ||
505 | - // TODO: add selection-highlite lines to links | ||
506 | } | 503 | } |
507 | 504 | ||
508 | function showTraffic(data) { | 505 | function showTraffic(data) { |
506 | + // TODO: review - making sure we are handling the payload correctly. | ||
507 | + // TODO: handle 'class' of link: primary, secondary, animated... | ||
509 | fnTrace('showTraffic', data.payload.id); | 508 | fnTrace('showTraffic', data.payload.id); |
510 | var paths = data.payload.paths; | 509 | var paths = data.payload.paths; |
511 | 510 | ||
... | @@ -524,7 +523,6 @@ | ... | @@ -524,7 +523,6 @@ |
524 | } | 523 | } |
525 | }); | 524 | }); |
526 | }); | 525 | }); |
527 | - //network.view.alert("showTraffic() -- TODO") | ||
528 | } | 526 | } |
529 | 527 | ||
530 | // ............................... | 528 | // ............................... |
... | @@ -571,42 +569,8 @@ | ... | @@ -571,42 +569,8 @@ |
571 | return true; | 569 | return true; |
572 | } | 570 | } |
573 | 571 | ||
574 | - function requestTraffic(hoverNode) { | ||
575 | - if (nSel() > 0 || hoverNode) { | ||
576 | - var nodes = hoverNode ? selectOrder.concat(hoverNode.id) : selectOrder; | ||
577 | - sendMessage('requestTraffic', { | ||
578 | - ids: nodes | ||
579 | - }); | ||
580 | - } else { | ||
581 | - userFeedback('Request-Traffic requires one or\n' + | ||
582 | - 'more items to be selected.'); | ||
583 | - } | ||
584 | - } | ||
585 | - | ||
586 | - function requestPath() { | ||
587 | - if (nSel() === 2 && allSelectionsClass('host')) { | ||
588 | - sendMessage('requestPath', { | ||
589 | - one: getSelId(0), | ||
590 | - two: getSelId(1) | ||
591 | - }); | ||
592 | - } else { | ||
593 | - userFeedback('Request-Path requires two\n' + | ||
594 | - 'hosts to be selected.'); | ||
595 | - } | ||
596 | - } | ||
597 | - | ||
598 | - function cancelTraffic(hoverNode) { | ||
599 | - if (hoverNode && selectOrder.length) { | ||
600 | - requestTraffic(); | ||
601 | - } else { | ||
602 | - // FIXME: from where do we get the intent id(s) to send to the server? | ||
603 | - sendMessage('cancelTraffic', { | ||
604 | - ids: ["need_the_intent_id"] | ||
605 | - }); | ||
606 | - } | ||
607 | - } | ||
608 | - | ||
609 | // request details for the selected element | 572 | // request details for the selected element |
573 | + // invoked from selection of a single node. | ||
610 | function requestDetails() { | 574 | function requestDetails() { |
611 | var data = getSel(0).obj, | 575 | var data = getSel(0).obj, |
612 | payload = { | 576 | payload = { |
... | @@ -616,6 +580,31 @@ | ... | @@ -616,6 +580,31 @@ |
616 | sendMessage('requestDetails', payload); | 580 | sendMessage('requestDetails', payload); |
617 | } | 581 | } |
618 | 582 | ||
583 | + function addIntentAction() { | ||
584 | + sendMessage('addHostIntent', { | ||
585 | + one: getSelId(0), | ||
586 | + two: getSelId(1) | ||
587 | + }); | ||
588 | + } | ||
589 | + | ||
590 | + function showTrafficAction() { | ||
591 | + // if nothing is hovered over, and nothing selected, send cancel request | ||
592 | + if (!hovered && nSel() === 0) { | ||
593 | + sendMessage('cancelTraffic', {}); | ||
594 | + return; | ||
595 | + } | ||
596 | + | ||
597 | + // NOTE: hover is only populated if "show traffic on hover" is | ||
598 | + // toggled on, and the item hovered is a host... | ||
599 | + var hoverId = (trafficHover() && hovered && hovered.class === 'host') | ||
600 | + ? hovered.id : ''; | ||
601 | + sendMessage('requestTraffic', { | ||
602 | + ids: selectOrder, | ||
603 | + hover: hoverId | ||
604 | + }); | ||
605 | + } | ||
606 | + | ||
607 | + | ||
619 | // ============================== | 608 | // ============================== |
620 | // force layout modification functions | 609 | // force layout modification functions |
621 | 610 | ||
... | @@ -942,18 +931,16 @@ | ... | @@ -942,18 +931,16 @@ |
942 | } | 931 | } |
943 | 932 | ||
944 | function nodeMouseOver(d) { | 933 | function nodeMouseOver(d) { |
945 | - console.log("Hover:", d); | ||
946 | hovered = d; | 934 | hovered = d; |
947 | - if (d.class === 'host') { | 935 | + if (trafficHover() && d.class === 'host') { |
948 | - //requestTraffic(d); | 936 | + showTrafficAction(); |
949 | } | 937 | } |
950 | } | 938 | } |
951 | 939 | ||
952 | function nodeMouseOut(d) { | 940 | function nodeMouseOut(d) { |
953 | - console.log("Unhover:", d); | ||
954 | hovered = null; | 941 | hovered = null; |
955 | - if (d.class === 'host') { | 942 | + if (trafficHover() && d.class === 'host') { |
956 | - //cancelTraffic(d); | 943 | + showTrafficAction(); |
957 | } | 944 | } |
958 | } | 945 | } |
959 | 946 | ||
... | @@ -1293,6 +1280,7 @@ | ... | @@ -1293,6 +1280,7 @@ |
1293 | var nSel = selectOrder.length; | 1280 | var nSel = selectOrder.length; |
1294 | if (!nSel) { | 1281 | if (!nSel) { |
1295 | detailPane.hide(); | 1282 | detailPane.hide(); |
1283 | + showTrafficAction(); // sends cancelTraffic event | ||
1296 | } else if (nSel === 1) { | 1284 | } else if (nSel === 1) { |
1297 | singleSelect(); | 1285 | singleSelect(); |
1298 | } else { | 1286 | } else { |
... | @@ -1307,7 +1295,6 @@ | ... | @@ -1307,7 +1295,6 @@ |
1307 | 1295 | ||
1308 | function multiSelect() { | 1296 | function multiSelect() { |
1309 | populateMultiSelect(); | 1297 | populateMultiSelect(); |
1310 | - // TODO: Add multi-select actions ... | ||
1311 | } | 1298 | } |
1312 | 1299 | ||
1313 | function addSep(tbody) { | 1300 | function addSep(tbody) { |
... | @@ -1339,6 +1326,8 @@ | ... | @@ -1339,6 +1326,8 @@ |
1339 | selectOrder.forEach(function (d, i) { | 1326 | selectOrder.forEach(function (d, i) { |
1340 | addProp(tbody, i+1, d); | 1327 | addProp(tbody, i+1, d); |
1341 | }); | 1328 | }); |
1329 | + | ||
1330 | + addMultiSelectActions(); | ||
1342 | } | 1331 | } |
1343 | 1332 | ||
1344 | function populateDetails(data) { | 1333 | function populateDetails(data) { |
... | @@ -1358,8 +1347,34 @@ | ... | @@ -1358,8 +1347,34 @@ |
1358 | addProp(tbody, p, data.props[p]); | 1347 | addProp(tbody, p, data.props[p]); |
1359 | } | 1348 | } |
1360 | }); | 1349 | }); |
1350 | + | ||
1351 | + addSingleSelectActions(); | ||
1361 | } | 1352 | } |
1362 | 1353 | ||
1354 | + function addSingleSelectActions() { | ||
1355 | + detailPane.append('hr'); | ||
1356 | + // always want to allow 'show traffic' | ||
1357 | + addAction('Show Traffic', showTrafficAction); | ||
1358 | + } | ||
1359 | + | ||
1360 | + function addMultiSelectActions() { | ||
1361 | + detailPane.append('hr'); | ||
1362 | + // always want to allow 'show traffic' | ||
1363 | + addAction('Show Traffic', showTrafficAction); | ||
1364 | + // if exactly two hosts are selected, also want 'add host intent' | ||
1365 | + if (nSel() === 2 && allSelectionsClass('host')) { | ||
1366 | + addAction('Add Host Intent', addIntentAction); | ||
1367 | + } | ||
1368 | + } | ||
1369 | + | ||
1370 | + function addAction(text, cb) { | ||
1371 | + detailPane.append('div') | ||
1372 | + .classed('actionBtn', true) | ||
1373 | + .text(text) | ||
1374 | + .on('click', cb); | ||
1375 | + } | ||
1376 | + | ||
1377 | + | ||
1363 | function zoomPan(scale, translate) { | 1378 | function zoomPan(scale, translate) { |
1364 | zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")"); | 1379 | zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")"); |
1365 | // keep the map lines constant width while zooming | 1380 | // keep the map lines constant width while zooming | ... | ... |
-
Please register or login to post a comment