|
3 | 3 | import { Badge } from "@/components/ui/badge";
|
4 | 4 | import { Button } from "@/components/ui/button";
|
5 | 5 | import * as Kanban from "@/registry/default/ui/kanban";
|
6 |
| -import { |
7 |
| - SortableContext, |
8 |
| - horizontalListSortingStrategy, |
9 |
| -} from "@dnd-kit/sortable"; |
10 | 6 | import { GripVertical } from "lucide-react";
|
11 | 7 | import * as React from "react";
|
12 | 8 |
|
@@ -113,36 +109,25 @@ export default function KanbanDemo() {
|
113 | 109 | >
|
114 | 110 | <Kanban.Board>
|
115 | 111 | {Object.entries(columns).map(([columnId, tasks]) => (
|
116 |
| - <Kanban.Column key={columnId} value={columnId}> |
117 |
| - <div className="flex items-center justify-between"> |
118 |
| - <div className="flex items-center gap-2"> |
119 |
| - <h3 className="font-semibold">{COLUMN_TITLES[columnId]}</h3> |
120 |
| - <Badge |
121 |
| - variant="secondary" |
122 |
| - className="pointer-events-none rounded-sm" |
123 |
| - > |
124 |
| - {tasks?.length ?? 0} |
125 |
| - </Badge> |
126 |
| - </div> |
127 |
| - <Kanban.ColumnHandle asChild> |
128 |
| - <Button variant="ghost" size="icon"> |
129 |
| - <GripVertical className="h-4 w-4" /> |
130 |
| - </Button> |
131 |
| - </Kanban.ColumnHandle> |
132 |
| - </div> |
133 |
| - <div className="flex flex-col gap-2 p-0.5"> |
134 |
| - {tasks?.map((task) => ( |
135 |
| - <TaskCard key={task.id} task={task} asHandle /> |
136 |
| - ))} |
137 |
| - </div> |
138 |
| - </Kanban.Column> |
| 112 | + <TaskColumn key={columnId} columnId={columnId} tasks={tasks} /> |
139 | 113 | ))}
|
140 | 114 | </Kanban.Board>
|
141 | 115 | <Kanban.Overlay>
|
142 |
| - {(activeItem) => { |
| 116 | + {({ value, type }) => { |
| 117 | + if (type === "column") { |
| 118 | + const [columnId, tasks] = |
| 119 | + Object.entries(columns).find(([id]) => id === value) ?? []; |
| 120 | + |
| 121 | + if (!columnId || !tasks) return null; |
| 122 | + |
| 123 | + return ( |
| 124 | + <TaskColumn key={columnId} columnId={columnId} tasks={tasks} /> |
| 125 | + ); |
| 126 | + } |
| 127 | + |
143 | 128 | const task = Object.values(columns)
|
144 | 129 | .flat()
|
145 |
| - .find((task) => task.id === activeItem.value); |
| 130 | + .find((task) => task.id === value); |
146 | 131 |
|
147 | 132 | if (!task) return null;
|
148 | 133 |
|
@@ -190,3 +175,37 @@ function TaskCard({ task, ...props }: TaskCardProps) {
|
190 | 175 | </Kanban.Item>
|
191 | 176 | );
|
192 | 177 | }
|
| 178 | + |
| 179 | +interface TaskColumnProps |
| 180 | + extends Omit< |
| 181 | + React.ComponentProps<typeof Kanban.Column>, |
| 182 | + "value" | "children" |
| 183 | + > { |
| 184 | + columnId: string; |
| 185 | + tasks: Task[]; |
| 186 | +} |
| 187 | + |
| 188 | +function TaskColumn({ columnId, tasks, ...props }: TaskColumnProps) { |
| 189 | + return ( |
| 190 | + <Kanban.Column key={columnId} value={columnId} {...props}> |
| 191 | + <div className="flex items-center justify-between"> |
| 192 | + <div className="flex items-center gap-2"> |
| 193 | + <h3 className="font-semibold">{COLUMN_TITLES[columnId]}</h3> |
| 194 | + <Badge variant="secondary" className="pointer-events-none rounded-sm"> |
| 195 | + {tasks?.length ?? 0} |
| 196 | + </Badge> |
| 197 | + </div> |
| 198 | + <Kanban.ColumnHandle asChild> |
| 199 | + <Button variant="ghost" size="icon"> |
| 200 | + <GripVertical className="h-4 w-4" /> |
| 201 | + </Button> |
| 202 | + </Kanban.ColumnHandle> |
| 203 | + </div> |
| 204 | + <div className="flex flex-col gap-2 p-0.5"> |
| 205 | + {tasks?.map((task) => ( |
| 206 | + <TaskCard key={task.id} task={task} asHandle /> |
| 207 | + ))} |
| 208 | + </div> |
| 209 | + </Kanban.Column> |
| 210 | + ); |
| 211 | +} |
0 commit comments