Keep canvas floating controls visible with the sidebar open

This commit is contained in:
2026-11-07 18:30:33 +08:00
parent 39b85bcda4
commit 6ff7c3064b
2 changed files with 16 additions and 8 deletions
+7 -6
View File
@@ -33,6 +33,7 @@ export default function DashboardSidebar() {
const [status, setStatus] = useState<"loading" | "ready" | "error">("loading");
const [error, setError] = useState("");
const [collapsed, setCollapsed] = useState(false);
const [accountAnchor, setAccountAnchor] = useState<"sidebar" | "floating">("sidebar");
const [query, setQuery] = useState("");
const [creating, setCreating] = useState(false);
const [panel, setPanel] = useState<"settings" | "workspace" | null>(null);
@@ -172,13 +173,13 @@ export default function DashboardSidebar() {
return (
<>
<AnimatePresence initial={false}>
{collapsed && (
<motion.div key="sidebar-controls" className={`${toolbar.surface} ${toolbar.relaxed} ${styles.floating}`} aria-label="侧边栏快捷操作"
{(collapsed || Boolean(params.board)) && (
<motion.div key="sidebar-controls" className={`${toolbar.surface} ${toolbar.relaxed} ${styles.floating}`} data-sidebar-collapsed={collapsed} aria-label={params.board ? "画布快捷操作" : "侧边栏快捷操作"}
initial={{ opacity: 0, y: reduceMotion ? 0 : -6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: reduceMotion ? 0 : -6 }}
transition={{ duration: reduceMotion ? 0 : 0.14 }}>
<IconButton ref={expandButton} icon={PanelLeftOpen} label="展开侧边栏" aria-expanded={false} aria-controls="workspace-sidebar" onClick={() => toggleSidebar(false)} />
{collapsed && <IconButton ref={expandButton} icon={PanelLeftOpen} label="展开侧边栏" aria-expanded={false} aria-controls="workspace-sidebar" onClick={() => toggleSidebar(false)} />}
<Link href="/" className={styles.logoLink} aria-label="啡跃 Endoleap 主页" title="返回主页">
<img src="/brand/png/logo-light/endoleap-32.png"
srcSet="/brand/png/logo-light/endoleap-32.png 1x, /brand/png/logo-light/endoleap-64.png 2x, /brand/png/logo-light/endoleap-128.png 4x"
@@ -189,7 +190,7 @@ export default function DashboardSidebar() {
editable={Boolean(params.board && activeBoard && matchesRef(activeBoard, params.board) && activeBoard.workspace_id === workspace && canCreate)}
onSave={renameTitle} className={styles.floatingTitle} />
<span className={toolbar.divider} aria-hidden="true" />
<button type="button" className={styles.floatingAvatarButton} popoverTarget="workspace-account" aria-label="账户和工作空间">
<button type="button" className={styles.floatingAvatarButton} popoverTarget="workspace-account" aria-label="账户和工作空间" onClick={() => setAccountAnchor("floating")}>
<UserAvatar name={name} url={profile?.avatar_url} />
</button>
{error && <p className={styles.floatingError} role="alert">{error}</p>}
@@ -220,7 +221,7 @@ export default function DashboardSidebar() {
</nav>
{error && <p className={styles.error} role="alert">{error}</p>}
<div className={styles.accountArea}>
<button type="button" className={styles.accountButton} popoverTarget="workspace-account" aria-label="账户和工作空间">
<button type="button" className={styles.accountButton} popoverTarget="workspace-account" aria-label="账户和工作空间" onClick={() => setAccountAnchor("sidebar")}>
<UserAvatar name={name} url={profile?.avatar_url} />
<span className={styles.accountText}><strong>{name}</strong><small>{currentWorkspace?.name || (demo ? "本地演示" : "个人账户")}</small></span>
<ChevronsUpDown size={14} strokeWidth={1.6} />
@@ -228,7 +229,7 @@ export default function DashboardSidebar() {
</div>
</motion.aside>
</motion.div>
<div ref={accountPopup} id="workspace-account" popover="auto" className={styles.accountPopup} data-floating={collapsed} aria-label="账户和工作空间菜单">
<div ref={accountPopup} id="workspace-account" popover="auto" className={styles.accountPopup} data-floating={accountAnchor === "floating"} data-sidebar-collapsed={collapsed} aria-label="账户和工作空间菜单">
<div className={styles.accountHeading}>
<span className={styles.workspaceAvatar} aria-hidden="true">{(currentWorkspace?.name || "啡跃").slice(0, 1)}</span>
<div><strong>{currentWorkspace?.name || "你的工作空间"}</strong><small>{roleLabel}{demo ? " · 本地演示" : ""}</small></div>