From f5dfd52f6efece2f4cad227044ecf4dd66301bbc Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 26 Aug 2023 21:36:58 +0800 Subject: ... --- FrontEnd/src/views/common/AppBar.css | 87 ---- FrontEnd/src/views/common/AppBar.tsx | 98 ----- FrontEnd/src/views/common/BlobImage.tsx | 26 -- FrontEnd/src/views/common/Card.css | 20 - FrontEnd/src/views/common/Card.tsx | 38 -- FrontEnd/src/views/common/Icon.css | 3 - FrontEnd/src/views/common/Icon.tsx | 30 -- FrontEnd/src/views/common/ImageCropper.css | 38 -- FrontEnd/src/views/common/ImageCropper.tsx | 312 -------------- FrontEnd/src/views/common/LoadFailReload.tsx | 37 -- FrontEnd/src/views/common/LoadingPage.tsx | 13 - FrontEnd/src/views/common/Page.tsx | 15 - FrontEnd/src/views/common/SearchInput.css | 8 - FrontEnd/src/views/common/SearchInput.tsx | 50 --- FrontEnd/src/views/common/Skeleton.css | 14 - FrontEnd/src/views/common/Skeleton.tsx | 32 -- FrontEnd/src/views/common/Spinner.css | 13 - FrontEnd/src/views/common/Spinner.tsx | 36 -- FrontEnd/src/views/common/TimelineLogo.tsx | 27 -- FrontEnd/src/views/common/alert/AlertHost.tsx | 113 ----- FrontEnd/src/views/common/alert/alert.css | 33 -- FrontEnd/src/views/common/breakpoints.ts | 3 - FrontEnd/src/views/common/button/Button.css | 64 --- FrontEnd/src/views/common/button/Button.tsx | 46 -- FrontEnd/src/views/common/button/ButtonRow.css | 0 FrontEnd/src/views/common/button/ButtonRow.tsx | 62 --- FrontEnd/src/views/common/button/ButtonRowV2.tsx | 143 ------- FrontEnd/src/views/common/button/FlatButton.css | 27 -- FrontEnd/src/views/common/button/FlatButton.tsx | 36 -- FrontEnd/src/views/common/button/IconButton.css | 30 -- FrontEnd/src/views/common/button/IconButton.tsx | 30 -- FrontEnd/src/views/common/button/LoadingButton.css | 13 - FrontEnd/src/views/common/button/LoadingButton.tsx | 40 -- FrontEnd/src/views/common/button/index.tsx | 15 - FrontEnd/src/views/common/common.ts | 14 - FrontEnd/src/views/common/dialog/ConfirmDialog.css | 0 FrontEnd/src/views/common/dialog/ConfirmDialog.tsx | 59 --- FrontEnd/src/views/common/dialog/Dialog.css | 60 --- FrontEnd/src/views/common/dialog/Dialog.tsx | 63 --- .../src/views/common/dialog/DialogContainer.css | 20 - .../src/views/common/dialog/DialogContainer.tsx | 95 ----- .../src/views/common/dialog/FullPageDialog.css | 44 -- .../src/views/common/dialog/FullPageDialog.tsx | 53 --- .../src/views/common/dialog/OperationDialog.css | 8 - .../src/views/common/dialog/OperationDialog.tsx | 228 ---------- FrontEnd/src/views/common/dialog/index.ts | 64 --- FrontEnd/src/views/common/hooks.ts | 14 - FrontEnd/src/views/common/index.css | 100 ----- FrontEnd/src/views/common/input/InputGroup.css | 54 --- FrontEnd/src/views/common/input/InputGroup.tsx | 461 --------------------- FrontEnd/src/views/common/list/ListContainer.css | 4 - FrontEnd/src/views/common/list/ListContainer.tsx | 23 - .../src/views/common/list/ListItemContainer.css | 3 - .../src/views/common/list/ListItemContainer.tsx | 23 - FrontEnd/src/views/common/list/index.ts | 4 - FrontEnd/src/views/common/menu/Menu.css | 36 -- FrontEnd/src/views/common/menu/Menu.tsx | 62 --- FrontEnd/src/views/common/menu/PopupMenu.css | 7 - FrontEnd/src/views/common/menu/PopupMenu.tsx | 73 ---- FrontEnd/src/views/common/tab/TabPages.tsx | 71 ---- FrontEnd/src/views/common/tab/Tabs.css | 33 -- FrontEnd/src/views/common/tab/Tabs.tsx | 62 --- FrontEnd/src/views/common/theme-color.css | 173 -------- FrontEnd/src/views/common/theme.css | 146 ------- FrontEnd/src/views/common/user/UserAvatar.tsx | 22 - 65 files changed, 3631 deletions(-) delete mode 100644 FrontEnd/src/views/common/AppBar.css delete mode 100644 FrontEnd/src/views/common/AppBar.tsx delete mode 100644 FrontEnd/src/views/common/BlobImage.tsx delete mode 100644 FrontEnd/src/views/common/Card.css delete mode 100644 FrontEnd/src/views/common/Card.tsx delete mode 100644 FrontEnd/src/views/common/Icon.css delete mode 100644 FrontEnd/src/views/common/Icon.tsx delete mode 100644 FrontEnd/src/views/common/ImageCropper.css delete mode 100644 FrontEnd/src/views/common/ImageCropper.tsx delete mode 100644 FrontEnd/src/views/common/LoadFailReload.tsx delete mode 100644 FrontEnd/src/views/common/LoadingPage.tsx delete mode 100644 FrontEnd/src/views/common/Page.tsx delete mode 100644 FrontEnd/src/views/common/SearchInput.css delete mode 100644 FrontEnd/src/views/common/SearchInput.tsx delete mode 100644 FrontEnd/src/views/common/Skeleton.css delete mode 100644 FrontEnd/src/views/common/Skeleton.tsx delete mode 100644 FrontEnd/src/views/common/Spinner.css delete mode 100644 FrontEnd/src/views/common/Spinner.tsx delete mode 100644 FrontEnd/src/views/common/TimelineLogo.tsx delete mode 100644 FrontEnd/src/views/common/alert/AlertHost.tsx delete mode 100644 FrontEnd/src/views/common/alert/alert.css delete mode 100644 FrontEnd/src/views/common/breakpoints.ts delete mode 100644 FrontEnd/src/views/common/button/Button.css delete mode 100644 FrontEnd/src/views/common/button/Button.tsx delete mode 100644 FrontEnd/src/views/common/button/ButtonRow.css delete mode 100644 FrontEnd/src/views/common/button/ButtonRow.tsx delete mode 100644 FrontEnd/src/views/common/button/ButtonRowV2.tsx delete mode 100644 FrontEnd/src/views/common/button/FlatButton.css delete mode 100644 FrontEnd/src/views/common/button/FlatButton.tsx delete mode 100644 FrontEnd/src/views/common/button/IconButton.css delete mode 100644 FrontEnd/src/views/common/button/IconButton.tsx delete mode 100644 FrontEnd/src/views/common/button/LoadingButton.css delete mode 100644 FrontEnd/src/views/common/button/LoadingButton.tsx delete mode 100644 FrontEnd/src/views/common/button/index.tsx delete mode 100644 FrontEnd/src/views/common/common.ts delete mode 100644 FrontEnd/src/views/common/dialog/ConfirmDialog.css delete mode 100644 FrontEnd/src/views/common/dialog/ConfirmDialog.tsx delete mode 100644 FrontEnd/src/views/common/dialog/Dialog.css delete mode 100644 FrontEnd/src/views/common/dialog/Dialog.tsx delete mode 100644 FrontEnd/src/views/common/dialog/DialogContainer.css delete mode 100644 FrontEnd/src/views/common/dialog/DialogContainer.tsx delete mode 100644 FrontEnd/src/views/common/dialog/FullPageDialog.css delete mode 100644 FrontEnd/src/views/common/dialog/FullPageDialog.tsx delete mode 100644 FrontEnd/src/views/common/dialog/OperationDialog.css delete mode 100644 FrontEnd/src/views/common/dialog/OperationDialog.tsx delete mode 100644 FrontEnd/src/views/common/dialog/index.ts delete mode 100644 FrontEnd/src/views/common/hooks.ts delete mode 100644 FrontEnd/src/views/common/index.css delete mode 100644 FrontEnd/src/views/common/input/InputGroup.css delete mode 100644 FrontEnd/src/views/common/input/InputGroup.tsx delete mode 100644 FrontEnd/src/views/common/list/ListContainer.css delete mode 100644 FrontEnd/src/views/common/list/ListContainer.tsx delete mode 100644 FrontEnd/src/views/common/list/ListItemContainer.css delete mode 100644 FrontEnd/src/views/common/list/ListItemContainer.tsx delete mode 100644 FrontEnd/src/views/common/list/index.ts delete mode 100644 FrontEnd/src/views/common/menu/Menu.css delete mode 100644 FrontEnd/src/views/common/menu/Menu.tsx delete mode 100644 FrontEnd/src/views/common/menu/PopupMenu.css delete mode 100644 FrontEnd/src/views/common/menu/PopupMenu.tsx delete mode 100644 FrontEnd/src/views/common/tab/TabPages.tsx delete mode 100644 FrontEnd/src/views/common/tab/Tabs.css delete mode 100644 FrontEnd/src/views/common/tab/Tabs.tsx delete mode 100644 FrontEnd/src/views/common/theme-color.css delete mode 100644 FrontEnd/src/views/common/theme.css delete mode 100644 FrontEnd/src/views/common/user/UserAvatar.tsx (limited to 'FrontEnd/src/views') diff --git a/FrontEnd/src/views/common/AppBar.css b/FrontEnd/src/views/common/AppBar.css deleted file mode 100644 index a0d975b5..00000000 --- a/FrontEnd/src/views/common/AppBar.css +++ /dev/null @@ -1,87 +0,0 @@ -.app-bar { - height: 56px; - position: fixed; - z-index: 1030; - top: 0; - left: 0; - right: 0; - background-color: var(--cru-primary-color); -} - -.app-bar { - display: flex; -} - -.app-bar .app-bar-brand { - display: flex; - align-items: center; -} - -.app-bar .app-bar-brand-icon { - height: 2em; -} - -.app-bar .app-bar-user-area { - display: flex; - margin-left: auto; -} - -.app-bar a { - background-color: var(--cru-primary-color); - color: var(--cru-push-button-text-color); - text-decoration: none; - display: flex; - align-items: center; - padding: 0 1em; - transition: all 0.5s; -} - -.app-bar a:hover { - background-color: var(--cru-clickable-primary-hover-color); -} - -.app-bar a:focus { - background-color: var(--cru-clickable-primary-focus-color); -} - -.app-bar a:active { - background-color: var(--cru-clickable-primary-active-color); -} - -/* the current page */ -.app-bar a.active { - background-color: var(--cru-clickable-primary-focus-color); -} - -.app-bar .app-bar-avatar img { - width: 45px; - height: 45px; - background-color: white; - border-radius: 50%; -} - -.app-bar.desktop .app-bar-link-area { - display: flex; -} - -.app-bar.mobile .app-bar-link-area { - position: absolute; - z-index: -1; - top: 56px; - left: 0; - right: 0; - transition: transform 0.5s; -} - -.app-bar.mobile a { - height: 56px; -} - -.app-bar.mobile.collapse .app-bar-link-area { - transform: translateY(-100%); -} - -.app-bar .toggler { - font-size: 2em; - margin-right: 0.5em; -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/AppBar.tsx b/FrontEnd/src/views/common/AppBar.tsx deleted file mode 100644 index b9ea825b..00000000 --- a/FrontEnd/src/views/common/AppBar.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { useState } from "react"; -import classnames from "classnames"; -import { Link, NavLink } from "react-router-dom"; - -import { I18nText, useC, useMobile } from "./common"; -import { useUser } from "@/services/user"; - -import TimelineLogo from "./TimelineLogo"; -import { IconButton } from "./button"; -import UserAvatar from "./user/UserAvatar"; - -import "./AppBar.css"; - -function AppBarNavLink({ - link, - className, - label, - onClick, - children, -}: { - link: string; - className?: string; - label?: I18nText; - onClick?: () => void; - children?: React.ReactNode; -}) { - if (label != null && children != null) { - throw new Error("AppBarNavLink: label and children cannot be both set"); - } - - const c = useC(); - - return ( - classnames(className, isActive && "active")} - onClick={onClick} - > - {children != null ? children : c(label)} - - ); -} - -export default function AppBar() { - const isMobile = useMobile(); - - const [isCollapse, setIsCollapse] = useState(true); - const collapse = isMobile ? () => setIsCollapse(true) : undefined; - const toggleCollapse = () => setIsCollapse(!isCollapse); - - const user = useUser(); - const hasAdministrationPermission = user && user.hasAdministrationPermission; - - return ( - - ); -} diff --git a/FrontEnd/src/views/common/BlobImage.tsx b/FrontEnd/src/views/common/BlobImage.tsx deleted file mode 100644 index 259c2210..00000000 --- a/FrontEnd/src/views/common/BlobImage.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { ComponentPropsWithoutRef, useState, useEffect } from "react"; - -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] = useState(undefined); - - useEffect(() => { - if (src instanceof Blob) { - const url = URL.createObjectURL(src); - setUrl(url); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setUrl(src); - } - }, [src]); - - return ; -} diff --git a/FrontEnd/src/views/common/Card.css b/FrontEnd/src/views/common/Card.css deleted file mode 100644 index 6d655eb9..00000000 --- a/FrontEnd/src/views/common/Card.css +++ /dev/null @@ -1,20 +0,0 @@ -.cru-card { - border-radius: var(--cru-card-border-radius); - transition: all 0.3s; -} - -.cru-card-background-none { - background-color: transparent; -} - -.cru-card-background-solid { - background-color: var(--cru-background-color); -} - -.cru-card-background-grayscale { - background-color: var(--cru-container-background-color); -} - -.cru-card-border-color { - border: 2px solid var(--cru-card-border-color); -} diff --git a/FrontEnd/src/views/common/Card.tsx b/FrontEnd/src/views/common/Card.tsx deleted file mode 100644 index a8f0d3cc..00000000 --- a/FrontEnd/src/views/common/Card.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { ComponentPropsWithoutRef, Ref } from "react"; -import classNames from "classnames"; - -import { ThemeColor } from "./common"; -import "./Card.css"; - -interface CardProps extends ComponentPropsWithoutRef<"div"> { - containerRef?: Ref; - color?: ThemeColor; - border?: "color" | "none"; - background?: "color" | "solid" | "grayscale" | "none"; -} - -export default function Card({ - color, - background, - border, - className, - children, - containerRef, - ...otherProps -}: CardProps) { - return ( -
- {children} -
- ); -} diff --git a/FrontEnd/src/views/common/Icon.css b/FrontEnd/src/views/common/Icon.css deleted file mode 100644 index fe980d7b..00000000 --- a/FrontEnd/src/views/common/Icon.css +++ /dev/null @@ -1,3 +0,0 @@ -.cru-icon { - font-size: 1.4rem; -} diff --git a/FrontEnd/src/views/common/Icon.tsx b/FrontEnd/src/views/common/Icon.tsx deleted file mode 100644 index 2ac3a7ca..00000000 --- a/FrontEnd/src/views/common/Icon.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ComponentPropsWithoutRef } from "react"; -import classNames from "classnames"; - -import { ThemeColor } from "./common"; - -import "./Icon.css"; - -interface IconButtonProps extends ComponentPropsWithoutRef<"i"> { - icon: string; - color?: ThemeColor | "on-surface"; - size?: string | number; -} - -export default function Icon(props: IconButtonProps) { - const { icon, color, size, style, className, ...otherProps } = props; - - const colorName = color === "on-surface" ? "surface-on" : color; - - return ( - - ); -} diff --git a/FrontEnd/src/views/common/ImageCropper.css b/FrontEnd/src/views/common/ImageCropper.css deleted file mode 100644 index 2c4d0a8c..00000000 --- a/FrontEnd/src/views/common/ImageCropper.css +++ /dev/null @@ -1,38 +0,0 @@ -.image-cropper-container { - position: relative; - box-sizing: border-box; - user-select: none; -} - -.image-cropper-container img { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; -} - -.image-cropper-mask-container { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - overflow: hidden; -} - -.image-cropper-mask { - position: absolute; - box-shadow: 0 0 0 10000px rgba(255, 255, 255, 0.8); - touch-action: none; -} - -.image-cropper-handler { - position: absolute; - width: 26px; - height: 26px; - border: black solid 2px; - border-radius: 50%; - background: white; - touch-action: none; -} diff --git a/FrontEnd/src/views/common/ImageCropper.tsx b/FrontEnd/src/views/common/ImageCropper.tsx deleted file mode 100644 index fcab74b0..00000000 --- a/FrontEnd/src/views/common/ImageCropper.tsx +++ /dev/null @@ -1,312 +0,0 @@ -import * as React from "react"; -import classnames from "classnames"; - -import { UiLogicError } from "@/common"; - -import "./ImageCropper.css"; -import BlobImage from "./BlobImage"; - -export interface Clip { - left: number; - top: number; - width: number; -} - -interface NormailizedClip extends Clip { - height: number; -} - -interface ImageInfo { - width: number; - height: number; - landscape: boolean; - ratio: number; - maxClipWidth: number; - maxClipHeight: number; -} - -interface ImageCropperSavedState { - clip: NormailizedClip; - x: number; - y: number; - pointerId: number; -} - -export interface ImageCropperProps { - clip: Clip | null; - image: string | Blob; - onChange: (clip: Clip) => void; - imageElementCallback?: (element: HTMLImageElement | null) => void; - className?: string; -} - -const ImageCropper = (props: ImageCropperProps): React.ReactElement => { - const { clip, image, onChange, imageElementCallback, className } = props; - - const [oldState, setOldState] = React.useState( - null, - ); - const [imageInfo, setImageInfo] = React.useState(null); - - const normalizeClip = (c: Clip | null | undefined): NormailizedClip => { - if (c == null) { - return { left: 0, top: 0, width: 0, height: 0 }; - } - - return { - left: c.left || 0, - top: c.top || 0, - width: c.width || 0, - height: imageInfo != null ? (c.width || 0) / imageInfo.ratio : 0, - }; - }; - - const c = normalizeClip(clip); - - const imgElementRef = React.useRef(null); - - const onImageRef = React.useCallback( - (e: HTMLImageElement | null) => { - imgElementRef.current = e; - if (imageElementCallback != null && e == null) { - imageElementCallback(null); - } - }, - [imageElementCallback], - ); - - const onImageLoad = React.useCallback( - (e: React.SyntheticEvent) => { - const img = e.currentTarget; - const landscape = img.naturalWidth >= img.naturalHeight; - - const info = { - width: img.naturalWidth, - height: img.naturalHeight, - landscape, - ratio: img.naturalHeight / img.naturalWidth, - maxClipWidth: landscape ? img.naturalHeight / img.naturalWidth : 1, - maxClipHeight: landscape ? 1 : img.naturalWidth / img.naturalHeight, - }; - setImageInfo(info); - onChange({ left: 0, top: 0, width: info.maxClipWidth }); - if (imageElementCallback != null) { - imageElementCallback(img); - } - }, - [onChange, imageElementCallback], - ); - - const onPointerDown = React.useCallback( - (e: React.PointerEvent) => { - if (oldState != null) return; - e.currentTarget.setPointerCapture(e.pointerId); - setOldState({ - x: e.clientX, - y: e.clientY, - clip: c, - pointerId: e.pointerId, - }); - }, - [oldState, c], - ); - - const onPointerUp = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null || oldState.pointerId !== e.pointerId) return; - e.currentTarget.releasePointerCapture(e.pointerId); - setOldState(null); - }, - [oldState], - ); - - const onPointerMove = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null) return; - - const oldClip = oldState.clip; - - const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y }; - - const { current: imgElement } = imgElementRef; - - if (imgElement == null) throw new UiLogicError("Image element is null."); - - const moveRatio = { - x: movement.x / imgElement.width, - y: movement.y / imgElement.height, - }; - - const newRatio = { - x: oldClip.left + moveRatio.x, - y: oldClip.top + moveRatio.y, - }; - if (newRatio.x < 0) { - newRatio.x = 0; - } else if (newRatio.x > 1 - oldClip.width) { - newRatio.x = 1 - oldClip.width; - } - if (newRatio.y < 0) { - newRatio.y = 0; - } else if (newRatio.y > 1 - oldClip.height) { - newRatio.y = 1 - oldClip.height; - } - - onChange({ left: newRatio.x, top: newRatio.y, width: oldClip.width }); - }, - [oldState, onChange], - ); - - const onHandlerPointerMove = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null) return; - - const oldClip = oldState.clip; - - const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y }; - - const ratio = imageInfo == null ? 1 : imageInfo.ratio; - - const { current: imgElement } = imgElementRef; - - if (imgElement == null) throw new UiLogicError("Image element is null."); - - const moveRatio = { - x: movement.x / imgElement.width, - y: movement.x / imgElement.width / ratio, - }; - - const newRatio = { - x: oldClip.width + moveRatio.x, - y: oldClip.height + moveRatio.y, - }; - - const maxRatio = { - x: Math.min(1 - oldClip.left, newRatio.x), - y: Math.min(1 - oldClip.top, newRatio.y), - }; - - const maxWidthRatio = Math.min(maxRatio.x, maxRatio.y * ratio); - - let newWidth; - if (newRatio.x < 0) { - newWidth = 0; - } else if (newRatio.x > maxWidthRatio) { - newWidth = maxWidthRatio; - } else { - newWidth = newRatio.x; - } - - onChange({ left: oldClip.left, top: oldClip.top, width: newWidth }); - }, - [imageInfo, oldState, onChange], - ); - - const toPercentage = (n: number): string => `${n}%`; - - // fuck!!! I just can't find a better way to implement this in pure css - const containerStyle: React.CSSProperties = (() => { - if (imageInfo == null) { - return { width: "100%", paddingTop: "100%", height: 0 }; - } else { - if (imageInfo.ratio > 1) { - return { - width: toPercentage(100 / imageInfo.ratio), - paddingTop: "100%", - height: 0, - }; - } else { - return { - width: "100%", - paddingTop: toPercentage(100 * imageInfo.ratio), - height: 0, - }; - } - } - })(); - - return ( -
- -
-
-
-
-
- ); -}; - -export default ImageCropper; - -export function applyClipToImage( - image: HTMLImageElement, - clip: Clip, - mimeType: string, -): Promise { - return new Promise((resolve, reject) => { - const naturalSize = { - width: image.naturalWidth, - height: image.naturalHeight, - }; - const clipArea = { - x: naturalSize.width * clip.left, - y: naturalSize.height * clip.top, - length: naturalSize.width * clip.width, - }; - - const canvas = document.createElement("canvas"); - canvas.width = clipArea.length; - canvas.height = clipArea.length; - const context = canvas.getContext("2d"); - - if (context == null) throw new Error("Failed to create context."); - - context.drawImage( - image, - clipArea.x, - clipArea.y, - clipArea.length, - clipArea.length, - 0, - 0, - clipArea.length, - clipArea.length, - ); - - canvas.toBlob((blob) => { - if (blob == null) { - reject(new Error("canvas.toBlob returns null")); - } else { - resolve(blob); - } - }, mimeType); - }); -} diff --git a/FrontEnd/src/views/common/LoadFailReload.tsx b/FrontEnd/src/views/common/LoadFailReload.tsx deleted file mode 100644 index 81ba1f67..00000000 --- a/FrontEnd/src/views/common/LoadFailReload.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from "react"; -import { Trans } from "react-i18next"; - -export interface LoadFailReloadProps { - className?: string; - style?: React.CSSProperties; - onReload: () => void; -} - -const LoadFailReload: React.FC = ({ - onReload, - className, - style, -}) => { - return ( - - 0 - { - onReload(); - e.preventDefault(); - }} - > - 1 - - 2 - - ); -}; - -export default LoadFailReload; diff --git a/FrontEnd/src/views/common/LoadingPage.tsx b/FrontEnd/src/views/common/LoadingPage.tsx deleted file mode 100644 index 35ee1aa8..00000000 --- a/FrontEnd/src/views/common/LoadingPage.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from "react"; - -import Spinner from "./Spinner"; - -const LoadingPage: React.FC = () => { - return ( -
- -
- ); -}; - -export default LoadingPage; diff --git a/FrontEnd/src/views/common/Page.tsx b/FrontEnd/src/views/common/Page.tsx deleted file mode 100644 index 86fdb2f5..00000000 --- a/FrontEnd/src/views/common/Page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { ComponentPropsWithoutRef, Ref } from "react"; -import classNames from "classnames"; - -interface PageProps extends ComponentPropsWithoutRef<"div"> { - noTopPadding?: boolean; - pageRef?: Ref; -} - -export default function Page({ noTopPadding, pageRef, className, children }: PageProps) { - return ( -
- {children} -
- ); -} diff --git a/FrontEnd/src/views/common/SearchInput.css b/FrontEnd/src/views/common/SearchInput.css deleted file mode 100644 index f0503016..00000000 --- a/FrontEnd/src/views/common/SearchInput.css +++ /dev/null @@ -1,8 +0,0 @@ -.cru-search-input { - display: flex; - flex-wrap: wrap; -} - -.cru-search-input-input { - width: 100%; -} diff --git a/FrontEnd/src/views/common/SearchInput.tsx b/FrontEnd/src/views/common/SearchInput.tsx deleted file mode 100644 index e3216b86..00000000 --- a/FrontEnd/src/views/common/SearchInput.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import classNames from "classnames"; - -import { useC, Text } from "./common"; -import LoadingButton from "./button/LoadingButton"; - -import "./SearchInput.css"; - -interface SearchInputProps { - value: string; - onChange: (value: string) => void; - onButtonClick: () => void; - loading?: boolean; - className?: string; - buttonText?: Text; -} - -export default function SearchInput({ - value, - onChange, - onButtonClick, - loading, - className, - buttonText, -}: SearchInputProps) { - const c = useC(); - - return ( -
- { - const { value } = event.currentTarget; - onChange(value); - }} - onKeyDown={(event) => { - if (event.key === "Enter") { - onButtonClick(); - event.preventDefault(); - } - }} - /> - - - {c(buttonText ?? "search")} - -
- ); -} diff --git a/FrontEnd/src/views/common/Skeleton.css b/FrontEnd/src/views/common/Skeleton.css deleted file mode 100644 index a571eead..00000000 --- a/FrontEnd/src/views/common/Skeleton.css +++ /dev/null @@ -1,14 +0,0 @@ -.cru-skeleton { - padding: 0 1em; -} - -.cru-skeleton-line { - height: 1em; - background-color: hsl(0, 0%, 90%); - margin: 0.7em 0; - border-radius: 0.2em; -} - -.cru-skeleton-line.last { - width: 50%; -} diff --git a/FrontEnd/src/views/common/Skeleton.tsx b/FrontEnd/src/views/common/Skeleton.tsx deleted file mode 100644 index 3b149db9..00000000 --- a/FrontEnd/src/views/common/Skeleton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import * as React from "react"; -import classnames from "classnames"; -import range from "lodash/range"; - -import "./Skeleton.css"; - -export interface SkeletonProps { - lineNumber?: number; - className?: string; - style?: React.CSSProperties; -} - -const Skeleton: React.FC = (props) => { - const { lineNumber: lineNumberProps, className, style } = props; - const lineNumber = lineNumberProps ?? 3; - - return ( -
- {range(lineNumber).map((i) => ( -
- ))} -
- ); -}; - -export default Skeleton; diff --git a/FrontEnd/src/views/common/Spinner.css b/FrontEnd/src/views/common/Spinner.css deleted file mode 100644 index a1de68d2..00000000 --- a/FrontEnd/src/views/common/Spinner.css +++ /dev/null @@ -1,13 +0,0 @@ -@keyframes cru-spinner-animation { - from { - transform: scale(0,0); - } -} - -.cru-spinner { - display: inline-block; - animation: cru-spinner-animation 0.5s infinite alternate; - background-color: currentColor; - border-radius: 50%; - transform-origin: center; -} diff --git a/FrontEnd/src/views/common/Spinner.tsx b/FrontEnd/src/views/common/Spinner.tsx deleted file mode 100644 index ec0c2c35..00000000 --- a/FrontEnd/src/views/common/Spinner.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { CSSProperties } from "react"; -import classnames from "classnames"; - -import { ThemeColor } from "./common"; - -import "./Spinner.css"; - -export interface SpinnerProps { - size?: "sm" | "md" | "lg" | number | string; - color?: ThemeColor; - className?: string; - style?: CSSProperties; -} - -export default function Spinner(props: SpinnerProps) { - const { size, color, className, style } = props; - const calculatedSize = - size === "sm" - ? "18px" - : size === "md" - ? "30px" - : size === "lg" - ? "42px" - : typeof size === "number" - ? size - : size == null - ? "20px" - : size; - - return ( - - ); -} diff --git a/FrontEnd/src/views/common/TimelineLogo.tsx b/FrontEnd/src/views/common/TimelineLogo.tsx deleted file mode 100644 index e06ed0f5..00000000 --- a/FrontEnd/src/views/common/TimelineLogo.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { SVGAttributes } from "react"; -import * as React from "react"; - -export interface TimelineLogoProps extends SVGAttributes { - color?: string; -} - -const TimelineLogo: React.FC = (props) => { - const { color, ...forwardProps } = props; - const coercedColor = color ?? "currentcolor"; - return ( - - - - - - ); -}; - -export default TimelineLogo; diff --git a/FrontEnd/src/views/common/alert/AlertHost.tsx b/FrontEnd/src/views/common/alert/AlertHost.tsx deleted file mode 100644 index 42074781..00000000 --- a/FrontEnd/src/views/common/alert/AlertHost.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import * as React from "react"; -import without from "lodash/without"; -import { useTranslation } from "react-i18next"; -import classNames from "classnames"; - -import { alertService, AlertInfoEx, AlertInfo } from "@/services/alert"; -import { convertI18nText } from "@/common"; - -import IconButton from "../button/IconButton"; - -import "./alert.css"; - -interface AutoCloseAlertProps { - alert: AlertInfo; - close: () => void; -} - -export const AutoCloseAlert: React.FC = (props) => { - const { alert, close } = props; - const { dismissTime } = alert; - - const { t } = useTranslation(); - - const timerTag = React.useRef(null); - const closeHandler = React.useRef<(() => void) | null>(null); - - React.useEffect(() => { - closeHandler.current = close; - }, [close]); - - React.useEffect(() => { - const tag = - dismissTime === "never" - ? null - : typeof dismissTime === "number" - ? window.setTimeout(() => closeHandler.current?.(), dismissTime) - : window.setTimeout(() => closeHandler.current?.(), 5000); - timerTag.current = tag; - return () => { - if (tag != null) { - window.clearTimeout(tag); - } - }; - }, [dismissTime]); - - const cancelTimer = (): void => { - const { current: tag } = timerTag; - if (tag != null) { - window.clearTimeout(tag); - } - }; - - return ( -
-
- {(() => { - const { message, customMessage } = alert; - if (customMessage != null) { - return customMessage; - } else { - return convertI18nText(message, t); - } - })()} -
-
- -
-
- ); -}; - -const AlertHost: React.FC = () => { - const [alerts, setAlerts] = React.useState([]); - - React.useEffect(() => { - const consume = (alert: AlertInfoEx): void => { - setAlerts((old) => [...old, alert]); - }; - - alertService.registerConsumer(consume); - return () => { - alertService.unregisterConsumer(consume); - }; - }, []); - - return ( -
- {alerts.map((alert) => { - return ( - { - setAlerts((old) => without(old, alert)); - }} - /> - ); - })} -
- ); -}; - -export default AlertHost; diff --git a/FrontEnd/src/views/common/alert/alert.css b/FrontEnd/src/views/common/alert/alert.css deleted file mode 100644 index 54c2b87f..00000000 --- a/FrontEnd/src/views/common/alert/alert.css +++ /dev/null @@ -1,33 +0,0 @@ -.alert-container { - position: fixed; - z-index: 1040; -} - -.cru-alert { - border-radius: 5px; - border: var(--cru-key-color) 1px solid; - color: var(--cru-key-t-color); - background-color: var(--cru-key-b1-color); - - display: flex; - overflow: hidden; -} - -.cru-alert-content { - padding: 0.5em 2em; -} - -.cru-alert-close-button-container { - flex-shrink: 0; - margin-left: auto; - width: 2em; - text-align: center; - display: flex; - align-items: center; - justify-content: center; - background-color: var(--cru-key-t-color); -} - -.cru-alert-close-button { - color: var(--cru-key-color); -} diff --git a/FrontEnd/src/views/common/breakpoints.ts b/FrontEnd/src/views/common/breakpoints.ts deleted file mode 100644 index fb281610..00000000 --- a/FrontEnd/src/views/common/breakpoints.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const breakpoints = { - sm: 576, -} as const; diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css deleted file mode 100644 index 1da70f0e..00000000 --- a/FrontEnd/src/views/common/button/Button.css +++ /dev/null @@ -1,64 +0,0 @@ -.cru-button { - font-size: 1rem; - padding: 0.4em 0.8em; - transition: all 0.3s; - border-radius: 0.2em; - border: 1px solid; - cursor: pointer; -} - -.cru-button:not(.outline) { - color: var(--cru-push-button-text-color); - background-color: var(--cru-clickable-normal-color); - border-color: var(--cru-clickable-normal-color); -} - -.cru-button:not(.outline):hover { - background-color: var(--cru-clickable-hover-color); - border-color: var(--cru-clickable-hover-color); -} - -.cru-button:not(.outline):focus { - background-color: var(--cru-clickable-focus-color); - border-color: var(--cru-clickable-focus-color); -} - -.cru-button:not(.outline):active { - background-color: var(--cru-clickable-active-color); - border-color: var(--cru-clickable-active-color); -} - -.cru-button:not(.outline):disabled { - color: var(--cru-push-button-disabled-text-color); - background-color: var(--cru-push-button-disabled-color); - border-color: var(--cru-push-button-disabled-color); - cursor: auto; -} - - -.cru-button.outline { - color: var(--cru-clickable-normal-color); - border-color: var(--cru-clickable-normal-color); - background-color: transparent; -} - -.cru-button.outline:hover { - color: var(--cru-clickable-hover-color); - border-color: var(--cru-clickable-hover-color); -} - -.cru-button.outline:focus { - color: var(--cru-clickable-focus-color); - border-color: var(--cru-clickable-focus-color); -} - -.cru-button.outline:active { - color: var(--cru-clickable-active-color); - border-color: var(--cru-clickable-active-color); -} - -.cru-button.outline:disabled { - color: var(--cru-clickable-disabled-color); - border-color: var(--cru-clickable-disabled-color); - cursor: auto; -} diff --git a/FrontEnd/src/views/common/button/Button.tsx b/FrontEnd/src/views/common/button/Button.tsx deleted file mode 100644 index 6c38e130..00000000 --- a/FrontEnd/src/views/common/button/Button.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ComponentPropsWithoutRef, Ref } from "react"; -import classNames from "classnames"; - -import { Text, useC, ThemeColor } from "../common"; - -import "./Button.css"; - -interface ButtonProps extends ComponentPropsWithoutRef<"button"> { - color?: ThemeColor; - text?: Text; - outline?: boolean; - buttonRef?: Ref | null; -} - -export default function Button(props: ButtonProps) { - const { - buttonRef, - color, - text, - outline, - className, - children, - ...otherProps - } = props; - - if (text != null && children != null) { - console.warn("You can't set both text and children props."); - } - - const c = useC(); - - return ( - - ); -} diff --git a/FrontEnd/src/views/common/button/ButtonRow.css b/FrontEnd/src/views/common/button/ButtonRow.css deleted file mode 100644 index e69de29b..00000000 diff --git a/FrontEnd/src/views/common/button/ButtonRow.tsx b/FrontEnd/src/views/common/button/ButtonRow.tsx deleted file mode 100644 index eea60cc4..00000000 --- a/FrontEnd/src/views/common/button/ButtonRow.tsx +++ /dev/null @@ -1,62 +0,0 @@ -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"; - -type ButtonRowButton = ( - | { - type: "normal"; - props: ComponentPropsWithoutRef; - } - | { - type: "flat"; - props: ComponentPropsWithoutRef; - } - | { - type: "icon"; - props: ComponentPropsWithoutRef; - } - | { type: "loading"; props: ComponentPropsWithoutRef } -) & { key: string | number }; - -interface ButtonRowProps { - className?: string; - containerRef?: Ref; - buttons: ButtonRowButton[]; - buttonsClassName?: string; -} - -export default function ButtonRow({ - className, - containerRef, - buttons, - buttonsClassName, -}: ButtonRowProps) { - return ( -
- {buttons.map((button) => { - const { type, key, props } = button; - const newClassName = classNames(props.className, buttonsClassName); - switch (type) { - case "normal": - return
- ); -} diff --git a/FrontEnd/src/views/common/button/ButtonRowV2.tsx b/FrontEnd/src/views/common/button/ButtonRowV2.tsx deleted file mode 100644 index 3467ad52..00000000 --- a/FrontEnd/src/views/common/button/ButtonRowV2.tsx +++ /dev/null @@ -1,143 +0,0 @@ -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/FlatButton.css b/FrontEnd/src/views/common/button/FlatButton.css deleted file mode 100644 index 2050946c..00000000 --- a/FrontEnd/src/views/common/button/FlatButton.css +++ /dev/null @@ -1,27 +0,0 @@ -.cru-flat-button { - font-size: 1rem; - padding: 0.4em 0.8em; - transition: all 0.5s; - border-radius: 0.2em; - background-color: var(--cru-clickable-grayscale-normal-color); - border: 1px none; - color: var(--cru-clickable-normal-color); - cursor: pointer; -} - -.cru-flat-button:hover { - background-color: var(--cru-clickable-grayscale-hover-color); -} - -.cru-flat-button:focus { - background-color: var(--cru-clickable-grayscale-focus-color); -} - -.cru-flat-button:active { - background-color: var(--cru-clickable-grayscale-active-color); -} - -.cru-flat-button:disabled { - color: var(--cru-clickable-disabled-color); - cursor: auto; -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/button/FlatButton.tsx b/FrontEnd/src/views/common/button/FlatButton.tsx deleted file mode 100644 index 9f074dd6..00000000 --- a/FrontEnd/src/views/common/button/FlatButton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ComponentPropsWithoutRef, Ref } from "react"; -import classNames from "classnames"; - -import { Text, useC, ThemeColor } from "../common"; - -import "./FlatButton.css"; - -interface FlatButtonProps extends ComponentPropsWithoutRef<"button"> { - color?: ThemeColor; - text?: Text; - buttonRef?: Ref | null; -} - -export default function FlatButton(props: FlatButtonProps) { - const { color, text, className, children, buttonRef, ...otherProps } = props; - - if (text != null && children != null) { - console.warn("You can't set both text and children props."); - } - - const c = useC(); - - return ( - - ); -} diff --git a/FrontEnd/src/views/common/button/IconButton.css b/FrontEnd/src/views/common/button/IconButton.css deleted file mode 100644 index a3747201..00000000 --- a/FrontEnd/src/views/common/button/IconButton.css +++ /dev/null @@ -1,30 +0,0 @@ -.cru-icon-button { - color: var(--cru-clickable-normal-color); - font-size: 1.4rem; - background: none; - border: none; - transition: all 0.5s; - cursor: pointer; - user-select: none; -} - -.cru-icon-button:hover { - color: var(--cru-clickable-hover-color); -} - -.cru-icon-button:focus { - color: var(--cru-clickable-focus-color); -} - -.cru-icon-button:active { - color: var(--cru-clickable-active-color); -} - -.cru-flat-button:disabled { - color: var(--cru-clickable-disabled-color); - cursor: auto; -} - -.cru-icon-button.large { - font-size: 1.6rem; -} diff --git a/FrontEnd/src/views/common/button/IconButton.tsx b/FrontEnd/src/views/common/button/IconButton.tsx deleted file mode 100644 index 95c58887..00000000 --- a/FrontEnd/src/views/common/button/IconButton.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ComponentPropsWithoutRef } from "react"; -import classNames from "classnames"; - -import { ThemeColor } from "../common"; - -import "./IconButton.css"; - -interface IconButtonProps extends ComponentPropsWithoutRef<"i"> { - icon: string; - color?: ThemeColor | "grayscale"; - large?: boolean; - disabled?: boolean; // TODO: Not implemented -} - -export default function IconButton(props: IconButtonProps) { - const { icon, color, className, large, ...otherProps } = props; - - return ( - - ); -} diff --git a/FrontEnd/src/views/common/button/index.tsx b/FrontEnd/src/views/common/button/index.tsx deleted file mode 100644 index b5aa5470..00000000 --- a/FrontEnd/src/views/common/button/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import Button from "./Button"; -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, - ButtonRowV2, -}; diff --git a/FrontEnd/src/views/common/common.ts b/FrontEnd/src/views/common/common.ts deleted file mode 100644 index 7af2643b..00000000 --- a/FrontEnd/src/views/common/common.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type { Text, I18nText } from "@/common"; -export { c, convertI18nText, useC } from "@/common"; - -export const themeColors = [ - "primary", - "secondary", - "danger", - "create", -] as const; - -export type ThemeColor = (typeof themeColors)[number]; - -export { breakpoints } from "./breakpoints"; -export { useMobile } from "./hooks"; diff --git a/FrontEnd/src/views/common/dialog/ConfirmDialog.css b/FrontEnd/src/views/common/dialog/ConfirmDialog.css deleted file mode 100644 index e69de29b..00000000 diff --git a/FrontEnd/src/views/common/dialog/ConfirmDialog.tsx b/FrontEnd/src/views/common/dialog/ConfirmDialog.tsx deleted file mode 100644 index dbbd15c6..00000000 --- a/FrontEnd/src/views/common/dialog/ConfirmDialog.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useC, Text, ThemeColor } from "@/views/common/common"; - -import Dialog from "./Dialog"; -import DialogContainer from "./DialogContainer"; - -export default function ConfirmDialog({ - open, - onClose, - onConfirm, - title, - body, - color, - bodyColor, -}: { - open: boolean; - onClose: () => void; - onConfirm: () => void; - title: Text; - body: Text; - color?: ThemeColor; - bodyColor?: ThemeColor; -}) { - const c = useC(); - - return ( - - { - onConfirm(); - onClose(); - }, - }, - }, - ]} - > -
{c(body)}
-
-
- ); -} diff --git a/FrontEnd/src/views/common/dialog/Dialog.css b/FrontEnd/src/views/common/dialog/Dialog.css deleted file mode 100644 index e4c61440..00000000 --- a/FrontEnd/src/views/common/dialog/Dialog.css +++ /dev/null @@ -1,60 +0,0 @@ -.cru-dialog-overlay { - position: fixed; - z-index: 1040; - left: 0; - top: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - overflow: auto; -} - -.cru-dialog-background { - position: absolute; - z-index: -1; - left: 0; - right: 0; - top: 0; - bottom: 0; - background-color: var(--cru-surface-dim-color); - opacity: 0.8; -} - -.cru-dialog-container { - max-width: 100%; - min-width: 30vw; - - margin: 2em auto; - - border: var(--cru-key-container-color) 1px solid; - border-radius: 5px; - padding: 1.5em; - background-color: var(--cru-surface-color); -} - -@media (min-width: 576px) { - .cru-dialog-container { - max-width: 800px; - } -} - -.cru-dialog-enter .cru-dialog-container { - transform: scale(0, 0); - opacity: 0; - transform-origin: center; -} - -.cru-dialog-enter-active .cru-dialog-container { - transform: scale(1, 1); - opacity: 1; - transition: transform 0.3s, opacity 0.3s; - transform-origin: center; -} - -.cru-dialog-exit-active .cru-dialog-container { - transition: transform 0.3s, opacity 0.3s; - transform: scale(0, 0); - opacity: 0; - transform-origin: center; -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/dialog/Dialog.tsx b/FrontEnd/src/views/common/dialog/Dialog.tsx deleted file mode 100644 index 2ff7bea8..00000000 --- a/FrontEnd/src/views/common/dialog/Dialog.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { ReactNode, useRef } from "react"; -import ReactDOM from "react-dom"; -import { CSSTransition } from "react-transition-group"; -import classNames from "classnames"; - -import { ThemeColor } from "../common"; - -import "./Dialog.css"; - -const optionalPortalElement = document.getElementById("portal"); -if (optionalPortalElement == null) { - throw new Error("Portal element not found"); -} -const portalElement = optionalPortalElement; - -interface DialogProps { - open: boolean; - onClose: () => void; - color?: ThemeColor; - children?: ReactNode; - disableCloseOnClickOnOverlay?: boolean; -} - -export default function Dialog({ - open, - onClose, - color, - children, - disableCloseOnClickOnOverlay, -}: DialogProps) { - color = color ?? "primary"; - - const nodeRef = useRef(null); - - return ReactDOM.createPortal( - -
-
{ - onClose(); - } - } - /> -
{children}
-
- , - portalElement, - ); -} diff --git a/FrontEnd/src/views/common/dialog/DialogContainer.css b/FrontEnd/src/views/common/dialog/DialogContainer.css deleted file mode 100644 index fbb18e0d..00000000 --- a/FrontEnd/src/views/common/dialog/DialogContainer.css +++ /dev/null @@ -1,20 +0,0 @@ -.cru-dialog-container-title { - font-size: 1.2em; - font-weight: bold; - color: var(--cru-key-color); - margin-bottom: 0.5em; -} - - -.cru-dialog-container-hr { - margin: 1em 0; -} - -.cru-dialog-container-button-row { - display: flex; - justify-content: flex-end; -} - -.cru-dialog-container-button { - margin-left: 1em; -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/dialog/DialogContainer.tsx b/FrontEnd/src/views/common/dialog/DialogContainer.tsx deleted file mode 100644 index afee2669..00000000 --- a/FrontEnd/src/views/common/dialog/DialogContainer.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { ComponentProps, Ref, ReactNode } from "react"; -import classNames from "classnames"; - -import { ThemeColor, Text, useC } from "../common"; -import { ButtonRow, ButtonRowV2 } from "../button"; - -import "./DialogContainer.css"; - -interface DialogContainerBaseProps { - className?: string; - title: Text; - titleColor?: ThemeColor; - titleClassName?: string; - titleRef?: Ref; - bodyContainerClassName?: string; - bodyContainerRef?: Ref; - buttonsClassName?: string; - buttonsContainerRef?: ComponentProps["containerRef"]; - children: ReactNode; -} - -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 ( -
-
- {c(title)} -
-
-
- {children} -
-
- {"buttons" in props ? ( - - ) : ( - - )} -
- ); -} diff --git a/FrontEnd/src/views/common/dialog/FullPageDialog.css b/FrontEnd/src/views/common/dialog/FullPageDialog.css deleted file mode 100644 index 2f1fc636..00000000 --- a/FrontEnd/src/views/common/dialog/FullPageDialog.css +++ /dev/null @@ -1,44 +0,0 @@ -.cru-full-page { - position: fixed; - z-index: 1030; - left: 0; - top: 0; - right: 0; - bottom: 0; - background-color: white; - padding-top: 56px; -} - -.cru-full-page-top-bar { - height: 56px; - position: absolute; - top: 0; - left: 0; - right: 0; - z-index: 1; - background-color: var(--cru-primary-color); - display: flex; - align-items: center; -} - -.cru-full-page-content-container { - overflow: scroll; -} - -.cru-full-page-back-button { - color: var(--cru-primary-t-color); -} - -.cru-full-page-enter { - transform: translate(100%, 0); -} - -.cru-full-page-enter-active { - transform: none; - transition: transform 0.3s; -} - -.cru-full-page-exit-active { - transition: transform 0.3s; - transform: translate(100%, 0); -} diff --git a/FrontEnd/src/views/common/dialog/FullPageDialog.tsx b/FrontEnd/src/views/common/dialog/FullPageDialog.tsx deleted file mode 100644 index 6368fc0a..00000000 --- a/FrontEnd/src/views/common/dialog/FullPageDialog.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from "react"; -import { createPortal } from "react-dom"; -import classnames from "classnames"; -import { CSSTransition } from "react-transition-group"; - -import "./FullPageDialog.css"; -import IconButton from "../button/IconButton"; - -export interface FullPageDialogProps { - show: boolean; - onBack: () => void; - contentContainerClassName?: string; - children: React.ReactNode; -} - -const FullPageDialog: React.FC = ({ - show, - onBack, - children, - contentContainerClassName, -}) => { - return createPortal( - -
-
- -
-
- {children} -
-
-
, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - document.getElementById("portal")! - ); -}; - -export default FullPageDialog; diff --git a/FrontEnd/src/views/common/dialog/OperationDialog.css b/FrontEnd/src/views/common/dialog/OperationDialog.css deleted file mode 100644 index f4b7237e..00000000 --- a/FrontEnd/src/views/common/dialog/OperationDialog.css +++ /dev/null @@ -1,8 +0,0 @@ -.cru-operation-dialog-prompt { - color: var(--cru-surface-on-color); -} - -.cru-operation-dialog-input-group { - display: block; - margin: 0.5em 0; -} diff --git a/FrontEnd/src/views/common/dialog/OperationDialog.tsx b/FrontEnd/src/views/common/dialog/OperationDialog.tsx deleted file mode 100644 index 4335b2b0..00000000 --- a/FrontEnd/src/views/common/dialog/OperationDialog.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import { useState, ReactNode, ComponentProps } from "react"; -import classNames from "classnames"; - -import { useC, Text, ThemeColor } from "../common"; - -import { - useInputs, - InputGroup, - Initializer as InputInitializer, - InputValueDict, - InputErrorDict, -} from "../input/InputGroup"; -import Dialog from "./Dialog"; -import DialogContainer from "./DialogContainer"; - -import "./OperationDialog.css"; - -export type { InputInitializer, InputValueDict, InputErrorDict }; - -interface OperationDialogPromptProps { - message?: Text; - customMessage?: Text; - customMessageNode?: ReactNode; - className?: string; -} - -function OperationDialogPrompt(props: OperationDialogPromptProps) { - const { message, customMessage, customMessageNode, className } = props; - - const c = useC(); - - return ( -
- {message &&

{c(message)}

} - {customMessageNode ?? (customMessage != null ? c(customMessage) : null)} -
- ); -} - -export interface OperationDialogProps { - open: boolean; - onClose: () => void; - - color?: ThemeColor; - inputColor?: ThemeColor; - title: Text; - inputPrompt?: Text; - inputPromptNode?: ReactNode; - successPrompt?: (data: TData) => Text; - successPromptNode?: (data: TData) => ReactNode; - failurePrompt?: (error: unknown) => Text; - failurePromptNode?: (error: unknown) => ReactNode; - - inputs: InputInitializer; - - onProcess: (inputs: InputValueDict) => Promise; - onSuccessAndClose?: (data: TData) => void; -} - -function OperationDialog(props: OperationDialogProps) { - const { - open, - onClose, - color, - inputColor, - title, - inputPrompt, - inputPromptNode, - successPrompt, - successPromptNode, - failurePrompt, - failurePromptNode, - inputs, - onProcess, - onSuccessAndClose, - } = props; - - if (process.env.NODE_ENV === "development") { - if (inputPrompt && inputPromptNode) { - console.log("InputPrompt and inputPromptNode are both set."); - } - if (successPrompt && successPromptNode) { - console.log("SuccessPrompt and successPromptNode are both set."); - } - if (failurePrompt && failurePromptNode) { - console.log("FailurePrompt and failurePromptNode are both set."); - } - } - - type Step = - | { type: "input" } - | { type: "process" } - | { - type: "success"; - data: TData; - } - | { - type: "failure"; - data: unknown; - }; - - const [step, setStep] = useState({ type: "input" }); - - const { inputGroupProps, hasErrorAndDirty, setAllDisabled, confirm } = - useInputs({ - init: inputs, - }); - - function close() { - if (step.type !== "process") { - onClose(); - if (step.type === "success" && onSuccessAndClose) { - onSuccessAndClose?.(step.data); - } - } else { - console.log("Attempt to close modal dialog when processing."); - } - } - - function onConfirm() { - const result = confirm(); - if (result.type === "ok") { - setStep({ type: "process" }); - setAllDisabled(true); - onProcess(result.values).then( - (d) => { - setStep({ - type: "success", - data: d, - }); - }, - (e: unknown) => { - setStep({ - type: "failure", - data: e, - }); - }, - ); - } - } - - let body: ReactNode; - let buttons: ComponentProps["buttons"]; - - if (step.type === "input" || step.type === "process") { - const isProcessing = step.type === "process"; - - body = ( -
- - -
- ); - buttons = [ - { - key: "cancel", - type: "normal", - props: { - text: "operationDialog.cancel", - color: "secondary", - outline: true, - onClick: close, - disabled: isProcessing, - }, - }, - { - key: "confirm", - type: "loading", - props: { - text: "operationDialog.confirm", - color, - loading: isProcessing, - disabled: hasErrorAndDirty, - onClick: onConfirm, - }, - }, - ]; - } else { - const result = step; - - const promptProps: OperationDialogPromptProps = - result.type === "success" - ? { - message: "operationDialog.success", - customMessage: successPrompt?.(result.data), - customMessageNode: successPromptNode?.(result.data), - } - : { - message: "operationDialog.error", - customMessage: failurePrompt?.(result.data), - customMessageNode: failurePromptNode?.(result.data), - }; - body = ( -
- -
- ); - - buttons = [ - { - key: "ok", - type: "normal", - props: { - text: "operationDialog.ok", - color: "primary", - onClick: close, - }, - }, - ]; - } - - return ( - - - {body} - - - ); -} - -export default OperationDialog; diff --git a/FrontEnd/src/views/common/dialog/index.ts b/FrontEnd/src/views/common/dialog/index.ts deleted file mode 100644 index 59f15791..00000000 --- a/FrontEnd/src/views/common/dialog/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { useState } from "react"; - -export { default as Dialog } from "./Dialog"; -export { default as FullPageDialog } from "./FullPageDialog"; -export { default as OperationDialog } from "./OperationDialog"; -export { default as ConfirmDialog } from "./ConfirmDialog"; - -type DialogMap = { - [K in D]: V; -}; - -type DialogKeyMap = DialogMap; - -type DialogPropsMap = DialogMap< - D, - { key: number | string; open: boolean; onClose: () => void } ->; - -export function useDialog( - dialogs: D[], - options?: { - initDialog?: D | null; - onClose?: { - [K in D]?: () => void; - }; - }, -): { - dialog: D | null; - switchDialog: (newDialog: D | null) => void; - dialogPropsMap: DialogPropsMap; - createDialogSwitch: (newDialog: D | null) => () => void; -} { - const [dialog, setDialog] = useState(options?.initDialog ?? null); - - const [dialogKeys, setDialogKeys] = useState>( - () => Object.fromEntries(dialogs.map((d) => [d, 0])) as DialogKeyMap, - ); - - const switchDialog = (newDialog: D | null) => { - if (dialog !== null) { - setDialogKeys({ ...dialogKeys, [dialog]: dialogKeys[dialog] + 1 }); - } - setDialog(newDialog); - }; - - return { - dialog, - switchDialog, - dialogPropsMap: Object.fromEntries( - dialogs.map((d) => [ - d, - { - key: `${d}-${dialogKeys[d]}`, - open: dialog === d, - onClose: () => { - switchDialog(null); - options?.onClose?.[d]?.(); - }, - }, - ]), - ) as DialogPropsMap, - createDialogSwitch: (newDialog: D | null) => () => switchDialog(newDialog), - }; -} diff --git a/FrontEnd/src/views/common/hooks.ts b/FrontEnd/src/views/common/hooks.ts deleted file mode 100644 index c1fa5774..00000000 --- a/FrontEnd/src/views/common/hooks.ts +++ /dev/null @@ -1,14 +0,0 @@ -// TODO: Migrate hooks - -export { - useIsSmallScreen, - useClickOutside, - useScrollToBottom, -} from "@/utilities/hooks"; - -import { useMediaQuery } from "react-responsive"; -import { breakpoints } from "./breakpoints"; - -export function useMobile(): boolean { - return useMediaQuery({ maxWidth: breakpoints.sm }); -} diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css deleted file mode 100644 index a8f5e9a5..00000000 --- a/FrontEnd/src/views/common/index.css +++ /dev/null @@ -1,100 +0,0 @@ -@import "./theme.css"; - -* { - box-sizing: border-box; - margin-inline: 0; - margin-block: 0; -} - -body { - font-family: var(--cru-default-font-family); - background: var(--cru-body-background-color); - color: var(--cru-text-primary-color); - line-height: 1.2; -} - -.cru-page { - padding: var(--cru-page-padding); -} - -.cru-page-no-top-padding { - padding-top: 0; -} - -.cru-text-center { - text-align: center; -} - -.cru-text-end { - text-align: end; -} - -.cru-float-left { - float: left; -} - -.cru-float-right { - float: right; -} - -.cru-align-text-bottom { - vertical-align: text-bottom; -} - -.cru-align-middle { - vertical-align: middle; -} - -.cru-clearfix::after { - clear: both; -} - -.cru-fill-parent { - width: 100%; - height: 100%; -} - -.cru-avatar { - width: 60px; - height: 60px; -} - -.cru-avatar.large { - width: 100px; - height: 100px; -} - -.cru-avatar.small { - width: 40px; - height: 40px; -} - -.cru-round { - border-radius: 50%; -} - -.cru-tab-pages-action-area { - display: flex; - align-items: center; -} - -.alert-container { - position: fixed; - z-index: 1070; -} - -@media (min-width: 576px) { - .alert-container { - bottom: 0; - right: 0; - } -} - -@media (max-width: 575.98px) { - .alert-container { - bottom: 0; - right: 0; - left: 0; - text-align: center; - } -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/input/InputGroup.css b/FrontEnd/src/views/common/input/InputGroup.css deleted file mode 100644 index 7e905b1e..00000000 --- a/FrontEnd/src/views/common/input/InputGroup.css +++ /dev/null @@ -1,54 +0,0 @@ -.cru-input-group { - display: block; -} - -.cru-input-container { - margin: 0.4em 0; -} - -.cru-input-label { - display: block; - color: var(--cru-clickable-normal-color); - font-size: 0.9em; - margin-bottom: 0.3em; -} - -.cru-input-label-inline { - margin-inline-start: 0.5em; -} - -.cru-input-type-text input { - appearance: none; - display: block; - border: 1px solid; - /* color: var(--cru-surface-on-color); */ - /* background-color: var(--cru-surface-color); */ - margin: 0; - font-size: 1em; - padding: 0.2em; -} - -.cru-input-type-text input:hover { - border-color: var(--cru-clickable-hover-color); -} - -.cru-input-type-text input:focus { - border-color: var(--cru-clickable-focus-color); -} - -.cru-input-type-text input:disabled { - border-color: var(--cru-clickable-disabled-color); -} - -.cru-input-error { - display: block; - font-size: 0.8em; - color: var(--cru-danger-color); - margin-top: 0.4em; -} - -.cru-input-helper { - display: block; - font-size: 0.8em; - color: var(--cru-primary-color); -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/input/InputGroup.tsx b/FrontEnd/src/views/common/input/InputGroup.tsx deleted file mode 100644 index d95bb29e..00000000 --- a/FrontEnd/src/views/common/input/InputGroup.tsx +++ /dev/null @@ -1,461 +0,0 @@ -/** - * Some notes for InputGroup: - * This is one of the most complicated components in this project. - * Probably because the feature is complex and involved user inputs. - * - * I hope it contains following features: - * - Input features - * - Supports a wide range of input types. - * - Validator to validate user inputs. - * - Can set initial values. - * - Dirty, aka, has user touched this input. - * - Developer friendly - * - Easy to use APIs. - * - Type check as much as possible. - * - UI - * - Configurable appearance. - * - Can display helper and error messages. - * - Easy to extend, like new input types. - * - * So here is some design decisions: - * Inputs are identified by its _key_. - * `InputGroup` component takes care of only UI and no logic. - * `useInputs` hook takes care of logic and generate props for `InputGroup`. - */ - -import { useState, Ref, useId } from "react"; -import classNames from "classnames"; - -import { useC, Text, ThemeColor } from "../common"; - -import "./InputGroup.css"; - -export interface InputBase { - key: string; - label: Text; - helper?: Text; - disabled?: boolean; - error?: Text; -} - -export interface TextInput extends InputBase { - type: "text"; - value: string; - password?: boolean; -} - -export interface BoolInput extends InputBase { - type: "bool"; - value: boolean; -} - -export interface SelectInputOption { - value: string; - label: Text; - icon?: string; -} - -export interface SelectInput extends InputBase { - type: "select"; - value: string; - options: SelectInputOption[]; -} - -export type Input = TextInput | BoolInput | SelectInput; - -export type InputValue = Input["value"]; - -export type InputValueDict = Record; -export type InputErrorDict = Record; -export type InputDisabledDict = Record; -export type InputDirtyDict = Record; - -export type GeneralInputErrorDict = - | { - [key: string]: Text | null | undefined; - } - | null - | undefined; - -type MakeInputInfo = Omit; - -export type InputInfo = { - [I in Input as I["type"]]: MakeInputInfo; -}[Input["type"]]; - -export type Validator = ( - values: InputValueDict, - inputs: InputInfo[], -) => GeneralInputErrorDict; - -export type InputScheme = { - inputs: InputInfo[]; - validator?: Validator; -}; - -export type InputData = { - values: InputValueDict; - errors: InputErrorDict; - disabled: InputDisabledDict; - dirties: InputDirtyDict; -}; - -export type State = { - scheme: InputScheme; - data: InputData; -}; - -export type DataInitialization = { - values?: InputValueDict; - errors?: GeneralInputErrorDict; - disabled?: InputDisabledDict; - dirties?: InputDirtyDict; -}; - -export type Initialization = { - scheme: InputScheme; - dataInit?: DataInitialization; -}; - -export type GeneralInitialization = Initialization | InputScheme | InputInfo[]; - -export type Initializer = GeneralInitialization | (() => GeneralInitialization); - -export interface InputGroupProps { - color?: ThemeColor; - containerClassName?: string; - containerRef?: Ref; - - inputs: Input[]; - onChange: (index: number, value: Input["value"]) => void; -} - -function cleanObject(o: Record): Record> { - const result = { ...o }; - for (const key of Object.keys(result)) { - if (result[key] == null) { - delete result[key]; - } - } - return result as never; -} - -export type ConfirmResult = - | { - type: "ok"; - values: InputValueDict; - } - | { - type: "error"; - errors: InputErrorDict; - }; - -function validate( - validator: Validator | null | undefined, - values: InputValueDict, - inputs: InputInfo[], -): InputErrorDict { - return cleanObject(validator?.(values, inputs) ?? {}); -} - -export function useInputs(options: { init: Initializer }): { - inputGroupProps: InputGroupProps; - hasError: boolean; - hasErrorAndDirty: boolean; - confirm: () => ConfirmResult; - setAllDisabled: (disabled: boolean) => void; -} { - function initializeValue( - input: InputInfo, - value?: InputValue | null, - ): InputValue { - if (input.type === "text") { - return value ?? ""; - } else if (input.type === "bool") { - return value ?? false; - } else if (input.type === "select") { - return value ?? input.options[0].value; - } - throw new Error("Unknown input type"); - } - - function initialize(generalInitialization: GeneralInitialization): State { - const initialization: Initialization = Array.isArray(generalInitialization) - ? { scheme: { inputs: generalInitialization } } - : "scheme" in generalInitialization - ? generalInitialization - : { scheme: generalInitialization }; - - const { scheme, dataInit } = initialization; - const { inputs, validator } = scheme; - const keys = inputs.map((input) => input.key); - - if (process.env.NODE_ENV === "development") { - const checkKeys = (dict: Record | undefined) => { - if (dict != null) { - for (const key of Object.keys(dict)) { - if (!keys.includes(key)) { - console.warn(""); - } - } - } - }; - - checkKeys(dataInit?.values); - checkKeys(dataInit?.errors ?? {}); - checkKeys(dataInit?.disabled); - checkKeys(dataInit?.dirties); - } - - function clean( - dict: Record | null | undefined, - ): Record> { - return dict != null ? cleanObject(dict) : {}; - } - - const values: InputValueDict = {}; - const disabled: InputDisabledDict = clean(dataInit?.disabled); - const dirties: InputDirtyDict = clean(dataInit?.dirties); - const isErrorSet = dataInit?.errors != null; - let errors: InputErrorDict = clean(dataInit?.errors); - - for (let i = 0; i < inputs.length; i++) { - const input = inputs[i]; - const { key } = input; - - values[key] = initializeValue(input, dataInit?.values?.[key]); - } - - if (isErrorSet) { - if (process.env.NODE_ENV === "development") { - console.log( - "You explicitly set errors (not undefined) in initializer, so validator won't run.", - ); - } - } else { - errors = validate(validator, values, inputs); - } - - return { - scheme, - data: { - values, - errors, - disabled, - dirties, - }, - }; - } - - const { init } = options; - const initializer = typeof init === "function" ? init : () => init; - - const [state, setState] = useState(() => initialize(initializer())); - - const { scheme, data } = state; - const { validator } = scheme; - - function createAllBooleanDict(value: boolean): Record { - const result: InputDirtyDict = {}; - for (const key of scheme.inputs.map((input) => input.key)) { - result[key] = value; - } - return result; - } - - const createAllDirties = () => createAllBooleanDict(true); - - const componentInputs: Input[] = []; - - for (let i = 0; i < scheme.inputs.length; i++) { - const input = scheme.inputs[i]; - const value = data.values[input.key]; - const error = data.errors[input.key]; - const disabled = data.disabled[input.key] ?? false; - const dirty = data.dirties[input.key] ?? false; - const componentInput: Input = { - ...input, - value: value as never, - disabled, - error: dirty ? error : undefined, - }; - componentInputs.push(componentInput); - } - - const hasError = Object.keys(data.errors).length > 0; - const hasDirty = Object.keys(data.dirties).some((key) => data.dirties[key]); - - return { - inputGroupProps: { - inputs: componentInputs, - onChange: (index, value) => { - const input = scheme.inputs[index]; - const { key } = input; - const newValues = { ...data.values, [key]: value }; - const newDirties = { ...data.dirties, [key]: true }; - const newErrors = validate(validator, newValues, scheme.inputs); - setState({ - scheme, - data: { - ...data, - values: newValues, - errors: newErrors, - dirties: newDirties, - }, - }); - }, - }, - hasError, - hasErrorAndDirty: hasError && hasDirty, - confirm() { - const newDirties = createAllDirties(); - const newErrors = validate(validator, data.values, scheme.inputs); - - setState({ - scheme, - data: { - ...data, - dirties: newDirties, - errors: newErrors, - }, - }); - - if (Object.keys(newErrors).length !== 0) { - return { - type: "error", - errors: newErrors, - }; - } else { - return { - type: "ok", - values: data.values, - }; - } - }, - setAllDisabled(disabled: boolean) { - setState({ - scheme, - data: { - ...data, - disabled: createAllBooleanDict(disabled), - }, - }); - }, - }; -} - -export function InputGroup({ - color, - inputs, - onChange, - containerRef, - containerClassName, -}: InputGroupProps) { - const c = useC(); - - const id = useId(); - - return ( -
- {inputs.map((item, index) => { - const { key, type, value, label, error, helper, disabled } = item; - - const getContainerClassName = ( - ...additionalClassNames: classNames.ArgumentArray - ) => - classNames( - `cru-input-container cru-input-type-${type}`, - error && "error", - ...additionalClassNames, - ); - - const changeValue = (value: InputValue) => { - onChange(index, value); - }; - - const inputId = `${id}-${key}`; - - if (type === "text") { - const { password } = item; - return ( -
- {label && ( - - )} - { - const v = event.target.value; - changeValue(v); - }} - disabled={disabled} - /> - {error &&
{c(error)}
} - {helper &&
{c(helper)}
} -
- ); - } else if (type === "bool") { - return ( -
- { - const v = event.currentTarget.checked; - changeValue(v); - }} - disabled={disabled} - /> - - {error &&
{c(error)}
} - {helper &&
{c(helper)}
} -
- ); - } else if (type === "select") { - return ( -
- - -
- ); - } - })} -
- ); -} diff --git a/FrontEnd/src/views/common/list/ListContainer.css b/FrontEnd/src/views/common/list/ListContainer.css deleted file mode 100644 index 53781834..00000000 --- a/FrontEnd/src/views/common/list/ListContainer.css +++ /dev/null @@ -1,4 +0,0 @@ -.cru-list-container { - border: 1px solid var(--cru-clickable-primary-normal-color); - border-radius: 5px; -} diff --git a/FrontEnd/src/views/common/list/ListContainer.tsx b/FrontEnd/src/views/common/list/ListContainer.tsx deleted file mode 100644 index aa00d12c..00000000 --- a/FrontEnd/src/views/common/list/ListContainer.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentPropsWithoutRef, forwardRef, Ref } from "react"; -import classNames from "classnames"; - -import "./ListContainer.css" - -function _ListContainer( - { className, children, ...otherProps }: ComponentPropsWithoutRef<"div">, - ref: Ref, -) { - return ( -
- {children} -
- ); -} - -const ListContainer = forwardRef(_ListContainer); - -export default ListContainer; diff --git a/FrontEnd/src/views/common/list/ListItemContainer.css b/FrontEnd/src/views/common/list/ListItemContainer.css deleted file mode 100644 index 8d7afa9f..00000000 --- a/FrontEnd/src/views/common/list/ListItemContainer.css +++ /dev/null @@ -1,3 +0,0 @@ -.cru-list-item-container { - border: 1px solid var(--cru-clickable-primary-normal-color); -} diff --git a/FrontEnd/src/views/common/list/ListItemContainer.tsx b/FrontEnd/src/views/common/list/ListItemContainer.tsx deleted file mode 100644 index 315cbd6e..00000000 --- a/FrontEnd/src/views/common/list/ListItemContainer.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentPropsWithoutRef, forwardRef, Ref } from "react"; -import classNames from "classnames"; - -import "./ListItemContainer.css"; - -function _ListItemContainer( - { className, children, ...otherProps }: ComponentPropsWithoutRef<"div">, - ref: Ref, -) { - return ( -
- {children} -
- ); -} - -const ListItemContainer = forwardRef(_ListItemContainer); - -export default ListItemContainer; diff --git a/FrontEnd/src/views/common/list/index.ts b/FrontEnd/src/views/common/list/index.ts deleted file mode 100644 index e183f7da..00000000 --- a/FrontEnd/src/views/common/list/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import ListContainer from "./ListContainer"; -import ListItemContainer from "./ListItemContainer"; - -export { ListContainer, ListItemContainer }; diff --git a/FrontEnd/src/views/common/menu/Menu.css b/FrontEnd/src/views/common/menu/Menu.css deleted file mode 100644 index 75734533..00000000 --- a/FrontEnd/src/views/common/menu/Menu.css +++ /dev/null @@ -1,36 +0,0 @@ -.cru-menu { - min-width: 200px; -} - -.cru-menu-item { - display: block; - font-size: 1em; - width: 100%; - padding: 0.5em 1.5em; - transition: all 0.5s; - color: var(--cru-clickable-normal-color); - background-color: var(--cru-clickable-grayscale-normal-color); - border: none; - cursor: pointer; -} - -.cru-menu-item:hover { - background-color: var(--cru-clickable-grayscale-hover-color); -} - -.cru-menu-item:focus { - background-color: var(--cru-clickable-grayscale-focus-color); -} - -.cru-menu-item:active { - background-color: var(--cru-clickable-grayscale-active-color); -} - -.cru-menu-item-icon { - margin-right: 1em; -} - -.cru-menu-divider { - border-width: 0; - border-top: 1px solid var(--cru-primary-color); -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/menu/Menu.tsx b/FrontEnd/src/views/common/menu/Menu.tsx deleted file mode 100644 index e8099c76..00000000 --- a/FrontEnd/src/views/common/menu/Menu.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { CSSProperties } from "react"; -import classNames from "classnames"; - -import { useC, Text, ThemeColor } from "../common"; - -import "./Menu.css"; -import Icon from "../Icon"; - -export type MenuItem = - | { - type: "divider"; - } - | { - type: "button"; - text: Text; - icon?: string; - color?: ThemeColor; - onClick: () => void; - }; - -export type MenuItems = MenuItem[]; - -export type MenuProps = { - items: MenuItems; - onItemClicked?: () => void; - className?: string; - style?: CSSProperties; -}; - -export default function Menu({ - items, - onItemClicked, - className, - style, -}: MenuProps) { - const c = useC(); - - return ( -
- {items.map((item, index) => { - if (item.type === "divider") { - return
; - } else { - const { text, color, icon, onClick } = item; - return ( - - ); - } - })} -
- ); -} diff --git a/FrontEnd/src/views/common/menu/PopupMenu.css b/FrontEnd/src/views/common/menu/PopupMenu.css deleted file mode 100644 index 149e0699..00000000 --- a/FrontEnd/src/views/common/menu/PopupMenu.css +++ /dev/null @@ -1,7 +0,0 @@ -.cru-popup-menu-menu-container { - z-index: 1040; - border-radius: 3px; - border: var(--cru-clickable-normal-color) 1.5px solid; - background-color: var(--cru-background-color); - overflow: hidden; -} diff --git a/FrontEnd/src/views/common/menu/PopupMenu.tsx b/FrontEnd/src/views/common/menu/PopupMenu.tsx deleted file mode 100644 index 5c8d5e98..00000000 --- a/FrontEnd/src/views/common/menu/PopupMenu.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useState, CSSProperties, ReactNode } from "react"; -import classNames from "classnames"; -import { createPortal } from "react-dom"; -import { usePopper } from "react-popper"; - -import { useClickOutside } from "@/utilities/hooks"; - -import Menu, { MenuItems } from "./Menu"; - -import { ThemeColor } from "../common"; - -import "./PopupMenu.css"; - -export interface PopupMenuProps { - color?: ThemeColor; - items: MenuItems; - children?: ReactNode; - containerClassName?: string; - containerStyle?: CSSProperties; -} - -export default function PopupMenu({ - color, - items, - children, - containerClassName, - containerStyle, -}: PopupMenuProps) { - const [show, setShow] = useState(false); - - const [referenceElement, setReferenceElement] = - useState(null); - const [popperElement, setPopperElement] = useState( - null, - ); - const { styles, attributes } = usePopper(referenceElement, popperElement); - - useClickOutside(popperElement, () => setShow(false), true); - - return ( -
setShow(true)} - > - {children} - {show && - createPortal( -
- { - setShow(false); - }} - /> -
, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - document.getElementById("portal")!, - )} -
- ); -} diff --git a/FrontEnd/src/views/common/tab/TabPages.tsx b/FrontEnd/src/views/common/tab/TabPages.tsx deleted file mode 100644 index cdb988e0..00000000 --- a/FrontEnd/src/views/common/tab/TabPages.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import * as React from "react"; - -import { I18nText, UiLogicError } from "@/common"; - -import Tabs from "./Tabs"; - -export interface TabPage { - name: string; - text: I18nText; - page: React.ReactNode; -} - -export interface TabPagesProps { - pages: TabPage[]; - actions?: React.ReactNode; - dense?: boolean; - className?: string; - style?: React.CSSProperties; - navClassName?: string; - navStyle?: React.CSSProperties; - pageContainerClassName?: string; - pageContainerStyle?: React.CSSProperties; -} - -const TabPages: React.FC = ({ - pages, - actions, - dense, - className, - style, - navClassName, - navStyle, - pageContainerClassName, - pageContainerStyle, -}) => { - if (pages.length === 0) { - throw new UiLogicError("Page list can't be empty."); - } - - const [tab, setTab] = React.useState(pages[0].name); - - const currentPage = pages.find((p) => p.name === tab); - - if (currentPage == null) { - throw new UiLogicError("Current tab value is bad."); - } - - return ( -
- ({ - name: page.name, - text: page.text, - onClick: () => { - setTab(page.name); - }, - }))} - dense={dense} - activeTabName={tab} - className={navClassName} - style={navStyle} - actions={actions} - /> -
- {currentPage.page} -
-
- ); -}; - -export default TabPages; diff --git a/FrontEnd/src/views/common/tab/Tabs.css b/FrontEnd/src/views/common/tab/Tabs.css deleted file mode 100644 index 395d16a7..00000000 --- a/FrontEnd/src/views/common/tab/Tabs.css +++ /dev/null @@ -1,33 +0,0 @@ -.cru-nav { - border-bottom: var(--cru-primary-color) 1px solid; - display: flex; -} - -.cru-nav-item { - color: var(--cru-primary-color); - border: var(--cru-background-2-color) 0.5px solid; - border-bottom: none; - padding: 0.5em 1.5em; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - transition: all 0.5s; - cursor: pointer; -} - -.cru-nav.dense .cru-nav-item { - padding: 0.2em 1em; -} - -.cru-nav-item:hover { - background-color: var(--cru-background-1-color); -} - -.cru-nav-item.active { - color: var(--cru-primary-t-color); - background-color: var(--cru-primary-color); - border-color: var(--cru-primary-color); -} - -.cru-nav-action-area { - margin-left: auto; -} diff --git a/FrontEnd/src/views/common/tab/Tabs.tsx b/FrontEnd/src/views/common/tab/Tabs.tsx deleted file mode 100644 index 3e3ef6fa..00000000 --- a/FrontEnd/src/views/common/tab/Tabs.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from "react"; -import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import classnames from "classnames"; - -import { convertI18nText, I18nText } from "@/common"; - -import "./Tabs.css"; - -export interface Tab { - name: string; - text: I18nText; - link?: string; - onClick?: () => void; -} - -export interface TabsProps { - activeTabName?: string; - actions?: React.ReactNode; - dense?: boolean; - tabs: Tab[]; - className?: string; - style?: React.CSSProperties; -} - -export default function Tabs(props: TabsProps): React.ReactElement | null { - const { tabs, activeTabName, className, style, dense, actions } = props; - - const { t } = useTranslation(); - - return ( -
- {tabs.map((tab) => { - const active = activeTabName === tab.name; - const className = classnames("cru-nav-item", active && "active"); - - if (tab.link != null) { - return ( - - {convertI18nText(tab.text, t)} - - ); - } else { - return ( - - {convertI18nText(tab.text, t)} - - ); - } - })} -
{actions}
-
- ); -} diff --git a/FrontEnd/src/views/common/theme-color.css b/FrontEnd/src/views/common/theme-color.css deleted file mode 100644 index 24a7e267..00000000 --- a/FrontEnd/src/views/common/theme-color.css +++ /dev/null @@ -1,173 +0,0 @@ -/* Generated by theme-generator.ts */ - -:root { - --cru-primary-color: hsl(210 100% 40%); - --cru-primary-1-color: hsl(210 100% 37%); - --cru-primary-2-color: hsl(210 100% 34%); - --cru-primary-on-color: hsl(210 100% 100%); - --cru-primary-container-color: hsl(210 100% 90%); - --cru-primary-container-1-color: hsl(210 100% 80%); - --cru-primary-container-2-color: hsl(210 100% 70%); - --cru-primary-on-container-color: hsl(210 100% 10%); - --cru-secondary-color: hsl(40 100% 40%); - --cru-secondary-1-color: hsl(40 100% 37%); - --cru-secondary-2-color: hsl(40 100% 34%); - --cru-secondary-on-color: hsl(40 100% 100%); - --cru-secondary-container-color: hsl(40 100% 90%); - --cru-secondary-container-1-color: hsl(40 100% 80%); - --cru-secondary-container-2-color: hsl(40 100% 70%); - --cru-secondary-on-container-color: hsl(40 100% 10%); - --cru-tertiary-color: hsl(160 100% 40%); - --cru-tertiary-1-color: hsl(160 100% 37%); - --cru-tertiary-2-color: hsl(160 100% 34%); - --cru-tertiary-on-color: hsl(160 100% 100%); - --cru-tertiary-container-color: hsl(160 100% 90%); - --cru-tertiary-container-1-color: hsl(160 100% 80%); - --cru-tertiary-container-2-color: hsl(160 100% 70%); - --cru-tertiary-on-container-color: hsl(160 100% 10%); - --cru-danger-color: hsl(0 100% 40%); - --cru-danger-1-color: hsl(0 100% 37%); - --cru-danger-2-color: hsl(0 100% 34%); - --cru-danger-on-color: hsl(0 100% 100%); - --cru-danger-container-color: hsl(0 100% 90%); - --cru-danger-container-1-color: hsl(0 100% 80%); - --cru-danger-container-2-color: hsl(0 100% 70%); - --cru-danger-on-container-color: hsl(0 100% 10%); - --cru-success-color: hsl(120 60% 40%); - --cru-success-1-color: hsl(120 60% 37%); - --cru-success-2-color: hsl(120 60% 34%); - --cru-success-on-color: hsl(120 60% 100%); - --cru-success-container-color: hsl(120 60% 90%); - --cru-success-container-1-color: hsl(120 60% 80%); - --cru-success-container-2-color: hsl(120 60% 70%); - --cru-success-on-container-color: hsl(120 60% 10%); - --cru-surface-dim-color: hsl(0 0% 87%); - --cru-surface-color: hsl(0 0% 98%); - --cru-surface-1-color: hsl(0 0% 90%); - --cru-surface-2-color: hsl(0 0% 82%); - --cru-surface-bright-color: hsl(0 0% 98%); - --cru-surface-container-lowest-color: hsl(0 0% 100%); - --cru-surface-container-low-color: hsl(0 0% 96%); - --cru-surface-container-color: hsl(0 0% 94%); - --cru-surface-container-high-color: hsl(0 0% 92%); - --cru-surface-container-highest-color: hsl(0 0% 90%); - --cru-surface-on-color: hsl(0 0% 10%); - --cru-surface-on-variant-color: hsl(0 0% 30%); - --cru-surface-outline-color: hsl(0 0% 50%); - --cru-surface-outline-variant-color: hsl(0 0% 80%); -} - -@media (prefers-color-scheme: dark) { - :root { - --cru-primary-color: hsl(210 100% 80%); - --cru-primary-1-color: hsl(210 100% 75%); - --cru-primary-2-color: hsl(210 100% 68%); - --cru-primary-on-color: hsl(210 100% 20%); - --cru-primary-container-color: hsl(210 100% 30%); - --cru-primary-container-1-color: hsl(210 100% 25%); - --cru-primary-container-2-color: hsl(210 100% 20%); - --cru-primary-on-container-color: hsl(210 100% 90%); - --cru-secondary-color: hsl(40 100% 80%); - --cru-secondary-1-color: hsl(40 100% 75%); - --cru-secondary-2-color: hsl(40 100% 68%); - --cru-secondary-on-color: hsl(40 100% 20%); - --cru-secondary-container-color: hsl(40 100% 30%); - --cru-secondary-container-1-color: hsl(40 100% 25%); - --cru-secondary-container-2-color: hsl(40 100% 20%); - --cru-secondary-on-container-color: hsl(40 100% 90%); - --cru-tertiary-color: hsl(160 100% 80%); - --cru-tertiary-1-color: hsl(160 100% 75%); - --cru-tertiary-2-color: hsl(160 100% 68%); - --cru-tertiary-on-color: hsl(160 100% 20%); - --cru-tertiary-container-color: hsl(160 100% 30%); - --cru-tertiary-container-1-color: hsl(160 100% 25%); - --cru-tertiary-container-2-color: hsl(160 100% 20%); - --cru-tertiary-on-container-color: hsl(160 100% 90%); - --cru-danger-color: hsl(0 100% 80%); - --cru-danger-1-color: hsl(0 100% 75%); - --cru-danger-2-color: hsl(0 100% 68%); - --cru-danger-on-color: hsl(0 100% 20%); - --cru-danger-container-color: hsl(0 100% 30%); - --cru-danger-container-1-color: hsl(0 100% 25%); - --cru-danger-container-2-color: hsl(0 100% 20%); - --cru-danger-on-container-color: hsl(0 100% 90%); - --cru-success-color: hsl(120 60% 80%); - --cru-success-1-color: hsl(120 60% 75%); - --cru-success-2-color: hsl(120 60% 68%); - --cru-success-on-color: hsl(120 60% 20%); - --cru-success-container-color: hsl(120 60% 30%); - --cru-success-container-1-color: hsl(120 60% 25%); - --cru-success-container-2-color: hsl(120 60% 20%); - --cru-success-on-container-color: hsl(120 60% 90%); - --cru-surface-dim-color: hsl(0 0% 6%); - --cru-surface-color: hsl(0 0% 6%); - --cru-surface-1-color: hsl(0 0% 25%); - --cru-surface-2-color: hsl(0 0% 40%); - --cru-surface-bright-color: hsl(0 0% 24%); - --cru-surface-container-lowest-color: hsl(0 0% 4%); - --cru-surface-container-low-color: hsl(0 0% 10%); - --cru-surface-container-color: hsl(0 0% 12%); - --cru-surface-container-high-color: hsl(0 0% 17%); - --cru-surface-container-highest-color: hsl(0 0% 22%); - --cru-surface-on-color: hsl(0 0% 90%); - --cru-surface-on-variant-color: hsl(0 0% 80%); - --cru-surface-outline-color: hsl(0 0% 60%); - --cru-surface-outline-variant-color: hsl(0 0% 30%); - } -} - -.cru-primary { - --cru-key-color: var(--cru-primary-color); - --cru-key-1-color: var(--cru-primary-1-color); - --cru-key-2-color: var(--cru-primary-2-color); - --cru-key-on-color: var(--cru-primary-on-color); - --cru-key-container-color: var(--cru-primary-container-color); - --cru-key-container-1-color: var(--cru-primary-container-1-color); - --cru-key-container-2-color: var(--cru-primary-container-2-color); - --cru-key-on-container-color: var(--cru-primary-on-container-color); -} - -.cru-secondary { - --cru-key-color: var(--cru-secondary-color); - --cru-key-1-color: var(--cru-secondary-1-color); - --cru-key-2-color: var(--cru-secondary-2-color); - --cru-key-on-color: var(--cru-secondary-on-color); - --cru-key-container-color: var(--cru-secondary-container-color); - --cru-key-container-1-color: var(--cru-secondary-container-1-color); - --cru-key-container-2-color: var(--cru-secondary-container-2-color); - --cru-key-on-container-color: var(--cru-secondary-on-container-color); -} - -.cru-tertiary { - --cru-key-color: var(--cru-tertiary-color); - --cru-key-1-color: var(--cru-tertiary-1-color); - --cru-key-2-color: var(--cru-tertiary-2-color); - --cru-key-on-color: var(--cru-tertiary-on-color); - --cru-key-container-color: var(--cru-tertiary-container-color); - --cru-key-container-1-color: var(--cru-tertiary-container-1-color); - --cru-key-container-2-color: var(--cru-tertiary-container-2-color); - --cru-key-on-container-color: var(--cru-tertiary-on-container-color); -} - -.cru-danger { - --cru-key-color: var(--cru-danger-color); - --cru-key-1-color: var(--cru-danger-1-color); - --cru-key-2-color: var(--cru-danger-2-color); - --cru-key-on-color: var(--cru-danger-on-color); - --cru-key-container-color: var(--cru-danger-container-color); - --cru-key-container-1-color: var(--cru-danger-container-1-color); - --cru-key-container-2-color: var(--cru-danger-container-2-color); - --cru-key-on-container-color: var(--cru-danger-on-container-color); -} - -.cru-success { - --cru-key-color: var(--cru-success-color); - --cru-key-1-color: var(--cru-success-1-color); - --cru-key-2-color: var(--cru-success-2-color); - --cru-key-on-color: var(--cru-success-on-color); - --cru-key-container-color: var(--cru-success-container-color); - --cru-key-container-1-color: var(--cru-success-container-1-color); - --cru-key-container-2-color: var(--cru-success-container-2-color); - --cru-key-on-container-color: var(--cru-success-on-container-color); -} - diff --git a/FrontEnd/src/views/common/theme.css b/FrontEnd/src/views/common/theme.css deleted file mode 100644 index 6ceb369f..00000000 --- a/FrontEnd/src/views/common/theme.css +++ /dev/null @@ -1,146 +0,0 @@ -@import "./theme-color.css"; - -:root { - --cru-default-font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - --cru-page-padding: 1em 2em; - - --cru-border-radius: 4px; - --cru-card-border-radius: 4px; -} - -/* theme colors */ -:root { - --cru-primary-color: hsl(210, 100%, 50%); - --cru-secondary-color: hsl(30, 100%, 50%); - --cru-create-color: hsl(120, 100%, 25%); - --cru-danger-color: hsl(0, 100%, 50%); -} - -/* common colors */ -:root { - --cru-background-color: hsl(0, 0%, 100%); - --cru-container-background-color: hsl(0, 0%, 97%); - --cru-text-primary-color: hsl(0, 0%, 0%); - --cru-text-secondary-color: hsl(0, 0%, 38%); -} - -@media (prefers-color-scheme: dark) { - :root { - --cru-background-color: hsl(0, 0%, 0%); - --cru-container-background-color: hsl(0, 0%, 2%); - --cru-text-primary-color: hsl(0, 0%, 100%); - --cru-text-secondary-color: hsl(0, 0%, 85%); - } -} - -:root { - --cru-body-background-color: var(--cru-background-color); -} - -/* clickable color */ -:root { - --cru-clickable-primary-normal-color: var(--cru-primary-color); - --cru-clickable-primary-hover-color: hsl(210, 100%, 60%); - --cru-clickable-primary-focus-color: hsl(210, 100%, 60%); - --cru-clickable-primary-active-color: hsl(210, 100%, 70%); - --cru-clickable-secondary-normal-color: var(--cru-secondary-color); - --cru-clickable-secondary-hover-color: hsl(30, 100%, 60%); - --cru-clickable-secondary-focus-color: hsl(30, 100%, 60%); - --cru-clickable-secondary-active-color: hsl(30, 100%, 70%); - --cru-clickable-create-normal-color: var(--cru-create-color); - --cru-clickable-create-hover-color: hsl(120, 100%, 35%); - --cru-clickable-create-focus-color: hsl(120, 100%, 35%); - --cru-clickable-create-active-color: hsl(120, 100%, 35%); - --cru-clickable-danger-normal-color: var(--cru-danger-color); - --cru-clickable-danger-hover-color: hsl(0, 100%, 60%); - --cru-clickable-danger-focus-color: hsl(0, 100%, 60%); - --cru-clickable-danger-active-color: hsl(0, 100%, 70%); - --cru-clickable-grayscale-normal-color: hsl(0, 0%, 100%); - --cru-clickable-grayscale-hover-color: hsl(0, 0%, 92%); - --cru-clickable-grayscale-focus-color: hsl(0, 0%, 92%); - --cru-clickable-grayscale-active-color: hsl(0, 0%, 88%); - --cru-clickable-disabled-color: hsl(0, 0%, 50%); -} - -@media (prefers-color-scheme: dark) { - :root { - --cru-clickable-grayscale-normal-color: hsl(0, 0%, 0%); - --cru-clickable-grayscale-hover-color: hsl(0, 0%, 10%); - --cru-clickable-grayscale-focus-color: hsl(0, 0%, 10%); - --cru-clickable-grayscale-active-color: hsl(0, 0%, 20%); - } -} - -.cru-clickable-primary { - --cru-clickable-normal-color: var(--cru-clickable-primary-normal-color); - --cru-clickable-hover-color: var(--cru-clickable-primary-hover-color); - --cru-clickable-focus-color: var(--cru-clickable-primary-focus-color); - --cru-clickable-active-color: var(--cru-clickable-primary-active-color); -} - -.cru-clickable-secondary { - --cru-clickable-normal-color: var(--cru-clickable-secondary-normal-color); - --cru-clickable-hover-color: var(--cru-clickable-secondary-hover-color); - --cru-clickable-focus-color: var(--cru-clickable-secondary-focus-color); - --cru-clickable-active-color: var(--cru-clickable-secondary-active-color); -} - -.cru-clickable-create { - --cru-clickable-normal-color: var(--cru-clickable-create-normal-color); - --cru-clickable-hover-color: var(--cru-clickable-create-hover-color); - --cru-clickable-focus-color: var(--cru-clickable-create-focus-color); - --cru-clickable-active-color: var(--cru-clickable-create-active-color); -} - -.cru-clickable-danger { - --cru-clickable-normal-color: var(--cru-clickable-danger-normal-color); - --cru-clickable-hover-color: var(--cru-clickable-danger-hover-color); - --cru-clickable-focus-color: var(--cru-clickable-danger-focus-color); - --cru-clickable-active-color: var(--cru-clickable-danger-active-color); -} - -.cru-clickable-grayscale { - --cru-clickable-normal-color: var(--cru-clickable-grayscale-normal-color); - --cru-clickable-hover-color: var(--cru-clickable-grayscale-hover-color); - --cru-clickable-focus-color: var(--cru-clickable-grayscale-focus-color); - --cru-clickable-active-color: var(--cru-clickable-grayscale-active-color); -} - -/* button colors */ -:root { - /* push button colors */ - --cru-push-button-text-color: #ffffff; - --cru-push-button-disabled-text-color: hsl(0, 0%, 80%); -} - -/* Card colors */ -:root { - --cru-card-background-primary-color: hsl(210, 100%, 50%); - --cru-card-border-primary-color: hsl(210, 100%, 50%); - --cru-card-background-secondary-color: hsl(30, 100%, 50%); - --cru-card-border-secondary-color: hsl(30, 100%, 50%); - --cru-card-background-create-color: hsl(120, 100%, 25%); - --cru-card-border-create-color: hsl(120, 100%, 25%); - --cru-card-background-danger-color: hsl(0, 100%, 50%); - --cru-card-border-danger-color: hsl(0, 100%, 50%); -} - -.cru-card-primary { - --cru-card-background-color: var(--cru-card-background-primary-color); - --cru-card-border-color: var(--cru-card-border-primary-color) -} - -.cru-card-secondary { - --cru-card-background-color: var(--cru-card-background-secondary-color); - --cru-card-border-color: var(--cru-card-border-secondary-color) -} - -.cru-card-create { - --cru-card-background-color: var(--cru-card-background-create-color); - --cru-card-border-color: var(--cru-card-border-create-color) -} - -.cru-card-danger { - --cru-card-background-color: var(--cru-card-background-danger-color); - --cru-card-border-color: var(--cru-card-border-danger-color) -} \ No newline at end of file diff --git a/FrontEnd/src/views/common/user/UserAvatar.tsx b/FrontEnd/src/views/common/user/UserAvatar.tsx deleted file mode 100644 index aea7bd48..00000000 --- a/FrontEnd/src/views/common/user/UserAvatar.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Ref, ComponentPropsWithoutRef } from "react"; - -import { getHttpUserClient } from "@/http/user"; - -export interface UserAvatarProps extends ComponentPropsWithoutRef<"img"> { - username: string; - imgRef?: Ref | null; -} - -export default function UserAvatar({ - username, - imgRef, - ...otherProps -}: UserAvatarProps) { - return ( - - ); -} -- cgit v1.2.3