Skip to content

Commit a0684ff

Browse files
feat(ui): consolidate imagecontextmenu and send to menu
Both support the same actions: - Open in new tab - Copy image (if supported by browser) - Use prompt - Use seed - Use all - Send to img2img - Send to canvas - Change board - Download image - Delete
1 parent 9e77a7d commit a0684ff

File tree

4 files changed

+84
-97
lines changed

4 files changed

+84
-97
lines changed

invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import { createSelector } from '@reduxjs/toolkit';
22
import { isEqual } from 'lodash-es';
33

4-
import { ButtonGroup, Flex, FlexProps, Link } from '@chakra-ui/react';
4+
import {
5+
ButtonGroup,
6+
Flex,
7+
FlexProps,
8+
Link,
9+
Menu,
10+
MenuButton,
11+
MenuItem,
12+
MenuList,
13+
} from '@chakra-ui/react';
514
// import { runESRGAN, runFacetool } from 'app/socketio/actions';
615
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
716
import IAIButton from 'common/components/IAIButton';
@@ -49,6 +58,8 @@ import {
4958
} from 'services/api/endpoints/images';
5059
import { useDebounce } from 'use-debounce';
5160
import { sentImageToCanvas, sentImageToImg2Img } from '../../store/actions';
61+
import { menuListMotionProps } from 'theme/components/menu';
62+
import SingleSelectionMenuItems from '../ImageContextMenu/SingleSelectionMenuItems';
5263

