From eb39fcfeaf2b5549b1eaeb582ed18be891cbecc6 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Mon, 11 Jun 2018 10:51:09 -0400 Subject: [PATCH] add basic output expand-collapse --- .../features/output/index.controller.js | 90 +++++++++++++++---- awx/ui/client/features/output/index.view.html | 12 +-- .../client/features/output/render.service.js | 38 ++++---- .../client/features/output/slide.service.js | 18 +++- 4 files changed, 113 insertions(+), 45 deletions(-) diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 64d53b4faa..4af5247e5b 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -67,14 +67,6 @@ function onFrames (events) { const capacity = slide.getCapacity(); - if (capacity >= events.length) { - return slide.pushFront(events); - } - - delete render.record; - - render.record = {}; - return slide.popBack(events.length - capacity) .then(() => slide.pushFront(events)) .then(() => { @@ -131,10 +123,6 @@ function last () { }); } -function compile (html) { - return $compile(html)($scope); -} - function follow () { scroll.pause(); // scroll.hide(); @@ -149,6 +137,69 @@ function unfollow () { scroll.resume(); } +function togglePanelExpand () { + vm.isPanelExpanded = !vm.isPanelExpanded; +} + +function toggleMenuExpand () { + if (scroll.isPaused()) return; + + const recordList = Object.keys(render.record).map(key => render.record[key]); + const minLevel = Math.min(...recordList.map(({ level }) => level)); + + const toggled = recordList + .filter(({ level }) => level === minLevel) + .map(({ uuid }) => getToggleElements(uuid)) + .filter(({ icon }) => icon.length > 0) + .map(({ icon, lines }) => setExpanded(icon, lines, !vm.isMenuExpanded)); + + if (toggled.length > 0) { + vm.isMenuExpanded = !vm.isMenuExpanded; + } +} + +function toggleLineExpand (uuid) { + if (scroll.isPaused()) return; + + const { icon, lines } = getToggleElements(uuid); + const isExpanded = icon.hasClass('fa-angle-down'); + + setExpanded(icon, lines, !isExpanded); + + vm.isMenuExpanded = !isExpanded; +} + +function getToggleElements (uuid) { + const record = render.record[uuid]; + const lines = $(`.child-of-${uuid}`); + + const iconSelector = '.at-Stdout-toggle > i'; + const additionalSelector = `#${(record.children || []).join(', #')}`; + + let icon = $(`#${uuid} ${iconSelector}`); + if (additionalSelector) { + icon = icon.add($(additionalSelector).find(iconSelector)); + } + + return { icon, lines }; +} + +function setExpanded (icon, lines, expanded) { + if (expanded) { + icon.removeClass('fa-angle-right'); + icon.addClass('fa-angle-down'); + lines.removeClass('hidden'); + } else { + icon.removeClass('fa-angle-down'); + icon.addClass('fa-angle-right'); + lines.addClass('hidden'); + } +} + +function compile (html) { + return $compile(html)($scope); +} + function showHostDetails (id, uuid) { $state.go('output.host-event.json', { eventId: id, taskUuid: uuid }); } @@ -203,13 +254,11 @@ function OutputIndexController ( vm = this || {}; // Panel + vm.title = $filter('sanitize')(resource.model.get('name')); vm.strings = strings; vm.resource = resource; - vm.title = $filter('sanitize')(resource.model.get('name')); - - vm.expanded = false; - vm.showHostDetails = showHostDetails; - vm.toggleExpanded = () => { vm.expanded = !vm.expanded; }; + vm.isPanelExpanded = false; + vm.togglePanelExpand = togglePanelExpand; // Stdout Navigation vm.menu = { @@ -218,6 +267,11 @@ function OutputIndexController ( up: previous, down: next, }; + vm.isMenuExpanded = true; + vm.toggleMenuExpand = toggleMenuExpand; + vm.toggleLineExpand = toggleLineExpand; + vm.showHostDetails = showHostDetails; + vm.toggleLineEnabled = resource.model.get('type') === 'job'; render.requestAnimationFrame(() => { bufferInit(); @@ -225,7 +279,7 @@ function OutputIndexController ( status.init(resource); slide.init(render, resource.events); - render.init({ compile }); + render.init({ compile, toggles: vm.toggleLineEnabled }); scroll.init({ previous, next }); stream.init({ diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index 441879b21f..fa973ec40d 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -1,25 +1,25 @@
- + - +
{{ vm.title }}
+ expanded="vm.isPanelExpanded">
-
- +
+
diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 3f315a1ac0..88d9cf6f39 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -30,13 +30,13 @@ const re = new RegExp(pattern); const hasAnsi = input => re.test(input); function JobRenderService ($q, $sce, $window) { - this.init = ({ compile }) => { + this.init = ({ compile, toggles }) => { this.parent = null; this.record = {}; this.el = $(ELEMENT_TBODY); this.hooks = { compile }; - this.createToggles = false; + this.createToggles = toggles; }; this.sortByLineNumber = (a, b) => { @@ -164,6 +164,24 @@ function JobRenderService ($q, $sce, $window) { return info; }; + this.deleteRecord = uuid => { + delete this.record[uuid]; + }; + + this.getParentEvents = (uuid, list) => { + list = list || []; + + if (this.record[uuid]) { + list.push(uuid); + + if (this.record[uuid].parents) { + list = list.concat(this.record[uuid].parents); + } + } + + return list; + }; + this.createRow = (current, ln, content) => { let id = ''; let timestamp = ''; @@ -180,7 +198,7 @@ function JobRenderService ($q, $sce, $window) { if (current) { if (this.createToggles && current.isParent && current.line === ln) { id = current.uuid; - tdToggle = ``; + tdToggle = ``; } if (current.isHost) { @@ -226,20 +244,6 @@ function JobRenderService ($q, $sce, $window) { return `${hour}:${minute}:${second}`; }; - this.getParentEvents = (uuid, list) => { - list = list || []; - - if (this.record[uuid]) { - list.push(uuid); - - if (this.record[uuid].parents) { - list = list.concat(this.record[uuid].parents); - } - } - - return list; - }; - this.remove = elements => this.requestAnimationFrame(() => elements.remove()); this.requestAnimationFrame = fn => $q(resolve => { diff --git a/awx/ui/client/features/output/slide.service.js b/awx/ui/client/features/output/slide.service.js index 507a277733..fdfbd41b42 100644 --- a/awx/ui/client/features/output/slide.service.js +++ b/awx/ui/client/features/output/slide.service.js @@ -60,7 +60,7 @@ function getOverlapArray (range, other) { function SlidingWindowService ($q) { this.init = (storage, api) => { - const { prepend, append, shift, pop } = storage; + const { prepend, append, shift, pop, deleteRecord } = storage; const { getMaxCounter, getRange, getFirst, getLast } = api; this.api = { @@ -74,10 +74,12 @@ function SlidingWindowService ($q) { prepend, append, shift, - pop + pop, + deleteRecord, }; this.records = {}; + this.uuids = {}; this.chain = $q.resolve(); }; @@ -87,8 +89,9 @@ function SlidingWindowService ($q) { return this.storage.append(newEvents) .then(() => { - newEvents.forEach(({ counter, start_line, end_line }) => { + newEvents.forEach(({ counter, start_line, end_line, uuid }) => { this.records[counter] = { start_line, end_line }; + this.uuids[counter] = uuid; }); return $q.resolve(); @@ -102,8 +105,9 @@ function SlidingWindowService ($q) { return this.storage.prepend(newEvents) .then(() => { - newEvents.forEach(({ counter, start_line, end_line }) => { + newEvents.forEach(({ counter, start_line, end_line, uuid }) => { this.records[counter] = { start_line, end_line }; + this.uuids[counter] = uuid; }); return $q.resolve(); @@ -130,6 +134,9 @@ function SlidingWindowService ($q) { .then(() => { for (let i = max; i >= min; --i) { delete this.records[i]; + + this.storage.deleteRecord(this.uuids[i]); + delete this.uuids[i]; } return $q.resolve(); @@ -156,6 +163,9 @@ function SlidingWindowService ($q) { .then(() => { for (let i = min; i <= max; ++i) { delete this.records[i]; + + this.storage.deleteRecord(this.uuids[i]); + delete this.uuids[i]; } return $q.resolve();