diff --git a/apps/client/src/features/base/components/views/kanban/kanban-column.tsx b/apps/client/src/features/base/components/views/kanban/kanban-column.tsx index 4459d20d0..1c8b28da9 100644 --- a/apps/client/src/features/base/components/views/kanban/kanban-column.tsx +++ b/apps/client/src/features/base/components/views/kanban/kanban-column.tsx @@ -4,6 +4,7 @@ import { KanbanCard } from "./kanban-card"; import { KanbanColumnHeader } from "./kanban-column-header"; import { KanbanAddCardButton } from "./kanban-add-card-button"; import type { CardDropPayload } from "@/features/base/hooks/use-kanban-card-drag"; +import { useKanbanColumnDrop } from "@/features/base/hooks/use-kanban-column-drop"; import classes from "@/features/base/styles/kanban.module.css"; type KanbanColumnProps = { @@ -21,6 +22,11 @@ export function KanbanColumn({ onAddCard, onCardDrop, }: KanbanColumnProps) { + const { ref: bodyRef, isOver } = useKanbanColumnDrop({ + columnKey: column.key, + onDrop: onCardDrop, + }); + return (
-
+
{column.rows.map((row) => ( void; +}) { + const ref = useRef(null); + const [isOver, setIsOver] = useState(false); + // Keep onDrop fresh without re-registering the effect each render. + const onDropRef = useRef(onDrop); + onDropRef.current = onDrop; + + useEffect(() => { + const el = ref.current; + if (!el) return; + return dropTargetForElements({ + element: el, + canDrop: ({ source }) => source.data.type === "base-kanban-card", + getIsSticky: () => true, + onDragEnter: () => setIsOver(true), + onDragLeave: () => setIsOver(false), + onDrop: ({ source }) => { + setIsOver(false); + if (source.data.type !== "base-kanban-card") return; + // If a card-level target inside this column already handled the + // drop, Pragmatic-dnd only invokes the innermost matching target, + // so this column-body handler won't fire. When it does fire, the + // user missed every card — append to the column. + onDropRef.current({ + draggedCardId: source.data.cardId as string, + targetCardId: COLUMN_BODY_TARGET_ID, + edge: "bottom", + sourceColumnKey: source.data.columnKey as string, + targetColumnKey: columnKey, + }); + }, + }); + }, [columnKey]); + + return { ref, isOver }; +} diff --git a/apps/client/src/features/base/styles/kanban.module.css b/apps/client/src/features/base/styles/kanban.module.css index 70bebbefd..9832303f6 100644 --- a/apps/client/src/features/base/styles/kanban.module.css +++ b/apps/client/src/features/base/styles/kanban.module.css @@ -52,6 +52,10 @@ min-height: 80px; } +.columnBody[data-over="true"] { + background: var(--mantine-color-blue-0); +} + .card { padding: 10px 12px; background: var(--mantine-color-body);