98 lines
3.2 KiB
JavaScript
98 lines
3.2 KiB
JavaScript
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { DragDropContext } from 'react-beautiful-dnd';
|
|
|
|
import useCurrentUser from 'shared/hooks/currentUser';
|
|
import api from 'shared/utils/api';
|
|
import { moveItemWithinArray, insertItemIntoArray } from 'shared/utils/javascript';
|
|
import { IssueStatus } from 'shared/constants/issues';
|
|
|
|
import List from './List';
|
|
import { Lists } from './Styles';
|
|
|
|
const propTypes = {
|
|
project: PropTypes.object.isRequired,
|
|
filters: PropTypes.object.isRequired,
|
|
updateLocalProjectIssues: PropTypes.func.isRequired,
|
|
};
|
|
|
|
const ProjectBoardLists = ({ project, filters, updateLocalProjectIssues }) => {
|
|
const { currentUserId } = useCurrentUser();
|
|
|
|
const handleIssueDrop = ({ draggableId, destination, source }) => {
|
|
if (!isPositionChanged(source, destination)) return;
|
|
|
|
const issueId = Number(draggableId);
|
|
|
|
api.optimisticUpdate(`/issues/${issueId}`, {
|
|
updatedFields: {
|
|
status: destination.droppableId,
|
|
listPosition: calculateIssueListPosition(project.issues, destination, source, issueId),
|
|
},
|
|
currentFields: project.issues.find(({ id }) => id === issueId),
|
|
setLocalData: fields => updateLocalProjectIssues(issueId, fields),
|
|
});
|
|
};
|
|
|
|
return (
|
|
<DragDropContext onDragEnd={handleIssueDrop}>
|
|
<Lists>
|
|
{Object.values(IssueStatus).map(status => (
|
|
<List
|
|
key={status}
|
|
status={status}
|
|
project={project}
|
|
filters={filters}
|
|
currentUserId={currentUserId}
|
|
/>
|
|
))}
|
|
</Lists>
|
|
</DragDropContext>
|
|
);
|
|
};
|
|
|
|
const isPositionChanged = (destination, source) => {
|
|
if (!destination) return false;
|
|
const isSameList = destination.droppableId === source.droppableId;
|
|
const isSamePosition = destination.index === source.index;
|
|
return !isSameList || !isSamePosition;
|
|
};
|
|
|
|
const calculateIssueListPosition = (...args) => {
|
|
const { prevIssue, nextIssue } = getAfterDropPrevNextIssue(...args);
|
|
let position;
|
|
|
|
if (!prevIssue && !nextIssue) {
|
|
position = 1;
|
|
} else if (!prevIssue) {
|
|
position = nextIssue.listPosition - 1;
|
|
} else if (!nextIssue) {
|
|
position = prevIssue.listPosition + 1;
|
|
} else {
|
|
position = prevIssue.listPosition + (nextIssue.listPosition - prevIssue.listPosition) / 2;
|
|
}
|
|
return position;
|
|
};
|
|
|
|
const getAfterDropPrevNextIssue = (allIssues, destination, source, droppedIssueId) => {
|
|
const beforeDropDestinationIssues = getSortedListIssues(allIssues, destination.droppableId);
|
|
const droppedIssue = allIssues.find(issue => issue.id === droppedIssueId);
|
|
const isSameList = destination.droppableId === source.droppableId;
|
|
|
|
const afterDropDestinationIssues = isSameList
|
|
? moveItemWithinArray(beforeDropDestinationIssues, droppedIssue, destination.index)
|
|
: insertItemIntoArray(beforeDropDestinationIssues, droppedIssue, destination.index);
|
|
|
|
return {
|
|
prevIssue: afterDropDestinationIssues[destination.index - 1],
|
|
nextIssue: afterDropDestinationIssues[destination.index + 1],
|
|
};
|
|
};
|
|
|
|
const getSortedListIssues = (issues, status) =>
|
|
issues.filter(issue => issue.status === status).sort((a, b) => a.listPosition - b.listPosition);
|
|
|
|
ProjectBoardLists.propTypes = propTypes;
|
|
|
|
export default ProjectBoardLists;
|