Delete key/value on backspace

This commit is contained in:
Gregory Schier
2023-10-29 10:26:38 -07:00
parent 4d93892249
commit c37c020cf0
4 changed files with 59 additions and 17 deletions

View File

@@ -37,6 +37,7 @@ export interface EditorProps {
onFocus?: () => void; onFocus?: () => void;
onBlur?: () => void; onBlur?: () => void;
onEnter?: () => void; onEnter?: () => void;
onKeyDown?: (e: KeyboardEvent) => void;
singleLine?: boolean; singleLine?: boolean;
wrapLines?: boolean; wrapLines?: boolean;
format?: (v: string) => string; format?: (v: string) => string;
@@ -60,6 +61,7 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
onChange, onChange,
onFocus, onFocus,
onBlur, onBlur,
onKeyDown,
onEnter, onEnter,
className, className,
singleLine, singleLine,
@@ -101,6 +103,12 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
handleBlur.current = onBlur; handleBlur.current = onBlur;
}, [onBlur]); }, [onBlur]);
// Use ref so we can update the onChange handler without re-initializing the editor
const handleKeyDown = useRef<EditorProps['onKeyDown']>(onKeyDown);
useEffect(() => {
handleKeyDown.current = onKeyDown;
}, [onKeyDown]);
// Update placeholder // Update placeholder
const placeholderCompartment = useRef(new Compartment()); const placeholderCompartment = useRef(new Compartment());
useEffect(() => { useEffect(() => {
@@ -168,6 +176,7 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
onChange: handleChange, onChange: handleChange,
onFocus: handleFocus, onFocus: handleFocus,
onBlur: handleBlur, onBlur: handleBlur,
onKeyDown: handleKeyDown,
}), }),
], ],
}); });
@@ -242,6 +251,7 @@ function getExtensions({
onChange, onChange,
onFocus, onFocus,
onBlur, onBlur,
onKeyDown,
onEnter, onEnter,
}: Pick<EditorProps, 'singleLine' | 'readOnly'> & { }: Pick<EditorProps, 'singleLine' | 'readOnly'> & {
container: HTMLDivElement | null; container: HTMLDivElement | null;
@@ -249,6 +259,7 @@ function getExtensions({
onFocus: MutableRefObject<EditorProps['onFocus']>; onFocus: MutableRefObject<EditorProps['onFocus']>;
onBlur: MutableRefObject<EditorProps['onBlur']>; onBlur: MutableRefObject<EditorProps['onBlur']>;
onEnter: MutableRefObject<EditorProps['onEnter']>; onEnter: MutableRefObject<EditorProps['onEnter']>;
onKeyDown: MutableRefObject<EditorProps['onKeyDown']>;
}) { }) {
// TODO: Ensure tooltips render inside the dialog if we are in one. // TODO: Ensure tooltips render inside the dialog if we are in one.
const parent = const parent =
@@ -282,6 +293,7 @@ function getExtensions({
EditorView.domEventHandlers({ EditorView.domEventHandlers({
focus: onFocus.current, focus: onFocus.current,
blur: onBlur.current, blur: onBlur.current,
keydown: onKeyDown.current,
}), }),
// Handle onChange // Handle onChange

View File

@@ -7,8 +7,21 @@ import { Editor } from './Editor';
import { IconButton } from './IconButton'; import { IconButton } from './IconButton';
import { HStack, VStack } from './Stacks'; import { HStack, VStack } from './Stacks';
export type InputProps = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> & export type InputProps = Omit<
Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete' | 'forceUpdateKey' | 'autoFocus' | 'autoSelect' | 'autocompleteVariables'> & { HTMLAttributes<HTMLInputElement>,
'onChange' | 'onFocus' | 'onKeyDown'
> &
Pick<
EditorProps,
| 'contentType'
| 'useTemplating'
| 'autocomplete'
| 'forceUpdateKey'
| 'autoFocus'
| 'autoSelect'
| 'autocompleteVariables'
| 'onKeyDown'
> & {
name: string; name: string;
type?: 'text' | 'password'; type?: 'text' | 'password';
label: string; label: string;

View File

@@ -132,14 +132,13 @@ export const PairEditor = memo(function PairEditor({
[setPairsAndSave], [setPairsAndSave],
); );
const handleFocus = useCallback( const handleFocus = useCallback((pair: PairContainer) => {
(pair: PairContainer) => return setPairs((pairs) => {
setPairs((pairs) => { setForceFocusPairId(null);
const isLast = pair.id === pairs[pairs.length - 1]?.id; const isLast = pair.id === pairs[pairs.length - 1]?.id;
return isLast ? [...pairs, newPairContainer()] : pairs; return isLast ? [...pairs, newPairContainer()] : pairs;
}), });
[], }, []);
);
// Ensure there's always at least one pair // Ensure there's always at least one pair
useEffect(() => { useEffect(() => {
@@ -177,10 +176,11 @@ export const PairEditor = memo(function PairEditor({
valuePlaceholder={valuePlaceholder} valuePlaceholder={valuePlaceholder}
nameValidate={nameValidate} nameValidate={nameValidate}
valueValidate={valueValidate} valueValidate={valueValidate}
showDelete={!isLast}
onSubmit={handleSubmitRow} onSubmit={handleSubmitRow}
onChange={handleChange} onChange={handleChange}
onFocus={handleFocus} onFocus={handleFocus}
onDelete={isLast ? undefined : handleDelete} onDelete={handleDelete}
onEnd={handleEnd} onEnd={handleEnd}
onMove={handleMove} onMove={handleMove}
/> />
@@ -199,6 +199,7 @@ type FormRowProps = {
className?: string; className?: string;
pairContainer: PairContainer; pairContainer: PairContainer;
forceFocusPairId?: string | null; forceFocusPairId?: string | null;
showDelete?: boolean;
onMove: (id: string, side: 'above' | 'below') => void; onMove: (id: string, side: 'above' | 'below') => void;
onEnd: (id: string) => void; onEnd: (id: string) => void;
onChange: (pair: PairContainer) => void; onChange: (pair: PairContainer) => void;
@@ -236,6 +237,7 @@ const FormRow = memo(function FormRow({
onMove, onMove,
onSubmit, onSubmit,
pairContainer, pairContainer,
showDelete,
valueAutocomplete, valueAutocomplete,
valuePlaceholder, valuePlaceholder,
valueValidate, valueValidate,
@@ -268,6 +270,20 @@ const FormRow = memo(function FormRow({
const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]); const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]);
const handleDelete = useCallback(() => onDelete?.(pairContainer), [onDelete, pairContainer]); const handleDelete = useCallback(() => onDelete?.(pairContainer), [onDelete, pairContainer]);
const handleKeyDownName = useMemo(
() => (e: KeyboardEvent) => {
if (
e.key === 'Backspace' &&
pairContainer.pair.name === '' &&
pairContainer.pair.value === ''
) {
e.preventDefault();
onDelete?.(pairContainer);
}
},
[pairContainer, onDelete],
);
const [, connectDrop] = useDrop<PairContainer>( const [, connectDrop] = useDrop<PairContainer>(
{ {
accept: ItemTypes.ROW, accept: ItemTypes.ROW,
@@ -349,6 +365,7 @@ const FormRow = memo(function FormRow({
defaultValue={pairContainer.pair.name} defaultValue={pairContainer.pair.name}
label="Name" label="Name"
name="name" name="name"
onKeyDown={handleKeyDownName}
onChange={handleChangeName} onChange={handleChangeName}
onFocus={handleFocus} onFocus={handleFocus}
placeholder={namePlaceholder ?? 'name'} placeholder={namePlaceholder ?? 'name'}
@@ -373,13 +390,13 @@ const FormRow = memo(function FormRow({
/> />
</form> </form>
<IconButton <IconButton
aria-hidden={!onDelete} aria-hidden={!showDelete}
disabled={!onDelete} disabled={!showDelete}
color="custom" color="custom"
icon={onDelete ? 'trash' : 'empty'} icon={showDelete ? 'trash' : 'empty'}
size="sm" size="sm"
title="Delete header" title="Delete header"
onClick={handleDelete} onClick={showDelete ? handleDelete : undefined}
className="ml-0.5 !opacity-0 group-hover:!opacity-100 focus-visible:!opacity-100" className="ml-0.5 !opacity-0 group-hover:!opacity-100 focus-visible:!opacity-100"
/> />
</div> </div>

View File

@@ -20,9 +20,9 @@ export function useUpdateRequest(id: string | null) {
const request = await getRequest(id); const request = await getRequest(id);
if (request === null) return; if (request === null) return;
const newRequest = typeof v === 'function' ? v(request) : { ...request, ...v }; const patchedRequest = typeof v === 'function' ? v(request) : { ...request, ...v };
queryClient.setQueryData<HttpRequest[]>(requestsQueryKey(request), (requests) => queryClient.setQueryData<HttpRequest[]>(requestsQueryKey(request), (requests) =>
(requests ?? []).map((r) => (r.id === newRequest.id ? newRequest : r)), (requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)),
); );
}, },
}); });