Files
Jyotisha/frontend/src/components/shell-frame.tsx
T
jesse-uxandGrok (agent loop) 38097ca82f 桌面加载和打开本命盘不再把整页卸掉再挂上(BUG-1444)。
侧栏和对话列始终留在原来的容器里。分栏到达、窗口跨过手机断点,子组件只挂载一次。

检查:在 frontend 下跑 shell-frame-stable-mount、sidebar-contract、chat-chart-panel-state、class-name-definition-contract。
结果:52 通过,0 失败,退出码 0。

Co-Authored-By: Grok (agent loop)
2026-10-26 22:56:23 +08:00

79 lines
2.5 KiB
TypeScript

"use client";
import { useEffect, useRef, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
import { useSidebar } from "@/components/ui/sidebar";
import {
SIDEBAR_WIDTH_COLLAPSED,
SIDEBAR_WIDTH_DEFAULT,
SIDEBAR_WIDTH_MAX,
SIDEBAR_WIDTH_MIN,
SIDEBAR_WIDTH_TABLET,
} from "@/lib/sidebar-state";
import { useUiStore } from "@/stores/ui-store";
export function ShellFrame({ sidebar, children }: { sidebar: ReactNode; children: ReactNode }) {
const { isMobile, open, setOpen, viewport, ready } = useSidebar();
const width = useUiStore((state) => state.sidebarWidth);
const hydrated = useUiStore((state) => state.sidebarWidthHydrated);
const hydrate = useUiStore((state) => state.hydrateSidebarWidth);
const setSidebarWidth = useUiStore((state) => state.setSidebarWidth);
const dragStop = useRef<(() => void) | null>(null);
const desktop = ready && !isMobile;
const expandedWidth = hydrated
? width
: viewport === "tablet" ? SIDEBAR_WIDTH_TABLET : SIDEBAR_WIDTH_DEFAULT;
const pixelWidth = open ? expandedWidth : SIDEBAR_WIDTH_COLLAPSED;
useEffect(() => {
hydrate();
}, [hydrate]);
useEffect(() => () => dragStop.current?.(), []);
function onResizeStart(event: ReactPointerEvent<HTMLButtonElement>) {
if (event.button !== 0) return;
event.preventDefault();
const startX = event.clientX;
const startWidth = open ? expandedWidth : SIDEBAR_WIDTH_COLLAPSED;
const move = (ev: PointerEvent) => {
const next = startWidth + ev.clientX - startX;
if (next < SIDEBAR_WIDTH_MIN) {
setOpen(false);
return;
}
setOpen(true);
setSidebarWidth(Math.min(SIDEBAR_WIDTH_MAX, Math.round(next)));
};
const stop = () => {
dragStop.current = null;
window.removeEventListener("pointermove", move);
window.removeEventListener("pointerup", stop);
};
dragStop.current = stop;
window.addEventListener("pointermove", move);
window.addEventListener("pointerup", stop);
}
return (
<div
className="shell-frame"
data-shell-resize={desktop ? "live" : "mobile"}
style={desktop ? { ["--shell-sidebar-width" as string]: `${pixelWidth}px` } : undefined}
>
<div className="shell-sidebar-slot">
{sidebar}
{desktop ? (
<button
type="button"
className="shell-resize-handle"
aria-label="调整侧栏宽度"
onPointerDown={onResizeStart}
/>
) : null}
</div>
<div className="shell-main-slot">{children}</div>
</div>
);
}