diff --git a/src/App.tsx b/src/App.tsx index 6132625..bb9c9dd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -27,42 +27,20 @@ import './App.css'; const initialNodes: AppNode[] = []; const initialEdges: AppEdge[] = []; const initialSettings : Settings = { - v4SubNetPrefix: [172, 29], listenPort: 38894, + mtu: 1420, }; -export function generateEdgeId() : string { - return `e-${crypto.randomUUID()}`; -} - -export function generateNodeId() : string { - return `n-${crypto.randomUUID()}`; -} - const nodeTypes : NodeTypes = { custom: CustomNode, }; -function getFirstAvailableId(ids: number[]) : number { - const idSet = new Set(ids); - let id = 1; - while (idSet.has(id)) { - id++; - } - return id; -} - -function generateNodeData(nodes : NodeData[]) : NodeData | null { - const hostId = getFirstAvailableId(nodes.filter(n => n.groupId == 0).map(n => n.hostId)) - if(hostId > 255) return null - +function generateNodeData(count: number) : NodeData | null { const privateKey = generateWireGuardPrivateKey(); - const node : NodeData = { - label: `Node-${hostId}`, - privateKey: privateKey, - groupId: 0, - hostId: hostId, + id: `n-${crypto.randomUUID()}`, + label: `Node-${count + 1}`, + privateKey: privateKey } return node } @@ -87,7 +65,7 @@ function FlowContent(): ReactNode { (params) => { const newEdge : AppEdge = { ...params, - id: `e-${Date.now()}`, + id: `e-${crypto.randomUUID()}`, animated: !enableTwoWay, markerEnd: enableTwoWay ? undefined : { type: MarkerType.ArrowClosed }, data : { @@ -118,10 +96,10 @@ function FlowContent(): ReactNode { [edges]); const handleAddNode = (): void => { - const result = generateNodeData(nodes.map(n => n.data)); + const result = generateNodeData(nodes.length); if(result == null) return; const newNode: AppNode = { - id: generateNodeId(), + id: result.id, position: { x: 0, y: 0 }, data: result, type: 'custom', diff --git a/src/components/Folder.css b/src/components/Folder.css new file mode 100644 index 0000000..2c3f79c --- /dev/null +++ b/src/components/Folder.css @@ -0,0 +1,66 @@ +.folder-item { + border: 1px solid #e5e7eb; + border-radius: 8px; + margin-bottom: 8px; + background-color: #ffffff; + overflow: hidden; + transition: box-shadow 0.2s ease; +} + +/* 悬停微阴影 */ +.folder-item:hover { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); +} + +.folder-header { + padding: 12px 16px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + background-color: #f9fafb; + font-weight: 500; + color: #374151; + + /* 核心修改:禁止选中标题文字 */ + user-select: none; + -webkit-user-select: none; +} + +.folder-header:hover { + background-color: #f3f4f6; +} + +/* 箭头旋转动画 */ +.arrow { + font-size: 12px; + transition: transform 0.3s ease; + color: #9ca3af; +} + +.show-arrow { + transform: rotate(180deg); +} + +/* 核心动画逻辑 */ +.folder-content { + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows 0.3s cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; +} + +.folder-content.show { + grid-template-rows: 1fr; +} + +.content-inner { + min-height: 0; + padding: 0 16px; /* 默认隐藏时 padding 也是 0 */ + transition: padding 0.3s; +} + +.show .content-inner { + padding: 12px 16px; /* 展开后增加内边距 */ + border-top: 1px solid #f3f4f6; +} \ No newline at end of file diff --git a/src/components/Folder.tsx b/src/components/Folder.tsx new file mode 100644 index 0000000..f5178fc --- /dev/null +++ b/src/components/Folder.tsx @@ -0,0 +1,27 @@ +import { useState, ReactNode } from 'react'; +import './Folder.css'; + +interface FolderProps { + title: string; + children?: ReactNode; +} + +export default function Folder({ title, children }: FolderProps): ReactNode { + const [isOpen, setIsOpen] = useState(false); + + return ( +
• {error}
))} @@ -81,95 +89,100 @@ export default function NodeEditor({