Skip to content

Commit

Permalink
Merge pull request #323 from boostcampwm2023/feature/drag-and-drop
Browse files Browse the repository at this point in the history
fix: 스토리 우선순위 변경 로직 수정
  • Loading branch information
surinkwon authored Aug 6, 2024
2 parents 352531b + bc286d9 commit b693606
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 48 deletions.
52 changes: 25 additions & 27 deletions frontend/src/components/backlog/EpicDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { ChangeEvent, useEffect, useMemo, useRef, useState } from "react";
import { ChangeEvent, useEffect, useRef, useState } from "react";
import { useOutletContext } from "react-router-dom";
import { Socket } from "socket.io-client";
import { EpicCategoryDTO } from "../../types/DTO/backlogDTO";
import CategoryChip from "./CategoryChip";
import useEpicEmitEvent from "../../hooks/pages/backlog/useEpicEmitEvent";
import { CATEGORY_COLOR } from "../../constants/backlog";
import getRandomNumber from "../../utils/getRandomNumber";
import {
BacklogCategoryColor,
BacklogSocketData,
BacklogSocketDomain,
BacklogSocketEpicAction,
} from "../../types/common/backlog";
import EpicDropdownOption from "./EpicDropdownOption";
import { LexoRank } from "lexorank";
import getNewColor from "../../utils/getNewColor";

