Enhance super tab

This commit is contained in:
Josh Perez 2023-05-09 11:52:03 -04:00 committed by GitHub
parent 8761bb8dae
commit 052aca3119
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 69 additions and 60 deletions

View file

@ -5182,6 +5182,10 @@
"messageformat": "(draft)",
"description": "Text shown in left pane as preview for conversation with saved a saved draft message"
},
"icu:Keyboard--focus-most-recent-message": {
"messageformat": "Focus oldest unread or last message",
"description": "Shown in shortcuts guide"
},
"Keyboard--navigate-by-section": {
"message": "Navigate by section",
"description": "(deleted 03/29/2023) Shown in the shortcuts guide"

View file

@ -1354,13 +1354,15 @@ export async function startApp(): Promise<void> {
}
// Super tab :)
if (commandOrCtrl && key === 'F6') {
if (
(commandOrCtrl && key === 'F6') ||
(commandOrCtrl && !shiftKey && (key === 't' || key === 'T'))
) {
window.enterKeyboardMode();
const focusedElement = document.activeElement;
const targets: Array<HTMLElement> = Array.from(
document.querySelectorAll('[data-supertab="true"]')
);
const focusedIndex = targets.findIndex(target => {
if (!target || !focusedElement) {
return false;
@ -1394,65 +1396,30 @@ export async function startApp(): Promise<void> {
}
}
targets[index]
.querySelectorAll<HTMLElement>(focusableSelectors.join(','))[0]
?.focus();
}
const node = targets[index];
const firstFocusableElement = node.querySelectorAll<HTMLElement>(
focusableSelectors.join(',')
)[0];
// Navigate by section
if (commandOrCtrl && !shiftKey && (key === 't' || key === 'T')) {
window.enterKeyboardMode();
const focusedElement = document.activeElement;
const targets: Array<HTMLElement | null> = [
document.querySelector('.module-main-header .module-avatar-button'),
document.querySelector(
'.module-left-pane__header__contents__back-button'
),
document.querySelector('.LeftPaneSearchInput__input'),
document.querySelector('.module-main-header__compose-icon'),
document.querySelector(
'.module-left-pane__compose-search-form__input'
),
document.querySelector(
'.module-conversation-list__item--contact-or-conversation'
),
document.querySelector('.module-search-results'),
document.querySelector('.CompositionArea .ql-editor'),
];
const focusedIndex = targets.findIndex(target => {
if (!target || !focusedElement) {
return false;
}
if (target === focusedElement) {
return true;
}
if (target.contains(focusedElement)) {
return true;
}
return false;
});
const lastIndex = targets.length - 1;
let index;
if (focusedIndex < 0 || focusedIndex >= lastIndex) {
index = 0;
if (firstFocusableElement) {
firstFocusableElement.focus();
} else {
index = focusedIndex + 1;
}
while (!targets[index]) {
index += 1;
if (index > lastIndex) {
index = 0;
const nodeInfo = Array.from(node.attributes)
.map(attr => `${attr.name}=${attr.value}`)
.join(',');
log.warn(
`supertab: could not find focus for DOM node ${node.nodeName}<${nodeInfo}>`
);
window.enterMouseMode();
const { activeElement } = document;
if (
activeElement &&
'blur' in activeElement &&
typeof activeElement.blur === 'function'
) {
activeElement.blur();
}
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
targets[index]!.focus();
}
// Cancel out of keyboard shortcut screen - has first precedence
@ -1611,6 +1578,23 @@ export async function startApp(): Promise<void> {
return;
}
if (
conversation &&
commandOrCtrl &&
!shiftKey &&
(key === 'j' || key === 'J')
) {
window.enterKeyboardMode();
const item: HTMLElement | null =
document.querySelector(
'.module-last-seen-indicator ~ div .module-message'
) ||
document.querySelector(
'.module-timeline__last-message .module-message'
);
item?.focus();
}
// Open all media
if (
conversation &&

View file

@ -781,6 +781,7 @@ export function CompositionInput(props: Props): React.ReactElement {
{({ ref }) => (
<div
className={getClassName('__input')}
data-supertab
ref={ref}
data-testid="CompositionInput"
data-enabled={disabled ? 'false' : 'true'}

View file

@ -659,6 +659,7 @@ export function LeftPane({
<div
aria-live="polite"
className="module-left-pane__list"
data-supertab
key={listKey}
role="presentation"
tabIndex={-1}

View file

@ -118,6 +118,7 @@ export function MainHeader({
<div className="module-main-header">
<div
className="module-main-header__avatar--container"
data-supertab
ref={setTargetElement}
>
<Avatar
@ -190,7 +191,7 @@ export function MainHeader({
</div>,
portalElement
)}
<div className="module-main-header__icon-container">
<div className="module-main-header__icon-container" data-supertab>
{areStoriesEnabled && (
<button
aria-label={i18n('icu:stories')}

View file

@ -49,7 +49,7 @@ export const SearchInput = forwardRef<HTMLInputElement, PropTypes>(
) {
const getClassName = getClassNamesFor(BASE_CLASS_NAME, moduleClassName);
return (
<div className={getClassName('__container')}>
<div className={getClassName('__container')} data-supertab>
{hasSearchIcon && <i className={getClassName('__icon')} />}
{children}
<input

View file

@ -23,6 +23,7 @@ type KeyType =
| 'enter'
| 'tab'
| 'ctrl'
| 'F6'
| '↑'
| '↓'
| ','
@ -60,7 +61,10 @@ function getNavigationShortcuts(i18n: LocalizerType): Array<ShortcutType> {
{
id: 'Keyboard--navigate-by-section',
description: i18n('icu:Keyboard--navigate-by-section'),
keys: [['commandOrCtrl', 'T']],
keys: [
['commandOrCtrl', 'T'],
['commandOrCtrl', 'F6'],
],
},
{
id: 'Keyboard--previous-conversation',
@ -93,6 +97,11 @@ function getNavigationShortcuts(i18n: LocalizerType): Array<ShortcutType> {
description: i18n('icu:Keyboard--conversation-by-index'),
keys: [['commandOrCtrl', '1 to 9']],
},
{
id: 'Keyboard--most-recent-message',
description: i18n('icu:Keyboard--focus-most-recent-message'),
keys: [['commandOrCtrl', 'J']],
},
{
id: 'Keyboard--preferences',
description: i18n('icu:Keyboard--preferences'),

View file

@ -885,6 +885,15 @@ export class Timeline extends React.Component<
messageNodes.push(
<div
key={messageId}
className={
itemIndex === items.length - 1
? 'module-timeline__last-message'
: undefined
}
data-supertab={
oldestUnseenIndex === itemIndex ||
(!oldestUnseenIndex && itemIndex === items.length - 1)
}
data-item-index={itemIndex}
data-message-id={messageId}
role="listitem"