1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-26 10:44:51 +03:00

frontend: make the navbar better and push it under the user avatar/name

This commit is contained in:
Quentin Gliech
2023-08-08 12:31:08 +02:00
parent 4556332efb
commit c2268cc164
9 changed files with 115 additions and 38 deletions

View File

@@ -19,13 +19,19 @@
} }
.footer { .footer {
margin-top: var(--cpd-space-6x);
padding: var(--cpd-space-6x) 0;
border-top: 1px solid var(--cpd-color-border-interactive-secondary);
text-align: center; text-align: center;
} }
.separator { .footer-links {
border: 0;
height: 1px;
background: var(--cpd-color-border-interactive-secondary);
margin: var(--cpd-space-4x) 0; margin: var(--cpd-space-4x) 0;
}
& > ul {
display: flex;
flex-direction: row;
justify-content: center;
gap: var(--cpd-space-2x);
}
}

View File

@@ -12,26 +12,42 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Link } from "@vector-im/compound-web";
import { useAtomValue } from "jotai";
import { currentUserIdAtom } from "../atoms";
import { isErr, unwrapErr, unwrapOk } from "../result";
import GraphQLError from "./GraphQLError";
import styles from "./Layout.module.css"; import styles from "./Layout.module.css";
import NavBar from "./NavBar"; import NavBar from "./NavBar";
import NavItem, { ExternalLink } from "./NavItem"; import NavItem from "./NavItem";
import NotLoggedIn from "./NotLoggedIn";
const Separator: React.FC = () => <hr className={styles.separator} />; import UserGreeting from "./UserGreeting";
const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => { const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
const result = useAtomValue(currentUserIdAtom);
if (isErr(result)) return <GraphQLError error={unwrapErr(result)} />;
const userId = unwrapOk(result);
if (userId === null)
return ( return (
<div className={styles.container}> <div className={styles.container}>
<NotLoggedIn />
</div>
);
return (
<div className={styles.container}>
<UserGreeting userId={userId} />
<NavBar> <NavBar>
<NavItem route={{ type: "home" }}>Sessions</NavItem> <NavItem route={{ type: "home" }}>Sessions</NavItem>
<NavItem route={{ type: "account" }}>Emails</NavItem> <NavItem route={{ type: "account" }}>Emails</NavItem>
</NavBar> </NavBar>
<Separator />
<main>{children}</main> <main>{children}</main>
<Separator />
<footer className={styles.footer}> <footer className={styles.footer}>
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener"> <a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
<img <img
@@ -43,17 +59,15 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
/> />
</a> </a>
<NavBar> <nav className={styles.footerLinks}>
<ExternalLink href="https://matrix.org/legal/copyright-notice"> <ul>
Info <Link href="https://matrix.org/legal/copyright-notice">Info</Link>
</ExternalLink> <Link href="https://matrix.org/legal/privacy-notice">Privacy</Link>
<ExternalLink href="https://matrix.org/legal/privacy-notice"> <Link href="https://matrix.org/legal/terms-and-conditions">
Privacy
</ExternalLink>
<ExternalLink href="https://matrix.org/legal/terms-and-conditions">
Terms & Conditions Terms & Conditions
</ExternalLink> </Link>
</NavBar> </ul>
</nav>
</footer> </footer>
</div> </div>
); );

View File

@@ -13,9 +13,16 @@
* limitations under the License. * limitations under the License.
*/ */
.nav-bar {
border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-gray-400);
margin: var(--cpd-space-6x) 0;
padding: 0 var(--cpd-space-10x);
}
.nav-bar-items { .nav-bar-items {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: flex-start;
gap: var(--cpd-space-4x); align-items: center;
gap: var(--cpd-space-3x);
} }

View File

@@ -15,7 +15,7 @@
import styles from "./NavBar.module.css"; import styles from "./NavBar.module.css";
const NavBar: React.FC<React.PropsWithChildren<{}>> = ({ children }) => ( const NavBar: React.FC<React.PropsWithChildren<{}>> = ({ children }) => (
<nav> <nav className={styles.navBar}>
<ul className={styles.navBarItems}>{children}</ul> <ul className={styles.navBarItems}>{children}</ul>
</nav> </nav>
); );

View File

@@ -13,11 +13,41 @@
* limitations under the License. * limitations under the License.
*/ */
.nav-tab {
padding: var(--cpd-space-4x) 0;
position: relative;
}
/* Underline effect */
.nav-tab::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 0;
border-radius: var(--cpd-radius-pill-effect) var(--cpd-radius-pill-effect) 0 0;
background-color: var(--cpd-color-bg-action-primary-rest);
transition: height 0.1s ease-in-out;
}
.nav-tab[data-current]::before {
/* This is not exactly right: designs says 3px, but there are no variables for that */
height: var(--cpd-border-width-4);
}
.nav-item { .nav-item {
padding: var(--cpd-space-1x) var(--cpd-space-2x); padding: var(--cpd-space-1x) var(--cpd-space-2x);
color: var(--cpd-color-text-secondary); color: var(--cpd-color-text-secondary);
line-height: var(--cpd-space-6x); line-height: var(--cpd-space-6x);
border-radius: var(--cpd-radius-pill-effect); border-radius: var(--cpd-radius-pill-effect);
border: transparent var(--cpd-border-width-2) solid;
}
.nav-item.external-link {
text-decoration: underline;
color: var(--cpd-color-text-primary);
} }
.nav-item:hover { .nav-item:hover {
@@ -25,6 +55,16 @@
background-color: var(--cpd-color-bg-subtle-secondary); background-color: var(--cpd-color-bg-subtle-secondary);
} }
.nav-item:active {
color: var(--cpd-color-text-primary);
background-color: var(--cpd-color-bg-subtle-primary);
}
.nav-item:focus {
outline: none;
border-color: var(--cpd-color-border-focused);
}
.nav-item[aria-current="page"] { .nav-item[aria-current="page"] {
color: var(--cpd-color-text-primary); color: var(--cpd-color-text-primary);
} }

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Link as CpdLink } from "@vector-im/compound-web"; import classNames from "classnames";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { Link, Route, routeAtom } from "../../Router"; import { Link, Route, routeAtom } from "../../Router";
@@ -24,12 +24,13 @@ const NavItem: React.FC<React.PropsWithChildren<{ route: Route }>> = ({
children, children,
}) => { }) => {
const currentRoute = useAtomValue(routeAtom); const currentRoute = useAtomValue(routeAtom);
const active = currentRoute.type === route.type;
return ( return (
<li> <li className={styles.navTab} data-current={active ? true : undefined}>
<Link <Link
className={styles.navItem} className={styles.navItem}
route={route} route={route}
aria-current={currentRoute.type === route.type ? "page" : undefined} aria-current={active ? "page" : undefined}
> >
{children} {children}
</Link> </Link>
@@ -40,8 +41,14 @@ const NavItem: React.FC<React.PropsWithChildren<{ route: Route }>> = ({
export const ExternalLink: React.FC< export const ExternalLink: React.FC<
React.PropsWithChildren<{ href: string }> React.PropsWithChildren<{ href: string }>
> = ({ href, children }) => ( > = ({ href, children }) => (
<li> <li className={styles.navTab}>
<CpdLink href={href}>{children}</CpdLink> <a
rel="noreferrer noopener"
className={classNames(styles.navItem, styles.externalLink)}
href={href}
>
{children}
</a>
</li> </li>
); );

View File

@@ -1,7 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`NavItem > render an active <NavItem /> 1`] = ` exports[`NavItem > render an active <NavItem /> 1`] = `
<li> <li
className="_navTab_8603fc"
data-current={true}
>
<a <a
aria-current="page" aria-current="page"
className="_navItem_8603fc" className="_navItem_8603fc"
@@ -14,7 +17,9 @@ exports[`NavItem > render an active <NavItem /> 1`] = `
`; `;
exports[`NavItem > render an inactive <NavItem /> 1`] = ` exports[`NavItem > render an inactive <NavItem /> 1`] = `
<li> <li
className="_navTab_8603fc"
>
<a <a
className="_navItem_8603fc" className="_navItem_8603fc"
href="" href=""
@@ -26,7 +31,9 @@ exports[`NavItem > render an inactive <NavItem /> 1`] = `
`; `;
exports[`NavItem > renders a different route 1`] = ` exports[`NavItem > renders a different route 1`] = `
<li> <li
className="_navTab_8603fc"
>
<a <a
className="_navItem_8603fc" className="_navItem_8603fc"
href="account" href="account"

View File

@@ -18,13 +18,11 @@ import { currentUserIdAtom } from "../atoms";
import GraphQLError from "../components/GraphQLError"; import GraphQLError from "../components/GraphQLError";
import NotLoggedIn from "../components/NotLoggedIn"; import NotLoggedIn from "../components/NotLoggedIn";
import UserEmailList from "../components/UserEmailList"; import UserEmailList from "../components/UserEmailList";
import UserGreeting from "../components/UserGreeting";
import { isErr, unwrapErr, unwrapOk } from "../result"; import { isErr, unwrapErr, unwrapOk } from "../result";
const UserAccount: React.FC<{ id: string }> = ({ id }) => { const UserAccount: React.FC<{ id: string }> = ({ id }) => {
return ( return (
<div className="grid grid-cols-1 gap-4"> <div className="grid grid-cols-1 gap-4">
<UserGreeting userId={id} />
<UserEmailList userId={id} /> <UserEmailList userId={id} />
</div> </div>
); );

View File

@@ -20,7 +20,6 @@ import CompatSessionList from "../components/CompatSessionList";
import GraphQLError from "../components/GraphQLError"; import GraphQLError from "../components/GraphQLError";
import NotLoggedIn from "../components/NotLoggedIn"; import NotLoggedIn from "../components/NotLoggedIn";
import OAuth2SessionList from "../components/OAuth2SessionList"; import OAuth2SessionList from "../components/OAuth2SessionList";
import UserGreeting from "../components/UserGreeting";
import { isErr, unwrapErr, unwrapOk } from "../result"; import { isErr, unwrapErr, unwrapOk } from "../result";
const Home: React.FC = () => { const Home: React.FC = () => {
@@ -32,7 +31,6 @@ const Home: React.FC = () => {
return ( return (
<> <>
<UserGreeting userId={currentUserId} />
<div className="mt-4 grid gap-8"> <div className="mt-4 grid gap-8">
<OAuth2SessionList userId={currentUserId} /> <OAuth2SessionList userId={currentUserId} />
<CompatSessionList userId={currentUserId} /> <CompatSessionList userId={currentUserId} />