mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
feat: draggable button
This commit is contained in:
@@ -23,7 +23,10 @@ export const KeyStatusNode: React.FC<{ data: KeyStatusNodeData }> = ({ data }) =
|
||||
const keyWidth = totalKeys > 5 ? `calc((100% - ${(totalKeys - 1) * 0.25}rem) / ${totalKeys})` : KEY_SIZE
|
||||
|
||||
return (
|
||||
<div className="relative p-4 bg-white rounded-lg shadow-sm node-card">
|
||||
<div
|
||||
className="relative p-4 bg-white rounded-lg shadow-sm node-card nodrag"
|
||||
style={{ border: '1px solid var(--border-color)' }}
|
||||
>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-sm text-gray-900">{title}</div>
|
||||
|
||||
@@ -39,33 +39,35 @@ export const ModelCardNode: React.FC<{ data: ModelCardNodeData }> = ({ data }) =
|
||||
|
||||
return (
|
||||
<div
|
||||
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[280px] relative group nodrag"
|
||||
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[280px] relative group"
|
||||
style={{ border: '1px solid var(--border-color)' }}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<Handle type="source" position={Position.Right} />
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<Icon icon="mdi:robot" className="text-xl text-[--primary-color]" />
|
||||
<span className="text-base text-gray-900">{title}</span>
|
||||
<Icon
|
||||
icon={status === 'success' ? 'mdi:check-circle' : 'mdi:close-circle'}
|
||||
className={`text-xl ${status === 'success' ? 'text-green-500' : 'text-red-500'}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<Icon icon="mdi:robot" className="text-xl text-[--primary-color]" />
|
||||
<span className="text-base text-gray-900">{title}</span>
|
||||
<Icon
|
||||
icon={status === 'success' ? 'mdi:check-circle' : 'mdi:close-circle'}
|
||||
className={`text-xl ${status === 'success' ? 'text-green-500' : 'text-red-500'}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="flex gap-2 transition-opacity duration-200">
|
||||
<Icon
|
||||
icon="mdi:cog"
|
||||
className="text-xl text-gray-400 cursor-pointer hover:text-[--primary-color]"
|
||||
onClick={() => console.log('Settings', data.id)}
|
||||
/>
|
||||
{/* Action buttons */}
|
||||
<div className="flex gap-2 transition-opacity duration-200">
|
||||
<Icon
|
||||
icon="mdi:cog"
|
||||
className="text-xl text-gray-400 cursor-pointer hover:text-[--primary-color]"
|
||||
onClick={() => console.log('Settings', data.id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-500">{defaultModel}</div>
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-500">{defaultModel}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import React from 'react'
|
||||
|
||||
export const ServiceCardNode: React.FC<NodeProps> = () => {
|
||||
return (
|
||||
<div className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[150px] relative">
|
||||
<div
|
||||
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[150px] relative nodrag"
|
||||
style={{ border: '1px solid var(--border-color)' }}
|
||||
>
|
||||
<Handle type="source" position={Position.Right} />
|
||||
<div className="flex flex-col gap-2 items-center">
|
||||
<Icon icon="mdi:robot" className="text-3xl text-[--primary-color]" />
|
||||
|
||||
@@ -35,7 +35,7 @@ const calculateNodePositions = (models: ModelData[], startY = 50, gap = 120) =>
|
||||
|
||||
const nodeTypes: NodeTypes = {
|
||||
modelCard: ModelCardNode,
|
||||
keyStatus: KeyStatusNode,
|
||||
keyCard: KeyStatusNode,
|
||||
serviceCard: ServiceCardNode
|
||||
} as const
|
||||
|
||||
@@ -155,7 +155,7 @@ const initialEdges = [
|
||||
source: 'service',
|
||||
target: 'openai',
|
||||
label: 'apis(12)',
|
||||
style: { stroke: '#3d46f2', cursor: 'pointer' },
|
||||
style: { stroke: '#ddd', cursor: 'pointer' },
|
||||
labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' }
|
||||
},
|
||||
{
|
||||
@@ -163,7 +163,7 @@ const initialEdges = [
|
||||
source: 'service',
|
||||
target: 'anthropic',
|
||||
label: 'apis(8)',
|
||||
style: { stroke: '#3d46f2', cursor: 'pointer' },
|
||||
style: { stroke: '#ddd', cursor: 'pointer' },
|
||||
labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' }
|
||||
},
|
||||
{
|
||||
@@ -171,7 +171,7 @@ const initialEdges = [
|
||||
source: 'service',
|
||||
target: 'gemini',
|
||||
label: 'apis(5)',
|
||||
style: { stroke: '#3d46f2', cursor: 'pointer' },
|
||||
style: { stroke: '#ddd', cursor: 'pointer' },
|
||||
labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' }
|
||||
},
|
||||
{
|
||||
@@ -179,7 +179,7 @@ const initialEdges = [
|
||||
source: 'service',
|
||||
target: 'mistral',
|
||||
label: 'apis(4)',
|
||||
style: { stroke: '#3d46f2', cursor: 'pointer' },
|
||||
style: { stroke: '#ddd', cursor: 'pointer' },
|
||||
labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' }
|
||||
},
|
||||
{
|
||||
@@ -187,7 +187,7 @@ const initialEdges = [
|
||||
source: 'service',
|
||||
target: 'cohere',
|
||||
label: 'apis(6)',
|
||||
style: { stroke: '#3d46f2', cursor: 'pointer' },
|
||||
style: { stroke: '#ddd', cursor: 'pointer' },
|
||||
labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' }
|
||||
},
|
||||
{
|
||||
@@ -195,7 +195,7 @@ const initialEdges = [
|
||||
source: 'service',
|
||||
target: 'azure',
|
||||
label: 'apis(10)',
|
||||
style: { stroke: '#3d46f2', cursor: 'pointer' },
|
||||
style: { stroke: '#ddd', cursor: 'pointer' },
|
||||
labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' }
|
||||
},
|
||||
...modelData.map((model) => ({
|
||||
@@ -203,7 +203,7 @@ const initialEdges = [
|
||||
source: model.id,
|
||||
target: `${model.id}-keys`,
|
||||
animated: true,
|
||||
style: { stroke: '#3d46f2' }
|
||||
style: { stroke: '#ddd' }
|
||||
}))
|
||||
]
|
||||
|
||||
@@ -215,6 +215,8 @@ const Playground = () => {
|
||||
|
||||
const onNodeDrag = useCallback(
|
||||
(_: any, node: any) => {
|
||||
if (node.type !== 'modelCard') return
|
||||
|
||||
// Update positions of connected nodes during drag
|
||||
setNodes((nds) => {
|
||||
return nds.map((n) => {
|
||||
@@ -236,6 +238,8 @@ const Playground = () => {
|
||||
|
||||
const onNodeDragStop = useCallback(
|
||||
(_: any, node: any) => {
|
||||
if (node.type !== 'modelCard') return
|
||||
|
||||
// Reorder nodes based on vertical position
|
||||
setNodes((nds) => {
|
||||
const modelNodes = nds.filter((n) => n.type === 'modelCard')
|
||||
@@ -274,7 +278,7 @@ const Playground = () => {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="w-full h-screen bg-gray-50">
|
||||
<div className="w-full h-screen">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
@@ -286,11 +290,11 @@ const Playground = () => {
|
||||
nodeTypes={nodeTypes}
|
||||
defaultEdgeOptions={{
|
||||
type: 'step',
|
||||
style: { stroke: '#3d46f2', strokeWidth: 2 },
|
||||
style: { stroke: '#000', strokeWidth: 2 },
|
||||
animated: true
|
||||
}}
|
||||
fitView
|
||||
nodesDraggable={true}
|
||||
nodesDraggable={(node) => node.type === 'modelCard'}
|
||||
nodesConnectable={false}
|
||||
zoomOnScroll={false}
|
||||
zoomOnPinch={false}
|
||||
|
||||
Reference in New Issue
Block a user