import type { Folder, HttpRequest } from '@yaakapp-internal/models'; import type { FormInput, FormInputCheckbox, FormInputEditor, FormInputFile, FormInputHttpRequest, FormInputSelect, FormInputText, JsonPrimitive, } from '@yaakapp-internal/plugins'; import classNames from 'classnames'; import { useCallback } from 'react'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useFolders } from '../hooks/useFolders'; import { useHttpRequests } from '../hooks/useHttpRequests'; import { capitalize } from '../lib/capitalize'; import { resolvedModelName } from '../lib/resolvedModelName'; import { Banner } from './core/Banner'; import { Checkbox } from './core/Checkbox'; import { Editor } from './core/Editor/Editor'; import { Input } from './core/Input'; import { Label } from './core/Label'; import { Select } from './core/Select'; import { VStack } from './core/Stacks'; import { Markdown } from './Markdown'; import { SelectFile } from './SelectFile'; // eslint-disable-next-line react-refresh/only-export-components export const DYNAMIC_FORM_NULL_ARG = '__NULL__'; const INPUT_SIZE = 'sm'; interface Props { inputs: FormInput[] | undefined | null; onChange: (value: T) => void; data: T; autocompleteFunctions?: boolean; autocompleteVariables?: boolean; stateKey: string; disabled?: boolean; } export function DynamicForm>({ inputs, data, onChange, autocompleteVariables, autocompleteFunctions, stateKey, disabled, }: Props) { const setDataAttr = useCallback( (name: string, value: JsonPrimitive) => { onChange({ ...data, [name]: value == DYNAMIC_FORM_NULL_ARG ? undefined : value }); }, [data, onChange], ); return ( ); } function FormInputs>({ inputs, autocompleteFunctions, autocompleteVariables, stateKey, setDataAttr, data, disabled, }: Pick< Props, 'inputs' | 'autocompleteFunctions' | 'autocompleteVariables' | 'stateKey' | 'data' > & { setDataAttr: (name: string, value: JsonPrimitive) => void; disabled?: boolean; }) { return ( {inputs?.map((input, i) => { if ('hidden' in input && input.hidden) { return null; } if ('disabled' in input && disabled != null) { input.disabled = disabled; } switch (input.type) { case 'select': return ( setDataAttr(input.name, v)} value={ data[input.name] ? String(data[input.name]) : (input.defaultValue ?? DYNAMIC_FORM_NULL_ARG) } /> ); case 'text': return ( setDataAttr(input.name, v)} value={ data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '') } /> ); case 'editor': return ( setDataAttr(input.name, v)} value={ data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '') } /> ); case 'checkbox': return ( setDataAttr(input.name, v)} value={data[input.name] != null ? data[input.name] === true : false} /> ); case 'http_request': return ( setDataAttr(input.name, v)} value={data[input.name] != null ? String(data[input.name]) : DYNAMIC_FORM_NULL_ARG} /> ); case 'file': return ( setDataAttr(input.name, v)} filePath={ data[input.name] != null ? String(data[input.name]) : DYNAMIC_FORM_NULL_ARG } /> ); case 'accordion': return (
{input.label}
); case 'banner': return ( ); case 'markdown': return {input.content}; } })}
); } function TextArg({ arg, onChange, value, autocompleteFunctions, autocompleteVariables, stateKey, }: { arg: FormInputText; value: string; onChange: (v: string) => void; autocompleteFunctions: boolean; autocompleteVariables: boolean; stateKey: string; }) { return ( ); } function EditorArg({ arg, onChange, value, autocompleteFunctions, autocompleteVariables, stateKey, }: { arg: FormInputEditor; value: string; onChange: (v: string) => void; autocompleteFunctions: boolean; autocompleteVariables: boolean; stateKey: string; }) { const id = `input-${arg.name}`; // Read-only editor force refresh for every defaultValue change // Should this be built into the component? const forceUpdateKey = arg.readOnly ? arg.defaultValue + stateKey : stateKey; return (
); } function SelectArg({ arg, value, onChange, }: { arg: FormInputSelect; value: string; onChange: (v: string) => void; }) { return ( { return { label: buildRequestBreadcrumbs(r, folders).join(' / ') + (r.id == activeRequest?.id ? ' (current)' : ''), value: r.id, }; }), ]} /> ); } function buildRequestBreadcrumbs(request: HttpRequest, folders: Folder[]): string[] { const ancestors: (HttpRequest | Folder)[] = [request]; const next = () => { const latest = ancestors[0]; if (latest == null) return []; const parent = folders.find((f) => f.id === latest.folderId); if (parent == null) return; ancestors.unshift(parent); next(); }; next(); return ancestors.map((a) => (a.model === 'folder' ? a.name : resolvedModelName(a))); } function CheckboxArg({ arg, onChange, value, }: { arg: FormInputCheckbox; value: boolean; onChange: (v: boolean) => void; }) { return ( ); }