Enhance super tab
This commit is contained in:
parent
8761bb8dae
commit
052aca3119
8 changed files with 69 additions and 60 deletions
|
@ -5182,6 +5182,10 @@
|
||||||
"messageformat": "(draft)",
|
"messageformat": "(draft)",
|
||||||
"description": "Text shown in left pane as preview for conversation with saved a saved draft message"
|
"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": {
|
"Keyboard--navigate-by-section": {
|
||||||
"message": "Navigate by section",
|
"message": "Navigate by section",
|
||||||
"description": "(deleted 03/29/2023) Shown in the shortcuts guide"
|
"description": "(deleted 03/29/2023) Shown in the shortcuts guide"
|
||||||
|
|
|
@ -1354,13 +1354,15 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Super tab :)
|
// Super tab :)
|
||||||
if (commandOrCtrl && key === 'F6') {
|
if (
|
||||||
|
(commandOrCtrl && key === 'F6') ||
|
||||||
|
(commandOrCtrl && !shiftKey && (key === 't' || key === 'T'))
|
||||||
|
) {
|
||||||
window.enterKeyboardMode();
|
window.enterKeyboardMode();
|
||||||
const focusedElement = document.activeElement;
|
const focusedElement = document.activeElement;
|
||||||
const targets: Array<HTMLElement> = Array.from(
|
const targets: Array<HTMLElement> = Array.from(
|
||||||
document.querySelectorAll('[data-supertab="true"]')
|
document.querySelectorAll('[data-supertab="true"]')
|
||||||
);
|
);
|
||||||
|
|
||||||
const focusedIndex = targets.findIndex(target => {
|
const focusedIndex = targets.findIndex(target => {
|
||||||
if (!target || !focusedElement) {
|
if (!target || !focusedElement) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1394,65 +1396,30 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
targets[index]
|
const node = targets[index];
|
||||||
.querySelectorAll<HTMLElement>(focusableSelectors.join(','))[0]
|
const firstFocusableElement = node.querySelectorAll<HTMLElement>(
|
||||||
?.focus();
|
focusableSelectors.join(',')
|
||||||
}
|
)[0];
|
||||||
|
|
||||||
// Navigate by section
|
if (firstFocusableElement) {
|
||||||
if (commandOrCtrl && !shiftKey && (key === 't' || key === 'T')) {
|
firstFocusableElement.focus();
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
index = focusedIndex + 1;
|
const nodeInfo = Array.from(node.attributes)
|
||||||
}
|
.map(attr => `${attr.name}=${attr.value}`)
|
||||||
|
.join(',');
|
||||||
while (!targets[index]) {
|
log.warn(
|
||||||
index += 1;
|
`supertab: could not find focus for DOM node ${node.nodeName}<${nodeInfo}>`
|
||||||
if (index > lastIndex) {
|
);
|
||||||
index = 0;
|
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
|
// Cancel out of keyboard shortcut screen - has first precedence
|
||||||
|
@ -1611,6 +1578,23 @@ export async function startApp(): Promise<void> {
|
||||||
return;
|
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
|
// Open all media
|
||||||
if (
|
if (
|
||||||
conversation &&
|
conversation &&
|
||||||
|
|
|
@ -781,6 +781,7 @@ export function CompositionInput(props: Props): React.ReactElement {
|
||||||
{({ ref }) => (
|
{({ ref }) => (
|
||||||
<div
|
<div
|
||||||
className={getClassName('__input')}
|
className={getClassName('__input')}
|
||||||
|
data-supertab
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data-testid="CompositionInput"
|
data-testid="CompositionInput"
|
||||||
data-enabled={disabled ? 'false' : 'true'}
|
data-enabled={disabled ? 'false' : 'true'}
|
||||||
|
|
|
@ -659,6 +659,7 @@ export function LeftPane({
|
||||||
<div
|
<div
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
className="module-left-pane__list"
|
className="module-left-pane__list"
|
||||||
|
data-supertab
|
||||||
key={listKey}
|
key={listKey}
|
||||||
role="presentation"
|
role="presentation"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
|
|
@ -118,6 +118,7 @@ export function MainHeader({
|
||||||
<div className="module-main-header">
|
<div className="module-main-header">
|
||||||
<div
|
<div
|
||||||
className="module-main-header__avatar--container"
|
className="module-main-header__avatar--container"
|
||||||
|
data-supertab
|
||||||
ref={setTargetElement}
|
ref={setTargetElement}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
|
@ -190,7 +191,7 @@ export function MainHeader({
|
||||||
</div>,
|
</div>,
|
||||||
portalElement
|
portalElement
|
||||||
)}
|
)}
|
||||||
<div className="module-main-header__icon-container">
|
<div className="module-main-header__icon-container" data-supertab>
|
||||||
{areStoriesEnabled && (
|
{areStoriesEnabled && (
|
||||||
<button
|
<button
|
||||||
aria-label={i18n('icu:stories')}
|
aria-label={i18n('icu:stories')}
|
||||||
|
|
|
@ -49,7 +49,7 @@ export const SearchInput = forwardRef<HTMLInputElement, PropTypes>(
|
||||||
) {
|
) {
|
||||||
const getClassName = getClassNamesFor(BASE_CLASS_NAME, moduleClassName);
|
const getClassName = getClassNamesFor(BASE_CLASS_NAME, moduleClassName);
|
||||||
return (
|
return (
|
||||||
<div className={getClassName('__container')}>
|
<div className={getClassName('__container')} data-supertab>
|
||||||
{hasSearchIcon && <i className={getClassName('__icon')} />}
|
{hasSearchIcon && <i className={getClassName('__icon')} />}
|
||||||
{children}
|
{children}
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -23,6 +23,7 @@ type KeyType =
|
||||||
| 'enter'
|
| 'enter'
|
||||||
| 'tab'
|
| 'tab'
|
||||||
| 'ctrl'
|
| 'ctrl'
|
||||||
|
| 'F6'
|
||||||
| '↑'
|
| '↑'
|
||||||
| '↓'
|
| '↓'
|
||||||
| ','
|
| ','
|
||||||
|
@ -60,7 +61,10 @@ function getNavigationShortcuts(i18n: LocalizerType): Array<ShortcutType> {
|
||||||
{
|
{
|
||||||
id: 'Keyboard--navigate-by-section',
|
id: 'Keyboard--navigate-by-section',
|
||||||
description: i18n('icu:Keyboard--navigate-by-section'),
|
description: i18n('icu:Keyboard--navigate-by-section'),
|
||||||
keys: [['commandOrCtrl', 'T']],
|
keys: [
|
||||||
|
['commandOrCtrl', 'T'],
|
||||||
|
['commandOrCtrl', 'F6'],
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Keyboard--previous-conversation',
|
id: 'Keyboard--previous-conversation',
|
||||||
|
@ -93,6 +97,11 @@ function getNavigationShortcuts(i18n: LocalizerType): Array<ShortcutType> {
|
||||||
description: i18n('icu:Keyboard--conversation-by-index'),
|
description: i18n('icu:Keyboard--conversation-by-index'),
|
||||||
keys: [['commandOrCtrl', '1 to 9']],
|
keys: [['commandOrCtrl', '1 to 9']],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'Keyboard--most-recent-message',
|
||||||
|
description: i18n('icu:Keyboard--focus-most-recent-message'),
|
||||||
|
keys: [['commandOrCtrl', 'J']],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Keyboard--preferences',
|
id: 'Keyboard--preferences',
|
||||||
description: i18n('icu:Keyboard--preferences'),
|
description: i18n('icu:Keyboard--preferences'),
|
||||||
|
|
|
@ -885,6 +885,15 @@ export class Timeline extends React.Component<
|
||||||
messageNodes.push(
|
messageNodes.push(
|
||||||
<div
|
<div
|
||||||
key={messageId}
|
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-item-index={itemIndex}
|
||||||
data-message-id={messageId}
|
data-message-id={messageId}
|
||||||
role="listitem"
|
role="listitem"
|
||||||
|
|
Loading…
Add table
Reference in a new issue