typescript src/input
(#487)
This commit is contained in:
parent
5474693711
commit
f554afd6b1
8 changed files with 120 additions and 41 deletions
|
@ -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>({
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()}
|
||||||
/>
|
/>
|
|
@ -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>;
|
||||||
}
|
}
|
|
@ -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();
|
|
@ -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(
|
|
@ -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]
|
||||||
|
|
|
@ -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[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue