({ value, onChange, disabled = false, variableItems }: JsonInputProps)
| 35 | * inject a variable into that specific key (matching the original Flowise behaviour). |
| 36 | */ |
| 37 | export function JsonInput({ value, onChange, disabled = false, variableItems }: JsonInputProps) { |
| 38 | const theme = useTheme() |
| 39 | const isDarkMode = theme.palette.mode === 'dark' |
| 40 | |
| 41 | const [myValue, setMyValue] = useState<object>(() => safeParse(value)) |
| 42 | const myValueRef = useRef(myValue) |
| 43 | myValueRef.current = myValue |
| 44 | |
| 45 | // Sync internal state when the value prop changes externally |
| 46 | useEffect(() => { |
| 47 | const parsed = safeParse(value) |
| 48 | if (JSON.stringify(parsed) !== JSON.stringify(myValueRef.current)) { |
| 49 | setMyValue(parsed) |
| 50 | } |
| 51 | }, [value]) |
| 52 | |
| 53 | // ── Per-key variable popover state ─────────────────────────────────────── |
| 54 | const mouseUpKeyRef = useRef('') |
| 55 | const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null) |
| 56 | // Counter that increments on variable injection to force ReactJson remount. |
| 57 | // ReactJson doesn't re-render when `src` changes via setState — it manages |
| 58 | // its own internal tree. Bumping the key forces a fresh mount with the new data. |
| 59 | const [remountKey, setRemountKey] = useState(0) |
| 60 | const openPopOver = Boolean(anchorEl) |
| 61 | |
| 62 | const handleClosePopOver = useCallback(() => { |
| 63 | setAnchorEl(null) |
| 64 | }, []) |
| 65 | |
| 66 | const setNewVal = useCallback( |
| 67 | (val: string) => { |
| 68 | setMyValue((prev) => { |
| 69 | const updated = { ...prev, [mouseUpKeyRef.current]: val } |
| 70 | onChange(JSON.stringify(updated)) |
| 71 | return updated |
| 72 | }) |
| 73 | setRemountKey((k) => k + 1) |
| 74 | }, |
| 75 | [onChange] |
| 76 | ) |
| 77 | |
| 78 | // ── Clipboard ──────────────────────────────────────────────────────────── |
| 79 | const onClipboardCopy = useCallback((e: { src: unknown }) => { |
| 80 | const src = e.src |
| 81 | if (Array.isArray(src) || typeof src === 'object') { |
| 82 | navigator.clipboard.writeText(JSON.stringify(src, null, ' ')) |
| 83 | } else { |
| 84 | navigator.clipboard.writeText(String(src)) |
| 85 | } |
| 86 | }, []) |
| 87 | |
| 88 | const hasVariables = variableItems && variableItems.length > 0 |
| 89 | const jsonTheme = isDarkMode ? 'ocean' : 'rjv-default' |
| 90 | |
| 91 | return ( |
| 92 | <> |
| 93 | <FormControl sx={{ mt: 1, width: '100%' }} size='small'> |
| 94 | {disabled && ( |
nothing calls this directly
no test coverage detected