mirror of https://github.com/vector-im/riot-web
Just use one ref prop in Field.tsx
parent
86c6ba9dd7
commit
e5c0cdc402
|
@ -12,6 +12,8 @@ import React, {
|
||||||
RefObject,
|
RefObject,
|
||||||
createRef,
|
createRef,
|
||||||
ComponentProps,
|
ComponentProps,
|
||||||
|
MutableRefObject,
|
||||||
|
RefCallback,
|
||||||
} from "react";
|
} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
|
@ -73,13 +75,9 @@ 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> | RefCallback<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.
|
||||||
|
@ -88,9 +86,7 @@ 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> | RefCallback<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.
|
||||||
|
@ -99,9 +95,7 @@ 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> | RefCallback<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;
|
||||||
|
@ -109,9 +103,7 @@ 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> | RefCallback<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;
|
||||||
|
@ -128,7 +120,17 @@ interface IState {
|
||||||
|
|
||||||
export default class Field extends React.PureComponent<PropShapes, IState> {
|
export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||||
private readonly id: string;
|
private readonly id: string;
|
||||||
private readonly _inputRef = createRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>();
|
private readonly _inputRef: MutableRefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null> =
|
||||||
|
createRef();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When props.inputRef is a callback ref, we will pass callbackRef to the DOM element.
|
||||||
|
* This is so that other methods here can still access the DOM element via this._inputRef.
|
||||||
|
*/
|
||||||
|
private readonly callbackRef: RefCallback<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> = (node) => {
|
||||||
|
this._inputRef.current = node;
|
||||||
|
(this.props.inputRef as RefCallback<unknown>)(node);
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly defaultProps = {
|
public static readonly defaultProps = {
|
||||||
element: "input",
|
element: "input",
|
||||||
|
@ -240,7 +242,12 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get inputRef(): RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> {
|
private get inputRef(): RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> {
|
||||||
return this.props.inputRef ?? this._inputRef;
|
const inputRef = this.props.inputRef;
|
||||||
|
if (typeof inputRef === "function") {
|
||||||
|
// This is a callback ref, so return _inputRef which will point to the actual DOM element.
|
||||||
|
return this._inputRef;
|
||||||
|
}
|
||||||
|
return (inputRef ?? this._inputRef) as RefObject<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onTooltipOpenChange = (open: boolean): void => {
|
private onTooltipOpenChange = (open: boolean): void => {
|
||||||
|
@ -294,7 +301,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.props.refCallback ?? this.inputRef,
|
ref: typeof this.props.inputRef === "function" ? this.callbackRef : this.inputRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldInput = React.createElement(this.props.element, inputProps_, children);
|
const fieldInput = React.createElement(this.props.element, inputProps_, children);
|
||||||
|
|
|
@ -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, RefObject } from "react";
|
import React, { useState, FormEvent } 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, refCallback, inputRef] = useRovingTabIndex<HTMLInputElement>();
|
const [onFocus, isActive, refCallback] = 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,8 +45,7 @@ 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={inputRef as RefObject<HTMLInputElement>}
|
inputRef={refCallback}
|
||||||
refCallback={refCallback}
|
|
||||||
tabIndex={isActive ? 0 : -1}
|
tabIndex={isActive ? 0 : -1}
|
||||||
/>
|
/>
|
||||||
<RovingAccessibleButton
|
<RovingAccessibleButton
|
||||||
|
|
Loading…
Reference in New Issue