refactor(canvas): derive toolbar offsets from panel geometry
This commit is contained in:
parent
93e5493993
commit
f2b752dd7d
@ -79,6 +79,10 @@ const SIZE_PRESETS = [
|
|||||||
{ label: "9:16", value: "9:16", width: 1024, height: 1820 },
|
{ label: "9:16", value: "9:16", width: 1024, height: 1820 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const FLOATING_PANEL_WIDTH = 460;
|
||||||
|
const FLOATING_PANEL_INSET = 16;
|
||||||
|
const FLOATING_UI_GAP = 16;
|
||||||
|
|
||||||
export default function CanvasEditorPage({ params }: PageProps) {
|
export default function CanvasEditorPage({ params }: PageProps) {
|
||||||
// 使用 useParams 确保客户端导航时能正确获取更新的 id
|
// 使用 useParams 确保客户端导航时能正确获取更新的 id
|
||||||
const routeParams = useParams();
|
const routeParams = useParams();
|
||||||
@ -124,6 +128,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
const [isPanelOpen, setIsPanelOpen] = useState(true);
|
const [isPanelOpen, setIsPanelOpen] = useState(true);
|
||||||
// 面板模式: 'chat' 聊天面板, 'fileList' 文件列表
|
// 面板模式: 'chat' 聊天面板, 'fileList' 文件列表
|
||||||
const [panelMode, setPanelMode] = useState<'chat' | 'fileList'>('chat');
|
const [panelMode, setPanelMode] = useState<'chat' | 'fileList'>('chat');
|
||||||
|
const [viewportWidth, setViewportWidth] = useState(0);
|
||||||
|
|
||||||
const { data: siteSettingsData } = useSWR<{
|
const { data: siteSettingsData } = useSWR<{
|
||||||
siteName?: string;
|
siteName?: string;
|
||||||
@ -145,6 +150,13 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
? nodes.find(n => n.id === selectedNodeIds[0] && n.type === 'image')
|
? nodes.find(n => n.id === selectedNodeIds[0] && n.type === 'image')
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const resolvedPanelWidth = viewportWidth > 0
|
||||||
|
? Math.min(FLOATING_PANEL_WIDTH, Math.max(viewportWidth - FLOATING_PANEL_INSET, 0))
|
||||||
|
: FLOATING_PANEL_WIDTH;
|
||||||
|
const panelOcclusionRight = isPanelOpen ? resolvedPanelWidth + FLOATING_PANEL_INSET : 0;
|
||||||
|
const floatingToolbarOffset = isPanelOpen ? -(panelOcclusionRight / 2) : 0;
|
||||||
|
const compactControlsRight = isPanelOpen ? panelOcclusionRight + FLOATING_UI_GAP : FLOATING_UI_GAP;
|
||||||
|
|
||||||
// 加载画布数据
|
// 加载画布数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[Canvas] Loading project:', id);
|
console.log('[Canvas] Loading project:', id);
|
||||||
@ -179,6 +191,13 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
loadProject();
|
loadProject();
|
||||||
}, [id]); // 只依赖 id,使用 getState() 直接调用 store actions
|
}, [id]); // 只依赖 id,使用 getState() 直接调用 store actions
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateViewportWidth = () => setViewportWidth(window.innerWidth);
|
||||||
|
updateViewportWidth();
|
||||||
|
window.addEventListener("resize", updateViewportWidth);
|
||||||
|
return () => window.removeEventListener("resize", updateViewportWidth);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const persistCanvasState = useCallback(
|
const persistCanvasState = useCallback(
|
||||||
async (reason: "autosave" | "delete" | "beforeunload") => {
|
async (reason: "autosave" | "delete" | "beforeunload") => {
|
||||||
const canvasData = getCanvasState();
|
const canvasData = getCanvasState();
|
||||||
@ -561,7 +580,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
|
|
||||||
{/* 1. Canvas Layer (Full Screen) - 使用 key 强制在 id 变化时重新挂载 */}
|
{/* 1. Canvas Layer (Full Screen) - 使用 key 强制在 id 变化时重新挂载 */}
|
||||||
<div className="absolute inset-0 z-0" key={`workspace-${id}`}>
|
<div className="absolute inset-0 z-0" key={`workspace-${id}`}>
|
||||||
<CanvasWorkspace occlusionRight={isPanelOpen ? 476 : 0} />
|
<CanvasWorkspace occlusionRight={panelOcclusionRight} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Brand Entry (Top Left) */}
|
{/* Brand Entry (Top Left) */}
|
||||||
@ -641,7 +660,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
|
|
||||||
{/* 3. Floating Actions (Top Right) */}
|
{/* 3. Floating Actions (Top Right) */}
|
||||||
{!isPanelOpen && (
|
{!isPanelOpen && (
|
||||||
<div className="absolute top-4 right-4 z-20">
|
<div className="absolute z-20" style={{ top: FLOATING_PANEL_INSET, right: FLOATING_PANEL_INSET }}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@ -660,9 +679,16 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
|
|
||||||
{/* 4. Floating Sidebar (Right) */}
|
{/* 4. Floating Sidebar (Right) */}
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
"absolute top-4 right-4 bottom-4 z-10 w-[460px] max-w-[calc(100vw-1rem)] transition-all duration-300 ease-in-out",
|
"absolute z-10 transition-all duration-300 ease-in-out",
|
||||||
isPanelOpen ? "translate-x-0 opacity-100" : "translate-x-[120%] opacity-0 pointer-events-none"
|
isPanelOpen ? "translate-x-0 opacity-100" : "translate-x-[120%] opacity-0 pointer-events-none"
|
||||||
)}>
|
)}
|
||||||
|
style={{
|
||||||
|
top: FLOATING_PANEL_INSET,
|
||||||
|
right: FLOATING_PANEL_INSET,
|
||||||
|
bottom: FLOATING_PANEL_INSET,
|
||||||
|
width: FLOATING_PANEL_WIDTH,
|
||||||
|
maxWidth: `calc(100vw - ${FLOATING_PANEL_INSET}px)`,
|
||||||
|
}}>
|
||||||
<div className="h-full w-full rounded-2xl border bg-white dark:bg-zinc-950 shadow-2xl overflow-hidden flex flex-col">
|
<div className="h-full w-full rounded-2xl border bg-white dark:bg-zinc-950 shadow-2xl overflow-hidden flex flex-col">
|
||||||
{panelMode === 'chat' ? (
|
{panelMode === 'chat' ? (
|
||||||
// AI 对话内容 - 使用 key 强制在 id 变化时重新挂载
|
// AI 对话内容 - 使用 key 强制在 id 变化时重新挂载
|
||||||
@ -687,9 +713,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
<div className="absolute bottom-5 left-1/2 z-20 -translate-x-1/2">
|
<div className="absolute bottom-5 left-1/2 z-20 -translate-x-1/2">
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-1 rounded-[24px] border bg-background/88 px-2.5 py-1.5 shadow-2xl backdrop-blur-md"
|
className="flex items-center gap-1 rounded-[24px] border bg-background/88 px-2.5 py-1.5 shadow-2xl backdrop-blur-md"
|
||||||
style={{
|
style={{ marginLeft: `${floatingToolbarOffset}px` }}
|
||||||
marginLeft: isPanelOpen ? "-238px" : "0px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
@ -797,7 +821,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
{/* 6. Bottom Right Compact Controls */}
|
{/* 6. Bottom Right Compact Controls */}
|
||||||
<div
|
<div
|
||||||
className="absolute bottom-5 z-20"
|
className="absolute bottom-5 z-20"
|
||||||
style={{ right: isPanelOpen ? "492px" : "16px" }}
|
style={{ right: `${compactControlsRight}px` }}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1 rounded-2xl border bg-background/88 px-2 py-1.5 shadow-xl backdrop-blur-md">
|
<div className="flex items-center gap-1 rounded-2xl border bg-background/88 px-2 py-1.5 shadow-xl backdrop-blur-md">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user