Skip to content
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
4 changes: 2 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Every WinLab app is framed by four fixed corners, 24px in from the viewport. `ap
| Corner | Default job | Default content |
|--------|-----|---------|
| Top left | Where you are | Breadcrumb: the lab (portal home), then the app (its home) |
| Top right | Where you can go | The app's pages; the current one in `foreground` |
| Bottom left | Who you are, which build | The signed-in member (to their profile), then the short commit of this build, linking to its CD run, with the build time (Asia/Taipei) as its tip |
| Top right | Where you can go | The app's pages, the current one in `foreground`; last, the account: "登入" when signed out, the member's name when signed in |
| Bottom left | Which build | The short commit of this build, linking to its CD run, with the build time (Asia/Taipei) as its tip |
| Bottom right | Whose it is | © year; its tip says NYCU WinLab |

- Actions are not navigation: a "新增訂單" button goes in the page header next to the content it changes, never in a corner.
Expand Down
6 changes: 2 additions & 4 deletions app/_docs/demos/app-shell.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export function Demo() {
return (
<p className="max-w-md text-center text-muted-foreground">
這個頁面的四個角就是
app-shell:左上是麵包屑,右上是導覽,左下是這次建置的版本(點了開 CD
紀錄),右下是版權年份。把滑鼠移到角落的項目上可以看到提示。
<p className="text-muted-foreground">
左上麵包屑、右上導覽與帳號、左下版本、右下版權。
</p>
)
}
3 changes: 1 addition & 2 deletions registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -680,13 +680,12 @@
"name": "app-shell",
"type": "registry:block",
"title": "App Shell",
"description": "The four corners of a WinLab app: breadcrumb, page nav, member and build version, copyright year.",
"description": "The four corners of a WinLab app: breadcrumb, page nav and account, build version, copyright year.",
"dependencies": [
"cn"
],
"registryDependencies": [
"https://ui.winlab.tw/r/utils.json",
"https://ui.winlab.tw/r/avatar.json",
"https://ui.winlab.tw/r/tooltip.json"
],
"files": [
Expand Down
39 changes: 10 additions & 29 deletions registry/winlab/blocks/app-shell/app-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import Link from "next/link"
import { usePathname } from "next/navigation"
import { cn } from "@/registry/winlab/lib/utils"

import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/winlab/ui/avatar"
import {
Tooltip,
TooltipContent,
Expand All @@ -21,16 +16,15 @@ import {
// holds); it never repeats the label.
type Crumb = { label: string; href: string; tip?: string }
type NavItem = { label: string; href: string; tip?: string }
type User = { name: string; href: string; image?: string; tip?: string }

// The chrome of every WinLab app is its four corners. By default each corner
// has one job, filled from the props below; an app that needs something else
// in a corner passes it in `corners`, built from CornerLink and CornerTip so
// it keeps the corner's look.
// top left where you are: breadcrumb back to the lab and the app
// top right where you can go: this app's pages
// bottom left who you are and which build: the signed-in member, then the
// commit this build came from (the theme follows the system)
// top right where you can go: this app's pages, then the account (登入
// when signed out, the member's name when signed in)
// bottom left which build: the commit this build came from
// bottom right the copyright year; the tip names the owner
// Every item can carry a tip, opening toward the page and lined up with the
// corner's outer edge so it never leaves the viewport.
Expand All @@ -43,14 +37,16 @@ type User = { name: string; href: string; image?: string; tip?: string }
function AppShell({
breadcrumb = [],
nav = [],
user,
account,
layout = "column",
corners,
children,
}: {
breadcrumb?: Crumb[]
nav?: NavItem[]
user?: User
/** Last in the top right: { label: "登入", href: "/sign-in" } signed out,
* the member's name linking to their page signed in. */
account?: NavItem
corners?: Partial<
Record<
"topLeft" | "topRight" | "bottomLeft" | "bottomRight",
Expand Down Expand Up @@ -106,9 +102,9 @@ function AppShell({

<div className="fixed top-6 right-6 z-50 flex items-center gap-4">
{corners?.topRight ??
(nav.length > 0 && (
((nav.length > 0 || account) && (
<nav aria-label="頁面" className="flex items-center gap-4">
{nav.map((item) => (
{[...nav, ...(account ? [account] : [])].map((item) => (
<CornerTip key={item.href} tip={item.tip} corner="top-right">
<Link
href={item.href}
Expand All @@ -127,22 +123,7 @@ function AppShell({
</div>

<div className="fixed bottom-6 left-6 z-50 flex items-center gap-4">
{corners?.bottomLeft ?? (
<>
{user && (
<CornerTip tip={user.tip ?? "個人頁面"} corner="bottom-left">
<Link href={user.href} className={cn(cornerLink, "gap-2")}>
<Avatar className="size-6">
{user.image && <AvatarImage src={user.image} alt="" />}
<AvatarFallback>{user.name.slice(0, 1)}</AvatarFallback>
</Avatar>
{user.name}
</Link>
</CornerTip>
)}
<BuildVersion />
</>
)}
{corners?.bottomLeft ?? <BuildVersion />}
</div>

<div className="fixed right-6 bottom-6 z-50 flex items-center gap-4">
Expand Down
Loading