interface EpicDropdownProps {
selectedEpic?: EpicCategoryDTO;
Expand All @@ -29,13 +28,10 @@ const EpicDropdown = ({
const { socket }: { socket: Socket } = useOutletContext();
const { emitEpicCreateEvent } = useEpicEmitEvent(socket);
const [value, setValue] = useState("");
const [epicColor, setEpicColor] = useState(
getNewColor(Object.keys(CATEGORY_COLOR))
);
const inputElementRef = useRef<HTMLInputElement | null>(null);
const epicColor = useMemo(() => {
const colors = Object.keys(CATEGORY_COLOR);
return colors[
getRandomNumber(0, colors.length - 1)
] as BacklogCategoryColor;
}, []);

const handleInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
const { value } = target;
Expand All @@ -60,8 +56,9 @@ const EpicDropdown = ({
.toString()
: LexoRank.middle().toString();

setValue("");
emitEpicCreateEvent({ name: value, color: epicColor, rankValue });
setValue("");
setEpicColor(getNewColor(Object.keys(CATEGORY_COLOR)));
}
};

Expand Down Expand Up @@ -109,27 +106,28 @@ const EpicDropdown = ({
ref={inputElementRef}
/>
</div>
<ul className="pt-1">
{...epicList.map((epic) => (
<li
key={epic.id}
onClick={() => {
handleEpicChange(epic.id);
}}
>
<EpicDropdownOption
key={epic.id}
epic={epic}
onEpicChange={handleEpicChange}
/>
</li>
))}
</ul>
{value && (
{value ? (
<div className="flex items-center gap-2 p-1">
<span>생성</span>
<CategoryChip content={value} bgColor={epicColor} />
</div>
) : (
<ul className="max-h-[16rem] overflow-y-auto scrollbar-thin pt-1">
{...epicList.map((epic) => (
<li
key={epic.id}
onClick={() => {
handleEpicChange(epic.id);
}}
>
<EpicDropdownOption
key={epic.id}
epic={epic}
onEpicChange={handleEpicChange}
/>
</li>
))}
</ul>
)}
</div>
);
Expand Down
68 changes: 48 additions & 20 deletions frontend/src/pages/backlog/UnfinishedStoryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,35 @@ import { LexoRank } from "lexorank";
import { BacklogDTO } from "../../types/DTO/backlogDTO";
import StoryCreateButton from "../../components/backlog/StoryCreateButton";
import StoryCreateForm from "../../components/backlog/StoryCreateForm";
import { DragEvent, useMemo, useRef, useState } from "react";
import { DragEvent, useEffect, useMemo, useRef, useState } from "react";
import changeEpicListToStoryList from "../../utils/changeEpicListToStoryList";
import StoryBlock from "../../components/backlog/StoryBlock";
import TaskBlock from "../../components/backlog/TaskBlock";
import useShowDetail from "../../hooks/pages/backlog/useShowDetail";
import useStoryEmitEvent from "../../hooks/pages/backlog/useStoryEmitEvent";
import getDragElementIndex from "../../utils/getDragElementIndex";
import { BacklogSocketData } from "../../types/common/backlog";

const UnfinishedStoryPage = () => {
const { socket, backlog }: { socket: Socket; backlog: BacklogDTO } =
useOutletContext();
const { showDetail, handleShowDetail } = useShowDetail();
const [storyElementIndex, setStoryElementIndex] = useState<number>();
const storyComponentRefList = useRef<HTMLDivElement[]>([]);
const draggingComponentIndexRef = useRef<number>();
const draggingComponentIdRef = useRef<number>();
const storyList = useMemo(
() =>
changeEpicListToStoryList(backlog.epicList).sort((storyA, storyB) => {
if (storyA.rankValue < storyB.rankValue) {
return -1;
}
if (storyA.rankValue > storyB.rankValue) {
return 1;
}
return 0;
}),
changeEpicListToStoryList(backlog.epicList)
.sort((storyA, storyB) => {
if (storyA.rankValue < storyB.rankValue) {
return -1;
}
if (storyA.rankValue > storyB.rankValue) {
return 1;
}
return 0;
})
.filter(({ status }) => status !== "완료"),
[backlog.epicList]
);
const epicCategoryList = useMemo(
Expand All @@ -52,23 +55,26 @@ const UnfinishedStoryPage = () => {
event.preventDefault();
const index = getDragElementIndex(
storyComponentRefList.current,
draggingComponentIndexRef.current,
draggingComponentIdRef.current,
event.clientY
);

setStoryElementIndex(index);
};

const handleDragStart = (index: number) => {
draggingComponentIndexRef.current = index;
const handleDragStart = (id: number) => {
draggingComponentIdRef.current = id;
};

const handleDragEnd = (event: DragEvent) => {
event.stopPropagation();
const targetIndex = storyList.findIndex(
({ id }) => id === draggingComponentIdRef.current
);
let rankValue;

if (storyElementIndex === draggingComponentIndexRef.current) {
draggingComponentIndexRef.current = undefined;
if (storyElementIndex === targetIndex) {
draggingComponentIdRef.current = undefined;
setStoryElementIndex(undefined);
return;
}
Expand All @@ -90,14 +96,36 @@ const UnfinishedStoryPage = () => {
}

emitStoryUpdateEvent({
id: storyList[draggingComponentIndexRef.current as number].id,
id: draggingComponentIdRef.current as number,
rankValue,
});

draggingComponentIndexRef.current = undefined;
draggingComponentIdRef.current = undefined;
setStoryElementIndex(undefined);
};

useEffect(() => {
const handleDragEvent = ({
domain,
action,
content,
}: BacklogSocketData) => {
if (
domain === "story" &&
action === "delete" &&
content.id === draggingComponentIdRef.current
) {
setStoryElementIndex(undefined);
}
};

socket.on("backlog", handleDragEvent);

return () => {
socket.off("backlog", handleDragEvent);
};
}, []);

return (
<div className="flex flex-col items-center gap-4">
<div className="w-full border-b" onDragOver={handleDragOver}>
Expand All @@ -116,7 +144,7 @@ const UnfinishedStoryPage = () => {
className="relative"
ref={setStoryComponentRef(index)}
draggable={true}
onDragStart={() => handleDragStart(index)}
onDragStart={() => handleDragStart(id)}
onDragEnd={handleDragEnd}
>
<div
Expand Down Expand Up @@ -146,7 +174,7 @@ const UnfinishedStoryPage = () => {
ref={setStoryComponentRef(storyList.length)}
className={`${
storyElementIndex === storyList.length
? "w-full h-1 bg-blue-400"
? "w-[67.9rem] h-1 bg-blue-400"
: ""
} absolute`}
/>
Expand Down
1 change: 0 additions & 1 deletion frontend/src/utils/getDragElementIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const getDragElementIndex = (
(closest, child, index) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
console.log(offset);

if (offset < 0 && offset > closest.offset) {
return { offset, index };
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/utils/getNewColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { BacklogCategoryColor } from "../types/common/backlog";
import getRandomNumber from "./getRandomNumber";

const getNewColor = (colors: string[]) =>
colors[getRandomNumber(0, colors.length - 1)] as BacklogCategoryColor;

export default getNewColor;

0 comments on commit b693606

Please sign in to comment.