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…
	
	Add table
		Add a link
		
	
		Reference in a new issue