mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-19 15:21:23 +02:00
Delete key/value on backspace
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user