fix: nested split pane flex squeezing and add divider drag resize
This commit is contained in:
@@ -14,8 +14,11 @@
|
||||
|
||||
.split-container {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
@@ -29,14 +32,14 @@
|
||||
}
|
||||
|
||||
.split-pane {
|
||||
flex: 1;
|
||||
flex: 1 1 0%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid transparent;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
min-width: 120px;
|
||||
min-height: 80px;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.split-pane.focused {
|
||||
@@ -49,6 +52,7 @@
|
||||
height: 100%;
|
||||
padding: 6px 10px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.xterm {
|
||||
@@ -65,19 +69,24 @@
|
||||
.split-divider {
|
||||
background: var(--border-color);
|
||||
z-index: 10;
|
||||
transition: background 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
transition: background 0.15s ease, box-shadow 0.15s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.split-container.horizontal > .split-divider {
|
||||
width: 2px;
|
||||
width: 4px;
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.split-container.vertical > .split-divider {
|
||||
height: 2px;
|
||||
height: 4px;
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
.split-divider:hover {
|
||||
.split-divider:hover,
|
||||
.split-divider.dragging {
|
||||
background: var(--accent-color);
|
||||
box-shadow: 0 0 10px var(--accent-glow);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export class TabSplitTree {
|
||||
this.onEmpty = options.onEmpty || (() => {});
|
||||
this.onTitleChange = options.onTitleChange || (() => {});
|
||||
|
||||
this.terminals = new Map(); // id -> TerminalView
|
||||
this.terminals = new Map(); // id -> { termView, paneElement, parentElement }
|
||||
this.activeTerminal = null;
|
||||
|
||||
this.rootElement = document.createElement('div');
|
||||
@@ -62,10 +62,14 @@ export class TabSplitTree {
|
||||
const currentPane = currentInfo.paneElement;
|
||||
const parent = currentPane.parentElement;
|
||||
|
||||
// 创建一个新的 split-container 容器替换原有 pane,实现嵌套分屏
|
||||
// 创建一个新的 split-container 容器替换原有 pane,实现嵌套等比分屏
|
||||
const splitWrapper = document.createElement('div');
|
||||
splitWrapper.className = `split-container ${direction}`;
|
||||
|
||||
// 继承原窗格在祖父容器中的 flex 权重
|
||||
splitWrapper.style.flex = currentPane.style.flex || '1 1 0%';
|
||||
currentPane.style.flex = '1 1 0%';
|
||||
|
||||
parent.replaceChild(splitWrapper, currentPane);
|
||||
splitWrapper.appendChild(currentPane);
|
||||
|
||||
@@ -76,6 +80,11 @@ export class TabSplitTree {
|
||||
|
||||
// 添加新窗格
|
||||
const newTerm = this.addPane(splitWrapper, direction);
|
||||
const newPane = this.terminals.get(newTerm.id).paneElement;
|
||||
newPane.style.flex = '1 1 0%';
|
||||
|
||||
// 为分割线绑定拖拽调节事件
|
||||
this.setupDividerDrag(divider, currentPane, newPane, direction);
|
||||
|
||||
// 刷新自适应尺寸
|
||||
setTimeout(() => {
|
||||
@@ -84,6 +93,43 @@ export class TabSplitTree {
|
||||
}, 50);
|
||||
}
|
||||
|
||||
setupDividerDrag(divider, firstElement, secondElement, direction) {
|
||||
const isHorizontal = direction === 'horizontal';
|
||||
|
||||
const onMouseDown = (e) => {
|
||||
e.preventDefault();
|
||||
divider.classList.add('dragging');
|
||||
const startPos = isHorizontal ? e.clientX : e.clientY;
|
||||
const firstSize = isHorizontal ? firstElement.offsetWidth : firstElement.offsetHeight;
|
||||
const secondSize = isHorizontal ? secondElement.offsetWidth : secondElement.offsetHeight;
|
||||
|
||||
const onMouseMove = (moveEvent) => {
|
||||
const currentPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
|
||||
const delta = currentPos - startPos;
|
||||
const newFirst = firstSize + delta;
|
||||
const newSecond = secondSize - delta;
|
||||
|
||||
if (newFirst > 50 && newSecond > 50) {
|
||||
firstElement.style.flex = `${newFirst} ${newFirst} 0%`;
|
||||
secondElement.style.flex = `${newSecond} ${newSecond} 0%`;
|
||||
this.fitAll();
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
divider.classList.remove('dragging');
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
window.removeEventListener('mouseup', onMouseUp);
|
||||
this.fitAll();
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
divider.addEventListener('mousedown', onMouseDown);
|
||||
}
|
||||
|
||||
setActiveTerminal(termView) {
|
||||
this.activeTerminal = termView;
|
||||
|
||||
@@ -110,8 +156,30 @@ export class TabSplitTree {
|
||||
const parent = pane.parentElement;
|
||||
|
||||
this.terminals.delete(termId);
|
||||
|
||||
// 找到相邻的分割线并移除
|
||||
const prev = pane.previousElementSibling;
|
||||
const next = pane.nextElementSibling;
|
||||
if (prev && prev.classList.contains('split-divider')) {
|
||||
prev.remove();
|
||||
} else if (next && next.classList.contains('split-divider')) {
|
||||
next.remove();
|
||||
}
|
||||
|
||||
pane.remove();
|
||||
|
||||
// 如果非根容器中仅剩 1 个有效窗格,消除冗余包裹层将其提升至上一层
|
||||
if (parent !== this.rootElement && parent.parentElement) {
|
||||
const remainingChildren = Array.from(parent.children).filter(
|
||||
(el) => !el.classList.contains('split-divider')
|
||||
);
|
||||
if (remainingChildren.length === 1) {
|
||||
const singleChild = remainingChildren[0];
|
||||
singleChild.style.flex = parent.style.flex || '1 1 0%';
|
||||
parent.parentElement.replaceChild(singleChild, parent);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.terminals.size === 0) {
|
||||
this.onEmpty();
|
||||
return;
|
||||
@@ -121,7 +189,7 @@ export class TabSplitTree {
|
||||
const nextTerm = this.terminals.values().next().value.termView;
|
||||
this.setActiveTerminal(nextTerm);
|
||||
nextTerm.focus();
|
||||
this.fitAll();
|
||||
setTimeout(() => this.fitAll(), 40);
|
||||
}
|
||||
|
||||
fitAll() {
|
||||
|
||||
Reference in New Issue
Block a user