You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-11-24 23:01:05 +03:00
frontend: make the navbar better and push it under the user avatar/name
This commit is contained in:
@@ -19,13 +19,19 @@
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.separator {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background: var(--cpd-color-border-interactive-secondary);
|
||||
.footer-links {
|
||||
margin: var(--cpd-space-4x) 0;
|
||||
}
|
||||
|
||||
& > ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: var(--cpd-space-2x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,26 +12,42 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// 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 NavBar from "./NavBar";
|
||||
import NavItem, { ExternalLink } from "./NavItem";
|
||||
|
||||
const Separator: React.FC = () => <hr className={styles.separator} />;
|
||||
import NavItem from "./NavItem";
|
||||
import NotLoggedIn from "./NotLoggedIn";
|
||||
import UserGreeting from "./UserGreeting";
|
||||
|
||||
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 (
|
||||
<div className={styles.container}>
|
||||
<NotLoggedIn />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<UserGreeting userId={userId} />
|
||||
|
||||
<NavBar>
|
||||
<NavItem route={{ type: "home" }}>Sessions</NavItem>
|
||||
<NavItem route={{ type: "account" }}>Emails</NavItem>
|
||||
</NavBar>
|
||||
|
||||
<Separator />
|
||||
|
||||
<main>{children}</main>
|
||||
|
||||
<Separator />
|
||||
|
||||
<footer className={styles.footer}>
|
||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
|
||||
<img
|
||||
@@ -43,17 +59,15 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||
/>
|
||||
</a>
|
||||
|
||||
<NavBar>
|
||||
<ExternalLink href="https://matrix.org/legal/copyright-notice">
|
||||
Info
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://matrix.org/legal/privacy-notice">
|
||||
Privacy
|
||||
</ExternalLink>
|
||||
<ExternalLink href="https://matrix.org/legal/terms-and-conditions">
|
||||
<nav className={styles.footerLinks}>
|
||||
<ul>
|
||||
<Link href="https://matrix.org/legal/copyright-notice">Info</Link>
|
||||
<Link href="https://matrix.org/legal/privacy-notice">Privacy</Link>
|
||||
<Link href="https://matrix.org/legal/terms-and-conditions">
|
||||
Terms & Conditions
|
||||
</ExternalLink>
|
||||
</NavBar>
|
||||
</Link>
|
||||
</ul>
|
||||
</nav>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,9 +13,16 @@
|
||||
* 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 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: var(--cpd-space-4x);
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--cpd-space-3x);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import styles from "./NavBar.module.css";
|
||||
|
||||
const NavBar: React.FC<React.PropsWithChildren<{}>> = ({ children }) => (
|
||||
<nav>
|
||||
<nav className={styles.navBar}>
|
||||
<ul className={styles.navBarItems}>{children}</ul>
|
||||
</nav>
|
||||
);
|
||||
|
||||
@@ -13,11 +13,41 @@
|
||||
* 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 {
|
||||
padding: var(--cpd-space-1x) var(--cpd-space-2x);
|
||||
color: var(--cpd-color-text-secondary);
|
||||
line-height: var(--cpd-space-6x);
|
||||
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 {
|
||||
@@ -25,6 +55,16 @@
|
||||
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"] {
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Link as CpdLink } from "@vector-im/compound-web";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import { Link, Route, routeAtom } from "../../Router";
|
||||
@@ -24,12 +24,13 @@ const NavItem: React.FC<React.PropsWithChildren<{ route: Route }>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const currentRoute = useAtomValue(routeAtom);
|
||||
const active = currentRoute.type === route.type;
|
||||
return (
|
||||
<li>
|
||||
<li className={styles.navTab} data-current={active ? true : undefined}>
|
||||
<Link
|
||||
className={styles.navItem}
|
||||
route={route}
|
||||
aria-current={currentRoute.type === route.type ? "page" : undefined}
|
||||
aria-current={active ? "page" : undefined}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
@@ -40,8 +41,14 @@ const NavItem: React.FC<React.PropsWithChildren<{ route: Route }>> = ({
|
||||
export const ExternalLink: React.FC<
|
||||
React.PropsWithChildren<{ href: string }>
|
||||
> = ({ href, children }) => (
|
||||
<li>
|
||||
<CpdLink href={href}>{children}</CpdLink>
|
||||
<li className={styles.navTab}>
|
||||
<a
|
||||
rel="noreferrer noopener"
|
||||
className={classNames(styles.navItem, styles.externalLink)}
|
||||
href={href}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`NavItem > render an active <NavItem /> 1`] = `
|
||||
<li>
|
||||
<li
|
||||
className="_navTab_8603fc"
|
||||
data-current={true}
|
||||
>
|
||||
<a
|
||||
aria-current="page"
|
||||
className="_navItem_8603fc"
|
||||
@@ -14,7 +17,9 @@ exports[`NavItem > render an active <NavItem /> 1`] = `
|
||||
`;
|
||||
|
||||
exports[`NavItem > render an inactive <NavItem /> 1`] = `
|
||||
<li>
|
||||
<li
|
||||
className="_navTab_8603fc"
|
||||
>
|
||||
<a
|
||||
className="_navItem_8603fc"
|
||||
href=""
|
||||
@@ -26,7 +31,9 @@ exports[`NavItem > render an inactive <NavItem /> 1`] = `
|
||||
`;
|
||||
|
||||
exports[`NavItem > renders a different route 1`] = `
|
||||
<li>
|
||||
<li
|
||||
className="_navTab_8603fc"
|
||||
>
|
||||
<a
|
||||
className="_navItem_8603fc"
|
||||
href="account"
|
||||
|
||||
@@ -18,13 +18,11 @@ import { currentUserIdAtom } from "../atoms";
|
||||
import GraphQLError from "../components/GraphQLError";
|
||||
import NotLoggedIn from "../components/NotLoggedIn";
|
||||
import UserEmailList from "../components/UserEmailList";
|
||||
import UserGreeting from "../components/UserGreeting";
|
||||
import { isErr, unwrapErr, unwrapOk } from "../result";
|
||||
|
||||
const UserAccount: React.FC<{ id: string }> = ({ id }) => {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<UserGreeting userId={id} />
|
||||
<UserEmailList userId={id} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -20,7 +20,6 @@ import CompatSessionList from "../components/CompatSessionList";
|
||||
import GraphQLError from "../components/GraphQLError";
|
||||
import NotLoggedIn from "../components/NotLoggedIn";
|
||||
import OAuth2SessionList from "../components/OAuth2SessionList";
|
||||
import UserGreeting from "../components/UserGreeting";
|
||||
import { isErr, unwrapErr, unwrapOk } from "../result";
|
||||
|
||||
const Home: React.FC = () => {
|
||||
@@ -32,7 +31,6 @@ const Home: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<UserGreeting userId={currentUserId} />
|
||||
<div className="mt-4 grid gap-8">
|
||||
<OAuth2SessionList userId={currentUserId} />
|
||||
<CompatSessionList userId={currentUserId} />
|
||||
|
||||
Reference in New Issue
Block a user