diff --git a/src-web/components/HeaderEditor.tsx b/src-web/components/HeaderEditor.tsx index 0aa9a84e..d64643ae 100644 --- a/src-web/components/HeaderEditor.tsx +++ b/src-web/components/HeaderEditor.tsx @@ -15,10 +15,6 @@ type PairWithId = { header: Partial; id: string }; export function HeaderEditor({ request, className }: Props) { const updateRequest = useUpdateRequest(request); - const saveHeaders = (pairs: PairWithId[]) => { - const headers = pairs.map((p) => ({ name: '', value: '', ...p.header })); - updateRequest.mutate({ headers }); - }; const newPair = () => { return { header: { name: '', value: '' }, id: Math.random().toString() }; @@ -31,7 +27,8 @@ export function HeaderEditor({ request, className }: Props) { const setPairsAndSave = (fn: (pairs: PairWithId[]) => PairWithId[]) => { setPairs((oldPairs) => { const newPairs = fn(oldPairs); - saveHeaders(newPairs); + const headers = newPairs.map((p) => ({ name: '', value: '', ...p.header })); + updateRequest.mutate({ headers }); return newPairs; }); }; @@ -47,7 +44,7 @@ export function HeaderEditor({ request, className }: Props) { useEffect(() => { const lastPair = pairs[pairs.length - 1]; if (lastPair === undefined) { - setPairs([newPair()]); + setPairsAndSave((pairs) => [...pairs, newPair()]); return; } @@ -63,14 +60,18 @@ export function HeaderEditor({ request, className }: Props) { return (
- {pairs.map((p, i) => ( - - ))} + {pairs.map((p, i) => { + const isLast = i === pairs.length - 1; + return ( + + ); + })}
); @@ -80,30 +81,38 @@ function FormRow({ pair, onChange, onDelete, + onFocus, + isLast, }: { pair: PairWithId; onChange: (pair: PairWithId) => void; onDelete?: (pair: PairWithId) => void; + onFocus?: (pair: PairWithId) => void; + isLast?: boolean; }) { return (
onChange({ id: pair.id, header: { name } })} + onFocus={() => onFocus?.(pair)} + placeholder="name" + useEditor={{ useTemplating: true }} /> onChange({ id: pair.id, header: { value } })} + onFocus={() => onFocus?.(pair)} + placeholder="value" + useEditor={{ useTemplating: true }} /> {onDelete && ( void; + onFocus?: () => void; singleLine?: boolean; } @@ -36,6 +36,7 @@ export function _Editor({ useTemplating, defaultValue, onChange, + onFocus, className, singleLine, }: _EditorProps) { @@ -72,6 +73,7 @@ export function _Editor({ placeholder, singleLine, onChange, + onFocus, contentType, useTemplating, }), @@ -100,7 +102,7 @@ export function _Editor({ {contentType?.includes('graphql') && ( { const doc = cm.current?.view.state.doc ?? ''; const insert = formatSdl(doc.toString()); @@ -118,11 +120,18 @@ function getExtensions({ singleLine, placeholder, onChange, + onFocus, contentType, useTemplating, }: Pick< _EditorProps, - 'singleLine' | 'onChange' | 'contentType' | 'useTemplating' | 'placeholder' | 'readOnly' + | 'singleLine' + | 'onChange' + | 'contentType' + | 'useTemplating' + | 'placeholder' + | 'readOnly' + | 'onFocus' > & { container: HTMLDivElement | null }) { const ext = getLanguageExtension({ contentType, useTemplating }); @@ -160,6 +169,13 @@ function getExtensions({ ] : []), + // Handle onFocus + EditorView.domEventHandlers({ + focus: () => { + onFocus?.(); + }, + }), + // Handle onChange EditorView.updateListener.of((update) => { if (typeof onChange === 'function' && update.docChanged) { diff --git a/src-web/components/core/Input.tsx b/src-web/components/core/Input.tsx index e01f2701..8b0a3fcf 100644 --- a/src-web/components/core/Input.tsx +++ b/src-web/components/core/Input.tsx @@ -1,16 +1,17 @@ import classnames from 'classnames'; -import type { ReactNode } from 'react'; +import type { HTMLAttributes, ReactNode } from 'react'; import type { EditorProps } from './Editor'; import { Editor } from './Editor'; import { HStack, VStack } from './Stacks'; -interface Props { +type Props = Omit, 'onChange' | 'onFocus'> & { name: string; label: string; hideLabel?: boolean; labelClassName?: string; containerClassName?: string; onChange?: (value: string) => void; + onFocus?: () => void; useEditor?: Pick; defaultValue?: string; leftSlot?: ReactNode; @@ -19,7 +20,7 @@ interface Props { className?: string; placeholder?: string; autoFocus?: boolean; -} +}; export function Input({ label,