import * as d3 from 'd3'; import { isNarrowScreen } from '../utils/responsive'; import { readPanelSplitRatio, writePanelSplitRatio } from '../utils/panelSplitStorage'; export type ChatPanelLayoutOptions = { /** 各页面独立 key,用于 localStorage 持久化分栏比例 */ storageKey: string; }; /** * Chat / 归因 / gen_attribute 等:左右分栏拖拽与窗口尺寸同步,不含侧栏逻辑。 */ export function initChatPanelLayout(options: ChatPanelLayoutOptions): void { const resizer = d3.select('#resizer'); const leftPanel = d3.select('.left_panel'); if (resizer.empty() || leftPanel.empty()) { return; } const { storageKey } = options; let leftPanelRatio = readPanelSplitRatio(storageKey); let isResizing = false; let startX = 0; let startWidth = 0; const updateLeftPanelWidth = (containerWidth: number): void => { const availableWidth = containerWidth - 8; const leftWidth = availableWidth * leftPanelRatio; const minWidth = containerWidth * 0.1; const maxWidth = containerWidth * 0.9; const clampedWidth = Math.max(minWidth, Math.min(maxWidth, leftWidth)); leftPanelRatio = clampedWidth / availableWidth; leftPanel.style('flex-basis', `${clampedWidth}px`); }; const reLayout = (w = window.innerWidth, h = window.innerHeight): void => { const mainFrame = d3.selectAll('.main_frame'); if (isNarrowScreen()) { mainFrame.style('height', null).style('width', null); } else { mainFrame.style('height', `${h - 53}px`).style('width', `${w}px`); updateLeftPanelWidth(w); } }; reLayout(); resizer.on('mousedown', (event: MouseEvent) => { event.preventDefault(); event.stopPropagation(); if (isNarrowScreen()) { return; } isResizing = true; startX = event.clientX; const cw = window.innerWidth; const currentFlexBasis = leftPanel.style('flex-basis'); const parsed = parseInt(currentFlexBasis, 10); const availableWidth = cw - 8; startWidth = Number.isFinite(parsed) ? parsed : availableWidth * leftPanelRatio; d3.select('body').style('cursor', 'col-resize').style('user-select', 'none'); d3.select(window) .on('mousemove.chatResizer', (ev: MouseEvent) => onMouseMove(ev)) .on('mouseup.chatResizer', () => onMouseUp()); }); const onMouseMove = (event: MouseEvent): void => { if (!isResizing) { return; } event.preventDefault(); const cw = window.innerWidth; const availableWidth = cw - 8; const deltaX = event.clientX - startX; const newWidth = Math.max( cw * 0.1, Math.min(cw * 0.9, startWidth + deltaX) ); leftPanel.style('flex-basis', `${newWidth}px`); leftPanelRatio = newWidth / availableWidth; }; const onMouseUp = (): void => { if (!isResizing) { return; } isResizing = false; writePanelSplitRatio(storageKey, leftPanelRatio); d3.select('body').style('cursor', null).style('user-select', null); d3.select(window) .on('mousemove.chatResizer', null) .on('mouseup.chatResizer', null); }; window.addEventListener('resize', () => { reLayout(window.innerWidth, window.innerHeight); }); }