mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix inconsistent shift modifier capture
The shift key listener didn't catch pressed when focused in a textarea or input field, causing jank on slider number inputs. Add keydown and keyup listeners to all such fields, which ensures that the `shift` state is always correct. Also add the action tracking it to `actionsDenylist` to not clutter up devtools.
This commit is contained in:
parent
26cea7b13d
commit
a0ccb4385f
@ -9,4 +9,5 @@ export const actionsDenylist = [
|
||||
'canvas/addPointToCurrentLine',
|
||||
'socket/socketGeneratorProgress',
|
||||
'socket/appSocketGeneratorProgress',
|
||||
'hotkeys/shiftKeyPressed',
|
||||
];
|
||||
|
@ -5,8 +5,10 @@ import {
|
||||
Input,
|
||||
InputProps,
|
||||
} from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { stopPastePropagation } from 'common/util/stopPastePropagation';
|
||||
import { ChangeEvent, memo } from 'react';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import { ChangeEvent, KeyboardEvent, memo, useCallback } from 'react';
|
||||
|
||||
interface IAIInputProps extends InputProps {
|
||||
label?: string;
|
||||
@ -25,6 +27,25 @@ const IAIInput = (props: IAIInputProps) => {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(true));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
isInvalid={isInvalid}
|
||||
@ -32,7 +53,12 @@ const IAIInput = (props: IAIInputProps) => {
|
||||
{...formControlProps}
|
||||
>
|
||||
{label !== '' && <FormLabel>{label}</FormLabel>}
|
||||
<Input {...rest} onPaste={stopPastePropagation} />
|
||||
<Input
|
||||
{...rest}
|
||||
onPaste={stopPastePropagation}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Tooltip, useColorMode, useToken } from '@chakra-ui/react';
|
||||
import { MultiSelect, MultiSelectProps } from '@mantine/core';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens';
|
||||
import { RefObject, memo } from 'react';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import { KeyboardEvent, RefObject, memo, useCallback } from 'react';
|
||||
import { mode } from 'theme/util/mode';
|
||||
|
||||
type IAIMultiSelectProps = MultiSelectProps & {
|
||||
@ -11,6 +13,7 @@ type IAIMultiSelectProps = MultiSelectProps & {
|
||||
|
||||
const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => {
|
||||
const { searchable = true, tooltip, inputRef, ...rest } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
base50,
|
||||
base100,
|
||||
@ -31,10 +34,30 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => {
|
||||
const [boxShadow] = useToken('shadows', ['dark-lg']);
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(true));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} placement="top" hasArrow isOpen={true}>
|
||||
<MultiSelect
|
||||
ref={inputRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
searchable={searchable}
|
||||
styles={() => ({
|
||||
label: {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Tooltip, useColorMode, useToken } from '@chakra-ui/react';
|
||||
import { Select, SelectProps } from '@mantine/core';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens';
|
||||
import { memo } from 'react';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import { KeyboardEvent, memo, useCallback } from 'react';
|
||||
import { mode } from 'theme/util/mode';
|
||||
|
||||
export type IAISelectDataType = {
|
||||
@ -16,6 +18,7 @@ type IAISelectProps = SelectProps & {
|
||||
|
||||
const IAIMantineSelect = (props: IAISelectProps) => {
|
||||
const { searchable = true, tooltip, ...rest } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
base50,
|
||||
base100,
|
||||
@ -36,11 +39,31 @@ const IAIMantineSelect = (props: IAISelectProps) => {
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(true));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const [boxShadow] = useToken('shadows', ['dark-lg']);
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} placement="top" hasArrow>
|
||||
<Select
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
searchable={searchable}
|
||||
styles={() => ({
|
||||
label: {
|
||||
|
@ -14,10 +14,19 @@ import {
|
||||
Tooltip,
|
||||
TooltipProps,
|
||||
} from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { stopPastePropagation } from 'common/util/stopPastePropagation';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import { clamp } from 'lodash-es';
|
||||
|
||||
import { FocusEvent, memo, useEffect, useState } from 'react';
|
||||
import {
|
||||
FocusEvent,
|
||||
KeyboardEvent,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
const numberStringRegex = /^-?(0\.)?\.?$/;
|
||||
|
||||
@ -60,6 +69,8 @@ const IAINumberInput = (props: Props) => {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
/**
|
||||
* Using a controlled input with a value that accepts decimals needs special
|
||||
* handling. If the user starts to type in "1.5", by the time they press the
|
||||
@ -109,6 +120,24 @@ const IAINumberInput = (props: Props) => {
|
||||
onChange(clamped);
|
||||
};
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(true));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip {...tooltipProps}>
|
||||
<FormControl
|
||||
@ -128,7 +157,11 @@ const IAINumberInput = (props: Props) => {
|
||||
{...rest}
|
||||
onPaste={stopPastePropagation}
|
||||
>
|
||||
<NumberInputField {...numberInputFieldProps} />
|
||||
<NumberInputField
|
||||
{...numberInputFieldProps}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
/>
|
||||
{showStepper && (
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper {...numberInputStepperProps} />
|
||||
|
@ -26,9 +26,12 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { clamp } from 'lodash-es';
|
||||
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import {
|
||||
FocusEvent,
|
||||
KeyboardEvent,
|
||||
memo,
|
||||
MouseEvent,
|
||||
useCallback,
|
||||
@ -107,7 +110,7 @@ const IAISlider = (props: IAIFullSliderProps) => {
|
||||
sliderIAIIconButtonProps,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [localInputValue, setLocalInputValue] = useState<
|
||||
@ -167,6 +170,24 @@ const IAISlider = (props: IAIFullSliderProps) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(true));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
onClick={forceInputBlur}
|
||||
@ -310,6 +331,8 @@ const IAISlider = (props: IAIFullSliderProps) => {
|
||||
{...sliderNumberInputProps}
|
||||
>
|
||||
<NumberInputField
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
minWidth={inputWidth}
|
||||
{...sliderNumberInputFieldProps}
|
||||
/>
|
||||
|
@ -1,9 +1,38 @@
|
||||
import { Textarea, TextareaProps, forwardRef } from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { stopPastePropagation } from 'common/util/stopPastePropagation';
|
||||
import { memo } from 'react';
|
||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||
import { KeyboardEvent, memo, useCallback } from 'react';
|
||||
|
||||
const IAITextarea = forwardRef((props: TextareaProps, ref) => {
|
||||
return <Textarea ref={ref} onPaste={stopPastePropagation} {...props} />;
|
||||
const dispatch = useAppDispatch();
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(true));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (!e.shiftKey) {
|
||||
dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
ref={ref}
|
||||
onPaste={stopPastePropagation}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default memo(IAITextarea);
|
||||
|
Loading…
Reference in New Issue
Block a user