From 9082e159490e7d3ac756545dc41abe3e9ae9a6f3 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 21 Jan 2026 12:56:20 -0300 Subject: [PATCH] feat: [US-024] - Add/remove choice options Co-Authored-By: Claude Opus 4.5 --- src/components/editor/nodes/ChoiceNode.tsx | 69 +++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/components/editor/nodes/ChoiceNode.tsx b/src/components/editor/nodes/ChoiceNode.tsx index b5067a3..7073d2d 100644 --- a/src/components/editor/nodes/ChoiceNode.tsx +++ b/src/components/editor/nodes/ChoiceNode.tsx @@ -2,6 +2,7 @@ import { useCallback, ChangeEvent } from 'react' import { Handle, Position, NodeProps, useReactFlow } from 'reactflow' +import { nanoid } from 'nanoid' type ChoiceOption = { id: string @@ -13,6 +14,9 @@ type ChoiceNodeData = { options: ChoiceOption[] } +const MIN_OPTIONS = 2 +const MAX_OPTIONS = 6 + export default function ChoiceNode({ id, data }: NodeProps) { const { setNodes } = useReactFlow() @@ -50,6 +54,48 @@ export default function ChoiceNode({ id, data }: NodeProps) { [id, setNodes] ) + const addOption = useCallback(() => { + if (data.options.length >= MAX_OPTIONS) return + setNodes((nodes) => + nodes.map((node) => + node.id === id + ? { + ...node, + data: { + ...node.data, + options: [ + ...node.data.options, + { id: nanoid(), label: '' }, + ], + }, + } + : node + ) + ) + }, [id, data.options.length, setNodes]) + + const removeOption = useCallback( + (optionId: string) => { + if (data.options.length <= MIN_OPTIONS) return + setNodes((nodes) => + nodes.map((node) => + node.id === id + ? { + ...node, + data: { + ...node.data, + options: node.data.options.filter( + (opt: ChoiceOption) => opt.id !== optionId + ), + }, + } + : node + ) + ) + }, + [id, data.options.length, setNodes] + ) + return (
) {
{data.options.map((option, index) => ( -
+
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" + className="flex-1 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" /> + ) {
))}
+ +
) }