Pair validation

This commit is contained in:
Gregory Schier
2023-03-20 00:17:29 -07:00
parent b16d74d55b
commit 333b9319b6
4 changed files with 60 additions and 57 deletions

View File

@@ -1,6 +1,6 @@
import classnames from 'classnames';
import type { FormEvent } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback } from 'react';
import { useIsResponseLoading } from '../hooks/useIsResponseLoading';
import { useSendRequest } from '../hooks/useSendRequest';
import { useUpdateRequest } from '../hooks/useUpdateRequest';
@@ -20,7 +20,6 @@ export const UrlBar = memo(function UrlBar({ request, className }: Props) {
const handleMethodChange = useCallback((method: string) => updateRequest.mutate({ method }), []);
const handleUrlChange = useCallback((url: string) => updateRequest.mutate({ url }), []);
const loading = useIsResponseLoading(request.id);
const useEditor = useMemo(() => ({ useTemplating: true, contentType: 'url' }), []);
const handleSubmit = useCallback(
async (e: FormEvent) => {
@@ -35,7 +34,8 @@ export const UrlBar = memo(function UrlBar({ request, className }: Props) {
<Input
key={request.id}
hideLabel
useEditor={useEditor}
useTemplating
contentType="url"
className="px-0"
name="url"
label="Enter URL"

View File

@@ -1,26 +1,29 @@
import classnames from 'classnames';
import { useMemo, useState } from 'react';
import type { HTMLAttributes, ReactNode } from 'react';
import type { EditorProps } from './Editor';
import { Editor } from './Editor';
import { HStack, VStack } from './Stacks';
type Props = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> & {
name: string;
label: string;
hideLabel?: boolean;
labelClassName?: string;
containerClassName?: string;
onChange?: (value: string) => void;
onFocus?: () => void;
useEditor?: Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'>;
defaultValue?: string;
leftSlot?: ReactNode;
rightSlot?: ReactNode;
size?: 'sm' | 'md';
className?: string;
placeholder?: string;
autoFocus?: boolean;
};
type Props = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> &
Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'> & {
name: string;
label: string;
hideLabel?: boolean;
labelClassName?: string;
containerClassName?: string;
onChange?: (value: string) => void;
onFocus?: () => void;
defaultValue?: string;
leftSlot?: ReactNode;
rightSlot?: ReactNode;
size?: 'sm' | 'md';
className?: string;
placeholder?: string;
autoFocus?: boolean;
validate?: (v: string) => boolean;
require?: boolean;
};
export function Input({
label,
@@ -31,13 +34,15 @@ export function Input({
onChange,
placeholder,
size = 'md',
useEditor,
name,
leftSlot,
rightSlot,
defaultValue,
validate,
require,
...props
}: Props) {
const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
const id = `input-${name}`;
const inputClassName = classnames(
className,
@@ -46,6 +51,17 @@ export function Input({
!!rightSlot && '!pr-0.5',
);
const isValid = useMemo(() => {
if (require && !validateRequire(currentValue)) return false;
if (validate && !validate(currentValue)) return false;
return true;
}, [currentValue, validate, require]);
const handleChange = (value: string) => {
setCurrentValue(value);
onChange?.(value);
};
return (
<VStack>
<label
@@ -64,34 +80,27 @@ export function Input({
containerClassName,
'relative w-full rounded-md text-gray-900',
'border border-gray-200 focus-within:border-focus',
!isValid && 'border-invalid',
size === 'md' && 'h-9',
size === 'sm' && 'h-7',
)}
>
{leftSlot}
{useEditor ? (
<Editor
id={id}
singleLine
defaultValue={defaultValue}
placeholder={placeholder}
onChange={onChange}
className={inputClassName}
{...props}
{...useEditor}
/>
) : (
<input
id={id}
onChange={(e) => onChange?.(e.currentTarget.value)}
placeholder={placeholder}
defaultValue={defaultValue}
className={inputClassName}
{...props}
/>
)}
<Editor
id={id}
singleLine
defaultValue={defaultValue}
placeholder={placeholder}
onChange={handleChange}
className={inputClassName}
{...props}
/>
{rightSlot}
</HStack>
</VStack>
);
}
function validateRequire(v: string) {
return v.length > 0;
}

View File

@@ -200,16 +200,6 @@ const FormRow = memo(function FormRow({
[onChange, pairContainer.pair.name, pairContainer.pair.enabled],
);
const nameEditorConfig = useMemo(
() => ({ useTemplating: true, autocomplete: nameAutocomplete }),
[nameAutocomplete],
);
const valueEditorConfig = useMemo(
() => ({ useTemplating: true, autocomplete: valueAutocomplete?.(pairContainer.pair.name) }),
[valueAutocomplete, pairContainer.pair.name],
);
const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]);
const handleDelete = useCallback(() => onDelete?.(pairContainer), [onDelete, pairContainer]);
@@ -263,13 +253,15 @@ const FormRow = memo(function FormRow({
<span className="w-1" />
)}
<Checkbox
disabled={isLast}
checked={!!pairContainer.pair.enabled}
onChange={handleChangeEnabled}
disabled={isLast || !pairContainer.pair.name}
checked={isLast || !pairContainer.pair.name ? false : !!pairContainer.pair.enabled}
className={isLast ? '!opacity-disabled' : undefined}
onChange={handleChangeEnabled}
/>
<Input
hideLabel
require={!isLast && !!pairContainer.pair.enabled && !!pairContainer.pair.value}
useTemplating
containerClassName={classnames(isLast && 'border-dashed')}
defaultValue={pairContainer.pair.name}
label="Name"
@@ -277,7 +269,7 @@ const FormRow = memo(function FormRow({
onChange={handleChangeName}
onFocus={handleFocus}
placeholder={namePlaceholder ?? 'name'}
useEditor={nameEditorConfig}
autocomplete={nameAutocomplete}
/>
<Input
hideLabel
@@ -288,7 +280,8 @@ const FormRow = memo(function FormRow({
onChange={handleChangeValue}
onFocus={handleFocus}
placeholder={valuePlaceholder ?? 'value'}
useEditor={valueEditorConfig}
useTemplating
autocomplete={valueAutocomplete?.(pairContainer.pair.name)}
/>
{onDelete ? (
<IconButton

View File

@@ -26,6 +26,7 @@ module.exports = {
},
colors: {
focus: "hsl(var(--color-blue-500) / 0.6)",
invalid: "hsl(var(--color-red-500))",
highlight: "hsl(var(--color-gray-200) / 0.3)",
transparent: "transparent",
white: "hsl(0 100% 100% / <alpha-value>)",