basic function
This commit is contained in:
111
src/components/graph/FunctionNode.tsx
Normal file
111
src/components/graph/FunctionNode.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import React, { memo, useCallback, useContext, useEffect, useRef, useState } from 'react'
|
||||
import Editor, { loader } from '@monaco-editor/react'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
import {
|
||||
BaseNode,
|
||||
BaseNodeContent,
|
||||
BaseNodeFooter,
|
||||
BaseNodeHeader,
|
||||
BaseNodeHeaderTitle,
|
||||
} from './BaseNode'
|
||||
import { InputHandle, OutputHandle } from './NodeHandles'
|
||||
import { Code2 } from 'lucide-react'
|
||||
|
||||
loader.config({ paths: { vs: 'https://unpkg.com/monaco-editor@latest/min/vs' } })
|
||||
|
||||
type Props = {
|
||||
id: string
|
||||
data: { body?: string }
|
||||
}
|
||||
|
||||
const DEFAULT_BODY = `// args[0], args[1], ... are the variable values (in order)
|
||||
return args[0];
|
||||
`
|
||||
|
||||
export const FunctionNode = memo(function FunctionNode({ id, data }: Props) {
|
||||
const [value, setValue] = useState<string>(data?.body ?? DEFAULT_BODY)
|
||||
const ctx = useContext(FlowContext)
|
||||
const setNodes = ctx?.setNodes
|
||||
const editorRef = useRef<any>(null)
|
||||
const monacoRef = useRef<any>(null)
|
||||
|
||||
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) => {
|
||||
const v = val ?? ''
|
||||
setValue(v)
|
||||
if (setNodes) {
|
||||
setNodes((nds: any[]) =>
|
||||
nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, body: v } } : n))
|
||||
)
|
||||
}
|
||||
},
|
||||
[id, setNodes]
|
||||
)
|
||||
|
||||
const handleEditorMount = useCallback((editor: any, monaco: any) => {
|
||||
editorRef.current = editor
|
||||
monacoRef.current = monaco
|
||||
try {
|
||||
editor.onKeyDown((e: any) => e?.browserEvent?.stopPropagation())
|
||||
editor.onMouseDown((e: any) => e?.event?.stopPropagation())
|
||||
} catch {}
|
||||
}, [])
|
||||
|
||||
const storedBody = ctx?.nodes?.find((n: any) => n.id === id)?.data?.body ?? ''
|
||||
|
||||
return (
|
||||
<BaseNode className="w-72">
|
||||
<BaseNodeHeader className="border-b">
|
||||
<Code2 className="size-4" />
|
||||
<BaseNodeHeaderTitle>{id}</BaseNodeHeaderTitle>
|
||||
</BaseNodeHeader>
|
||||
|
||||
<BaseNodeContent>
|
||||
<p className="text-[10px] text-muted-foreground mb-1">
|
||||
Call in config: <code className="rounded bg-muted px-0.5">${{id}(var1, var2)}</code>
|
||||
</p>
|
||||
<div style={{ height: 120 }} className="w-full nodrag nopan rounded border border-input overflow-hidden">
|
||||
<Editor
|
||||
height="100%"
|
||||
defaultLanguage="javascript"
|
||||
value={value}
|
||||
theme="vs-light"
|
||||
onMount={handleEditorMount}
|
||||
onChange={onChange}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
fontSize: 11,
|
||||
lineHeight: 16,
|
||||
lineNumbers: 'on',
|
||||
lineDecorationsWidth: 2,
|
||||
scrollBeyondLastLine: false,
|
||||
padding: { top: 4, bottom: 4 },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</BaseNodeContent>
|
||||
|
||||
<BaseNodeFooter>
|
||||
<div className="w-full text-xs text-muted-foreground">
|
||||
Body: {storedBody ? `${storedBody.length} chars` : 'none'}
|
||||
</div>
|
||||
</BaseNodeFooter>
|
||||
|
||||
<InputHandle id="in" />
|
||||
<OutputHandle id="out" />
|
||||
</BaseNode>
|
||||
)
|
||||
})
|
||||
|
||||
FunctionNode.displayName = 'FunctionNode'
|
||||
|
||||
export default FunctionNode
|
||||
Reference in New Issue
Block a user