Only jump to date after pressing the 'go' button (#8548)
parent
c67b41fbde
commit
97c99c6aae
|
@ -19,7 +19,6 @@ import classNames from 'classnames';
|
|||
import { debounce } from "lodash";
|
||||
|
||||
import { IFieldState, IValidationResult } from "./Validation";
|
||||
import { ComponentClass } from "../../../@types/common";
|
||||
import Tooltip from "./Tooltip";
|
||||
|
||||
// Invoke validation from user input (when typing, etc.) at most once every N ms.
|
||||
|
@ -83,7 +82,6 @@ export interface IInputProps extends IProps, InputHTMLAttributes<HTMLInputElemen
|
|||
inputRef?: RefObject<HTMLInputElement>;
|
||||
// The element to create. Defaults to "input".
|
||||
element?: "input";
|
||||
componentClass?: undefined;
|
||||
// The input's value. This is a controlled component, so the value is required.
|
||||
value: string;
|
||||
}
|
||||
|
@ -93,7 +91,6 @@ interface ISelectProps extends IProps, SelectHTMLAttributes<HTMLSelectElement> {
|
|||
inputRef?: RefObject<HTMLSelectElement>;
|
||||
// To define options for a select, use <Field><option ... /></Field>
|
||||
element: "select";
|
||||
componentClass?: undefined;
|
||||
// The select's value. This is a controlled component, so the value is required.
|
||||
value: string;
|
||||
}
|
||||
|
@ -102,7 +99,6 @@ interface ITextareaProps extends IProps, TextareaHTMLAttributes<HTMLTextAreaElem
|
|||
// The ref pass through to the textarea
|
||||
inputRef?: RefObject<HTMLTextAreaElement>;
|
||||
element: "textarea";
|
||||
componentClass?: undefined;
|
||||
// The textarea's value. This is a controlled component, so the value is required.
|
||||
value: string;
|
||||
}
|
||||
|
@ -111,8 +107,6 @@ export interface INativeOnChangeInputProps extends IProps, InputHTMLAttributes<H
|
|||
// The ref pass through to the input
|
||||
inputRef?: RefObject<HTMLInputElement>;
|
||||
element: "input";
|
||||
// The custom component to render
|
||||
componentClass: ComponentClass;
|
||||
// The input's value. This is a controlled component, so the value is required.
|
||||
value: string;
|
||||
}
|
||||
|
@ -248,7 +242,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
|||
|
||||
public render() {
|
||||
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||
const { element, componentClass, inputRef, prefixComponent, postfixComponent, className, onValidate, children,
|
||||
const { element, inputRef, prefixComponent, postfixComponent, className, onValidate, children,
|
||||
tooltipContent, forceValidity, tooltipClassName, list, validateOnBlur, validateOnChange, validateOnFocus,
|
||||
usePlaceholderAsHint, forceTooltipVisible,
|
||||
...inputProps } = this.props;
|
||||
|
@ -265,7 +259,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
|||
// Appease typescript's inference
|
||||
const inputProps_ = { ...inputProps, ref: this.inputRef, list };
|
||||
|
||||
const fieldInput = React.createElement(this.props.componentClass || this.props.element, inputProps_, children);
|
||||
const fieldInput = React.createElement(this.props.element, inputProps_, children);
|
||||
|
||||
let prefixContainer = null;
|
||||
if (prefixComponent) {
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useCombinedRefs } from "../../../hooks/useCombinedRefs";
|
||||
|
||||
interface IProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onInput'> {
|
||||
onChange?: (event: Event) => void;
|
||||
onInput?: (event: Event) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component restores the native 'onChange' and 'onInput' behavior of
|
||||
* JavaScript which have important differences for certain <input> types. This is
|
||||
* necessary because in React, the `onChange` handler behaves like the native
|
||||
* `oninput` handler and there is no way to tell the difference between an
|
||||
* `input` vs `change` event.
|
||||
*
|
||||
* via https://stackoverflow.com/a/62383569/796832 and
|
||||
* https://github.com/facebook/react/issues/9657#issuecomment-643970199
|
||||
*
|
||||
* See:
|
||||
* - https://reactjs.org/docs/dom-elements.html#onchange
|
||||
* - https://github.com/facebook/react/issues/3964
|
||||
* - https://github.com/facebook/react/issues/9657
|
||||
* - https://github.com/facebook/react/issues/14857
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* We use this for the <input type="date"> date picker so we can distinguish from
|
||||
* a final date picker selection (onChange) vs navigating the months in the date
|
||||
* picker (onInput).
|
||||
*
|
||||
* This is also potentially useful for <input type="range" /> because the native
|
||||
* events behave in such a way that moving the slider around triggers an onInput
|
||||
* event and releasing it triggers onChange.
|
||||
*/
|
||||
const NativeOnChangeInput: React.FC<IProps> = React.forwardRef((props: IProps, ref) => {
|
||||
const registerCallbacks = (input: HTMLInputElement | null) => {
|
||||
if (input) {
|
||||
input.onchange = props.onChange;
|
||||
input.oninput = props.onInput;
|
||||
}
|
||||
};
|
||||
|
||||
return <input
|
||||
ref={useCombinedRefs(registerCallbacks, ref)}
|
||||
{...props}
|
||||
// These are just here so we don't get a read-only input warning from React
|
||||
onChange={() => {}}
|
||||
onInput={() => {}}
|
||||
/>;
|
||||
});
|
||||
|
||||
export default NativeOnChangeInput;
|
|
@ -18,7 +18,6 @@ import React, { useState, FormEvent } from 'react';
|
|||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Field from "../elements/Field";
|
||||
import NativeOnChangeInput from "../elements/NativeOnChangeInput";
|
||||
import { RovingAccessibleButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
|
||||
|
||||
interface IProps {
|
||||
|
@ -34,41 +33,11 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
|||
const dateDefaultValue = `${year}-${month}-${day}`;
|
||||
|
||||
const [dateValue, setDateValue] = useState(dateDefaultValue);
|
||||
// Whether or not to automatically navigate to the given date after someone
|
||||
// selects a day in the date picker. We want to disable this after someone
|
||||
// starts manually typing in the input instead of picking.
|
||||
const [navigateOnDatePickerSelection, setNavigateOnDatePickerSelection] = useState(true);
|
||||
|
||||
// Since we're using NativeOnChangeInput with native JavaScript behavior, this
|
||||
// tracks the date value changes as they come in.
|
||||
const onDateValueInput = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
setDateValue(e.target.value);
|
||||
};
|
||||
|
||||
// Since we're using NativeOnChangeInput with native JavaScript behavior, the change
|
||||
// event listener will trigger when a date is picked from the date picker
|
||||
// or when the text is fully filled out. In order to not trigger early
|
||||
// as someone is typing out a date, we need to disable when we see keydowns.
|
||||
const onDateValueChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
setDateValue(e.target.value);
|
||||
|
||||
// Don't auto navigate if they were manually typing out a date
|
||||
if (navigateOnDatePickerSelection) {
|
||||
onDatePicked(dateValue);
|
||||
}
|
||||
};
|
||||
|
||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
||||
|
||||
const onDateInputKeyDown = (e: React.KeyboardEvent): void => {
|
||||
// When we see someone manually typing out a date, disable the auto
|
||||
// submit on change.
|
||||
setNavigateOnDatePickerSelection(false);
|
||||
};
|
||||
|
||||
const onDateValueInput = (ev: React.ChangeEvent<HTMLInputElement>) => setDateValue(ev.target.value);
|
||||
const onJumpToDateSubmit = (ev: FormEvent): void => {
|
||||
ev.preventDefault();
|
||||
|
||||
onDatePicked(dateValue);
|
||||
};
|
||||
|
||||
|
@ -79,11 +48,9 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
|||
>
|
||||
<span className="mx_JumpToDatePicker_label">{ _t("Jump to date") }</span>
|
||||
<Field
|
||||
componentClass={NativeOnChangeInput}
|
||||
element="input"
|
||||
type="date"
|
||||
onChange={onDateValueChange}
|
||||
onInput={onDateValueInput}
|
||||
onKeyDown={onDateInputKeyDown}
|
||||
value={dateValue}
|
||||
className="mx_JumpToDatePicker_datePicker"
|
||||
label={_t("Pick a date to jump to")}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
|
||||
// Takes in multiple React refs and combines them to reference the same target/element
|
||||
//
|
||||
// via https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd
|
||||
export const useCombinedRefs = (...refs) => {
|
||||
const targetRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
refs.forEach(ref => {
|
||||
if (!ref) return;
|
||||
|
||||
if (typeof ref === 'function') {
|
||||
ref(targetRef.current);
|
||||
} else {
|
||||
ref.current = targetRef.current;
|
||||
}
|
||||
});
|
||||
}, [refs]);
|
||||
|
||||
return targetRef;
|
||||
};
|
Loading…
Reference in New Issue