fix: non-virtual dnd
This commit is contained in:
2538
pnpm-lock.yaml
generated
2538
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,77 +1,71 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
|
||||||
import { Course, Status } from '@shared/types/Course';
|
import { Course, Status } from '@shared/types/Course';
|
||||||
import { CourseMeeting } from '@shared/types/CourseMeeting';
|
import { CourseMeeting } from '@shared/types/CourseMeeting';
|
||||||
import Instructor from '@shared/types/Instructor';
|
import Instructor from '@shared/types/Instructor';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import List from '@views/components/common/List/List';
|
import List from '@views/components/common/List/List';
|
||||||
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
|
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
|
||||||
import { getCourseColors } from 'src/shared/util/colors';
|
import React from 'react';
|
||||||
import { test_colors } from './PopupCourseBlock.stories';
|
import { test_colors } from './PopupCourseBlock.stories';
|
||||||
|
|
||||||
|
|
||||||
const numberOfCourses = 5;
|
const numberOfCourses = 5;
|
||||||
|
|
||||||
const generateCourses = (count) => {
|
const generateCourses = count => {
|
||||||
const courses = [];
|
const courses = [];
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const course = new Course({
|
const course = new Course({
|
||||||
courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB',
|
courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB',
|
||||||
creditHours: 3,
|
creditHours: 3,
|
||||||
department: 'C S',
|
department: 'C S',
|
||||||
description: [
|
description: [
|
||||||
'Problem solving and fundamental algorithms for various applications in science, business, and on the World Wide Web, and introductory programming in a modern object-oriented programming language.',
|
'Problem solving and fundamental algorithms for various applications in science, business, and on the World Wide Web, and introductory programming in a modern object-oriented programming language.',
|
||||||
'Only one of the following may be counted: Computer Science 303E, 312, 312H. Credit for Computer Science 303E may not be earned after a student has received credit for Computer Science 314, or 314H. May not be counted toward a degree in computer science.',
|
'Only one of the following may be counted: Computer Science 303E, 312, 312H. Credit for Computer Science 303E may not be earned after a student has received credit for Computer Science 314, or 314H. May not be counted toward a degree in computer science.',
|
||||||
'May be counted toward the Quantitative Reasoning flag requirement.',
|
'May be counted toward the Quantitative Reasoning flag requirement.',
|
||||||
'Designed to accommodate 100 or more students.',
|
'Designed to accommodate 100 or more students.',
|
||||||
'Taught as a Web-based course.',
|
'Taught as a Web-based course.',
|
||||||
],
|
],
|
||||||
flags: ['Quantitative Reasoning'],
|
flags: ['Quantitative Reasoning'],
|
||||||
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
|
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
|
||||||
instructionMode: 'Online',
|
instructionMode: 'Online',
|
||||||
instructors: [
|
instructors: [
|
||||||
new Instructor({
|
new Instructor({
|
||||||
firstName: 'Bevo',
|
firstName: 'Bevo',
|
||||||
lastName: 'Bevo',
|
lastName: 'Bevo',
|
||||||
fullName: 'Bevo Bevo',
|
fullName: 'Bevo Bevo',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
isReserved: false,
|
isReserved: false,
|
||||||
number: '303E',
|
number: '303E',
|
||||||
schedule: {
|
schedule: {
|
||||||
meetings: [
|
meetings: [
|
||||||
new CourseMeeting({
|
new CourseMeeting({
|
||||||
days: ['Tuesday', 'Thursday'],
|
days: ['Tuesday', 'Thursday'],
|
||||||
endTime: 660,
|
endTime: 660,
|
||||||
startTime: 570,
|
startTime: 570,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
semester: {
|
semester: {
|
||||||
code: '12345',
|
code: '12345',
|
||||||
season: 'Spring',
|
season: 'Spring',
|
||||||
year: 2024,
|
year: 2024,
|
||||||
},
|
},
|
||||||
status: Status.WAITLISTED,
|
status: Status.WAITLISTED,
|
||||||
uniqueId: 12345 + i, // Make uniqueId different for each course
|
uniqueId: 12345 + i, // Make uniqueId different for each course
|
||||||
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
|
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
|
||||||
});
|
});
|
||||||
|
|
||||||
courses.push(course);
|
courses.push(course);
|
||||||
}
|
}
|
||||||
|
|
||||||
return courses;
|
return courses;
|
||||||
};
|
};
|
||||||
|
|
||||||
const exampleCourses = generateCourses(numberOfCourses);
|
const exampleCourses = generateCourses(numberOfCourses);
|
||||||
const generateCourseBlocks = (exampleCourses, colors) => {
|
const generateCourseBlocks = (exampleCourses, colors) =>
|
||||||
return exampleCourses.map((course, i) => (
|
exampleCourses.map((course, i) => <PopupCourseBlock key={course.uniqueId} course={course} colors={colors[i]} />);
|
||||||
<PopupCourseBlock key={course.uniqueId} course={course} colors={colors[i]} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
const exampleCourseBlocks = generateCourseBlocks(exampleCourses, test_colors);
|
const exampleCourseBlocks = generateCourseBlocks(exampleCourses, test_colors);
|
||||||
|
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Components/Common/List',
|
title: 'Components/Common/List',
|
||||||
component: List,
|
component: List,
|
||||||
@@ -97,6 +91,6 @@ export const Default: Story = {
|
|||||||
itemHeight: 55,
|
itemHeight: 55,
|
||||||
listHeight: 300,
|
listHeight: 300,
|
||||||
listWidth: 300,
|
listWidth: 300,
|
||||||
gap: 8,
|
gap: 12,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useState, ReactElement, useCallback } from 'react';
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||||
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
|
import React, { ReactElement, useCallback, useState } from 'react';
|
||||||
import { FixedSizeList, areEqual } from 'react-window';
|
import { areEqual } from 'react-window';
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ctrl + f dragHandleProps on PopupCourseBlock.tsx for example implementation of drag handle (two lines of code)
|
* Ctrl + f dragHandleProps on PopupCourseBlock.tsx for example implementation of drag handle (two lines of code)
|
||||||
@@ -9,16 +8,15 @@ import { FixedSizeList, areEqual } from 'react-window';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the List component.
|
* Props for the List component.
|
||||||
*/
|
*/
|
||||||
export interface ListProps {
|
export interface ListProps {
|
||||||
draggableElements: any[]; // Will later define draggableElements based on what types
|
draggableElements: any[]; // Will later define draggableElements based on what types
|
||||||
// of components are draggable.
|
// of components are draggable.
|
||||||
itemHeight: number;
|
itemHeight: number;
|
||||||
listHeight: number;
|
listHeight: number;
|
||||||
listWidth: number;
|
listWidth: number;
|
||||||
gap: number; // Impacts the spacing between items in the list
|
gap: number; // Impacts the spacing between items in the list
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initial(draggableElements: any[] = []) {
|
function initial(draggableElements: any[] = []) {
|
||||||
@@ -28,7 +26,7 @@ function initial(draggableElements: any[] = []) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function reorder(list: { id: string, content: ReactElement }[], startIndex: number, endIndex: number) {
|
function reorder(list: { id: string; content: ReactElement }[], startIndex: number, endIndex: number) {
|
||||||
const result = Array.from(list);
|
const result = Array.from(list);
|
||||||
const [removed] = result.splice(startIndex, 1);
|
const [removed] = result.splice(startIndex, 1);
|
||||||
result.splice(endIndex, 0, removed);
|
result.splice(endIndex, 0, removed);
|
||||||
@@ -38,29 +36,29 @@ function reorder(list: { id: string, content: ReactElement }[], startIndex: numb
|
|||||||
function getStyle({ provided, style /* , isDragging, gap */ }) {
|
function getStyle({ provided, style /* , isDragging, gap */ }) {
|
||||||
const combined = {
|
const combined = {
|
||||||
...style,
|
...style,
|
||||||
...provided.draggableProps.style
|
...provided.draggableProps.style,
|
||||||
};
|
};
|
||||||
|
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Item({ provided, item, style, isDragging/* , gap */ }) {
|
function Item({ provided, item, style, isDragging /* , gap */ }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...provided.draggableProps}
|
{...provided.draggableProps}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
style={getStyle({ provided, style/* , isDragging, gap */ })}
|
style={getStyle({ provided, style /* , isDragging, gap */ })}
|
||||||
className={`item ${isDragging ? "is-dragging" : ""}`}
|
className={`item ${isDragging ? 'is-dragging' : ''}`}
|
||||||
>
|
>
|
||||||
{React.cloneElement(item.content, {dragHandleProps: provided.dragHandleProps})}
|
{React.cloneElement(item.content, { dragHandleProps: provided.dragHandleProps })}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RowProps {
|
interface RowProps {
|
||||||
data: any, // DraggableElements[]; Need to define DraggableElements interface once those components are ready
|
data: any; // DraggableElements[]; Need to define DraggableElements interface once those components are ready
|
||||||
index: number,
|
index: number;
|
||||||
style: React.CSSProperties,
|
style: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Row: React.FC<RowProps> = React.memo(({ data: { items, gap }, index, style }) => {
|
const Row: React.FC<RowProps> = React.memo(({ data: { items, gap }, index, style }) => {
|
||||||
@@ -68,12 +66,12 @@ const Row: React.FC<RowProps> = React.memo(({ data: { items, gap }, index, style
|
|||||||
const adjustedStyle = {
|
const adjustedStyle = {
|
||||||
...style,
|
...style,
|
||||||
height: `calc(${style.height}px - ${gap}px)`, // Reduce the height by gap to accommodate the margin
|
height: `calc(${style.height}px - ${gap}px)`, // Reduce the height by gap to accommodate the margin
|
||||||
marginBottom: `${gap}px` // Add gap as bottom margin
|
marginBottom: `${gap}px`, // Add gap as bottom margin
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Draggable draggableId={item.id} index={index} key={item.id}>
|
<Draggable draggableId={item.id} index={index} key={item.id}>
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore */}
|
||||||
{provided => <Item provided={provided} item={item} style={adjustedStyle} gap={gap}/>}
|
{provided => <Item provided={provided} item={item} style={adjustedStyle} gap={gap} />}
|
||||||
</Draggable>
|
</Draggable>
|
||||||
);
|
);
|
||||||
}, areEqual);
|
}, areEqual);
|
||||||
@@ -84,116 +82,87 @@ const Row: React.FC<RowProps> = React.memo(({ data: { items, gap }, index, style
|
|||||||
* @example
|
* @example
|
||||||
* <List draggableElements={elements} />
|
* <List draggableElements={elements} />
|
||||||
*/
|
*/
|
||||||
const List: React.FC<ListProps> = ( { draggableElements, itemHeight, listHeight, listWidth, gap=8 }: ListProps) => {
|
const List: React.FC<ListProps> = ({ draggableElements, itemHeight, listHeight, listWidth, gap = 12 }: ListProps) => {
|
||||||
const [items, setItems] = useState(() => initial(draggableElements));
|
const [items, setItems] = useState(() => initial(draggableElements));
|
||||||
|
|
||||||
const onDragEnd = useCallback((result) => {
|
const onDragEnd = useCallback(
|
||||||
if (!result.destination) {
|
result => {
|
||||||
return;
|
if (!result.destination) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
if (result.source.index === result.destination.index) {
|
|
||||||
return;
|
if (result.source.index === result.destination.index) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const newItems = reorder(
|
|
||||||
items,
|
const newItems = reorder(items, result.source.index, result.destination.index);
|
||||||
result.source.index,
|
|
||||||
result.destination.index
|
setItems(newItems as { id: string; content: React.ReactElement }[]);
|
||||||
);
|
},
|
||||||
|
[items]
|
||||||
setItems(newItems as { id: string, content: React.ReactElement }[]);
|
);
|
||||||
}, [items]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ overflow: 'hidden', width: listWidth }}>
|
<div style={{ overflow: 'hidden', width: listWidth }}>
|
||||||
<DragDropContext onDragEnd = {onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<div style = {{
|
<Droppable
|
||||||
display: "flex",
|
droppableId='droppable'
|
||||||
flexDirection: "column",
|
direction='vertical'
|
||||||
alignItems: "center"
|
|
||||||
}}>
|
|
||||||
<Droppable
|
|
||||||
droppableId="droppable"
|
|
||||||
/* mode="virtual" */
|
|
||||||
direction="vertical"
|
|
||||||
renderClone={(provided, snapshot, rubric) => {
|
renderClone={(provided, snapshot, rubric) => {
|
||||||
let { style } = provided.draggableProps;
|
let { style } = provided.draggableProps;
|
||||||
const transform = style?.transform;
|
const transform = style?.transform;
|
||||||
|
|
||||||
if (snapshot.isDragging && transform) {
|
|
||||||
let [, y] = transform
|
|
||||||
.replace('translate(', '')
|
|
||||||
.replace(')', '')
|
|
||||||
.split(',')
|
|
||||||
.map((v) => parseInt(v, 10));
|
|
||||||
|
|
||||||
/*
|
|
||||||
const minTranslateY = -1 * rubric.source.index * itemHeight;
|
|
||||||
const maxTranslateY = (items.length - rubric.source.index - 1) * itemHeight;
|
|
||||||
|
|
||||||
if (y < minTranslateY) {
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (y > maxTranslateY) {
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
style.transform = `translate(0px, ${y}px)`; // Apply constrained y value
|
if (snapshot.isDragging && transform) {
|
||||||
}
|
console.log(transform);
|
||||||
|
let [, _x, y] = transform.match(/translate\(([-\d]+)px, ([-\d]+)px\)/) || [];
|
||||||
return (
|
|
||||||
<Item
|
style.transform = `translate3d(0px, ${y}px, 0px)`; // Apply constrained y value
|
||||||
provided={provided}
|
}
|
||||||
isDragging={snapshot.isDragging}
|
|
||||||
item={items[rubric.source.index]}
|
return (
|
||||||
style = {{
|
<Item
|
||||||
style
|
provided={provided}
|
||||||
}}
|
isDragging={snapshot.isDragging}
|
||||||
/* gap = {gap} */
|
item={items[rubric.source.index]}
|
||||||
/>
|
style={{
|
||||||
)}
|
style,
|
||||||
}
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{(provided) => (
|
{provided => (
|
||||||
<div
|
<div
|
||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
style={{ width: `${listWidth}px` }}
|
style={{ width: `${listWidth}px` }}
|
||||||
className="space-y-2"
|
className=''
|
||||||
>
|
>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<Draggable key={item.id} draggableId={item.id} index={index}>
|
<Draggable key={item.id} draggableId={item.id} index={index}>
|
||||||
{(provided, snapshot) => (
|
{draggableProvided => (
|
||||||
<div
|
<div
|
||||||
ref={provided.innerRef}
|
ref={draggableProvided.innerRef}
|
||||||
key={item.id}
|
{...draggableProvided.draggableProps}
|
||||||
{...provided.draggableProps}
|
{...draggableProvided.dragHandleProps}
|
||||||
{...provided.dragHandleProps}
|
style={{
|
||||||
style={{
|
...draggableProvided.draggableProps.style,
|
||||||
...provided.draggableProps.style,
|
// if last item, don't add margin
|
||||||
marginBottom: '8px',
|
marginBottom: index === items.length - 1 ? '0px' : `${gap}px`,
|
||||||
background: snapshot.isDragging ? '#f4f4f4' : 'transparent',
|
}}
|
||||||
}}
|
>
|
||||||
className="p-2"
|
{item.content}
|
||||||
>
|
</div>
|
||||||
{item.content}
|
)}
|
||||||
</div>
|
</Draggable>
|
||||||
)}
|
))}
|
||||||
</Draggable>
|
|
||||||
))}
|
|
||||||
{provided.placeholder}
|
{provided.placeholder}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</Droppable>
|
||||||
</div>
|
</DragDropContext>
|
||||||
</DragDropContext>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default List;
|
export default List;
|
||||||
|
|||||||
Reference in New Issue
Block a user