improvements
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import React, { memo, useCallback, useContext, useMemo, useRef } from 'react'
|
||||
import { autocompletion } from '@codemirror/autocomplete'
|
||||
import CodeMirror from '@uiw/react-codemirror'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
MenubarSubTrigger,
|
||||
} from '../ui/menubar'
|
||||
import { InputHandle, OutputHandle } from './NodeHandles'
|
||||
import { NodeHeaderTitle } from './NodeHeaderTitle'
|
||||
import { NodeMenubar } from './NodeMenubar'
|
||||
|
||||
type Props = {
|
||||
@@ -30,12 +31,13 @@ type Props = {
|
||||
height?: number
|
||||
}
|
||||
|
||||
const DEFAULT_PLANTUML = '@startuml\n\n@enduml\n'
|
||||
|
||||
export const ConfigNode = memo(function ConfigNode({ id, data, width, height }: Props) {
|
||||
const [value, setValue] = useState<string>(data?.plantuml ?? '@startuml\n\n@enduml\n')
|
||||
const plantumlValue = data?.plantuml ?? DEFAULT_PLANTUML
|
||||
const { theme } = useTheme()
|
||||
const ctx = useContext(FlowContext)
|
||||
const setNodes = ctx?.setNodes
|
||||
const storedPlantuml = ctx?.nodes?.find((n: any) => n.id === id)?.data?.plantuml ?? ''
|
||||
|
||||
const editorRef = useRef<unknown>(null)
|
||||
|
||||
@@ -46,16 +48,8 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
const connectedFunctionNodes = (ctx?.nodes ?? []).filter((n: any) => incomingIds.includes(n.id) && n.type === 'function')
|
||||
const hasDependencies = connectedConfigNodes.length > 0 || connectedVariableNodes.length > 0 || connectedFunctionNodes.length > 0
|
||||
|
||||
useEffect(() => {
|
||||
if (setNodes) {
|
||||
setNodes((nds: any[]) => nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, plantuml: value } } : n)))
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const onChange = useCallback(
|
||||
(val: string) => {
|
||||
setValue(val)
|
||||
if (setNodes) {
|
||||
setNodes((nds: any[]) => nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, plantuml: val } } : n)))
|
||||
}
|
||||
@@ -85,12 +79,12 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
return
|
||||
}
|
||||
if (mode === 'prepend') {
|
||||
onChange(insertText + value)
|
||||
onChange(insertText + plantumlValue)
|
||||
} else {
|
||||
onChange(value + insertText)
|
||||
onChange(plantumlValue + insertText)
|
||||
}
|
||||
},
|
||||
[onChange, value]
|
||||
[onChange, plantumlValue]
|
||||
)
|
||||
|
||||
const insertExtendsFromNode = useCallback(
|
||||
@@ -146,6 +140,21 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
)
|
||||
const [editorHeight, editorContainerRef] = useResizeHeight(180)
|
||||
|
||||
const nunjucksTagSnippets = useMemo(
|
||||
() => [
|
||||
{ label: 'Variable {{ }}', snippet: '{{ }}' },
|
||||
{ label: 'if / endif', snippet: '{% if %}\n \n{% endif %}' },
|
||||
{ label: 'for / endfor', snippet: '{% for in %}\n \n{% endfor %}' },
|
||||
{ label: 'set', snippet: '{% set = %}' },
|
||||
{ label: 'block / endblock', snippet: '{% block %}\n \n{% endblock %}' },
|
||||
{ label: 'extends', snippet: '{% extends "" %}' },
|
||||
{ label: 'include', snippet: '{% include "" %}' },
|
||||
{ label: 'import', snippet: '{% import "" as %}' },
|
||||
{ label: 'raw / endraw', snippet: '{% raw %}\n \n{% endraw %}' },
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const dimensions =
|
||||
width != null && height != null && width > 0 && height > 0
|
||||
? { width, height }
|
||||
@@ -153,7 +162,7 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
|
||||
return (
|
||||
<BaseNode className="min-w-80 min-h-[280px]" dimensions={dimensions} resizable nodeId={id} handles={<><InputHandle id="ain" /><OutputHandle id="out" /></>}>
|
||||
<BaseNodeHeaderRow icon={<ScrollText className="size-4" />} title={`${id}.puml`} />
|
||||
<BaseNodeHeaderRow icon={<ScrollText className="size-4" />} title={<NodeHeaderTitle nodeId={id} displayTitle={`${id}.puml`} />} />
|
||||
|
||||
<BaseNodeContent>
|
||||
<div className="shrink-0 w-full">
|
||||
@@ -223,6 +232,19 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
</>
|
||||
) : undefined
|
||||
}
|
||||
insertTagsContent={
|
||||
<>
|
||||
{nunjucksTagSnippets.map(({ label, snippet }) => (
|
||||
<MenubarItem
|
||||
key={label}
|
||||
className="text-xs"
|
||||
onClick={() => insertAt(snippet, 'cursor')}
|
||||
>
|
||||
{label}
|
||||
</MenubarItem>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -230,7 +252,7 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
<CodeMirror
|
||||
// @ts-expect-error ref is { view, state, editor }; package ref type not in our node_modules
|
||||
ref={editorRef}
|
||||
value={value}
|
||||
value={plantumlValue}
|
||||
height={`${editorHeight}px`}
|
||||
theme={theme}
|
||||
extensions={extensions}
|
||||
@@ -242,7 +264,7 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
</BaseNodeContent>
|
||||
|
||||
<BaseNodeFooter>
|
||||
<BaseNodeFooterText>{storedPlantuml ? `${storedPlantuml.length} chars` : 'none'}</BaseNodeFooterText>
|
||||
<BaseNodeFooterText>{plantumlValue ? `${plantumlValue.length} chars` : 'none'}</BaseNodeFooterText>
|
||||
</BaseNodeFooter>
|
||||
</BaseNode>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import React, { memo, useCallback, useContext, useMemo } from 'react'
|
||||
import CodeMirror from '@uiw/react-codemirror'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
BaseNodeHeaderRow,
|
||||
} from './BaseNode'
|
||||
import { InputHandle, OutputHandle } from './NodeHandles'
|
||||
import { NodeHeaderTitle } from './NodeHeaderTitle'
|
||||
import { NodeMenubar } from './NodeMenubar'
|
||||
import { Code2 } from 'lucide-react'
|
||||
|
||||
@@ -22,28 +23,14 @@ type Props = {
|
||||
height?: number
|
||||
}
|
||||
|
||||
const DEFAULT_BODY = `// args[0], args[1], ... are the variable values (in order)
|
||||
return args[0];
|
||||
`
|
||||
|
||||
export const FunctionNode = memo(function FunctionNode({ id, data, width, height }: Props) {
|
||||
const [value, setValue] = useState<string>(data?.body ?? DEFAULT_BODY)
|
||||
const bodyValue = data?.body ?? ''
|
||||
const { theme } = useTheme()
|
||||
const ctx = useContext(FlowContext)
|
||||
const setNodes = ctx?.setNodes
|
||||
|
||||
useEffect(() => {
|
||||
if (setNodes) {
|
||||
setNodes((nds: any[]) =>
|
||||
nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, body: value } } : n))
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const onChange = useCallback(
|
||||
(val: string) => {
|
||||
setValue(val)
|
||||
if (setNodes) {
|
||||
setNodes((nds: any[]) =>
|
||||
nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, body: val } } : n))
|
||||
@@ -53,8 +40,6 @@ export const FunctionNode = memo(function FunctionNode({ id, data, width, height
|
||||
[id, setNodes]
|
||||
)
|
||||
|
||||
const storedBody = ctx?.nodes?.find((n: any) => n.id === id)?.data?.body ?? ''
|
||||
|
||||
const extensions = useMemo(() => [javascript()], [])
|
||||
const [editorHeight, editorContainerRef] = useResizeHeight(120)
|
||||
const dimensions =
|
||||
@@ -64,7 +49,7 @@ export const FunctionNode = memo(function FunctionNode({ id, data, width, height
|
||||
|
||||
return (
|
||||
<BaseNode className="min-w-72 min-h-[260px]" dimensions={dimensions} resizable nodeId={id} handles={<><InputHandle id="in" /><OutputHandle id="out" /></>}>
|
||||
<BaseNodeHeaderRow icon={<Code2 className="size-4" />} title={id} />
|
||||
<BaseNodeHeaderRow icon={<Code2 className="size-4" />} title={<NodeHeaderTitle nodeId={id} displayTitle={id} />} />
|
||||
|
||||
<BaseNodeContent>
|
||||
<div className="shrink-0 w-full mb-1">
|
||||
@@ -75,7 +60,7 @@ export const FunctionNode = memo(function FunctionNode({ id, data, width, height
|
||||
</p>
|
||||
<div ref={editorContainerRef} className="min-h-0 flex-1 w-full nodrag nopan overflow-hidden rounded border border-input">
|
||||
<CodeMirror
|
||||
value={value}
|
||||
value={bodyValue}
|
||||
height={`${editorHeight}px`}
|
||||
theme={theme}
|
||||
extensions={extensions}
|
||||
@@ -87,7 +72,7 @@ export const FunctionNode = memo(function FunctionNode({ id, data, width, height
|
||||
</BaseNodeContent>
|
||||
|
||||
<BaseNodeFooter>
|
||||
<BaseNodeFooterText>Body: {storedBody ? `${storedBody.length} chars` : 'none'}</BaseNodeFooterText>
|
||||
<BaseNodeFooterText>Body: {bodyValue ? `${bodyValue.length} chars` : 'none'}</BaseNodeFooterText>
|
||||
</BaseNodeFooter>
|
||||
</BaseNode>
|
||||
)
|
||||
|
||||
82
src/components/graph/NodeHeaderTitle.tsx
Normal file
82
src/components/graph/NodeHeaderTitle.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
import { replaceNodeIdInGraph } from '../../lib/flowUtils'
|
||||
|
||||
type Props = {
|
||||
nodeId: string
|
||||
displayTitle: string
|
||||
}
|
||||
|
||||
export function NodeHeaderTitle({ nodeId, displayTitle }: Props) {
|
||||
const ctx = useContext(FlowContext)
|
||||
const nodes = ctx?.nodes ?? []
|
||||
const setNodes = ctx?.setNodes
|
||||
const edges = ctx?.edges ?? []
|
||||
const setEdges = ctx?.setEdges
|
||||
const renamingNodeId = ctx?.renamingNodeId ?? null
|
||||
const setRenamingNodeId = ctx?.setRenamingNodeId
|
||||
|
||||
const [inputValue, setInputValue] = useState(nodeId)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const isRenaming = renamingNodeId === nodeId
|
||||
|
||||
useEffect(() => {
|
||||
if (isRenaming) {
|
||||
setInputValue(nodeId)
|
||||
inputRef.current?.focus()
|
||||
inputRef.current?.select()
|
||||
}
|
||||
}, [isRenaming, nodeId])
|
||||
|
||||
const applyRename = useCallback(() => {
|
||||
if (!setNodes || !setEdges || !setRenamingNodeId) return
|
||||
const newId = inputValue.trim()
|
||||
if (!newId || newId === nodeId) {
|
||||
setRenamingNodeId(null)
|
||||
return
|
||||
}
|
||||
const existingIds = nodes.map((n: any) => n.id)
|
||||
if (existingIds.includes(newId)) {
|
||||
return
|
||||
}
|
||||
const { nodes: nextNodes, edges: nextEdges } = replaceNodeIdInGraph(nodes, edges, nodeId, newId)
|
||||
setNodes(nextNodes)
|
||||
setEdges(nextEdges)
|
||||
setRenamingNodeId(null)
|
||||
}, [nodeId, inputValue, nodes, edges, setNodes, setEdges, setRenamingNodeId])
|
||||
|
||||
const cancelRename = useCallback(() => {
|
||||
setRenamingNodeId?.(null)
|
||||
}, [setRenamingNodeId])
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
applyRename()
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault()
|
||||
cancelRename()
|
||||
}
|
||||
},
|
||||
[applyRename, cancelRename]
|
||||
)
|
||||
|
||||
if (!isRenaming) {
|
||||
return <>{displayTitle}</>
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={cancelRename}
|
||||
className="nodrag nopan flex-1 min-w-0 rounded border border-input bg-background px-1.5 py-0 text-sm font-semibold outline-none focus:ring-1 focus:ring-ring"
|
||||
data-slot="base-node-title"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useCallback, useContext } from 'react'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
import { DEFAULT_NODE_STYLE, genId, getDefaultDataForType } from '../../lib/flowUtils'
|
||||
import { DEFAULT_NODE_STYLE, getNextNodeId, getResetDataForType } from '../../lib/flowUtils'
|
||||
import {
|
||||
Menubar,
|
||||
MenubarContent,
|
||||
MenubarItem,
|
||||
MenubarMenu,
|
||||
MenubarSeparator,
|
||||
MenubarSub,
|
||||
MenubarSubContent,
|
||||
MenubarSubTrigger,
|
||||
@@ -19,11 +20,13 @@ type NodeType = 'config' | 'render' | 'variable' | 'function'
|
||||
type Props = {
|
||||
nodeId: string
|
||||
nodeType: NodeType
|
||||
/** Content for Edit → Inputs (config and function nodes only) */
|
||||
/** Content for Insert → Inputs (config and function nodes only) */
|
||||
editInputsContent?: React.ReactNode
|
||||
/** Content for Insert → Tags (e.g. Nunjucks tag snippets, config nodes) */
|
||||
insertTagsContent?: React.ReactNode
|
||||
}
|
||||
|
||||
export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
export function NodeMenubar({ nodeId, nodeType, editInputsContent, insertTagsContent }: Props) {
|
||||
const ctx = useContext(FlowContext)
|
||||
const nodes = ctx?.nodes ?? []
|
||||
const setNodes = ctx?.setNodes
|
||||
@@ -36,17 +39,19 @@ export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
|
||||
const onDuplicate = useCallback(() => {
|
||||
if (!setNodes || !node) return
|
||||
const newId = genId()
|
||||
const pos = node.position ?? { x: 0, y: 0 }
|
||||
const newNode = {
|
||||
id: newId,
|
||||
type: node.type,
|
||||
position: { x: pos.x + DUPLICATE_OFFSET.x, y: pos.y + DUPLICATE_OFFSET.y },
|
||||
data: typeof node.data === 'object' && node.data !== null ? { ...node.data } : node.data,
|
||||
style: DEFAULT_NODE_STYLE[nodeType] ?? DEFAULT_NODE_STYLE.config,
|
||||
}
|
||||
if (newNode.data?.title && nodeType === 'config') newNode.data.title = `config-${newId}`
|
||||
setNodes((nds: any[]) => nds.concat(newNode))
|
||||
setNodes((nds: any[]) => {
|
||||
const newId = getNextNodeId(nodeType, nds.map((n: any) => n.id))
|
||||
const newNode = {
|
||||
id: newId,
|
||||
type: node.type,
|
||||
position: { x: pos.x + DUPLICATE_OFFSET.x, y: pos.y + DUPLICATE_OFFSET.y },
|
||||
data: typeof node.data === 'object' && node.data !== null ? { ...node.data } : node.data,
|
||||
style: DEFAULT_NODE_STYLE[nodeType] ?? DEFAULT_NODE_STYLE.config,
|
||||
}
|
||||
if (newNode.data?.title && nodeType === 'config') newNode.data.title = `config-${newId}`
|
||||
return nds.concat(newNode)
|
||||
})
|
||||
}, [node, nodeType, setNodes])
|
||||
|
||||
const onCopy = useCallback(() => {
|
||||
@@ -57,9 +62,9 @@ export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
|
||||
const onReset = useCallback(() => {
|
||||
if (!setNodes) return
|
||||
const defaultData = getDefaultDataForType(nodeType, nodeId)
|
||||
const resetData = getResetDataForType(nodeType, nodeId)
|
||||
setNodes((nds: any[]) =>
|
||||
nds.map((n) => (n.id === nodeId ? { ...n, data: defaultData } : n))
|
||||
nds.map((n) => (n.id === nodeId ? { ...n, data: resetData } : n))
|
||||
)
|
||||
}, [nodeId, nodeType, setNodes])
|
||||
|
||||
@@ -69,11 +74,15 @@ export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
setEdges((eds: any[]) => eds.filter((e: any) => e.source !== nodeId && e.target !== nodeId))
|
||||
}, [nodeId, setNodes, setEdges])
|
||||
|
||||
const onRename = useCallback(() => {
|
||||
ctx?.setRenamingNodeId?.(nodeId)
|
||||
}, [nodeId, ctx])
|
||||
|
||||
return (
|
||||
<Menubar className="h-auto min-h-0 flex items-center bg-none p-1 border-none shadow-none">
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger className="px-1.5 py-0 text-xs">
|
||||
File
|
||||
Node
|
||||
</MenubarTrigger>
|
||||
<MenubarContent className="min-w-[10rem]">
|
||||
<MenubarItem className="text-xs" onClick={onDuplicate}>
|
||||
@@ -82,9 +91,13 @@ export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
<MenubarItem className="text-xs" onClick={onCopy}>
|
||||
Copy
|
||||
</MenubarItem>
|
||||
<MenubarItem className="text-xs" onClick={onRename}>
|
||||
Rename
|
||||
</MenubarItem>
|
||||
<MenubarItem className="text-xs" onClick={onReset}>
|
||||
Reset
|
||||
</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem className="text-xs text-destructive focus:text-destructive" onClick={onDelete}>
|
||||
Delete
|
||||
</MenubarItem>
|
||||
@@ -93,7 +106,7 @@ export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
{hasEdit && (
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger className="px-1.5 py-0 text-xs">
|
||||
Edit
|
||||
Insert
|
||||
</MenubarTrigger>
|
||||
<MenubarContent className="min-w-[10rem]">
|
||||
<MenubarSub>
|
||||
@@ -104,6 +117,16 @@ export function NodeMenubar({ nodeId, nodeType, editInputsContent }: Props) {
|
||||
{editInputsContent}
|
||||
</MenubarSubContent>
|
||||
</MenubarSub>
|
||||
{insertTagsContent != null && (
|
||||
<MenubarSub>
|
||||
<MenubarSubTrigger className="text-xs">
|
||||
Tags
|
||||
</MenubarSubTrigger>
|
||||
<MenubarSubContent className="min-w-[12rem]">
|
||||
{insertTagsContent}
|
||||
</MenubarSubContent>
|
||||
</MenubarSub>
|
||||
)}
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
BaseNodeFooterText,
|
||||
BaseNodeHeaderRow,
|
||||
} from './BaseNode'
|
||||
import { DEFAULT_NODE_STYLE, getDefaultDataForType, getNextNodeId } from '../../lib/flowUtils'
|
||||
import { NodeHeaderTitle } from './NodeHeaderTitle'
|
||||
import { NodeMenubar } from './NodeMenubar'
|
||||
import { Sparkles } from 'lucide-react'
|
||||
import { InputHandle, OutputHandle } from './NodeHandles'
|
||||
@@ -23,8 +25,6 @@ type Props = {
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_NODE_STYLE = { width: 320, height: 320 }
|
||||
|
||||
const DARK_SKINPARAMS = `
|
||||
skinparam backgroundColor #1e1e1e
|
||||
skinparam defaultFontColor #e0e0e0
|
||||
@@ -357,7 +357,7 @@ export const RenderingNode = memo(function RenderingNode({ id, width, height }:
|
||||
|
||||
return (
|
||||
<BaseNode className="min-w-96 min-h-[320px]" dimensions={dimensions} resizable nodeId={id} handles={<><InputHandle id="ain" /><OutputHandle id="out" /></>}>
|
||||
<BaseNodeHeaderRow icon={<Sparkles className="size-4" />} title={id} />
|
||||
<BaseNodeHeaderRow icon={<Sparkles className="size-4" />} title={<NodeHeaderTitle nodeId={id} displayTitle={id} />} />
|
||||
|
||||
<BaseNodeContent>
|
||||
<div className="shrink-0 w-full">
|
||||
@@ -378,15 +378,13 @@ export const RenderingNode = memo(function RenderingNode({ id, width, height }:
|
||||
className="inline-flex items-center rounded bg-primary px-3 py-1 text-xs text-primary-foreground"
|
||||
onClick={() => {
|
||||
if (!setNodes || !setEdges) return
|
||||
const genId = () => `node_${Math.random().toString(36).slice(2, 9)}`
|
||||
const nid = genId()
|
||||
const nid = getNextNodeId('config', nodes.map((n: any) => n.id))
|
||||
const thisNode = nodes.find((n: any) => n.id === id)
|
||||
const pos = thisNode?.position ?? { x: 0, y: 0 }
|
||||
const newPos = { x: pos.x - 220, y: pos.y }
|
||||
const newNode = { id: nid, type: 'config', position: newPos, data: { plantuml: '@startuml\n\n@enduml\n', title: `config-${nid}` }, style: DEFAULT_CONFIG_NODE_STYLE }
|
||||
const newNode = { id: nid, type: 'config', position: newPos, data: getDefaultDataForType('config', nid), style: DEFAULT_NODE_STYLE.config }
|
||||
setNodes((nds: any[]) => nds.concat(newNode))
|
||||
const edgeId = `e-${nid}-${id}`
|
||||
setEdges((eds: any[]) => eds.concat({ id: edgeId, source: nid, target: id }))
|
||||
setEdges((eds: any[]) => eds.concat({ id: `e-${nid}-${id}`, source: nid, target: id }))
|
||||
}}
|
||||
>
|
||||
Create Config
|
||||
|
||||
@@ -11,6 +11,7 @@ import { NodeMenubar } from './NodeMenubar'
|
||||
import { Input } from '../ui/input'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
|
||||
import { Switch } from '../ui/switch'
|
||||
import { NodeHeaderTitle } from './NodeHeaderTitle'
|
||||
import { OutputHandle } from './NodeHandles'
|
||||
import { Variable } from 'lucide-react'
|
||||
|
||||
@@ -91,7 +92,7 @@ export const VariableNode = memo(function VariableNode({ id, data }: Props) {
|
||||
|
||||
return (
|
||||
<BaseNode className="min-w-56 min-h-[180px]" handles={<OutputHandle id="out" />}>
|
||||
<BaseNodeHeaderRow icon={<Variable className="size-4" />} title={id} />
|
||||
<BaseNodeHeaderRow icon={<Variable className="size-4" />} title={<NodeHeaderTitle nodeId={id} displayTitle={id} />} />
|
||||
|
||||
<BaseNodeContent className="gap-2 p-3">
|
||||
<div className="shrink-0 w-full">
|
||||
|
||||
Reference in New Issue
Block a user