aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-07-12 15:25:15 +0800
committerGitHub <noreply@github.com>2023-07-12 15:25:15 +0800
commit4a069bf1268f393d5467166356f691eb89963152 (patch)
tree1bfa25fda4bd970a609c161b18e6616b5d5e8221 /FrontEnd
parent78f0934815a87573289c8e52af2666ea38c93251 (diff)
parent7781eede43be5fa277305ce9bd51bfc6a2a6ff46 (diff)
downloadtimeline-4a069bf1268f393d5467166356f691eb89963152.tar.gz
timeline-4a069bf1268f393d5467166356f691eb89963152.tar.bz2
timeline-4a069bf1268f393d5467166356f691eb89963152.zip
Merge pull request #1386 from crupest/dev
Develop.
Diffstat (limited to 'FrontEnd')
-rw-r--r--FrontEnd/src/common.ts31
-rw-r--r--FrontEnd/src/i18n.ts38
-rw-r--r--FrontEnd/src/utilities/hooks/use-c.ts7
-rw-r--r--FrontEnd/src/views/common/button/Button.tsx42
-rw-r--r--FrontEnd/src/views/common/button/FlatButton.tsx32
-rw-r--r--FrontEnd/src/views/common/button/IconButton.css3
-rw-r--r--FrontEnd/src/views/common/button/IconButton.tsx8
-rw-r--r--FrontEnd/src/views/common/dialog/Dialog.tsx21
8 files changed, 105 insertions, 77 deletions
diff --git a/FrontEnd/src/common.ts b/FrontEnd/src/common.ts
index b819d209..965f9933 100644
--- a/FrontEnd/src/common.ts
+++ b/FrontEnd/src/common.ts
@@ -1,33 +1,10 @@
-import { TFunction } from "i18next";
-
// This error is thrown when ui goes wrong with bad logic.
// Such as a variable should not be null, but it does.
// This error should never occur. If it does, it indicates there is some logic bug in codes.
export class UiLogicError extends Error {}
-export type I18nText =
- | string
- | { type: "custom"; value: string }
- | { type: "i18n"; value: string };
-
-export function convertI18nText(text: I18nText, t: TFunction): string;
-export function convertI18nText(
- text: I18nText | null | undefined,
- t: TFunction
-): string | null;
-export function convertI18nText(
- text: I18nText | null | undefined,
- t: TFunction
-): string | null {
- if (text == null) {
- return null;
- } else if (typeof text === "string") {
- return t(text);
- } else if (text.type === "i18n") {
- return t(text.value);
- } else {
- return text.value;
- }
-}
-
export const highlightTimelineUsername = "crupest";
+
+export type { I18nText } from "./i18n";
+export { c, convertI18nText } from "./i18n";
+export { default as useC } from "./utilities/hooks/use-c";
diff --git a/FrontEnd/src/i18n.ts b/FrontEnd/src/i18n.ts
index 59b6f64a..3166ec3c 100644
--- a/FrontEnd/src/i18n.ts
+++ b/FrontEnd/src/i18n.ts
@@ -74,3 +74,41 @@ if (module.hot) {
}
export default i18n;
+
+export type I18nText =
+ | string
+ | { type: "text" | "custom"; value: string }
+ | { type: "i18n"; value: string };
+
+type T = typeof i18n.t;
+
+export function convertI18nText(text: I18nText, t: T): string;
+export function convertI18nText(
+ text: I18nText | null | undefined,
+ t: T,
+): string | null;
+export function convertI18nText(
+ text: I18nText | null | undefined,
+ t: T,
+): string | null {
+ if (text == null) {
+ return null;
+ } else if (typeof text === "string") {
+ return t(text);
+ } else if (text.type === "i18n") {
+ return t(text.value);
+ } else {
+ return text.value;
+ }
+}
+
+export interface C {
+ (text: I18nText): string;
+ (text: I18nText | null | undefined): string | null;
+}
+
+export function createC(t: T): C {
+ return ((text) => convertI18nText(text, t)) as C;
+}
+
+export const c = createC(i18n.t);
diff --git a/FrontEnd/src/utilities/hooks/use-c.ts b/FrontEnd/src/utilities/hooks/use-c.ts
new file mode 100644
index 00000000..96195ae2
--- /dev/null
+++ b/FrontEnd/src/utilities/hooks/use-c.ts
@@ -0,0 +1,7 @@
+import { useTranslation } from "react-i18next";
+import { C, createC } from "../../i18n";
+
+export default function useC(ns?: string): C {
+ const { t } = useTranslation(ns);
+ return createC(t);
+}
diff --git a/FrontEnd/src/views/common/button/Button.tsx b/FrontEnd/src/views/common/button/Button.tsx
index c5976909..be605328 100644
--- a/FrontEnd/src/views/common/button/Button.tsx
+++ b/FrontEnd/src/views/common/button/Button.tsx
@@ -1,43 +1,47 @@
-import * as React from "react";
+import { ComponentPropsWithoutRef, Ref } from "react";
import classNames from "classnames";
-import { useTranslation } from "react-i18next";
-import { convertI18nText, I18nText } from "@/common";
+import { I18nText, useC } from "@/common";
import { PaletteColorType } from "@/palette";
import "./Button.css";
-function _Button(
- props: {
- color?: PaletteColorType;
- text?: I18nText;
- outline?: boolean;
- } & React.ComponentPropsWithoutRef<"button">,
- ref: React.ForwardedRef<HTMLButtonElement>
-): JSX.Element {
- const { t } = useTranslation();
+interface ButtonProps extends ComponentPropsWithoutRef<"button"> {
+ color?: PaletteColorType;
+ text?: I18nText;
+ outline?: boolean;
+ buttonRef?: Ref<HTMLButtonElement> | null;
+}
- const { color, text, outline, className, children, ...otherProps } = props;
+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 (
<button
- ref={ref}
+ ref={buttonRef}
className={classNames(
"cru-" + (color ?? "primary"),
"cru-button",
outline && "outline",
- className
+ className,
)}
{...otherProps}
>
- {text != null ? convertI18nText(text, t) : children}
+ {text != null ? c(text) : children}
</button>
);
}
-
-const Button = React.forwardRef(_Button);
-export default Button;
diff --git a/FrontEnd/src/views/common/button/FlatButton.tsx b/FrontEnd/src/views/common/button/FlatButton.tsx
index b42c5b3a..49912b68 100644
--- a/FrontEnd/src/views/common/button/FlatButton.tsx
+++ b/FrontEnd/src/views/common/button/FlatButton.tsx
@@ -1,41 +1,37 @@
-import * as React from "react";
-import { useTranslation } from "react-i18next";
+import { ComponentPropsWithoutRef, Ref } from "react";
import classNames from "classnames";
-import { convertI18nText, I18nText } from "@/common";
+import { I18nText, useC } from "@/common";
import { PaletteColorType } from "@/palette";
import "./FlatButton.css";
-function _FlatButton(
- props: {
- color?: PaletteColorType;
- text?: I18nText;
- } & React.ComponentPropsWithoutRef<"button">,
- ref: React.ForwardedRef<HTMLButtonElement>
-): React.ReactElement | null {
- const { t } = useTranslation();
+interface FlatButtonProps extends ComponentPropsWithoutRef<"button"> {
+ color?: PaletteColorType;
+ text?: I18nText;
+ buttonRef?: Ref<HTMLButtonElement> | null;
+}
- const { color, text, className, children, ...otherProps } = props;
+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 (
<button
- ref={ref}
+ ref={buttonRef}
className={classNames(
"cru-" + (color ?? "primary"),
"cru-flat-button",
- className
+ className,
)}
{...otherProps}
>
- {text != null ? convertI18nText(text, t) : children}
+ {text != null ? c(text) : children}
</button>
);
}
-
-const FlatButton = React.forwardRef(_FlatButton);
-export default FlatButton;
diff --git a/FrontEnd/src/views/common/button/IconButton.css b/FrontEnd/src/views/common/button/IconButton.css
index ef4dca00..45fb103c 100644
--- a/FrontEnd/src/views/common/button/IconButton.css
+++ b/FrontEnd/src/views/common/button/IconButton.css
@@ -1,7 +1,8 @@
.cru-icon-button {
color: var(--cru-theme-color);
font-size: 1.4rem;
- cursor: pointer;
+ background: none;
+ border: none;
}
.cru-icon-button.large {
diff --git a/FrontEnd/src/views/common/button/IconButton.tsx b/FrontEnd/src/views/common/button/IconButton.tsx
index 3ba56277..652a8b09 100644
--- a/FrontEnd/src/views/common/button/IconButton.tsx
+++ b/FrontEnd/src/views/common/button/IconButton.tsx
@@ -1,21 +1,21 @@
-import * as React from "react";
+import { ComponentPropsWithoutRef } from "react";
import classNames from "classnames";
import { PaletteColorType } from "@/palette";
import "./IconButton.css";
-export interface IconButtonProps extends React.ComponentPropsWithRef<"i"> {
+interface IconButtonProps extends ComponentPropsWithoutRef<"i"> {
icon: string;
color?: PaletteColorType;
large?: boolean;
}
-export default function IconButton(props: IconButtonProps): JSX.Element {
+export default function IconButton(props: IconButtonProps) {
const { icon, color, className, large, ...otherProps } = props;
return (
- <i
+ <button
className={classNames(
"cru-icon-button",
large && "large",
diff --git a/FrontEnd/src/views/common/dialog/Dialog.tsx b/FrontEnd/src/views/common/dialog/Dialog.tsx
index c755950d..923c636b 100644
--- a/FrontEnd/src/views/common/dialog/Dialog.tsx
+++ b/FrontEnd/src/views/common/dialog/Dialog.tsx
@@ -1,17 +1,23 @@
-import * as React from "react";
+import { ReactNode } from "react";
import ReactDOM from "react-dom";
import { CSSTransition } from "react-transition-group";
import "./Dialog.css";
-export interface DialogProps {
+const optionalPortalElement = document.getElementById("portal");
+if (optionalPortalElement == null) {
+ throw new Error("Portal element not found");
+}
+const portalElement = optionalPortalElement;
+
+interface DialogProps {
onClose: () => void;
open: boolean;
- children?: React.ReactNode;
+ children?: ReactNode;
disableCloseOnClickOnOverlay?: boolean;
}
-export default function Dialog(props: DialogProps): React.ReactElement | null {
+export default function Dialog(props: DialogProps) {
const { open, onClose, children, disableCloseOnClickOnOverlay } = props;
return ReactDOM.createPortal(
@@ -24,7 +30,7 @@ export default function Dialog(props: DialogProps): React.ReactElement | null {
>
<div
className="cru-dialog-overlay"
- onClick={
+ onPointerDown={
disableCloseOnClickOnOverlay
? undefined
: () => {
@@ -34,13 +40,12 @@ export default function Dialog(props: DialogProps): React.ReactElement | null {
>
<div
className="cru-dialog-container"
- onClick={(e) => e.stopPropagation()}
+ onPointerDown={(e) => e.stopPropagation()}
>
{children}
</div>
</div>
</CSSTransition>,
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- document.getElementById("portal")!
+ portalElement,
);
}