Skip to content

Commit 21ab650

Browse files
psychedelicioushipsterusername
authored andcommitted
feat(ui): move canvas tool to nanostores
I was troubleshooting a hotkeys issue on canvas and thought I had broken the tool logic in a past change so I redid it moving it to nanostores. In the end, the issue was an upstream but with the hotkeys library, but I like having tool in nanostores so I'm leaving it. It's ephemeral interaction state anyways, doesn't need to be in redux.
1 parent b501bd7 commit 21ab650

File tree

13 files changed

+142
-158
lines changed

13 files changed

+142
-158
lines changed

invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
$isMouseOverBoundingBox,
1515
$isMovingStage,
1616
$isTransformingBoundingBox,
17+
$tool,
1718
} from 'features/canvas/store/canvasNanostore';
1819
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
1920
import {
@@ -61,7 +62,6 @@ const IAICanvas = () => {
6162
);
6263
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
6364
const stageScale = useAppSelector((s) => s.canvas.stageScale);
64-
const tool = useAppSelector((s) => s.canvas.tool);
6565
const shouldShowIntermediates = useAppSelector(
6666
(s) => s.canvas.shouldShowIntermediates
6767
);
@@ -78,6 +78,7 @@ const IAICanvas = () => {
7878
const isMovingStage = useStore($isMovingStage);
7979
const isTransformingBoundingBox = useStore($isTransformingBoundingBox);
8080
const isMouseOverBoundingBox = useStore($isMouseOverBoundingBox);
81+
const tool = useStore($tool);
8182
useCanvasHotkeys();
8283
const canvasStageRefCallback = useCallback((el: Konva.Stage) => {
8384
setCanvasStage(el as Konva.Stage);

invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasBoundingBox.tsx

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import {
88
} from 'common/util/roundDownToMultiple';
99
import {
1010
$isDrawing,
11+
$isMouseOverBoundingBox,
12+
$isMouseOverBoundingBoxOutline,
1113
$isMovingBoundingBox,
1214
$isTransformingBoundingBox,
13-
setIsMouseOverBoundingBox,
14-
setIsMovingBoundingBox,
15-
setIsTransformingBoundingBox,
15+
$tool,
1616
} from 'features/canvas/store/canvasNanostore';
1717
import {
1818
aspectRatioChanged,
@@ -30,7 +30,7 @@ import type Konva from 'konva';
3030
import type { GroupConfig } from 'konva/lib/Group';
3131
import type { KonvaEventObject } from 'konva/lib/Node';
3232
import type { Vector2d } from 'konva/lib/types';
33-
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
33+
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
3434
import { useHotkeys } from 'react-hotkeys-hook';
3535
import { Group, Rect, Transformer } from 'react-konva';
3636

@@ -49,18 +49,19 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
4949
);
5050
const stageScale = useAppSelector((s) => s.canvas.stageScale);
5151
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
52-
const tool = useAppSelector((s) => s.canvas.tool);
5352
const hitStrokeWidth = useAppSelector((s) => 20 / s.canvas.stageScale);
5453
const aspectRatio = useAppSelector((s) => s.canvas.aspectRatio);
5554
const optimalDimension = useAppSelector(selectOptimalDimension);
5655
const transformerRef = useRef<Konva.Transformer>(null);
5756
const shapeRef = useRef<Konva.Rect>(null);
5857
const shift = useStore($shift);
58+
const tool = useStore($tool);
5959
const isDrawing = useStore($isDrawing);
6060
const isMovingBoundingBox = useStore($isMovingBoundingBox);
6161
const isTransformingBoundingBox = useStore($isTransformingBoundingBox);
62-
const [isMouseOverBoundingBoxOutline, setIsMouseOverBoundingBoxOutline] =
63-
useState(false);
62+
const isMouseOverBoundingBoxOutline = useStore(
63+
$isMouseOverBoundingBoxOutline
64+
);
6465

6566
useEffect(() => {
6667
if (!transformerRef.current || !shapeRef.current) {
@@ -228,43 +229,43 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
228229
);
229230

230231
const handleStartedTransforming = useCallback(() => {
231-
setIsTransformingBoundingBox(true);
232+
$isTransformingBoundingBox.set(true);
232233
}, []);
233234

234235
const handleEndedTransforming = useCallback(() => {
235-
setIsTransformingBoundingBox(false);
236-
setIsMovingBoundingBox(false);
237-
setIsMouseOverBoundingBox(false);
238-
setIsMouseOverBoundingBoxOutline(false);
236+
$isTransformingBoundingBox.set(false);
237+
$isMovingBoundingBox.set(false);
238+
$isMouseOverBoundingBox.set(false);
239+
$isMouseOverBoundingBoxOutline.set(false);
239240
}, []);
240241

241242
const handleStartedMoving = useCallback(() => {
242-
setIsMovingBoundingBox(true);
243+
$isMovingBoundingBox.set(true);
243244
}, []);
244245

245246
const handleEndedModifying = useCallback(() => {
246-
setIsTransformingBoundingBox(false);
247-
setIsMovingBoundingBox(false);
248-
setIsMouseOverBoundingBox(false);
249-
setIsMouseOverBoundingBoxOutline(false);
247+
$isTransformingBoundingBox.set(false);
248+
$isMovingBoundingBox.set(false);
249+
$isMouseOverBoundingBox.set(false);
250+
$isMouseOverBoundingBoxOutline.set(false);
250251
}, []);
251252

252253
const handleMouseOver = useCallback(() => {
253-
setIsMouseOverBoundingBoxOutline(true);
254+
$isMouseOverBoundingBoxOutline.set(true);
254255
}, []);
255256

256257
const handleMouseOut = useCallback(() => {
257258
!isTransformingBoundingBox &&
258259
!isMovingBoundingBox &&
259-
setIsMouseOverBoundingBoxOutline(false);
260+
$isMouseOverBoundingBoxOutline.set(false);
260261
}, [isMovingBoundingBox, isTransformingBoundingBox]);
261262

262263
const handleMouseEnterBoundingBox = useCallback(() => {
263-
setIsMouseOverBoundingBox(true);
264+
$isMouseOverBoundingBox.set(true);
264265
}, []);
265266

266267
const handleMouseLeaveBoundingBox = useCallback(() => {
267-
setIsMouseOverBoundingBox(false);
268+
$isMouseOverBoundingBox.set(false);
268269
}, []);
269270

270271
const stroke = useMemo(() => {

invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Box, Flex } from '@chakra-ui/react';
2+
import { useStore } from '@nanostores/react';
23
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
34
import IAIColorPicker from 'common/components/IAIColorPicker';
45
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
@@ -10,14 +11,16 @@ import {
1011
InvPopoverTrigger,
1112
} from 'common/components/InvPopover/wrapper';
1213
import { InvSlider } from 'common/components/InvSlider/InvSlider';
13-
import { resetToolInteractionState } from 'features/canvas/store/canvasNanostore';
14+
import {
15+
$tool,
16+
resetToolInteractionState,
17+
} from 'features/canvas/store/canvasNanostore';
1418
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
1519
import {
1620
addEraseRect,
1721
addFillRect,
1822
setBrushColor,
1923
setBrushSize,
20-
setTool,
2124
} from 'features/canvas/store/canvasSlice';
2225
import { InvIconButton, InvPopover } from 'index';
2326
import { clamp } from 'lodash-es';
@@ -34,9 +37,11 @@ import {
3437
PiXBold,
3538
} from 'react-icons/pi';
3639

40+
const marks = [1, 25, 50, 75, 100];
41+
3742
const IAICanvasToolChooserOptions = () => {
3843
const dispatch = useAppDispatch();
39-
const tool = useAppSelector((s) => s.canvas.tool);
44+
const tool = useStore($tool);
4045
const brushColor = useAppSelector((s) => s.canvas.brushColor);
4146
const brushSize = useAppSelector((s) => s.canvas.brushSize);
4247
const isStaging = useAppSelector(isStagingSelector);
@@ -163,17 +168,17 @@ const IAICanvasToolChooserOptions = () => {
163168
);
164169

165170
const handleSelectBrushTool = useCallback(() => {
166-
dispatch(setTool('brush'));
171+
$tool.set('brush');
167172
resetToolInteractionState();
168-
}, [dispatch]);
173+
}, []);
169174
const handleSelectEraserTool = useCallback(() => {
170-
dispatch(setTool('eraser'));
175+
$tool.set('eraser');
171176
resetToolInteractionState();
172-
}, [dispatch]);
177+
}, []);
173178
const handleSelectColorPickerTool = useCallback(() => {
174-
dispatch(setTool('colorPicker'));
179+
$tool.set('colorPicker');
175180
resetToolInteractionState();
176-
}, [dispatch]);
181+
}, []);
177182
const handleFillRect = useCallback(() => {
178183
dispatch(addFillRect());
179184
}, [dispatch]);
@@ -281,5 +286,3 @@ const IAICanvasToolChooserOptions = () => {
281286
};
282287

283288
export default memo(IAICanvasToolChooserOptions);
284-
285-
const marks = [1, 25, 50, 75, 100];

invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Flex } from '@chakra-ui/react';
2+
import { useStore } from '@nanostores/react';
23
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
34
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
45
import { InvControl } from 'common/components/InvControl/InvControl';
@@ -14,13 +15,13 @@ import {
1415
canvasMerged,
1516
canvasSavedToGallery,
1617
} from 'features/canvas/store/actions';
18+
import { $tool } from 'features/canvas/store/canvasNanostore';
1719
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
1820
import {
1921
resetCanvas,
2022
resetCanvasView,
2123
setIsMaskEnabled,
2224
setLayer,
23-
setTool,
2425
} from 'features/canvas/store/canvasSlice';
2526
import type { CanvasLayer } from 'features/canvas/store/canvasTypes';
2627
import { LAYER_NAMES_DICT } from 'features/canvas/store/canvasTypes';
@@ -50,7 +51,7 @@ const IAICanvasToolbar = () => {
5051
const dispatch = useAppDispatch();
5152
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
5253
const layer = useAppSelector((s) => s.canvas.layer);
53-
const tool = useAppSelector((s) => s.canvas.tool);
54+
const tool = useStore($tool);
5455
const isStaging = useAppSelector(isStagingSelector);
5556
const canvasBaseLayer = getCanvasBaseLayer();
5657
const { t } = useTranslation();
@@ -133,8 +134,8 @@ const IAICanvasToolbar = () => {
133134
);
134135

135136
const handleSelectMoveTool = useCallback(() => {
136-
dispatch(setTool('move'));
137-
}, [dispatch]);
137+
$tool.set('move');
138+
}, []);
138139

