From b897cb9fe0fc327df18bd1892ff1d35b26f79eaf Mon Sep 17 00:00:00 2001 From: Iveta Date: Tue, 13 Feb 2024 17:43:39 -0500 Subject: [PATCH] Layout mostly done --- package.json | 2 +- src/app/(sidebar)/account/create/page.tsx | 5 + src/app/(sidebar)/account/fund/page.tsx | 5 + src/app/(sidebar)/account/page.tsx | 5 + src/app/(sidebar)/account/template.tsx | 38 ++++ src/app/(sidebar)/layout.tsx | 9 + src/app/globals.scss | 1 - src/app/layout.tsx | 8 +- src/app/not-found.tsx | 15 ++ src/app/page.tsx | 12 +- src/app/ui/MainLayout.tsx | 18 -- src/components/LayoutMain.tsx | 31 ++++ src/components/LayoutSidebar.tsx | 7 + src/components/LayoutSidebarContent.tsx | 77 ++++++++ src/components/MainNav.tsx | 78 +++++++++ src/components/NavLink.tsx | 11 ++ src/constants/routes.ts | 10 ++ src/styles/globals.scss | 203 ++++++++++++++++++++++ src/styles/utils.scss | 12 ++ tsconfig.json | 22 ++- yarn.lock | 8 +- 21 files changed, 541 insertions(+), 36 deletions(-) create mode 100644 src/app/(sidebar)/account/create/page.tsx create mode 100644 src/app/(sidebar)/account/fund/page.tsx create mode 100644 src/app/(sidebar)/account/page.tsx create mode 100644 src/app/(sidebar)/account/template.tsx create mode 100644 src/app/(sidebar)/layout.tsx delete mode 100644 src/app/globals.scss create mode 100644 src/app/not-found.tsx delete mode 100644 src/app/ui/MainLayout.tsx create mode 100644 src/components/LayoutMain.tsx create mode 100644 src/components/LayoutSidebar.tsx create mode 100644 src/components/LayoutSidebarContent.tsx create mode 100644 src/components/MainNav.tsx create mode 100644 src/components/NavLink.tsx create mode 100644 src/constants/routes.ts create mode 100644 src/styles/globals.scss create mode 100644 src/styles/utils.scss diff --git a/package.json b/package.json index 21f67781..9ca808dc 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/app/(sidebar)/account/create/page.tsx b/src/app/(sidebar)/account/create/page.tsx new file mode 100644 index 00000000..3aa44803 --- /dev/null +++ b/src/app/(sidebar)/account/create/page.tsx @@ -0,0 +1,5 @@ +"use client"; + +export default function CreateAccount() { + return
Create Account
; +} diff --git a/src/app/(sidebar)/account/fund/page.tsx b/src/app/(sidebar)/account/fund/page.tsx new file mode 100644 index 00000000..afb29bdc --- /dev/null +++ b/src/app/(sidebar)/account/fund/page.tsx @@ -0,0 +1,5 @@ +"use client"; + +export default function FundAccount() { + return
Fund Account
; +} diff --git a/src/app/(sidebar)/account/page.tsx b/src/app/(sidebar)/account/page.tsx new file mode 100644 index 00000000..b9e1da7f --- /dev/null +++ b/src/app/(sidebar)/account/page.tsx @@ -0,0 +1,5 @@ +"use client"; + +export default function Account() { + return
Account content
; +} diff --git a/src/app/(sidebar)/account/template.tsx b/src/app/(sidebar)/account/template.tsx new file mode 100644 index 00000000..4c636a47 --- /dev/null +++ b/src/app/(sidebar)/account/template.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { Icon } from "@stellar/design-system"; +import { LayoutSidebarContent } from "@/components/LayoutSidebarContent"; +import { Routes } from "@/constants/routes"; + +export default function AccountTemplate({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/src/app/(sidebar)/layout.tsx b/src/app/(sidebar)/layout.tsx new file mode 100644 index 00000000..2e47d2c1 --- /dev/null +++ b/src/app/(sidebar)/layout.tsx @@ -0,0 +1,9 @@ +import { LayoutSidebar } from "@/components/LayoutSidebar"; + +export default function LayoutWithSidebar({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/src/app/globals.scss b/src/app/globals.scss deleted file mode 100644 index 2c65a946..00000000 --- a/src/app/globals.scss +++ /dev/null @@ -1 +0,0 @@ -@import url("https://fonts.googleapis.com/css2?family=Inter+Tight&family=Inter:wght@400;500;600&family=Roboto+Mono&display=swap"); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f62e2769..d15be565 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,9 +1,9 @@ import type { Metadata } from "next"; -import { MainLayout } from "./ui/MainLayout"; +import { LayoutMain } from "@/components/LayoutMain"; import "@stellar/design-system/build/styles.min.css"; -import "./globals.scss"; +import "@/styles/globals.scss"; export const metadata: Metadata = { title: "Laboratory - Stellar", @@ -18,8 +18,8 @@ export default function RootLayout({ return ( -
- {children} +
+ {children}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 00000000..0195d480 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,15 @@ +"use client"; + +import Link from "next/link"; + +import { Routes } from "@/constants/routes"; + +export default function NotFound() { + return ( +
+

Not Found

+

Could not find requested resource

+ Return Home +
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 5a7ab853..c9fb33c7 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,11 @@ "use client"; -import { Layout } from "@stellar/design-system"; - -export default function Home() { - return Page; +export default function Introduction() { + return ( +
+
+
Introduction
+
+
+ ); } diff --git a/src/app/ui/MainLayout.tsx b/src/app/ui/MainLayout.tsx deleted file mode 100644 index 4c48d93e..00000000 --- a/src/app/ui/MainLayout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -"use client"; - -import { ReactNode } from "react"; -import { Layout } from "@stellar/design-system"; - -export const MainLayout = ({ children }: { children: ReactNode }) => { - return ( - <> - - {children} - - - ); -}; diff --git a/src/components/LayoutMain.tsx b/src/components/LayoutMain.tsx new file mode 100644 index 00000000..05fd6127 --- /dev/null +++ b/src/components/LayoutMain.tsx @@ -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 ( + <> +
+
+ } + /> + +
+ +
+
+ +
+ + {children} + + ); +}; diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx new file mode 100644 index 00000000..f244d74b --- /dev/null +++ b/src/components/LayoutSidebar.tsx @@ -0,0 +1,7 @@ +"use client"; + +import { ReactNode } from "react"; + +export const LayoutSidebar = ({ children }: { children: ReactNode }) => { + return
{children}
; +}; diff --git a/src/components/LayoutSidebarContent.tsx b/src/components/LayoutSidebarContent.tsx new file mode 100644 index 00000000..7bcc8cfb --- /dev/null +++ b/src/components/LayoutSidebarContent.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { ReactNode } from "react"; +import { usePathname } from "next/navigation"; +import NextLink from "next/link"; +import { Routes } from "@/constants/routes"; +import { Icon } from "@stellar/design-system"; + +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 }) => ( + + {item.icon ?? null} {item.label} + + ); + + return ( + <> +
+
+ {/* TODO: add instruction */} + {/* TODO: render nested items */} + + {sidebar.navItems.map((item) => ( + + ))} +
+
+
+ {sidebar.bottomItems?.map((bi) => ( + + ))} +
+
+ + Got product feedback? + +
+
+
+
+
{children}
+
+ + ); +}; diff --git a/src/components/MainNav.tsx b/src/components/MainNav.tsx new file mode 100644 index 00000000..b2ecc2e4 --- /dev/null +++ b/src/components/MainNav.tsx @@ -0,0 +1,78 @@ +import NextLink from "next/link"; +import { usePathname } from "next/navigation"; + +import { Routes } from "@/constants/routes"; + +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 = [ + { + href: "https://developers.stellar.org/docs", + label: "View Documentation", + }, +]; + +export const MainNav = () => { + const pathname = usePathname(); + + const externalLinkProps = (href: string) => { + const isExternalLink = href?.startsWith("http") || href?.startsWith("//"); + + return isExternalLink + ? { rel: "noreferrer noopener", target: "_blank" } + : {}; + }; + + const isActiveRoute = (link: string) => { + if (link.startsWith("http")) { + return false; + } + + return pathname.split("/")[1] === link.split("/")[1]; + }; + + const NavItem = ({ link }: { link: NavLink }) => ( + + {link.label} + + ); + + return ( + + ); +}; diff --git a/src/components/NavLink.tsx b/src/components/NavLink.tsx new file mode 100644 index 00000000..a64c647d --- /dev/null +++ b/src/components/NavLink.tsx @@ -0,0 +1,11 @@ +import NextLink from "next/link"; +import { Props as LinkProps, Link } from "@stellar/design-system"; + +/** Use `NavLink` instead of `Link` from Stellar Design System to support client-side routing. `NavLink` uses `Link` from `next/link` internally. */ +export const NavLink = (props: LinkProps) => { + return ( + }> + {props.children} + + ); +}; diff --git a/src/constants/routes.ts b/src/constants/routes.ts new file mode 100644 index 00000000..c5524e5e --- /dev/null +++ b/src/constants/routes.ts @@ -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", +} diff --git a/src/styles/globals.scss b/src/styles/globals.scss new file mode 100644 index 00000000..a82f3daa --- /dev/null +++ b/src/styles/globals.scss @@ -0,0 +1,203 @@ +@use "./utils.scss" as *; + +// Fonts for SDS +@import url("https://fonts.googleapis.com/css2?family=Inter+Tight&family=Inter:wght@400;500;600&family=Roboto+Mono&display=swap"); + +// Layout +#root { + &.LabLayout { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto 1fr; + height: 100vh; + overflow: hidden; + min-width: 0; + min-height: 0; + } +} + +.LabLayout { + color: var(--sds-clr-gray-11); + background-color: var(--sds-clr-gray-02); + + // Header + &__header { + background-color: var(--sds-clr-gray-01); + border-bottom: 1px solid var(--sds-clr-gray-06); + + &__main { + display: flex; + align-items: center; + justify-content: space-between; + gap: pxToRem(16px); + padding: pxToRem(32px); + } + + &__settings { + display: flex; + align-items: center; + justify-content: flex-end; + flex: 1; + gap: pxToRem(12px); + } + + &__nav { + display: flex; + align-items: center; + justify-content: space-between; + gap: pxToRem(16px); + padding: 0 pxToRem(32px); + + font-size: pxToRem(14px); + line-height: pxToRem(20px); + font-weight: var(--sds-fw-medium); + margin-bottom: -1px; + + & > div { + display: flex; + gap: pxToRem(20px); + flex: 1; + } + + &--secondary { + justify-content: flex-end; + } + } + } + + // Sidebar layout + &__withSidebar { + display: grid; + grid-template-columns: + pxToRem(296px) + 1fr; + overflow: hidden; + } + + // Content + &__container { + padding: pxToRem(32px); + overflow-x: auto; + } + + &__content { + width: pxToRem(672px); + position: relative; + margin: 0 auto; + display: flex; + flex-direction: column; + gap: pxToRem(12px); + } + + // Sideber + &__sidebar { + min-height: 0; + background-color: var(--sds-clr-gray-01); + border-right: 1px solid var(--sds-clr-gray-06); + display: grid; + grid-template-columns: 1fr; + grid-template-rows: + minmax(0, max-content) + 1fr; + + &--top { + overflow-x: auto; + padding: pxToRem(32px); + padding-bottom: pxToRem(16px); + display: flex; + flex-direction: column; + gap: pxToRem(8px); + flex: 1; + } + + &--bottom { + --Sidebar-bottomItems-border-color: transparent; + + flex-grow: 1; + flex-shrink: 0; + border-top: 1px solid var(--Sidebar-bottomItems-border-color); + background-color: var(--sds-clr-gray-01); + display: flex; + flex-direction: column; + justify-content: space-between; + gap: pxToRem(8px); + padding-top: pxToRem(16px); + padding-bottom: pxToRem(32px); + margin-left: pxToRem(32px); + margin-right: pxToRem(32px); + + &--border { + --Sidebar-bottomItems-border-color: var(--sds-clr-gray-06); + } + } + + &__wrapper { + display: flex; + flex-direction: column; + gap: pxToRem(8px); + } + } +} + +// TODO: update name +.NavLink { + --Nav-navLink-color: var(--sds-clr-gray-11); + --Nav-navLink-border-color: transparent; + + color: var(--Nav-navLink-color); + text-decoration: none; + padding: pxToRem(12px) 0; + transition: + color var(--sds-anim-transition-default), + border-color var(--sds-anim-transition-default); + border-bottom: 2px solid var(--Nav-navLink-border-color); + + @media (hover: hover) { + &:hover { + --Nav-navLink-color: var(--sds-clr-lilac-11); + --Nav-navLink-border-color: var(--sds-clr-lilac-09); + } + } + + &--active { + cursor: default; + --Nav-navLink-color: var(--sds-clr-lilac-11); + --Nav-navLink-border-color: var(--sds-clr-lilac-09); + } +} + +.SidebarLink { + --SidebarLink-color: var(--sds-clr-gray-11); + + font-size: pxToRem(14px); + line-height: pxToRem(20px); + font-weight: var(--sds-fw-medium); + color: var(--SidebarLink-color); + transition: + color var(--sds-anim-transition-default), + font-weight var(--sds-anim-transition-default); + text-decoration: none; + display: flex; + align-items: center; + gap: pxToRem(4px); + + svg { + display: block; + width: pxToRem(16px); + height: pxToRem(16px); + stroke: var(--SidebarLink-color); + } + + @media (hover: hover) { + &:hover { + --SidebarLink-color: var(--sds-clr-lilac-11); + font-weight: var(--sds-fw-semi-bold); + } + } + + &--active { + --SidebarLink-color: var(--sds-clr-lilac-11); + cursor: default; + font-weight: var(--sds-fw-semi-bold); + } +} diff --git a/src/styles/utils.scss b/src/styles/utils.scss new file mode 100644 index 00000000..9486e6b8 --- /dev/null +++ b/src/styles/utils.scss @@ -0,0 +1,12 @@ +@use "sass:math"; + +// Convert px to rem +$base-font-size: 16px; + +@function removePxUnit($value) { + @return math.div($value, $value * 0 + 1); +} + +@function pxToRem($pxValue) { + @return #{math.div(removePxUnit($pxValue), removePxUnit($base-font-size))}rem; +} diff --git a/tsconfig.json b/tsconfig.json index e59724b2..93a7f047 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -19,9 +23,19 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "build/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/yarn.lock b/yarn.lock index 2b02a91b..a0e5370b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -168,10 +168,10 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.6.1.tgz#9ab8f811930d7af3e3d549183a50884f9eb83f36" integrity sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw== -"@stellar/design-system@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@stellar/design-system/-/design-system-1.1.2.tgz#09f27c13fef69975c6d9a214e770e419985c0b8e" - integrity sha512-Vge1vYGE2QrGO9ASVyW/o68bK7/e7+TthS4MjaQ6KZB8F2VNEhgTsyOPfCIKs2iewim0jYetHA5PIBs//tapZQ== +"@stellar/design-system@^2.0.0-beta.1": + version "2.0.0-beta.1" + resolved "https://registry.yarnpkg.com/@stellar/design-system/-/design-system-2.0.0-beta.1.tgz#ac4a73459f2bee5fd2b0f5839d02dccf1decd532" + integrity sha512-0q4FI2E7BrzyjBxGb3y2KuLVfEyqD+/jcAwDIImkwDyN25fb2pw0AXuZNPbSSU+3hE6Vt89n4lGXnBp/sPEVBw== dependencies: "@floating-ui/dom" "^1.5.3" bignumber.js "^9.1.1"