Adjustments to Drag and Drop features, as well as theme features
This commit is contained in:
parent
9a1aad6ed0
commit
0d7336858c
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { useSortable } from "@dnd-kit/sortable";
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
||||||
import { useDroppable } from "@dnd-kit/core";
|
import { useDndContext, useDroppable } from "@dnd-kit/core";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { GripVertical, MoreVertical, Trash2 } from "lucide-react";
|
import { GripVertical, MoreVertical, Trash2 } from "lucide-react";
|
||||||
|
|
||||||
|
|
@ -21,6 +21,19 @@ import type { CategoryDTO } from "@/types/board";
|
||||||
export function CategoryLane({ category }: { category: CategoryDTO }) {
|
export function CategoryLane({ category }: { category: CategoryDTO }) {
|
||||||
const { removeCategory } = useBoard();
|
const { removeCategory } = useBoard();
|
||||||
|
|
||||||
|
// The lane itself is sortable (so lanes can be reordered), which
|
||||||
|
// registers its *entire* rect -- header, cards, empty space, all of it --
|
||||||
|
// as a droppable. Dragging a group card over empty lane space then had
|
||||||
|
// two overlapping droppables to resolve against: this lane's own
|
||||||
|
// "category" droppable and the narrower "category-dropzone" below. With
|
||||||
|
// `closestCenter`, the much larger lane rect often won, so drop handling
|
||||||
|
// never saw a "group" or "category-dropzone" target and silently did
|
||||||
|
// nothing. Disabling this lane's droppable side while a *group* (not a
|
||||||
|
// lane) is being dragged removes it from collision detection entirely,
|
||||||
|
// leaving only the group cards and the dropzone as valid targets.
|
||||||
|
const { active } = useDndContext();
|
||||||
|
const isDraggingGroup = active?.data.current?.type === "group";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setNodeRef,
|
setNodeRef,
|
||||||
setActivatorNodeRef,
|
setActivatorNodeRef,
|
||||||
|
|
@ -32,6 +45,7 @@ export function CategoryLane({ category }: { category: CategoryDTO }) {
|
||||||
} = useSortable({
|
} = useSortable({
|
||||||
id: category.id,
|
id: category.id,
|
||||||
data: { type: "category" },
|
data: { type: "category" },
|
||||||
|
disabled: { draggable: false, droppable: isDraggingGroup },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { setNodeRef: setDropzoneRef } = useDroppable({
|
const { setNodeRef: setDropzoneRef } = useDroppable({
|
||||||
|
|
@ -75,7 +89,7 @@ export function CategoryLane({ category }: { category: CategoryDTO }) {
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onSelect={() => removeCategory(category.id)}
|
onClick={() => removeCategory(category.id)}
|
||||||
>
|
>
|
||||||
<Trash2 className="size-4" />
|
<Trash2 className="size-4" />
|
||||||
Delete category
|
Delete category
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useSortable } from "@dnd-kit/sortable";
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
|
@ -47,10 +47,18 @@ export function GroupCard({ group }: { group: GroupDTO }) {
|
||||||
// portaled Popover/Dialog content nested inside the card (the notes
|
// portaled Popover/Dialog content nested inside the card (the notes
|
||||||
// editor, the "add to-do" popover). Wiring the same node as the
|
// editor, the "add to-do" popover). Wiring the same node as the
|
||||||
// activator restores the target check.
|
// activator restores the target check.
|
||||||
function setCardRef(node: HTMLDivElement | null) {
|
//
|
||||||
|
// This must be memoized: a fresh function identity on every render makes
|
||||||
|
// React detach+reattach the ref each time, which during a drag (many
|
||||||
|
// re-renders as transform/isDragging change) resets dnd-kit's node
|
||||||
|
// registration and broke drop detection entirely.
|
||||||
|
const setCardRef = useCallback(
|
||||||
|
(node: HTMLDivElement | null) => {
|
||||||
setNodeRef(node);
|
setNodeRef(node);
|
||||||
setActivatorNodeRef(node);
|
setActivatorNodeRef(node);
|
||||||
}
|
},
|
||||||
|
[setNodeRef, setActivatorNodeRef]
|
||||||
|
);
|
||||||
|
|
||||||
const color = getGroupColor(group.color);
|
const color = getGroupColor(group.color);
|
||||||
const borderColor = resolvedTheme === "dark" ? color.dark : color.light;
|
const borderColor = resolvedTheme === "dark" ? color.dark : color.light;
|
||||||
|
|
@ -90,7 +98,7 @@ export function GroupCard({ group }: { group: GroupDTO }) {
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onSelect={() => removeGroup(group.id, group.categoryId)}
|
onClick={() => removeGroup(group.id, group.categoryId)}
|
||||||
>
|
>
|
||||||
<Trash2 className="size-4" />
|
<Trash2 className="size-4" />
|
||||||
Delete group
|
Delete group
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export function ThemeToggle({ collapsed }: { collapsed?: boolean }) {
|
||||||
/>
|
/>
|
||||||
<DropdownMenuContent align="start" side="right">
|
<DropdownMenuContent align="start" side="right">
|
||||||
{OPTIONS.map((option) => (
|
{OPTIONS.map((option) => (
|
||||||
<DropdownMenuItem key={option.value} onSelect={() => setTheme(option.value)}>
|
<DropdownMenuItem key={option.value} onClick={() => setTheme(option.value)}>
|
||||||
<option.icon className="size-4" />
|
<option.icon className="size-4" />
|
||||||
{option.label}
|
{option.label}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue