diff --git a/src-web/components/HeaderEditor.tsx b/src-web/components/HeaderEditor.tsx index 793305ab..87bc08b9 100644 --- a/src-web/components/HeaderEditor.tsx +++ b/src-web/components/HeaderEditor.tsx @@ -18,6 +18,7 @@ export function HeaderEditor({ headers, onChange }: Props) { ({ label: t, type: 'constant', boost: 99 - i })), }; + +const validateHttpHeader = (v: string) => { + if (v === '') { + return true; + } + + return v.match(/^[a-zA-Z0-9-_]+$/) !== null; +}; diff --git a/src-web/components/core/Input.tsx b/src-web/components/core/Input.tsx index 0a78808d..8c638bbc 100644 --- a/src-web/components/core/Input.tsx +++ b/src-web/components/core/Input.tsx @@ -1,11 +1,11 @@ import classnames from 'classnames'; -import { useMemo, useState } from 'react'; import type { HTMLAttributes, ReactNode } from 'react'; +import { useMemo, useState } from 'react'; import type { EditorProps } from './Editor'; import { Editor } from './Editor'; import { HStack, VStack } from './Stacks'; -type Props = Omit, 'onChange' | 'onFocus'> & +export type InputProps = Omit, 'onChange' | 'onFocus'> & Pick & { name: string; label: string; @@ -41,7 +41,7 @@ export function Input({ validate, require, ...props -}: Props) { +}: InputProps) { const [currentValue, setCurrentValue] = useState(defaultValue ?? ''); const id = `input-${name}`; const inputClassName = classnames( @@ -80,7 +80,7 @@ export function Input({ containerClassName, 'relative w-full rounded-md text-gray-900', 'border border-gray-200 focus-within:border-focus', - !isValid && 'border-invalid', + !isValid && '!border-invalid', size === 'md' && 'h-md', size === 'sm' && 'h-sm', )} diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index 5bd532c5..89454757 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -10,8 +10,8 @@ import { Checkbox } from './Checkbox'; import type { GenericCompletionConfig } from './Editor/genericCompletion'; import { Icon } from './Icon'; import { IconButton } from './IconButton'; +import type { InputProps } from './Input'; import { Input } from './Input'; -import { HStack } from './Stacks'; export type PairEditorProps = { pairs: Pair[]; @@ -21,6 +21,8 @@ export type PairEditorProps = { valuePlaceholder?: string; nameAutocomplete?: GenericCompletionConfig; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; + nameValidate?: InputProps['validate']; + valueValidate?: InputProps['validate']; }; type Pair = { @@ -40,6 +42,8 @@ export const PairEditor = memo(function PairEditor({ valueAutocomplete, namePlaceholder, valuePlaceholder, + nameValidate, + valueValidate, className, onChange, }: PairEditorProps) { @@ -140,6 +144,8 @@ export const PairEditor = memo(function PairEditor({ valueAutocomplete={valueAutocomplete} namePlaceholder={namePlaceholder} valuePlaceholder={valuePlaceholder} + nameValidate={nameValidate} + valueValidate={valueValidate} onChange={handleChange} onFocus={handleFocus} onDelete={isLast ? undefined : handleDelete} @@ -168,7 +174,12 @@ type FormRowProps = { isLast?: boolean; } & Pick< PairEditorProps, - 'nameAutocomplete' | 'valueAutocomplete' | 'namePlaceholder' | 'valuePlaceholder' + | 'nameAutocomplete' + | 'valueAutocomplete' + | 'namePlaceholder' + | 'valuePlaceholder' + | 'nameValidate' + | 'valueValidate' >; const FormRow = memo(function FormRow({ @@ -183,6 +194,8 @@ const FormRow = memo(function FormRow({ valueAutocomplete, namePlaceholder, valuePlaceholder, + nameValidate, + valueValidate, }: FormRowProps) { const { id } = pairContainer; const ref = useRef(null); @@ -272,6 +285,7 @@ const FormRow = memo(function FormRow({ hideLabel size="sm" require={!isLast && !!pairContainer.pair.enabled && !!pairContainer.pair.value} + validate={nameValidate} useTemplating containerClassName={classnames(isLast && 'border-dashed')} defaultValue={pairContainer.pair.name} @@ -286,6 +300,7 @@ const FormRow = memo(function FormRow({ hideLabel size="sm" containerClassName={classnames(isLast && 'border-dashed')} + validate={valueValidate} defaultValue={pairContainer.pair.value} label="Value" name="value"