Skip to content

Commit dbd63bb

Browse files
committed
feat: update kanban
1 parent 86b6cbd commit dbd63bb

File tree

3 files changed

+74
-53
lines changed

3 files changed

+74
-53
lines changed

docs/registry/default/ui/kanban.tsx

+34-22
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from "@dnd-kit/core";
2424
import {
2525
SortableContext,
26+
type SortableContextProps,
2627
arrayMove,
2728
sortableKeyboardCoordinates,
2829
useSortable,
@@ -55,6 +56,8 @@ const KANBAN_ERROR = {
5556
interface KanbanContextValue<T> {
5657
id: string;
5758
items: Record<UniqueIdentifier, T[]>;
59+
modifiers: DndContextProps["modifiers"];
60+
strategy: SortableContextProps["strategy"];
5861
activeId: UniqueIdentifier | null;
5962
setActiveId: (id: UniqueIdentifier | null) => void;
6063
getItemValue: (item: T) => UniqueIdentifier;
@@ -78,7 +81,9 @@ type KanbanProps<T> = DndContextProps & {
7881
value: Record<UniqueIdentifier, T[]>;
7982
onValueChange?: (columns: Record<UniqueIdentifier, T[]>) => void;
8083
onMove?: (event: DragEndEvent) => void;
84+
modifiers?: DndContextProps["modifiers"];
8185
sensors?: DndContextProps["sensors"];
86+
strategy?: SortableContextProps["strategy"];
8287
flatCursor?: boolean;
8388
} & (T extends object
8489
? { getItemValue: (item: T) => UniqueIdentifier }
@@ -89,7 +94,9 @@ function Kanban<T>(props: KanbanProps<T>) {
8994
id = React.useId(),
9095
value,
9196
onValueChange,
97+
modifiers,
9298
sensors: sensorsProp,
99+
strategy = verticalListSortingStrategy,
93100
onMove,
94101
getItemValue: getItemValueProp,
95102
flatCursor = false,
@@ -125,18 +132,6 @@ function Kanban<T>(props: KanbanProps<T>) {
125132
[getItemValueProp],
126133
);
127134

128-
const contextValue = React.useMemo<KanbanContextValue<T>>(
129-
() => ({
130-
id,
131-
items: value,
132-
activeId,
133-
setActiveId,
134-
getItemValue,
135-
flatCursor,
136-
}),
137-
[id, value, activeId, getItemValue, flatCursor],
138-
);
139-
140135
const getContainer = React.useCallback(
141136
(id: UniqueIdentifier) => {
142137
if (id in value) return id;
@@ -203,10 +198,25 @@ function Kanban<T>(props: KanbanProps<T>) {
203198
[activeId, value, getItemValue],
204199
);
205200

201+
const contextValue = React.useMemo<KanbanContextValue<T>>(
202+
() => ({
203+
id,
204+
items: value,
205+
modifiers,
206+
strategy,
207+
activeId,
208+
setActiveId,
209+
getItemValue,
210+
flatCursor,
211+
}),
212+
[id, value, activeId, modifiers, strategy, getItemValue, flatCursor],
213+
);
214+
206215
return (
207216
<KanbanContext.Provider value={contextValue as KanbanContextValue<unknown>}>
208217
<DndContext
209218
id={id}
219+
modifiers={modifiers}
210220
sensors={sensorsProp ?? sensors}
211221
collisionDetection={collisionDetection}
212222
measuring={{
@@ -444,7 +454,7 @@ interface KanbanColumnProps extends SlotProps {
444454

445455
const KanbanColumn = React.forwardRef<HTMLDivElement, KanbanColumnProps>(
446456
(props, forwardedRef) => {
447-
const { value, asChild, ...columnProps } = props;
457+
const { value, asChild, className, ...columnProps } = props;
448458
const context = useKanbanContext("board");
449459
const inBoard = React.useContext(KanbanBoardContext);
450460

@@ -453,19 +463,20 @@ const KanbanColumn = React.forwardRef<HTMLDivElement, KanbanColumnProps>(
453463
}
454464

455465
const ColumnSlot = asChild ? Slot : "div";
456-
const items = context.items[value] ?? [];
466+
467+
const items = React.useMemo(() => {
468+
const items = context.items[value] ?? [];
469+
return items.map((item) => context.getItemValue(item));
470+
}, [context.items, value, context.getItemValue]);
457471

458472
return (
459-
<SortableContext
460-
items={items.map((item) => context.getItemValue(item))}
461-
strategy={verticalListSortingStrategy}
462-
>
473+
<SortableContext strategy={context.strategy} items={items}>
463474
<ColumnSlot
464475
{...columnProps}
465476
ref={forwardedRef}
466477
className={cn(
467-
"flex h-full min-h-[200px] w-[300px] flex-none flex-col gap-2 rounded-lg bg-muted/50 p-4",
468-
props.className,
478+
"flex size-full flex-col gap-2 rounded-lg border bg-card p-4 text-card-foreground",
479+
className,
469480
)}
470481
/>
471482
</SortableContext>
@@ -543,6 +554,7 @@ const KanbanItem = React.forwardRef<HTMLDivElement, KanbanItemProps>(
543554
ref={composeRefs(forwardedRef, (node) => setNodeRef(node))}
544555
style={composedStyle}
545556
className={cn(
557+
"data-[dragging]:focus-visible:outline-none data-[dragging]:focus-visible:ring-1 data-[dragging]:focus-visible:ring-ring data-[dragging]:focus-visible:ring-offset-1",
546558
{
547559
"touch-none select-none": asGrip,
548560
"cursor-default": context.flatCursor,
@@ -656,17 +668,17 @@ const ItemGrip = KanbanItemGrip;
656668
const Overlay = KanbanOverlay;
657669

658670
export {
659-
Root,
660671
Board,
661672
Column,
662673
Item,
663674
ItemGrip,
664-
Overlay,
665675
//
666676
Kanban,
667677
KanbanBoard,
668678
KanbanColumn,
669679
KanbanItem,
670680
KanbanItemGrip,
671681
KanbanOverlay,
682+
Overlay,
683+
Root,
672684
};

docs/registry/default/ui/sortable.tsx

+30-31
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type SortableProps<T> = DndContextProps & {
101101
collisionDetection?: DndContextProps["collisionDetection"];
102102
modifiers?: DndContextProps["modifiers"];
103103
sensors?: DndContextProps["sensors"];
104+
strategy?: SortableContextProps["strategy"];
104105
orientation?: "vertical" | "horizontal" | "mixed";
105106
flatCursor?: boolean;
106107
} & (T extends object
@@ -115,6 +116,7 @@ function Sortable<T>(props: SortableProps<T>) {
115116
collisionDetection,
116117
modifiers,
117118
sensors: sensorsProp,
119+
strategy,
118120
onMove,
119121
orientation = "vertical",
120122
flatCursor = false,
@@ -152,7 +154,7 @@ function Sortable<T>(props: SortableProps<T>) {
152154
id,
153155
items: value,
154156
modifiers: modifiers ?? config.modifiers,
155-
strategy: config.strategy,
157+
strategy: strategy ?? config.strategy,
156158
activeId,
157159
setActiveId,
158160
getItemValue,
@@ -162,6 +164,7 @@ function Sortable<T>(props: SortableProps<T>) {
162164
id,
163165
value,
164166
modifiers,
167+
strategy,
165168
config.modifiers,
166169
config.strategy,
167170
activeId,
@@ -256,12 +259,14 @@ const SortableContent = React.forwardRef<HTMLDivElement, SortableContentProps>(
256259

257260
const ContentSlot = asChild ? Slot : "div";
258261

262+
const items = React.useMemo(() => {
263+
return context.items.map((item) => context.getItemValue(item));
264+
}, [context.items, context.getItemValue]);
265+
259266
return (
260267
<SortableContentContext.Provider value={true}>
261268
<SortableContext
262-
items={context.items.map((item) => ({
263-
id: context.getItemValue(item),
264-
}))}
269+
items={items}
265270
strategy={strategyProp ?? context.strategy}
266271
>
267272
<ContentSlot {...contentProps} ref={forwardedRef} />
@@ -367,37 +372,34 @@ const SortableItem = React.forwardRef<HTMLDivElement, SortableItemProps>(
367372
const { value, style, asGrip, asChild, className, ...itemProps } = props;
368373
const context = useSortableContext("item");
369374
const id = React.useId();
370-
const sortableContext = useSortable({ id: value });
375+
const {
376+
attributes,
377+
listeners,
378+
setNodeRef,
379+
transform,
380+
transition,
381+
isDragging,
382+
} = useSortable({ id: value });
371383

372384
const composedStyle = React.useMemo<React.CSSProperties>(() => {
373385
return {
374-
opacity: sortableContext.isDragging ? 0.5 : 1,
375-
transform: CSS.Translate.toString(sortableContext.transform),
376-
transition: sortableContext.transition,
386+
opacity: isDragging ? 0.5 : 1,
387+
transform: CSS.Translate.toString(transform),
388+
transition,
377389
...style,
378390
};
379-
}, [
380-
sortableContext.isDragging,
381-
sortableContext.transform,
382-
sortableContext.transition,
383-
style,
384-
]);
391+
}, [isDragging, transform, transition, style]);
385392

386393
const ItemSlot = asChild ? Slot : "div";
387394

388395
const itemContext = React.useMemo<SortableItemContextValue>(
389396
() => ({
390397
id,
391-
attributes: sortableContext.attributes,
392-
listeners: sortableContext.listeners,
393-
isDragging: sortableContext.isDragging,
398+
attributes,
399+
listeners,
400+
isDragging,
394401
}),
395-
[
396-
id,
397-
sortableContext.attributes,
398-
sortableContext.listeners,
399-
sortableContext.isDragging,
400-
],
402+
[id, attributes, listeners, isDragging],
401403
);
402404

403405
if (value === "") {
@@ -408,25 +410,22 @@ const SortableItem = React.forwardRef<HTMLDivElement, SortableItemProps>(
408410
<SortableItemContext.Provider value={itemContext}>
409411
<ItemSlot
410412
id={id}
411-
data-dragging={sortableContext.isDragging ? "" : undefined}
413+
data-dragging={isDragging ? "" : undefined}
412414
{...itemProps}
413-
ref={composeRefs(forwardedRef, (node) =>
414-
sortableContext.setNodeRef(node),
415-
)}
415+
ref={composeRefs(forwardedRef, (node) => setNodeRef(node))}
416416
style={composedStyle}
417417
className={cn(
418418
"data-[dragging]:focus-visible:outline-none data-[dragging]:focus-visible:ring-1 data-[dragging]:focus-visible:ring-ring data-[dragging]:focus-visible:ring-offset-1",
419419
{
420420
"touch-none select-none": asGrip,
421421
"cursor-default": context.flatCursor,
422422
"data-[dragging]:cursor-grabbing": !context.flatCursor,
423-
"cursor-grab":
424-
!sortableContext.isDragging && asGrip && !context.flatCursor,
423+
"cursor-grab": !isDragging && asGrip && !context.flatCursor,
425424
},
426425
className,
427426
)}
428-
{...(asGrip ? sortableContext.attributes : {})}
429-
{...(asGrip ? sortableContext.listeners : {})}
427+
{...(asGrip ? attributes : {})}
428+
{...(asGrip ? listeners : {})}
430429
/>
431430
</SortableItemContext.Provider>
432431
);

docs/types/docs/sortable.ts

+10
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ export interface RootProps<TData> extends DndContextProps {
5454
*/
5555
modifiers?: DndContextProps["modifiers"];
5656

57+
/**
58+
* The strategy to use for sorting the items.
59+
* @default
60+
* Automatically selected based on orientation:
61+
* - vertical: verticalListSortingStrategy
62+
* - horizontal: horizontalListSortingStrategy
63+
* - mixed: undefined
64+
*/
65+
strategy?: SortableContextProps["strategy"];
66+
5767
/**
5868
* An array of sensors that will be used to detect the position of the sortable items.\n
5969
* @default

0 commit comments

Comments
 (0)