aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/views
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-07-19 18:26:00 +0800
committercrupest <crupest@outlook.com>2023-07-19 18:26:00 +0800
commitadc91a81fe53fdbc3d63065baa0b56862c104824 (patch)
tree6dc610d24c231186b81eb01f3b56f24e6660a1c3 /FrontEnd/src/views
parentd712d3847506c5c2df62f741d9d2cb91e34f6bfd (diff)
downloadtimeline-adc91a81fe53fdbc3d63065baa0b56862c104824.tar.gz
timeline-adc91a81fe53fdbc3d63065baa0b56862c104824.tar.bz2
timeline-adc91a81fe53fdbc3d63065baa0b56862c104824.zip
AppBar done!
Diffstat (limited to 'FrontEnd/src/views')
-rw-r--r--FrontEnd/src/views/common/AppBar.css72
-rw-r--r--FrontEnd/src/views/common/AppBar.tsx143
-rw-r--r--FrontEnd/src/views/common/button/Button.css5
-rw-r--r--FrontEnd/src/views/common/button/FlatButton.css4
-rw-r--r--FrontEnd/src/views/common/button/IconButton.css41
-rw-r--r--FrontEnd/src/views/common/button/IconButton.tsx8
-rw-r--r--FrontEnd/src/views/common/user/UserAvatar.tsx17
7 files changed, 137 insertions, 153 deletions
diff --git a/FrontEnd/src/views/common/AppBar.css b/FrontEnd/src/views/common/AppBar.css
index c278aa1e..bd8d0986 100644
--- a/FrontEnd/src/views/common/AppBar.css
+++ b/FrontEnd/src/views/common/AppBar.css
@@ -8,29 +8,25 @@
background-color: var(--cru-primary-color);
}
-.app-bar.desktop {
+.app-bar {
display: flex;
}
-.app-bar.desktop .app-bar-brand {
+.app-bar .app-bar-brand {
display: flex;
align-items: center;
}
-.app-bar.desktop .app-bar-brand-icon {
+.app-bar .app-bar-brand-icon {
height: 2em;
}
-.app-bar.desktop .app-bar-main-area {
- display: flex;
- flex-grow: 1;
-}
-
-.app-bar.desktop .app-bar-link-area {
+.app-bar .app-bar-user-area {
display: flex;
+ margin-left: auto;
}
-.app-bar.desktop a {
+.app-bar a {
background-color: var(--cru-primary-color);
color: var(--cru-primary-on-color);
text-decoration: none;
@@ -40,63 +36,47 @@
transition: all 0.5s;
}
-.app-bar.desktop a:hover {
+.app-bar a:hover {
background-color: var(--cru-primary-1-color);
}
-.app-bar.desktop a:focus {
+.app-bar a:focus {
background-color: var(--cru-primary-1-color);
}
-.app-bar.desktop a:active {
+.app-bar a:active {
background-color: var(--cru-primary-2-color);
}
-.app-bar.desktop .app-bar-user-area {
+.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;
- margin-left: auto;
}
-.small-screen .app-bar-main-area {
+.app-bar.mobile .app-bar-link-area {
position: absolute;
+ z-index: -1;
top: 56px;
left: 0;
right: 0;
- transform-origin: top;
- transition: transform 0.6s, background-color 1s;
- background-color: var(--cru-primary-color);
- flex-direction: column;
-}
-
-.small-screen .app-bar-main-area.app-bar-collapse {
- transform: scale(1, 0);
-}
-
-.small-screen .app-bar-main-area a {
- text-align: left;
- padding: 0.5em 0.5em;
+ transition: transform 0.5s;
}
-.small-screen .app-bar-link-area {
- flex-direction: column;
- align-items: stretch;
-}
-
-.small-screen .app-bar-user-area {
- flex-direction: column;
- align-items: stretch;
- margin-left: unset;
+.app-bar.mobile a {
+ height: 56px;
}
-.small-screen .app-bar-avatar {
- align-self: flex-end;
+.app-bar.mobile.collapse .app-bar-link-area {
+ transform: translateY(-100%);
}
-.app-bar-toggler {
- margin-left: auto;
+.app-bar .toggler {
font-size: 2em;
- margin-right: 1em;
- color: var(--cru-primary-t-color);
- cursor: pointer;
- user-select: none;
+ 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
index 180d0db6..dacd608a 100644
--- a/FrontEnd/src/views/common/AppBar.tsx
+++ b/FrontEnd/src/views/common/AppBar.tsx
@@ -1,12 +1,12 @@
-import * as React from "react";
+import { useState } from "react";
import classnames from "classnames";
import { Link, NavLink } from "react-router-dom";
-import { useMediaQuery } from "react-responsive";
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";
@@ -15,11 +15,13 @@ function AppBarNavLink({
link,
className,
label,
+ onClick,
children,
}: {
link: string;
className?: string;
label?: I18nText;
+ onClick?: () => void;
children?: React.ReactNode;
}) {
if (label != null && children != null) {
@@ -32,117 +34,70 @@ function AppBarNavLink({
<NavLink
to={link}
className={({ isActive }) => classnames(className, isActive && "active")}
+ onClick={onClick}
>
{children != null ? children : c(label)}
</NavLink>
);
}
-function DesktopAppBar() {
- const user = useUser();
- const hasAdministrationPermission = user && user.hasAdministrationPermission;
-
- return (
- <nav className="desktop app-bar">
- <Link to="/" className="app-bar-brand active">
- <TimelineLogo className="app-bar-brand-icon" />
- Timeline
- </Link>
- <div className="app-bar-main-area">
- <div className="app-bar-link-area">
- <AppBarNavLink link="/settings" label="nav.settings" />
- <AppBarNavLink link="/about" label="nav.about" />
- {hasAdministrationPermission && (
- <AppBarNavLink link="/admin" label="nav.administration" />
- )}
- </div>
-
- <div className="app-bar-user-area">
- {user != null ? (
- <AppBarNavLink link="/" className="app-bar-avatar">
- <UserAvatar
- username={user.username}
- className="cru-avatar small cru-round cursor-pointer ml-auto"
- />
- </AppBarNavLink>
- ) : (
- <AppBarNavLink link="/login" label="nav.login" />
- )}
- </div>
- </div>
- </nav>
- );
-}
+export default function AppBar() {
+ const isMobile = useMobile();
-// TODO: Go make this!
-function MobileAppBar() {
- const c = useC();
+ const [isCollapse, setIsCollapse] = useState<boolean>(true);
+ const collapse = isMobile ? () => setIsCollapse(true) : undefined;
+ const toggleCollapse = () => setIsCollapse(!isCollapse);
const user = useUser();
const hasAdministrationPermission = user && user.hasAdministrationPermission;
- const isSmallScreen = useMediaQuery({ maxWidth: 576 });
-
- const [expand, setExpand] = React.useState<boolean>(false);
- const collapse = (): void => setExpand(false);
- const toggleExpand = (): void => setExpand(!expand);
-
- const createLink = (
- link: string,
- label: React.ReactNode,
- className?: string,
- ): React.ReactNode => (
- <NavLink
- to={link}
- onClick={collapse}
- className={({ isActive }) => classnames(className, isActive && "active")}
- >
- {label}
- </NavLink>
- );
-
return (
- <nav className={classnames("app-bar", isSmallScreen && "small-screen")}>
- <Link to="/" className="app-bar-brand active">
+ <nav
+ className={classnames(
+ isMobile ? "mobile" : "desktop",
+ "app-bar",
+ isCollapse && "collapse",
+ )}
+ >
+ <Link to="/" className="app-bar-brand active" onClick={collapse}>
<TimelineLogo className="app-bar-brand-icon" />
Timeline
</Link>
- {isSmallScreen && (
- <i className="bi-list app-bar-toggler" onClick={toggleExpand} />
- )}
-
- <div
- className={classnames(
- "app-bar-main-area",
- !expand && "app-bar-collapse",
+ <div className="app-bar-link-area">
+ <AppBarNavLink
+ link="/settings"
+ label="nav.settings"
+ onClick={collapse}
+ />
+ <AppBarNavLink link="/about" label="nav.about" onClick={collapse} />
+ {hasAdministrationPermission && (
+ <AppBarNavLink
+ link="/admin"
+ label="nav.administration"
+ onClick={collapse}
+ />
)}
- >
- <div className="app-bar-link-area">
- {createLink("/settings", c("nav.settings"))}
- {createLink("/about", c("nav.about"))}
- {hasAdministrationPermission &&
- createLink("/admin", c("nav.administration"))}
- </div>
+ </div>
- <div className="app-bar-user-area">
- {user != null
- ? createLink(
- "/",
- <UserAvatar
- username={user.username}
- className="cru-avatar small cru-round cursor-pointer ml-auto"
- />,
- "app-bar-avatar",
- )
- : createLink("/login", c("nav.login"))}
- </div>
+ <div className="app-bar-user-area">
+ {user != null ? (
+ <AppBarNavLink link="/" className="app-bar-avatar" onClick={collapse}>
+ <UserAvatar username={user.username} />
+ </AppBarNavLink>
+ ) : (
+ <AppBarNavLink link="/login" label="nav.login" onClick={collapse} />
+ )}
</div>
+
+ {isMobile && (
+ <IconButton
+ icon="list"
+ className="toggler"
+ color="on-surface"
+ onClick={toggleCollapse}
+ />
+ )}
</nav>
);
}
-
-export default function AppBar() {
- const isMobile = useMobile();
- return isMobile ? <MobileAppBar /> : <DesktopAppBar />;
-}
diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css
index 55563e00..12c6903e 100644
--- a/FrontEnd/src/views/common/button/Button.css
+++ b/FrontEnd/src/views/common/button/Button.css
@@ -30,6 +30,7 @@
.cru-button:not(.outline):disabled {
background-color: var(--cru-surface-on-color);
+ border-color: var(--cru-surface-on-color);
cursor: auto;
}
@@ -55,8 +56,8 @@
}
.cru-button.outline:disabled {
- color: var(--cru-disabled-color);
- border-color: var(--cru-disabled-color);
+ color: var(--cru-surface-on-color);
+ border-color: var(--cru-surface-on-color);
background-color: white;
cursor: auto;
} \ No newline at end of file
diff --git a/FrontEnd/src/views/common/button/FlatButton.css b/FrontEnd/src/views/common/button/FlatButton.css
index 921fafe4..373371c2 100644
--- a/FrontEnd/src/views/common/button/FlatButton.css
+++ b/FrontEnd/src/views/common/button/FlatButton.css
@@ -21,7 +21,7 @@
background-color: var(--cru-surface-2-color);
}
-.cru-flat-button.disabled {
- color: var(--cru-disabled-color);
+.cru-flat-button:disabled {
+ color: var(--cru-surface-on-color);
cursor: auto;
} \ No newline at end of file
diff --git a/FrontEnd/src/views/common/button/IconButton.css b/FrontEnd/src/views/common/button/IconButton.css
index 25c5f84c..a1ac67f7 100644
--- a/FrontEnd/src/views/common/button/IconButton.css
+++ b/FrontEnd/src/views/common/button/IconButton.css
@@ -3,8 +3,49 @@
font-size: 1.4rem;
background: none;
border: none;
+ transition: all 0.5s;
+ cursor: pointer;
+ user-select: none;
+}
+
+.cru-icon-button:hover {
+ color: var(--cru-key-1-color);
+}
+
+.cru-icon-button:focus {
+ color: var(--cru-key-1-color);
+}
+
+.cru-icon-button:active {
+ color: var(--cru-key-2-color);
+}
+
+.cru-flat-button:disabled {
+ color: var(--cru-surface-on-color);
+ cursor: auto;
}
.cru-icon-button.large {
font-size: 1.6rem;
}
+
+.cru-icon-button.on-surface {
+ color: var(--cru-surface-color);
+}
+
+.cru-icon-button.on-surface:hover {
+ color: var(--cru-surface-1-color);
+}
+
+.cru-icon-button.on-surface:focus {
+ color: var(--cru-surface-1-color);
+}
+
+.cru-icon-button.on-surface:active {
+ color: var(--cru-surface-2-color);
+}
+
+.cru-flat-button.on-surface:disabled {
+ color: var(--cru-surface-on-color);
+ cursor: auto;
+} \ No newline at end of file
diff --git a/FrontEnd/src/views/common/button/IconButton.tsx b/FrontEnd/src/views/common/button/IconButton.tsx
index e5454574..ac746a7b 100644
--- a/FrontEnd/src/views/common/button/IconButton.tsx
+++ b/FrontEnd/src/views/common/button/IconButton.tsx
@@ -7,7 +7,7 @@ import "./IconButton.css";
interface IconButtonProps extends ComponentPropsWithoutRef<"i"> {
icon: string;
- color?: ThemeColor;
+ color?: ThemeColor | "on-surface";
large?: boolean;
}
@@ -20,7 +20,11 @@ export default function IconButton(props: IconButtonProps) {
"cru-icon-button",
large && "large",
"bi-" + icon,
- color ? "cru-" + color : "cru-primary",
+ color === "on-surface"
+ ? "on-surface"
+ : color != null
+ ? "cru-" + color
+ : "cru-primary",
className,
)}
{...otherProps}
diff --git a/FrontEnd/src/views/common/user/UserAvatar.tsx b/FrontEnd/src/views/common/user/UserAvatar.tsx
index fcff8c69..aea7bd48 100644
--- a/FrontEnd/src/views/common/user/UserAvatar.tsx
+++ b/FrontEnd/src/views/common/user/UserAvatar.tsx
@@ -1,19 +1,22 @@
-import * as React from "react";
+import { Ref, ComponentPropsWithoutRef } from "react";
import { getHttpUserClient } from "@/http/user";
-export interface UserAvatarProps
- extends React.ImgHTMLAttributes<HTMLImageElement> {
+export interface UserAvatarProps extends ComponentPropsWithoutRef<"img"> {
username: string;
+ imgRef?: Ref<HTMLImageElement> | null;
}
-const UserAvatar: React.FC<UserAvatarProps> = ({ username, ...otherProps }) => {
+export default function UserAvatar({
+ username,
+ imgRef,
+ ...otherProps
+}: UserAvatarProps) {
return (
<img
+ ref={imgRef}
src={getHttpUserClient().generateAvatarUrl(username)}
{...otherProps}
/>
);
-};
-
-export default UserAvatar;
+}