139140
const handleClickResetCanvasView = useSingleAndDoubleClick(
140141
() => handleResetCanvasView(false),

invokeai/frontend/web/src/features/canvas/hooks/useCanvasDragMove.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { useStore } from '@nanostores/react';
21
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
32
import {
43
$isMovingBoundingBox,
5-
setIsMovingStage,
4+
$isMovingStage,
5+
$tool,
66
} from 'features/canvas/store/canvasNanostore';
77
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
88
import { setStageCoordinates } from 'features/canvas/store/canvasSlice';
@@ -12,34 +12,37 @@ import { useCallback } from 'react';
1212
const useCanvasDrag = () => {
1313
const dispatch = useAppDispatch();
1414
const isStaging = useAppSelector(isStagingSelector);
15-
const tool = useAppSelector((s) => s.canvas.tool);
16-
const isMovingBoundingBox = useStore($isMovingBoundingBox);
1715
const handleDragStart = useCallback(() => {
18-
if (!((tool === 'move' || isStaging) && !isMovingBoundingBox)) {
16+
if (
17+
!(($tool.get() === 'move' || isStaging) && !$isMovingBoundingBox.get())
18+
) {
1919
return;
2020
}
21-
setIsMovingStage(true);
22-
}, [isMovingBoundingBox, isStaging, tool]);
21+
$isMovingStage.set(true);
22+
}, [isStaging]);
2323

2424
const handleDragMove = useCallback(
2525
(e: KonvaEventObject<MouseEvent>) => {
26-
if (!((tool === 'move' || isStaging) && !isMovingBoundingBox)) {
26+
const tool = $tool.get();
27+
if (!((tool === 'move' || isStaging) && !$isMovingBoundingBox.get())) {
2728
return;
2829
}
2930

3031
const newCoordinates = { x: e.target.x(), y: e.target.y() };
3132

3233
dispatch(setStageCoordinates(newCoordinates));
3334
},
34-
[dispatch, isMovingBoundingBox, isStaging, tool]
35+
[dispatch, isStaging]
3536
);
3637

3738
const handleDragEnd = useCallback(() => {
38-
if (!((tool === 'move' || isStaging) && !isMovingBoundingBox)) {
39+
if (
40+
!(($tool.get() === 'move' || isStaging) && !$isMovingBoundingBox.get())
41+
) {
3942
return;
4043
}
41-
setIsMovingStage(false);
42-
}, [isMovingBoundingBox, isStaging, tool]);
44+
$isMovingStage.set(false);
45+
}, [isStaging]);
4346

4447
return {
4548
handleDragStart,

0 commit comments

Comments
 (0)