addressed feedback

This commit is contained in:
Jennifer Player 2024-02-08 17:31:56 -05:00 committed by psychedelicious
parent 2ce70b4457
commit 5de2288cfa
3 changed files with 39 additions and 136 deletions

View File

@ -1,84 +0,0 @@
import type { ContextMenuProps } from '@invoke-ai/ui-library';
import { ContextMenu, MenuGroup, MenuItem, MenuList } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useFieldInputKind } from 'features/nodes/hooks/useFieldInputKind';
import { useFieldLabel } from 'features/nodes/hooks/useFieldLabel';
import { useFieldTemplateTitle } from 'features/nodes/hooks/useFieldTemplateTitle';
import {
selectWorkflowSlice,
workflowExposedFieldAdded,
workflowExposedFieldRemoved,
} from 'features/nodes/store/workflowSlice';
import type { ReactNode } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiMinusBold, PiPlusBold } from 'react-icons/pi';
type Props = {
nodeId: string;
fieldName: string;
kind: 'input' | 'output';
children: ContextMenuProps<HTMLDivElement>['children'];
};
const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
const dispatch = useAppDispatch();
const label = useFieldLabel(nodeId, fieldName);
const fieldTemplateTitle = useFieldTemplateTitle(nodeId, fieldName, kind);
const input = useFieldInputKind(nodeId, fieldName);
const { t } = useTranslation();
const selectIsExposed = useMemo(
() =>
createSelector(selectWorkflowSlice, (workflow) => {
return Boolean(workflow.exposedFields.find((f) => f.nodeId === nodeId && f.fieldName === fieldName));
}),
[fieldName, nodeId]
);
const mayExpose = useMemo(() => input && ['any', 'direct'].includes(input), [input]);
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 menuItems = useMemo(() => {
const menuItems: ReactNode[] = [];
if (mayExpose && !isExposed) {
menuItems.push(
<MenuItem key={`${nodeId}.${fieldName}.expose-field`} icon={<PiPlusBold />} onClick={handleExposeField}>
{t('nodes.addLinearView')}
</MenuItem>
);
}
if (mayExpose && isExposed) {
menuItems.push(
<MenuItem key={`${nodeId}.${fieldName}.unexpose-field`} icon={<PiMinusBold />} onClick={handleUnexposeField}>
{t('nodes.removeLinearView')}
</MenuItem>
);
}
return menuItems;
}, [fieldName, handleExposeField, handleUnexposeField, isExposed, mayExpose, nodeId, t]);
const renderMenuFunc = useCallback(
() =>
!menuItems.length ? null : (
<MenuList visibility="visible">
<MenuGroup title={label || fieldTemplateTitle || t('nodes.unknownField')}>{menuItems}</MenuGroup>
</MenuList>
),
[fieldTemplateTitle, label, menuItems, t]
);
return <ContextMenu renderMenu={renderMenuFunc}>{children}</ContextMenu>;
};
export default memo(FieldContextMenu);

View File

@ -13,10 +13,9 @@ import { PiMinusBold, PiPlusBold } from 'react-icons/pi';
type Props = {
nodeId: string;
fieldName: string;
isHovered: boolean;
};
const FieldLinearViewToggle = ({ nodeId, fieldName, isHovered }: Props) => {
const FieldLinearViewToggle = ({ nodeId, fieldName }: Props) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
@ -38,37 +37,31 @@ const FieldLinearViewToggle = ({ nodeId, fieldName, isHovered }: Props) => {
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;
if (!isExposed) {
return (
<IconButton
variant="ghost"
tooltip={t('nodes.addLinearView')}
aria-label={t('nodes.addLinearView')}
icon={<PiPlusBold />}
onClick={handleExposeField}
pointerEvents="auto"
size="xs"
/>
);
} else {
return (
<IconButton
variant="ghost"
tooltip={t('nodes.removeLinearView')}
aria-label={t('nodes.removeLinearView')}
icon={<PiMinusBold />}
onClick={handleUnexposeField}
pointerEvents="auto"
size="xs"
/>
);
}
};
export default memo(FieldLinearViewToggle);

View File

@ -8,7 +8,6 @@ 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';
@ -48,11 +47,11 @@ const InputField = ({ nodeId, fieldName }: Props) => {
return false;
}, [fieldTemplate, isConnected, doesFieldHaveValue]);
const handleMouseOver = useCallback(() => {
const onMouseEnter = useCallback(() => {
setIsHovered(true);
}, []);
const handleMouseOut = useCallback(() => {
const onMouseLeave = useCallback(() => {
setIsHovered(false);
}, []);
@ -97,22 +96,17 @@ 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} onMouseEnter={handleMouseOver} onMouseLeave={handleMouseOut}>
<FieldContextMenu nodeId={nodeId} fieldName={fieldName} kind="input">
{(ref) => (
<Flex>
<EditableFieldTitle
ref={ref}
nodeId={nodeId}
fieldName={fieldName}
kind="input"
isMissingInput={isMissingInput}
withTooltip
/>
<FieldLinearViewToggle nodeId={nodeId} fieldName={fieldName} isHovered={isHovered} />
</Flex>
)}
</FieldContextMenu>
<Flex flexDir="column" w="full" gap={1} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
<Flex>
<EditableFieldTitle
nodeId={nodeId}
fieldName={fieldName}
kind="input"
isMissingInput={isMissingInput}
withTooltip
/>
{isHovered && <FieldLinearViewToggle nodeId={nodeId} fieldName={fieldName} />}
</Flex>
<InputFieldRenderer nodeId={nodeId} fieldName={fieldName} />
</Flex>
</FormControl>