mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix mouse interactions (#3764)
This commit is contained in:
commit
54b813c981
@ -28,7 +28,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
const numberStringRegex = /^-?(0\.)?\.?$/;
|
export const numberStringRegex = /^-?(0\.)?\.?$/;
|
||||||
|
|
||||||
interface Props extends Omit<NumberInputProps, 'onChange'> {
|
interface Props extends Omit<NumberInputProps, 'onChange'> {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Flex, Heading, Icon, Tooltip } from '@chakra-ui/react';
|
import { Flex, Heading, Icon, Tooltip } from '@chakra-ui/react';
|
||||||
|
import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/hooks/useBuildInvocation';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { FaInfoCircle } from 'react-icons/fa';
|
import { FaInfoCircle } from 'react-icons/fa';
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ const IAINodeHeader = (props: IAINodeHeaderProps) => {
|
|||||||
const { nodeId, title, description } = props;
|
const { nodeId, title, description } = props;
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
|
className={DRAG_HANDLE_CLASSNAME}
|
||||||
sx={{
|
sx={{
|
||||||
borderTopRadius: 'md',
|
borderTopRadius: 'md',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {
|
|
||||||
InputFieldTemplate,
|
|
||||||
InputFieldValue,
|
|
||||||
InvocationTemplate,
|
|
||||||
} from 'features/nodes/types/types';
|
|
||||||
import { memo, ReactNode, useCallback } from 'react';
|
|
||||||
import { map } from 'lodash-es';
|
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { RootState } from 'app/store/store';
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
HStack,
|
HStack,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Divider,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import FieldHandle from '../FieldHandle';
|
import { RootState } from 'app/store/store';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection';
|
import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection';
|
||||||
import InputFieldComponent from '../InputFieldComponent';
|
|
||||||
import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants';
|
import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants';
|
||||||
|
import {
|
||||||
|
InputFieldTemplate,
|
||||||
|
InputFieldValue,
|
||||||
|
InvocationTemplate,
|
||||||
|
} from 'features/nodes/types/types';
|
||||||
|
import { map } from 'lodash-es';
|
||||||
|
import { ReactNode, memo, useCallback } from 'react';
|
||||||
|
import FieldHandle from '../FieldHandle';
|
||||||
|
import InputFieldComponent from '../InputFieldComponent';
|
||||||
|
|
||||||
interface IAINodeInputProps {
|
interface IAINodeInputProps {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
@ -35,6 +35,7 @@ function IAINodeInput(props: IAINodeInputProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
className="nopan"
|
||||||
position="relative"
|
position="relative"
|
||||||
borderColor={
|
borderColor={
|
||||||
!template
|
!template
|
||||||
@ -136,7 +137,7 @@ const IAINodeInputs = (props: IAINodeInputsProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDir="column" gap={2} p={2}>
|
<Flex className="nopan" flexDir="column" gap={2} p={2}>
|
||||||
{IAINodeInputsToRender}
|
{IAINodeInputsToRender}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -23,7 +23,14 @@ export const InvocationComponent = memo((props: NodeProps<InvocationValue>) => {
|
|||||||
if (!template) {
|
if (!template) {
|
||||||
return (
|
return (
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected}>
|
||||||
<Flex sx={{ alignItems: 'center', justifyContent: 'center' }}>
|
<Flex
|
||||||
|
className="nopan"
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
cursor: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
as={FaExclamationCircle}
|
as={FaExclamationCircle}
|
||||||
sx={{
|
sx={{
|
||||||
@ -46,7 +53,9 @@ export const InvocationComponent = memo((props: NodeProps<InvocationValue>) => {
|
|||||||
description={template.description}
|
description={template.description}
|
||||||
/>
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
|
className={'nopan'}
|
||||||
sx={{
|
sx={{
|
||||||
|
cursor: 'auto',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderBottomRadius: 'md',
|
borderBottomRadius: 'md',
|
||||||
py: 2,
|
py: 2,
|
||||||
|
@ -2,6 +2,8 @@ import { Box, useToken } from '@chakra-ui/react';
|
|||||||
import { NODE_MIN_WIDTH } from 'app/constants';
|
import { NODE_MIN_WIDTH } from 'app/constants';
|
||||||
|
|
||||||
import { PropsWithChildren } from 'react';
|
import { PropsWithChildren } from 'react';
|
||||||
|
import { DRAG_HANDLE_CLASSNAME } from '../hooks/useBuildInvocation';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
|
||||||
type NodeWrapperProps = PropsWithChildren & {
|
type NodeWrapperProps = PropsWithChildren & {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
@ -13,8 +15,11 @@ const NodeWrapper = (props: NodeWrapperProps) => {
|
|||||||
'dark-lg',
|
'dark-lg',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const shift = useAppSelector((state) => state.hotkeys.shift);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
className={shift ? DRAG_HANDLE_CLASSNAME : 'nopan'}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
borderRadius: 'md',
|
borderRadius: 'md',
|
||||||
|
@ -21,6 +21,7 @@ const ProgressImageNode = (props: NodeProps<InvocationValue>) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
|
className="nopan"
|
||||||
sx={{
|
sx={{
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderBottomRadius: 'md',
|
borderBottomRadius: 'md',
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
NumberInputStepper,
|
NumberInputStepper,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { numberStringRegex } from 'common/components/IAINumberInput';
|
||||||
import { fieldValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import {
|
import {
|
||||||
FloatInputFieldTemplate,
|
FloatInputFieldTemplate,
|
||||||
@ -13,7 +14,7 @@ import {
|
|||||||
IntegerInputFieldTemplate,
|
IntegerInputFieldTemplate,
|
||||||
IntegerInputFieldValue,
|
IntegerInputFieldValue,
|
||||||
} from 'features/nodes/types/types';
|
} from 'features/nodes/types/types';
|
||||||
import { memo } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { FieldComponentProps } from './types';
|
import { FieldComponentProps } from './types';
|
||||||
|
|
||||||
const NumberInputFieldComponent = (
|
const NumberInputFieldComponent = (
|
||||||
@ -23,17 +24,42 @@ const NumberInputFieldComponent = (
|
|||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
const { nodeId, field } = props;
|
const { nodeId, field } = props;
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const [valueAsString, setValueAsString] = useState<string>(
|
||||||
|
String(field.value)
|
||||||
|
);
|
||||||
|
|
||||||
const handleValueChanged = (_: string, value: number) => {
|
const handleValueChanged = (v: string) => {
|
||||||
dispatch(fieldValueChanged({ nodeId, fieldName: field.name, value }));
|
setValueAsString(v);
|
||||||
|
// This allows negatives and decimals e.g. '-123', `.5`, `-0.2`, etc.
|
||||||
|
if (!v.match(numberStringRegex)) {
|
||||||
|
// Cast the value to number. Floor it if it should be an integer.
|
||||||
|
dispatch(
|
||||||
|
fieldValueChanged({
|
||||||
|
nodeId,
|
||||||
|
fieldName: field.name,
|
||||||
|
value:
|
||||||
|
props.template.type === 'integer'
|
||||||
|
? Math.floor(Number(v))
|
||||||
|
: Number(v),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!valueAsString.match(numberStringRegex) &&
|
||||||
|
field.value !== Number(valueAsString)
|
||||||
|
) {
|
||||||
|
setValueAsString(String(field.value));
|
||||||
|
}
|
||||||
|
}, [field.value, valueAsString]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NumberInput
|
<NumberInput
|
||||||
onChange={handleValueChanged}
|
onChange={handleValueChanged}
|
||||||
value={field.value}
|
value={valueAsString}
|
||||||
step={props.template.type === 'integer' ? 1 : 0.1}
|
step={props.template.type === 'integer' ? 1 : 0.1}
|
||||||
precision={props.template.type === 'integer' ? 0 : 3}
|
precision={props.template.type === 'integer' ? 0 : 3}
|
||||||
>
|
>
|
||||||
|
@ -18,6 +18,12 @@ const templatesSelector = createSelector(
|
|||||||
(nodes) => nodes.invocationTemplates
|
(nodes) => nodes.invocationTemplates
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const DRAG_HANDLE_CLASSNAME = 'node-drag-handle';
|
||||||
|
|
||||||
|
export const SHARED_NODE_PROPERTIES: Partial<Node> = {
|
||||||
|
dragHandle: `.${DRAG_HANDLE_CLASSNAME}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const useBuildInvocation = () => {
|
export const useBuildInvocation = () => {
|
||||||
const invocationTemplates = useAppSelector(templatesSelector);
|
const invocationTemplates = useAppSelector(templatesSelector);
|
||||||
|
|
||||||
@ -32,6 +38,7 @@ export const useBuildInvocation = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const node: Node = {
|
const node: Node = {
|
||||||
|
...SHARED_NODE_PROPERTIES,
|
||||||
id: 'progress_image',
|
id: 'progress_image',
|
||||||
type: 'progress_image',
|
type: 'progress_image',
|
||||||
position: { x: x, y: y },
|
position: { x: x, y: y },
|
||||||
@ -91,6 +98,7 @@ export const useBuildInvocation = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const invocation: Node<InvocationValue> = {
|
const invocation: Node<InvocationValue> = {
|
||||||
|
...SHARED_NODE_PROPERTIES,
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
type: 'invocation',
|
type: 'invocation',
|
||||||
position: { x: x, y: y },
|
position: { x: x, y: y },
|
||||||
|
Loading…
Reference in New Issue
Block a user