diff options
Diffstat (limited to 'FrontEnd/src/components/button')
-rw-r--r-- | FrontEnd/src/components/button/Button.css | 64 | ||||
-rw-r--r-- | FrontEnd/src/components/button/Button.tsx | 46 | ||||
-rw-r--r-- | FrontEnd/src/components/button/ButtonRow.css | 0 | ||||
-rw-r--r-- | FrontEnd/src/components/button/ButtonRow.tsx | 62 | ||||
-rw-r--r-- | FrontEnd/src/components/button/ButtonRowV2.tsx | 146 | ||||
-rw-r--r-- | FrontEnd/src/components/button/FlatButton.css | 27 | ||||
-rw-r--r-- | FrontEnd/src/components/button/FlatButton.tsx | 36 | ||||
-rw-r--r-- | FrontEnd/src/components/button/IconButton.css | 30 | ||||
-rw-r--r-- | FrontEnd/src/components/button/IconButton.tsx | 30 | ||||
-rw-r--r-- | FrontEnd/src/components/button/LoadingButton.css | 13 | ||||
-rw-r--r-- | FrontEnd/src/components/button/LoadingButton.tsx | 39 | ||||
-rw-r--r-- | FrontEnd/src/components/button/index.tsx | 15 |
12 files changed, 508 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..30ea8c11 --- /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, ClickableColor } from "../common"; + +import "./Button.css"; + +interface ButtonProps extends ComponentPropsWithoutRef<"button"> { + color?: ClickableColor; + 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..a54425cc --- /dev/null +++ b/FrontEnd/src/components/button/ButtonRowV2.tsx @@ -0,0 +1,146 @@ +import { ComponentPropsWithoutRef, Ref } from "react"; +import classNames from "classnames"; + +import { Text, ClickableColor } from "../common"; + +import Button from "./Button"; +import FlatButton from "./FlatButton"; +import IconButton from "./IconButton"; +import LoadingButton from "./LoadingButton"; + +import "./ButtonRow.css"; + +type ButtonAction = "major" | "minor"; + +interface ButtonRowV2ButtonBase { + key: string | number; + action?: ButtonAction; + color?: ClickableColor; + 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: ButtonAction = action ?? "minor"; + const realColor = + color ?? (realAction === "major" ? "primary" : "minor"); + + 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 !== "major"} + {...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..aad02e76 --- /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, ClickableColor } from "../common"; + +import "./FlatButton.css"; + +interface FlatButtonProps extends ComponentPropsWithoutRef<"button"> { + color?: ClickableColor; + 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..e0862167 --- /dev/null +++ b/FrontEnd/src/components/button/IconButton.tsx @@ -0,0 +1,30 @@ +import { ComponentPropsWithoutRef } from "react"; +import classNames from "classnames"; + +import { ClickableColor } from "../common"; + +import "./IconButton.css"; + +interface IconButtonProps extends ComponentPropsWithoutRef<"i"> { + icon: string; + color?: ClickableColor; + 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..9d65a2b3 --- /dev/null +++ b/FrontEnd/src/components/button/LoadingButton.tsx @@ -0,0 +1,39 @@ +import classNames from "classnames"; + +import { I18nText, ClickableColor, useC } from "../common"; +import Spinner from "../Spinner"; + +import "./LoadingButton.css"; + +interface LoadingButtonProps extends React.ComponentPropsWithoutRef<"button"> { + color?: ClickableColor; + 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, +}; |