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