import { LongTextFieldDefinition, ConfigurableValue, ConfigurableFieldDefinition, PartialEditorPorts, } from "@flyde/core"; import { SimpleJsonEditor } from "../SimpleJsonEditor"; import { useState, useEffect } from "react"; import { SecretSelector } from "./SecretSelector"; import { Checkbox, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea } from "../.."; const inputClassName = "w-full"; export function ConfigurableValueBaseEditor(props: { value: ConfigurableValue; onChange: (value: ConfigurableValue) => void; fieldDefinition: ConfigurableFieldDefinition; isExpanded?: boolean; rawJsonData?: string; onRawJsonDataChange?: (rawData: string) => void; ports: PartialEditorPorts; }) { const { value, onChange, fieldDefinition, ports, isExpanded, rawJsonData, onRawJsonDataChange, } = props; const { prompt: _prompt } = ports; const [options, setOptions] = useState< { value: string | number; label: string }[] >( (fieldDefinition.type === "select" ? fieldDefinition.typeData.options : [] ).map((opt) => typeof opt === "object" ? opt : { value: opt, label: String(opt) } ) || [] ); // Track raw JSON data internally if not provided from parent const [internalRawJsonData, setInternalRawJsonData] = useState( rawJsonData || (value.type === "json" ? JSON.stringify(value.value, null, 2) : "") ); // Update internal state when rawJsonData prop changes useEffect(() => { if (rawJsonData !== undefined && value.type === "json") { setInternalRawJsonData(rawJsonData); } }, [rawJsonData, value.type]); // Handle raw JSON data changes const handleRawJsonDataChange = (rawData: string) => { setInternalRawJsonData(rawData); if (onRawJsonDataChange) { onRawJsonDataChange(rawData); } }; useEffect(() => { if ( fieldDefinition.type === "select" && !options.some((option) => option.value === value.value) && fieldDefinition.typeConfigurable !== false ) { setOptions([ ...options, { value: value.value, label: String(value.value) }, ]); } }, [value, options, fieldDefinition.type, fieldDefinition.typeConfigurable]); const handleAddOption = async () => { if (fieldDefinition.typeConfigurable === false) { return; // Don't allow adding options when typeConfigurable is false } const newOption = await _prompt({ text: "Enter a new option:" }); if (newOption && !options.some((option) => option.value === newOption)) { const updatedOptions = [ ...options, { value: newOption, label: newOption, }, ]; setOptions(updatedOptions); onChange({ type: "select", value: newOption }); } }; switch (value.type) { case "number": return ( props.onChange({ type: "number", value: parseFloat(e.target.value), }) } min={fieldDefinition.type === "number" && fieldDefinition.typeData?.min !== undefined ? fieldDefinition.typeData.min : undefined} max={fieldDefinition.type === "number" && fieldDefinition.typeData?.max !== undefined ? fieldDefinition.typeData.max : undefined} className={inputClassName} /> ); case "string": { // Special handling for secrets if (fieldDefinition.type === "secret" && ports) { return ( props.onChange({ type: "string", value: newValue })} ports={ports} typeEditorData={fieldDefinition.typeData} /> ); } if (fieldDefinition.type === "longtext") { return (