feat(enhanced-kanban): implement synchronous reordering for tasks and groups
- Added synchronous state updates for task and group reordering in the EnhancedKanbanBoard component, improving UI responsiveness during drag-and-drop operations. - Introduced new actions `reorderTasks` and `reorderGroups` in the enhanced-kanban slice for better state management. - Updated EnhancedKanbanGroup and EnhancedKanbanTaskCard components to utilize the new layout change animations, enhancing the user experience during reordering.
This commit is contained in:
@@ -27,7 +27,9 @@ import {
|
|||||||
fetchEnhancedKanbanGroups,
|
fetchEnhancedKanbanGroups,
|
||||||
reorderEnhancedKanbanTasks,
|
reorderEnhancedKanbanTasks,
|
||||||
reorderEnhancedKanbanGroups,
|
reorderEnhancedKanbanGroups,
|
||||||
setDragState
|
setDragState,
|
||||||
|
reorderTasks,
|
||||||
|
reorderGroups,
|
||||||
} from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
} from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
import EnhancedKanbanGroup from './EnhancedKanbanGroup';
|
import EnhancedKanbanGroup from './EnhancedKanbanGroup';
|
||||||
import EnhancedKanbanTaskCard from './EnhancedKanbanTaskCard';
|
import EnhancedKanbanTaskCard from './EnhancedKanbanTaskCard';
|
||||||
@@ -210,12 +212,10 @@ const EnhancedKanbanBoard: React.FC<EnhancedKanbanBoardProps> = ({ projectId, cl
|
|||||||
const [movedGroup] = reorderedGroups.splice(fromIndex, 1);
|
const [movedGroup] = reorderedGroups.splice(fromIndex, 1);
|
||||||
reorderedGroups.splice(toIndex, 0, movedGroup);
|
reorderedGroups.splice(toIndex, 0, movedGroup);
|
||||||
|
|
||||||
// Dispatch group reorder action
|
// Synchronous UI update
|
||||||
dispatch(reorderEnhancedKanbanGroups({
|
dispatch(reorderGroups({ fromIndex, toIndex, reorderedGroups }));
|
||||||
fromIndex,
|
// Async backend sync (optional)
|
||||||
toIndex,
|
dispatch(reorderEnhancedKanbanGroups({ fromIndex, toIndex, reorderedGroups }) as any);
|
||||||
reorderedGroups,
|
|
||||||
}) as any);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,17 @@ const EnhancedKanbanBoard: React.FC<EnhancedKanbanBoardProps> = ({ projectId, cl
|
|||||||
updatedTargetTasks.splice(targetIndex, 0, movedTask);
|
updatedTargetTasks.splice(targetIndex, 0, movedTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch the reorder action
|
// Synchronous UI update
|
||||||
|
dispatch(reorderTasks({
|
||||||
|
activeGroupId: sourceGroup.id,
|
||||||
|
overGroupId: targetGroup.id,
|
||||||
|
fromIndex: sourceIndex,
|
||||||
|
toIndex: targetIndex,
|
||||||
|
task: movedTask,
|
||||||
|
updatedSourceTasks,
|
||||||
|
updatedTargetTasks,
|
||||||
|
}));
|
||||||
|
// Async backend sync (optional)
|
||||||
dispatch(reorderEnhancedKanbanTasks({
|
dispatch(reorderEnhancedKanbanTasks({
|
||||||
activeGroupId: sourceGroup.id,
|
activeGroupId: sourceGroup.id,
|
||||||
overGroupId: targetGroup.id,
|
overGroupId: targetGroup.id,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useMemo, useRef, useEffect, useState } from 'react';
|
import React, { useMemo, useRef, useEffect, useState } from 'react';
|
||||||
import { useDroppable } from '@dnd-kit/core';
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
import { SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
import { SortableContext, verticalListSortingStrategy, useSortable, defaultAnimateLayoutChanges } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { ITaskListGroup } from '@/types/tasks/taskList.types';
|
import { ITaskListGroup } from '@/types/tasks/taskList.types';
|
||||||
import EnhancedKanbanTaskCard from './EnhancedKanbanTaskCard';
|
import EnhancedKanbanTaskCard from './EnhancedKanbanTaskCard';
|
||||||
@@ -46,6 +46,7 @@ const EnhancedKanbanGroup: React.FC<EnhancedKanbanGroupProps> = React.memo(({
|
|||||||
type: 'group',
|
type: 'group',
|
||||||
group,
|
group,
|
||||||
},
|
},
|
||||||
|
animateLayoutChanges: defaultAnimateLayoutChanges,
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupRef = useRef<HTMLDivElement>(null);
|
const groupRef = useRef<HTMLDivElement>(null);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable, defaultAnimateLayoutChanges } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
||||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||||
@@ -34,6 +34,7 @@ const EnhancedKanbanTaskCard: React.FC<EnhancedKanbanTaskCardProps> = React.memo
|
|||||||
task,
|
task,
|
||||||
},
|
},
|
||||||
disabled: isDragOverlay,
|
disabled: isDragOverlay,
|
||||||
|
animateLayoutChanges: defaultAnimateLayoutChanges,
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
|
|||||||
@@ -402,6 +402,44 @@ const enhancedKanbanSlice = createSlice({
|
|||||||
resetState: (state) => {
|
resetState: (state) => {
|
||||||
return { ...initialState, groupBy: state.groupBy };
|
return { ...initialState, groupBy: state.groupBy };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Synchronous reorder for tasks
|
||||||
|
reorderTasks: (state, action: PayloadAction<{
|
||||||
|
activeGroupId: string;
|
||||||
|
overGroupId: string;
|
||||||
|
fromIndex: number;
|
||||||
|
toIndex: number;
|
||||||
|
task: IProjectTask;
|
||||||
|
updatedSourceTasks: IProjectTask[];
|
||||||
|
updatedTargetTasks: IProjectTask[];
|
||||||
|
}>) => {
|
||||||
|
const { activeGroupId, overGroupId, updatedSourceTasks, updatedTargetTasks } = action.payload;
|
||||||
|
const sourceGroupIndex = state.taskGroups.findIndex(group => group.id === activeGroupId);
|
||||||
|
const targetGroupIndex = state.taskGroups.findIndex(group => group.id === overGroupId);
|
||||||
|
if (sourceGroupIndex !== -1) {
|
||||||
|
state.taskGroups[sourceGroupIndex].tasks = updatedSourceTasks;
|
||||||
|
state.groupCache[activeGroupId] = state.taskGroups[sourceGroupIndex];
|
||||||
|
}
|
||||||
|
if (targetGroupIndex !== -1 && activeGroupId !== overGroupId) {
|
||||||
|
state.taskGroups[targetGroupIndex].tasks = updatedTargetTasks;
|
||||||
|
state.groupCache[overGroupId] = state.taskGroups[targetGroupIndex];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Synchronous reorder for groups
|
||||||
|
reorderGroups: (state, action: PayloadAction<{
|
||||||
|
fromIndex: number;
|
||||||
|
toIndex: number;
|
||||||
|
reorderedGroups: ITaskListGroup[];
|
||||||
|
}>) => {
|
||||||
|
const { reorderedGroups } = action.payload;
|
||||||
|
state.taskGroups = reorderedGroups;
|
||||||
|
state.groupCache = reorderedGroups.reduce((cache, group) => {
|
||||||
|
cache[group.id] = group;
|
||||||
|
return cache;
|
||||||
|
}, {} as Record<string, ITaskListGroup>);
|
||||||
|
state.columnOrder = reorderedGroups.map(group => group.id);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder
|
builder
|
||||||
@@ -488,6 +526,8 @@ export const {
|
|||||||
updateTaskPriority,
|
updateTaskPriority,
|
||||||
deleteTask,
|
deleteTask,
|
||||||
resetState,
|
resetState,
|
||||||
|
reorderTasks,
|
||||||
|
reorderGroups,
|
||||||
} = enhancedKanbanSlice.actions;
|
} = enhancedKanbanSlice.actions;
|
||||||
|
|
||||||
export default enhancedKanbanSlice.reducer;
|
export default enhancedKanbanSlice.reducer;
|
||||||
Reference in New Issue
Block a user