refactored to just use a new dnd context, got reordering working and fixed flicker

This commit is contained in:
Jennifer Player 2024-02-14 14:20:08 -05:00
parent 8a147bd6e6
commit 2071972a8c
4 changed files with 91 additions and 33 deletions

View File

@ -0,0 +1,45 @@
import { Box } from '@invoke-ai/ui-library';
import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks';
import type { TypesafeDroppableData } from 'features/dnd/types';
import { isValidDrop } from 'features/dnd/util/isValidDrop';
import { AnimatePresence } from 'framer-motion';
import type { ReactNode } from 'react';
import { memo, useRef } from 'react';
import { v4 as uuidv4 } from 'uuid';
import IAIDropOverlay from './IAIDropOverlay';
type IAISortableItemProps = {
dropLabel?: ReactNode;
disabled?: boolean;
data?: TypesafeDroppableData;
};
const IAISortableItem = (props: IAISortableItemProps) => {
const { dropLabel, data, disabled } = props;
const dndId = useRef(uuidv4());
const { isOver, setNodeRef, active } = useDroppableTypesafe({
id: dndId.current,
disabled,
data,
});
return (
<Box
ref={setNodeRef}
position="absolute"
top={0}
insetInlineStart={0}
w="full"
h="full"
pointerEvents={active ? 'auto' : 'none'}
>
<AnimatePresence>
{isValidDrop(data, active) && <IAIDropOverlay isOver={isOver} label={dropLabel} />}
</AnimatePresence>
</Box>
);
};
export default memo(IAISortableItem);

View File

@ -23,11 +23,12 @@ const LinearViewField = ({ nodeId, fieldName }: Props) => {
const dispatch = useAppDispatch();
const { isMouseOverNode, handleMouseOut, handleMouseOver } = useMouseOverNode(nodeId);
const { t } = useTranslation();
const handleRemoveField = useCallback(() => {
dispatch(workflowExposedFieldRemoved({ nodeId, fieldName }));
}, [dispatch, fieldName, nodeId]);
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: nodeId });
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: `${nodeId}.${fieldName}` });
const style = {
transform: CSS.Transform.toString(transform),

View File

@ -1,15 +1,15 @@
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { DndContext } from '@dnd-kit/core';
import { arrayMove, SortableContext } from '@dnd-kit/sortable';
import { Box, Flex } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import IAIDroppable from 'common/components/IAIDroppable';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import type { TypesafeDroppableData } from 'features/dnd/types';
import type { DragEndEvent } from 'features/dnd/types';
import LinearViewField from 'features/nodes/components/flow/nodes/Invocation/fields/LinearViewField';
import { selectWorkflowSlice } from 'features/nodes/store/workflowSlice';
import { memo, useMemo } from 'react';
import { selectWorkflowSlice, workflowExposedFieldsReordered } from 'features/nodes/store/workflowSlice';
import { FieldIdentifier } from 'features/nodes/types/field';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetOpenAPISchemaQuery } from 'services/api/endpoints/appInfo';
@ -19,32 +19,46 @@ const WorkflowLinearTab = () => {
const fields = useAppSelector(selector);
const { isLoading } = useGetOpenAPISchemaQuery();
const { t } = useTranslation();
const dispatch = useAppDispatch();
const droppableData = useMemo<TypesafeDroppableData | undefined>(
() => ({
id: 'current-image',
actionType: 'SET_CURRENT_IMAGE',
}),
[]
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
console.log({ active, over });
const fieldsStrings = fields.map((field) => `${field.nodeId}.${field.fieldName}`);
if (over && active.id !== over.id) {
const oldIndex = fieldsStrings.indexOf(active.id as string);
const newIndex = fieldsStrings.indexOf(over.id as string);
const newFields = arrayMove(fieldsStrings, oldIndex, newIndex)
.map((field) => fields.find((obj) => `${obj.nodeId}.${obj.fieldName}` === field))
.filter((field) => field) as FieldIdentifier[];
dispatch(workflowExposedFieldsReordered(newFields));
}
},
[dispatch, fields]
);
return (
<Box position="relative" w="full" h="full">
<IAIDroppable data={droppableData} disabled={false} dropLabel="drop label" />
<ScrollableContent>
<SortableContext items={fields.map((field) => field.nodeId)} strategy={verticalListSortingStrategy}>
<Flex position="relative" flexDir="column" alignItems="flex-start" p={1} gap={2} h="full" w="full">
{isLoading ? (
<IAINoContentFallback label={t('nodes.loadingNodes')} icon={null} />
) : fields.length ? (
fields.map(({ nodeId, fieldName }) => (
<LinearViewField key={`${nodeId}.${fieldName}`} nodeId={nodeId} fieldName={fieldName} />
))
) : (
<IAINoContentFallback label={t('nodes.noFieldsLinearview')} icon={null} />
)}
</Flex>
</SortableContext>
<DndContext onDragEnd={handleDragEnd}>
<SortableContext items={fields.map((field) => `${field.nodeId}.${field.fieldName}`)}>
<Flex position="relative" flexDir="column" alignItems="flex-start" p={1} gap={2} h="full" w="full">
{isLoading ? (
<IAINoContentFallback label={t('nodes.loadingNodes')} icon={null} />
) : fields.length ? (
fields.map(({ nodeId, fieldName }) => (
<LinearViewField key={`${nodeId}.${fieldName}`} nodeId={nodeId} fieldName={fieldName} />
))
) : (
<IAINoContentFallback label={t('nodes.noFieldsLinearview')} icon={null} />
)}
</Flex>
</SortableContext>
</DndContext>
</ScrollableContent>
</Box>
);

View File

@ -42,11 +42,8 @@ export const workflowSlice = createSlice({
state.exposedFields = state.exposedFields.filter((field) => !isEqual(field, action.payload));
state.isTouched = true;
},
workflowExposedFieldsReordered: (state, action: PayloadAction<string>) => {
state.exposedFields = action.payload.split(',').map((id) => {
const [nodeId, fieldName] = id.split('.');
return { nodeId, fieldName };
});
workflowExposedFieldsReordered: (state, action: PayloadAction<FieldIdentifier[]>) => {
state.exposedFields = action.payload;
state.isTouched = true;
},
workflowNameChanged: (state, action: PayloadAction<string>) => {
@ -113,6 +110,7 @@ export const workflowSlice = createSlice({
export const {
workflowExposedFieldAdded,
workflowExposedFieldRemoved,
workflowExposedFieldsReordered,
workflowNameChanged,
workflowCategoryChanged,
workflowDescriptionChanged,