Improve RovingTabIndex & Room List filtering performance (#6987)
This commit is contained in:
parent
39e61c4fa3
commit
04c06b6aa8
9 changed files with 471 additions and 327 deletions
|
@ -249,6 +249,8 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
let handled = true;
|
||||
|
||||
switch (ev.key) {
|
||||
// XXX: this is imitating roving behaviour, it should really use the RovingTabIndex utils
|
||||
// to inherit proper handling of unmount edge cases
|
||||
case Key.TAB:
|
||||
case Key.ESCAPE:
|
||||
case Key.ARROW_LEFT: // close on left and right arrows too for when it is a context menu on a <Toolbar />
|
||||
|
|
|
@ -40,6 +40,7 @@ import { replaceableComponent } from "../../utils/replaceableComponent";
|
|||
import SpaceStore, { UPDATE_SELECTED_SPACE } from "../../stores/SpaceStore";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
|
||||
import UIStore from "../../stores/UIStore";
|
||||
import { findSiblingElement, IState as IRovingTabIndexState } from "../../accessibility/RovingTabIndex";
|
||||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
|
@ -51,19 +52,12 @@ interface IState {
|
|||
activeSpace?: Room;
|
||||
}
|
||||
|
||||
// List of CSS classes which should be included in keyboard navigation within the room list
|
||||
const cssClasses = [
|
||||
"mx_RoomSearch_input",
|
||||
"mx_RoomSearch_minimizedHandle", // minimized <RoomSearch />
|
||||
"mx_RoomSublist_headerText",
|
||||
"mx_RoomTile",
|
||||
"mx_RoomSublist_showNButton",
|
||||
];
|
||||
|
||||
@replaceableComponent("structures.LeftPanel")
|
||||
export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
private ref: React.RefObject<HTMLDivElement> = createRef();
|
||||
private listContainerRef: React.RefObject<HTMLDivElement> = createRef();
|
||||
private ref = createRef<HTMLDivElement>();
|
||||
private listContainerRef = createRef<HTMLDivElement>();
|
||||
private roomSearchRef = createRef<RoomSearch>();
|
||||
private roomListRef = createRef<RoomList>();
|
||||
private focusedElement = null;
|
||||
private isDoingStickyHeaders = false;
|
||||
|
||||
|
@ -283,16 +277,25 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
this.focusedElement = null;
|
||||
};
|
||||
|
||||
private onKeyDown = (ev: React.KeyboardEvent) => {
|
||||
private onKeyDown = (ev: React.KeyboardEvent, state?: IRovingTabIndexState) => {
|
||||
if (!this.focusedElement) return;
|
||||
|
||||
const action = getKeyBindingsManager().getRoomListAction(ev);
|
||||
switch (action) {
|
||||
case RoomListAction.NextRoom:
|
||||
if (!state) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.roomListRef.current?.focus();
|
||||
}
|
||||
break;
|
||||
|
||||
case RoomListAction.PrevRoom:
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.onMoveFocus(action === RoomListAction.PrevRoom);
|
||||
if (state && state.activeRef === findSiblingElement(state.refs, 0)) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.roomSearchRef.current?.focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
@ -305,45 +308,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
private onMoveFocus = (up: boolean) => {
|
||||
let element = this.focusedElement;
|
||||
|
||||
let descending = false; // are we currently descending or ascending through the DOM tree?
|
||||
let classes: DOMTokenList;
|
||||
|
||||
do {
|
||||
const child = up ? element.lastElementChild : element.firstElementChild;
|
||||
const sibling = up ? element.previousElementSibling : element.nextElementSibling;
|
||||
|
||||
if (descending) {
|
||||
if (child) {
|
||||
element = child;
|
||||
} else if (sibling) {
|
||||
element = sibling;
|
||||
} else {
|
||||
descending = false;
|
||||
element = element.parentElement;
|
||||
}
|
||||
} else {
|
||||
if (sibling) {
|
||||
element = sibling;
|
||||
descending = true;
|
||||
} else {
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
if (element) {
|
||||
classes = element.classList;
|
||||
}
|
||||
} while (element && (!cssClasses.some(c => classes.contains(c)) || element.offsetParent === null));
|
||||
|
||||
if (element) {
|
||||
element.focus();
|
||||
this.focusedElement = element;
|
||||
}
|
||||
};
|
||||
|
||||
private renderHeader(): React.ReactNode {
|
||||
return (
|
||||
<div className="mx_LeftPanel_userHeader">
|
||||
|
@ -388,7 +352,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
>
|
||||
<RoomSearch
|
||||
isMinimized={this.props.isMinimized}
|
||||
onKeyDown={this.onKeyDown}
|
||||
ref={this.roomSearchRef}
|
||||
onSelectRoom={this.selectRoom}
|
||||
/>
|
||||
|
||||
|
@ -417,6 +381,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
activeSpace={this.state.activeSpace}
|
||||
onResize={this.refreshStickyHeaders}
|
||||
onListCollapse={this.refreshStickyHeaders}
|
||||
ref={this.roomListRef}
|
||||
/>;
|
||||
|
||||
const containerClasses = classNames({
|
||||
|
|
|
@ -32,7 +32,6 @@ import SpaceStore, { UPDATE_SELECTED_SPACE, UPDATE_TOP_LEVEL_SPACES } from "../.
|
|||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
onKeyDown(ev: React.KeyboardEvent): void;
|
||||
/**
|
||||
* @returns true if a room has been selected and the search field should be cleared
|
||||
*/
|
||||
|
@ -133,11 +132,6 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
|||
this.clearInput();
|
||||
defaultDispatcher.fire(Action.FocusSendMessageComposer);
|
||||
break;
|
||||
case RoomListAction.NextRoom:
|
||||
case RoomListAction.PrevRoom:
|
||||
// we don't handle these actions here put pass the event on to the interested party (LeftPanel)
|
||||
this.props.onKeyDown(ev);
|
||||
break;
|
||||
case RoomListAction.SelectRoom: {
|
||||
const shouldClear = this.props.onSelectRoom();
|
||||
if (shouldClear) {
|
||||
|
@ -151,6 +145,10 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
public focus(): void {
|
||||
this.inputRef.current?.focus();
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const classes = classNames({
|
||||
'mx_RoomSearch': true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue