Skip to content

Commit 3b0d7f0

Browse files
tidy(ui): rename from "editor" to "cropper", minor cleanup
1 parent e1acbcd commit 3b0d7f0

File tree

7 files changed

+73
-65
lines changed

7 files changed

+73
-65
lines changed

invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { GlobalImageHotkeys } from 'app/components/GlobalImageHotkeys';
22
import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardModal';
33
import { CanvasPasteModal } from 'features/controlLayers/components/CanvasPasteModal';
44
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
5+
import { CropImageModal } from 'features/cropper/components/CropImageModal';
56
import { DeleteImageModal } from 'features/deleteImageModal/components/DeleteImageModal';
67
import { DeleteVideoModal } from 'features/deleteVideoModal/components/DeleteVideoModal';
78
import { FullscreenDropzone } from 'features/dnd/FullscreenDropzone';
89
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
9-
import { EditImageModal } from 'features/editImageModal/components/EditImageModal';
1010
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
1111
import { ImageContextMenu } from 'features/gallery/components/ContextMenu/ImageContextMenu';
1212
import { VideoContextMenu } from 'features/gallery/components/ContextMenu/VideoContextMenu';
@@ -59,7 +59,7 @@ export const GlobalModalIsolator = memo(() => {
5959
<CanvasPasteModal />
6060
</CanvasManagerProviderGate>
6161
<LoadWorkflowFromGraphModal />
62-
<EditImageModal />
62+
<CropImageModal />
6363
</>
6464
);
6565
});

invokeai/frontend/web/src/features/editImageModal/components/EditorContainer.tsx renamed to invokeai/frontend/web/src/features/cropper/components/CropImageEditor.tsx

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,35 @@ import {
1010
Text,
1111
} from '@invoke-ai/ui-library';
1212
import { useAppSelector } from 'app/store/storeHooks';
13-
import type { CropBox } from 'features/editImageModal/lib/editor';
14-
import { closeEditImageModal, type EditImageModalState } from 'features/editImageModal/store';
13+
import type { AspectRatioID } from 'features/controlLayers/store/types';
14+
import { ASPECT_RATIO_MAP, isAspectRatioID } from 'features/controlLayers/store/types';
15+
import type { CropBox } from 'features/cropper/lib/editor';
16+
import { cropImageModalApi, type CropImageModalState } from 'features/cropper/store';
1517
import { selectAutoAddBoardId } from 'features/gallery/store/gallerySelectors';
16-
import React, { useCallback, useEffect, useRef, useState } from 'react';
18+
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
1719
import { useUploadImageMutation } from 'services/api/endpoints/images';
20+
import { objectEntries } from 'tsafe';
1821

1922
type Props = {
20-
editor: EditImageModalState['editor'];
21-
onApplyCrop: EditImageModalState['onApplyCrop'];
22-
onReady: EditImageModalState['onReady'];
23+
editor: CropImageModalState['editor'];
24+
onApplyCrop: CropImageModalState['onApplyCrop'];
25+
onReady: CropImageModalState['onReady'];
2326
};
2427

25-
const CROP_ASPECT_RATIO_MAP: Record<string, number> = {
26-
'16:9': 16 / 9,
27-
'3:2': 3 / 2,
28-
'4:3': 4 / 3,
29-
'1:1': 1,
30-
'3:4': 3 / 4,
31-
'2:3': 2 / 3,
32-
'9:16': 9 / 16,
33-
};
34-
35-
const getAspectRatioString = (ratio: number | null) => {
28+
const getAspectRatioString = (ratio: number | null): AspectRatioID => {
3629
if (!ratio) {
37-
return 'free';
30+
return 'Free';
3831
}
39-
const entries = Object.entries(CROP_ASPECT_RATIO_MAP);
32+
const entries = objectEntries(ASPECT_RATIO_MAP);
4033
for (const [key, value] of entries) {
41-
if (value === ratio) {
34+
if (value.ratio === ratio) {
4235
return key;
4336
}
4437
}
45-
return 'free';
38+
return 'Free';
4639
};
4740

48-
export const EditorContainer = ({ editor, onApplyCrop, onReady }: Props) => {
41+
export const CropImageEditor = memo(({ editor, onApplyCrop, onReady }: Props) => {
4942
const containerRef = useRef<HTMLDivElement>(null);
5043
const [zoom, setZoom] = useState(100);
5144
const [cropBox, setCropBox] = useState<CropBox | null>(null);
@@ -90,12 +83,15 @@ export const EditorContainer = ({ editor, onApplyCrop, onReady }: Props) => {
9083
const handleAspectRatioChange = useCallback(
9184
(e: React.ChangeEvent<HTMLSelectElement>) => {
9285
const newRatio = e.target.value;
86+
if (!isAspectRatioID(newRatio)) {
87+
return;
88+
}
9389
setAspectRatio(newRatio);
9490

95-
if (newRatio === 'free') {
91+
if (newRatio === 'Free') {
9692
editor.setCropAspectRatio(null);
9793
} else {
98-
editor.setCropAspectRatio(CROP_ASPECT_RATIO_MAP[newRatio] ?? null);
94+
editor.setCropAspectRatio(ASPECT_RATIO_MAP[newRatio]?.ratio ?? null);
9995
}
10096
},
10197
[editor]
@@ -107,11 +103,11 @@ export const EditorContainer = ({ editor, onApplyCrop, onReady }: Props) => {
107103

108104
const handleApplyCrop = useCallback(async () => {
109105
await onApplyCrop();
110-
closeEditImageModal();
106+
cropImageModalApi.close();
111107
}, [onApplyCrop]);
112108

113109
const handleCancelCrop = useCallback(() => {
114-
closeEditImageModal();
110+
cropImageModalApi.close();
115111
}, []);
116112

117113
const handleExport = useCallback(async () => {
@@ -157,15 +153,15 @@ export const EditorContainer = ({ editor, onApplyCrop, onReady }: Props) => {
157153
<Flex gap={2} alignItems="center">
158154
<FormControl flex={1}>
159155
<FormLabel>Aspect Ratio:</FormLabel>
160-
<Select size="sm" value={aspectRatio} onChange={handleAspectRatioChange} w={64}>
161-
<option value="free">Free</option>
156+
<Select size="sm" value={aspectRatio} onChange={handleAspectRatioChange} w={32}>
157+
<option value="Free">Free</option>
162158
<option value="16:9">16:9</option>
163159
<option value="3:2">3:2</option>
164160
<option value="4:3">4:3</option>
165-
<option value="1:1">1:1 (Square)</option>
161+
<option value="1:1">1:1</option>
166162
<option value="3:4">3:4</option>
167-
<option value="2:3">2:3 (Portrait)</option>
168-
<option value="9:16">9:16 (Portrait)</option>
163+
<option value="2:3">2:3</option>
164+
<option value="9:16">9:16</option>
169165
</Select>
170166
</FormControl>
171167

@@ -184,7 +180,7 @@ export const EditorContainer = ({ editor, onApplyCrop, onReady }: Props) => {
184180
<Button onClick={handleApplyCrop}>Apply</Button>
185181
<Button onClick={handleResetCrop}>Reset</Button>
186182
<Button onClick={handleCancelCrop}>Cancel</Button>
187-
<Button onClick={handleExport}>Export</Button>
183+
<Button onClick={handleExport}>Save to Assets</Button>
188184
</ButtonGroup>
189185
</Flex>
190186

@@ -212,4 +208,6 @@ export const EditorContainer = ({ editor, onApplyCrop, onReady }: Props) => {
212208
</Flex>
213209
</Flex>
214210
);
215-
};
211+
});
212+
213+
CropImageEditor.displayName = 'CropImageEditor';
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import { Modal, ModalBody, ModalContent, ModalHeader, ModalOverlay } from '@invoke-ai/ui-library';
22
import { useStore } from '@nanostores/react';
3-
import { $editImageModalState, closeEditImageModal } from 'features/editImageModal/store';
3+
import { cropImageModalApi } from 'features/cropper/store';
4+
import { memo } from 'react';
45

5-
import { EditorContainer } from './EditorContainer';
6+
import { CropImageEditor } from './CropImageEditor';
67

7-
export const EditImageModal = () => {
8-
const state = useStore($editImageModalState);
8+
export const CropImageModal = memo(() => {
9+
const state = useStore(cropImageModalApi.$state);
910

1011
if (!state) {
1112
return null;
1213
}
1314

1415
return (
15-
<Modal isOpen={true} onClose={closeEditImageModal} isCentered useInert={false} size="full">
16+
// This modal is always open when this component is rendered
17+
<Modal isOpen={true} onClose={cropImageModalApi.close} isCentered useInert={false} size="full">
1618
<ModalOverlay />
1719
<ModalContent minH="unset" minW="unset" maxH="90vh" maxW="90vw" w="full" h="full" borderRadius="base">
1820
<ModalHeader>Crop Image</ModalHeader>
1921
<ModalBody px={4} pb={4} pt={0}>
20-
<EditorContainer editor={state.editor} onApplyCrop={state.onApplyCrop} onReady={state.onReady} />
22+
<CropImageEditor editor={state.editor} onApplyCrop={state.onApplyCrop} onReady={state.onReady} />
2123
</ModalBody>
2224
</ModalContent>
2325
</Modal>
2426
);
25-
};
27+
});
28+
29+
CropImageModal.displayName = 'CropImageModal';
File renamed without changes.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Editor } from 'features/cropper/lib/editor';
2+
import { atom } from 'nanostores';
3+
4+
export type CropImageModalState = {
5+
editor: Editor;
6+
onApplyCrop: () => Promise<void> | void;
7+
onReady: () => Promise<void> | void;
8+
};
9+
10+
const $state = atom<CropImageModalState | null>(null);
11+
12+
const open = (state: CropImageModalState) => {
13+
$state.set(state);
14+
};
15+
16+
const close = () => {
17+
const state = $state.get();
18+
state?.editor.destroy();
19+
$state.set(null);
20+
};
21+
22+
export const cropImageModalApi = {
23+
$state,
24+
open,
25+
close,
26+
};

invokeai/frontend/web/src/features/editImageModal/store/index.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

invokeai/frontend/web/src/features/settingsAccordions/components/VideoSettingsAccordion/StartingFrameImage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
44
import { UploadImageIconButton } from 'common/hooks/useImageUploadButton';
55
import { ASPECT_RATIO_MAP } from 'features/controlLayers/store/types';
66
import { imageDTOToCroppableImage, imageDTOToImageWithDims } from 'features/controlLayers/store/util';
7+
import { Editor } from 'features/cropper/lib/editor';
8+
import { cropImageModalApi } from 'features/cropper/store';
79
import { videoFrameFromImageDndTarget } from 'features/dnd/dnd';
810
import { DndDropTarget } from 'features/dnd/DndDropTarget';
911
import { DndImage } from 'features/dnd/DndImage';
1012
import { DndImageIcon, imageButtonSx } from 'features/dnd/DndImageIcon';
11-
import { Editor } from 'features/editImageModal/lib/editor';
12-
import { openEditImageModal } from 'features/editImageModal/store';
1313
import {
1414
selectStartingFrameImage,
1515
selectVideoAspectRatio,
@@ -93,7 +93,7 @@ export const StartingFrameImage = () => {
9393
await editor.loadImage(originalImageDTO.image_url, initial);
9494
};
9595

96-
openEditImageModal({ editor, onApplyCrop, onReady });
96+
cropImageModalApi.open({ editor, onApplyCrop, onReady });
9797
}, [dispatch, originalImageDTO, startingFrameImage?.crop, uploadImage]);
9898

9999
const fitsCurrentAspectRatio = useMemo(() => {

0 commit comments

Comments
 (0)