Implemented List component
Used CalendarCourseMeeting component in story for demonstration purposes
This commit is contained in:
67
src/stories/components/List.stories.tsx
Normal file
67
src/stories/components/List.stories.tsx
Normal 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" />
|
||||
],
|
||||
},
|
||||
};
|
||||
141
src/views/components/common/List/List.tsx
Normal file
141
src/views/components/common/List/List.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user