5364
const currentImageButtonsSelector = createSelector(
5465
[stateSelector, activeTabNameSelector],
@@ -345,65 +356,18 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
345356
{...props}
346357
>
347358
<ButtonGroup isAttached={true} isDisabled={shouldDisableToolbarButtons}>
348-
<IAIPopover
349-
triggerComponent={
350-
<IAIIconButton
351-
aria-label={`${t('parameters.sendTo')}...`}
352-
tooltip={`${t('parameters.sendTo')}...`}
353-
isDisabled={!imageDTO}
354-
icon={<FaShareAlt />}
355-
/>
356-
}
357-
>
358-
<Flex
359-
sx={{
360-
flexDirection: 'column',
361-
rowGap: 2,
362-
}}
363-
>
364-
<IAIButton
365-
size="sm"
366-
onClick={handleSendToImageToImage}
367-
leftIcon={<FaShare />}
368-
id="send-to-img2img"
369-
>
370-
{t('parameters.sendToImg2Img')}
371-
</IAIButton>
372-
{isCanvasEnabled && (
373-
<IAIButton
374-
size="sm"
375-
onClick={handleSendToCanvas}
376-
leftIcon={<FaShare />}
377-
id="send-to-canvas"
378-
>
379-
{t('parameters.sendToUnifiedCanvas')}
380-
</IAIButton>
381-
)}
382-
383-
{isClipboardAPIAvailable && (
384-
<IAIButton
385-
size="sm"
386-
onClick={handleCopyImage}
387-
leftIcon={<FaCopy />}
388-
>
389-
{t('parameters.copyImage')}
390-
</IAIButton>
391-
)}
392-
<IAIButton
393-
size="sm"
394-
onClick={handleCopyImageLink}
395-
leftIcon={<FaCopy />}
396-
>
397-
{t('parameters.copyImageToLink')}
398-
</IAIButton>
399-
400-
<Link download={true} href={imageDTO?.image_url} target="_blank">
401-
<IAIButton leftIcon={<FaDownload />} size="sm" w="100%">
402-
{t('parameters.downloadImage')}
403-
</IAIButton>
404-
</Link>
405-
</Flex>
406-
</IAIPopover>
359+
<Menu>
360+
<MenuButton
361+
as={IAIIconButton}
362+
aria-label={`${t('parameters.sendTo')}...`}
363+
tooltip={`${t('parameters.sendTo')}...`}
364+
isDisabled={!imageDTO}
365+
icon={<FaShareAlt />}
366+
/>
367+
<MenuList motionProps={menuListMotionProps}>
368+
{imageDTO && <SingleSelectionMenuItems imageDTO={imageDTO} />}
369+
</MenuList>
370+
</Menu>
407371
</ButtonGroup>
408372

409373
<ButtonGroup isAttached={true} isDisabled={shouldDisableToolbarButtons}>

invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,15 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
66
import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu';
77
import { MouseEvent, memo, useCallback, useMemo } from 'react';
88
import { ImageDTO } from 'services/api/types';
9+
import { menuListMotionProps } from 'theme/components/menu';
910
import MultipleSelectionMenuItems from './MultipleSelectionMenuItems';
1011
import SingleSelectionMenuItems from './SingleSelectionMenuItems';
11-
import { MotionProps } from 'framer-motion';
1212

1313
type Props = {
1414
imageDTO: ImageDTO | undefined;
1515
children: ContextMenuProps<HTMLDivElement>['children'];
1616
};
1717

18-
const motionProps: MotionProps = {
19-
variants: {
20-
enter: {
21-
visibility: 'visible',
22-
opacity: 1,
23-
scale: 1,
24-
transition: {
25-
duration: 0.07,
26-
ease: [0.4, 0, 0.2, 1],
27-
},
28-
},
29-
exit: {
30-
transitionEnd: {
31-
visibility: 'hidden',
32-
},
33-
opacity: 0,
34-
scale: 0.8,
35-
transition: {
36-
duration: 0.07,
37-
easings: 'easeOut',
38-
},
39-
},
40-
},
41-
};
42-
4318
const ImageContextMenu = ({ imageDTO, children }: Props) => {
4419
const selector = useMemo(
4520
() =>
@@ -72,7 +47,7 @@ const ImageContextMenu = ({ imageDTO, children }: Props) => {
7247
imageDTO ? (
7348
<MenuList
7449
sx={{ visibility: 'visible !important' }}
75-
motionProps={motionProps}
50+
motionProps={menuListMotionProps}
7651
onContextMenu={handleContextMenu}
7752
>
7853
{selectionCount === 1 ? (

invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { ExternalLinkIcon } from '@chakra-ui/icons';
2-
import { MenuItem } from '@chakra-ui/react';
1+
import { Link, MenuItem } from '@chakra-ui/react';
32
import { createSelector } from '@reduxjs/toolkit';
43
import { useAppToaster } from 'app/components/Toaster';
54
import { stateSelector } from 'app/store/store';
@@ -18,8 +17,17 @@ import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboa
1817
import { setActiveTab } from 'features/ui/store/uiSlice';
1918
import { memo, useCallback, useContext, useMemo } from 'react';
2019
import { useTranslation } from 'react-i18next';
21-
import { FaCopy, FaFolder, FaShare, FaTrash } from 'react-icons/fa';
22-
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
20+
import {
21+
FaAsterisk,
22+
FaCopy,
23+
FaDownload,
24+
FaExternalLinkAlt,
25+
FaFolder,
26+
FaQuoteRight,
27+
FaSeedling,
28+
FaShare,
29+
FaTrash,
30+
} from 'react-icons/fa';
2331
import { useRemoveImageFromBoardMutation } from 'services/api/endpoints/boardImages';
2432
import { useGetImageMetadataQuery } from 'services/api/endpoints/images';
2533
import { ImageDTO } from 'services/api/types';
@@ -140,16 +148,21 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
140148

141149
return (
142150
<>
143-
<MenuItem icon={<ExternalLinkIcon />} onClickCapture={handleOpenInNewTab}>
144-
{t('common.openInNewTab')}
145-
</MenuItem>
151+
<Link href={imageDTO.image_url} target="_blank">
152+
<MenuItem
153+
icon={<FaExternalLinkAlt />}
154+
onClickCapture={handleOpenInNewTab}
155+
>
156+
{t('common.openInNewTab')}
157+
</MenuItem>
158+
</Link>
146159
{isClipboardAPIAvailable && (
147160
<MenuItem icon={<FaCopy />} onClickCapture={handleCopyImage}>
148161
{t('parameters.copyImage')}
149162
</MenuItem>
150163
)}
151164
<MenuItem
152-
icon={<IoArrowUndoCircleOutline />}
165+
icon={<FaQuoteRight />}
153166
onClickCapture={handleRecallPrompt}
154167
isDisabled={
155168
metadata?.positive_prompt === undefined &&
@@ -160,14 +173,14 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
160173
</MenuItem>
161174

162175
<MenuItem
163-
icon={<IoArrowUndoCircleOutline />}
176+
icon={<FaSeedling />}
164177
onClickCapture={handleRecallSeed}
165178
isDisabled={metadata?.seed === undefined}
166179
>
167180
{t('parameters.useSeed')}
168181
</MenuItem>
169182
<MenuItem
170-
icon={<IoArrowUndoCircleOutline />}
183+
icon={<FaAsterisk />}
171184
onClickCapture={handleUseAllParameters}
172185
isDisabled={!metadata}
173186
>
@@ -206,6 +219,11 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
206219
Remove from Board
207220
</MenuItem>
208221
)}
222+
<Link download={true} href={imageDTO.image_url} target="_blank">
223+
<MenuItem icon={<FaDownload />} w="100%">
224+
{t('parameters.downloadImage')}
225+
</MenuItem>
226+
</Link>
209227
<MenuItem
210228
sx={{ color: 'error.600', _dark: { color: 'error.300' } }}
211229
icon={<FaTrash />}

invokeai/frontend/web/src/theme/components/menu.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { menuAnatomy } from '@chakra-ui/anatomy';
22
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
33
import { mode } from '@chakra-ui/theme-tools';
4+
import { MotionProps } from 'framer-motion';
45

56
const { definePartsStyle, defineMultiStyleConfig } =
67
createMultiStyleConfigHelpers(menuAnatomy.keys);
@@ -21,6 +22,7 @@ const invokeAI = definePartsStyle((props) => ({
2122
},
2223
list: {
2324
zIndex: 9999,
25+
color: mode('base.900', 'base.150')(props),
2426
bg: mode('base.200', 'base.800')(props),
2527
shadow: 'dark-lg',
2628
border: 'none',
@@ -35,6 +37,9 @@ const invokeAI = definePartsStyle((props) => ({
3537
_focus: {
3638
bg: mode('base.400', 'base.600')(props),
3739
},
40+
svg: {
41+
opacity: 0.5,
42+
},
3843
},
3944
}));
4045

@@ -46,3 +51,28 @@ export const menuTheme = defineMultiStyleConfig({
4651
variant: 'invokeAI',
4752
},
4853
});
54+
55+
export const menuListMotionProps: MotionProps = {
56+
variants: {
57+
enter: {
58+
visibility: 'visible',
59+
opacity: 1,
60+
scale: 1,
61+
transition: {
62+
duration: 0.07,
63+
ease: [0.4, 0, 0.2, 1],
64+
},
65+
},
66+
exit: {
67+
transitionEnd: {
68+
visibility: 'hidden',
69+
},
70+
opacity: 0,
71+
scale: 0.8,
72+
transition: {
73+
duration: 0.07,
74+
easings: 'easeOut',
75+
},
76+
},
77+
},
78+
};

0 commit comments

Comments
 (0)