Skip to content

Fix line-button issue after file selection in file tree (#34574) #34576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web_src/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ overflow-menu .ui.label {
}

.file-view tr.active .lines-num,
.file-view tr.active .lines-escape,
.file-view tr.active .lines-code {
background: var(--color-highlight-bg) !important;
}
Expand Down
13 changes: 10 additions & 3 deletions web_src/js/features/repo-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,15 @@ function showLineButton() {
}

export function initRepoCodeView() {
if (!document.querySelector('.code-view .lines-num')) return;
// When viewing a file or blame, there is always a ".file-view" element,
// but the ".code-view" class is only present when viewing the "code" of a file; it is not present when viewing a PDF file.
// Since the ".file-view" will be dynamically reloaded when navigating via the left file tree (eg: view a PDF file, then view a source code file, etc.)
// the "code-view" related event listeners should always be added when the current page contains ".file-view" element.
if (!document.querySelector('.repo-view-container .file-view')) return;

// "file code view" and "blame" pages need this "line number button" feature
let selRangeStart: string;
addDelegatedEventListener(document, 'click', '.lines-num span', (el: HTMLElement, e: KeyboardEvent) => {
addDelegatedEventListener(document, 'click', '.code-view .lines-num span', (el: HTMLElement, e: KeyboardEvent) => {
if (!selRangeStart || !e.shiftKey) {
selRangeStart = el.getAttribute('id');
selectRange(selRangeStart);
Expand All @@ -125,12 +130,14 @@ export function initRepoCodeView() {
showLineButton();
});

// apply the selected range from the URL hash
const onHashChange = () => {
if (!window.location.hash) return;
if (!document.querySelector('.code-view .lines-num')) return;
const range = window.location.hash.substring(1);
const first = selectRange(range);
if (first) {
// set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
// set scrollRestoration to 'manual' when there is a hash in the URL, so that the scroll position will not be remembered after refreshing
if (window.history.scrollRestoration !== 'manual') window.history.scrollRestoration = 'manual';
first.scrollIntoView({block: 'start'});
showLineButton();
Expand Down
27 changes: 20 additions & 7 deletions web_src/js/modules/tippy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
}
}
visibleInstances.add(instance);
target.setAttribute('aria-controls', instance.popper.id);
return onShow?.(instance);
},
arrow: arrow ?? (theme === 'bare' ? false : arrowSvg),
Expand Down Expand Up @@ -180,13 +181,25 @@ export function initGlobalTooltips(): void {
}

export function showTemporaryTooltip(target: Element, content: Content): void {
// if the target is inside a dropdown, the menu will be hidden soon
// so display the tooltip on the dropdown instead
target = target.closest('.ui.dropdown') || target;
const tippy = target._tippy ?? attachTooltip(target, content);
tippy.setContent(content);
if (!tippy.state.isShown) tippy.show();
tippy.setProps({
// if the target is inside a dropdown or tippy popup, the menu will be hidden soon
// so display the tooltip on the "aria-controls" element or dropdown instead
let refClientRect: DOMRect;
const popupTippyId = target.closest(`[data-tippy-root]`)?.id;
if (popupTippyId) {
// for example, the "Copy Permalink" button in the "File View" page for the selected lines
target = document.body;
refClientRect = document.querySelector(`[aria-controls="${CSS.escape(popupTippyId)}"]`)?.getBoundingClientRect();
refClientRect = refClientRect ?? new DOMRect(0, 0, 0, 0); // fallback to empty rect if not found, tippy doesn't accept null
} else {
// for example, the "Copy Link" button in the issue header dropdown menu
target = target.closest('.ui.dropdown') ?? target;
refClientRect = target.getBoundingClientRect();
}
const tooltipTippy = target._tippy ?? attachTooltip(target, content);
tooltipTippy.setContent(content);
tooltipTippy.setProps({getReferenceClientRect: () => refClientRect});
if (!tooltipTippy.state.isShown) tooltipTippy.show();
tooltipTippy.setProps({
onHidden: (tippy) => {
// reset the default tooltip content, if no default, then this temporary tooltip could be destroyed
if (!attachTooltip(target)) {
Expand Down