@etsoo/materialui
Version:
TypeScript Material-UI Implementation
239 lines (238 loc) • 9.45 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DnDItemStyle = void 0;
exports.DnDList = DnDList;
const jsx_runtime_1 = require("react/jsx-runtime");
const Skeleton_1 = __importDefault(require("@mui/material/Skeleton"));
const styles_1 = require("@mui/material/styles");
const react_1 = __importDefault(require("react"));
function SortableItem(props) {
// Destruct
const { id, useSortableType, CSSType, itemRenderer, style = {} } = props;
// Use sortable
const { attributes, listeners, setNodeRef, transform, transition, setActivatorNodeRef } = useSortableType({ id });
const allStyle = {
...style,
transform: CSSType.Transform.toString(transform),
transition
};
const nodeRef = {
style: allStyle,
ref: setNodeRef,
...attributes
};
const actionNodeRef = {
...listeners,
ref: setActivatorNodeRef
};
return itemRenderer(nodeRef, actionNodeRef);
}
/**
* DnD item default style
* @param index Item index
* @param isDragging Is dragging
* @param theme Theme
* @returns Style
*/
const DnDItemStyle = (index, isDragging, theme) => ({
padding: theme.spacing(1),
zIndex: isDragging ? 1 : "auto",
background: isDragging
? theme.palette.primary.light
: index % 2 === 0
? theme.palette.grey[100]
: theme.palette.grey[50]
});
exports.DnDItemStyle = DnDItemStyle;
/**
* DnD (Drag and Drop) sortable list
* @param props Props
* @returns Component
*/
function DnDList(props) {
// Destruct
const { componentProps, height = 360, itemRenderer, labelField, mRef, sortingStrategy, onChange, onFormChange, onDragEnd } = props;
const Component = props.component || react_1.default.Fragment;
// Theme
const theme = (0, styles_1.useTheme)();
// States
const [items, setItems] = react_1.default.useState([]);
const [activeId, setActiveId] = react_1.default.useState();
react_1.default.useEffect(() => {
setItems(props.items);
}, [props.items]);
const doFormChange = react_1.default.useCallback((newItems) => {
if (onFormChange) {
const locals = Array.isArray(newItems) ? newItems : items;
onFormChange(locals);
}
}, [items, onFormChange]);
const changeItems = react_1.default.useCallback((newItems) => {
// Possible to alter items with the handler
if (onChange)
onChange(newItems);
doFormChange(newItems);
// Update state
setItems(newItems);
}, [onChange, doFormChange]);
// Methods
react_1.default.useImperativeHandle(mRef, () => {
return {
addItem(newItem) {
// Existence check
if (items.some((item) => item[labelField] === newItem[labelField])) {
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) => item[labelField] === newItem[labelField])) {
return;
}
newItems.push(newItem);
});
// Update the state
changeItems(newItems);
return newItems.length - items.length;
},
editItem(newItem, index) {
// Existence check
const newIndex = items.findIndex((item) => item[labelField] === newItem[labelField]);
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, labelField, changeItems]);
// Dynamic import library
const [dnd, setDnd] = react_1.default.useState();
react_1.default.useEffect(() => {
Promise.all([
import("@dnd-kit/core"),
import("@dnd-kit/sortable"),
import("@dnd-kit/utilities")
]).then(([{ DndContext }, { SortableContext, useSortable, rectSortingStrategy, rectSwappingStrategy, horizontalListSortingStrategy, verticalListSortingStrategy }, { CSS }]) => {
setDnd([
DndContext,
SortableContext,
useSortable,
rectSortingStrategy,
rectSwappingStrategy,
horizontalListSortingStrategy,
verticalListSortingStrategy,
CSS
]);
});
}, []);
const setupDiv = (div, clearup = false) => {
// Inputs
div
.querySelectorAll("input")
.forEach((input) => clearup
? input.removeEventListener("change", doFormChange)
: input.addEventListener("change", doFormChange));
// Textareas
div
.querySelectorAll("textarea")
.forEach((input) => clearup
? input.removeEventListener("change", doFormChange)
: input.addEventListener("change", doFormChange));
// Select
div
.querySelectorAll("select")
.forEach((input) => clearup
? input.removeEventListener("change", doFormChange)
: input.addEventListener("change", doFormChange));
};
const divRef = react_1.default.useRef();
if (dnd == null) {
return (0, jsx_runtime_1.jsx)(Skeleton_1.default, { variant: "rectangular", width: "100%", height: height });
}
const [DndContextType, SortableContextType, useSortableType, rectSortingStrategyType, rectSwappingStrategyType, horizontalListSortingStrategyType, verticalListSortingStrategyType, CSSType] = dnd;
const strategy = typeof sortingStrategy === "function"
? sortingStrategy()
: sortingStrategy === "rect"
? rectSortingStrategyType
: sortingStrategy === "rectSwapping"
? rectSwappingStrategyType
: sortingStrategy === "horizontal"
? horizontalListSortingStrategyType
: sortingStrategy === "vertical"
? verticalListSortingStrategyType
: undefined;
let getItemStyle = props.getItemStyle;
if (getItemStyle == null) {
getItemStyle = (index, isDragging) => (0, exports.DnDItemStyle)(index, isDragging, theme);
}
// Drag event handlers
function handleDragStart(event) {
const { active } = event;
setActiveId(active.id);
}
function handleDragEnd(event) {
const { active, over } = event;
if (over && active.id !== over.id) {
// Indices
const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over.id);
// Clone
const newItems = [...items];
// Removed item
const [removed] = newItems.splice(oldIndex, 1);
// Insert to the destination index
newItems.splice(newIndex, 0, removed);
changeItems(newItems);
// Drag end handler
if (onDragEnd)
onDragEnd(newItems);
}
setActiveId(undefined);
}
const children = ((0, jsx_runtime_1.jsx)(DndContextType, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: (0, jsx_runtime_1.jsx)(SortableContextType, { items: items, strategy: strategy, children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps, children: items.map((item, index) => {
const id = item.id;
return ((0, jsx_runtime_1.jsx)(SortableItem, { id: id, useSortableType: useSortableType, CSSType: CSSType, style: getItemStyle(index, id === activeId), itemRenderer: (nodeRef, actionNodeRef) => itemRenderer(item, index, nodeRef, actionNodeRef) }, id));
}) }) }) }));
if (onFormChange) {
return ((0, jsx_runtime_1.jsx)("div", { style: { width: "100%" }, ref: (div) => {
if (div && divRef.current != div) {
if (divRef.current) {
setupDiv(divRef.current, true);
}
divRef.current = div;
setupDiv(div);
}
}, children: children }));
}
return children;
}