Yugen UI

AppShell

AppShell là khung của ứng dụng: sidebar thu về icon hoặc mở thành Drawer, thanh trên cùng và nội dung chính.

AppShell xếp các phần vào lưới: Sidebar bên trái, AppShellHeader phía trên, AppShellMain ở giữa và AppShellAside bên phải; phần nào không dùng thì không chiếm chỗ. Từ breakpoint (container query, theo độ rộng khung chứ không theo màn hình) sidebar nằm trong bố cục và SidebarTrigger thu nó về rail chỉ còn icon, tên mục hiện trong Tooltip. Hẹp hơn, cùng nội dung mở trong Drawer: theo một link hoặc chọn một mục Nav là Drawer đóng, focus quay về nút đã mở. Ctrl+B (⌘B) làm việc giống SidebarTrigger, trừ khi đang gõ trong ô nhập hoặc bằng bộ gõ; trang có nhiều AppShell (như trang này) thì chỉ khung đang được focus hoặc vừa được bấm vào nhận phím. persistKey lưu trạng thái thu gọn (và độ rộng) vào cookie; Drawer không bao giờ được lưu. Render ở server: đọc cookie bằng readAppShellCookie(cookieHeader, key) rồi truyền defaultCollapsed để không nháy. AppShell là container tên app-shell: phần nào cần theo độ rộng khung thì dùng @3xl/app-shell:…. Chiều cao thanh trên cùng đổi qua biến --app-shell-header (mặc định 3.5rem), SidebarHeader và các phần dính theo cùng biến. Nút SidebarTrigger có Tooltip mang nhãn và phím tắt. Các demo dưới đây nằm trong trang docs nên AppShellMain dùng asChild với div (một trang chỉ có một <main>).
Trang chủ

Import

Cài từ npm, chép mã nguồn vào dự án bằng @yugen-ui/cli hoặc shadcn CLI, hoặc import thẳng khi làm việc trong repo này.

TSX
// npm install yugen-uiimport {  AppShell, AppShellHeader, AppShellMain, AppShellAside, AppShellFooter,  Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarGroup, SidebarTrigger, SidebarRail,  useAppShell, readAppShellCookie,} from "yugen-ui";

Biến thể

Mỗi ô là một mẫu độc lập: hover để Copy, hoặc bấm Code để xem mã nguồn.

Trang chủ
Nội dung trang

Khung hẹp

Hẹp hơn breakpoint, SidebarTrigger mở sidebar trong Drawer. Chọn một mục là Drawer đóng.

Trang chủ

Khung từ 42rem: sidebar nằm trong bố cục.

Đổi breakpoint

breakpoint="2xl" (42rem). Mốc đo theo độ rộng của AppShell chứ không theo màn hình: kéo góc dưới của khung để thử. Qua mốc khi Drawer đang mở thì Drawer đóng, focus về SidebarTrigger, nút bị ẩn thì về nội dung; trạng thái thu gọn giữ nguyên. Phần tự làm cần đổi cùng lúc thì dùng @2xl/app-shell:…, hoặc useAppShell().narrow cho hành vi (luôn là false khi render ở server). md: và useMediaQuery theo màn hình nên sẽ lệch mốc.

Hộp thư
Danh sách thư

Sidebar + cột phụ

AppShellAside nằm bên phải nội dung, chỉ hiện từ breakpoint. Trên màn hẹp, đưa nội dung của nó vào Sheet hoặc một trang riêng.

Sakura
© Sakura

Trang nội dung: cả trang cuộn

Không có Sidebar, cột của nó rộng 0. scroll="page" cho site nội dung (blog, cửa hàng, landing): cả trang cuộn, thanh trên cùng dính lại, AppShellFooter ở cuối trang. Trong một vùng tự cuộn như khung này, vùng đó thay cho cửa sổ.

Sakura Admin
Danh sách khách hàng

Thanh trên cùng trải ngang

layout="header": thanh trên cùng (logo, tìm kiếm, tài khoản) trải hết chiều ngang, sidebar nằm bên dưới. Hợp với trang quản trị.

Kế hoạch tuần
Tin nhắn

Ẩn hẳn sidebar & useAppShell

collapsible="offcanvas" cho danh sách không có icon (hội thoại, tệp): thu gọn là ẩn hẳn. Nút trong sidebar không phải link thì tự đóng Drawer bằng useAppShell().

Cách dùng

Nên
  • Đặt AppShell ở gốc ứng dụng, cao bằng màn hình; nội dung cuộn trong AppShellMain.
  • Mỗi NavItem trong sidebar có icon khi dùng collapsible="icon", để rail vẫn dùng được.
  • Dùng persistKey để người dùng mở lại ứng dụng vẫn thấy sidebar như lần trước.
