| import * as d3 from 'd3'; |
| import { isNarrowScreen } from '../utils/responsive'; |
| import { readPanelSplitRatio, writePanelSplitRatio } from '../utils/panelSplitStorage'; |
|
|
| export type ChatPanelLayoutOptions = { |
| |
| storageKey: string; |
| }; |
|
|
| |
| |
| |
| 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); |
| }); |
| } |
|
|