mirror of
https://github.com/ansible/awx.git
synced 2026-03-01 16:58:46 -03:30
all things scroll following and anchoring
This commit is contained in:
committed by
jaredevantabor
parent
1c907a1f07
commit
c94d86206d
@@ -85,6 +85,24 @@
|
|||||||
color: @default-interface-txt;
|
color: @default-interface-txt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.JobResultsStdOut-followButton.is-engaged {
|
||||||
|
background-color: @default-link;
|
||||||
|
color: @default-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResultsStdOut-followButton.is-engaged .JobResultsStdOut-followIcon {
|
||||||
|
color: @default-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResultsStdOut-followButton.is-engaged:hover {
|
||||||
|
background-color: @default-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
.JobResultsStdOut-followButton.is-engaged:hover .JobResultsStdOut-followIcon,
|
||||||
|
.JobResultsStdOut-followButton.is-engaged .JobResultsStdOut-followIcon:hover {
|
||||||
|
color: @default-border;
|
||||||
|
}
|
||||||
|
|
||||||
.JobResultsStdOut-stdoutContainer {
|
.JobResultsStdOut-stdoutContainer {
|
||||||
height: ~"calc(100% - 48px)";
|
height: ~"calc(100% - 48px)";
|
||||||
background-color: @default-no-items-bord;
|
background-color: @default-no-items-bord;
|
||||||
@@ -167,3 +185,10 @@
|
|||||||
width: 70px;
|
width: 70px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.foo {
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-left: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,14 +5,132 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
// import hostStatusBarController from './host-status-bar.controller';
|
// import hostStatusBarController from './host-status-bar.controller';
|
||||||
export default [ 'templateUrl', '$timeout',
|
export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', '$log',
|
||||||
function(templateUrl, $timeout) {
|
function(templateUrl, $timeout, $location, $anchorScroll, $log) {
|
||||||
return {
|
return {
|
||||||
scope: false,
|
scope: false,
|
||||||
templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'),
|
templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'),
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
link: function(scope, element, attrs) {
|
link: function(scope, element) {
|
||||||
|
|
||||||
|
var findTopLines = function() {
|
||||||
|
scope.visibleItems = "";
|
||||||
|
var $container = $('.JobResultsStdOut-stdoutContainer');
|
||||||
|
var visItem,
|
||||||
|
parentItem;
|
||||||
|
$container.find('.JobResultsStdOut-aLineOfStdOut').each( function () {
|
||||||
|
var $this = $(this);
|
||||||
|
if ( $this.position().top + $this.height() > $container.position().top &&
|
||||||
|
$this.position().top < ($container.height() + $container.position().top) ) {
|
||||||
|
visItem = parseInt($($this.children()[0]).text());
|
||||||
|
|
||||||
|
var $head,
|
||||||
|
classList,
|
||||||
|
header;
|
||||||
|
if ($this.hasClass("header_play") || $this.hasClass("header_task")) {
|
||||||
|
classList = $this.attr("class")
|
||||||
|
.split(" ");
|
||||||
|
header = classList
|
||||||
|
.filter(n => n.indexOf("header_task_") > -1)[0];
|
||||||
|
if (!header) {
|
||||||
|
header = classList
|
||||||
|
.filter(n => n.indexOf("header_play_") > -1)[0];
|
||||||
|
}
|
||||||
|
$head = $(".actual_header." + header);
|
||||||
|
} else {
|
||||||
|
classList = $this.attr("class")
|
||||||
|
.split(" ");
|
||||||
|
header = classList
|
||||||
|
.filter(n => n.indexOf("task_") > -1)[0];
|
||||||
|
if (!header) {
|
||||||
|
header = classList
|
||||||
|
.filter(n => n.indexOf("play_") > -1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$head = $(".actual_header.header_" + header);
|
||||||
|
}
|
||||||
|
parentItem = parseInt($($head.children()[0]).text());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
scope.visLine = visItem;
|
||||||
|
scope.parentVisLine = parentItem;
|
||||||
|
scope.visibleItems = "First visible line: " + visItem + ", Parent line: " + parentItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
var lastScrollTop = 0;
|
||||||
|
$(".JobResultsStdOut-stdoutContainer").on('scroll', function() {
|
||||||
|
var st = $(this).scrollTop();
|
||||||
|
if (st < lastScrollTop){
|
||||||
|
// user up scrolled, so disengage follow
|
||||||
|
scope.followEngaged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($(this).scrollTop() + $(this).innerHeight() >=
|
||||||
|
$(this)[0].scrollHeight) {
|
||||||
|
// user scrolled all the way to bottom, so engage
|
||||||
|
// follow
|
||||||
|
scope.followEngaged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScrollTop = st;
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.followScroll = function() {
|
||||||
|
$(".JobResultsStdOut-followAnchor")
|
||||||
|
.appendTo(".JobResultsStdOut-stdoutContainer");
|
||||||
|
|
||||||
|
$location.hash('followAnchor');
|
||||||
|
$anchorScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.topLineAnchor = function() {
|
||||||
|
$location.hash('topLineAnchor');
|
||||||
|
$anchorScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// follow button for completed job should specify that the
|
||||||
|
// button will jump to the bottom of the standard out pane,
|
||||||
|
// not follow lines as they come in
|
||||||
|
if (scope.jobFinished) {
|
||||||
|
scope.followTooltip = "Jump to last line";
|
||||||
|
}
|
||||||
|
|
||||||
|
// if following becomes active, go ahead and get to the bottom
|
||||||
|
// of the standard out pane
|
||||||
|
scope.$watch('followEngaged', function(val) {
|
||||||
|
if (val) {
|
||||||
|
scope.followScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scope.jobFinished) {
|
||||||
|
if (val) {
|
||||||
|
scope.followTooltip = "Follow standard out";
|
||||||
|
} else {
|
||||||
|
scope.followTooltip = "Unfollow standard out";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.followToggleClicked = function() {
|
||||||
|
if (scope.jobFinished) {
|
||||||
|
scope.followScroll();
|
||||||
|
} else {
|
||||||
|
scope.followEngaged = !scope.followEngaged;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
scope.toggleAllStdout = function(type) {
|
scope.toggleAllStdout = function(type) {
|
||||||
|
findTopLines();
|
||||||
|
|
||||||
|
if (type === 'expand') {
|
||||||
|
$(".line_num_" + scope.visLine)
|
||||||
|
.prepend($("#topLineAnchor"));
|
||||||
|
} else {
|
||||||
|
$(".line_num_" + scope.parentVisLine)
|
||||||
|
.prepend($("#topLineAnchor"));
|
||||||
|
}
|
||||||
|
|
||||||
var expandClass;
|
var expandClass;
|
||||||
if (type === 'expand') {
|
if (type === 'expand') {
|
||||||
expandClass = "fa-caret-right";
|
expandClass = "fa-caret-right";
|
||||||
@@ -24,6 +142,7 @@ export default [ 'templateUrl', '$timeout',
|
|||||||
.each((i, val) => {
|
.each((i, val) => {
|
||||||
$timeout(function(){
|
$timeout(function(){
|
||||||
angular.element(val).trigger('click');
|
angular.element(val).trigger('click');
|
||||||
|
scope.topLineAnchor();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,6 +157,7 @@ export default [ 'templateUrl', '$timeout',
|
|||||||
$timeout(function(){
|
$timeout(function(){
|
||||||
angular.element(val)
|
angular.element(val)
|
||||||
.trigger('click');
|
.trigger('click');
|
||||||
|
scope.topLineAnchor();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,16 +17,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="JobResultsStdOut-toolbarStdoutColumn">
|
<div class="JobResultsStdOut-toolbarStdoutColumn">
|
||||||
|
<!-- <span class="foo">{{ visibleItems }}</span> -->
|
||||||
<div class="JobResultsStdOut-followButton"
|
<div class="JobResultsStdOut-followButton"
|
||||||
aw-tool-tip="Follow standard out."
|
ng-class="{'is-engaged': followEngaged && !jobFinished}"
|
||||||
data-placement="left">
|
aw-tool-tip="Follow standard out"
|
||||||
|
data-placement="left"
|
||||||
|
ng-click="followToggleClicked()">
|
||||||
<i class="JobResultsStdOut-followIcon fa fa-arrow-down">
|
<i class="JobResultsStdOut-followIcon fa fa-arrow-down">
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- TODO: get followTooltip watch working
|
||||||
|
<div class="JobResultsStdOut-followButton"
|
||||||
|
aw-tool-tip="{{followTooltip}}"
|
||||||
|
data-tip-watch="followTooltip"
|
||||||
|
data-placement="left"
|
||||||
|
ng-click="followToggleClicked()">
|
||||||
|
<i class="JobResultsStdOut-followIcon fa fa-arrow-down">
|
||||||
|
</i>
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="JobResultsStdOut-stdoutContainer">
|
<div class="JobResultsStdOut-stdoutContainer">
|
||||||
<div class="JobResultsStdOut-numberColumnPreload"></div>
|
<div class="JobResultsStdOut-numberColumnPreload"></div>
|
||||||
|
<div id='topLineAnchor'></div>
|
||||||
|
<div id="followAnchor"
|
||||||
|
class="JobResultsStdOut-followAnchor">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="JobResultsStdOut-footer">
|
<div class="JobResultsStdOut-footer">
|
||||||
<div class="JobResultsStdOut-footerNumberColumn"></div>
|
<div class="JobResultsStdOut-footerNumberColumn"></div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', 'eventQueue', '$compile', function(jobData, jobDataOptions, jobLabels, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope, eventQueue, $compile) {
|
export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', 'eventQueue', '$compile', function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope, eventQueue, $compile) {
|
||||||
var getTowerLinks = function() {
|
var getTowerLinks = function() {
|
||||||
var getTowerLink = function(key) {
|
var getTowerLink = function(key) {
|
||||||
if ($scope.job.related[key]) {
|
if ($scope.job.related[key]) {
|
||||||
@@ -81,6 +81,11 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa
|
|||||||
$scope.countFinished = count.countFinished;
|
$scope.countFinished = count.countFinished;
|
||||||
$scope.stdoutArr = [];
|
$scope.stdoutArr = [];
|
||||||
|
|
||||||
|
// if the job is still running engage following of the last line in the
|
||||||
|
// standard out pane
|
||||||
|
$scope.jobFinished = jobFinished;
|
||||||
|
$scope.followEngaged = !$scope.jobFinished;
|
||||||
|
|
||||||
// EVENT STUFF BELOW
|
// EVENT STUFF BELOW
|
||||||
|
|
||||||
// just putting the event queue on scope so it can be inspected in the
|
// just putting the event queue on scope so it can be inspected in the
|
||||||
@@ -121,6 +126,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa
|
|||||||
|
|
||||||
if (change === 'finishedTime' && !$scope.job.finished) {
|
if (change === 'finishedTime' && !$scope.job.finished) {
|
||||||
$scope.job.finished = mungedEvent.finishedTime;
|
$scope.job.finished = mungedEvent.finishedTime;
|
||||||
|
$scope.jobFinished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change === 'countFinished') {
|
if (change === 'countFinished') {
|
||||||
@@ -136,6 +142,10 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa
|
|||||||
.element(".JobResultsStdOut-stdoutContainer")
|
.element(".JobResultsStdOut-stdoutContainer")
|
||||||
.append($compile(mungedEvent
|
.append($compile(mungedEvent
|
||||||
.stdout)($scope));
|
.stdout)($scope));
|
||||||
|
|
||||||
|
if ($scope.followEngaged) {
|
||||||
|
$scope.followScroll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ export default {
|
|||||||
});
|
});
|
||||||
return val.promise;
|
return val.promise;
|
||||||
}],
|
}],
|
||||||
|
// used to signify if job is completed or still running
|
||||||
|
jobFinished: ['jobData', function(jobData) {
|
||||||
|
if (jobData.finished) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}],
|
||||||
// after the GET for the job, this helps us keep the status bar from
|
// after the GET for the job, this helps us keep the status bar from
|
||||||
// flashing as rest data comes in. If the job is finished and
|
// flashing as rest data comes in. If the job is finished and
|
||||||
// there's a playbook_on_stats event, go ahead and resolve the count
|
// there's a playbook_on_stats event, go ahead and resolve the count
|
||||||
|
|||||||
@@ -31,15 +31,23 @@ export default [function(){
|
|||||||
line = line.replace(/\[0m/g, '</span>');
|
line = line.replace(/\[0m/g, '</span>');
|
||||||
return line;
|
return line;
|
||||||
},
|
},
|
||||||
getCollapseClasses: function(event) {
|
getCollapseClasses: function(event, line, lineNum) {
|
||||||
var string = "";
|
var string = "";
|
||||||
if (event.event_name === "playbook_on_play_start") {
|
if (event.event_name === "playbook_on_play_start") {
|
||||||
string += " header_play";
|
string += " header_play";
|
||||||
|
string += " header_play_" + event.event_data.play_uuid;
|
||||||
|
if (line) {
|
||||||
|
string += " actual_header";
|
||||||
|
}
|
||||||
} else if (event.event_name === "playbook_on_task_start") {
|
} else if (event.event_name === "playbook_on_task_start") {
|
||||||
string += " header_task";
|
string += " header_task";
|
||||||
|
string += " header_task_" + event.event_data.task_uuid;
|
||||||
if (event.event_data.play_uuid) {
|
if (event.event_data.play_uuid) {
|
||||||
string += " play_" + event.event_data.play_uuid;
|
string += " play_" + event.event_data.play_uuid;
|
||||||
}
|
}
|
||||||
|
if (line) {
|
||||||
|
string += " actual_header";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (event.event_data.play_uuid) {
|
if (event.event_data.play_uuid) {
|
||||||
string += " play_" + event.event_data.play_uuid;
|
string += " play_" + event.event_data.play_uuid;
|
||||||
@@ -48,7 +56,7 @@ export default [function(){
|
|||||||
string += " task_" + event.event_data.task_uuid;
|
string += " task_" + event.event_data.task_uuid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
string += " line_num_" + lineNum;
|
||||||
return string;
|
return string;
|
||||||
},
|
},
|
||||||
getCollapseIcon: function(event, line) {
|
getCollapseIcon: function(event, line) {
|
||||||
@@ -94,7 +102,7 @@ export default [function(){
|
|||||||
event.stdout.split("\r\n").slice(0, -1))
|
event.stdout.split("\r\n").slice(0, -1))
|
||||||
.map(lineArr => {
|
.map(lineArr => {
|
||||||
return `
|
return `
|
||||||
<div class="JobResultsStdOut-aLineOfStdOut${this.getCollapseClasses(event)}">
|
<div class="JobResultsStdOut-aLineOfStdOut${this.getCollapseClasses(event, lineArr[1], lineArr[0])}">
|
||||||
<div class="JobResultsStdOut-lineNumberColumn">${this.getCollapseIcon(event, lineArr[1])}${lineArr[0]}</div>
|
<div class="JobResultsStdOut-lineNumberColumn">${this.getCollapseIcon(event, lineArr[1])}${lineArr[0]}</div>
|
||||||
<div class="JobResultsStdOut-stdoutColumn">${this.prettify(lineArr[1])}</div>
|
<div class="JobResultsStdOut-stdoutColumn">${this.prettify(lineArr[1])}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|||||||
Reference in New Issue
Block a user