Không nên
  • Không đặt phần tử khác ngoài các phần của AppShell trực tiếp trong AppShell: lưới sẽ xếp nó vào ô trống.
  • Không lồng AppShellMain (thẻ <main>) trong một trang đã có <main>; dùng asChild với div.
  • Không đổi Ctrl+B sang phím trùng phím tắt của trình duyệt hay của ô soạn thảo.

Props

Prop · KiểuMặc địnhMô tả
breakpoint"lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl"Mặc định: "4xl"Độ rộng của AppShell (không phải màn hình) từ đó sidebar nằm trong bố cục và AppShellAside hiện: 32 / 36 / 42 / 48 / 56 / 64rem, trùng mốc container query @lg … @5xl. Hẹp hơn là Drawer.
layout"sidebar" | "header"Mặc định: "sidebar"sidebar: sidebar cao hết khung, thanh trên cùng bên cạnh. header: thanh trên cùng trải ngang, sidebar bên dưới.
scroll"main" | "page"Mặc định: "main"main: khung cao bằng màn hình, AppShellMain tự cuộn (ứng dụng). page: cả trang cuộn, thanh trên cùng và sidebar dính lại (site nội dung).
collapsed / defaultCollapsedbooleanMặc định: falseSidebar thu gọn trên màn rộng.
onCollapsedChange(collapsed: boolean) => voidMặc định: —Gọi khi thu gọn / mở rộng.
persistKeystringMặc định: —Tên cookie lưu trạng thái thu gọn và độ rộng; giá trị đã lưu thắng defaultCollapsed, cookie thiếu hoặc hỏng thì dùng defaultCollapsed. Chỉ gồm chữ, số, _ và -: tên khác thì không lưu gì (có cảnh báo khi phát triển). Cookie chỉ chứa collapsed hoặc expanded, kèm độ rộng (px) khi Sidebar resizable đã được kéo. Cookie đặt ở path=/ với samesite=lax, thêm secure trên https, nên mỗi ứng dụng cùng domain cần một tên riêng.
shortcutstring | falseMặc định: "b"Phím đi cùng Ctrl / ⌘; false để tắt. Nhiều AppShell trên một trang: chỉ khung đang được focus hoặc vừa được bấm vào nhận phím.
skipLinkbooleanMặc định: trueLink “Chuyển tới nội dung” hiện khi được focus.
Sidebar.collapsible"icon" | "offcanvas" | "none"Mặc định: "icon"Thu về rail chỉ icon, ẩn hẳn, hoặc luôn mở.
Sidebar.resizablebooleanMặc định: falseSidebarRail thành thanh chia kéo đổi độ rộng (mũi tên, Home / End, Enter thu gọn).
Sidebar.defaultWidth / minWidth / maxWidthnumberMặc định: 256 / 200 / 400Độ rộng khi mở, px.
Sidebar.labelstringMặc định: —Tên của Drawer trên màn hẹp.
SidebarGroup.labelReactNodeMặc định: —Tên nhóm; trong rail chỉ còn cho trình đọc màn hình.
AppShellHeader / AppShellMain / AppShellAside / AppShellFooter.asChildbooleanMặc định: —Đổi thẻ header / main / aside / footer sang phần tử con.
useAppShell()AppShellStateMặc định: —narrow, collapsed, setCollapsed, drawerOpen, setDrawerOpen, toggleSidebar, rail.
readAppShellCookie(cookie, persistKey){ collapsed, width? } | undefinedMặc định: —Đọc trạng thái đã lưu từ document.cookie hoặc header Cookie khi render ở server. Gọi được trong Server Component khi import từ yugen-ui (gốc gói).

Messages

Chữ có sẵn của component: nhãn, aria-label, thông báo cho trình đọc màn hình.

Mặc định là tiếng Anh. Đổi qua prop messages của YugenProvider, theo namespace bên dưới; messagesVi chứa bản tiếng Việt. Xem Ngôn ngữ giao diện.

messages.appShell

Khoá · KiểuMặc định (tiếng Anh)messagesVi
skipToContentstring

Skip link đầu trang, tới AppShellMain.

Mặc định (tiếng Anh)"Skip to content"messagesVi"Chuyển tới nội dung"
sidebarstring

Tên của Drawer chứa sidebar trên màn hẹp.

Mặc định (tiếng Anh)"Sidebar"messagesVi"Thanh bên"
openSidebarstring

SidebarTrigger trên màn hẹp.

Mặc định (tiếng Anh)"Open sidebar"messagesVi"Mở thanh bên"
collapseSidebarstring

SidebarTrigger và SidebarRail trên màn rộng.

Mặc định (tiếng Anh)"Collapse sidebar"messagesVi"Thu gọn thanh bên"
expandSidebarstringMặc định (tiếng Anh)"Expand sidebar"messagesVi"Mở rộng thanh bên"
resizeSidebarstring

Thanh chia kéo đổi độ rộng (Sidebar resizable).

Mặc định (tiếng Anh)"Resize sidebar"messagesVi"Đổi độ rộng thanh bên"