Pop out dynamic form editor into dialog

This commit is contained in:
Gregory Schier
2025-07-11 08:33:04 -07:00
parent 791e5ad486
commit ad4e073f62
2 changed files with 82 additions and 18 deletions

View File

@@ -14,11 +14,14 @@ import classNames from 'classnames';
import { useAtomValue } from 'jotai';
import { useCallback } from 'react';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useRandomKey } from '../hooks/useRandomKey';
import { capitalize } from '../lib/capitalize';
import { showDialog } from '../lib/dialog';
import { resolvedModelName } from '../lib/resolvedModelName';
import { Banner } from './core/Banner';
import { Checkbox } from './core/Checkbox';
import { Editor } from './core/Editor/Editor';
import { IconButton } from './core/IconButton';
import { Input } from './core/Input';
import { Label } from './core/Label';
import { Select } from './core/Select';
@@ -271,10 +274,11 @@ function EditorArg({
// Read-only editor force refresh for every defaultValue change
// Should this be built into the <Editor/> component?
const forceUpdateKey = arg.readOnly ? arg.defaultValue + stateKey : stateKey;
const [popoutKey, regeneratePopoutKey] = useRandomKey();
const forceUpdateKey = popoutKey + (arg.readOnly ? arg.defaultValue + stateKey : stateKey);
return (
<div className=" w-full grid grid-cols-1 grid-rows-[auto_minmax(0,1fr)]">
<div className="w-full grid grid-cols-1 grid-rows-[auto_minmax(0,1fr)]">
<Label
htmlFor={id}
required={!arg.optional}
@@ -284,26 +288,85 @@ function EditorArg({
>
{arg.label}
</Label>
<Editor
id={id}
<div
className={classNames(
'border border-border rounded-md overflow-hidden px-2 py-1',
'focus-within:border-border-focus',
'max-h-[15rem]', // So it doesn't take up too much space
'max-h-[10rem]', // So it doesn't take up too much space
)}
autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined}
disabled={arg.disabled}
language={arg.language}
onChange={onChange}
heightMode="auto"
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
placeholder={arg.placeholder ?? undefined}
autocompleteFunctions={autocompleteFunctions}
autocompleteVariables={autocompleteVariables}
stateKey={stateKey}
forceUpdateKey={forceUpdateKey}
hideGutter
/>
>
<Editor
id={id}
autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined}
disabled={arg.disabled}
language={arg.language}
onChange={onChange}
heightMode="auto"
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
placeholder={arg.placeholder ?? undefined}
autocompleteFunctions={autocompleteFunctions}
autocompleteVariables={autocompleteVariables}
stateKey={stateKey}
forceUpdateKey={forceUpdateKey}
actions={
<div>
<IconButton
variant="border"
size="sm"
className="my-0.5 opacity-60 group-hover:opacity-100"
icon="expand"
title="Pop out to large editor"
onClick={() => {
showDialog({
id: 'id',
size: 'dynamic',
title: 'Edit Value',
description: (
<Label
htmlFor={id}
required={!arg.optional}
visuallyHidden={arg.hideLabel}
help={arg.description}
tags={arg.language ? [capitalize(arg.language)] : undefined}
>
{arg.label}
</Label>
),
onClose() {
// Force the main editor to update on close
regeneratePopoutKey();
},
render() {
return (
<div className="w-[100vw] max-w-[40rem] h-[calc(100vh-10rem)] max-h-[40rem]">
<Editor
id={id}
autocomplete={
arg.completionOptions ? { options: arg.completionOptions } : undefined
}
disabled={arg.disabled}
language={arg.language}
onChange={onChange}
defaultValue={
value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value
}
placeholder={arg.placeholder ?? undefined}
autocompleteFunctions={autocompleteFunctions}
autocompleteVariables={autocompleteVariables}
stateKey={stateKey}
forceUpdateKey={forceUpdateKey}
/>
</div>
);
},
});
}}
/>
</div>
}
hideGutter
/>
</div>
</div>
);
}

View File

@@ -45,6 +45,7 @@ const icons = {
download: lucide.DownloadIcon,
ellipsis: lucide.EllipsisIcon,
external_link: lucide.ExternalLinkIcon,
expand: lucide.ExpandIcon,
eye: lucide.EyeIcon,
eye_closed: lucide.EyeOffIcon,
file_code: lucide.FileCodeIcon,