added button on hover for exposing fields to linear workflow ui

This commit is contained in:
Jennifer Player 2024-02-08 12:24:19 -05:00 committed by psychedelicious
parent 6c5f743e2b
commit 2ce70b4457
2 changed files with 97 additions and 10 deletions

View File

@ -0,0 +1,74 @@
import { IconButton } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
selectWorkflowSlice,
workflowExposedFieldAdded,
workflowExposedFieldRemoved,
} from 'features/nodes/store/workflowSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiMinusBold, PiPlusBold } from 'react-icons/pi';
type Props = {
nodeId: string;
fieldName: string;
isHovered: boolean;
};
const FieldLinearViewToggle = ({ nodeId, fieldName, isHovered }: Props) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const selectIsExposed = useMemo(
() =>
createSelector(selectWorkflowSlice, (workflow) => {
return Boolean(workflow.exposedFields.find((f) => f.nodeId === nodeId && f.fieldName === fieldName));
}),
[fieldName, nodeId]
);
const isExposed = useAppSelector(selectIsExposed);
const handleExposeField = useCallback(() => {
dispatch(workflowExposedFieldAdded({ nodeId, fieldName }));
}, [dispatch, fieldName, nodeId]);
const handleUnexposeField = useCallback(() => {
dispatch(workflowExposedFieldRemoved({ nodeId, fieldName }));
}, [dispatch, fieldName, nodeId]);
const ToggleButton = useMemo(() => {
if (!isHovered) {
return null;
} else if (!isExposed) {
return (
<IconButton
mx="2"
tooltip={t('nodes.addLinearView')}
aria-label={t('nodes.addLinearView')}
icon={<PiPlusBold />}
onClick={handleExposeField}
pointerEvents="auto"
size="xs"
/>
);
} else if (isExposed) {
return (
<IconButton
mx="2"
tooltip={t('nodes.removeLinearView')}
aria-label={t('nodes.removeLinearView')}
icon={<PiMinusBold />}
onClick={handleUnexposeField}
pointerEvents="auto"
size="xs"
/>
);
}
}, [isHovered, handleExposeField, handleUnexposeField, isExposed, t]);
return ToggleButton;
};
export default memo(FieldLinearViewToggle);

View File

@ -4,12 +4,13 @@ import { useDoesInputHaveValue } from 'features/nodes/hooks/useDoesInputHaveValu
import { useFieldInputInstance } from 'features/nodes/hooks/useFieldInputInstance';
import { useFieldInputTemplate } from 'features/nodes/hooks/useFieldInputTemplate';
import type { PropsWithChildren } from 'react';
import { memo, useMemo } from 'react';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import EditableFieldTitle from './EditableFieldTitle';
import FieldContextMenu from './FieldContextMenu';
import FieldHandle from './FieldHandle';
import FieldLinearViewToggle from './FieldLinearViewToggle';
import InputFieldRenderer from './InputFieldRenderer';
interface Props {
@ -22,6 +23,7 @@ const InputField = ({ nodeId, fieldName }: Props) => {
const fieldTemplate = useFieldInputTemplate(nodeId, fieldName);
const fieldInstance = useFieldInputInstance(nodeId, fieldName);
const doesFieldHaveValue = useDoesInputHaveValue(nodeId, fieldName);
const [isHovered, setIsHovered] = useState(false);
const { isConnected, isConnectionInProgress, isConnectionStartField, connectionError, shouldDim } =
useConnectionState({ nodeId, fieldName, kind: 'input' });
@ -46,6 +48,14 @@ const InputField = ({ nodeId, fieldName }: Props) => {
return false;
}, [fieldTemplate, isConnected, doesFieldHaveValue]);
const handleMouseOver = useCallback(() => {
setIsHovered(true);
}, []);
const handleMouseOut = useCallback(() => {
setIsHovered(false);
}, []);
if (!fieldTemplate || !fieldInstance) {
return (
<InputFieldWrapper shouldDim={shouldDim}>
@ -87,17 +97,20 @@ const InputField = ({ nodeId, fieldName }: Props) => {
return (
<InputFieldWrapper shouldDim={shouldDim}>
<FormControl isInvalid={isMissingInput} isDisabled={isConnected} orientation="vertical" px={2}>
<Flex flexDir="column" w="full" gap={1}>
<Flex flexDir="column" w="full" gap={1} onMouseEnter={handleMouseOver} onMouseLeave={handleMouseOut}>
<FieldContextMenu nodeId={nodeId} fieldName={fieldName} kind="input">
{(ref) => (
<EditableFieldTitle
ref={ref}
nodeId={nodeId}
fieldName={fieldName}
kind="input"
isMissingInput={isMissingInput}
withTooltip
/>
<Flex>
<EditableFieldTitle
ref={ref}
nodeId={nodeId}
fieldName={fieldName}
kind="input"
isMissingInput={isMissingInput}
withTooltip
/>
<FieldLinearViewToggle nodeId={nodeId} fieldName={fieldName} isHovered={isHovered} />
</Flex>
)}
</FieldContextMenu>
<InputFieldRenderer nodeId={nodeId} fieldName={fieldName} />