重写ssh前端组件,通用化
This commit is contained in:
parent
743255d5c5
commit
6d2f0cb95c
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import React, { useRef, useState, useEffect } from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import type { LayoutOrientation } from './types';
|
import type { LayoutOrientation } from './types';
|
||||||
|
import styles from './index.module.less';
|
||||||
|
|
||||||
interface TerminalSplitDividerProps {
|
interface TerminalSplitDividerProps {
|
||||||
orientation: LayoutOrientation;
|
orientation: LayoutOrientation;
|
||||||
@ -56,6 +57,7 @@ export const TerminalSplitDivider: React.FC<TerminalSplitDividerProps> = ({ orie
|
|||||||
? (orientation === 'horizontal' ? container.clientWidth : container.clientHeight)
|
? (orientation === 'horizontal' ? container.clientWidth : container.clientHeight)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
console.log(`[TerminalSplitDivider] onResize, orientation: ${orientation}, delta: ${delta}, containerSize: ${containerSize}`);
|
||||||
onResize(delta, containerSize);
|
onResize(delta, containerSize);
|
||||||
lastMousePosRef.current = currentMousePos;
|
lastMousePosRef.current = currentMousePos;
|
||||||
};
|
};
|
||||||
@ -79,6 +81,7 @@ export const TerminalSplitDivider: React.FC<TerminalSplitDividerProps> = ({ orie
|
|||||||
|
|
||||||
const handleMouseDown = (e: React.MouseEvent) => {
|
const handleMouseDown = (e: React.MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
console.log(`[TerminalSplitDivider] mouseDown, orientation: ${orientation}`);
|
||||||
const initialPos = orientation === 'horizontal' ? e.clientX : e.clientY;
|
const initialPos = orientation === 'horizontal' ? e.clientX : e.clientY;
|
||||||
startPosRef.current = initialPos;
|
startPosRef.current = initialPos;
|
||||||
lastMousePosRef.current = initialPos;
|
lastMousePosRef.current = initialPos;
|
||||||
@ -88,33 +91,8 @@ export const TerminalSplitDivider: React.FC<TerminalSplitDividerProps> = ({ orie
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={dividerRef}
|
ref={dividerRef}
|
||||||
className={`
|
className={`${styles.divider} ${styles[orientation]} ${isDragging ? styles.dragging : ''}`}
|
||||||
${orientation === 'horizontal' ? 'w-[2px] cursor-col-resize' : 'h-[2px] cursor-row-resize'}
|
|
||||||
${isDragging ? 'bg-blue-500' : 'bg-gray-400 dark:bg-gray-600 hover:bg-blue-500'}
|
|
||||||
transition-all duration-150
|
|
||||||
relative
|
|
||||||
group
|
|
||||||
flex-shrink-0
|
|
||||||
`}
|
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
>
|
/>
|
||||||
{/* 中心可见线 */}
|
|
||||||
<div className={`
|
|
||||||
absolute inset-0
|
|
||||||
${orientation === 'horizontal' ? 'w-[1px] left-1/2 -translate-x-1/2' : 'h-[1px] top-1/2 -translate-y-1/2'}
|
|
||||||
${isDragging ? 'bg-blue-600' : 'bg-gray-500 dark:bg-gray-500'}
|
|
||||||
`} />
|
|
||||||
|
|
||||||
{/* 拖动热区 - 增加可拖动区域 */}
|
|
||||||
<div
|
|
||||||
className={`
|
|
||||||
absolute
|
|
||||||
${orientation === 'horizontal'
|
|
||||||
? 'w-3 -left-1.5 top-0 bottom-0'
|
|
||||||
: 'h-3 left-0 right-0 -top-1.5'
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,21 @@
|
|||||||
border-bottom: 1px solid #374151;
|
border-bottom: 1px solid #374151;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 搜索栏样式 */
|
||||||
|
.searchBar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: #f9fafb;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .searchBar {
|
||||||
|
background: #1f2937;
|
||||||
|
border-bottom: 1px solid #374151;
|
||||||
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -204,3 +219,29 @@
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 分隔线样式 */
|
||||||
|
.divider {
|
||||||
|
flex-shrink: 0;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: #9ca3af;
|
||||||
|
transition: all 150ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(59 130 246 / 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
background-color: rgb(59 130 246 / 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.horizontal {
|
||||||
|
width: 4px;
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vertical {
|
||||||
|
height: 4px;
|
||||||
|
cursor: row-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,22 @@ import { useState, useCallback } from 'react';
|
|||||||
import type { EditorGroup, TerminalTab, SplitDirection, SplitLayout, SplitNode, SplitContainer, LayoutOrientation } from './types';
|
import type { EditorGroup, TerminalTab, SplitDirection, SplitLayout, SplitNode, SplitContainer, LayoutOrientation } from './types';
|
||||||
import { TerminalInstanceManager } from './core/TerminalInstanceManager';
|
import { TerminalInstanceManager } from './core/TerminalInstanceManager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据容器尺寸和方向计算最小尺寸(像素)
|
||||||
|
* @param containerSize 容器尺寸(px)
|
||||||
|
* @param orientation 分割方向
|
||||||
|
* @returns 最小尺寸(px)
|
||||||
|
*/
|
||||||
|
const getMinSize = (containerSize: number, orientation: LayoutOrientation): number => {
|
||||||
|
if (orientation === 'horizontal') {
|
||||||
|
// 左右分屏:最小宽度520px
|
||||||
|
return 520;
|
||||||
|
} else {
|
||||||
|
// 上下分屏:根据容器高度计算,最小200px,最多占40%
|
||||||
|
return Math.max(200, Math.min(containerSize * 0.4, 300));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
interface UseSplitViewOptions {
|
interface UseSplitViewOptions {
|
||||||
initialTab: TerminalTab;
|
initialTab: TerminalTab;
|
||||||
onWindowClose?: () => void; // 最后一个Tab关闭时的回调
|
onWindowClose?: () => void; // 最后一个Tab关闭时的回调
|
||||||
@ -370,12 +386,19 @@ export const useSplitView = ({ initialTab, onWindowClose }: UseSplitViewOptions)
|
|||||||
|
|
||||||
// 调整分屏大小
|
// 调整分屏大小
|
||||||
const resizeGroups = useCallback((nodeId: string, delta: number, containerSize?: number) => {
|
const resizeGroups = useCallback((nodeId: string, delta: number, containerSize?: number) => {
|
||||||
|
console.log(`[useSplitView.resizeGroups] nodeId: ${nodeId}, delta: ${delta}, containerSize: ${containerSize}`);
|
||||||
setLayout(prev => {
|
setLayout(prev => {
|
||||||
const result = findParent(prev.root, nodeId);
|
const result = findParent(prev.root, nodeId);
|
||||||
if (!result || !result.parent) return prev;
|
if (!result || !result.parent) {
|
||||||
|
console.log(`[useSplitView.resizeGroups] findParent failed`);
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
const { parent, index } = result;
|
const { parent, index } = result;
|
||||||
if (index >= parent.children.length - 1) return prev;
|
if (index >= parent.children.length - 1) {
|
||||||
|
console.log(`[useSplitView.resizeGroups] index check failed: ${index} >= ${parent.children.length - 1}`);
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
const current = parent.children[index];
|
const current = parent.children[index];
|
||||||
const next = parent.children[index + 1];
|
const next = parent.children[index + 1];
|
||||||
@ -385,12 +408,14 @@ export const useSplitView = ({ initialTab, onWindowClose }: UseSplitViewOptions)
|
|||||||
// delta是像素值,需要转换为百分比:(delta / containerSize) * totalSize
|
// delta是像素值,需要转换为百分比:(delta / containerSize) * totalSize
|
||||||
const deltaPercent = containerSize ? (delta / containerSize) * totalSize : delta;
|
const deltaPercent = containerSize ? (delta / containerSize) * totalSize : delta;
|
||||||
|
|
||||||
// 计算最小尺寸百分比:520px / 容器尺寸 * 100
|
// 计算最小尺寸百分比:使用totalSize的20%作为最小值
|
||||||
// 如果没有容器尺寸,使用默认20%作为兜底
|
// 这样每个分屏至少占两个分屏总和的20%(例如:总100%时,最小20%)
|
||||||
const minSizePercent = containerSize ? Math.min(50, (520 / containerSize) * 100) : 20;
|
const minSizePercent = totalSize * 0.2;
|
||||||
|
|
||||||
const newCurrentSize = Math.max(minSizePercent, Math.min(totalSize - minSizePercent, current.size + deltaPercent));
|
const newCurrentSize = Math.max(minSizePercent, Math.min(totalSize - minSizePercent, current.size + deltaPercent));
|
||||||
const newNextSize = totalSize - newCurrentSize;
|
const newNextSize = totalSize - newCurrentSize;
|
||||||
|
|
||||||
|
console.log(`[useSplitView.resizeGroups] current.size: ${current.size}, deltaPercent: ${deltaPercent}, newCurrentSize: ${newCurrentSize}, newNextSize: ${newNextSize}`);
|
||||||
|
|
||||||
const newChildren = parent.children.map((child, i) => {
|
const newChildren = parent.children.map((child, i) => {
|
||||||
if (i === index) return { ...child, size: newCurrentSize };
|
if (i === index) return { ...child, size: newCurrentSize };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user