Skip to content

Commit ab56018

Browse files
committed
#4020 first attempt editing task column and task list content for dynamic droppable area
1 parent 90e509b commit ab56018

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

src/frontend/src/pages/ProjectDetailPage/ProjectViewContainer/TaskList/v2/TaskColumn.tsx

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Droppable } from '@hello-pangea/dnd';
22
import { Box, Typography, useTheme } from '@mui/material';
3-
import { useState } from 'react';
3+
import { useEffect, useRef, useState } from 'react';
44
import { Project, Task, TaskStatus, TaskWithIndex } from 'shared';
55
import { statusNames, TaskCard } from '.';
66
import { NERButton } from '../../../../../components/NERButton';
@@ -13,22 +13,44 @@ export const TaskColumn = ({
1313
status,
1414
tasks,
1515
project,
16+
equalizedHeight,
1617
onEditTask,
1718
onDeleteTask,
18-
onAddTask
19+
onAddTask,
20+
onHeightChange
1921
}: {
2022
status: Task['status'];
2123
tasks: TaskWithIndex[];
2224
project: Project;
25+
equalizedHeight: number;
2326
onEditTask: (task: Task) => void;
2427
onDeleteTask: (taskId: string) => void;
2528
onAddTask: (task: Task) => void;
29+
onHeightChange: (status: Task['status'], height: number) => void;
2630
}) => {
2731
const { mutateAsync: createTask } = useCreateTask();
2832
const [showCreateTaskModal, setShowCreateTaskModal] = useState(false);
2933
const toast = useToast();
3034
const theme = useTheme();
3135

36+
// create ref to droppable box dom node so we can measure height
37+
const droppableBoxRef = useRef<HTMLElement | null>(null);
38+
39+
// effectively runs once on mount because both deps (task status and callback func) are stable
40+
useEffect(() => {
41+
if (!droppableBoxRef.current) return;
42+
43+
const droppableBox = droppableBoxRef.current;
44+
45+
const observer = new ResizeObserver(() => {
46+
onHeightChange(status, droppableBox.scrollHeight);
47+
});
48+
observer.observe(droppableBox);
49+
50+
// cleanup func to disconnect observer when component unmounts
51+
return () => observer.disconnect();
52+
}, [status, onHeightChange]);
53+
3254
const handleCreateTask = async ({ notes, title, deadline, assignees, priority, startDate }: EditTaskFormInput) => {
3355
try {
3456
const task = await createTask({
@@ -75,14 +97,18 @@ export const TaskColumn = ({
7597
<Droppable droppableId={status}>
7698
{(droppableProvided, snapshot) => (
7799
<Box
78-
ref={droppableProvided.innerRef}
100+
ref={(droppableBox: HTMLElement | null) => {
101+
droppableProvided.innerRef(droppableBox); // give dnd lib access to dom node
102+
droppableBoxRef.current = droppableBox; // give ourselves access to dom node
103+
}}
79104
{...droppableProvided.droppableProps}
80105
className={snapshot.isDraggingOver ? ' isDraggingOver' : ''}
81106
sx={{
82107
display: 'flex',
83108
flexDirection: 'column',
84109
borderRadius: 5,
85110
padding: '5px',
111+
minHeight: `${equalizedHeight}px`,
86112
'&.isDraggingOver': {
87113
bgcolor: '#dadadf'
88114
}

src/frontend/src/pages/ProjectDetailPage/ProjectViewContainer/TaskList/v2/TaskListContent.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd';
22
import { Box } from '@mui/material';
3-
import { useState } from 'react';
3+
import { useCallback, useRef, useState } from 'react';
44
import { Project, Task, TaskWithIndex } from 'shared';
55
import { getTasksByStatus, statuses, TasksByStatus } from '.';
66
import { useSetTaskStatus } from '../../../../../hooks/tasks.hooks';
@@ -19,6 +19,16 @@ export const TaskListContent = ({ project }: TaskListProps) => {
1919

2020
const toast = useToast();
2121

22+
// ref to mapping of each column's status to its measured height, partial because heights may not exist
23+
const columnHeightsRef = useRef<Partial<Record<Task['status'], number>>>({});
24+
const [equalizedHeight, setEqualizedHeight] = useState(0);
25+
26+
const onHeightChange = useCallback((status: Task['status'], height: number) => {
27+
columnHeightsRef.current[status] = height;
28+
const max = Math.max(...(Object.values(columnHeightsRef.current) as number[]));
29+
setEqualizedHeight(max);
30+
}, []);
31+
2232
const onDeleteTask = (taskId: string) => {
2333
setTasksByStatus((prev) => {
2434
const newTasksByStatus = { ...prev };
@@ -117,10 +127,12 @@ export const TaskListContent = ({ project }: TaskListProps) => {
117127
onAddTask={onAddTask}
118128
onDeleteTask={onDeleteTask}
119129
onEditTask={onEditTask}
130+
onHeightChange={onHeightChange}
120131
status={status}
121132
tasks={tasksByStatus[status]}
122133
key={status}
123134
project={project}
135+
equalizedHeight={equalizedHeight}
124136
/>
125137
))}
126138
</Box>

0 commit comments

Comments
 (0)