diff --git a/webapp/src/components/kanban/kanbanCard.tsx b/webapp/src/components/kanban/kanbanCard.tsx index 01b6a0f45..856cd3908 100644 --- a/webapp/src/components/kanban/kanbanCard.tsx +++ b/webapp/src/components/kanban/kanbanCard.tsx @@ -29,7 +29,7 @@ type Props = { const KanbanCard = React.memo((props: Props) => { const cardRef = useRef(null) - const {card, intl, isSelected} = props + const {card, intl} = props const [{isDragging}, drag] = useDrag(() => ({ type: 'card', item: card, diff --git a/webapp/src/components/table/table.tsx b/webapp/src/components/table/table.tsx index f459e4aa0..fccedbfc0 100644 --- a/webapp/src/components/table/table.tsx +++ b/webapp/src/components/table/table.tsx @@ -54,6 +54,29 @@ const Table = (props: Props) => { }, }), [changed]) + const onDropToCard = (srcCard: Card, dstCard: Card) => { + Utils.log(`onDropToCard: ${dstCard.title}`) + const {activeView} = boardTree + const {selectedCardIds} = props + + const draggedCardIds = Array.from(new Set(selectedCardIds).add(srcCard.id)) + const description = draggedCardIds.length > 1 ? `drag ${draggedCardIds.length} cards` : 'drag card' + + // Update dstCard order + let cardOrder = Array.from(new Set([...activeView.cardOrder, ...boardTree.cards.map((o) => o.id)])) + const isDraggingDown = cardOrder.indexOf(srcCard.id) <= cardOrder.indexOf(dstCard.id) + cardOrder = cardOrder.filter((id) => !draggedCardIds.includes(id)) + let destIndex = cardOrder.indexOf(dstCard.id) + if (isDraggingDown) { + destIndex += 1 + } + cardOrder.splice(destIndex, 0, ...draggedCardIds) + + mutator.performAsUndoGroup(async () => { + await mutator.changeViewCardOrder(activeView, cardOrder, description) + }) + } + const onDropToColumn = async (template: IPropertyTemplate, container: IPropertyTemplate) => { Utils.log(`ondrop. Source column: ${template.name}, dest column: ${container.name}`) @@ -139,6 +162,7 @@ const Table = (props: Props) => { }} showCard={props.showCard} readonly={props.readonly} + onDrop={onDropToCard} />) return tableRow diff --git a/webapp/src/components/table/tableRow.tsx b/webapp/src/components/table/tableRow.tsx index add56b354..45aa18a22 100644 --- a/webapp/src/components/table/tableRow.tsx +++ b/webapp/src/components/table/tableRow.tsx @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React, {useState, useRef, useEffect} from 'react' import {FormattedMessage} from 'react-intl' +import {useDrop, useDrag} from 'react-dnd' import {Card} from '../../blocks/card' import {Constants} from '../../constants' @@ -22,11 +23,31 @@ type Props = { showCard: (cardId: string) => void readonly: boolean onClick?: (e: React.MouseEvent) => void + onDrop: (srcCard: Card, dstCard: Card) => void } const TableRow = React.memo((props: Props) => { const titleRef = useRef(null) const [title, setTitle] = useState(props.card.title) + const cardRef = useRef(null) + const {card} = props + const [{isDragging}, drag] = useDrag(() => ({ + type: 'card', + item: card, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + }), [card]) + + const [{isOver}, drop] = useDrop(() => ({ + accept: 'card', + collect: (monitor) => ({ + isOver: monitor.isOver(), + }), + drop: (item: Card) => { + props.onDrop(item, card) + }, + }), [card, props.onDrop]) useEffect(() => { if (props.focusOnMount) { @@ -38,15 +59,22 @@ const TableRow = React.memo((props: Props) => { return Math.max(Constants.minColumnWidth, props.boardTree.activeView.columnWidths[templateId] || 0) } - const {boardTree, card, onSaveWithEnter} = props + const {boardTree, onSaveWithEnter} = props const {board, activeView} = boardTree - const className = props.isSelected ? 'TableRow octo-table-row selected' : 'TableRow octo-table-row' + let className = props.isSelected ? 'TableRow octo-table-row selected' : 'TableRow octo-table-row' + if (isOver) { + className += ' dragover' + } + + drop(drag(cardRef)) return (
{/* Name / title */}