mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): more dnd cleanup and tidy
This commit is contained in:
@@ -1,19 +1,33 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useCanvasEntityListDnd } from 'features/controlLayers/components/CanvasEntityList/useCanvasEntityListDnd';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useEntityIsSelected } from 'features/controlLayers/hooks/useEntityIsSelected';
|
||||
import { useEntitySelectionColor } from 'features/controlLayers/hooks/useEntitySelectionColor';
|
||||
import { entitySelected } from 'features/controlLayers/store/canvasSlice';
|
||||
import { DndListDropIndicator } from 'features/dnd/DndListDropIndicator';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
|
||||
const sx = {
|
||||
position: 'relative',
|
||||
flexDir: 'column',
|
||||
w: 'full',
|
||||
bg: 'base.850',
|
||||
borderRadius: 'base',
|
||||
'&[data-selected=true]': {
|
||||
bg: 'base.800',
|
||||
},
|
||||
'&[data-is-dragging=true]': {
|
||||
opacity: 0.3,
|
||||
},
|
||||
transitionProperty: 'common',
|
||||
} satisfies SystemStyleObject;
|
||||
|
||||
export const CanvasEntityContainer = memo((props: PropsWithChildren) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const isSelected = useEntityIsSelected(entityIdentifier);
|
||||
const selectionColor = useEntitySelectionColor(entityIdentifier);
|
||||
const onClick = useCallback(() => {
|
||||
if (isSelected) {
|
||||
return;
|
||||
@@ -22,26 +36,22 @@ export const CanvasEntityContainer = memo((props: PropsWithChildren) => {
|
||||
}, [dispatch, entityIdentifier, isSelected]);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const dndState = useCanvasEntityListDnd(ref, entityIdentifier);
|
||||
const [dndListState, isDragging] = useCanvasEntityListDnd(ref, entityIdentifier);
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
<Flex
|
||||
// This is used to trigger the post-move flash animation
|
||||
data-entity-id={entityIdentifier.id}
|
||||
data-selected={isSelected}
|
||||
data-is-dragging={isDragging}
|
||||
ref={ref}
|
||||
position="relative"
|
||||
flexDir="column"
|
||||
w="full"
|
||||
bg={isSelected ? 'base.800' : 'base.850'}
|
||||
onClick={onClick}
|
||||
borderInlineStartWidth={5}
|
||||
borderColor={isSelected ? selectionColor : 'base.800'}
|
||||
borderRadius="base"
|
||||
sx={sx}
|
||||
>
|
||||
{props.children}
|
||||
</Flex>
|
||||
<DndListDropIndicator dndState={dndState} />
|
||||
<DndListDropIndicator dndState={dndListState} />
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@ import { useEffect, useState } from 'react';
|
||||
export const singleCanvasEntity = buildDndSourceApi<{ entityIdentifier: CanvasEntityIdentifier }>('SingleCanvasEntity');
|
||||
|
||||
export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdentifier: CanvasEntityIdentifier) => {
|
||||
const [dndState, setDndState] = useState<DndListState>(idle);
|
||||
const [dndListState, setDndListState] = useState<DndListState>(idle);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
@@ -27,10 +28,12 @@ export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdenti
|
||||
return singleCanvasEntity.getData({ entityIdentifier });
|
||||
},
|
||||
onDragStart() {
|
||||
setDndState({ type: 'is-dragging' });
|
||||
setDndListState({ type: 'is-dragging' });
|
||||
setIsDragging(true);
|
||||
},
|
||||
onDrop() {
|
||||
setDndState(idle);
|
||||
setDndListState(idle);
|
||||
setIsDragging(false);
|
||||
},
|
||||
}),
|
||||
dropTargetForElements({
|
||||
@@ -57,14 +60,14 @@ export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdenti
|
||||
},
|
||||
onDragEnter({ self }) {
|
||||
const closestEdge = extractClosestEdge(self.data);
|
||||
setDndState({ type: 'is-dragging-over', closestEdge });
|
||||
setDndListState({ type: 'is-dragging-over', closestEdge });
|
||||
},
|
||||
onDrag({ self }) {
|
||||
const closestEdge = extractClosestEdge(self.data);
|
||||
|
||||
// Only need to update react state if nothing has changed.
|
||||
// Prevents re-rendering.
|
||||
setDndState((current) => {
|
||||
setDndListState((current) => {
|
||||
if (current.type === 'is-dragging-over' && current.closestEdge === closestEdge) {
|
||||
return current;
|
||||
}
|
||||
@@ -72,14 +75,14 @@ export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdenti
|
||||
});
|
||||
},
|
||||
onDragLeave() {
|
||||
setDndState(idle);
|
||||
setDndListState(idle);
|
||||
},
|
||||
onDrop() {
|
||||
setDndState(idle);
|
||||
setDndListState(idle);
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [entityIdentifier, ref]);
|
||||
|
||||
return dndState;
|
||||
return [dndListState, isDragging] as const;
|
||||
};
|
||||
|
||||
@@ -4,12 +4,12 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityGroupList';
|
||||
import { IPAdapter } from 'features/controlLayers/components/IPAdapter/IPAdapter';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasSlice, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.referenceImages.entities.map(mapId).reverse();
|
||||
const selectEntityIdentifiers = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return canvas.referenceImages.entities.map(getEntityIdentifier).toReversed();
|
||||
});
|
||||
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
|
||||
return selectedEntityIdentifier?.type === 'reference_image';
|
||||
@@ -17,17 +17,17 @@ const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selecte
|
||||
|
||||
export const IPAdapterList = memo(() => {
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
const ipaIds = useAppSelector(selectEntityIds);
|
||||
const entityIdentifiers = useAppSelector(selectEntityIdentifiers);
|
||||
|
||||
if (ipaIds.length === 0) {
|
||||
if (entityIdentifiers.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ipaIds.length > 0) {
|
||||
if (entityIdentifiers.length > 0) {
|
||||
return (
|
||||
<CanvasEntityGroupList type="reference_image" isSelected={isSelected}>
|
||||
{ipaIds.map((id) => (
|
||||
<IPAdapter key={id} id={id} />
|
||||
<CanvasEntityGroupList type="reference_image" isSelected={isSelected} entityIdentifiers={entityIdentifiers}>
|
||||
{entityIdentifiers.map((entityIdentifiers) => (
|
||||
<IPAdapter key={entityIdentifiers.id} id={entityIdentifiers.id} />
|
||||
))}
|
||||
</CanvasEntityGroupList>
|
||||
);
|
||||
|
||||
@@ -85,12 +85,8 @@ export const IPAdapterSettings = memo(() => {
|
||||
[entityIdentifier.id]
|
||||
);
|
||||
const targetData = useMemo<Dnd.types['TargetDataTypeMap']['setGlobalReferenceImage']>(
|
||||
() =>
|
||||
Dnd.Target.setGlobalReferenceImage.getData(
|
||||
{ globalReferenceImageId: entityIdentifier.id },
|
||||
ipAdapter.image?.image_name
|
||||
),
|
||||
[entityIdentifier.id, ipAdapter.image?.image_name]
|
||||
() => Dnd.Target.setGlobalReferenceImage.getData({ entityIdentifier }, ipAdapter.image?.image_name),
|
||||
[entityIdentifier, ipAdapter.image?.image_name]
|
||||
);
|
||||
const pullBboxIntoIPAdapter = usePullBboxIntoGlobalReferenceImage(entityIdentifier);
|
||||
const isBusy = useCanvasIsBusy();
|
||||
|
||||
@@ -94,10 +94,10 @@ export const RegionalGuidanceIPAdapterSettings = memo(({ referenceImageId }: Pro
|
||||
const targetData = useMemo<Dnd.types['TargetDataTypeMap']['setRegionalGuidanceReferenceImage']>(
|
||||
() =>
|
||||
Dnd.Target.setRegionalGuidanceReferenceImage.getData(
|
||||
{ regionalGuidanceId: entityIdentifier.id, referenceImageId },
|
||||
{ entityIdentifier, referenceImageId },
|
||||
ipAdapter.image?.image_name
|
||||
),
|
||||
[entityIdentifier.id, ipAdapter.image?.image_name, referenceImageId]
|
||||
[entityIdentifier, ipAdapter.image?.image_name, referenceImageId]
|
||||
);
|
||||
|
||||
const postUploadAction = useMemo<RGIPAdapterImagePostUploadAction>(
|
||||
|
||||
Reference in New Issue
Block a user