aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/components/button
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-08-26 21:36:58 +0800
committercrupest <crupest@outlook.com>2023-08-26 21:36:58 +0800
commitf5dfd52f6efece2f4cad227044ecf4dd66301bbc (patch)
tree0d64daae438ac6d95f0848a0b3387134d9528438 /FrontEnd/src/components/button
parent4daa84ede781cdf6f424d68c967a17c7e1c79f59 (diff)
downloadtimeline-f5dfd52f6efece2f4cad227044ecf4dd66301bbc.tar.gz
timeline-f5dfd52f6efece2f4cad227044ecf4dd66301bbc.tar.bz2
timeline-f5dfd52f6efece2f4cad227044ecf4dd66301bbc.zip
...
Diffstat (limited to 'FrontEnd/src/components/button')
-rw-r--r--FrontEnd/src/components/button/Button.css64
-rw-r--r--FrontEnd/src/components/button/Button.tsx46
-rw-r--r--FrontEnd/src/components/button/ButtonRow.css0
-rw-r--r--FrontEnd/src/components/button/ButtonRow.tsx62
-rw-r--r--FrontEnd/src/components/button/ButtonRowV2.tsx143
-rw-r--r--FrontEnd/src/components/button/FlatButton.css27
-rw-r--r--FrontEnd/src/components/button/FlatButton.tsx36
-rw-r--r--FrontEnd/src/components/button/IconButton.css30
-rw-r--r--FrontEnd/src/components/button/IconButton.tsx30
-rw-r--r--FrontEnd/src/components/button/LoadingButton.css13
-rw-r--r--FrontEnd/src/components/button/LoadingButton.tsx40
-rw-r--r--FrontEnd/src/components/button/index.tsx15
12 files changed, 506 insertions, 0 deletions
diff --git a/FrontEnd/src/components/button/Button.css b/FrontEnd/src/components/button/Button.css
new file mode 100644
index 00000000..1da70f0e
--- /dev/null
+++ b/FrontEnd/src/components/button/Button.css
@@ -0,0 +1,64 @@
+.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/components/button/Button.tsx b/FrontEnd/src/components/button/Button.tsx
new file mode 100644
index 00000000..6c38e130
--- /dev/null
+++ b/FrontEnd/src/components/button/Button.tsx
@@ -0,0 +1,46 @@
+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<HTMLButtonElement> | 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 (
+ <button
+ ref={buttonRef}
+ className={classNames(
+ "cru-button",
+ `cru-clickable-${color ?? "primary"}`,
+ outline && "outline",
+ className,
+ )}
+ {...otherProps}
+ >
+ {text != null ? c(text) : children}
+ </button>
+ );
+}
diff --git a/FrontEnd/src/components/button/ButtonRow.css b/FrontEnd/src/components/button/ButtonRow.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/FrontEnd/src/components/button/ButtonRow.css
diff --git a/FrontEnd/src/components/button/ButtonRow.tsx b/FrontEnd/src/components/button/ButtonRow.tsx
new file mode 100644
index 00000000..eea60cc4
--- /dev/null
+++ b/FrontEnd/src/components/button/ButtonRow.tsx
@@ -0,0 +1,62 @@
+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<typeof Button>;
+ }
+ | {
+ type: "flat";
+ props: ComponentPropsWithoutRef<typeof FlatButton>;
+ }
+ | {
+ type: "icon";
+ props: ComponentPropsWithoutRef<typeof IconButton>;
+ }
+ | { type: "loading"; props: ComponentPropsWithoutRef<typeof LoadingButton> }
+) & { key: string | number };
+
+interface ButtonRowProps {
+ className?: string;
+ containerRef?: Ref<HTMLDivElement>;
+ buttons: ButtonRowButton[];
+ buttonsClassName?: string;
+}
+
+export default function ButtonRow({
+ className,
+ containerRef,
+ buttons,
+ buttonsClassName,
+}: ButtonRowProps) {
+ return (
+ <div ref={containerRef} className={classNames("cru-button-row", className)}>
+ {buttons.map((button) => {
+ const { type, key, props } = button;
+ const newClassName = classNames(props.className, buttonsClassName);
+ switch (type) {
+ case "normal":
+ return <Button key={key} {...props} className={newClassName} />;
+ case "flat":
+ return <FlatButton key={key} {...props} className={newClassName} />;
+ case "icon":
+ return <IconButton key={key} {...props} className={newClassName} />;
+ case "loading":
+ return (
+ <LoadingButton key={key} {...props} className={newClassName} />
+ );
+ default:
+ throw new Error();
+ }
+ })}
+ </div>
+ );
+}
diff --git a/FrontEnd/src/components/button/ButtonRowV2.tsx b/FrontEnd/src/components/button/ButtonRowV2.tsx
new file mode 100644
index 00000000..3467ad52
--- /dev/null
+++ b/FrontEnd/src/components/button/ButtonRowV2.tsx
@@ -0,0 +1,143 @@
+import { ComponentPropsWithoutRef, Ref } from "react";
+import classNames from "classnames";
+
+import Button from "./Button";
+import FlatButton from "./FlatButton";
+import IconButton from "./IconButton";
+import LoadingButton from "./LoadingButton";
+
+import "./ButtonRow.css";
+import { Text, ThemeColor } from "../common";
+
+interface ButtonRowV2ButtonBase {
+ key: string | number;
+ action?: "primary" | "secondary";
+ color?: ThemeColor;
+ disabled?: boolean;
+ onClick?: () => void;
+}
+
+interface ButtonRowV2ButtonWithNoType extends ButtonRowV2ButtonBase {
+ type?: undefined | null;
+ text: Text;
+ outline?: boolean;
+ props?: ComponentPropsWithoutRef<typeof Button>;
+}
+
+interface ButtonRowV2NormalButton extends ButtonRowV2ButtonBase {
+ type: "normal";
+ text: Text;
+ outline?: boolean;
+ props?: ComponentPropsWithoutRef<typeof Button>;
+}
+
+interface ButtonRowV2FlatButton extends ButtonRowV2ButtonBase {
+ type: "flat";
+ text: Text;
+ props?: ComponentPropsWithoutRef<typeof FlatButton>;
+}
+
+interface ButtonRowV2IconButton extends ButtonRowV2ButtonBase {
+ type: "icon";
+ icon: string;
+ props?: ComponentPropsWithoutRef<typeof IconButton>;
+}
+
+interface ButtonRowV2LoadingButton extends ButtonRowV2ButtonBase {
+ type: "loading";
+ text: Text;
+ loading?: boolean;
+ props?: ComponentPropsWithoutRef<typeof LoadingButton>;
+}
+
+type ButtonRowV2Button =
+ | ButtonRowV2ButtonWithNoType
+ | ButtonRowV2NormalButton
+ | ButtonRowV2FlatButton
+ | ButtonRowV2IconButton
+ | ButtonRowV2LoadingButton;
+
+interface ButtonRowV2Props {
+ className?: string;
+ containerRef?: Ref<HTMLDivElement>;
+ buttons: ButtonRowV2Button[];
+ buttonsClassName?: string;
+}
+
+export default function ButtonRowV2({
+ className,
+ containerRef,
+ buttons,
+ buttonsClassName,
+}: ButtonRowV2Props) {
+ return (
+ <div ref={containerRef} className={classNames("cru-button-row", className)}>
+ {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 (
+ <Button
+ {...commonProps}
+ text={text}
+ outline={outline ?? realAction !== "primary"}
+ {...props}
+ className={newClassName}
+ />
+ );
+ }
+ case "flat": {
+ const { text, props } = button;
+ return (
+ <FlatButton
+ {...commonProps}
+ text={text}
+ {...props}
+ className={newClassName}
+ />
+ );
+ }
+ case "icon": {
+ const { icon, props } = button;
+ return (
+ <IconButton
+ {...commonProps}
+ icon={icon}
+ {...props}
+ className={newClassName}
+ />
+ );
+ }
+ case "loading": {
+ const { text, loading, props } = button;
+ return (
+ <LoadingButton
+ {...commonProps}
+ text={text}
+ loading={loading}
+ {...props}
+ className={newClassName}
+ />
+ );
+ }
+ default:
+ throw new Error();
+ }
+ })}
+ </div>
+ );
+}
diff --git a/FrontEnd/src/components/button/FlatButton.css b/FrontEnd/src/components/button/FlatButton.css
new file mode 100644
index 00000000..2050946c
--- /dev/null
+++ b/FrontEnd/src/components/button/FlatButton.css
@@ -0,0 +1,27 @@
+.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/components/button/FlatButton.tsx b/FrontEnd/src/components/button/FlatButton.tsx
new file mode 100644
index 00000000..9f074dd6
--- /dev/null
+++ b/FrontEnd/src/components/button/FlatButton.tsx
@@ -0,0 +1,36 @@
+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<HTMLButtonElement> | 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 (
+ <button
+ ref={buttonRef}
+ className={classNames(
+ "cru-flat-button",
+ `cru-clickable-${color ?? "primary"}`,
+ className,
+ )}
+ {...otherProps}
+ >
+ {text != null ? c(text) : children}
+ </button>
+ );
+}
diff --git a/FrontEnd/src/components/button/IconButton.css b/FrontEnd/src/components/button/IconButton.css
new file mode 100644
index 00000000..a3747201
--- /dev/null
+++ b/FrontEnd/src/components/button/IconButton.css
@@ -0,0 +1,30 @@
+.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/components/button/IconButton.tsx b/FrontEnd/src/components/button/IconButton.tsx
new file mode 100644
index 00000000..95c58887
--- /dev/null
+++ b/FrontEnd/src/components/button/IconButton.tsx
@@ -0,0 +1,30 @@
+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 (
+ <button
+ className={classNames(
+ "cru-icon-button",
+ `cru-clickable-${color ?? "grayscale"}`,
+ large && "large",
+ "bi-" + icon,
+ className,
+ )}
+ {...otherProps}
+ />
+ );
+}
diff --git a/FrontEnd/src/components/button/LoadingButton.css b/FrontEnd/src/components/button/LoadingButton.css
new file mode 100644
index 00000000..23fadd3d
--- /dev/null
+++ b/FrontEnd/src/components/button/LoadingButton.css
@@ -0,0 +1,13 @@
+.cru-loading-button {
+ display: flex;
+ align-items: center;
+}
+
+.cru-loading-button-spinner {
+ margin-left: 0.5em;
+}
+
+.cru-loading-button-loading {
+ color: var(--cru-clickable-normal-color) !important;
+ border-color: var(--cru-clickable-normal-color) !important;
+} \ No newline at end of file
diff --git a/FrontEnd/src/components/button/LoadingButton.tsx b/FrontEnd/src/components/button/LoadingButton.tsx
new file mode 100644
index 00000000..7e7d08e6
--- /dev/null
+++ b/FrontEnd/src/components/button/LoadingButton.tsx
@@ -0,0 +1,40 @@
+import classNames from "classnames";
+
+import { I18nText, ThemeColor, useC } from "../common";
+
+import Spinner from "../Spinner";
+
+import "./LoadingButton.css";
+
+interface LoadingButtonProps extends React.ComponentPropsWithoutRef<"button"> {
+ color?: ThemeColor;
+ text?: I18nText;
+ loading?: boolean;
+}
+
+export default function LoadingButton(props: LoadingButtonProps) {
+ const c = useC();
+
+ const { color, text, loading, disabled, className, children, ...otherProps } =
+ props;
+
+ if (text != null && children != null) {
+ console.warn("You can't set both text and children props.");
+ }
+
+ return (
+ <button
+ disabled={disabled || loading}
+ className={classNames(
+ "cru-button outline cru-loading-button",
+ `cru-clickable-${color ?? "primary"}`,
+ loading && "cru-loading-button-loading",
+ className,
+ )}
+ {...otherProps}
+ >
+ {text != null ? c(text) : children}
+ {loading && <Spinner className="cru-loading-button-spinner" />}
+ </button>
+ );
+}
diff --git a/FrontEnd/src/components/button/index.tsx b/FrontEnd/src/components/button/index.tsx
new file mode 100644
index 00000000..b5aa5470
--- /dev/null
+++ b/FrontEnd/src/components/button/index.tsx
@@ -0,0 +1,15 @@
+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,
+};