typescript src/input (#487)

This commit is contained in:
Timo 2022-08-09 11:44:46 +02:00 committed by GitHub
parent 5474693711
commit f554afd6b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 120 additions and 41 deletions

View file

@ -23,10 +23,10 @@ import classNames from "classnames";
import styles from "./ListBox.module.css"; import styles from "./ListBox.module.css";
interface ListBoxProps<T> extends AriaListBoxOptions<T> { interface ListBoxProps<T> extends AriaListBoxOptions<T> {
className: string;
optionClassName: string; optionClassName: string;
listBoxRef: React.MutableRefObject<HTMLUListElement>;
state: ListState<T>; state: ListState<T>;
className?: string;
listBoxRef?: React.MutableRefObject<HTMLUListElement>;
} }
export function ListBox<T>({ export function ListBox<T>({

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import React, { import React, {
ChangeEvent,
FC, FC,
FormEvent, FormEvent,
useCallback, useCallback,
@ -166,7 +167,9 @@ export const RegisterPage: FC = () => {
required required
name="password" name="password"
type="password" type="password"
onChange={(e) => setPassword(e.target.value)} onChange={(e: ChangeEvent<HTMLInputElement>) =>
setPassword(e.target.value)
}
value={password} value={password}
placeholder="Password" placeholder="Password"
label="Password" label="Password"
@ -177,7 +180,9 @@ export const RegisterPage: FC = () => {
required required
type="password" type="password"
name="passwordConfirmation" name="passwordConfirmation"
onChange={(e) => setPasswordConfirmation(e.target.value)} onChange={(e: ChangeEvent<HTMLInputElement>) =>
setPasswordConfirmation(e.target.value)
}
value={passwordConfirmation} value={passwordConfirmation}
placeholder="Confirm Password" placeholder="Confirm Password"
label="Confirm Password" label="Confirm Password"

View file

@ -15,42 +15,52 @@ limitations under the License.
*/ */
import { useObjectRef } from "@react-aria/utils"; import { useObjectRef } from "@react-aria/utils";
import React, { useEffect } from "react"; import React, { AllHTMLAttributes, useEffect } from "react";
import { useCallback } from "react"; import { useCallback } from "react";
import { useState } from "react"; import { useState } from "react";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { Avatar } from "../Avatar";
import { Button } from "../button";
import classNames from "classnames"; import classNames from "classnames";
import { Avatar, Size } from "../Avatar";
import { Button } from "../button";
import { ReactComponent as EditIcon } from "../icons/Edit.svg"; import { ReactComponent as EditIcon } from "../icons/Edit.svg";
import styles from "./AvatarInputField.module.css"; import styles from "./AvatarInputField.module.css";
export const AvatarInputField = forwardRef( interface Props extends AllHTMLAttributes<HTMLInputElement> {
id: string;
label: string;
avatarUrl: string;
displayName: string;
onRemoveAvatar: () => void;
}
export const AvatarInputField = forwardRef<HTMLInputElement, Props>(
( (
{ id, label, className, avatarUrl, displayName, onRemoveAvatar, ...rest }, { id, label, className, avatarUrl, displayName, onRemoveAvatar, ...rest },
ref ref
) => { ) => {
const [removed, setRemoved] = useState(false); const [removed, setRemoved] = useState(false);
const [objUrl, setObjUrl] = useState(null); const [objUrl, setObjUrl] = useState<string>(null);
const fileInputRef = useObjectRef(ref); const fileInputRef = useObjectRef(ref);
useEffect(() => { useEffect(() => {
const onChange = (e) => { const currentInput = fileInputRef.current;
if (e.target.files.length > 0) {
setObjUrl(URL.createObjectURL(e.target.files[0])); const onChange = (e: Event) => {
const inputEvent = e as unknown as React.ChangeEvent<HTMLInputElement>;
if (inputEvent.target.files.length > 0) {
setObjUrl(URL.createObjectURL(inputEvent.target.files[0]));
setRemoved(false); setRemoved(false);
} else { } else {
setObjUrl(null); setObjUrl(null);
} }
}; };
fileInputRef.current.addEventListener("change", onChange); currentInput.addEventListener("change", onChange);
return () => { return () => {
if (fileInputRef.current) { currentInput?.removeEventListener("change", onChange);
fileInputRef.current.removeEventListener("change", onChange);
}
}; };
}); });
@ -63,7 +73,7 @@ export const AvatarInputField = forwardRef(
<div className={classNames(styles.avatarInputField, className)}> <div className={classNames(styles.avatarInputField, className)}>
<div className={styles.avatarContainer}> <div className={styles.avatarContainer}>
<Avatar <Avatar
size="xl" size={Size.XL}
src={removed ? null : objUrl || avatarUrl} src={removed ? null : objUrl || avatarUrl}
fallback={displayName.slice(0, 1).toUpperCase()} fallback={displayName.slice(0, 1).toUpperCase()}
/> />

View file

@ -14,12 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { forwardRef } from "react"; import React, { ChangeEvent, forwardRef, ReactNode } from "react";
import classNames from "classnames"; import classNames from "classnames";
import styles from "./Input.module.css"; import styles from "./Input.module.css";
import { ReactComponent as CheckIcon } from "../icons/Check.svg"; import { ReactComponent as CheckIcon } from "../icons/Check.svg";
export function FieldRow({ children, rightAlign, className, ...rest }) { interface FieldRowProps {
children: ReactNode;
rightAlign?: boolean;
className?: string;
}
export function FieldRow({
children,
rightAlign,
className,
}: FieldRowProps): JSX.Element {
return ( return (
<div <div
className={classNames( className={classNames(
@ -33,11 +44,42 @@ export function FieldRow({ children, rightAlign, className, ...rest }) {
); );
} }
export function Field({ children, className, ...rest }) { interface FieldProps {
children: ReactNode;
className?: string;
}
export function Field({ children, className }: FieldProps): JSX.Element {
return <div className={classNames(styles.field, className)}>{children}</div>; return <div className={classNames(styles.field, className)}>{children}</div>;
} }
export const InputField = forwardRef( interface InputFieldProps {
label: string;
type: string;
prefix?: string;
suffix?: string;
id?: string;
checked?: boolean;
className?: string;
description?: string;
disabled?: boolean;
required?: boolean;
// this is a hack. Those variables should be part of `HTMLAttributes<HTMLInputElement> | HTMLAttributes<HTMLTextAreaElement>`
// but extending from this union type does not work
name?: string;
autoComplete?: string;
autoCorrect?: string;
autoCapitalize?: string;
value?: string;
placeholder?: string;
defaultChecked?: boolean;
onChange?: (event: ChangeEvent) => void;
}
export const InputField = forwardRef<
HTMLInputElement | HTMLTextAreaElement,
InputFieldProps
>(
( (
{ {
id, id,
@ -68,19 +110,18 @@ export const InputField = forwardRef(
{type === "textarea" ? ( {type === "textarea" ? (
<textarea <textarea
id={id} id={id}
{...rest} ref={ref as React.ForwardedRef<HTMLTextAreaElement>}
ref={ref}
type={type}
disabled={disabled} disabled={disabled}
{...rest}
/> />
) : ( ) : (
<input <input
id={id} id={id}
{...rest} ref={ref as React.ForwardedRef<HTMLInputElement>}
ref={ref}
type={type} type={type}
checked={checked} checked={checked}
disabled={disabled} disabled={disabled}
{...rest}
/> />
)} )}
@ -99,6 +140,10 @@ export const InputField = forwardRef(
} }
); );
export function ErrorMessage({ children }) { export function ErrorMessage({
children,
}: {
children: ReactNode;
}): JSX.Element {
return <p className={styles.errorMessage}>{children}</p>; return <p className={styles.errorMessage}>{children}</p>;
} }

View file

@ -15,16 +15,21 @@ limitations under the License.
*/ */
import React, { useRef } from "react"; import React, { useRef } from "react";
import { HiddenSelect, useSelect } from "@react-aria/select"; import { AriaSelectOptions, HiddenSelect, useSelect } from "@react-aria/select";
import { useButton } from "@react-aria/button"; import { useButton } from "@react-aria/button";
import { useSelectState } from "@react-stately/select"; import { useSelectState } from "@react-stately/select";
import classNames from "classnames";
import { Popover } from "../popover/Popover"; import { Popover } from "../popover/Popover";
import { ListBox } from "../ListBox"; import { ListBox } from "../ListBox";
import styles from "./SelectInput.module.css"; import styles from "./SelectInput.module.css";
import classNames from "classnames";
import { ReactComponent as ArrowDownIcon } from "../icons/ArrowDown.svg"; import { ReactComponent as ArrowDownIcon } from "../icons/ArrowDown.svg";
export function SelectInput(props) { interface Props extends AriaSelectOptions<object> {
className?: string;
}
export function SelectInput(props: Props): JSX.Element {
const state = useSelectState(props); const state = useSelectState(props);
const ref = useRef(); const ref = useRef();

View file

@ -15,22 +15,37 @@ limitations under the License.
*/ */
import React, { useCallback, useRef } from "react"; import React, { useCallback, useRef } from "react";
import styles from "./Toggle.module.css";
import { useToggleButton } from "@react-aria/button"; import { useToggleButton } from "@react-aria/button";
import classNames from "classnames"; import classNames from "classnames";
import styles from "./Toggle.module.css";
import { Field } from "./Input"; import { Field } from "./Input";
export function Toggle({ id, label, className, onChange, isSelected }) { interface Props {
const buttonRef = useRef(); id: string;
label: string;
onChange: (selected: boolean) => void;
isSelected: boolean;
className?: string;
}
export function Toggle({
id,
label,
className,
onChange,
isSelected,
}: Props): JSX.Element {
const buttonRef = useRef<HTMLButtonElement>();
const toggle = useCallback(() => { const toggle = useCallback(() => {
onChange(!isSelected); onChange(!isSelected);
}); }, [isSelected, onChange]);
const { buttonProps } = useToggleButton(
const buttonProps = useToggleButton(
{ isSelected }, { isSelected },
{ toggle }, { isSelected: isSelected, setSelected: undefined, toggle },
buttonRef buttonRef
); );
return ( return (
<Field <Field
className={classNames( className={classNames(

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { useCallback, useEffect, useState } from "react"; import React, { ChangeEvent, useCallback, useEffect, useState } from "react";
import { MatrixClient } from "matrix-js-sdk"; import { MatrixClient } from "matrix-js-sdk";
import { Button } from "../button"; import { Button } from "../button";
@ -47,7 +47,7 @@ export function ProfileModal({ client, ...rest }: Props) {
}, []); }, []);
const onChangeDisplayName = useCallback( const onChangeDisplayName = useCallback(
(e) => { (e: ChangeEvent<HTMLInputElement>) => {
setDisplayName(e.target.value); setDisplayName(e.target.value);
}, },
[setDisplayName] [setDisplayName]

View file

@ -22,7 +22,6 @@ import React, {
useRef, useRef,
createContext, createContext,
useContext, useContext,
Dispatch,
} from "react"; } from "react";
import ReactJson, { CollapsedFieldProps } from "react-json-view"; import ReactJson, { CollapsedFieldProps } from "react-json-view";
import mermaid from "mermaid"; import mermaid from "mermaid";
@ -156,7 +155,7 @@ interface SequenceDiagramViewerProps {
localUserId: string; localUserId: string;
remoteUserIds: string[]; remoteUserIds: string[];
selectedUserId: string; selectedUserId: string;
onSelectUserId: Dispatch<(prevState: undefined) => undefined>; onSelectUserId: (userId: string) => void;
events: SequenceDiagramMatrixEvent[]; events: SequenceDiagramMatrixEvent[];
} }