Files
next-tjwater-frontend/src/features/workbench/layout/workbench-layout.ts
T
jiang 2d8506fea0
Generic Container CI/CD / test-build-publish (push) Successful in 2m9s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m39s
feat: add project-aware agent workspace
2026-10-22 18:52:18 +08:00

175 lines
5.4 KiB
TypeScript

export const WORKBENCH_LAYOUT = {
headerHeight: 56,
desktopMinWidth: 1024,
persistentConditionMinWidth: 1280,
wideMinWidth: 1536,
collapsedAgentWidth: 0,
collapsedAgentRailWidth: 64,
minAgentWidth: 400,
maxAgentWidth: 640,
agentPanelLeftInset: 12,
conditionPanelRightInset: 64,
minimumAgentMapCorridor: 256,
mapEdgeGap: 24,
desktop: {
agentWidth: 500,
conditionWidth: 432,
conditionExpandedWidth: 880,
toolbarWidth: 48,
tickerWidth: 420
},
wide: {
agentWidth: 540,
conditionWidth: 432,
conditionExpandedWidth: 960,
toolbarWidth: 48,
tickerWidth: 460
},
mobileSheet: {
halfViewportRatio: 0.52,
fullTopGap: 68
},
mobileOverlay: {
edgeInset: 12,
gap: 8,
dockHeight: 44,
timelineSummaryHeight: 48
}
} as const;
export function resolveMobileWorkbenchOverlayLayout(timelineVisible: boolean) {
const { edgeInset, gap, dockHeight, timelineSummaryHeight } = WORKBENCH_LAYOUT.mobileOverlay;
const dockBottom = timelineVisible ? edgeInset + timelineSummaryHeight + gap : edgeInset;
return {
timelineBottom: edgeInset,
dockBottom,
mapControlBottom: dockBottom + dockHeight + gap
};
}
export function clampAgentPanelWidth(
width: number,
viewportWidth: number,
conditionExpanded = false
) {
const maxWidth = getAgentPanelMaxWidth(viewportWidth, conditionExpanded);
return Math.round(
Math.min(Math.max(width, WORKBENCH_LAYOUT.minAgentWidth), maxWidth)
);
}
export function getAgentPanelDefaultWidth(viewportWidth: number, conditionExpanded = false) {
const layout = getWorkbenchViewportLayout(viewportWidth);
if (viewportWidth < WORKBENCH_LAYOUT.desktopMinWidth) {
return layout.agentWidth;
}
const conditionWidth = conditionExpanded ? layout.conditionExpandedWidth : layout.conditionWidth;
const collisionSafeWidth =
viewportWidth -
WORKBENCH_LAYOUT.agentPanelLeftInset -
WORKBENCH_LAYOUT.mapEdgeGap -
conditionWidth -
WORKBENCH_LAYOUT.mapEdgeGap -
layout.toolbarWidth;
return Math.min(layout.agentWidth, collisionSafeWidth);
}
export function getAgentPanelMaxWidth(viewportWidth: number, conditionExpanded = false) {
const layout = getWorkbenchViewportLayout(viewportWidth);
const conditionWidth = conditionExpanded ? layout.conditionExpandedWidth : layout.conditionWidth;
const widthWithMapCorridor =
viewportWidth -
WORKBENCH_LAYOUT.agentPanelLeftInset -
WORKBENCH_LAYOUT.conditionPanelRightInset -
conditionWidth -
WORKBENCH_LAYOUT.minimumAgentMapCorridor;
return Math.min(WORKBENCH_LAYOUT.maxAgentWidth, Math.max(layout.agentWidth, widthWithMapCorridor));
}
export type WorkbenchPanelState = {
agentOpen: boolean;
agentWidth?: number;
conditionOpen: boolean;
conditionExpanded?: boolean;
};
export type WorkbenchBasemapTone = "light" | "satellite" | "none";
export type WorkbenchViewportLayout = {
agentWidth: number;
conditionWidth: number;
conditionExpandedWidth: number;
toolbarWidth: number;
tickerWidth: number;
};
export function getWorkbenchViewportLayout(viewportWidth: number): WorkbenchViewportLayout {
return viewportWidth >= WORKBENCH_LAYOUT.wideMinWidth
? WORKBENCH_LAYOUT.wide
: WORKBENCH_LAYOUT.desktop;
}
export function getWorkbenchBasemapTone(activeBaseLayerId: string): WorkbenchBasemapTone {
if (["mapbox-light", "tianditu-vector"].includes(activeBaseLayerId)) {
return "light";
}
if (["mapbox-satellite", "tianditu-image"].includes(activeBaseLayerId)) {
return "satellite";
}
return "none";
}
export function getWorkbenchCameraPadding(viewportWidth: number, panels: WorkbenchPanelState) {
if (viewportWidth < WORKBENCH_LAYOUT.desktopMinWidth) {
return { top: 72, right: 24, bottom: 72, left: 24 };
}
const layout = getWorkbenchViewportLayout(viewportWidth);
const agentWidth = panels.agentOpen
? panels.agentWidth === undefined
? getAgentPanelDefaultWidth(
viewportWidth,
panels.conditionOpen && Boolean(panels.conditionExpanded)
)
: clampAgentPanelWidth(
panels.agentWidth,
viewportWidth,
panels.conditionOpen && Boolean(panels.conditionExpanded)
)
: WORKBENCH_LAYOUT.collapsedAgentWidth;
const conditionWidth = panels.conditionOpen
? panels.conditionExpanded
? layout.conditionExpandedWidth
: layout.conditionWidth
: 0;
return {
top: WORKBENCH_LAYOUT.headerHeight + 16,
right: WORKBENCH_LAYOUT.mapEdgeGap + layout.toolbarWidth + conditionWidth,
bottom: 32,
left: WORKBENCH_LAYOUT.mapEdgeGap + agentWidth
};
}
export function getWorkbenchLayoutCssVariables() {
return {
"--workbench-agent-width": `${WORKBENCH_LAYOUT.desktop.agentWidth}px`,
"--workbench-agent-width-wide": `${WORKBENCH_LAYOUT.wide.agentWidth}px`,
"--workbench-agent-rail-width": `${WORKBENCH_LAYOUT.collapsedAgentRailWidth}px`,
"--workbench-condition-width": `${WORKBENCH_LAYOUT.desktop.conditionWidth}px`,
"--workbench-condition-width-wide": `${WORKBENCH_LAYOUT.wide.conditionWidth}px`,
"--workbench-condition-expanded-width": `${WORKBENCH_LAYOUT.desktop.conditionExpandedWidth}px`,
"--workbench-condition-expanded-width-wide": `${WORKBENCH_LAYOUT.wide.conditionExpandedWidth}px`,
"--workbench-toolbar-width": `${WORKBENCH_LAYOUT.desktop.toolbarWidth}px`,
"--workbench-ticker-width": `${WORKBENCH_LAYOUT.desktop.tickerWidth}px`,
"--workbench-ticker-width-wide": `${WORKBENCH_LAYOUT.wide.tickerWidth}px`
} as const;
}