mirror of
https://github.com/docmost/docmost.git
synced 2026-07-27 08:54:43 +10:00
feat(bases): drop cards onto empty kanban columns / below last card
This commit is contained in:
@@ -4,6 +4,7 @@ import { KanbanCard } from "./kanban-card";
|
|||||||
import { KanbanColumnHeader } from "./kanban-column-header";
|
import { KanbanColumnHeader } from "./kanban-column-header";
|
||||||
import { KanbanAddCardButton } from "./kanban-add-card-button";
|
import { KanbanAddCardButton } from "./kanban-add-card-button";
|
||||||
import type { CardDropPayload } from "@/features/base/hooks/use-kanban-card-drag";
|
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";
|
import classes from "@/features/base/styles/kanban.module.css";
|
||||||
|
|
||||||
type KanbanColumnProps = {
|
type KanbanColumnProps = {
|
||||||
@@ -21,6 +22,11 @@ export function KanbanColumn({
|
|||||||
onAddCard,
|
onAddCard,
|
||||||
onCardDrop,
|
onCardDrop,
|
||||||
}: KanbanColumnProps) {
|
}: KanbanColumnProps) {
|
||||||
|
const { ref: bodyRef, isOver } = useKanbanColumnDrop({
|
||||||
|
columnKey: column.key,
|
||||||
|
onDrop: onCardDrop,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.column} data-column-key={column.key}>
|
<div className={classes.column} data-column-key={column.key}>
|
||||||
<KanbanColumnHeader
|
<KanbanColumnHeader
|
||||||
@@ -28,7 +34,12 @@ export function KanbanColumn({
|
|||||||
color={column.color}
|
color={column.color}
|
||||||
count={column.rows.length}
|
count={column.rows.length}
|
||||||
/>
|
/>
|
||||||
<div className={classes.columnBody} data-column-body={column.key}>
|
<div
|
||||||
|
ref={bodyRef}
|
||||||
|
className={classes.columnBody}
|
||||||
|
data-column-body={column.key}
|
||||||
|
data-over={isOver || undefined}
|
||||||
|
>
|
||||||
{column.rows.map((row) => (
|
{column.rows.map((row) => (
|
||||||
<KanbanCard
|
<KanbanCard
|
||||||
key={row.id}
|
key={row.id}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
||||||
|
import type { CardDropPayload } from "./use-kanban-card-drag";
|
||||||
|
|
||||||
|
export const COLUMN_BODY_TARGET_ID = "__column-body__";
|
||||||
|
|
||||||
|
export function useKanbanColumnDrop({
|
||||||
|
columnKey,
|
||||||
|
onDrop,
|
||||||
|
}: {
|
||||||
|
columnKey: string;
|
||||||
|
onDrop: (payload: CardDropPayload) => void;
|
||||||
|
}) {
|
||||||
|
const ref = useRef<HTMLDivElement>(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 };
|
||||||
|
}
|
||||||
@@ -52,6 +52,10 @@
|
|||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.columnBody[data-over="true"] {
|
||||||
|
background: var(--mantine-color-blue-0);
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
background: var(--mantine-color-body);
|
background: var(--mantine-color-body);
|
||||||
|
|||||||
Reference in New Issue
Block a user