From d6c1c9f2c9eddd7d6e4e91b2a9de71cfd9db6b73 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 19 Aug 2023 02:13:26 +0800 Subject: ... --- FrontEnd/src/views/common/BlobImage.tsx | 33 +++-- FrontEnd/src/views/common/ImageCropper.tsx | 30 +++-- FrontEnd/src/views/common/button/ButtonRowV2.tsx | 143 +++++++++++++++++++++ FrontEnd/src/views/common/button/IconButton.tsx | 1 + FrontEnd/src/views/common/button/index.tsx | 10 +- .../src/views/common/dialog/DialogContainer.tsx | 71 ++++++---- 6 files changed, 236 insertions(+), 52 deletions(-) create mode 100644 FrontEnd/src/views/common/button/ButtonRowV2.tsx (limited to 'FrontEnd/src/views') diff --git a/FrontEnd/src/views/common/BlobImage.tsx b/FrontEnd/src/views/common/BlobImage.tsx index 5e050ebe..259c2210 100644 --- a/FrontEnd/src/views/common/BlobImage.tsx +++ b/FrontEnd/src/views/common/BlobImage.tsx @@ -1,27 +1,26 @@ -import * as React from "react"; +import { ComponentPropsWithoutRef, useState, useEffect } from "react"; -const BlobImage: React.FC< - Omit, "src"> & { - blob?: Blob | unknown; - } -> = (props) => { - const { blob, ...otherProps } = props; +type BlobImageProps = Omit, "src"> & { + imgRef?: React.Ref; + src?: Blob | string | null; +}; + +export default function BlobImage(props: BlobImageProps) { + const { imgRef, src, ...otherProps } = props; - const [url, setUrl] = React.useState(undefined); + const [url, setUrl] = useState(undefined); - React.useEffect(() => { - if (blob instanceof Blob) { - const url = URL.createObjectURL(blob); + useEffect(() => { + if (src instanceof Blob) { + const url = URL.createObjectURL(src); setUrl(url); return () => { URL.revokeObjectURL(url); }; } else { - setUrl(undefined); + setUrl(src); } - }, [blob]); - - return ; -}; + }, [src]); -export default BlobImage; + return ; +} diff --git a/FrontEnd/src/views/common/ImageCropper.tsx b/FrontEnd/src/views/common/ImageCropper.tsx index 04e17415..fcab74b0 100644 --- a/FrontEnd/src/views/common/ImageCropper.tsx +++ b/FrontEnd/src/views/common/ImageCropper.tsx @@ -4,6 +4,7 @@ import classnames from "classnames"; import { UiLogicError } from "@/common"; import "./ImageCropper.css"; +import BlobImage from "./BlobImage"; export interface Clip { left: number; @@ -33,17 +34,17 @@ interface ImageCropperSavedState { export interface ImageCropperProps { clip: Clip | null; - imageUrl: string; + image: string | Blob; onChange: (clip: Clip) => void; imageElementCallback?: (element: HTMLImageElement | null) => void; className?: string; } const ImageCropper = (props: ImageCropperProps): React.ReactElement => { - const { clip, imageUrl, onChange, imageElementCallback, className } = props; + const { clip, image, onChange, imageElementCallback, className } = props; const [oldState, setOldState] = React.useState( - null + null, ); const [imageInfo, setImageInfo] = React.useState(null); @@ -71,7 +72,7 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { imageElementCallback(null); } }, - [imageElementCallback] + [imageElementCallback], ); const onImageLoad = React.useCallback( @@ -93,7 +94,7 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { imageElementCallback(img); } }, - [onChange, imageElementCallback] + [onChange, imageElementCallback], ); const onPointerDown = React.useCallback( @@ -107,7 +108,7 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { pointerId: e.pointerId, }); }, - [oldState, c] + [oldState, c], ); const onPointerUp = React.useCallback( @@ -116,7 +117,7 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { e.currentTarget.releasePointerCapture(e.pointerId); setOldState(null); }, - [oldState] + [oldState], ); const onPointerMove = React.useCallback( @@ -153,7 +154,7 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { onChange({ left: newRatio.x, top: newRatio.y, width: oldClip.width }); }, - [oldState, onChange] + [oldState, onChange], ); const onHandlerPointerMove = React.useCallback( @@ -198,7 +199,7 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { onChange({ left: oldClip.left, top: oldClip.top, width: newWidth }); }, - [imageInfo, oldState, onChange] + [imageInfo, oldState, onChange], ); const toPercentage = (n: number): string => `${n}%`; @@ -229,7 +230,12 @@ const ImageCropper = (props: ImageCropperProps): React.ReactElement => { className={classnames("image-cropper-container", className)} style={containerStyle} > - to crop +
{ return new Promise((resolve, reject) => { const naturalSize = { @@ -292,7 +298,7 @@ export function applyClipToImage( 0, 0, clipArea.length, - clipArea.length + clipArea.length, ); canvas.toBlob((blob) => { diff --git a/FrontEnd/src/views/common/button/ButtonRowV2.tsx b/FrontEnd/src/views/common/button/ButtonRowV2.tsx new file mode 100644 index 00000000..3467ad52 --- /dev/null +++ b/FrontEnd/src/views/common/button/ButtonRowV2.tsx @@ -0,0 +1,143 @@ +import { ComponentPropsWithoutRef, Ref } from "react"; +import classNames from "classnames"; + +import Button from "./Button"; +import FlatButton from "./FlatButton"; +import IconButton from "./IconButton"; +import LoadingButton from "./LoadingButton"; + +import "./ButtonRow.css"; +import { Text, ThemeColor } from "../common"; + +interface ButtonRowV2ButtonBase { + key: string | number; + action?: "primary" | "secondary"; + color?: ThemeColor; + disabled?: boolean; + onClick?: () => void; +} + +interface ButtonRowV2ButtonWithNoType extends ButtonRowV2ButtonBase { + type?: undefined | null; + text: Text; + outline?: boolean; + props?: ComponentPropsWithoutRef; +} + +interface ButtonRowV2NormalButton extends ButtonRowV2ButtonBase { + type: "normal"; + text: Text; + outline?: boolean; + props?: ComponentPropsWithoutRef; +} + +interface ButtonRowV2FlatButton extends ButtonRowV2ButtonBase { + type: "flat"; + text: Text; + props?: ComponentPropsWithoutRef; +} + +interface ButtonRowV2IconButton extends ButtonRowV2ButtonBase { + type: "icon"; + icon: string; + props?: ComponentPropsWithoutRef; +} + +interface ButtonRowV2LoadingButton extends ButtonRowV2ButtonBase { + type: "loading"; + text: Text; + loading?: boolean; + props?: ComponentPropsWithoutRef; +} + +type ButtonRowV2Button = + | ButtonRowV2ButtonWithNoType + | ButtonRowV2NormalButton + | ButtonRowV2FlatButton + | ButtonRowV2IconButton + | ButtonRowV2LoadingButton; + +interface ButtonRowV2Props { + className?: string; + containerRef?: Ref; + buttons: ButtonRowV2Button[]; + buttonsClassName?: string; +} + +export default function ButtonRowV2({ + className, + containerRef, + buttons, + buttonsClassName, +}: ButtonRowV2Props) { + return ( +
+ {buttons.map((button) => { + const { key, action, color, disabled, onClick } = button; + + const realAction = action ?? "primary"; + const realColor = + color ?? (realAction === "primary" ? "primary" : "secondary"); + + const commonProps = { key, color: realColor, disabled, onClick }; + const newClassName = classNames( + button.props?.className, + buttonsClassName, + ); + + switch (button.type) { + case null: + case undefined: + case "normal": { + const { text, outline, props } = button; + return ( +
+ ); +} diff --git a/FrontEnd/src/views/common/button/IconButton.tsx b/FrontEnd/src/views/common/button/IconButton.tsx index 60050e0d..126f4263 100644 --- a/FrontEnd/src/views/common/button/IconButton.tsx +++ b/FrontEnd/src/views/common/button/IconButton.tsx @@ -9,6 +9,7 @@ interface IconButtonProps extends ComponentPropsWithoutRef<"i"> { icon: string; color?: ThemeColor; large?: boolean; + disabled?: boolean; // TODO: Not implemented } export default function IconButton(props: IconButtonProps) { diff --git a/FrontEnd/src/views/common/button/index.tsx b/FrontEnd/src/views/common/button/index.tsx index 73038849..b5aa5470 100644 --- a/FrontEnd/src/views/common/button/index.tsx +++ b/FrontEnd/src/views/common/button/index.tsx @@ -3,5 +3,13 @@ import FlatButton from "./FlatButton"; import IconButton from "./IconButton"; import LoadingButton from "./LoadingButton"; import ButtonRow from "./ButtonRow"; +import ButtonRowV2 from "./ButtonRowV2"; -export { Button, FlatButton, IconButton, LoadingButton, ButtonRow }; +export { + Button, + FlatButton, + IconButton, + LoadingButton, + ButtonRow, + ButtonRowV2, +}; diff --git a/FrontEnd/src/views/common/dialog/DialogContainer.tsx b/FrontEnd/src/views/common/dialog/DialogContainer.tsx index b0a87ea5..afee2669 100644 --- a/FrontEnd/src/views/common/dialog/DialogContainer.tsx +++ b/FrontEnd/src/views/common/dialog/DialogContainer.tsx @@ -2,11 +2,11 @@ import { ComponentProps, Ref, ReactNode } from "react"; import classNames from "classnames"; import { ThemeColor, Text, useC } from "../common"; -import { ButtonRow } from "../button"; +import { ButtonRow, ButtonRowV2 } from "../button"; import "./DialogContainer.css"; -interface DialogContainerProps { +interface DialogContainerBaseProps { className?: string; title: Text; titleColor?: ThemeColor; @@ -14,25 +14,37 @@ interface DialogContainerProps { titleRef?: Ref; bodyContainerClassName?: string; bodyContainerRef?: Ref; - buttons: ComponentProps["buttons"]; buttonsClassName?: string; buttonsContainerRef?: ComponentProps["containerRef"]; children: ReactNode; } -export default function DialogContainer({ - className, - title, - titleColor, - titleClassName, - titleRef, - bodyContainerClassName, - bodyContainerRef, - buttons, - buttonsClassName, - buttonsContainerRef, - children, -}: DialogContainerProps) { +interface DialogContainerWithButtonsProps extends DialogContainerBaseProps { + buttons: ComponentProps["buttons"]; +} + +interface DialogContainerWithButtonsV2Props extends DialogContainerBaseProps { + buttonsV2: ComponentProps["buttons"]; +} + +type DialogContainerProps = + | DialogContainerWithButtonsProps + | DialogContainerWithButtonsV2Props; + +export default function DialogContainer(props: DialogContainerProps) { + const { + className, + title, + titleColor, + titleClassName, + titleRef, + bodyContainerClassName, + bodyContainerRef, + buttonsClassName, + buttonsContainerRef, + children, + } = props; + const c = useC(); return ( @@ -57,12 +69,27 @@ export default function DialogContainer({ {children}

- + {"buttons" in props ? ( + + ) : ( + + )}
); } -- cgit v1.2.3