react-dnd#DragObjectWithType TypeScript Examples
The following examples show how to use
react-dnd#DragObjectWithType.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ElementListItemDraggable.tsx From openchakra with MIT License | 5 votes |
ElementListItemDraggable: React.FC<Props> = ({
type,
id,
onSelect,
moveItem,
index,
onHover,
onUnhover,
name,
}) => {
const ref = useRef<HTMLDivElement>(null)
const [, drop] = useDrop({
accept: ITEM_TYPE,
hover(item: DragObjectWithType, monitor) {
if (!ref.current) {
return
}
// @ts-ignore
const dragIndex = item.index
const hoverIndex = index
if (dragIndex === hoverIndex) {
return
}
const hoverBoundingRect = ref.current.getBoundingClientRect()
const hoverMiddleY =
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
const clientOffset = monitor.getClientOffset()
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return
}
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return
}
if (moveItem) {
moveItem(dragIndex, hoverIndex)
}
// @ts-ignore
item.index = hoverIndex
},
})
const [{ isDragging }, drag] = useDrag({
item: { type: ITEM_TYPE, id, index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
})
const opacity = isDragging ? 0 : 1
drag(drop(ref))
const onSelectElement = () => {
onSelect(id)
}
const onMouseOver = () => {
onHover(id)
}
return (
<ElementListItem
ref={ref}
onSelect={onSelectElement}
opacity={opacity}
onMouseOver={onMouseOver}
onMouseOut={onUnhover}
type={type}
draggable
name={name}
/>
)
}
Example #2
Source File: DropZone.tsx From next-core with GNU General Public License v3.0 | 4 votes |
export function DropZone({
nodeUid,
isRoot,
separateCanvas,
isPortalCanvas,
independentPortalCanvas,
canvasIndex,
mountPoint,
fullscreen,
delegatedContext,
dropZoneStyle,
dropZoneBodyStyle,
slotContentLayout,
showOutlineIfEmpty,
hiddenWrapper = true,
emptyClassName,
}: DropZoneProps): React.ReactElement {
const dropZoneBody = React.useRef<HTMLDivElement>();
const [dropPositionCursor, setDropPositionCursor] =
React.useState<DropPositionCursor>(null);
const dropPositionCursorRef = React.useRef<DropPositionCursor>();
const contextMenuStatus = useBuilderContextMenuStatus();
const manager = useBuilderDataManager();
const { nodes, edges, wrapperNode } = useBuilderData();
const isWrapper = hiddenWrapper && isRoot && !isEmpty(wrapperNode);
const node = useBuilderNode({ nodeUid, isRoot, isWrapper });
const groupedChildNodes = useBuilderGroupedChildNodes({
nodeUid,
isRoot,
doNotExpandTemplates: isWrapper,
isWrapper,
});
const isGeneralizedPortalCanvas = independentPortalCanvas
? canvasIndex > 0
: isPortalCanvas;
const hasTabs = separateCanvas || independentPortalCanvas;
const canDrop = useCanDrop();
const refinedSlotContentLayout =
slotContentLayout ?? EditorSlotContentLayout.BLOCK;
const selfChildNodes = React.useMemo(
() =>
groupedChildNodes.find((group) => group.mountPoint === mountPoint)
?.childNodes ?? [],
[groupedChildNodes, mountPoint]
);
const canvasList = useCanvasList(selfChildNodes);
const selfChildNodesInCurrentCanvas = React.useMemo(
() =>
separateCanvas
? selfChildNodes.filter((child) =>
Boolean(Number(Boolean(isPortalCanvas)) ^ Number(!child.portal))
)
: independentPortalCanvas
? canvasList[clamp(canvasIndex ?? 0, 0, canvasList.length - 1)]
: selfChildNodes,
[
canvasIndex,
independentPortalCanvas,
isPortalCanvas,
selfChildNodes,
canvasList,
separateCanvas,
]
);
const canvasSettings = React.useMemo(
() =>
selfChildNodesInCurrentCanvas[0]?.$$parsedProperties
._canvas_ as BuilderCanvasSettings,
[selfChildNodesInCurrentCanvas]
);
const getDroppingIndexInFullCanvas = React.useCallback(
(droppingIndexInCurrentCanvas: number) => {
if (!hasTabs) {
return droppingIndexInCurrentCanvas;
}
if (selfChildNodesInCurrentCanvas.length > 0) {
const cursorNode =
selfChildNodesInCurrentCanvas[
droppingIndexInCurrentCanvas === 0
? 0
: droppingIndexInCurrentCanvas - 1
];
return (
selfChildNodes.findIndex((child) => child === cursorNode) +
(droppingIndexInCurrentCanvas === 0 ? 0 : 1)
);
}
return isGeneralizedPortalCanvas ? selfChildNodes.length : 0;
},
[
hasTabs,
selfChildNodesInCurrentCanvas,
isGeneralizedPortalCanvas,
selfChildNodes,
]
);
const getDroppingContext = React.useCallback(() => {
if (delegatedContext) {
const siblingGroups = getBuilderGroupedChildNodes({
nodeUid: delegatedContext.templateUid,
nodes,
edges,
doNotExpandTemplates: true,
});
return {
droppingParentUid: delegatedContext.templateUid,
droppingParentInstanceId: nodes.find(
(item) => item.$$uid === delegatedContext.templateUid
).instanceId,
droppingMountPoint: delegatedContext.templateMountPoint,
droppingChildNodes:
siblingGroups.find(
(group) => group.mountPoint === delegatedContext.templateMountPoint
)?.childNodes ?? [],
droppingSiblingGroups: siblingGroups,
};
}
return {
droppingParentUid: node.$$uid,
droppingParentInstanceId: isWrapper
? wrapperNode.instanceId
: node.instanceId,
droppingMountPoint: mountPoint,
droppingChildNodes: selfChildNodes,
droppingSiblingGroups: groupedChildNodes,
};
}, [
delegatedContext,
edges,
groupedChildNodes,
mountPoint,
node,
nodes,
selfChildNodes,
isWrapper,
wrapperNode,
]);
const [{ isDraggingOverCurrent }, dropRef] = useDrop({
accept: [
BuilderDataTransferType.NODE_TO_ADD,
BuilderDataTransferType.NODE_TO_MOVE,
BuilderDataTransferType.SNIPPET_TO_APPLY,
],
canDrop: (
item: DragObjectWithType &
(
| BuilderDataTransferPayloadOfNodeToAdd
| BuilderDataTransferPayloadOfNodeToMove
)
) =>
independentPortalCanvas && isGeneralizedPortalCanvas
? selfChildNodesInCurrentCanvas.length === 0
: item.type === BuilderDataTransferType.NODE_TO_ADD ||
item.type === BuilderDataTransferType.SNIPPET_TO_APPLY ||
isRoot ||
canDrop(
(item as BuilderDataTransferPayloadOfNodeToMove).nodeUid,
nodeUid
),
collect: (monitor) => ({
isDraggingOverCurrent:
monitor.isOver({ shallow: true }) && monitor.canDrop(),
}),
hover: (item, monitor) => {
if (monitor.isOver({ shallow: true }) && monitor.canDrop()) {
const { x, y } = monitor.getClientOffset();
dropPositionCursorRef.current = getDropPosition(
x,
y,
dropZoneBody.current.parentElement,
dropZoneBody.current
);
setDropPositionCursor(dropPositionCursorRef.current);
}
},
drop: (item, monitor) => {
if (!monitor.didDrop()) {
const { type, ...data } = item;
processDrop({
type: type as BuilderDataTransferType,
data,
droppingIndex: getDroppingIndexInFullCanvas(
dropPositionCursorRef.current.index
),
isPortalCanvas: isGeneralizedPortalCanvas,
manager,
...getDroppingContext(),
});
}
},
});
React.useEffect(() => {
manager.updateDroppingStatus(
delegatedContext ? delegatedContext.templateUid : node.$$uid,
delegatedContext ? delegatedContext.templateMountPoint : mountPoint,
isDraggingOverCurrent
);
}, [isDraggingOverCurrent, mountPoint, manager, delegatedContext, node]);
const droppable =
!!delegatedContext ||
isWrapper ||
!(node.$$isExpandableTemplate || node.$$isTemplateInternalNode);
const dropZoneRef = React.useRef<HTMLElement>();
const dropZoneRefCallback = React.useCallback(
(element: HTMLElement) => {
dropZoneRef.current = element;
if (droppable) {
dropRef(element);
}
},
[dropRef, droppable]
);
const handleContextMenu = React.useCallback(
(event: React.MouseEvent) => {
// `event.stopPropagation()` not working across bricks.
if (
!isGeneralizedPortalCanvas &&
isCurrentTargetByClassName(
event.target as HTMLElement,
dropZoneRef.current
)
) {
event.preventDefault();
manager.contextMenuChange({
active: true,
node,
x: event.clientX,
y: event.clientY,
});
}
},
[isGeneralizedPortalCanvas, manager, node]
);
return (
<div
ref={dropZoneRefCallback}
className={classNames(
styles.dropZone,
isRoot
? classNames(
styles.isRoot,
canvasSettings?.mode &&
String(canvasSettings.mode)
.split(/\s+/g)
.map((mode) => styles[`mode-${mode}`]),
{
[styles.fullscreen]: fullscreen,
[styles.hasTabs]: hasTabs,
}
)
: styles.isSlot,
{
[styles.isPortalCanvas]: isGeneralizedPortalCanvas,
[styles.dropping]: isDraggingOverCurrent,
[styles.active]:
isRoot &&
contextMenuStatus.active &&
contextMenuStatus.node.$$uid === node.$$uid,
[styles.showOutlineIfEmpty]:
!isRoot && showOutlineIfEmpty && selfChildNodes.length === 0,
[styles.slotContentLayoutBlock]:
refinedSlotContentLayout === EditorSlotContentLayout.BLOCK,
[styles.slotContentLayoutInline]:
refinedSlotContentLayout === EditorSlotContentLayout.INLINE,
[styles.slotContentLayoutGrid]:
refinedSlotContentLayout === EditorSlotContentLayout.GRID,
}
)}
style={dropZoneStyle}
onContextMenu={isRoot ? handleContextMenu : null}
>
<div
ref={dropZoneBody}
className={classNames(
styles.dropZoneBody,
selfChildNodesInCurrentCanvas.length === 0 && emptyClassName
)}
data-slot-id={mountPoint}
style={dropZoneBodyStyle}
>
{selfChildNodesInCurrentCanvas.map((child) => (
<EditorBrickAsComponent
key={child.$$uid}
node={child}
slotContentLayout={refinedSlotContentLayout}
/>
))}
</div>
{
<div
className={classNames(
styles.dropCursor,
dropPositionCursor?.isVertical
? styles.dropCursorVertical
: styles.dropCursorHorizontal
)}
style={{
top: dropPositionCursor?.y,
left: dropPositionCursor?.x,
height: dropPositionCursor?.height,
}}
></div>
}
</div>
);
}
Example #3
Source File: kanban.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
PureKanban = (props: IKanbanProps) => {
const { data, boards, execOperation, setBoard, setIsDrag, isDrag, setCurrentCard, currentCard, CardRender, ...rest } =
props;
const {
id: boardId,
title: boardTitle,
cards: boardCards,
pageNo: propsPageNo,
operations,
total = 0,
pageSize = 20,
...dataRest
} = data || {};
const titleArr = map(boards, 'title');
const otherTitle = without(titleArr, boardTitle);
const [pageNo, setPageNo] = React.useState(propsPageNo);
const [cards, setCards] = React.useState(boardCards || []);
const [title, setTitle] = React.useState(boardTitle);
const [showShadow, setShowShadow] = React.useState(false);
const cardType = 'kanban-info-card';
const boardDataRef = {
id: boardId,
title: boardTitle,
pageNo: propsPageNo,
operations,
total,
pageSize,
...dataRest,
};
useUpdateEffect(() => {
if (propsPageNo > pageNo) {
boardCards && setCards((prev) => prev.concat(boardCards));
} else {
setCards(boardCards || []);
}
setPageNo(propsPageNo);
}, [boardCards, propsPageNo]);
const boardLoadMoreOp = operations?.boardLoadMore;
const hasMore = boardLoadMoreOp && total > cards.length;
const [{ isOver, isAllowDrop }, drop] = useDrop({
accept: cardType,
drop: (item: Merge<DragObjectWithType, { data: ICardData }>) => {
const { cardMoveTo } = item.data.operations;
const targetKeys = cardMoveTo.serverData?.allowedTargetBoardIDs || [];
if (!targetKeys?.includes(boardId)) {
setCurrentCard(null);
return;
}
setCurrentCard(item.data);
const dragColKey = item.data.boardId;
const dropColKey = boardId;
let newTargetKeys = [...targetKeys];
if (!newTargetKeys.includes(dragColKey)) {
newTargetKeys.push(dragColKey);
}
newTargetKeys = newTargetKeys.filter((t) => t !== dropColKey);
const newItem = produce(item, (draft: { data: Obj }) => {
draft.data.operations.cardMoveTo.serverData.allowedTargetBoardIDs = newTargetKeys;
});
setBoard((prev) => {
return prev.map((col) => {
if (col.id === dropColKey) {
return {
...col,
cards: col.cards ? [newItem.data, ...col.cards] : [newItem.data],
total: +(col.total || 0) + 1,
};
} else if (col.id === dragColKey) {
return {
...col,
cards: col.cards?.filter((a) => a.id !== newItem.data.id),
total: Math.max((col.total || 0) - 1, 0),
};
}
return col;
});
});
execOperation({
reload: true,
...cardMoveTo,
clientData: { targetBoardID: boardId, dataRef: item.data.dataRef },
});
},
collect: (monitor) => {
const item = monitor?.getItem && monitor?.getItem();
const targetKeys = get(item, 'data.operations.cardMoveTo.serverData.allowedTargetBoardIDs') || [];
let _isAllowDrop = true;
if (!targetKeys?.length || !targetKeys.includes(boardId)) {
_isAllowDrop = false;
}
return {
isOver: monitor.isOver(),
isAllowDrop: _isAllowDrop,
};
},
});
const changeData = (item: CP_KANBAN.ICard) => {
const { operations: cardOp } = item;
return {
...item,
boardId,
operations: {
...(cardOp?.cardMoveTo ? { cardMoveTo: { key: 'cardMoveTo', ...cardOp.cardMoveTo } } : {}),
},
dataRef: item,
};
};
let cls = isOver ? 'drag-over' : '';
cls = isDrag && !isAllowDrop ? `drop-disable ${cls}` : cls;
cls = isDrag && !isOver ? `not-drag ${cls}` : cls;
const deleteBoardOp = operations?.boardDelete;
const deleteAuth = deleteBoardOp?.disabled !== true;
const updateBoardOp = operations?.boardUpdate;
const updateAuth = updateBoardOp?.disabled !== true;
const doUpdate = () => {
if (title === boardTitle) return;
if (!title) {
setTitle(boardTitle);
return notify('error', i18n.t('can not be empty'));
}
if (otherTitle.includes(title)) {
setTitle(boardTitle);
return notify('error', i18n.t('{name} already exists', { name: boardTitle }));
}
execOperation({
key: 'boardUpdate',
reload: true,
...operations?.boardUpdate,
clientData: { dataRef: data, title },
});
};
const handleScroll = (e: any) => {
setShowShadow(e.target.scrollTop !== 0);
};
const loadMore = () => {
execOperation({
key: 'boardLoadMore',
reload: true,
...boardLoadMoreOp,
clientData: {
pageNo: pageNo + 1,
pageSize,
dataRef: boardDataRef,
},
} as CP_COMMON.Operation);
};
return (
<div className={classnames(`cp-kanban-col ${cls}`, { 'cp-kanban-col-special-pdd': updateBoardOp })} ref={drop}>
<div
className={`flex justify-between items-center cp-kanban-col-header ${showShadow ? 'shadow' : ''} ${
updateBoardOp ? 'inp' : ''
}`}
>
<div className="text-base font-medium text-default-8 flex-1 flex items-center ">
{updateBoardOp ? (
updateAuth ? (
<Input
className="text-base font-medium cp-kanban-label-input"
value={title}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setTitle(e.target.value)}
onPressEnter={doUpdate}
onBlur={doUpdate}
/>
) : (
<Tooltip title={updateBoardOp.disabledTip || i18n.t('common:No permission to operate')}>
<Input className="text-base font-medium cp-kanban-label-input update-disabled" readOnly value={title} />
</Tooltip>
)
) : (
title
)}
<div className="text-default-8 ml-1 text-sm px-2.5 rounded-lg bg-default-06">{total}</div>
</div>
{deleteBoardOp ? (
deleteBoardOp.confirm ? (
<WithAuth pass={deleteAuth} noAuthTip={deleteBoardOp.disabledTip}>
<Popconfirm
title={deleteBoardOp.confirm}
onConfirm={() =>
execOperation({
key: 'boardDelete',
reload: true,
...deleteBoardOp,
clientData: { dataRef: boardDataRef },
})
}
>
<ErdaIcon type="delete1" className="ml-3 cursor-pointer" />
</Popconfirm>
</WithAuth>
) : (
<WithAuth pass={deleteAuth} noAuthTip={deleteBoardOp.disabledTip}>
<ErdaIcon
type="delete1"
className="ml-3 cursor-pointer"
onClick={() =>
execOperation({
key: 'boardDelete',
reload: true,
...deleteBoardOp,
clientData: { dataRef: boardDataRef },
})
}
/>
</WithAuth>
)
) : null}
</div>
<div className="cp-kanban-col-content" onScroll={handleScroll}>
{map(cards, (item) => {
const curDragOp = item.operations?.cardMoveTo;
return (
<CardItem
key={item.id}
card={changeData(item)}
CardRender={CardRender}
cardType={cardType}
draggable={curDragOp && !curDragOp.disabled}
className={`${isDrag ? 'hidden' : ''} kanban-list-item ${
currentCard?.id === item.id ? 'dragged-card' : ''
}`}
setIsDrag={setIsDrag}
onClick={() => {
rest?.customOp?.clickCard?.(item);
}}
/>
);
})}
{hasMore && !isDrag ? (
<div className="hover-active py-1 text-center load-more" onClick={() => loadMore()}>
{i18n.t('load more')}
</div>
) : null}
</div>
</div>
);
}