Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 Layout and basic page setup #740

Merged
merged 6 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"git-info": "rm -rf src/generated/ && mkdir src/generated/ && echo export default \"{\\\"commitHash\\\": \\\"$(git rev-parse --short HEAD)\\\", \\\"version\\\": \\\"$(git describe --tags --always)\\\"};\" > src/generated/gitInfo.ts"
},
"dependencies": {
"@stellar/design-system": "^1.1.2",
"@stellar/design-system": "^2.0.0-beta.1",
"next": "14.0.4",
"react": "^18",
"react-dom": "^18"
Expand Down
5 changes: 5 additions & 0 deletions src/app/(sidebar)/account/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use client";

export default function CreateAccount() {
return <div>Create Account</div>;
}
5 changes: 5 additions & 0 deletions src/app/(sidebar)/account/fund/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use client";

export default function FundAccount() {
return <div>Fund Account</div>;
}
5 changes: 5 additions & 0 deletions src/app/(sidebar)/account/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use client";

export default function Account() {
return <div>Account content</div>;
}
29 changes: 29 additions & 0 deletions src/app/(sidebar)/account/template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import { LayoutSidebarContent } from "@/components/layout/LayoutSidebarContent";
import { Routes } from "@/constants/routes";

export default function AccountTemplate({
children,
}: {
children: React.ReactNode;
}) {
return (
<LayoutSidebarContent
sidebar={{
navItems: [
{
route: Routes.CREATE_ACCOUNT,
label: "Create Account",
},
{
route: Routes.FUND_ACCOUNT,
label: "Fund Account",
},
],
}}
>
{children}
</LayoutSidebarContent>
);
}
5 changes: 5 additions & 0 deletions src/app/(sidebar)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { LayoutWithSidebar } from "@/components/layout/LayoutWithSidebar";

export default function Layout({ children }: { children: React.ReactNode }) {
return <LayoutWithSidebar>{children}</LayoutWithSidebar>;
}
1 change: 0 additions & 1 deletion src/app/globals.scss

This file was deleted.

9 changes: 5 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { Metadata } from "next";

import { MainLayout } from "./ui/MainLayout";
import { LayoutMain } from "@/components/layout/LayoutMain";

import "@stellar/design-system/build/styles.min.css";
import "./globals.scss";
import "@/styles/globals.scss";

// TODO: update metadata
export const metadata: Metadata = {
title: "Laboratory - Stellar",
description: "",
Expand All @@ -18,8 +19,8 @@ export default function RootLayout({
return (
<html lang="en">
<body>
<div id="root">
<MainLayout>{children}</MainLayout>
<div id="root" className="LabLayout">
<LayoutMain>{children}</LayoutMain>
</div>
</body>
</html>
Expand Down
17 changes: 17 additions & 0 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use client";

import Link from "next/link";

import { Routes } from "@/constants/routes";
import { LayoutContentContainer } from "@/components/layout/LayoutContentContainer";

// TODO: update 404
export default function NotFound() {
return (
<LayoutContentContainer>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href={Routes.ROOT}>Return Home</Link>
</LayoutContentContainer>
);
}
10 changes: 7 additions & 3 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"use client";

import { Layout } from "@stellar/design-system";
import { LayoutContentContainer } from "@/components/layout/LayoutContentContainer";

export default function Home() {
return <Layout.Inset>Page</Layout.Inset>;
export default function Introduction() {
return (
<LayoutContentContainer>
<div>Introduction</div>
</LayoutContentContainer>
);
}
18 changes: 0 additions & 18 deletions src/app/ui/MainLayout.tsx

This file was deleted.

73 changes: 73 additions & 0 deletions src/components/MainNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { usePathname, useSearchParams } from "next/navigation";

import { Routes } from "@/constants/routes";
import { NextLink } from "@/components/NextLink";

type NavLink = {
href: Routes | string;
label: string;
};

const primaryNavLinks: NavLink[] = [
{
href: Routes.ROOT,
label: "Introduction",
},
{
href: Routes.CREATE_ACCOUNT,
label: "Account",
},
{
href: Routes.EXPLORE_ENDPOINTS,
label: "Explore Endpoints",
},
];

const secondaryNavLinks: NavLink[] = [
{
href: "https://developers.stellar.org/docs",
label: "View Documentation",
},
];

export const MainNav = () => {
const pathname = usePathname();
const searchParams = useSearchParams();

console.log(">>> pathname: ", pathname);
console.log(">>> searchParams: ", searchParams.toString());
Comment on lines +37 to +38
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving these for now to test server setup


const isActiveRoute = (link: string) => {
if (link.startsWith("http")) {
return false;
}

return pathname.split("/")[1] === link.split("/")[1];
};

const NavItem = ({ link }: { link: NavLink }) => (
<NextLink
href={link.href}
className={`NavLink ${isActiveRoute(link.href) ? "NavLink--active" : ""}`}
>
{link.label}
</NextLink>
);

return (
<nav className="LabLayout__header__nav">
{/* Primary nav links */}
<div className="LabLayout__header__nav--primary">
{primaryNavLinks.map((l) => (
<NavItem key={l.href} link={l} />
))}
</div>
{/* Secondary nav links */}
<div className="LabLayout__header__nav--secondary">
{secondaryNavLinks.map((sl) => (
<NavItem key={sl.href} link={sl} />
))}
</div>
</nav>
);
};
17 changes: 17 additions & 0 deletions src/components/NextLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ComponentProps } from "react";
import Link from "next/link";

type LinkProps = ComponentProps<typeof Link>;

/** `NextLink` is extended `next/link`. */
export const NextLink = (props: LinkProps) => {
const externalLinkProps = (href: string) => {
const isExternalLink = href?.startsWith("http") || href?.startsWith("//");

return isExternalLink
? { rel: "noreferrer noopener", target: "_blank" }
: {};
};

return <Link {...props} {...externalLinkProps(props.href.toString())} />;
};
11 changes: 11 additions & 0 deletions src/components/SdsLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import NextLink from "next/link";
import { Props as LinkProps, Link } from "@stellar/design-system";

/** Use `SdsLink` instead of `Link` from Stellar Design System to support client-side routing. `SdsLink` uses `next/link` internally. */
export const SdsLink = (props: LinkProps) => {
return (
<Link {...props} customAnchor={<NextLink href={props.href || ""} />}>
{props.children}
</Link>
);
};
9 changes: 9 additions & 0 deletions src/components/layout/LayoutContentContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const LayoutContentContainer = ({
children,
}: {
children: React.ReactNode;
}) => (
<div className="LabLayout__container">
<div className="LabLayout__content">{children}</div>
</div>
);
31 changes: 31 additions & 0 deletions src/components/layout/LayoutMain.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";

import { ReactNode } from "react";
import Link from "next/link";

import { ProjectLogo, ThemeSwitch } from "@stellar/design-system";

import { MainNav } from "@/components/MainNav";

export const LayoutMain = ({ children }: { children: ReactNode }) => {
return (
<>
<div className="LabLayout__header">
<header className="LabLayout__header__main">
<ProjectLogo
title="Laboratory"
link="/"
customAnchor={<Link href="/" />}
/>

<div className="LabLayout__header__settings">
<ThemeSwitch storageKeyId="stellarTheme:Laboratory" />
</div>
</header>
<MainNav />
</div>

{children}
</>
);
};
81 changes: 81 additions & 0 deletions src/components/layout/LayoutSidebarContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client";

import { ReactNode } from "react";
import { usePathname } from "next/navigation";
import { Icon } from "@stellar/design-system";
import { Routes } from "@/constants/routes";
import { NextLink } from "@/components/NextLink";

export type SidebarLink = {
route: Routes | string;
label: string;
icon?: ReactNode;
nestedItems?: SidebarLink[];
};

export type Sidebar = {
navItems: SidebarLink[];
instruction?: string;
bottomItems?: SidebarLink[];
};

export const LayoutSidebarContent = ({
children,
sidebar,
}: {
children: ReactNode;
sidebar: Sidebar;
}) => {
const pathname = usePathname();

const Link = ({ item }: { item: SidebarLink }) => (
<NextLink
href={item.route}
className={`SidebarLink ${
pathname === item.route ? "SidebarLink--active" : ""
}`}
>
{item.icon ?? null} {item.label}
</NextLink>
);

return (
<>
<div className="LabLayout__sidebar">
<div className="LabLayout__sidebar--top">
{sidebar.instruction ? (
<div className="LabLayout__sidebar__instruction">
{sidebar.instruction}
</div>
) : null}

{/* TODO: render nested items */}
{sidebar.navItems.map((item) => (
<Link key={item.route} item={item} />
))}
</div>
<div
className={`LabLayout__sidebar--bottom ${
sidebar.bottomItems?.length
? "LabLayout__sidebar--bottom--border"
: ""
}`}
>
<div className="LabLayout__sidebar__wrapper">
{sidebar.bottomItems?.map((bi) => (
<Link key={bi.route} item={bi} />
))}
</div>
<div className="LabLayout__sidebar__wrapper">
<NextLink href="https://stellar.org" className="SidebarLink">
<Icon.MessageTextSquare02 /> Got product feedback?
</NextLink>
</div>
</div>
</div>
<div className="LabLayout__container">
<div className="LabLayout__content">{children}</div>
</div>
</>
);
};
7 changes: 7 additions & 0 deletions src/components/layout/LayoutWithSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client";

import { ReactNode } from "react";

export const LayoutWithSidebar = ({ children }: { children: ReactNode }) => {
return <div className="LabLayout__withSidebar">{children}</div>;
};
10 changes: 10 additions & 0 deletions src/constants/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export enum Routes {
ROOT = "/",
// Account
CREATE_ACCOUNT = "/account/create",
FUND_ACCOUNT = "/account/fund",
CREATE_MUXED_ACCOUNT = "/account/muxed-create",
PARSE_MUXED_ACCOUNT = "/account/muxed-parse",
// Explore Endpoints
EXPLORE_ENDPOINTS = "/explore-endpoints",
}
Loading
Loading