Skip to content

Commit fff9534

Browse files
committed
improve log group auto-scrolling
1 parent e52badf commit fff9534

File tree

1 file changed

+21
-4
lines changed

1 file changed

+21
-4
lines changed

web_src/js/components/RepoActionView.vue

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import {SvgIcon} from '../svg.ts';
33
import ActionRunStatus from './ActionRunStatus.vue';
44
import {defineComponent, type PropType} from 'vue';
5-
import {createElementFromAttrs, toggleElem} from '../utils/dom.ts';
5+
import {addDelegatedEventListener, createElementFromAttrs, toggleElem} from '../utils/dom.ts';
66
import {formatDatetime} from '../utils/time.ts';
77
import {renderAnsi} from '../render/ansi.ts';
88
import {POST, DELETE} from '../modules/fetch.ts';
@@ -188,6 +188,19 @@ export default defineComponent({
188188
// load job data and then auto-reload periodically
189189
// need to await first loadJob so this.currentJobStepsStates is initialized and can be used in hashChangeListener
190190
await this.loadJob();
191+
192+
// auto-scroll to the bottom of the log group when it is opened
193+
// "toggle" event doesn't bubble, so we need to use 'click' event delegation to handle it
194+
addDelegatedEventListener(this.elStepsContainer(), 'click', 'summary.job-log-group-summary', (el, _) => {
195+
if (!this.optionAlwaysAutoScroll) return;
196+
const elJobLogGroup = el.closest('details.job-log-group') as HTMLDetailsElement;
197+
setTimeout(() => {
198+
if (elJobLogGroup.open && !isLogElementInViewport(elJobLogGroup)) {
199+
elJobLogGroup.scrollIntoView({behavior: 'smooth', block: 'end'});
200+
}
201+
}, 0);
202+
});
203+
191204
this.intervalID = setInterval(() => this.loadJob(), 1000);
192205
document.body.addEventListener('click', this.closeDropdown);
193206
this.hashChangeListener();
@@ -424,9 +437,13 @@ export default defineComponent({
424437
if (this.menuVisible) this.menuVisible = false;
425438
},
426439
440+
elStepsContainer(): HTMLElement {
441+
return this.$refs.stepsContainer as HTMLElement;
442+
},
443+
427444
toggleTimeDisplay(type: 'seconds' | 'stamp') {
428445
this.timeVisible[`log-time-${type}`] = !this.timeVisible[`log-time-${type}`];
429-
for (const el of (this.$refs.steps as HTMLElement).querySelectorAll(`.log-time-${type}`)) {
446+
for (const el of this.elStepsContainer().querySelectorAll(`.log-time-${type}`)) {
430447
toggleElem(el, this.timeVisible[`log-time-${type}`]);
431448
}
432449
},
@@ -448,7 +465,7 @@ export default defineComponent({
448465
// so logline can be selected by querySelector
449466
await this.loadJob();
450467
}
451-
const logLine = (this.$refs.steps as HTMLElement).querySelector(selectedLogStep);
468+
const logLine = this.elStepsContainer().querySelector(selectedLogStep);
452469
if (!logLine) return;
453470
logLine.querySelector<HTMLAnchorElement>('.line-num').click();
454471
},
@@ -583,7 +600,7 @@ export default defineComponent({
583600
</div>
584601
</div>
585602
</div>
586-
<div class="job-step-container" ref="steps" v-if="currentJob.steps.length">
603+
<div class="job-step-container" ref="stepsContainer" v-if="currentJob.steps.length">
587604
<div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
588605
<div class="job-step-summary" @click.stop="isExpandable(jobStep.status) && toggleStepLogs(i)" :class="[currentJobStepsStates[i].expanded ? 'selected' : '', isExpandable(jobStep.status) && 'step-expandable']">
589606
<!-- If the job is done and the job step log is loaded for the first time, show the loading icon

0 commit comments

Comments
 (0)