99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
'use client'
|
|
|
|
import { useCallback, ChangeEvent } from 'react'
|
|
import { Handle, Position, NodeProps, useReactFlow } from 'reactflow'
|
|
|
|
type ChoiceOption = {
|
|
id: string
|
|
label: string
|
|
}
|
|
|
|
type ChoiceNodeData = {
|
|
prompt: string
|
|
options: ChoiceOption[]
|
|
}
|
|
|
|
export default function ChoiceNode({ id, data }: NodeProps<ChoiceNodeData>) {
|
|
const { setNodes } = useReactFlow()
|
|
|
|
const updatePrompt = useCallback(
|
|
(e: ChangeEvent<HTMLInputElement>) => {
|
|
setNodes((nodes) =>
|
|
nodes.map((node) =>
|
|
node.id === id
|
|
? { ...node, data: { ...node.data, prompt: e.target.value } }
|
|
: node
|
|
)
|
|
)
|
|
},
|
|
[id, setNodes]
|
|
)
|
|
|
|
const updateOptionLabel = useCallback(
|
|
(optionId: string, label: string) => {
|
|
setNodes((nodes) =>
|
|
nodes.map((node) =>
|
|
node.id === id
|
|
? {
|
|
...node,
|
|
data: {
|
|
...node.data,
|
|
options: node.data.options.map((opt: ChoiceOption) =>
|
|
opt.id === optionId ? { ...opt, label } : opt
|
|
),
|
|
},
|
|
}
|
|
: node
|
|
)
|
|
)
|
|
},
|
|
[id, setNodes]
|
|
)
|
|
|
|
return (
|
|
<div className="min-w-[220px] rounded-lg border-2 border-green-500 bg-green-50 p-3 shadow-md dark:border-green-400 dark:bg-green-950">
|
|
<Handle
|
|
type="target"
|
|
position={Position.Top}
|
|
id="input"
|
|
className="!h-3 !w-3 !border-2 !border-green-500 !bg-white dark:!bg-zinc-800"
|
|
/>
|
|
|
|
<div className="mb-2 text-xs font-semibold uppercase tracking-wide text-green-700 dark:text-green-300">
|
|
Choice
|
|
</div>
|
|
|
|
<input
|
|
type="text"
|
|
value={data.prompt || ''}
|
|
onChange={updatePrompt}
|
|
placeholder="What do you choose?"
|
|
className="mb-3 w-full rounded border border-green-300 bg-white px-2 py-1 text-sm focus:border-green-500 focus:outline-none focus:ring-1 focus:ring-green-500 dark:border-green-600 dark:bg-zinc-800 dark:text-white dark:placeholder-zinc-400"
|
|
/>
|
|
|
|
<div className="space-y-2">
|
|
{data.options.map((option, index) => (
|
|
<div key={option.id} className="relative">
|
|
<input
|
|
type="text"
|
|
value={option.label}
|
|
onChange={(e) => updateOptionLabel(option.id, e.target.value)}
|
|
placeholder={`Option ${index + 1}`}
|
|
className="w-full rounded border border-green-300 bg-white px-2 py-1 pr-3 text-sm focus:border-green-500 focus:outline-none focus:ring-1 focus:ring-green-500 dark:border-green-600 dark:bg-zinc-800 dark:text-white dark:placeholder-zinc-400"
|
|
/>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Bottom}
|
|
id={`option-${index}`}
|
|
className="!h-3 !w-3 !border-2 !border-green-500 !bg-white dark:!bg-zinc-800"
|
|
style={{
|
|
left: `${((index + 1) / (data.options.length + 1)) * 100}%`,
|
|
}}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|