Anchor post navigation via hotkeys to top of viewport (#38036)

This commit is contained in:
diondiondion 2026-03-03 12:02:04 +01:00 committed by GitHub
parent 80066631ee
commit 6e5aa00436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 23 additions and 24 deletions

View File

@ -47,7 +47,7 @@ function focusColumnTitle(index: number, multiColumn: boolean) {
/** /**
* Move focus to the column of the passed index (1-based). * Move focus to the column of the passed index (1-based).
* Focus is placed on the topmost visible item, or the column title * Focus is placed on the topmost visible item, or the column title.
*/ */
export function focusColumn(index = 1) { export function focusColumn(index = 1) {
// Skip the leftmost drawer in multi-column mode // Skip the leftmost drawer in multi-column mode
@ -94,8 +94,16 @@ export function focusColumn(index = 1) {
window.innerWidth || document.documentElement.clientWidth; window.innerWidth || document.documentElement.clientWidth;
const { item, rect } = itemToFocus; const { item, rect } = itemToFocus;
const scrollParent = isMultiColumnLayout
? container
: document.documentElement;
const columnHeaderHeight =
parseInt(
getComputedStyle(scrollParent).getPropertyValue('--column-header-height'),
) || 0;
if ( if (
container.scrollTop > item.offsetTop || scrollParent.scrollTop > item.offsetTop - columnHeaderHeight ||
rect.right > viewportWidth || rect.right > viewportWidth ||
rect.left < 0 rect.left < 0
) { ) {
@ -141,11 +149,7 @@ export function focusFirstItem() {
/** /**
* Focus the item next to the one with the provided index * Focus the item next to the one with the provided index
*/ */
export function focusItemSibling( export function focusItemSibling(index: number, direction: 1 | -1) {
index: number,
direction: 1 | -1,
scrollThreshold = 62,
) {
const focusedElement = document.activeElement; const focusedElement = document.activeElement;
const itemList = focusedElement?.closest('.item-list'); const itemList = focusedElement?.closest('.item-list');
@ -173,17 +177,9 @@ export function focusItemSibling(
} }
if (targetElement) { if (targetElement) {
const elementRect = targetElement.getBoundingClientRect(); targetElement.scrollIntoView({
block: 'start',
const isFullyVisible = });
elementRect.top >= scrollThreshold &&
elementRect.bottom <= window.innerHeight;
if (!isFullyVisible) {
targetElement.scrollIntoView({
block: direction === 1 ? 'start' : 'center',
});
}
targetElement.focus(); targetElement.focus();
} }

View File

@ -19,6 +19,15 @@ html {
&.rtl { &.rtl {
--text-x-direction: -1; --text-x-direction: -1;
} }
// Compensate for column header height when scrolling elements into view
--column-header-height: 62px;
scroll-padding-top: var(--column-header-height);
&:has(.layout-multi-column) {
--column-header-height: 0;
}
} }
html.has-modal { html.has-modal {

View File

@ -2984,8 +2984,6 @@ a.account__display-name {
} }
&__main { &__main {
--column-header-height: 62px;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
flex: 0 1 auto; flex: 0 1 auto;
@ -9143,10 +9141,6 @@ noscript {
.conversation { .conversation {
position: relative; position: relative;
// When scrolling these elements into view, take into account
// the column header height
scroll-margin-top: var(--column-header-height, 0);
&.unread { &.unread {
&::before { &::before {
content: ''; content: '';