@etsoo/materialui
Version:
TypeScript Material-UI Implementation
144 lines (143 loc) • 5.47 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import React from "react";
import { useTheme } from "@mui/material/styles";
import { isSortableOperation, useSortable } from "@dnd-kit/react/sortable";
import { DragDropProvider } from "@dnd-kit/react";
/**
* DnD sortable item default style
* @param index Item index
* @param isDragging Is dragging
* @param theme Theme
* @returns Style
*/
export const DnDSortableItemStyle = (index, isDragging, theme) => ({
padding: theme.spacing(1),
transform: isDragging ? "scale(1.03)" : "none",
zIndex: isDragging ? 1 : "auto",
boxShadow: isDragging
? `-1px 0 8px 0 ${theme.palette.grey[400]}, 0px 8px 8px 0 ${theme.palette.grey[200]}`
: "none",
background: isDragging
? theme.palette.primary.light
: index % 2 === 0
? theme.palette.grey[100]
: theme.palette.grey[50]
});
function SortableItem(props) {
const theme = useTheme();
const { id, data, index, itemRenderer, itemStyle = (index, isDragging) => DnDSortableItemStyle(index, isDragging, theme) } = props;
const state = useSortable({ id, data, index });
const style = itemStyle(index, state.isDragging);
return itemRenderer(data, style, state);
}
/**
* DnD sortable list component
* @param props Props
* @returns Component
*/
export function DnDSortableList(props) {
// Destruct
const Component = props.component || React.Fragment;
const { componentProps, idField, itemRenderer, itemStyle, labelField, mRef, onChange, onDragStart, onDragEnd } = props;
const idFn = typeof idField === "function"
? idField
: (item) => !idField
? Reflect.get(item, "id")
: item[idField];
const labelFn = React.useCallback(typeof labelField === "function"
? labelField
: (item) => item[labelField], [labelField]);
// States
const [items, setItems] = React.useState([]);
React.useEffect(() => {
setItems(props.items);
}, [props.items]);
const changeItems = React.useCallback((newItems) => {
// Possible to alter items with the handler
onChange?.(newItems);
// Update state
setItems(newItems);
}, [onChange]);
// Methods
React.useImperativeHandle(mRef, () => {
return {
addItem(newItem) {
// Existence check
if (items.some((item) => labelFn(item) === labelFn(newItem))) {
return false;
}
// Clone
const newItems = [newItem, ...items];
// Update the state
changeItems(newItems);
return true;
},
addItems(inputItems) {
// Clone
const newItems = [...items];
// Insert items
inputItems.forEach((newItem) => {
// Existence check
if (newItems.some((item) => labelFn(item) === labelFn(newItem))) {
return;
}
newItems.push(newItem);
});
// Update the state
changeItems(newItems);
return newItems.length - items.length;
},
editItem(newItem, index) {
// Existence check
const newIndex = items.findIndex((item) => labelFn(item) === labelFn(newItem));
if (newIndex >= 0 && newIndex !== index) {
// Label field is the same with a different item
return false;
}
// Clone
const newItems = [...items];
// Remove the item
newItems.splice(index, 1, newItem);
// Update the state
changeItems(newItems);
return true;
},
deleteItem(index) {
// Clone
const newItems = [...items];
// Remove the item
newItems.splice(index, 1);
// Update the state
changeItems(newItems);
},
getItems() {
return items;
}
};
}, [items, labelFn, changeItems]);
function handleDragEnd(...args) {
// Event
const event = args[0];
// Cancelled
if (event.canceled)
return;
if (isSortableOperation(event.operation) && event.operation.source) {
const { initialIndex, index } = event.operation.source;
if (initialIndex === index)
return;
// Clone
const newItems = [...items];
// Removed item
const [removed] = newItems.splice(initialIndex, 1);
// Insert to the destination index
newItems.splice(index, 0, removed);
changeItems(newItems);
// Drag end handler
onDragEnd?.(newItems, ...args);
}
}
return (_jsx(DragDropProvider, { onDragStart: (event) => onDragStart?.(items, event), onDragEnd: (event, manager) => handleDragEnd(event, manager), children: _jsx(Component, { ...componentProps, children: items.map((item, index) => {
const id = idFn(item);
return (_jsx(SortableItem, { id: id, index: index, data: item, itemRenderer: itemRenderer, itemStyle: itemStyle }, id));
}) }) }));
}