Remove useTemplating prop (#184)

This commit is contained in:
Gregory Schier
2025-03-18 05:34:38 -07:00
committed by GitHub
parent f42f3d0e27
commit b9ed554aca
20 changed files with 134 additions and 231 deletions

View File

@@ -1,80 +0,0 @@
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
import { Input } from './core/Input';
import { VStack } from './core/Stacks';
interface Props<T> {
request: T;
}
export function BasicAuth<T extends HttpRequest | GrpcRequest>({ request }: Props<T>) {
const updateHttpRequest = useUpdateAnyHttpRequest();
const updateGrpcRequest = useUpdateAnyGrpcRequest();
return (
<VStack className="py-2 overflow-y-auto h-full" space={2}>
<Input
useTemplating
autocompleteVariables
stateKey={`basic.username.${request.id}`}
forceUpdateKey={request.id}
placeholder="username"
label="Username"
name="username"
size="sm"
defaultValue={`${request.authentication.username}`}
onChange={(username: string) => {
if (request.model === 'http_request') {
updateHttpRequest.mutate({
id: request.id,
update: (r: HttpRequest) => ({
...r,
authentication: { password: r.authentication.password, username },
}),
});
} else {
updateGrpcRequest.mutate({
id: request.id,
update: (r: GrpcRequest) => ({
...r,
authentication: { password: r.authentication.password, username },
}),
});
}
}}
/>
<Input
useTemplating
autocompleteVariables
forceUpdateKey={request?.id}
stateKey={`basic.password.${request.id}`}
placeholder="password"
label="Password"
name="password"
size="sm"
type="password"
defaultValue={`${request.authentication.password}`}
onChange={(password: string) => {
if (request.model === 'http_request') {
updateHttpRequest.mutate({
id: request.id,
update: (r: HttpRequest) => ({
...r,
authentication: { username: r.authentication.username, password },
}),
});
} else {
updateGrpcRequest.mutate({
id: request.id,
update: (r: GrpcRequest) => ({
...r,
authentication: { username: r.authentication.username, password },
}),
});
}
}}
/>
</VStack>
);
}

View File

@@ -1,49 +0,0 @@
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
import { Input } from './core/Input';
import { VStack } from './core/Stacks';
interface Props<T> {
request: T;
}
export function BearerAuth<T extends HttpRequest | GrpcRequest>({ request }: Props<T>) {
const updateHttpRequest = useUpdateAnyHttpRequest();
const updateGrpcRequest = useUpdateAnyGrpcRequest();
return (
<VStack className="my-2" space={2}>
<Input
useTemplating
autocompleteVariables
placeholder="token"
stateKey={`bearer.${request.id}`}
type="password"
label="Token"
name="token"
size="sm"
defaultValue={`${request.authentication.token}`}
onChange={(token: string) => {
if (request.model === 'http_request') {
updateHttpRequest.mutate({
id: request.id ?? null,
update: (r: HttpRequest) => ({
...r,
authentication: { token },
}),
});
} else {
updateGrpcRequest.mutate({
id: request.id ?? null,
update: (r: GrpcRequest) => ({
...r,
authentication: { token },
}),
});
}
}}
/>
</VStack>
);
}

View File

@@ -34,7 +34,7 @@ interface Props<T> {
inputs: FormInput[] | undefined | null; inputs: FormInput[] | undefined | null;
onChange: (value: T) => void; onChange: (value: T) => void;
data: T; data: T;
useTemplating?: boolean; autocompleteFunctions?: boolean;
autocompleteVariables?: boolean; autocompleteVariables?: boolean;
stateKey: string; stateKey: string;
disabled?: boolean; disabled?: boolean;
@@ -44,8 +44,8 @@ export function DynamicForm<T extends Record<string, JsonPrimitive>>({
inputs, inputs,
data, data,
onChange, onChange,
useTemplating,
autocompleteVariables, autocompleteVariables,
autocompleteFunctions,
stateKey, stateKey,
disabled, disabled,
}: Props<T>) { }: Props<T>) {
@@ -62,7 +62,7 @@ export function DynamicForm<T extends Record<string, JsonPrimitive>>({
inputs={inputs} inputs={inputs}
setDataAttr={setDataAttr} setDataAttr={setDataAttr}
stateKey={stateKey} stateKey={stateKey}
useTemplating={useTemplating} autocompleteFunctions={autocompleteFunctions}
autocompleteVariables={autocompleteVariables} autocompleteVariables={autocompleteVariables}
data={data} data={data}
/> />
@@ -71,13 +71,16 @@ export function DynamicForm<T extends Record<string, JsonPrimitive>>({
function FormInputs<T extends Record<string, JsonPrimitive>>({ function FormInputs<T extends Record<string, JsonPrimitive>>({
inputs, inputs,
autocompleteFunctions,
autocompleteVariables, autocompleteVariables,
stateKey, stateKey,
useTemplating,
setDataAttr, setDataAttr,
data, data,
disabled, disabled,
}: Pick<Props<T>, 'inputs' | 'useTemplating' | 'autocompleteVariables' | 'stateKey' | 'data'> & { }: Pick<
Props<T>,
'inputs' | 'autocompleteFunctions' | 'autocompleteVariables' | 'stateKey' | 'data'
> & {
setDataAttr: (name: string, value: JsonPrimitive) => void; setDataAttr: (name: string, value: JsonPrimitive) => void;
disabled?: boolean; disabled?: boolean;
}) { }) {
@@ -112,7 +115,7 @@ function FormInputs<T extends Record<string, JsonPrimitive>>({
key={i} key={i}
stateKey={stateKey} stateKey={stateKey}
arg={input} arg={input}
useTemplating={useTemplating || false} autocompleteFunctions={autocompleteFunctions || false}
autocompleteVariables={autocompleteVariables || false} autocompleteVariables={autocompleteVariables || false}
onChange={(v) => setDataAttr(input.name, v)} onChange={(v) => setDataAttr(input.name, v)}
value={ value={
@@ -126,7 +129,7 @@ function FormInputs<T extends Record<string, JsonPrimitive>>({
key={i} key={i}
stateKey={stateKey} stateKey={stateKey}
arg={input} arg={input}
useTemplating={useTemplating || false} autocompleteFunctions={autocompleteFunctions || false}
autocompleteVariables={autocompleteVariables || false} autocompleteVariables={autocompleteVariables || false}
onChange={(v) => setDataAttr(input.name, v)} onChange={(v) => setDataAttr(input.name, v)}
value={ value={
@@ -175,7 +178,7 @@ function FormInputs<T extends Record<string, JsonPrimitive>>({
inputs={input.inputs} inputs={input.inputs}
setDataAttr={setDataAttr} setDataAttr={setDataAttr}
stateKey={stateKey} stateKey={stateKey}
useTemplating={useTemplating} autocompleteFunctions={autocompleteFunctions || false}
autocompleteVariables={autocompleteVariables} autocompleteVariables={autocompleteVariables}
/> />
</div> </div>
@@ -195,7 +198,7 @@ function FormInputs<T extends Record<string, JsonPrimitive>>({
inputs={input.inputs} inputs={input.inputs}
setDataAttr={setDataAttr} setDataAttr={setDataAttr}
stateKey={stateKey} stateKey={stateKey}
useTemplating={useTemplating} autocompleteFunctions={autocompleteFunctions || false}
autocompleteVariables={autocompleteVariables} autocompleteVariables={autocompleteVariables}
/> />
</Banner> </Banner>
@@ -212,14 +215,14 @@ function TextArg({
arg, arg,
onChange, onChange,
value, value,
useTemplating, autocompleteFunctions,
autocompleteVariables, autocompleteVariables,
stateKey, stateKey,
}: { }: {
arg: FormInputText; arg: FormInputText;
value: string; value: string;
onChange: (v: string) => void; onChange: (v: string) => void;
useTemplating: boolean; autocompleteFunctions: boolean;
autocompleteVariables: boolean; autocompleteVariables: boolean;
stateKey: string; stateKey: string;
}) { }) {
@@ -237,7 +240,7 @@ function TextArg({
hideLabel={arg.label == null} hideLabel={arg.label == null}
placeholder={arg.placeholder ?? undefined} placeholder={arg.placeholder ?? undefined}
autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined} autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined}
useTemplating={useTemplating} autocompleteFunctions={autocompleteFunctions}
autocompleteVariables={autocompleteVariables} autocompleteVariables={autocompleteVariables}
stateKey={stateKey} stateKey={stateKey}
forceUpdateKey={stateKey} forceUpdateKey={stateKey}
@@ -249,14 +252,14 @@ function EditorArg({
arg, arg,
onChange, onChange,
value, value,
useTemplating, autocompleteFunctions,
autocompleteVariables, autocompleteVariables,
stateKey, stateKey,
}: { }: {
arg: FormInputEditor; arg: FormInputEditor;
value: string; value: string;
onChange: (v: string) => void; onChange: (v: string) => void;
useTemplating: boolean; autocompleteFunctions: boolean;
autocompleteVariables: boolean; autocompleteVariables: boolean;
stateKey: string; stateKey: string;
}) { }) {
@@ -290,7 +293,7 @@ function EditorArg({
heightMode="auto" heightMode="auto"
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value} defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
placeholder={arg.placeholder ?? undefined} placeholder={arg.placeholder ?? undefined}
useTemplating={useTemplating} autocompleteFunctions={autocompleteFunctions}
autocompleteVariables={autocompleteVariables} autocompleteVariables={autocompleteVariables}
stateKey={stateKey} stateKey={stateKey}
forceUpdateKey={forceUpdateKey} forceUpdateKey={forceUpdateKey}

View File

@@ -116,7 +116,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
}; };
const EnvironmentEditor = function ({ const EnvironmentEditor = function ({
environment, environment: activeEnvironment,
className, className,
}: { }: {
environment: Environment; environment: Environment;
@@ -127,8 +127,8 @@ const EnvironmentEditor = function ({
key: 'environmentValueVisibility', key: 'environmentValueVisibility',
fallback: true, fallback: true,
}); });
const { subEnvironments } = useEnvironments(); const { allEnvironments } = useEnvironments();
const updateEnvironment = useUpdateEnvironment(environment?.id ?? null); const updateEnvironment = useUpdateEnvironment(activeEnvironment?.id ?? null);
const handleChange = useCallback<PairEditorProps['onChange']>( const handleChange = useCallback<PairEditorProps['onChange']>(
(variables) => updateEnvironment.mutate({ variables }), (variables) => updateEnvironment.mutate({ variables }),
[updateEnvironment], [updateEnvironment],
@@ -136,26 +136,28 @@ const EnvironmentEditor = function ({
// Gather a list of env names from other environments, to help the user get them aligned // Gather a list of env names from other environments, to help the user get them aligned
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => { const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
const allVariableNames = const options: GenericCompletionOption[] = [];
environment == null const isBaseEnv = activeEnvironment.environmentId == null;
? [] // Nothing to autocomplete if we're in the base environment if (isBaseEnv) {
: subEnvironments return { options };
.filter((e) => e.environmentId != null) }
.flatMap((e) => e.variables.map((v) => v.name));
// Filter out empty strings and variables that already exist const allVariables = allEnvironments.flatMap((e) => e?.variables);
const variableNames = allVariableNames.filter( const allVariableNames = new Set(allVariables.map((v) => v?.name));
(name) => name != '' && !environment.variables.find((v) => v.name === name), for (const name of allVariableNames) {
); const containingEnvs = allEnvironments.filter((e) =>
const uniqueVariableNames = [...new Set(variableNames)]; e.variables.some((v) => v.name === name),
const options = uniqueVariableNames.map( );
(name): GenericCompletionOption => ({ const isAlreadyInActive = containingEnvs.find((e) => e.id === activeEnvironment.id);
if (isAlreadyInActive) continue;
options.push({
label: name, label: name,
type: 'constant', type: 'constant',
}), detail: containingEnvs.map((e) => e.name).join(', '),
); });
}
return { options }; return { options };
}, [subEnvironments, environment]); }, [activeEnvironment.environmentId, activeEnvironment.id, allEnvironments]);
const validateName = useCallback((name: string) => { const validateName = useCallback((name: string) => {
// Empty just means the variable doesn't have a name yet, and is unusable // Empty just means the variable doesn't have a name yet, and is unusable
@@ -167,7 +169,7 @@ const EnvironmentEditor = function ({
<VStack space={4} className={classNames(className, 'pl-4')}> <VStack space={4} className={classNames(className, 'pl-4')}>
<HStack space={2} className="justify-between"> <HStack space={2} className="justify-between">
<Heading className="w-full flex items-center gap-1"> <Heading className="w-full flex items-center gap-1">
<div>{environment?.name}</div> <div>{activeEnvironment?.name}</div>
<IconButton <IconButton
size="sm" size="sm"
icon={valueVisibility.value ? 'eye' : 'eye_closed'} icon={valueVisibility.value ? 'eye' : 'eye_closed'}
@@ -183,15 +185,15 @@ const EnvironmentEditor = function ({
allowMultilineValues allowMultilineValues
preferenceName="environment" preferenceName="environment"
nameAutocomplete={nameAutocomplete} nameAutocomplete={nameAutocomplete}
nameAutocompleteVariables={false}
namePlaceholder="VAR_NAME" namePlaceholder="VAR_NAME"
nameValidate={validateName} nameValidate={validateName}
valueType={valueVisibility.value ? 'text' : 'password'} valueType={valueVisibility.value ? 'text' : 'password'}
valueAutocompleteVariables={true} valueAutocompleteVariables
forceUpdateKey={environment.id} valueAutocompleteFunctions
pairs={environment.variables} forceUpdateKey={activeEnvironment.id}
pairs={activeEnvironment.variables}
onChange={handleChange} onChange={handleChange}
stateKey={`environment.${environment.id}`} stateKey={`environment.${activeEnvironment.id}`}
/> />
</div> </div>
</VStack> </VStack>

View File

@@ -40,8 +40,10 @@ export function FormMultipartEditor({ request, forceUpdateKey, onChange }: Props
return ( return (
<PairEditor <PairEditor
valueAutocompleteFunctions
valueAutocompleteVariables valueAutocompleteVariables
nameAutocompleteVariables nameAutocompleteVariables
nameAutocompleteFunctions
allowFileValues allowFileValues
allowMultilineValues allowMultilineValues
pairs={pairs} pairs={pairs}

View File

@@ -31,7 +31,9 @@ export function FormUrlencodedEditor({ request, forceUpdateKey, onChange }: Prop
<PairOrBulkEditor <PairOrBulkEditor
allowMultilineValues allowMultilineValues
preferenceName="form_urlencoded" preferenceName="form_urlencoded"
valueAutocompleteFunctions
valueAutocompleteVariables valueAutocompleteVariables
nameAutocompleteFunctions
nameAutocompleteVariables nameAutocompleteVariables
namePlaceholder="entry_name" namePlaceholder="entry_name"
valuePlaceholder="Value" valuePlaceholder="Value"

View File

@@ -183,7 +183,7 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
onChange={handleChangeVariables} onChange={handleChangeVariables}
placeholder="{}" placeholder="{}"
stateKey={'graphql_vars.' + request.id} stateKey={'graphql_vars.' + request.id}
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
{...extraEditorProps} {...extraEditorProps}
/> />

View File

@@ -181,8 +181,8 @@ export function GrpcEditor({
<div className="h-full w-full grid grid-cols-1 grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]"> <div className="h-full w-full grid grid-cols-1 grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]">
<Editor <Editor
language="json" language="json"
autocompleteFunctions
autocompleteVariables autocompleteVariables
useTemplating
forceUpdateKey={request.id} forceUpdateKey={request.id}
defaultValue={request.message} defaultValue={request.message}
heightMode="auto" heightMode="auto"

View File

@@ -21,7 +21,9 @@ export function HeadersEditor({ stateKey, headers, onChange, forceUpdateKey }: P
<PairOrBulkEditor <PairOrBulkEditor
preferenceName="headers" preferenceName="headers"
stateKey={stateKey} stateKey={stateKey}
valueAutocompleteFunctions
valueAutocompleteVariables valueAutocompleteVariables
nameAutocompleteFunctions
nameAutocompleteVariables nameAutocompleteVariables
pairs={headers} pairs={headers}
onChange={onChange} onChange={onChange}

View File

@@ -75,7 +75,7 @@ export function HttpAuthenticationEditor({ request }: Props) {
<DynamicForm <DynamicForm
disabled={request.authentication.disabled} disabled={request.authentication.disabled}
autocompleteVariables autocompleteVariables
useTemplating autocompleteFunctions
stateKey={`auth.${request.id}.${request.authenticationType}`} stateKey={`auth.${request.id}.${request.authenticationType}`}
inputs={authConfig.data.args} inputs={authConfig.data.args}
data={request.authentication} data={request.authentication}

View File

@@ -411,7 +411,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
{activeRequest.bodyType === BODY_TYPE_JSON ? ( {activeRequest.bodyType === BODY_TYPE_JSON ? (
<Editor <Editor
forceUpdateKey={forceUpdateKey} forceUpdateKey={forceUpdateKey}
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
placeholder="..." placeholder="..."
heightMode={fullHeight ? 'full' : 'auto'} heightMode={fullHeight ? 'full' : 'auto'}
@@ -423,7 +423,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
) : activeRequest.bodyType === BODY_TYPE_XML ? ( ) : activeRequest.bodyType === BODY_TYPE_XML ? (
<Editor <Editor
forceUpdateKey={forceUpdateKey} forceUpdateKey={forceUpdateKey}
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
placeholder="..." placeholder="..."
heightMode={fullHeight ? 'full' : 'auto'} heightMode={fullHeight ? 'full' : 'auto'}
@@ -462,7 +462,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
) : typeof activeRequest.bodyType === 'string' ? ( ) : typeof activeRequest.bodyType === 'string' ? (
<Editor <Editor
forceUpdateKey={forceUpdateKey} forceUpdateKey={forceUpdateKey}
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
language={languageFromContentType(contentType)} language={languageFromContentType(contentType)}
placeholder="..." placeholder="..."

View File

@@ -68,12 +68,12 @@ export const UrlBar = memo(function UrlBar({
<form onSubmit={handleSubmit} className={classNames('x-theme-urlBar', className)}> <form onSubmit={handleSubmit} className={classNames('x-theme-urlBar', className)}>
<Input <Input
ref={inputRef} ref={inputRef}
autocompleteFunctions
autocompleteVariables autocompleteVariables
stateKey={stateKey} stateKey={stateKey}
size="sm" size="sm"
wrapLines={isFocused} wrapLines={isFocused}
hideLabel hideLabel
useTemplating
language="url" language="url"
className="px-1.5 py-0.5" className="px-1.5 py-0.5"
label="Enter URL" label="Enter URL"

View File

@@ -35,12 +35,14 @@ export function UrlParametersEditor({ pairs, forceUpdateKey, onChange, stateKey
ref={pairEditor} ref={pairEditor}
allowMultilineValues allowMultilineValues
forceUpdateKey={forceUpdateKey + urlParametersKey} forceUpdateKey={forceUpdateKey + urlParametersKey}
nameAutocompleteFunctions
nameAutocompleteVariables nameAutocompleteVariables
namePlaceholder="param_name" namePlaceholder="param_name"
onChange={onChange} onChange={onChange}
pairs={pairs} pairs={pairs}
preferenceName="url_parameters" preferenceName="url_parameters"
stateKey={stateKey} stateKey={stateKey}
valueAutocompleteFunctions
valueAutocompleteVariables valueAutocompleteVariables
valuePlaceholder="Value" valuePlaceholder="Value"
/> />

View File

@@ -301,7 +301,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
<TabContent value={TAB_MESSAGE}> <TabContent value={TAB_MESSAGE}>
<Editor <Editor
forceUpdateKey={forceUpdateKey} forceUpdateKey={forceUpdateKey}
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
placeholder="..." placeholder="..."
heightMode={fullHeight ? 'full' : 'auto'} heightMode={fullHeight ? 'full' : 'auto'}

View File

@@ -33,7 +33,7 @@ export function BulkPairEditor({
return ( return (
<Editor <Editor
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
stateKey={`bulk_pair.${stateKey}`} stateKey={`bulk_pair.${stateKey}`}
forceUpdateKey={forceUpdateKey} forceUpdateKey={forceUpdateKey}

View File

@@ -55,36 +55,36 @@ const keymapExtensions: Record<EditorKeymap, Extension> = {
}; };
export interface EditorProps { export interface EditorProps {
id?: string; actions?: ReactNode;
readOnly?: boolean;
disabled?: boolean;
type?: 'text' | 'password';
className?: string;
heightMode?: 'auto' | 'full';
language?: EditorLanguage | 'pairs' | 'url';
forceUpdateKey?: string | number;
autoFocus?: boolean; autoFocus?: boolean;
autoSelect?: boolean; autoSelect?: boolean;
autocomplete?: GenericCompletionConfig;
autocompleteFunctions?: boolean;
autocompleteVariables?: boolean;
className?: string;
defaultValue?: string | null; defaultValue?: string | null;
placeholder?: string; disableTabIndent?: boolean;
tooltipContainer?: HTMLElement; disabled?: boolean;
useTemplating?: boolean; extraExtensions?: Extension[];
forceUpdateKey?: string | number;
format?: (v: string) => Promise<string>;
heightMode?: 'auto' | 'full';
hideGutter?: boolean;
id?: string;
language?: EditorLanguage | 'pairs' | 'url';
onBlur?: () => void;
onChange?: (value: string) => void; onChange?: (value: string) => void;
onFocus?: () => void;
onKeyDown?: (e: KeyboardEvent) => void;
onPaste?: (value: string) => void; onPaste?: (value: string) => void;
onPasteOverwrite?: (e: ClipboardEvent, value: string) => void; onPasteOverwrite?: (e: ClipboardEvent, value: string) => void;
onFocus?: () => void; placeholder?: string;
onBlur?: () => void; readOnly?: boolean;
onKeyDown?: (e: KeyboardEvent) => void;
singleLine?: boolean; singleLine?: boolean;
wrapLines?: boolean;
disableTabIndent?: boolean;
format?: (v: string) => Promise<string>;
autocomplete?: GenericCompletionConfig;
autocompleteVariables?: boolean;
extraExtensions?: Extension[];
actions?: ReactNode;
hideGutter?: boolean;
stateKey: string | null; stateKey: string | null;
tooltipContainer?: HTMLElement;
type?: 'text' | 'password';
wrapLines?: boolean;
} }
const stateFields = { history: historyField, folds: foldState }; const stateFields = { history: historyField, folds: foldState };
@@ -94,34 +94,34 @@ const emptyExtension: Extension = [];
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor( export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
{ {
readOnly, actions,
type,
heightMode,
language,
autoFocus, autoFocus,
autoSelect, autoSelect,
placeholder, autocomplete,
useTemplating, autocompleteFunctions,
autocompleteVariables,
className,
defaultValue, defaultValue,
disableTabIndent,
disabled,
extraExtensions,
forceUpdateKey, forceUpdateKey,
format,
heightMode,
hideGutter,
language,
onBlur,
onChange, onChange,
onFocus,
onKeyDown,
onPaste, onPaste,
onPasteOverwrite, onPasteOverwrite,
onFocus, placeholder,
onBlur, readOnly,
onKeyDown,
className,
disabled,
singleLine, singleLine,
format,
autocomplete,
extraExtensions,
autocompleteVariables,
actions,
wrapLines,
disableTabIndent,
hideGutter,
stateKey, stateKey,
type,
wrapLines,
}: EditorProps, }: EditorProps,
ref, ref,
) { ) {
@@ -129,6 +129,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
const allEnvironmentVariables = useActiveEnvironmentVariables(); const allEnvironmentVariables = useActiveEnvironmentVariables();
const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables; const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables;
const useTemplating = !!(autocompleteFunctions || autocompleteVariables || autocomplete);
if (settings && wrapLines === undefined) { if (settings && wrapLines === undefined) {
wrapLines = settings.editorSoftWrap; wrapLines = settings.editorSoftWrap;
@@ -340,16 +341,19 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
[focusParamValue], [focusParamValue],
); );
const completionOptions = useTemplateFunctionCompletionOptions(onClickFunction); const completionOptions = useTemplateFunctionCompletionOptions(
onClickFunction,
!!autocompleteFunctions,
);
// Update the language extension when the language changes // Update the language extension when the language changes
useEffect(() => { useEffect(() => {
if (cm.current === null) return; if (cm.current === null) return;
const { view, languageCompartment } = cm.current; const { view, languageCompartment } = cm.current;
const ext = getLanguageExtension({ const ext = getLanguageExtension({
useTemplating,
language, language,
environmentVariables, environmentVariables,
useTemplating,
autocomplete, autocomplete,
completionOptions, completionOptions,
onClickVariable, onClickVariable,
@@ -360,13 +364,13 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
}, [ }, [
language, language,
autocomplete, autocomplete,
useTemplating,
environmentVariables, environmentVariables,
onClickFunction, onClickFunction,
onClickVariable, onClickVariable,
onClickMissingVariable, onClickMissingVariable,
onClickPathParameter, onClickPathParameter,
completionOptions, completionOptions,
useTemplating,
]); ]);
// Initialize the editor when ref mounts // Initialize the editor when ref mounts
@@ -381,8 +385,8 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
try { try {
const languageCompartment = new Compartment(); const languageCompartment = new Compartment();
const langExt = getLanguageExtension({ const langExt = getLanguageExtension({
language,
useTemplating, useTemplating,
language,
completionOptions, completionOptions,
autocomplete, autocomplete,
environmentVariables, environmentVariables,

View File

@@ -91,8 +91,8 @@ const syntaxExtensions: Record<NonNullable<EditorProps['language']>, LanguageSup
const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ['json', 'javascript', 'graphql']; const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ['json', 'javascript', 'graphql'];
export function getLanguageExtension({ export function getLanguageExtension({
useTemplating,
language = 'text', language = 'text',
useTemplating = false,
environmentVariables, environmentVariables,
autocomplete, autocomplete,
onClickVariable, onClickVariable,
@@ -100,12 +100,13 @@ export function getLanguageExtension({
onClickPathParameter, onClickPathParameter,
completionOptions, completionOptions,
}: { }: {
useTemplating: boolean;
environmentVariables: EnvironmentVariable[]; environmentVariables: EnvironmentVariable[];
onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void; onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void;
onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void; onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void;
onClickPathParameter: (name: string) => void; onClickPathParameter: (name: string) => void;
completionOptions: TwigCompletionOption[]; completionOptions: TwigCompletionOption[];
} & Pick<EditorProps, 'language' | 'useTemplating' | 'autocomplete'>) { } & Pick<EditorProps, 'language' | 'autocomplete'>) {
const extraExtensions: Extension[] = []; const extraExtensions: Extension[] = [];
if (language === 'url') { if (language === 'url') {

View File

@@ -13,13 +13,13 @@ import { HStack } from './Stacks';
export type InputProps = Pick< export type InputProps = Pick<
EditorProps, EditorProps,
| 'language' | 'language'
| 'useTemplating'
| 'autocomplete' | 'autocomplete'
| 'forceUpdateKey' | 'forceUpdateKey'
| 'disabled' | 'disabled'
| 'autoFocus' | 'autoFocus'
| 'autoSelect' | 'autoSelect'
| 'autocompleteVariables' | 'autocompleteVariables'
| 'autocompleteFunctions'
| 'onKeyDown' | 'onKeyDown'
| 'readOnly' | 'readOnly'
> & { > & {

View File

@@ -43,6 +43,7 @@ export type PairEditorProps = {
className?: string; className?: string;
forceUpdateKey?: string; forceUpdateKey?: string;
nameAutocomplete?: GenericCompletionConfig; nameAutocomplete?: GenericCompletionConfig;
nameAutocompleteFunctions?: boolean;
nameAutocompleteVariables?: boolean; nameAutocompleteVariables?: boolean;
namePlaceholder?: string; namePlaceholder?: string;
nameValidate?: InputProps['validate']; nameValidate?: InputProps['validate'];
@@ -51,6 +52,7 @@ export type PairEditorProps = {
pairs: Pair[]; pairs: Pair[];
stateKey: InputProps['stateKey']; stateKey: InputProps['stateKey'];
valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined;
valueAutocompleteFunctions?: boolean;
valueAutocompleteVariables?: boolean; valueAutocompleteVariables?: boolean;
valuePlaceholder?: string; valuePlaceholder?: string;
valueType?: 'text' | 'password'; valueType?: 'text' | 'password';
@@ -82,6 +84,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
className, className,
forceUpdateKey, forceUpdateKey,
nameAutocomplete, nameAutocomplete,
nameAutocompleteFunctions,
nameAutocompleteVariables, nameAutocompleteVariables,
namePlaceholder, namePlaceholder,
nameValidate, nameValidate,
@@ -89,6 +92,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
onChange, onChange,
pairs: originalPairs, pairs: originalPairs,
valueAutocomplete, valueAutocomplete,
valueAutocompleteFunctions,
valueAutocompleteVariables, valueAutocompleteVariables,
valuePlaceholder, valuePlaceholder,
valueType, valueType,
@@ -237,6 +241,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
index={i} index={i}
isLast={isLast} isLast={isLast}
nameAutocomplete={nameAutocomplete} nameAutocomplete={nameAutocomplete}
nameAutocompleteFunctions={nameAutocompleteFunctions}
nameAutocompleteVariables={nameAutocompleteVariables} nameAutocompleteVariables={nameAutocompleteVariables}
namePlaceholder={namePlaceholder} namePlaceholder={namePlaceholder}
nameValidate={nameValidate} nameValidate={nameValidate}
@@ -248,6 +253,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
pair={p} pair={p}
stateKey={stateKey} stateKey={stateKey}
valueAutocomplete={valueAutocomplete} valueAutocomplete={valueAutocomplete}
valueAutocompleteFunctions={valueAutocompleteFunctions}
valueAutocompleteVariables={valueAutocompleteVariables} valueAutocompleteVariables={valueAutocompleteVariables}
valuePlaceholder={valuePlaceholder} valuePlaceholder={valuePlaceholder}
valueType={valueType} valueType={valueType}
@@ -291,8 +297,10 @@ type PairEditorRowProps = {
| 'nameAutocompleteVariables' | 'nameAutocompleteVariables'
| 'namePlaceholder' | 'namePlaceholder'
| 'nameValidate' | 'nameValidate'
| 'nameAutocompleteFunctions'
| 'stateKey' | 'stateKey'
| 'valueAutocomplete' | 'valueAutocomplete'
| 'valueAutocompleteFunctions'
| 'valueAutocompleteVariables' | 'valueAutocompleteVariables'
| 'valuePlaceholder' | 'valuePlaceholder'
| 'valueType' | 'valueType'
@@ -309,9 +317,10 @@ function PairEditorRow({
index, index,
isLast, isLast,
nameAutocomplete, nameAutocomplete,
nameAutocompleteVariables,
namePlaceholder, namePlaceholder,
nameValidate, nameValidate,
nameAutocompleteFunctions,
nameAutocompleteVariables,
onChange, onChange,
onDelete, onDelete,
onEnd, onEnd,
@@ -320,6 +329,7 @@ function PairEditorRow({
pair, pair,
stateKey, stateKey,
valueAutocomplete, valueAutocomplete,
valueAutocompleteFunctions,
valueAutocompleteVariables, valueAutocompleteVariables,
valuePlaceholder, valuePlaceholder,
valueType, valueType,
@@ -486,7 +496,6 @@ function PairEditorRow({
<Input <Input
ref={nameInputRef} ref={nameInputRef}
hideLabel hideLabel
useTemplating
stateKey={`name.${pair.id}.${stateKey}`} stateKey={`name.${pair.id}.${stateKey}`}
wrapLines={false} wrapLines={false}
readOnly={pair.readOnlyName} readOnly={pair.readOnlyName}
@@ -503,6 +512,7 @@ function PairEditorRow({
placeholder={namePlaceholder ?? 'name'} placeholder={namePlaceholder ?? 'name'}
autocomplete={nameAutocomplete} autocomplete={nameAutocomplete}
autocompleteVariables={nameAutocompleteVariables} autocompleteVariables={nameAutocompleteVariables}
autocompleteFunctions={nameAutocompleteFunctions}
/> />
)} )}
<div className="w-full grid grid-cols-[minmax(0,1fr)_auto] gap-1 items-center"> <div className="w-full grid grid-cols-[minmax(0,1fr)_auto] gap-1 items-center">
@@ -534,7 +544,6 @@ function PairEditorRow({
<Input <Input
ref={valueInputRef} ref={valueInputRef}
hideLabel hideLabel
useTemplating
stateKey={`value.${pair.id}.${stateKey}`} stateKey={`value.${pair.id}.${stateKey}`}
wrapLines={false} wrapLines={false}
size="sm" size="sm"
@@ -549,6 +558,7 @@ function PairEditorRow({
type={isLast ? 'text' : valueType} type={isLast ? 'text' : valueType}
placeholder={valuePlaceholder ?? 'value'} placeholder={valuePlaceholder ?? 'value'}
autocomplete={valueAutocomplete?.(pair.name)} autocomplete={valueAutocomplete?.(pair.name)}
autocompleteFunctions={valueAutocompleteFunctions}
autocompleteVariables={valueAutocompleteVariables} autocompleteVariables={valueAutocompleteVariables}
/> />
)} )}
@@ -695,7 +705,7 @@ function MultilineEditDialog({
language={language} language={language}
onChange={setValue} onChange={setValue}
stateKey={null} stateKey={null}
useTemplating autocompleteFunctions
autocompleteVariables autocompleteVariables
/> />
<div> <div>

View File

@@ -11,9 +11,13 @@ const templateFunctionsAtom = atom<TemplateFunction[]>([]);
export function useTemplateFunctionCompletionOptions( export function useTemplateFunctionCompletionOptions(
onClick: (fn: TemplateFunction, ragTag: string, pos: number) => void, onClick: (fn: TemplateFunction, ragTag: string, pos: number) => void,
enabled: boolean,
) { ) {
const templateFunctions = useAtomValue(templateFunctionsAtom); const templateFunctions = useAtomValue(templateFunctionsAtom);
return useMemo<TwigCompletionOption[]>(() => { return useMemo<TwigCompletionOption[]>(() => {
if (!enabled) {
return [];
}
return ( return (
templateFunctions.map((fn) => { templateFunctions.map((fn) => {
const NUM_ARGS = 2; const NUM_ARGS = 2;
@@ -35,7 +39,7 @@ export function useTemplateFunctionCompletionOptions(
}; };
}) ?? [] }) ?? []
); );
}, [onClick, templateFunctions]); }, [enabled, onClick, templateFunctions]);
} }
export function useSubscribeTemplateFunctions() { export function useSubscribeTemplateFunctions() {