File size: 3,519 Bytes
494c9e4
 
b704fe2
 
 
 
 
 
494c9e4
 
b704fe2
494c9e4
b704fe2
494c9e4
 
 
 
 
 
b704fe2
 
494c9e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b704fe2
 
494c9e4
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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);
    });
}