Merge branch 'hotkey-nav' into 'develop'

Fix hotkey navigation?

See merge request soapbox-pub/soapbox-fe!1409
This commit is contained in:
marcin mikołajczak 2022-05-20 21:17:34 +00:00
commit f90f76ae02
6 changed files with 54 additions and 44 deletions

View file

@ -42,6 +42,7 @@ interface IScrollableList extends VirtuosoProps<any, any> {
onRefresh?: () => Promise<any>,
className?: string,
itemClassName?: string,
id?: string,
style?: React.CSSProperties,
useWindowScroll?: boolean
}
@ -60,6 +61,7 @@ const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(({
onLoadMore,
className,
itemClassName,
id,
hasMore,
placeholderComponent: Placeholder,
placeholderCount = 0,
@ -133,6 +135,7 @@ const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(({
const renderFeed = (): JSX.Element => (
<Virtuoso
ref={ref}
id={id}
useWindowScroll={useWindowScroll}
className={className}
data={data}

View file

@ -282,13 +282,11 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
}
handleHotkeyMoveUp = (e?: KeyboardEvent): void => {
// FIXME: what's going on here?
// this.props.onMoveUp(this.props.status.id, e?.target?.getAttribute('data-featured'));
this.props.onMoveUp(this.props.status.id, this.props.featured);
}
handleHotkeyMoveDown = (e?: KeyboardEvent): void => {
// FIXME: what's going on here?
// this.props.onMoveDown(this.props.status.id, e?.target?.getAttribute('data-featured'));
this.props.onMoveDown(this.props.status.id, this.props.featured);
}
handleHotkeyToggleHidden = (): void => {
@ -601,7 +599,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
return (
<HotKeys handlers={handlers} data-testid='status'>
<div
className='status cursor-pointer'
className={classNames('status cursor-pointer', { focusable: this.props.focusable })}
tabIndex={this.props.focusable && !this.props.muted ? 0 : undefined}
data-featured={featured ? 'true' : null}
aria-label={textForScreenReader(intl, status, rebloggedByText)}

View file

@ -77,18 +77,18 @@ export default class StatusList extends ImmutablePureComponent {
this.props.onLoadMore(loadMoreID);
}, 300, { leading: true })
_selectChild(index, align_top) {
const container = this.node.node;
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
_selectChild(index) {
this.node.scrollIntoView({
index,
behavior: 'smooth',
done: () => {
const element = document.querySelector(`#status-list [data-index="${index}"] .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();
}
if (element) {
element.focus();
}
},
});
}
handleDequeueTimeline = () => {
@ -216,6 +216,7 @@ export default class StatusList extends ImmutablePureComponent {
message={messages.queue}
/>,
<ScrollableList
id='status-list'
key='scrollable-list'
isLoading={isLoading}
showLoading={isLoading && statusIds.size === 0}

View file

@ -20,26 +20,26 @@ export default class ConversationsList extends ImmutablePureComponent {
handleMoveUp = id => {
const elementIndex = this.getCurrentIndex(id) - 1;
this._selectChild(elementIndex, true);
this._selectChild(elementIndex);
}
handleMoveDown = id => {
const elementIndex = this.getCurrentIndex(id) + 1;
this._selectChild(elementIndex, false);
this._selectChild(elementIndex);
}
_selectChild(index, align_top) {
const container = this.node.node;
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
_selectChild(index) {
this.node.scrollIntoView({
index,
behavior: 'smooth',
done: () => {
const element = document.querySelector(`#direct-list [data-index="${index}"] .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();
}
if (element) {
element.focus();
}
},
});
}
setRef = c => {
@ -58,7 +58,9 @@ export default class ConversationsList extends ImmutablePureComponent {
<ScrollableList
{...other}
onLoadMore={onLoadMore && this.handleLoadOlder}
scrollKey='direct' ref={this.setRef}
id='direct-list'
scrollKey='direct'
ref={this.setRef}
isLoading={isLoading}
showLoading={isLoading && conversations.size === 0}
>

View file

@ -111,32 +111,37 @@ class Notifications extends React.PureComponent {
this.props.dispatch(scrollTopNotifications(false));
}, 100);
setRef = c => {
this.node = c;
}
setColumnRef = c => {
this.column = c;
}
handleMoveUp = id => {
const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) - 1;
this._selectChild(elementIndex, true);
this._selectChild(elementIndex);
}
handleMoveDown = id => {
const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) + 1;
this._selectChild(elementIndex, false);
this._selectChild(elementIndex);
}
_selectChild(index, align_top) {
const container = this.column;
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
_selectChild(index) {
this.node.scrollIntoView({
index,
behavior: 'smooth',
done: () => {
const container = this.column;
const element = container.querySelector(`[data-index="${index}"] .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();
}
if (element) {
element.focus();
}
},
});
}
handleDequeueNotifications = () => {
@ -161,6 +166,7 @@ class Notifications extends React.PureComponent {
const scrollContainer = (
<PullToRefresh onRefresh={this.handleRefresh}>
<Virtuoso
ref={this.setRef}
useWindowScroll
data={showLoading ? Array(20).fill() : notifications.toArray()}
startReached={this.handleScrollToTop}

View file

@ -715,7 +715,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
<HotKeys handlers={handlers}>
<div
ref={this.setStatusRef}
className={classNames('detailed-status__wrapper')}
className='detailed-status__wrapper focusable'
tabIndex={0}
// FIXME: no "reblogged by" text is added for the screen reader
aria-label={textForScreenReader(intl, status)}