From 6f3bf4fd1b6bf16248527fed06e1655635d1c3dc Mon Sep 17 00:00:00 2001 From: Ben Thomasson Date: Tue, 6 Feb 2018 11:43:04 -0500 Subject: [PATCH] Adds search field and jump-to a device UI. Adds a search field in the network UI and a jump-to level menu. This allows users to quickly find a device on the canvas or jump to a certain mode/zoom-level. Adds animation to smooth out the transition from the current viewport to a viewport centered on the searched for device or zoom-level. * Adds animation FSM and changes the 0 hot key to use it * Adds jump to animation * Adds search bar type ahead * Adds jump animation to search and jump-to menus * Adds keybinding FSM * Updates the dropdown when devices are added/edit/removed * Highlights the searched for host --- awx/network_ui/designs/animation_fsm.yml | 29 +++ awx/network_ui/designs/keybindings.png | Bin 0 -> 85412 bytes awx/network_ui/designs/keybindings.yml | 25 +++ awx/network_ui/static/network_ui/extract.js | 2 +- .../static/network_ui/extract_messages.js | 8 +- awx/ui/client/features/_index.less | 1 - awx/ui/client/features/index.js | 4 +- .../networking/networking.controller.js | 66 ------ awx/ui/client/src/network-ui/animation.fsm.js | 81 +++++++ awx/ui/client/src/network-ui/animations.js | 51 +++++ .../client/src/network-ui/debug.partial.svg | 18 +- awx/ui/client/src/network-ui/hotkeys.fsm.js | 13 +- .../client/src/network-ui/keybindings.fsm.js | 59 +++++ awx/ui/client/src/network-ui/mode.fsm.js | 10 + awx/ui/client/src/network-ui/models.js | 18 +- awx/ui/client/src/network-ui/move.js | 2 + .../network-ui/network-nav}/_index.less | 9 +- .../network-ui/network-nav}/index.js | 8 +- .../network-nav/network.nav.controller.js | 136 ++++++++++++ .../network-nav/network.nav.strings.js} | 0 .../network-nav/network.nav.view.html} | 30 ++- .../client/src/network-ui/network.ui.app.js | 3 + .../src/network-ui/network.ui.controller.js | 210 +++++++++++++----- .../src/network-ui/network_ui.partial.svg | 2 +- .../src/network-ui/quadrants.partial.svg | 42 ++-- awx/ui/client/src/network-ui/style.less | 2 +- awx/ui/client/src/network-ui/view.js | 2 +- 27 files changed, 661 insertions(+), 170 deletions(-) create mode 100644 awx/network_ui/designs/animation_fsm.yml create mode 100644 awx/network_ui/designs/keybindings.png create mode 100644 awx/network_ui/designs/keybindings.yml delete mode 100644 awx/ui/client/features/networking/networking.controller.js create mode 100644 awx/ui/client/src/network-ui/animation.fsm.js create mode 100644 awx/ui/client/src/network-ui/animations.js create mode 100644 awx/ui/client/src/network-ui/keybindings.fsm.js rename awx/ui/client/{features/networking => src/network-ui/network-nav}/_index.less (97%) rename awx/ui/client/{features/networking => src/network-ui/network-nav}/index.js (82%) create mode 100644 awx/ui/client/src/network-ui/network-nav/network.nav.controller.js rename awx/ui/client/{features/networking/networking.strings.js => src/network-ui/network-nav/network.nav.strings.js} (100%) rename awx/ui/client/{features/networking/networking.view.html => src/network-ui/network-nav/network.nav.view.html} (90%) diff --git a/awx/network_ui/designs/animation_fsm.yml b/awx/network_ui/designs/animation_fsm.yml new file mode 100644 index 0000000000..c99b99dd1c --- /dev/null +++ b/awx/network_ui/designs/animation_fsm.yml @@ -0,0 +1,29 @@ +diagram_id: 58 +name: animation_fsm +states: +- id: 4 + label: Cancelled + x: 590 + y: 602 +- id: 3 + label: Completed + x: 225 + y: 604 +- id: 2 + label: Running + x: 418 + y: 362 +- id: 1 + label: Start + x: 454 + y: 158 +transitions: +- from_state: Running + label: onAnimationCancelled + to_state: Cancelled +- from_state: Running + label: onAnimationCompleted + to_state: Completed +- from_state: Start + label: start + to_state: Running diff --git a/awx/network_ui/designs/keybindings.png b/awx/network_ui/designs/keybindings.png new file mode 100644 index 0000000000000000000000000000000000000000..e54b6522111b1fd5e0197e66a66cf1382a238fbd GIT binary patch literal 85412 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 L(5RZ-QCa^0AP$m& literal 0 HcmV?d00001 diff --git a/awx/network_ui/designs/keybindings.yml b/awx/network_ui/designs/keybindings.yml new file mode 100644 index 0000000000..11ce6fa212 --- /dev/null +++ b/awx/network_ui/designs/keybindings.yml @@ -0,0 +1,25 @@ +diagram_id: 60 +name: diagram +states: +- id: 3 + label: Enabled + x: 842 + y: 533 +- id: 2 + label: Start + x: 839 + y: 270 +- id: 6 + label: Disabled + x: 1231 + y: 532 +transitions: +- from_state: Start + label: start + to_state: Enabled +- from_state: Disabled + label: onBindDocument + to_state: Enabled +- from_state: Enabled + label: onUnbindDocument + to_state: Disabled diff --git a/awx/network_ui/static/network_ui/extract.js b/awx/network_ui/static/network_ui/extract.js index 40c14c7462..79edb17f60 100755 --- a/awx/network_ui/static/network_ui/extract.js +++ b/awx/network_ui/static/network_ui/extract.js @@ -26,7 +26,7 @@ var transition = null; var i = 0; while(next_state !== undefined) { state = implementation[next_state]; - transition_iter = Iterator(state.constructor.prototype) + transition_iter = Iterator(state.constructor.prototype); next_transition = transition_iter.next(); while (next_transition !== undefined) { transition = state.constructor.prototype[next_transition]; diff --git a/awx/network_ui/static/network_ui/extract_messages.js b/awx/network_ui/static/network_ui/extract_messages.js index 914e5c4cb0..aee32413de 100755 --- a/awx/network_ui/static/network_ui/extract_messages.js +++ b/awx/network_ui/static/network_ui/extract_messages.js @@ -20,19 +20,19 @@ var next_message = message_iter.next(); var next_field = null; var message = null; var message_instance = null; -var field = null; var fields = null; -var i = 0; +// var field = null; +// var i = 0; while(next_message !== undefined) { message = implementation[next_message]; try { - message_instance = new message() + message_instance = new message(); } catch(err) { next_message = message_iter.next(); continue; } fields = []; - field_iter = Iterator(message_instance) + field_iter = Iterator(message_instance); next_field = field_iter.next(); while (next_field !== undefined) { fields.push(next_field); diff --git a/awx/ui/client/features/_index.less b/awx/ui/client/features/_index.less index 053f270ddd..e2339dc9e4 100644 --- a/awx/ui/client/features/_index.less +++ b/awx/ui/client/features/_index.less @@ -1,3 +1,2 @@ @import 'credentials/_index'; @import 'users/tokens/_index'; -@import 'networking/_index'; diff --git a/awx/ui/client/features/index.js b/awx/ui/client/features/index.js index 5eb1cd96b2..01216e575f 100644 --- a/awx/ui/client/features/index.js +++ b/awx/ui/client/features/index.js @@ -6,7 +6,6 @@ import atFeaturesApplications from '~features/applications'; import atFeaturesCredentials from '~features/credentials'; import atFeaturesTemplates from '~features/templates'; import atFeaturesUsers from '~features/users'; -import atFeaturesNetworking from '~features/networking'; const MODULE_NAME = 'at.features'; @@ -17,8 +16,7 @@ angular.module(MODULE_NAME, [ atFeaturesApplications, atFeaturesCredentials, atFeaturesTemplates, - atFeaturesUsers, - atFeaturesNetworking + atFeaturesUsers ]); export default MODULE_NAME; diff --git a/awx/ui/client/features/networking/networking.controller.js b/awx/ui/client/features/networking/networking.controller.js deleted file mode 100644 index f330d30ac1..0000000000 --- a/awx/ui/client/features/networking/networking.controller.js +++ /dev/null @@ -1,66 +0,0 @@ -function NetworkingController (models, $state, $scope, strings) { - const vm = this || {}; - - const { - inventory - } = models; - - vm.strings = strings; - vm.panelTitle = `${strings.get('state.BREADCRUMB_LABEL')} | ${inventory.name}`; - vm.hostDetail = {}; - - vm.rightPanelIsExpanded = false; - vm.leftPanelIsExpanded = true; - vm.jumpToPanelExpanded = false; - vm.keyPanelExpanded = false; - vm.close = () => { - $state.go('inventories'); - }; - - vm.redirectButtonHandler = (string) => { - $scope.$broadcast('toolbarButtonEvent', string); - }; - - vm.jumpTo = (string) => { - vm.jumpToPanelExpanded = !vm.jumpToPanelExpanded; - vm.keyPanelExpanded = false; - if (string) { - $scope.$broadcast('jumpTo', string); - } - }; - - vm.key = () => { - vm.keyPanelExpanded = !vm.keyPanelExpanded; - vm.jumpToPanelExpanded = false; - }; - - $scope.$on('overall_toolbox_collapsed', () => { - vm.leftPanelIsExpanded = !vm.leftPanelIsExpanded; - }); - - $scope.$on('closeDetailsPanel', () => { - vm.rightPanelIsExpanded = false; - vm.jumpToPanelExpanded = false; - vm.keyPanelExpanded = false; - }); - - $scope.$on('showDetails', (e, data, expand) => { - if (expand) { - vm.rightPanelIsExpanded = true; - } - if (!_.has(data, 'host_id')) { - $scope.item = data; - } else { - $scope.item = data; - } - }); -} - -NetworkingController.$inject = [ - 'resolvedModels', - '$state', - '$scope', - 'NetworkingStrings' -]; - -export default NetworkingController; diff --git a/awx/ui/client/src/network-ui/animation.fsm.js b/awx/ui/client/src/network-ui/animation.fsm.js new file mode 100644 index 0000000000..19fe58c731 --- /dev/null +++ b/awx/ui/client/src/network-ui/animation.fsm.js @@ -0,0 +1,81 @@ +/* Copyright (c) 2018 Benjamin Thomasson */ +/* Copyright (c) 2018 Red Hat, Inc. */ + +var inherits = require('inherits'); +var fsm = require('./fsm.js'); + + +function _Start () { + this.name = 'Start'; +} +inherits(_Start, fsm._State); +var Start = new _Start(); +exports.Start = Start; + +function _Completed () { + this.name = 'Completed'; +} +inherits(_Completed, fsm._State); +var Completed = new _Completed(); +exports.Completed = Completed; + +function _Cancelled () { + this.name = 'Cancelled'; +} +inherits(_Cancelled, fsm._State); +var Cancelled = new _Cancelled(); +exports.Cancelled = Cancelled; + +function _Running () { + this.name = 'Running'; +} +inherits(_Running, fsm._State); +var Running = new _Running(); +exports.Running = Running; + + +_Start.prototype.start = function (controller) { + + controller.changeState(Running); +}; +_Start.prototype.start.transitions = ['Running']; + +_Running.prototype.start = function (controller) { + + controller.scope.interval = setInterval(function () { + controller.scope.frame_number = controller.scope.frame_number_seq(); + if (!controller.scope.active) { + return; + } + if (controller.scope.frame_number > controller.scope.steps) { + controller.scope.fsm.handle_message('AnimationCompleted'); + return; + } + controller.scope.callback(controller.scope); + controller.scope.scope.$apply(); + }, 17); +}; + +_Running.prototype.onAnimationCancelled = function (controller) { + + controller.changeState(Cancelled); + +}; +_Running.prototype.onAnimationCancelled.transitions = ['Cancelled']; + +_Running.prototype.onAnimationCompleted = function (controller) { + + controller.changeState(Completed); + +}; +_Running.prototype.onAnimationCompleted.transitions = ['Completed']; + +_Completed.prototype.start = function (controller) { + controller.scope.active = false; + clearInterval(controller.scope.interval); +}; + +_Cancelled.prototype.start = function (controller) { + controller.scope.active = false; + clearInterval(controller.scope.interval); +}; diff --git a/awx/ui/client/src/network-ui/animations.js b/awx/ui/client/src/network-ui/animations.js new file mode 100644 index 0000000000..b5caee6b3d --- /dev/null +++ b/awx/ui/client/src/network-ui/animations.js @@ -0,0 +1,51 @@ + +/* + * Uses y = cx^2 * cdx to calculate the height of the camera + * Uses scale = 1 / (height + 1) to calculate the scale of the virtual canvas + */ + +function scale_animation (scope) { + + var d = scope.steps; + var c = scope.data.c; + var x = scope.frame_number; + var initial_height = ((1 / scope.data.current_scale) - 1); + var a = -1 * initial_height / (c * d); + var height = 0; + if(scope.data.distance > 0) { + height = (x + a) * (x - d) * c + scope.data.end_height; + } else { + height = (scope.data.end_height - initial_height) * (scope.frame_number / scope.steps) + initial_height + } + //console.log({x: x, + // c: c, + // d: d, + // a: a, + // h: height, + // i: initial_height}); + scope.data.scope.current_scale = 1 / (1 + height); + //console.log(scope.data.scope.current_scale); + //scope.data.scope.current_scale = 1.0; + scope.data.scope.first_channel.send("ScaleChanged", {}); + scope.data.scope.first_channel.send("ScaleChanged", {}); + scope.data.scope.updatePanAndScale(); +} +exports.scale_animation = scale_animation; + +function pan_animation (scope) { + var incr_x = (scope.data.x2 - scope.data.x1) / scope.steps; + var incr_y = (scope.data.y2 - scope.data.y1) / scope.steps; + //console.log({incr_x: incr_x, incr_y: incr_y}); + var v_x = incr_x * scope.frame_number + scope.data.x1; + var v_y = incr_y * scope.frame_number + scope.data.y1; + var p = scope.data.scope.to_pan(v_x, v_y); + //console.log({v_x: v_x, v_y: v_y}); + //console.log({p_x: p.x, p_y: p.y}); + //scope.data.scope.panX = scope.data.scope.graph.width/2 - scope.data.scope.current_scale * p.x / 1.0; + //scope.data.scope.panY = scope.data.scope.graph.height/2 - scope.data.scope.current_scale * p.y / 1.0; + scope.data.scope.panX = p.x + scope.data.scope.graph.width/2; + scope.data.scope.panY = p.y + scope.data.scope.graph.height/2; + scope.data.scope.first_channel.send("PanChanged", {}); + scope.data.scope.updatePanAndScale(); +} +exports.pan_animation = pan_animation; diff --git a/awx/ui/client/src/network-ui/debug.partial.svg b/awx/ui/client/src/network-ui/debug.partial.svg index 60ee70aa85..f9816f49a7 100644 --- a/awx/ui/client/src/network-ui/debug.partial.svg +++ b/awx/ui/client/src/network-ui/debug.partial.svg @@ -1,7 +1,8 @@ - + + view_port.x: {{view_port.x}} view_port.y: {{view_port.y}} view_port.width: {{view_port.width}} @@ -45,5 +46,16 @@ Inventory Toolbox State: {{inventory_toolbox_controller.state.name}} Rack Toolbox State: {{rack_toolbox_controller.state.name}} Site Toolbox State: {{site_toolbox_controller.state.name}} - - + + + + + diff --git a/awx/ui/client/src/network-ui/hotkeys.fsm.js b/awx/ui/client/src/network-ui/hotkeys.fsm.js index 17556054c8..2c310380c6 100644 --- a/awx/ui/client/src/network-ui/hotkeys.fsm.js +++ b/awx/ui/client/src/network-ui/hotkeys.fsm.js @@ -77,8 +77,13 @@ _Enabled.prototype.onKeyDown = function(controller, msg_type, $event) { scope.hide_interfaces = !scope.hide_interfaces; return; } + if($event.keyCode === 27){ + // 27 is the escape key + scope.reset_fsm_state(); + return; + } - if ($event.key === 'r') { + if ($event.key === 'r' && !($event.ctrlKey || $event.metaKey)) { scope.first_channel.send("NewDevice", new messages.NewDevice("router")); return; } @@ -103,11 +108,7 @@ _Enabled.prototype.onKeyDown = function(controller, msg_type, $event) { return; } else if ($event.key === '0') { - scope.panX = 0; - scope.panY = 0; - scope.current_scale = 1.0; - scope.updateScaledXY(); - scope.updatePanAndScale(); + scope.jump_to_animation(0, 0, 1.0); } controller.delegate_channel.send(msg_type, $event); diff --git a/awx/ui/client/src/network-ui/keybindings.fsm.js b/awx/ui/client/src/network-ui/keybindings.fsm.js new file mode 100644 index 0000000000..5087b1c5b5 --- /dev/null +++ b/awx/ui/client/src/network-ui/keybindings.fsm.js @@ -0,0 +1,59 @@ +var inherits = require('inherits'); +var fsm = require('./fsm.js'); + +function _State () { +} +inherits(_State, fsm._State); + + +function _Disabled () { + this.name = 'Disabled'; +} +inherits(_Disabled, _State); +var Disabled = new _Disabled(); +exports.Disabled = Disabled; + +function _Start () { + this.name = 'Start'; +} +inherits(_Start, _State); +var Start = new _Start(); +exports.Start = Start; + +function _Enabled () { + this.name = 'Enabled'; +} +inherits(_Enabled, _State); +var Enabled = new _Enabled(); +exports.Enabled = Enabled; + + + + +_Disabled.prototype.onBindDocument = function (controller) { + + $(document).bind("keydown", controller.scope.onKeyDown); + controller.changeState(Enabled); + +}; +_Disabled.prototype.onBindDocument.transitions = ['Enabled']; + + + +_Start.prototype.start = function (controller) { + + $(document).bind("keydown", controller.scope.onKeyDown); + controller.changeState(Enabled); + +}; +_Start.prototype.start.transitions = ['Enabled']; + + + +_Enabled.prototype.onUnbindDocument = function (controller) { + + $(document).unbind("keydown", controller.scope.onKeyDown); + controller.changeState(Disabled); + +}; +_Enabled.prototype.onUnbindDocument.transitions = ['Disabled']; diff --git a/awx/ui/client/src/network-ui/mode.fsm.js b/awx/ui/client/src/network-ui/mode.fsm.js index bf657dd82d..85bfbab285 100644 --- a/awx/ui/client/src/network-ui/mode.fsm.js +++ b/awx/ui/client/src/network-ui/mode.fsm.js @@ -88,6 +88,8 @@ _Interface.prototype.onMouseWheel = function (controller, msg_type, $event) { }; _Interface.prototype.onMouseWheel.transitions = ['Device']; +_Interface.prototype.onScaleChanged = _Interface.prototype.onMouseWheel; + _Site.prototype.start = function (controller) { controller.scope.current_mode = controller.state.name; controller.scope.rack_toolbox_controller.handle_message('Enable', {}); @@ -115,6 +117,7 @@ _Site.prototype.onMouseWheel = function (controller, msg_type, $event) { }; _Site.prototype.onMouseWheel.transitions = ['MultiSite', 'Rack']; +_Site.prototype.onScaleChanged = _Site.prototype.onMouseWheel; _Process.prototype.onMouseWheel = function (controller, msg_type, $event) { @@ -126,6 +129,8 @@ _Process.prototype.onMouseWheel = function (controller, msg_type, $event) { }; _Process.prototype.onMouseWheel.transitions = ['Device']; +_Process.prototype.onScaleChanged = _Process.prototype.onMouseWheel; + _MultiSite.prototype.start = function (controller) { controller.scope.current_mode = controller.state.name; controller.scope.site_toolbox_controller.handle_message('Enable', {}); @@ -149,6 +154,8 @@ _MultiSite.prototype.onMouseWheel = function (controller, msg_type, $event) { }; _MultiSite.prototype.onMouseWheel.transitions = ['Site']; +_MultiSite.prototype.onScaleChanged = _MultiSite.prototype.onMouseWheel; + _Device.prototype.start = function (controller) { controller.scope.current_mode = controller.state.name; controller.scope.app_toolbox_controller.handle_message('Enable', {}); @@ -175,6 +182,7 @@ _Device.prototype.onMouseWheel = function (controller, msg_type, $event) { }; _Device.prototype.onMouseWheel.transitions = ['Process', 'Interface', 'Rack']; +_Device.prototype.onScaleChanged = _Device.prototype.onMouseWheel; _Rack.prototype.start = function (controller) { controller.scope.current_mode = controller.state.name; @@ -203,3 +211,5 @@ _Rack.prototype.onMouseWheel = function (controller, msg_type, $event) { controller.delegate_channel.send(msg_type, $event); }; _Rack.prototype.onMouseWheel.transitions = ['Site', 'Device']; + +_Rack.prototype.onScaleChanged = _Rack.prototype.onMouseWheel; diff --git a/awx/ui/client/src/network-ui/models.js b/awx/ui/client/src/network-ui/models.js index be2fd5dd54..df25cc5880 100644 --- a/awx/ui/client/src/network-ui/models.js +++ b/awx/ui/client/src/network-ui/models.js @@ -1,8 +1,9 @@ -/* Copyright (c) 2017 Red Hat, Inc. */ +/* Copyright (c) 2017-2018 Red Hat, Inc. */ var fsm = require('./fsm.js'); var button = require('./button.js'); var util = require('./util.js'); var inherits = require('inherits'); +var animation_fsm = require('./animation.fsm.js'); function Device(id, name, x, y, type, host_id) { this.id = id; @@ -908,3 +909,18 @@ function TestResult(id, name, result, date, code_under_test) { this.code_under_test = code_under_test; } exports.TestResult = TestResult; + +function Animation(id, steps, data, scope, tracer, callback) { + + this.id = id; + this.steps = steps; + this.active = true; + this.frame_number_seq = util.natural_numbers(-1); + this.frame_number = 0; + this.data = data; + this.callback = callback; + this.scope = scope; + this.interval = null; + this.fsm = new fsm.FSMController(this, "animation_fsm", animation_fsm.Start, tracer); +} +exports.Animation = Animation; diff --git a/awx/ui/client/src/network-ui/move.js b/awx/ui/client/src/network-ui/move.js index d3070d3d9b..41c721289b 100644 --- a/awx/ui/client/src/network-ui/move.js +++ b/awx/ui/client/src/network-ui/move.js @@ -139,6 +139,7 @@ _Ready.prototype.onNewDevice = function (controller, msg_type, message) { device.host_id)); scope.selected_devices.push(device); device.selected = true; + scope.$emit('addSearchOption', device); controller.changeState(Placing); } }; @@ -451,6 +452,7 @@ _EditLabel.prototype.onKeyDown = function (controller, msg_type, $event) { } else if ($event.keyCode >= 186 && $event.keyCode <=222) { //Punctuation item.name += $event.key; } else if ($event.keyCode === 13) { //Enter + controller.scope.$emit('editSearchOption', item); controller.changeState(Selected2); } if (item.constructor.name === "Device") { diff --git a/awx/ui/client/features/networking/_index.less b/awx/ui/client/src/network-ui/network-nav/_index.less similarity index 97% rename from awx/ui/client/features/networking/_index.less rename to awx/ui/client/src/network-ui/network-nav/_index.less index a7a1dabf3c..a12cddfd9d 100644 --- a/awx/ui/client/features/networking/_index.less +++ b/awx/ui/client/src/network-ui/network-nav/_index.less @@ -190,7 +190,14 @@ } .Networking-searchBarContainer{ - height: 30px + height: 30px; + flex: 1 0 auto; + display: flex; + margin-top:-5px; +} + +.Networking-dropDown{ + left:-2px!important; } .Networking-searchButton{ diff --git a/awx/ui/client/features/networking/index.js b/awx/ui/client/src/network-ui/network-nav/index.js similarity index 82% rename from awx/ui/client/features/networking/index.js rename to awx/ui/client/src/network-ui/network-nav/index.js index 71c20257e9..1a05acfefd 100644 --- a/awx/ui/client/features/networking/index.js +++ b/awx/ui/client/src/network-ui/network-nav/index.js @@ -1,9 +1,9 @@ -import NetworkingController from './networking.controller'; -import NetworkingStrings from './networking.strings'; +import NetworkingController from './network.nav.controller'; +import NetworkingStrings from './network.nav.strings'; const MODULE_NAME = 'at.features.networking'; -const networkingTemplate = require('~features/networking/networking.view.html'); +const networkNavTemplate = require('~network-ui/network-nav/network.nav.view.html'); function NetworkingResolve ($stateParams, resourceData) { const resolve = { @@ -31,7 +31,7 @@ function NetworkingRun ($stateExtender, strings) { }, views: { 'networking@': { - templateUrl: networkingTemplate, + templateUrl: networkNavTemplate, controller: NetworkingController, controllerAs: 'vm' } diff --git a/awx/ui/client/src/network-ui/network-nav/network.nav.controller.js b/awx/ui/client/src/network-ui/network-nav/network.nav.controller.js new file mode 100644 index 0000000000..88bbc5824e --- /dev/null +++ b/awx/ui/client/src/network-ui/network-nav/network.nav.controller.js @@ -0,0 +1,136 @@ +/* eslint-disable */ +function NetworkingController (models, $state, $scope, strings, CreateSelect2) { + const vm = this || {}; + + const { + inventory + } = models; + + vm.strings = strings; + vm.panelTitle = `${strings.get('state.BREADCRUMB_LABEL')} | ${inventory.name}`; + vm.hostDetail = {}; + + vm.rightPanelIsExpanded = false; + vm.leftPanelIsExpanded = true; + vm.jumpToPanelExpanded = false; + vm.keyPanelExpanded = false; + $scope.devices = []; + // $scope.device = null; + vm.close = () => { + $state.go('inventories'); + }; + + vm.redirectButtonHandler = (string) => { + $scope.$broadcast('toolbarButtonEvent', string); + }; + + vm.jumpTo = (thing) => { + vm.jumpToPanelExpanded = !vm.jumpToPanelExpanded; + vm.keyPanelExpanded = false; + if (thing && typeof thing === 'string') { + $scope.$broadcast('jumpTo', thing); + } + if (thing && typeof thing === 'object') { + $scope.$broadcast('search', thing); + } + }; + + vm.key = () => { + vm.keyPanelExpanded = !vm.keyPanelExpanded; + vm.jumpToPanelExpanded = false; + }; + + $scope.$on('overall_toolbox_collapsed', () => { + vm.leftPanelIsExpanded = !vm.leftPanelIsExpanded; + }); + + $scope.$on('closeDetailsPanel', () => { + vm.rightPanelIsExpanded = false; + vm.jumpToPanelExpanded = false; + vm.keyPanelExpanded = false; + }); + + $scope.$on('showDetails', (e, data, expand) => { + if (expand) { + vm.rightPanelIsExpanded = true; + } + if (!_.has(data, 'host_id')) { + $scope.item = data; + } else { + $scope.item = data; + } + }); + + $scope.$on('instatiateSelect', (e, devices) => { + for(var i = 0; i < devices.length; i++){ + let device = devices[i]; + $scope.devices.push({ + value: device.id, + text: device.name, + label: device.name, + id: device.id + }); + } + + $("#networking-search").select2({ + width:'100%', + containerCssClass: 'Form-dropDown', + placeholder: 'SEARCH' + }); + }); + + $scope.$on('addSearchOption', (e, device) => { + $scope.devices.push({ + value: device.id, + text: device.name, + label: device.name, + id: device.id + }); + }); + + $scope.$on('editSearchOption', (e, device) => { + for(var i = 0; i < $scope.devices.length; i++){ + if(device.id === $scope.devices[i].id){ + $scope.devices[i].text = device.name; + $scope.devices[i].label = device.name; + } + } + }); + + $scope.$on('removeSearchOption', (e, device) => { + for (var i = 0; i < $scope.devices.length; i++) { + if ($scope.devices[i].id === device.id) { + $scope.devices.splice(i, 1); + } + } + }); + + $('#networking-search').on('select2:select', (e) => { + $scope.$broadcast('search', e.params.data); + }); + + $('#networking-search').on('select2:open', () => { + $('.select2-dropdown').addClass('Networking-dropDown'); + $scope.$broadcast('unbind'); + }); + + $('#networking-search').on('select2:close', () => { + setTimeout(function() { + $('.select2-container-active').removeClass('select2-container-active'); + $(':focus').blur(); + }, 1); + $scope.$broadcast('bind'); + }); + +} + +NetworkingController.$inject = [ + 'resolvedModels', + '$state', + '$scope', + 'NetworkingStrings', + 'CreateSelect2' +]; + +export default NetworkingController; +/* eslint-disable */ diff --git a/awx/ui/client/features/networking/networking.strings.js b/awx/ui/client/src/network-ui/network-nav/network.nav.strings.js similarity index 100% rename from awx/ui/client/features/networking/networking.strings.js rename to awx/ui/client/src/network-ui/network-nav/network.nav.strings.js diff --git a/awx/ui/client/features/networking/networking.view.html b/awx/ui/client/src/network-ui/network-nav/network.nav.view.html similarity index 90% rename from awx/ui/client/features/networking/networking.view.html rename to awx/ui/client/src/network-ui/network-nav/network.nav.view.html index 6d225101c4..4e90642869 100644 --- a/awx/ui/client/features/networking/networking.view.html +++ b/awx/ui/client/src/network-ui/network-nav/network.nav.view.html @@ -22,14 +22,13 @@
-
- -
- -
-
- -
+
+ + + +
-
foo-bar
+
- +
@@ -144,6 +143,17 @@ + +
+ + + + +
+ +
{{item.type}} DETAILS NOT AVAILABLE
diff --git a/awx/ui/client/src/network-ui/network.ui.app.js b/awx/ui/client/src/network-ui/network.ui.app.js index b1caa34156..6590dee7ed 100644 --- a/awx/ui/client/src/network-ui/network.ui.app.js +++ b/awx/ui/client/src/network-ui/network.ui.app.js @@ -1,5 +1,7 @@ /* Copyright (c) 2017 Red Hat, Inc. */ +import atFeaturesNetworking from './network-nav/index'; + //console.log = function () { }; var angular = require('angular'); var NetworkUIController = require('./network.ui.controller.js'); @@ -31,6 +33,7 @@ var awxNetworkUI = require('./network.ui.directive.js'); var networkUI = angular.module('networkUI', [ 'monospaced.mousewheel', + atFeaturesNetworking ]) .controller('NetworkUIController', NetworkUIController.NetworkUIController) .directive('awxNetCursor', cursor.cursor) diff --git a/awx/ui/client/src/network-ui/network.ui.controller.js b/awx/ui/client/src/network-ui/network.ui.controller.js index b27ce65587..206a8863f9 100644 --- a/awx/ui/client/src/network-ui/network.ui.controller.js +++ b/awx/ui/client/src/network-ui/network.ui.controller.js @@ -19,6 +19,8 @@ var test_fsm = require('./test.fsm.js'); var util = require('./util.js'); var models = require('./models.js'); var messages = require('./messages.js'); +var animations = require('./animations.js'); +var keybindings = require('./keybindings.fsm.js'); var svg_crowbar = require('./svg-crowbar.js'); var ReconnectingWebSocket = require('reconnectingwebsocket'); @@ -106,6 +108,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.message_id_seq = util.natural_numbers(0); $scope.stream_id_seq = util.natural_numbers(0); $scope.test_result_id_seq = util.natural_numbers(0); + $scope.animation_id_seq = util.natural_numbers(0); $scope.overall_toolbox_collapsed = false; $scope.time_pointer = -1; $scope.frame = 0; @@ -124,6 +127,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.test_results = []; $scope.test_errors = []; $scope.streams = []; + $scope.animations = []; $scope.view_port = {'x': 0, 'y': 0, 'width': 0, @@ -131,6 +135,10 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.trace_id_seq = util.natural_numbers(0); $scope.trace_order_seq = util.natural_numbers(0); $scope.trace_id = $scope.trace_id_seq(); + $scope.jump = {from_x: 0, + from_y: 0, + to_x: 0, + to_y: 0}; $scope.send_trace_message = function (message) { if (!$scope.recording) { @@ -150,9 +158,30 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, } }; + $scope.onKeyDown = function ($event) { + if ($scope.recording) { + $scope.send_control_message(new messages.KeyEvent($scope.client_id, + $event.key, + $event.keyCode, + $event.type, + $event.altKey, + $event.shiftKey, + $event.ctrlKey, + $event.metaKey, + $scope.trace_id)); + } + $scope.last_event = $event; + $scope.last_key = $event.key; + $scope.last_key_code = $event.keyCode; + $scope.first_channel.send('KeyDown', $event); + $scope.$apply(); + $event.preventDefault(); + }; + //Define the FSMs $scope.null_controller = new fsm.FSMController($scope, "null_fsm", null_fsm.Start, $scope); $scope.hotkeys_controller = new fsm.FSMController($scope, "hotkeys_fsm", hotkeys.Start, $scope); + $scope.keybindings_controller = new fsm.FSMController($scope, "keybindings_fsm", keybindings.Start, $scope); $scope.view_controller = new fsm.FSMController($scope, "view_fsm", view.Start, $scope); $scope.device_detail_controller = new fsm.FSMController($scope, "device_detail_fsm", device_detail_fsm.Start, $scope); $scope.move_controller = new fsm.FSMController($scope, "move_fsm", move.Start, $scope); @@ -202,21 +231,28 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, .then(function(response) { let hosts = response.data.results; for(var i = 0; i { + console.log([data, status]); ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get host data: ' + status }); }); } @@ -269,9 +305,13 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.mode_controller = new fsm.FSMController($scope, "mode_fsm", mode_fsm.Start, $scope); //Wire up the FSMs - $scope.view_controller.delegate_channel = new fsm.Channel($scope.view_controller, + $scope.keybindings_controller.delegate_channel = new fsm.Channel($scope.keybindings_controller, $scope.hotkeys_controller, $scope); + + $scope.view_controller.delegate_channel = new fsm.Channel($scope.view_controller, + $scope.keybindings_controller, + $scope); $scope.device_detail_controller.delegate_channel = new fsm.Channel($scope.device_detail_controller, $scope.view_controller, $scope); @@ -354,6 +394,24 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, g.setAttribute('transform','translate(' + $scope.panX + ',' + $scope.panY + ') scale(' + $scope.current_scale + ')'); }; + $scope.to_virtual_coordinates = function (b_x, b_y) { + var v_x = (b_x - $scope.panX) / $scope.current_scale; + var v_y = (b_y - $scope.panY) / $scope.current_scale; + return {x: v_x, y: v_y}; + }; + + $scope.to_browser_coordinates = function (v_x, v_y) { + var b_x = (v_x * $scope.current_scale) + $scope.panX; + var b_y = (v_y * $scope.current_scale) + $scope.panY; + return {x: b_x, y: b_y}; + }; + + $scope.to_pan = function (v_x, v_y) { + var p_x = v_x * $scope.current_scale * -1; + var p_y = v_y * $scope.current_scale * -1; + return {x: p_x, y: p_y}; + }; + $scope.clear_selections = function () { var i = 0; @@ -565,41 +623,22 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $event.preventDefault(); }; - $scope.onKeyDown = function ($event) { - if ($scope.recording) { - $scope.send_control_message(new messages.KeyEvent($scope.client_id, - $event.key, - $event.keyCode, - $event.type, - $event.altKey, - $event.shiftKey, - $event.ctrlKey, - $event.metaKey, - $scope.trace_id)); - } - $scope.last_event = $event; - $scope.last_key = $event.key; - $scope.last_key_code = $event.keyCode; - $scope.first_channel.send('KeyDown', $event); - $scope.$apply(); - $event.preventDefault(); - }; - - $document.bind("keydown", $scope.onKeyDown); - // Conext Menu Button Handlers - $scope.removeContextMenu = function(){ let context_menu = $scope.context_menus[0]; context_menu.enabled = false; context_menu.x = -100000; context_menu.y = -100000; - context_menu.buttons.forEach(function(button, index){ + context_menu.buttons.forEach(function(button){ button.enabled = false; button.x = -100000; button.y = -100000; }); - } + }; + + $scope.closeDetailsPanel = function () { + $scope.$emit('closeDetailsPanel'); + }; $scope.onDetailsContextButton = function (panelBoolean) { if (!$scope.disconnected) { @@ -689,6 +728,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, index = $scope.devices.indexOf(devices[i]); if (index !== -1) { $scope.devices.splice(index, 1); + $scope.$emit('removeSearchOption', devices[i]); $scope.send_control_message(new messages.DeviceDestroy($scope.client_id, devices[i].id, devices[i].x, @@ -790,29 +830,85 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope[`on${functionName}Button`](); }); - $scope.$on('jumpTo', function(e, zoomLevel){ + $scope.$on('unbind', function(){ + $scope.first_channel.send('UnbindDocument', {}); + }); + + $scope.$on('bind', function(){ + $scope.first_channel.send('BindDocument', {}); + }); + + $scope.jump_to_animation = function(jump_to_x, jump_to_y, jump_to_scale) { + $scope.cancel_animations(); + var v_center = $scope.to_virtual_coordinates($scope.graph.width/2, $scope.graph.height/2); + //console.log({v_center: v_center}); + $scope.jump.from_x = v_center.x; + $scope.jump.from_y = v_center.y; + $scope.jump.to_x = jump_to_x; + $scope.jump.to_y = jump_to_y; + var distance = util.distance(v_center.x, v_center.y, jump_to_x, jump_to_y); + //console.log({distance: distance}); + var num_frames = 30 * Math.floor((1 + 4 * distance / (distance + 3000))); + //console.log({num_frames: num_frames}); + var scale_animation = new models.Animation($scope.animation_id_seq(), + num_frames, + { + c: -0.1, + distance: distance, + end_height: (1.0/jump_to_scale) - 1, + current_scale: $scope.current_scale, + scope: $scope + }, + $scope, + $scope, + animations.scale_animation); + $scope.animations.push(scale_animation); + var pan_animation = new models.Animation($scope.animation_id_seq(), + num_frames, + { + x2: jump_to_x, + y2: jump_to_y, + x1: v_center.x, + y1: v_center.y, + scope: $scope + }, + $scope, + $scope, + animations.pan_animation); + $scope.animations.push(pan_animation); + }; + + $scope.$on('search', function(e, device){ + + var num_frames = 30; + var searched; + for(var i = 0; i < $scope.devices.length; i++){ + if(Number(device.id) === $scope.devices[i].id){ + searched = $scope.devices[i]; + } + } + searched.selected = true; + $scope.selected_devices.push(searched); + //console.log(searched); + $scope.jump_to_animation(searched.x, searched.y, 1.0); + }); + + $scope.$on('jumpTo', function(e, zoomLevel) { + var v_center = $scope.to_virtual_coordinates($scope.graph.width/2, $scope.graph.height/2); switch (zoomLevel){ case 'site': - $scope.current_scale = 0.051; + $scope.jump_to_animation(v_center.x, v_center.y, 0.051); break; case 'rack': - $scope.current_scale = 0.11; + $scope.jump_to_animation(v_center.x, v_center.y, 0.11); break; case 'inventory': - $scope.current_scale = 0.51; + $scope.jump_to_animation(v_center.x, v_center.y, 0.51); break; case 'process': - $scope.current_scale = 1.1; + $scope.jump_to_animation(v_center.x, v_center.y, 5.1); break; } - // var new_panX = controller.scope.{{somethinghere}} - new_scale * ((controller.scope.mouseX - controller.scope.panX) / controller.scope.current_scale); - // var new_panY = controller.scope.mouseY - new_scale * ((controller.scope.mouseY - controller.scope.panY) / controller.scope.current_scale); - // // controller.scope.updateScaledXY(); - // // controller.scope.current_scale = new_scale; - // controller.scope.panX = new_panX; - // controller.scope.panY = new_panY; - $scope.updateScaledXY(); - $scope.updatePanAndScale(); }); $scope.onDeployButton = function (button) { @@ -1691,6 +1787,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, } $scope.updateInterfaceDots(); + $scope.$emit('instatiateSelect', $scope.devices); }; $scope.updateInterfaceDots = function() { @@ -1774,7 +1871,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.$on('$destroy', function () { console.log("Network UI stopping"); - $document.unbind('keydown', $scope.onKeyDown); + $scope.first_channel.send('UnbindDocument', {}); }); $scope.update_toolbox_heights = function(){ @@ -1877,6 +1974,8 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.null_controller.state.start($scope.null_controller); $scope.hotkeys_controller.state = hotkeys.Start; $scope.hotkeys_controller.state.start($scope.hotkeys_controller); + $scope.keybindings_controller.state = keybindings.Start; + $scope.keybindings_controller.state.start($scope.keybindings_controller); $scope.view_controller.state = view.Start; $scope.view_controller.state.start($scope.view_controller); $scope.device_detail_controller.state = device_detail_fsm.Start; @@ -1927,6 +2026,15 @@ var NetworkUIController = function($scope, $document, $location, $window, $http, $scope.rack_toolbox.items = []; $scope.site_toolbox.items = []; }; + + $scope.cancel_animations = function () { + + var i = 0; + for (i = 0; i < $scope.animations.length; i++) { + this.animations[i].fsm.handle_message('AnimationCancelled'); + } + $scope.animations = []; + }; }; exports.NetworkUIController = NetworkUIController; diff --git a/awx/ui/client/src/network-ui/network_ui.partial.svg b/awx/ui/client/src/network-ui/network_ui.partial.svg index 5c1e03981e..b102480a10 100644 --- a/awx/ui/client/src/network-ui/network_ui.partial.svg +++ b/awx/ui/client/src/network-ui/network_ui.partial.svg @@ -85,7 +85,7 @@
- + diff --git a/awx/ui/client/src/network-ui/quadrants.partial.svg b/awx/ui/client/src/network-ui/quadrants.partial.svg index e5160bf169..142b733d42 100644 --- a/awx/ui/client/src/network-ui/quadrants.partial.svg +++ b/awx/ui/client/src/network-ui/quadrants.partial.svg @@ -1,17 +1,27 @@ - - - + + + + + + + + diff --git a/awx/ui/client/src/network-ui/style.less b/awx/ui/client/src/network-ui/style.less index 9db36896ef..5b4a599df8 100644 --- a/awx/ui/client/src/network-ui/style.less +++ b/awx/ui/client/src/network-ui/style.less @@ -1,5 +1,5 @@ /* Copyright (c) 2017 Red Hat, Inc. */ - +@import 'network-nav/_index'; @font-face { font-family: 'Open Sans'; font-style: normal; diff --git a/awx/ui/client/src/network-ui/view.js b/awx/ui/client/src/network-ui/view.js index 24cfdd7bab..4c39e8754b 100644 --- a/awx/ui/client/src/network-ui/view.js +++ b/awx/ui/client/src/network-ui/view.js @@ -50,7 +50,7 @@ _Ready.prototype.onMouseDown = function (controller) { controller.scope.pressedY = controller.scope.mouseY; controller.scope.lastPanX = controller.scope.panX; controller.scope.lastPanY = controller.scope.panY; - controller.scope.$emit('closeDetailsPanel'); + controller.scope.closeDetailsPanel(); controller.changeState(Pressed); };