Implemented List component

Used CalendarCourseMeeting component in story for demonstration purposes
This commit is contained in:
knownotunknown
2024-02-02 13:08:55 -06:00
parent 77a1d67af3
commit 3da4a395dd
4 changed files with 323 additions and 21 deletions

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor';
import List from 'src/views/components/common/List/List';
import CalendarCourseMeeting from 'src/views/components/common/CalendarCourseBlock/CalendarCourseMeeting';
const placeholderCourse: Course = new Course({
uniqueId: 123,
number: '311C',
fullName: "311C - Bevo's Default Course",
courseName: "Bevo's Default Course",
department: 'BVO',
creditHours: 3,
status: Status.OPEN,
instructors: [new Instructor({ firstName: '', lastName: 'Bevo', fullName: 'Bevo' })],
isReserved: false,
url: '',
flags: [],
schedule: new CourseSchedule({
meetings: [
new CourseMeeting({
days: [DAY_MAP.M, DAY_MAP.W, DAY_MAP.F],
startTime: 480,
endTime: 570,
location: {
building: 'UTC',
room: '123',
},
}),
],
}),
instructionMode: 'In Person',
semester: {
year: 2024,
season: 'Spring',
},
});
const meta = {
title: 'Components/Common/List',
component: List,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
draggableElements: { control: 'object' },
},
} satisfies Meta<typeof List>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
draggableElements: [
<CalendarCourseMeeting key="1" course={placeholderCourse} meetingIdx={0} color="lightblue" />,
<CalendarCourseMeeting key="2" course={placeholderCourse} meetingIdx={0} color="lightgreen" />,
<CalendarCourseMeeting key="3" course={placeholderCourse} meetingIdx={0} color="lightcoral" />,
<CalendarCourseMeeting key="4" course={placeholderCourse} meetingIdx={0} color="lightgoldenrodyellow" />,
<CalendarCourseMeeting key="5" course={placeholderCourse} meetingIdx={0} color="lightpink" />
],
},
};

View File

@@ -0,0 +1,141 @@
import React from 'react';
import { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { FixedSizeList, areEqual } from "react-window";
/**
* Props for the List component.
*/
export interface ListProps {
draggableElements: any[]; //Will later define draggableElements based on what types
//of components are draggable.
}
function initial(draggableElements: any[] = []) {
return draggableElements.map((element, index) => ({
id: `id:${index}`,
content: element
}));
}
function reorder(list, startIndex: number, endIndex: number) {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
}
function getStyle({ provided, style, isDragging }) {
/*const combined = {
...style,
...provided.draggableProps.style
};
const marginBottom = 8;
const withSpacing = {
...combined,
height: isDragging ? combined.height : combined.height - marginBottom,
marginBottom
};
return withSpacing;*/
return style;
}
function Item({ provided, item, style, isDragging }) {
return (
<div
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
style={getStyle({ provided, style, isDragging })}
className={`item ${isDragging ? "is-dragging" : ""}`}
>
{item.content}
</div>
);
}
interface RowProps {
data: any, //DraggableElements[]; Need to define DraggableElements interface once those components are ready
index: number,
style: any
}
const Row: React.FC<RowProps> = React.memo(({ data: items, index, style }) => {
const item = items[index];
return (
<Draggable draggableId={item.id} index={index} key={item.id}>
{/*@ts-ignore*/}
{provided => <Item provided={provided} item={item} style={style} />}
</Draggable>
);
}, areEqual);
/**
* `List` is a functional component that displays a course meeting.
*
* @example
* <List draggableElements={elements} />
*/
const List: React.FC<ListProps> = ({
draggableElements
}: ListProps) => {
const [items, setItems] = useState(() => initial(draggableElements));
function onDragEnd(result) {
if (!result.destination) {
return;
}
if (result.source.index === result.destination.index) {
return;
}
const newItems = reorder(
items,
result.source.index,
result.destination.index
);
setItems(newItems as {id: string, content: any}[]);
}
return (
<DragDropContext onDragEnd = {onDragEnd}>
<div style = {{
display: "flex",
flexDirection: "column",
alignItems: "center"
}}>
<Droppable
droppableId="droppable"
mode="virtual"
renderClone={(provided, snapshot, rubric) => (
<Item
provided={provided}
isDragging={snapshot.isDragging}
item={items[rubric.source.index]}
style = {{
}}
/>
)}
>
{provided => (
<FixedSizeList
height={500}
itemCount={items.length}
itemSize={80}
width={300}
outerRef={provided.innerRef}
itemData={items}
>
{Row}
</FixedSizeList>
)}
</Droppable>
</div>
</DragDropContext>
);
};
export default List;