Skip to content

Commit

Permalink
Merge pull request #1386 from crupest/dev
Browse files Browse the repository at this point in the history
Develop.
  • Loading branch information
crupest committed Jul 12, 2023
2 parents f216e5a + 89c1c6c commit b006d65
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 77 deletions.
31 changes: 4 additions & 27 deletions 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";
38 changes: 38 additions & 0 deletions FrontEnd/src/i18n.ts
Expand Up @@ -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);
7 changes: 7 additions & 0 deletions 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);
}
42 changes: 23 additions & 19 deletions 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;
32 changes: 14 additions & 18 deletions 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;
3 changes: 2 additions & 1 deletion 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 {
Expand Down
8 changes: 4 additions & 4 deletions 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",
Expand Down
21 changes: 13 additions & 8 deletions 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(
Expand All @@ -24,7 +30,7 @@ export default function Dialog(props: DialogProps): React.ReactElement | null {
>
<div
className="cru-dialog-overlay"
onClick={
onPointerDown={
disableCloseOnClickOnOverlay
? undefined
: () => {
Expand All @@ -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,
);
}

0 comments on commit b006d65

Please sign in to comment.