mirror of https://github.com/vector-im/riot-web
Make corresponding changes in consumers
parent
0faf298e05
commit
86c6ba9dd7
|
@ -114,7 +114,7 @@ const Tile: React.FC<ITileProps> = ({
|
||||||
(room.room_type === RoomType.Space ? _t("common|unnamed_space") : _t("common|unnamed_room"));
|
(room.room_type === RoomType.Space ? _t("common|unnamed_space") : _t("common|unnamed_room"));
|
||||||
|
|
||||||
const [showChildren, toggleShowChildren] = useStateToggle(true);
|
const [showChildren, toggleShowChildren] = useStateToggle(true);
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex();
|
const [onFocus, isActive, ref, nodeRef] = useRovingTabIndex();
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
|
|
||||||
const onPreviewClick = (ev: ButtonEvent): void => {
|
const onPreviewClick = (ev: ButtonEvent): void => {
|
||||||
|
@ -288,7 +288,7 @@ const Tile: React.FC<ITileProps> = ({
|
||||||
case KeyBindingAction.ArrowLeft:
|
case KeyBindingAction.ArrowLeft:
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
ref.current?.focus();
|
nodeRef.current?.focus();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -315,7 +315,7 @@ const Tile: React.FC<ITileProps> = ({
|
||||||
case KeyBindingAction.ArrowRight:
|
case KeyBindingAction.ArrowRight:
|
||||||
handled = true;
|
handled = true;
|
||||||
if (showChildren) {
|
if (showChildren) {
|
||||||
const childSection = ref.current?.nextElementSibling;
|
const childSection = nodeRef.current?.nextElementSibling;
|
||||||
childSection?.querySelector<HTMLDivElement>(".mx_SpaceHierarchy_roomTile")?.focus();
|
childSection?.querySelector<HTMLDivElement>(".mx_SpaceHierarchy_roomTile")?.focus();
|
||||||
} else {
|
} else {
|
||||||
toggleShowChildren();
|
toggleShowChildren();
|
||||||
|
@ -790,7 +790,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
|
||||||
const onKeyDown = (ev: KeyboardEvent, state: IState): void => {
|
const onKeyDown = (ev: KeyboardEvent, state: IState): void => {
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||||
if (action === KeyBindingAction.ArrowDown && ev.currentTarget.classList.contains("mx_SpaceHierarchy_search")) {
|
if (action === KeyBindingAction.ArrowDown && ev.currentTarget.classList.contains("mx_SpaceHierarchy_search")) {
|
||||||
state.refs[0]?.current?.focus();
|
state.nodes[0]?.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -294,7 +294,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case KeyBindingAction.Enter: {
|
case KeyBindingAction.Enter: {
|
||||||
state.activeRef?.current?.querySelector<HTMLButtonElement>(".mx_ForwardList_sendButton")?.click();
|
state.activeNode?.querySelector<HTMLButtonElement>(".mx_ForwardList_sendButton")?.click();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,13 +347,13 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
|
||||||
onSearch={(query: string): void => {
|
onSearch={(query: string): void => {
|
||||||
setQuery(query);
|
setQuery(query);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const ref = context.state.refs[0];
|
const node = context.state.nodes[0];
|
||||||
if (ref) {
|
if (node) {
|
||||||
context.dispatch({
|
context.dispatch({
|
||||||
type: Type.SetFocus,
|
type: Type.SetFocus,
|
||||||
payload: { ref },
|
payload: { node },
|
||||||
});
|
});
|
||||||
ref.current?.scrollIntoView?.({
|
node?.scrollIntoView?.({
|
||||||
block: "nearest",
|
block: "nearest",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -361,7 +361,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
|
||||||
}}
|
}}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
onKeyDown={onKeyDownHandler}
|
onKeyDown={onKeyDownHandler}
|
||||||
aria-activedescendant={context.state.activeRef?.current?.id}
|
aria-activedescendant={context.state.activeNode?.id}
|
||||||
aria-owns="mx_ForwardDialog_resultsList"
|
aria-owns="mx_ForwardDialog_resultsList"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -7,13 +7,12 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React, { ReactNode, RefObject } from "react";
|
import React, { ReactNode } from "react";
|
||||||
|
|
||||||
import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex";
|
import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex";
|
||||||
import AccessibleButton, { ButtonEvent } from "../../elements/AccessibleButton";
|
import AccessibleButton, { ButtonEvent } from "../../elements/AccessibleButton";
|
||||||
|
|
||||||
interface OptionProps {
|
interface OptionProps {
|
||||||
inputRef?: RefObject<HTMLLIElement>;
|
|
||||||
endAdornment?: ReactNode;
|
endAdornment?: ReactNode;
|
||||||
id?: string;
|
id?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -21,8 +20,8 @@ interface OptionProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Option: React.FC<OptionProps> = ({ inputRef, children, endAdornment, className, ...props }) => {
|
export const Option: React.FC<OptionProps> = ({ children, endAdornment, className, ...props }) => {
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLLIElement>(inputRef);
|
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLLIElement>();
|
||||||
return (
|
return (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import { normalize } from "matrix-js-sdk/src/utils";
|
import { normalize } from "matrix-js-sdk/src/utils";
|
||||||
import React, { ChangeEvent, RefObject, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
import React, { ChangeEvent, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
import sanitizeHtml from "sanitize-html";
|
||||||
|
|
||||||
import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts";
|
import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts";
|
||||||
|
@ -90,8 +90,8 @@ interface IProps {
|
||||||
onFinished(): void;
|
onFinished(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refIsForRecentlyViewed(ref?: RefObject<HTMLElement>): boolean {
|
function nodeIsForRecentlyViewed(node?: HTMLElement): boolean {
|
||||||
return ref?.current?.id?.startsWith("mx_SpotlightDialog_button_recentlyViewed_") === true;
|
return node?.id?.startsWith("mx_SpotlightDialog_button_recentlyViewed_") === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoomTypes(filter: Filter | null): Set<RoomType | null> {
|
function getRoomTypes(filter: Filter | null): Set<RoomType | null> {
|
||||||
|
@ -498,13 +498,13 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const ref = rovingContext.state.refs[0];
|
const node = rovingContext.state.nodes[0];
|
||||||
if (ref) {
|
if (node) {
|
||||||
rovingContext.dispatch({
|
rovingContext.dispatch({
|
||||||
type: Type.SetFocus,
|
type: Type.SetFocus,
|
||||||
payload: { ref },
|
payload: { node },
|
||||||
});
|
});
|
||||||
ref.current?.scrollIntoView?.({
|
node?.scrollIntoView?.({
|
||||||
block: "nearest",
|
block: "nearest",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1128,7 +1128,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ref: RefObject<HTMLElement> | undefined;
|
let node: HTMLElement | undefined;
|
||||||
const accessibilityAction = getKeyBindingsManager().getAccessibilityAction(ev);
|
const accessibilityAction = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||||
switch (accessibilityAction) {
|
switch (accessibilityAction) {
|
||||||
case KeyBindingAction.Escape:
|
case KeyBindingAction.Escape:
|
||||||
|
@ -1141,20 +1141,20 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
if (rovingContext.state.activeRef && rovingContext.state.refs.length > 0) {
|
if (rovingContext.state.activeNode && rovingContext.state.nodes.length > 0) {
|
||||||
let refs = rovingContext.state.refs;
|
let nodes = rovingContext.state.nodes;
|
||||||
if (!query && !filter !== null) {
|
if (!query && !filter !== null) {
|
||||||
// If the current selection is not in the recently viewed row then only include the
|
// If the current selection is not in the recently viewed row then only include the
|
||||||
// first recently viewed so that is the target when the user is switching into recently viewed.
|
// first recently viewed so that is the target when the user is switching into recently viewed.
|
||||||
const keptRecentlyViewedRef = refIsForRecentlyViewed(rovingContext.state.activeRef)
|
const keptRecentlyViewedRef = nodeIsForRecentlyViewed(rovingContext.state.activeNode)
|
||||||
? rovingContext.state.activeRef
|
? rovingContext.state.activeNode
|
||||||
: refs.find(refIsForRecentlyViewed);
|
: nodes.find(nodeIsForRecentlyViewed);
|
||||||
// exclude all other recently viewed items from the list so up/down arrows skip them
|
// exclude all other recently viewed items from the list so up/down arrows skip them
|
||||||
refs = refs.filter((ref) => ref === keptRecentlyViewedRef || !refIsForRecentlyViewed(ref));
|
nodes = nodes.filter((ref) => ref === keptRecentlyViewedRef || !nodeIsForRecentlyViewed(ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
const idx = refs.indexOf(rovingContext.state.activeRef);
|
const idx = nodes.indexOf(rovingContext.state.activeNode);
|
||||||
ref = findSiblingElement(refs, idx + (accessibilityAction === KeyBindingAction.ArrowUp ? -1 : 1));
|
node = findSiblingElement(nodes, idx + (accessibilityAction === KeyBindingAction.ArrowUp ? -1 : 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1164,27 +1164,30 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
if (
|
if (
|
||||||
!query &&
|
!query &&
|
||||||
!filter !== null &&
|
!filter !== null &&
|
||||||
rovingContext.state.activeRef &&
|
rovingContext.state.activeNode &&
|
||||||
rovingContext.state.refs.length > 0 &&
|
rovingContext.state.nodes.length > 0 &&
|
||||||
refIsForRecentlyViewed(rovingContext.state.activeRef)
|
nodeIsForRecentlyViewed(rovingContext.state.activeNode)
|
||||||
) {
|
) {
|
||||||
// we only intercept left/right arrows when the field is empty, and they'd do nothing anyway
|
// we only intercept left/right arrows when the field is empty, and they'd do nothing anyway
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const refs = rovingContext.state.refs.filter(refIsForRecentlyViewed);
|
const nodes = rovingContext.state.nodes.filter(nodeIsForRecentlyViewed);
|
||||||
const idx = refs.indexOf(rovingContext.state.activeRef);
|
const idx = nodes.indexOf(rovingContext.state.activeNode);
|
||||||
ref = findSiblingElement(refs, idx + (accessibilityAction === KeyBindingAction.ArrowLeft ? -1 : 1));
|
node = findSiblingElement(
|
||||||
|
nodes,
|
||||||
|
idx + (accessibilityAction === KeyBindingAction.ArrowLeft ? -1 : 1),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref) {
|
if (node) {
|
||||||
rovingContext.dispatch({
|
rovingContext.dispatch({
|
||||||
type: Type.SetFocus,
|
type: Type.SetFocus,
|
||||||
payload: { ref },
|
payload: { node },
|
||||||
});
|
});
|
||||||
ref.current?.scrollIntoView({
|
node?.scrollIntoView({
|
||||||
block: "nearest",
|
block: "nearest",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1204,12 +1207,12 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
case KeyBindingAction.Enter:
|
case KeyBindingAction.Enter:
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
rovingContext.state.activeRef?.current?.click();
|
rovingContext.state.activeNode?.click();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeDescendant = rovingContext.state.activeRef?.current?.id;
|
const activeDescendant = rovingContext.state.activeNode?.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -73,9 +73,13 @@ interface IProps {
|
||||||
// All other props pass through to the <input>.
|
// All other props pass through to the <input>.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RefCallback<T extends HTMLElement> = (node: T | null) => void;
|
||||||
|
|
||||||
export interface IInputProps extends IProps, InputHTMLAttributes<HTMLInputElement> {
|
export interface IInputProps extends IProps, InputHTMLAttributes<HTMLInputElement> {
|
||||||
// The ref pass through to the input
|
// The ref pass through to the input
|
||||||
inputRef?: RefObject<HTMLInputElement>;
|
inputRef?: RefObject<HTMLInputElement>;
|
||||||
|
// Ref callback that will be attached to the input. This takes precedence over inputRef.
|
||||||
|
refCallback?: RefCallback<HTMLInputElement>;
|
||||||
// The element to create. Defaults to "input".
|
// The element to create. Defaults to "input".
|
||||||
element: "input";
|
element: "input";
|
||||||
// The input's value. This is a controlled component, so the value is required.
|
// The input's value. This is a controlled component, so the value is required.
|
||||||
|
@ -85,6 +89,8 @@ export interface IInputProps extends IProps, InputHTMLAttributes<HTMLInputElemen
|
||||||
interface ISelectProps extends IProps, SelectHTMLAttributes<HTMLSelectElement> {
|
interface ISelectProps extends IProps, SelectHTMLAttributes<HTMLSelectElement> {
|
||||||
// The ref pass through to the select
|
// The ref pass through to the select
|
||||||
inputRef?: RefObject<HTMLSelectElement>;
|
inputRef?: RefObject<HTMLSelectElement>;
|
||||||
|
// Ref callback that will be attached to the input. This takes precedence over inputRef.
|
||||||
|
refCallback?: RefCallback<HTMLSelectElement>;
|
||||||
// To define options for a select, use <Field><option ... /></Field>
|
// To define options for a select, use <Field><option ... /></Field>
|
||||||
element: "select";
|
element: "select";
|
||||||
// The select's value. This is a controlled component, so the value is required.
|
// The select's value. This is a controlled component, so the value is required.
|
||||||
|
@ -94,6 +100,8 @@ interface ISelectProps extends IProps, SelectHTMLAttributes<HTMLSelectElement> {
|
||||||
interface ITextareaProps extends IProps, TextareaHTMLAttributes<HTMLTextAreaElement> {
|
interface ITextareaProps extends IProps, TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||||
// The ref pass through to the textarea
|
// The ref pass through to the textarea
|
||||||
inputRef?: RefObject<HTMLTextAreaElement>;
|
inputRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
// Ref callback that will be attached to the input. This takes precedence over inputRef.
|
||||||
|
refCallback?: RefCallback<HTMLTextAreaElement>;
|
||||||
element: "textarea";
|
element: "textarea";
|
||||||
// The textarea's value. This is a controlled component, so the value is required.
|
// The textarea's value. This is a controlled component, so the value is required.
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -102,6 +110,8 @@ interface ITextareaProps extends IProps, TextareaHTMLAttributes<HTMLTextAreaElem
|
||||||
export interface INativeOnChangeInputProps extends IProps, InputHTMLAttributes<HTMLInputElement> {
|
export interface INativeOnChangeInputProps extends IProps, InputHTMLAttributes<HTMLInputElement> {
|
||||||
// The ref pass through to the input
|
// The ref pass through to the input
|
||||||
inputRef?: RefObject<HTMLInputElement>;
|
inputRef?: RefObject<HTMLInputElement>;
|
||||||
|
// Ref callback that will be attached to the input. This takes precedence over inputRef.
|
||||||
|
refCallback?: RefCallback<HTMLInputElement>;
|
||||||
element: "input";
|
element: "input";
|
||||||
// The input's value. This is a controlled component, so the value is required.
|
// The input's value. This is a controlled component, so the value is required.
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -284,7 +294,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||||
const inputProps_: React.HTMLAttributes<HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement> &
|
const inputProps_: React.HTMLAttributes<HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement> &
|
||||||
React.ClassAttributes<HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement> = {
|
React.ClassAttributes<HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement> = {
|
||||||
...inputProps,
|
...inputProps,
|
||||||
ref: this.inputRef,
|
ref: this.props.refCallback ?? this.inputRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldInput = React.createElement(this.props.element, inputProps_, children);
|
const fieldInput = React.createElement(this.props.element, inputProps_, children);
|
||||||
|
|
|
@ -16,7 +16,7 @@ export enum CheckboxStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||||
inputRef?: React.RefObject<HTMLInputElement>;
|
inputRef?: (node: HTMLInputElement | null) => void;
|
||||||
kind?: CheckboxStyle;
|
kind?: CheckboxStyle;
|
||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import React from "react";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
|
|
||||||
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||||
inputRef?: React.RefObject<HTMLInputElement>;
|
inputRef?: (node: HTMLInputElement | null) => void;
|
||||||
outlined?: boolean;
|
outlined?: boolean;
|
||||||
// If true (default), the children will be contained within a <label> element
|
// If true (default), the children will be contained within a <label> element
|
||||||
// If false, they'll be in a div. Putting interactive components that have labels
|
// If false, they'll be in a div. Putting interactive components that have labels
|
||||||
|
|
|
@ -154,22 +154,22 @@ class EmojiPicker extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private keyboardNavigation(ev: React.KeyboardEvent, state: RovingState, dispatch: Dispatch<RovingAction>): void {
|
private keyboardNavigation(ev: React.KeyboardEvent, state: RovingState, dispatch: Dispatch<RovingAction>): void {
|
||||||
const node = state.activeRef?.current;
|
const node = state.activeNode?.current;
|
||||||
const parent = node?.parentElement;
|
const parent = node?.parentElement;
|
||||||
if (!parent || !state.activeRef) return;
|
if (!parent || !state.activeNode) return;
|
||||||
const rowIndex = Array.from(parent.children).indexOf(node);
|
const rowIndex = Array.from(parent.children).indexOf(node);
|
||||||
const refIndex = state.refs.indexOf(state.activeRef);
|
const refIndex = state.nodes.indexOf(state.activeNode);
|
||||||
|
|
||||||
let focusRef: Ref | undefined;
|
let focusRef: Ref | undefined;
|
||||||
let newParent: HTMLElement | undefined;
|
let newParent: HTMLElement | undefined;
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.ARROW_LEFT:
|
case Key.ARROW_LEFT:
|
||||||
focusRef = state.refs[refIndex - 1];
|
focusRef = state.nodes[refIndex - 1];
|
||||||
newParent = focusRef?.current?.parentElement ?? undefined;
|
newParent = focusRef?.current?.parentElement ?? undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.ARROW_RIGHT:
|
case Key.ARROW_RIGHT:
|
||||||
focusRef = state.refs[refIndex + 1];
|
focusRef = state.nodes[refIndex + 1];
|
||||||
newParent = focusRef?.current?.parentElement ?? undefined;
|
newParent = focusRef?.current?.parentElement ?? undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -178,11 +178,11 @@ class EmojiPicker extends React.Component<IProps, IState> {
|
||||||
// For up/down we find the prev/next parent by inspecting the refs either side of our row
|
// For up/down we find the prev/next parent by inspecting the refs either side of our row
|
||||||
const ref =
|
const ref =
|
||||||
ev.key === Key.ARROW_UP
|
ev.key === Key.ARROW_UP
|
||||||
? state.refs[refIndex - rowIndex - 1]
|
? state.nodes[refIndex - rowIndex - 1]
|
||||||
: state.refs[refIndex - rowIndex + EMOJIS_PER_ROW];
|
: state.nodes[refIndex - rowIndex + EMOJIS_PER_ROW];
|
||||||
newParent = ref?.current?.parentElement ?? undefined;
|
newParent = ref?.current?.parentElement ?? undefined;
|
||||||
const newTarget = newParent?.children[clamp(rowIndex, 0, newParent.children.length - 1)];
|
const newTarget = newParent?.children[clamp(rowIndex, 0, newParent.children.length - 1)];
|
||||||
focusRef = state.refs.find((r) => r.current === newTarget);
|
focusRef = state.nodes.find((r) => r.current === newTarget);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onKeyDown = (ev: React.KeyboardEvent, state: RovingState, dispatch: Dispatch<RovingAction>): void => {
|
private onKeyDown = (ev: React.KeyboardEvent, state: RovingState, dispatch: Dispatch<RovingAction>): void => {
|
||||||
if (
|
if (
|
||||||
state.activeRef?.current &&
|
state.activeNode?.current &&
|
||||||
[Key.ARROW_DOWN, Key.ARROW_RIGHT, Key.ARROW_LEFT, Key.ARROW_UP].includes(ev.key)
|
[Key.ARROW_DOWN, Key.ARROW_RIGHT, Key.ARROW_LEFT, Key.ARROW_UP].includes(ev.key)
|
||||||
) {
|
) {
|
||||||
this.keyboardNavigation(ev, state, dispatch);
|
this.keyboardNavigation(ev, state, dispatch);
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Search extends React.PureComponent<IProps> {
|
||||||
onChange={(ev) => this.props.onChange(ev.target.value)}
|
onChange={(ev) => this.props.onChange(ev.target.value)}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
ref={this.inputRef}
|
ref={this.inputRef}
|
||||||
aria-activedescendant={this.context.state.activeRef?.current?.id}
|
aria-activedescendant={this.context.state.activeNode?.id}
|
||||||
aria-controls="mx_EmojiPicker_body"
|
aria-controls="mx_EmojiPicker_body"
|
||||||
aria-haspopup="grid"
|
aria-haspopup="grid"
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, FormEvent } from "react";
|
import React, { useState, FormEvent, RefObject } from "react";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
|
@ -23,7 +23,7 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
||||||
const dateInputDefaultValue = formatDateForInput(date);
|
const dateInputDefaultValue = formatDateForInput(date);
|
||||||
|
|
||||||
const [dateValue, setDateValue] = useState(dateInputDefaultValue);
|
const [dateValue, setDateValue] = useState(dateInputDefaultValue);
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
const [onFocus, isActive, refCallback, inputRef] = useRovingTabIndex<HTMLInputElement>();
|
||||||
|
|
||||||
const onDateValueInput = (ev: React.ChangeEvent<HTMLInputElement>): void => setDateValue(ev.target.value);
|
const onDateValueInput = (ev: React.ChangeEvent<HTMLInputElement>): void => setDateValue(ev.target.value);
|
||||||
const onJumpToDateSubmit = (ev: FormEvent): void => {
|
const onJumpToDateSubmit = (ev: FormEvent): void => {
|
||||||
|
@ -45,7 +45,8 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
||||||
className="mx_JumpToDatePicker_datePicker"
|
className="mx_JumpToDatePicker_datePicker"
|
||||||
label={_t("room|jump_to_date_prompt")}
|
label={_t("room|jump_to_date_prompt")}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
inputRef={ref}
|
inputRef={inputRef as RefObject<HTMLInputElement>}
|
||||||
|
refCallback={refCallback}
|
||||||
tabIndex={isActive ? 0 : -1}
|
tabIndex={isActive ? 0 : -1}
|
||||||
/>
|
/>
|
||||||
<RovingAccessibleButton
|
<RovingAccessibleButton
|
||||||
|
|
Loading…
Reference in New Issue