diff options
author | crupest <crupest@outlook.com> | 2023-07-19 18:26:00 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2023-07-19 18:26:00 +0800 |
commit | adc91a81fe53fdbc3d63065baa0b56862c104824 (patch) | |
tree | 6dc610d24c231186b81eb01f3b56f24e6660a1c3 /FrontEnd/src/views | |
parent | d712d3847506c5c2df62f741d9d2cb91e34f6bfd (diff) | |
download | timeline-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.css | 72 | ||||
-rw-r--r-- | FrontEnd/src/views/common/AppBar.tsx | 143 | ||||
-rw-r--r-- | FrontEnd/src/views/common/button/Button.css | 5 | ||||
-rw-r--r-- | FrontEnd/src/views/common/button/FlatButton.css | 4 | ||||
-rw-r--r-- | FrontEnd/src/views/common/button/IconButton.css | 41 | ||||
-rw-r--r-- | FrontEnd/src/views/common/button/IconButton.tsx | 8 | ||||
-rw-r--r-- | FrontEnd/src/views/common/user/UserAvatar.tsx | 17 |
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; +} |