linkmore-design
Version:
🌈 🚀lm组件库。🚀
589 lines (581 loc) • 18.3 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TRASH_ID = void 0;
exports.default = DndContainer;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _react = _interopRequireWildcard(require("react"));
var _reactDom = require("react-dom");
var _core = require("@dnd-kit/core");
var _sortable = require("@dnd-kit/sortable");
var _utilities = require("@dnd-kit/utilities");
var _multipleContainersKeyboardCoordinates = _interopRequireDefault(require("../multipleContainersKeyboardCoordinates"));
var _Item = require("./Item");
var _Container = require("./Container");
var _classnames = _interopRequireDefault(require("classnames"));
const defaultDropAnimationSideEffects = options => ({
active,
dragOverlay
}) => {
const originalStyles = {};
const {
styles,
className
} = options;
if (styles?.active) {
for (const [key, value] of Object.entries(styles.active)) {
if (value === undefined) {
continue;
}
originalStyles[key] = active.node.style.getPropertyValue(key);
active.node.style.setProperty(key, value);
}
}
if (styles?.dragOverlay) {
for (const [key, value] of Object.entries(styles.dragOverlay)) {
if (value === undefined) {
continue;
}
dragOverlay.node.style.setProperty(key, value);
}
}
if (className?.active) {
active.node.classList.add(className.active);
}
if (className?.dragOverlay) {
dragOverlay.node.classList.add(className.dragOverlay);
}
return function cleanup() {
for (const [key, value] of Object.entries(originalStyles)) {
active.node.style.setProperty(key, value);
}
if (className?.active) {
active.node.classList.remove(className.active);
}
};
};
const animateLayoutChanges = args => (0, _sortable.defaultAnimateLayoutChanges)({
...args,
wasDragging: true
});
function DroppableContainer({
children,
columns = 1,
disabled,
id,
items,
style,
...props
}) {
const {
active,
attributes,
isDragging,
listeners,
over,
setNodeRef,
transition,
transform
} = (0, _sortable.useSortable)({
id,
data: {
type: 'container',
children: items
},
animateLayoutChanges
});
const isOverContainer = over ? id === over.id && active?.data.current?.type !== 'container' || items.includes(over.id) : false;
return /*#__PURE__*/_react.default.createElement(_Container.Container, (0, _extends2.default)({
ref: disabled ? undefined : setNodeRef,
style: {
...style,
transition,
transform: _utilities.CSS.Translate.toString(transform),
opacity: isDragging ? 0.5 : undefined
},
hover: isOverContainer,
handleProps: {
...attributes,
...listeners
},
columns: columns
}, props), children);
}
const dropAnimation = {
sideEffects: defaultDropAnimationSideEffects({
styles: {
active: {
opacity: '0.5'
}
}
})
};
const TRASH_ID = 'void';
// const PLACEHOLDER_ID = 'placeholder';
// const empty: UniqueIdentifier[] = [];
exports.TRASH_ID = TRASH_ID;
function DndContainer({
// 调整比例尺
adjustScale = false,
itemCount = 3,
cancelDrop,
columns,
handle = false,
items: initialItems,
containerStyle,
coordinateGetter = _multipleContainersKeyboardCoordinates.default,
getItemStyles = () => ({}),
wrapperStyle = () => ({}),
modifiers,
renderItem,
trashable = false,
scrollable,
children,
updateItems = () => ({}),
rowGroupTitle = '行分组',
colGroupTitle = '列分组',
filterColumns = []
}) {
const [items, setItems] = (0, _react.useState)(() => initialItems);
(0, _react.useEffect)(() => {
setItems(initialItems);
}, [initialItems]);
const [containers, setContainers] = (0, _react.useState)(Object.keys(items));
const [activeId, setActiveId] = (0, _react.useState)(null);
const lastOverId = (0, _react.useRef)(null);
// 是否移动到新容器
const recentlyMovedToNewContainer = (0, _react.useRef)(false);
const isSortingContainer = activeId ? containers.includes(activeId) : false;
// 执行顺序: onDragStart,collisionDetection,onDragOver,[...collisionDetection],onDragEnd
/**
* Custom collision detection strategy optimized for multiple containers
* 针对多个容器优化的自定义碰撞检测策略
* - First, find any droppable containers intersecting with the pointer.
* - If there are none, find intersecting containers with the active draggable.
* - If there are no intersecting containers, return the last matched intersection
* 1.首先,找到与指针相交的任何可放置容器。2.如果没有,则查找具有活动可拖动对象的相交容器。3.如果没有交叉容器,则返回最后匹配的交叉
*/
const collisionDetectionStrategy = (0, _react.useCallback)(args => {
if (activeId && activeId in items) {
return (0, _core.closestCenter)({
...args,
droppableContainers: args.droppableContainers.filter(container => container.id in items)
});
}
// Start by finding any intersecting droppable
const pointerIntersections = (0, _core.pointerWithin)(args);
const intersections = pointerIntersections.length > 0 ?
// If there are droppables intersecting with the pointer, return those
pointerIntersections : (0, _core.rectIntersection)(args);
let overId = (0, _core.getFirstCollision)(intersections, 'id');
if (overId != null) {
if (overId === TRASH_ID) {
// If the intersecting droppable is the trash, return early
// Remove this if you're not using trashable functionality in your app
return intersections;
}
if (overId in items) {
const containerItems = items[overId];
// If a container is matched and it contains items (columns 'A', 'B', 'C')
if (containerItems.length > 0) {
// 返回该容器中最近的滴管
// Return the closest droppable within that container
overId = (0, _core.closestCenter)({
...args,
droppableContainers: args.droppableContainers.filter(container => container.id !== overId && containerItems.includes(container.id))
})[0]?.id;
}
}
lastOverId.current = overId;
return [{
id: overId
}];
}
// When a draggable item moves to a new container, the layout may shift
// and the `overId` may become `null`. We manually set the cached `lastOverId`
// to the id of the draggable item that was moved to the new container, otherwise
// the previous `overId` will be returned which can cause items to incorrectly shift positions
if (recentlyMovedToNewContainer.current) {
lastOverId.current = activeId;
}
// If no droppable is matched, return the last match
return lastOverId.current ? [{
id: lastOverId.current
}] : [];
}, [activeId, items]);
const [clonedItems, setClonedItems] = (0, _react.useState)(null);
// 传感器
const sensors = (0, _core.useSensors)((0, _core.useSensor)(_core.MouseSensor), (0, _core.useSensor)(_core.TouchSensor), (0, _core.useSensor)(_core.KeyboardSensor, {
coordinateGetter
}));
const findContainer = id => {
if (id in items) {
return id;
}
return Object.keys(items).find(key => items[key].includes(id));
};
const getIndex = id => {
const container = findContainer(id);
if (!container) {
return -1;
}
const index = items[container].indexOf(id);
return index;
};
const onDragCancel = () => {
if (clonedItems) {
// Reset items to their original state in case items have been
// Dragged across containers
setItems(clonedItems);
}
setActiveId(null);
setClonedItems(null);
};
(0, _react.useEffect)(() => {
const id = requestAnimationFrame(() => {
recentlyMovedToNewContainer.current = false;
});
return () => cancelAnimationFrame(id);
}, [items]);
const isOk = (active, over) => {
const item = filterColumns.find(item => item.title === active.id);
if (over.id === 'row' || items?.row?.includes(over.id)) {
return item && item.rowGroup;
} else if (over.id === 'col' || items?.col?.includes(over.id)) {
return item && item.colGroup;
}
return true;
};
const topContainers = containers.filter(item => item !== 'tableHeader');
const tableContainer = containers.filter(item => item === 'tableHeader')[0];
return /*#__PURE__*/_react.default.createElement(_core.DndContext, {
sensors: sensors,
collisionDetection: collisionDetectionStrategy,
measuring: {
droppable: {
strategy: _core.MeasuringStrategy.Always
}
},
onDragStart: ({
active
}) => {
setActiveId(active.id);
setClonedItems(items);
},
onDragOver: ({
active,
over
}) => {
const overId = over?.id;
if (overId == null || overId === TRASH_ID || active.id in items) {
return;
}
if (!isOk(active, over)) {
return;
}
const overContainer = findContainer(overId);
const activeContainer = findContainer(active.id);
if (!overContainer || !activeContainer) {
return;
}
if (activeContainer !== overContainer) {
setItems(items => {
const activeItems = items[activeContainer];
const overItems = items[overContainer];
const overIndex = overItems.indexOf(overId);
const activeIndex = activeItems.indexOf(active.id);
let newIndex;
if (overId in items) {
newIndex = overItems.length + 1;
} else {
// 当前激活的是否在目标下面
const isBelowOverItem = over && active.rect.current.translated && active.rect.current.translated.top > over.rect.top + over.rect.height;
const modifier = isBelowOverItem ? 1 : 0;
newIndex = overIndex >= 0 ? overIndex + modifier : overItems.length + 1;
}
recentlyMovedToNewContainer.current = true;
return {
...items,
[activeContainer]: items[activeContainer].filter(item => item !== active.id),
[overContainer]: [...items[overContainer].slice(0, newIndex), items[activeContainer][activeIndex], ...items[overContainer].slice(newIndex, items[overContainer].length)]
};
});
}
},
onDragEnd: ({
active,
over
}) => {
// 容器拖拽
if (active.id in items && over?.id) {
setContainers(containers => {
const activeIndex = containers.indexOf(active.id);
const overIndex = containers.indexOf(over.id);
return (0, _sortable.arrayMove)(containers, activeIndex, overIndex);
});
}
const activeContainer = findContainer(active.id);
if (!activeContainer) {
setActiveId(null);
return;
}
const overId = over?.id;
if (overId == null) {
setActiveId(null);
return;
}
if (overId === TRASH_ID) {
console.log('TRASH_ID', overId);
setItems(items => ({
...items,
[activeContainer]: items[activeContainer].filter(id => id !== activeId)
}));
setActiveId(null);
return;
}
const overContainer = findContainer(overId);
if (overContainer) {
const activeIndex = items[activeContainer].indexOf(active.id);
const overIndex = items[overContainer].indexOf(overId);
if (activeIndex !== overIndex) {
const newItems = {
...items,
[overContainer]: (0, _sortable.arrayMove)(items[overContainer], activeIndex, overIndex)
};
// setItems(newItems);
updateItems(newItems);
} else {
updateItems(items);
}
}
setActiveId(null);
},
cancelDrop: cancelDrop,
onDragCancel: onDragCancel,
modifiers: modifiers
}, /*#__PURE__*/_react.default.createElement("div", {
style: {
display: 'flex',
boxSizing: 'border-box'
}
}, topContainers.map(containerId => {
return /*#__PURE__*/_react.default.createElement(DroppableContainer, {
key: containerId,
id: containerId,
label: containerId === 'row' ? rowGroupTitle : colGroupTitle,
columns: columns,
items: items[containerId],
scrollable: true,
style: containerStyle,
unstyled: false
}, /*#__PURE__*/_react.default.createElement(_sortable.SortableContext, {
items: items[containerId],
strategy: _sortable.horizontalListSortingStrategy
}, items[containerId].map((value, index) => {
return /*#__PURE__*/_react.default.createElement(SortableItem, {
disabled: isSortingContainer,
key: value,
id: value,
index: index,
handle: true,
style: getItemStyles,
wrapperStyle: wrapperStyle,
renderItem: renderItem,
containerId: containerId,
getIndex: getIndex,
onRemove: () => {
updateItems(items, value);
}
});
})));
})), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)('dnd_table_container'),
style: {
display: 'flex',
boxSizing: 'border-box',
justifyContent: 'column'
}
}, /*#__PURE__*/_react.default.createElement(DroppableContainer, {
key: tableContainer,
id: tableContainer,
columns: 1,
items: items[tableContainer],
scrollable: scrollable,
style: containerStyle,
unstyled: false,
ulClassName: 'ulClassName',
isNoneBorder: true
}, /*#__PURE__*/_react.default.createElement(_sortable.SortableContext, {
items: items[tableContainer],
strategy: _sortable.horizontalListSortingStrategy
}, /*#__PURE__*/_react.default.createElement("li", {
style: {
width: '100%'
}
}, children)))), /*#__PURE__*/(0, _reactDom.createPortal)( /*#__PURE__*/_react.default.createElement(_core.DragOverlay, {
adjustScale: adjustScale,
dropAnimation: dropAnimation
}, activeId ? containers.includes(activeId) ? renderContainerDragOverlay(activeId) : renderSortableItemDragOverlay(activeId) : null), document.body), trashable && activeId && !containers.includes(activeId) ? /*#__PURE__*/_react.default.createElement(Trash, {
id: TRASH_ID
}) : null);
function renderSortableItemDragOverlay(id) {
return /*#__PURE__*/_react.default.createElement(_Item.Item, {
value: id,
handle: handle,
style: getItemStyles({
containerId: findContainer(id),
overIndex: -1,
index: getIndex(id),
value: id,
isSorting: true,
isDragging: true,
isDragOverlay: true
}),
color: getColor(id),
wrapperStyle: wrapperStyle({
index: 0
}),
renderItem: renderItem,
dragOverlay: true
});
}
function renderContainerDragOverlay(containerId) {
return /*#__PURE__*/_react.default.createElement(_Container.Container, {
label: `Column ${containerId}`,
columns: columns,
style: {
height: '100%'
},
shadow: true,
unstyled: false
}, items[containerId].map((item, index) => /*#__PURE__*/_react.default.createElement(_Item.Item, {
key: item,
value: item,
handle: handle,
style: getItemStyles({
containerId,
overIndex: -1,
index: getIndex(item),
value: item,
isDragging: false,
isSorting: false,
isDragOverlay: false
}),
color: getColor(item),
wrapperStyle: wrapperStyle({
index
}),
renderItem: renderItem
})));
}
// function getNextContainerId() {
// const containerIds = Object.keys(items);
// const lastContainerId = containerIds[containerIds.length - 1];
// return String.fromCharCode(lastContainerId.charCodeAt(0) + 1);
// }
}
function getColor(id) {
switch (String(id)[0]) {
case 'A':
return '#7193f1';
case 'B':
return '#ffda6c';
case 'C':
return '#00bcd4';
case 'D':
return '#ef769f';
}
return undefined;
}
function Trash({
id
}) {
const {
setNodeRef,
isOver
} = (0, _core.useDroppable)({
id
});
return /*#__PURE__*/_react.default.createElement("div", {
ref: setNodeRef,
style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'fixed',
left: '50%',
marginLeft: -150,
bottom: 20,
width: 300,
height: 60,
borderRadius: 5,
border: '1px solid',
borderColor: isOver ? 'red' : '#DDD'
}
}, "Drop here to delete");
}
function SortableItem({
disabled,
id,
index,
handle,
renderItem,
style,
containerId,
getIndex,
wrapperStyle,
onRemove
}) {
const {
setNodeRef,
listeners,
isDragging,
isSorting,
over,
overIndex,
transform,
transition
} = (0, _sortable.useSortable)({
id
});
const mounted = useMountStatus();
const mountedWhileDragging = isDragging && !mounted;
return /*#__PURE__*/_react.default.createElement(_Item.Item, {
ref: disabled ? undefined : setNodeRef,
value: id,
dragging: isDragging,
sorting: isSorting,
handle: handle,
handleProps: undefined,
index: index,
wrapperStyle: wrapperStyle({
index
}),
style: style({
index,
value: id,
isDragging,
isSorting,
overIndex: over ? getIndex(over.id) : overIndex,
containerId
}),
color: getColor(id),
transition: transition,
transform: transform,
fadeIn: mountedWhileDragging,
listeners: listeners,
renderItem: renderItem,
onRemove: onRemove
});
}
function useMountStatus() {
const [isMounted, setIsMounted] = (0, _react.useState)(false);
(0, _react.useEffect)(() => {
const timeout = setTimeout(() => setIsMounted(true), 500);
return () => clearTimeout(timeout);
}, []);
return isMounted;
}