fix: Fix glitchy status keyboard navigation (#35455)

This commit is contained in:
diondiondion 2025-07-23 09:39:36 +02:00 committed by GitHub
parent 760d00b7f7
commit d36236cbcd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 19 deletions

View File

@ -40,6 +40,12 @@ export default class StatusList extends ImmutablePureComponent {
trackScroll: true,
};
componentDidMount() {
this.columnHeaderHeight = parseFloat(
getComputedStyle(this.node.node).getPropertyValue('--column-header-height')
) || 0;
}
getFeaturedStatusCount = () => {
return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
};
@ -53,35 +59,68 @@ export default class StatusList extends ImmutablePureComponent {
};
handleMoveUp = (id, featured) => {
const elementIndex = this.getCurrentStatusIndex(id, featured) - 1;
this._selectChild(elementIndex, true);
const index = this.getCurrentStatusIndex(id, featured);
this._selectChild(id, index, -1);
};
handleMoveDown = (id, featured) => {
const elementIndex = this.getCurrentStatusIndex(id, featured) + 1;
this._selectChild(elementIndex, false);
const index = this.getCurrentStatusIndex(id, featured);
this._selectChild(id, index, 1);
};
_selectChild = (id, index, direction) => {
const listContainer = this.node.node;
let listItem = listContainer.querySelector(
// :nth-child uses 1-based indexing
`.item-list > :nth-child(${index + 1 + direction})`
);
if (!listItem) {
return;
}
// If selected container element is empty, we skip it
if (listItem.matches(':empty')) {
this._selectChild(id, index + direction, direction);
return;
}
// Check if the list item is a post
let targetElement = listItem.querySelector('.focusable');
// Otherwise, check if the item contains follow suggestions or
// is a 'load more' button.
if (
!targetElement && (
listItem.querySelector('.inline-follow-suggestions') ||
listItem.matches('.load-more')
)
) {
targetElement = listItem;
}
if (targetElement) {
const elementRect = targetElement.getBoundingClientRect();
const isFullyVisible =
elementRect.top >= this.columnHeaderHeight &&
elementRect.bottom <= window.innerHeight;
if (!isFullyVisible) {
targetElement.scrollIntoView({
block: direction === 1 ? 'start' : 'center',
});
}
targetElement.focus();
}
}
handleLoadOlder = debounce(() => {
const { statusIds, lastId, onLoadMore } = this.props;
onLoadMore(lastId || (statusIds.size > 0 ? statusIds.last() : undefined));
}, 300, { leading: true });
_selectChild (index, align_top) {
const container = this.node.node;
// TODO: This breaks at the inline-follow-suggestions container
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
if (element) {
if (align_top && container.scrollTop > element.offsetTop) {
element.scrollIntoView(true);
} else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
element.scrollIntoView(false);
}
element.focus();
}
}
setRef = c => {
this.node = c;
};

View File

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