Skip to content

Commit ee83592

Browse files
feat(ui): more dnd cleanup and tidy
1 parent f0c80a8 commit ee83592

File tree

8 files changed

+75
-55
lines changed

8 files changed

+75
-55
lines changed

invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/CanvasEntityContainer.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
1+
import type { SystemStyleObject } from '@invoke-ai/ui-library';
12
import { Box, Flex } from '@invoke-ai/ui-library';
23
import { useAppDispatch } from 'app/store/storeHooks';
34
import { useCanvasEntityListDnd } from 'features/controlLayers/components/CanvasEntityList/useCanvasEntityListDnd';
45
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
56
import { useEntityIsSelected } from 'features/controlLayers/hooks/useEntityIsSelected';
6-
import { useEntitySelectionColor } from 'features/controlLayers/hooks/useEntitySelectionColor';
77
import { entitySelected } from 'features/controlLayers/store/canvasSlice';
88
import { DndListDropIndicator } from 'features/dnd/DndListDropIndicator';
99
import type { PropsWithChildren } from 'react';
1010
import { memo, useCallback, useRef } from 'react';
1111

12+
const sx = {
13+
position: 'relative',
14+
flexDir: 'column',
15+
w: 'full',
16+
bg: 'base.850',
17+
borderRadius: 'base',
18+
'&[data-selected=true]': {
19+
bg: 'base.800',
20+
},
21+
'&[data-is-dragging=true]': {
22+
opacity: 0.3,
23+
},
24+
transitionProperty: 'common',
25+
} satisfies SystemStyleObject;
26+
1227
export const CanvasEntityContainer = memo((props: PropsWithChildren) => {
1328
const dispatch = useAppDispatch();
1429
const entityIdentifier = useEntityIdentifierContext();
1530
const isSelected = useEntityIsSelected(entityIdentifier);
16-
const selectionColor = useEntitySelectionColor(entityIdentifier);
1731
const onClick = useCallback(() => {
1832
if (isSelected) {
1933
return;
@@ -22,26 +36,22 @@ export const CanvasEntityContainer = memo((props: PropsWithChildren) => {
2236
}, [dispatch, entityIdentifier, isSelected]);
2337
const ref = useRef<HTMLDivElement>(null);
2438

25-
const dndState = useCanvasEntityListDnd(ref, entityIdentifier);
39+
const [dndListState, isDragging] = useCanvasEntityListDnd(ref, entityIdentifier);
2640

2741
return (
2842
<Box position="relative">
2943
<Flex
3044
// This is used to trigger the post-move flash animation
3145
data-entity-id={entityIdentifier.id}
46+
data-selected={isSelected}
47+
data-is-dragging={isDragging}
3248
ref={ref}
33-
position="relative"
34-
flexDir="column"
35-
w="full"
36-
bg={isSelected ? 'base.800' : 'base.850'}
3749
onClick={onClick}
38-
borderInlineStartWidth={5}
39-
borderColor={isSelected ? selectionColor : 'base.800'}
40-
borderRadius="base"
50+
sx={sx}
4151
>
4252
{props.children}
4353
</Flex>
44-
<DndListDropIndicator dndState={dndState} />
54+
<DndListDropIndicator dndState={dndListState} />
4555
</Box>
4656
);
4757
});

invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/useCanvasEntityListDnd.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { useEffect, useState } from 'react';
1313
export const singleCanvasEntity = buildDndSourceApi<{ entityIdentifier: CanvasEntityIdentifier }>('SingleCanvasEntity');
1414

1515
export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdentifier: CanvasEntityIdentifier) => {
16-
const [dndState, setDndState] = useState<DndListState>(idle);
16+
const [dndListState, setDndListState] = useState<DndListState>(idle);
17+
const [isDragging, setIsDragging] = useState(false);
1718

1819
useEffect(() => {
1920
const element = ref.current;
@@ -27,10 +28,12 @@ export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdenti
2728
return singleCanvasEntity.getData({ entityIdentifier });
2829
},
2930
onDragStart() {
30-
setDndState({ type: 'is-dragging' });
31+
setDndListState({ type: 'is-dragging' });
32+
setIsDragging(true);
3133
},
3234
onDrop() {
33-
setDndState(idle);
35+
setDndListState(idle);
36+
setIsDragging(false);
3437
},
3538
}),
3639
dropTargetForElements({
@@ -57,29 +60,29 @@ export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdenti
5760
},
5861
onDragEnter({ self }) {
5962
const closestEdge = extractClosestEdge(self.data);
60-
setDndState({ type: 'is-dragging-over', closestEdge });
63+
setDndListState({ type: 'is-dragging-over', closestEdge });
6164
},
6265
onDrag({ self }) {
6366
const closestEdge = extractClosestEdge(self.data);
6467

6568
// Only need to update react state if nothing has changed.
6669
// Prevents re-rendering.
67-
setDndState((current) => {
70+
setDndListState((current) => {
6871
if (current.type === 'is-dragging-over' && current.closestEdge === closestEdge) {
6972
return current;
7073
}
7174
return { type: 'is-dragging-over', closestEdge };
7275
});
7376
},
7477
onDragLeave() {
75-
setDndState(idle);
78+
setDndListState(idle);
7679
},
7780
onDrop() {
78-
setDndState(idle);
81+
setDndListState(idle);
7982
},
8083
})
8184
);
8285
}, [entityIdentifier, ref]);
8386

