import { useState } from "react"; import { AppShell, Flex, PageContainer, Sidebar, Topbar, TopbarItem } from "@godxjp/ui/layout"; import type { SidebarSectionProp } from "@godxjp/ui/layout"; import { Avatar, AvatarFallback, Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@godxjp/ui/data-display"; import { Button, Text } from "@godxjp/ui/general"; import { Bell, CalendarDays, ClipboardList, FileText, LayoutDashboard, MessageSquare, Search, Settings, Users, } from "lucide-react"; /** * AppShell · 390px の細いバー — ブランドの縮退と Topbar の溢れ契約(gh#728)。 * * 再現している形: 6:1 のロゴロックアップ(`--logo-size-sm` で 144x24)+ start 1 セル + * end 4 セル + ユーザーセル + AppShell 自身のドロワートリガー。27.8.0 ではこの組み合わせが * 390px で破綻していた — brand はどの幅でも 143.7px のまま、`.ui-topbar-start` は width 0、 * end の最後のセルは x=384.8 w=132.3 で viewport の外に描かれていた。 * * ここで見える契約は 2 つ: * 1. `logoCompact` — 狭いバーで `logo` の代わりに描かれる別ノード。別ノードなので `viewBox` * ごと差し替えられる(stylesheet では属性を書き換えられない)。渡さなくても brand セル * 自体が縮むようになった(`--app-shell-brand-max-inline-size`)。 * 2. `Topbar overflow="scroll"`(既定)— 足りなくなったらセルを切り落とさず、バー自身が * インライン方向にスクロールする。セルは DOM に残るので Tab で到達でき、フォーカスが * 当たったセルはブラウザが可視域へ全体を送り込む。 */ const SECTIONS: SidebarSectionProp[] = [ { label: "業務", items: [ { id: "dashboard", label: "ダッシュボード", icon: LayoutDashboard }, { id: "tasks", label: "タスク", icon: ClipboardList }, { id: "docs", label: "ドキュメント", icon: FileText }, { id: "members", label: "メンバー", icon: Users }, ], }, ]; /** 6:1 のロックアップ — design が実物の logotype を渡してくる形そのまま(inline ``)。 */ function BrandLockup() { return ( GoDX Workspace ); } /** 同じブランドの「マークだけ」— viewBox が違うので stylesheet では作れないノード。 */ function BrandMark() { return ( ); } export default function Demo() { const [activeId, setActiveId] = useState("dashboard"); const [compactBrand, setCompactBrand] = useState(true); const [unread, setUnread] = useState(7); const end = ( <> } /> } /> } /> } onClick={() => setUnread(0)} /> 佐藤 ); return ( } logoCompact={compactBrand ? : undefined} sidebar={} topbar={ } labelHideBelow="md" > 統括 } end={end} /> } > ブランドセルは狭いバーで席を譲る `logoCompact` を渡すと、`logoCompactBelow`(既定 `sm`)の段より下でそのノードが `logo` の代わりに描かれる。 渡さない場合でも brand セルは sm 未満で `--app-shell-brand-compact-max-inline-size`(=バーの高さ)まで詰められ、 はみ出した 分は inline-end 側から落ちる。27.8.0 のように 143.7px のまま居座ることはもうない。ボタンで両方を比べられる。 {compactBrand ? "マークだけの viewBox に差し替え" : "6:1 ロックアップを縮小"} 溢れたクラスタは切り落とさずスクロールする `Topbar overflow="scroll"`(既定)。320px ではこのバーはまだ 44px 足りない。足りない分は start だけに押し付けられず(end も `flex: 0 1 auto` になった)、残りは バー自身が横スクロールで引き受ける — start はセル 1 つ分の下限を、end はセルの実寸を保つので、 セルが「半分だけ描かれる」ことはない。 `display: none` にもならないので Tab で到達でき、フォーカスの当たったセルはブラウザが可視域へ全体を送り込む。 `overflow="clip"` で 27.8.0 以前の切り落としに戻せる。 {[ { prop: "start", desc: "統括セル(md 未満はアイコンのみ · 名前は残る)" }, { prop: "end", desc: "検索 / 予定 / メッセージ / 通知 + アカウント" }, { prop: "overflow", desc: '"scroll"(既定)| "clip"' }, ].map(({ prop, desc }) => ( {prop} {desc} ))} ); }