feat: made List more extensible
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
import React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
||||
import { FixedSizeList, areEqual } from "react-window";
|
||||
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
|
||||
import { FixedSizeList, areEqual } from 'react-window';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
/*Ctrl + f dragHandleProps on PopupCourseBlock.tsx for example implementation of drag handle (two lines of code)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Props for the List component.
|
||||
@@ -10,48 +14,45 @@ import { FixedSizeList, areEqual } from "react-window";
|
||||
export interface ListProps {
|
||||
draggableElements: any[]; //Will later define draggableElements based on what types
|
||||
//of components are draggable.
|
||||
itemHeight: number;
|
||||
listHeight: number;
|
||||
listWidth: number;
|
||||
gap: number; //Impacts the spacing between items in the list
|
||||
|
||||
}
|
||||
|
||||
function initial(draggableElements: any[] = []) {
|
||||
return draggableElements.map((element, index) => ({
|
||||
id: `id:${index}`,
|
||||
content: element
|
||||
content: element as ReactElement,
|
||||
}));
|
||||
}
|
||||
|
||||
function reorder(list, startIndex: number, endIndex: number) {
|
||||
function reorder(list: { id: string, content: ReactElement }[], 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 }) {
|
||||
function getStyle({ provided, style, isDragging, gap }) {
|
||||
const combined = {
|
||||
...style,
|
||||
...provided.draggableProps.style
|
||||
};
|
||||
|
||||
const marginBottom = 8;
|
||||
const withSpacing = {
|
||||
...combined,
|
||||
height: isDragging ? combined.height : combined.height - marginBottom,
|
||||
marginBottom
|
||||
};
|
||||
return withSpacing;
|
||||
//return style;
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
function Item({ provided, item, style, isDragging }) {
|
||||
function Item({ provided, item, style, isDragging, gap }) {
|
||||
return (
|
||||
<div
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
style={getStyle({ provided, style, isDragging })}
|
||||
style={getStyle({ provided, style, isDragging, gap })}
|
||||
className={`item ${isDragging ? "is-dragging" : ""}`}
|
||||
>
|
||||
{item.content}
|
||||
{React.cloneElement(item.content, {dragHandleProps: provided.dragHandleProps})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -59,15 +60,20 @@ function Item({ provided, item, style, isDragging }) {
|
||||
interface RowProps {
|
||||
data: any, //DraggableElements[]; Need to define DraggableElements interface once those components are ready
|
||||
index: number,
|
||||
style: any
|
||||
style: React.CSSProperties,
|
||||
}
|
||||
|
||||
const Row: React.FC<RowProps> = React.memo(({ data: items, index, style }) => {
|
||||
const Row: React.FC<RowProps> = React.memo(({ data: { items, gap }, index, style }) => {
|
||||
const item = items[index];
|
||||
const adjustedStyle = {
|
||||
...style,
|
||||
height: `calc(${style.height}px - ${gap}px)`, // Reduce the height by gap to accommodate the margin
|
||||
marginBottom: `${gap}px` // Add gap as bottom margin
|
||||
};
|
||||
return (
|
||||
<Draggable draggableId={item.id} index={index} key={item.id}>
|
||||
{/*@ts-ignore*/}
|
||||
{provided => <Item provided={provided} item={item} style={style} />}
|
||||
{provided => <Item provided={provided} item={item} style={adjustedStyle} gap={gap}/>}
|
||||
</Draggable>
|
||||
);
|
||||
}, areEqual);
|
||||
@@ -78,15 +84,14 @@ const Row: React.FC<RowProps> = React.memo(({ data: items, index, style }) => {
|
||||
* @example
|
||||
* <List draggableElements={elements} />
|
||||
*/
|
||||
const List: React.FC<ListProps> = ({
|
||||
draggableElements
|
||||
}: ListProps) => {
|
||||
const List: React.FC<ListProps> = ( { draggableElements, itemHeight, listHeight, listWidth, gap=8 }: ListProps) => {
|
||||
const [items, setItems] = useState(() => initial(draggableElements));
|
||||
|
||||
function onDragEnd(result) {
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.source.index === result.destination.index) {
|
||||
return;
|
||||
}
|
||||
@@ -96,10 +101,11 @@ const List: React.FC<ListProps> = ({
|
||||
result.source.index,
|
||||
result.destination.index
|
||||
);
|
||||
setItems(newItems as {id: string, content: any}[]);
|
||||
setItems(newItems as {id: string, content: ReactElement}[]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ overflow: 'hidden', width: listWidth, height: listHeight }}>
|
||||
<DragDropContext onDragEnd = {onDragEnd}>
|
||||
<div style = {{
|
||||
display: "flex",
|
||||
@@ -121,11 +127,21 @@ const List: React.FC<ListProps> = ({
|
||||
.split(',')
|
||||
.map((v) => parseInt(v, 10));
|
||||
|
||||
style.transform = `translate(0px, ${y}px)`;
|
||||
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
|
||||
}
|
||||
|
||||
style.fontFamily = "Inter";
|
||||
|
||||
|
||||
return (
|
||||
<Item
|
||||
provided={provided}
|
||||
@@ -134,18 +150,19 @@ const List: React.FC<ListProps> = ({
|
||||
style = {{
|
||||
style
|
||||
}}
|
||||
gap = {gap}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
>
|
||||
{provided => (
|
||||
<FixedSizeList
|
||||
height={500}
|
||||
height={listHeight}
|
||||
itemCount={items.length}
|
||||
itemSize={80}
|
||||
width={300}
|
||||
itemSize={itemHeight}
|
||||
width={listWidth}
|
||||
outerRef={provided.innerRef}
|
||||
itemData={items}
|
||||
itemData={{ items, gap }}
|
||||
>
|
||||
{Row}
|
||||
</FixedSizeList>
|
||||
@@ -153,7 +170,8 @@ const List: React.FC<ListProps> = ({
|
||||
</Droppable>
|
||||
</div>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default List;
|
||||
export default List;
|
||||
@@ -13,6 +13,7 @@ export interface PopupCourseBlockProps {
|
||||
className?: string;
|
||||
course: Course;
|
||||
colors: CourseColors;
|
||||
dragHandleProps?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,7 +21,7 @@ export interface PopupCourseBlockProps {
|
||||
*
|
||||
* @param props PopupCourseBlockProps
|
||||
*/
|
||||
export default function PopupCourseBlock({ className, course, colors }: PopupCourseBlockProps): JSX.Element {
|
||||
export default function PopupCourseBlock({ className, course, colors, dragHandleProps }: PopupCourseBlockProps): JSX.Element {
|
||||
// whiteText based on secondaryColor
|
||||
const fontColor = pickFontColor(colors.primaryColor);
|
||||
|
||||
@@ -36,6 +37,7 @@ export default function PopupCourseBlock({ className, course, colors }: PopupCou
|
||||
backgroundColor: colors.secondaryColor,
|
||||
}}
|
||||
className='flex cursor-move items-center self-stretch rounded rounded-r-0'
|
||||
{...dragHandleProps}
|
||||
>
|
||||
<DragIndicatorIcon className='h-6 w-6 text-white' />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user