84-
return dndState;
87+
return [dndListState, isDragging] as const;
8588
};

invokeai/frontend/web/src/features/controlLayers/components/IPAdapter/IPAdapterList.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,30 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
44
import { useAppSelector } from 'app/store/storeHooks';
55
import { CanvasEntityGroupList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityGroupList';
66
import { IPAdapter } from 'features/controlLayers/components/IPAdapter/IPAdapter';
7-
import { mapId } from 'features/controlLayers/konva/util';
87
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
8+
import { getEntityIdentifier } from 'features/controlLayers/store/types';
99
import { memo } from 'react';
1010

11-
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
12-
return canvas.referenceImages.entities.map(mapId).reverse();
11+
const selectEntityIdentifiers = createMemoizedSelector(selectCanvasSlice, (canvas) => {
12+
return canvas.referenceImages.entities.map(getEntityIdentifier).toReversed();
1313
});
1414
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
1515
return selectedEntityIdentifier?.type === 'reference_image';
1616
});
1717

1818
export const IPAdapterList = memo(() => {
1919
const isSelected = useAppSelector(selectIsSelected);
20-
const ipaIds = useAppSelector(selectEntityIds);
20+
const entityIdentifiers = useAppSelector(selectEntityIdentifiers);
2121

22-
if (ipaIds.length === 0) {
22+
if (entityIdentifiers.length === 0) {
2323
return null;
2424
}
2525

26-
if (ipaIds.length > 0) {
26+
if (entityIdentifiers.length > 0) {
2727
return (
28-
<CanvasEntityGroupList type="reference_image" isSelected={isSelected}>
29-
{ipaIds.map((id) => (
30-
<IPAdapter key={id} id={id} />
28+
<CanvasEntityGroupList type="reference_image" isSelected={isSelected} entityIdentifiers={entityIdentifiers}>
29+
{entityIdentifiers.map((entityIdentifiers) => (
30+
<IPAdapter key={entityIdentifiers.id} id={entityIdentifiers.id} />
3131
))}
3232
</CanvasEntityGroupList>
3333
);

invokeai/frontend/web/src/features/controlLayers/components/IPAdapter/IPAdapterSettings.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,8 @@ export const IPAdapterSettings = memo(() => {
8585
[entityIdentifier.id]
8686
);
8787
const targetData = useMemo<Dnd.types['TargetDataTypeMap']['setGlobalReferenceImage']>(
88-
() =>
89-
Dnd.Target.setGlobalReferenceImage.getData(
90-
{ globalReferenceImageId: entityIdentifier.id },
91-
ipAdapter.image?.image_name
92-
),
93-
[entityIdentifier.id, ipAdapter.image?.image_name]
88+
() => Dnd.Target.setGlobalReferenceImage.getData({ entityIdentifier }, ipAdapter.image?.image_name),
89+
[entityIdentifier, ipAdapter.image?.image_name]
9490
);
9591
const pullBboxIntoIPAdapter = usePullBboxIntoGlobalReferenceImage(entityIdentifier);
9692
const isBusy = useCanvasIsBusy();

invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ export const RegionalGuidanceIPAdapterSettings = memo(({ referenceImageId }: Pro
9494
const targetData = useMemo<Dnd.types['TargetDataTypeMap']['setRegionalGuidanceReferenceImage']>(
9595
() =>
9696
Dnd.Target.setRegionalGuidanceReferenceImage.getData(
97-
{ regionalGuidanceId: entityIdentifier.id, referenceImageId },
97+
{ entityIdentifier, referenceImageId },
9898
ipAdapter.image?.image_name
9999
),
100-
[entityIdentifier.id, ipAdapter.image?.image_name, referenceImageId]
100+
[entityIdentifier, ipAdapter.image?.image_name, referenceImageId]
101101
);
102102

103103
const postUploadAction = useMemo<RGIPAdapterImagePostUploadAction>(

invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/types';
33
import type { SystemStyleObject } from '@invoke-ai/ui-library';
44
import { Box } from '@invoke-ai/ui-library';
55
import type { DndListState } from 'features/dnd/dnd';
6-
import type { CSSProperties } from 'react';
76

87
/**
98
* Design decisions for the drop indicator's main line
@@ -100,8 +99,7 @@ function DndDropIndicatorInternal({ edge, gap = '0px' }: DropIndicatorProps) {
10099

101100
return (
102101
<Box
103-
sx={{ ...lineStyles, ...orientationStyles[orientation], ...edgeStyles[edge] }}
104-
style={{ '--local-line-offset': lineOffset } as CSSProperties}
102+
sx={{ ...lineStyles, ...orientationStyles[orientation], ...edgeStyles[edge], '--local-line-offset': lineOffset }}
105103
/>
106104
);
107105
}

invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/LinearViewField.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { SystemStyleObject } from '@invoke-ai/ui-library';
12
import { Box, Circle, Flex, Icon, IconButton, Spacer, Tooltip } from '@invoke-ai/ui-library';
23
import { useAppDispatch } from 'app/store/storeHooks';
34
import { DndListDropIndicator } from 'features/dnd/DndListDropIndicator';
@@ -20,6 +21,19 @@ type Props = {
2021
fieldIdentifier: FieldIdentifier;
2122
};
2223

24+
const sx = {
25+
layerStyle: 'second',
26+
alignItems: 'center',
27+
position: 'relative',
28+
borderRadius: 'base',
29+
w: 'full',
30+
p: 2,
31+
'&[data-is-dragging=true]': {
32+
opacity: 0.3,
33+
},
34+
transitionProperty: 'common',
35+
} satisfies SystemStyleObject;
36+
2337
const LinearViewFieldInternal = ({ fieldIdentifier }: Props) => {
2438
const dispatch = useAppDispatch();
2539
const { isValueChanged, onReset } = useFieldOriginalValue(fieldIdentifier.nodeId, fieldIdentifier.fieldName);
@@ -31,22 +45,18 @@ const LinearViewFieldInternal = ({ fieldIdentifier }: Props) => {
3145
}, [dispatch, fieldIdentifier]);
3246

3347
const ref = useRef<HTMLDivElement>(null);
34-
const dndState = useLinearViewFieldDnd(ref, fieldIdentifier);
48+
const [dndListState, isDragging] = useLinearViewFieldDnd(ref, fieldIdentifier);
3549

3650
return (
3751
<Box position="relative" w="full">
3852
<Flex
3953
ref={ref}
4054
// This is used to trigger the post-move flash animation
4155
data-field-name={fieldIdentifier.fieldName}
56+
data-is-dragging={isDragging}
4257
onMouseEnter={handleMouseOver}
4358
onMouseLeave={handleMouseOut}
44-
layerStyle="second"
45-
alignItems="center"
46-
position="relative"
47-
borderRadius="base"
48-
w="full"
49-
p={2}
59+
sx={sx}
5060
>
5161
<Flex flexDir="column" w="full">
5262
<Flex alignItems="center" gap={2}>
@@ -90,7 +100,7 @@ const LinearViewFieldInternal = ({ fieldIdentifier }: Props) => {
90100
<InputFieldRenderer nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName} />
91101
</Flex>
92102
</Flex>
93-
<DndListDropIndicator dndState={dndState} />
103+
<DndListDropIndicator dndState={dndListState} />
94104
</Box>
95105
);
96106
};

invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/useLinearViewFieldDnd.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { useEffect, useState } from 'react';
1313
export const singleWorkflowField = buildDndSourceApi<{ fieldIdentifier: FieldIdentifier }>('SingleWorkflowField');
1414

1515
export const useLinearViewFieldDnd = (ref: RefObject<HTMLElement>, fieldIdentifier: FieldIdentifier) => {
16-
const [dndState, setDndState] = useState<DndListState>(idle);
16+
const [dndListState, setListDndState] = useState<DndListState>(idle);
17+
const [isDragging, setIsDragging] = useState(false);
1718

1819
useEffect(() => {
1920
const element = ref.current;
@@ -27,10 +28,12 @@ export const useLinearViewFieldDnd = (ref: RefObject<HTMLElement>, fieldIdentifi
2728
return singleWorkflowField.getData({ fieldIdentifier });
2829
},
2930
onDragStart() {
30-
setDndState({ type: 'is-dragging' });
31+
setListDndState({ type: 'is-dragging' });
32+
setIsDragging(true);
3133
},
3234
onDrop() {
33-
setDndState(idle);
35+
setListDndState(idle);
36+
setIsDragging(false);
3437
},
3538
}),
3639
dropTargetForElements({
@@ -54,29 +57,29 @@ export const useLinearViewFieldDnd = (ref: RefObject<HTMLElement>, fieldIdentifi
5457
},
5558
onDragEnter({ self }) {
5659
const closestEdge = extractClosestEdge(self.data);
57-
setDndState({ type: 'is-dragging-over', closestEdge });
60+
setListDndState({ type: 'is-dragging-over', closestEdge });
5861
},
5962
onDrag({ self }) {
6063
const closestEdge = extractClosestEdge(self.data);
6164

6265
// Only need to update react state if nothing has changed.
6366
// Prevents re-rendering.
64-
setDndState((current) => {
67+
setListDndState((current) => {
6568
if (current.type === 'is-dragging-over' && current.closestEdge === closestEdge) {
6669
return current;
6770
}
6871
return { type: 'is-dragging-over', closestEdge };
6972
});
7073
},
7174
onDragLeave() {
72-
setDndState(idle);
75+
setListDndState(idle);
7376
},
7477
onDrop() {
75-
setDndState(idle);
78+
setListDndState(idle);
7679
},
7780
})
7881
);
7982
}, [fieldIdentifier, ref]);
8083

81-
return dndState;
84+
return [dndListState, isDragging] as const;
8285
};

0 commit comments

Comments
 (0)