11import type { SystemStyleObject } from '@invoke-ai/ui-library' ;
2- import { Flex , Grid , GridItem , IconButton } from '@invoke-ai/ui-library' ;
3- import { useAppDispatch } from 'app/store/storeHooks' ;
2+ import { Flex , Grid , GridItem } from '@invoke-ai/ui-library' ;
3+ import { useAppStore } from 'app/store/nanostores/store' ;
4+ import { IAINoContentFallback , IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback' ;
45import { UploadMultipleImageButton } from 'common/hooks/useImageUploadButton' ;
56import type { AddImagesToNodeImageFieldCollection } from 'features/dnd/dnd' ;
67import { addImagesToNodeImageFieldCollectionDndTarget } from 'features/dnd/dnd' ;
78import { DndDropTarget } from 'features/dnd/DndDropTarget' ;
8- import { DndImageFromImageName } from 'features/dnd/DndImageFromImageName' ;
9+ import { DndImage } from 'features/dnd/DndImage' ;
10+ import { DndImageIcon } from 'features/dnd/DndImageIcon' ;
11+ import { removeImageFromNodeImageFieldCollectionAction } from 'features/imageActions/actions' ;
912import { useFieldIsInvalid } from 'features/nodes/hooks/useFieldIsInvalid' ;
1013import { fieldImageCollectionValueChanged } from 'features/nodes/store/nodesSlice' ;
1114import type { ImageFieldCollectionInputInstance , ImageFieldCollectionInputTemplate } from 'features/nodes/types/field' ;
1215import { memo , useCallback , useMemo } from 'react' ;
1316import { useTranslation } from 'react-i18next' ;
14- import { PiArrowCounterClockwiseBold } from 'react-icons/pi' ;
17+ import { PiArrowCounterClockwiseBold , PiExclamationMarkBold } from 'react-icons/pi' ;
18+ import { useGetImageDTOQuery } from 'services/api/endpoints/images' ;
1519import type { ImageDTO } from 'services/api/types' ;
1620
1721import type { FieldComponentProps } from './types' ;
1822
1923const sx = {
24+ borderWidth : 1 ,
2025 '&[data-error=true]' : {
2126 borderColor : 'error.500' ,
2227 borderStyle : 'solid' ,
23- borderWidth : 1 ,
2428 } ,
2529} satisfies SystemStyleObject ;
2630
2731export const ImageFieldCollectionInputComponent = memo (
2832 ( props : FieldComponentProps < ImageFieldCollectionInputInstance , ImageFieldCollectionInputTemplate > ) => {
2933 const { t } = useTranslation ( ) ;
3034 const { nodeId, field } = props ;
31- const dispatch = useAppDispatch ( ) ;
32- const isInvalid = useFieldIsInvalid ( nodeId , field . name ) ;
35+ const store = useAppStore ( ) ;
3336
34- const onReset = useCallback ( ( ) => {
35- dispatch (
36- fieldImageCollectionValueChanged ( {
37- nodeId,
38- fieldName : field . name ,
39- value : [ ] ,
40- } )
41- ) ;
42- } , [ dispatch , field . name , nodeId ] ) ;
37+ const isInvalid = useFieldIsInvalid ( nodeId , field . name ) ;
4338
4439 const dndTargetData = useMemo < AddImagesToNodeImageFieldCollection > (
45- ( ) => addImagesToNodeImageFieldCollectionDndTarget . getData ( { fieldIdentifer : { nodeId, fieldName : field . name } } ) ,
40+ ( ) =>
41+ addImagesToNodeImageFieldCollectionDndTarget . getData ( { fieldIdentifier : { nodeId, fieldName : field . name } } ) ,
4642 [ field , nodeId ]
4743 ) ;
4844
4945 const onUpload = useCallback (
5046 ( imageDTOs : ImageDTO [ ] ) => {
51- dispatch (
47+ store . dispatch (
5248 fieldImageCollectionValueChanged ( {
5349 nodeId,
5450 fieldName : field . name ,
5551 value : imageDTOs ,
5652 } )
5753 ) ;
5854 } ,
59- [ dispatch , field . name , nodeId ]
55+ [ store , nodeId , field . name ]
56+ ) ;
57+
58+ const onRemoveImage = useCallback (
59+ ( imageName : string ) => {
60+ removeImageFromNodeImageFieldCollectionAction ( {
61+ imageName,
62+ fieldIdentifier : { nodeId, fieldName : field . name } ,
63+ dispatch : store . dispatch ,
64+ getState : store . getState ,
65+ } ) ;
66+ } ,
67+ [ field . name , nodeId , store . dispatch , store . getState ]
6068 ) ;
6169
6270 return (
@@ -80,33 +88,23 @@ export const ImageFieldCollectionInputComponent = memo(
8088 />
8189 ) }
8290 { field . value && field . value . length > 0 && (
83- < >
84- < Grid
85- className = "nopan"
86- borderRadius = "base"
87- w = "full"
88- h = "full"
89- templateColumns = { `repeat(${ Math . min ( field . value . length , 3 ) } , 1fr)` }
90- gap = { 1 }
91- sx = { sx }
92- data-error = { isInvalid }
93- p = { 1 }
94- >
95- { field . value . map ( ( { image_name } ) => (
96- < GridItem key = { image_name } >
97- < DndImageFromImageName imageName = { image_name } asThumbnail />
98- </ GridItem >
99- ) ) }
100- </ Grid >
101- < IconButton
102- aria-label = "reset"
103- icon = { < PiArrowCounterClockwiseBold /> }
104- position = "absolute"
105- top = { 0 }
106- insetInlineEnd = { 0 }
107- onClick = { onReset }
108- />
109- </ >
91+ < Grid
92+ className = "nopan"
93+ borderRadius = "base"
94+ w = "full"
95+ h = "full"
96+ templateColumns = "repeat(3, 1fr)"
97+ gap = { 1 }
98+ sx = { sx }
99+ data-error = { isInvalid }
100+ p = { 1 }
101+ >
102+ { field . value . map ( ( { image_name } ) => (
103+ < GridItem key = { image_name } position = "relative" >
104+ < ImageGridItemContent imageName = { image_name } onRemoveImage = { onRemoveImage } />
105+ </ GridItem >
106+ ) ) }
107+ </ Grid >
110108 ) }
111109 < DndDropTarget
112110 dndTarget = { addImagesToNodeImageFieldCollectionDndTarget }
@@ -119,3 +117,37 @@ export const ImageFieldCollectionInputComponent = memo(
119117) ;
120118
121119ImageFieldCollectionInputComponent . displayName = 'ImageFieldCollectionInputComponent' ;
120+
121+ const ImageGridItemContent = memo (
122+ ( { imageName, onRemoveImage } : { imageName : string ; onRemoveImage : ( imageName : string ) => void } ) => {
123+ const query = useGetImageDTOQuery ( imageName ) ;
124+ const onClickRemove = useCallback ( ( ) => {
125+ onRemoveImage ( imageName ) ;
126+ } , [ imageName , onRemoveImage ] ) ;
127+
128+ if ( query . isLoading ) {
129+ return < IAINoContentFallbackWithSpinner /> ;
130+ }
131+
132+ if ( ! query . data ) {
133+ return < IAINoContentFallback icon = { < PiExclamationMarkBold /> } /> ;
134+ }
135+
136+ return (
137+ < >
138+ < DndImage imageDTO = { query . data } asThumbnail />
139+ < DndImageIcon
140+ onClick = { onClickRemove }
141+ icon = { < PiArrowCounterClockwiseBold /> }
142+ tooltip = "Reset Image"
143+ position = "absolute"
144+ flexDir = "column"
145+ top = { 1 }
146+ insetInlineEnd = { 1 }
147+ gap = { 1 }
148+ />
149+ </ >
150+ ) ;
151+ }
152+ ) ;
153+ ImageGridItemContent . displayName = 'ImageGridItemContent' ;
0 commit comments