@object-ui/plugin-kanban 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/CHANGELOG.md +9 -0
  3. package/dist/{KanbanEnhanced-BPIKjTDv.js → KanbanEnhanced-CXDSLlGR.js} +338 -324
  4. package/dist/KanbanImpl-4dgoNPtI.js +350 -0
  5. package/dist/index-CyNcIIS1.js +1077 -0
  6. package/dist/index.js +9 -4
  7. package/dist/index.umd.cjs +4 -4
  8. package/dist/src/CardTemplates.d.ts +25 -0
  9. package/dist/src/CardTemplates.d.ts.map +1 -0
  10. package/dist/src/InlineQuickAdd.d.ts +29 -0
  11. package/dist/src/InlineQuickAdd.d.ts.map +1 -0
  12. package/dist/src/KanbanEnhanced.d.ts +12 -1
  13. package/dist/src/KanbanEnhanced.d.ts.map +1 -1
  14. package/dist/src/KanbanImpl.d.ts +15 -1
  15. package/dist/src/KanbanImpl.d.ts.map +1 -1
  16. package/dist/src/ObjectKanban.d.ts.map +1 -1
  17. package/dist/src/index.d.ts +22 -1
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/types.d.ts +97 -1
  20. package/dist/src/types.d.ts.map +1 -1
  21. package/dist/src/useColumnWidths.d.ts +30 -0
  22. package/dist/src/useColumnWidths.d.ts.map +1 -0
  23. package/dist/src/useCrossSwimlaneMove.d.ts +46 -0
  24. package/dist/src/useCrossSwimlaneMove.d.ts.map +1 -0
  25. package/dist/src/useQuickAddReorder.d.ts +28 -0
  26. package/dist/src/useQuickAddReorder.d.ts.map +1 -0
  27. package/package.json +9 -9
  28. package/src/CardTemplates.tsx +123 -0
  29. package/src/InlineQuickAdd.tsx +189 -0
  30. package/src/KanbanEnhanced.tsx +140 -9
  31. package/src/KanbanImpl.tsx +266 -23
  32. package/src/ObjectKanban.tsx +39 -24
  33. package/src/__tests__/KanbanGrouping.test.tsx +164 -0
  34. package/src/__tests__/KanbanSwimlanes.test.tsx +194 -0
  35. package/src/__tests__/ObjectKanbanTitle.test.tsx +93 -0
  36. package/src/__tests__/SwimlanePersistence.test.tsx +159 -0
  37. package/src/__tests__/performance-benchmark.test.tsx +14 -14
  38. package/src/__tests__/phase13-features.test.tsx +387 -0
  39. package/src/index.tsx +49 -6
  40. package/src/types.ts +106 -1
  41. package/src/useColumnWidths.ts +125 -0
  42. package/src/useCrossSwimlaneMove.ts +116 -0
  43. package/src/useQuickAddReorder.ts +107 -0
  44. package/dist/KanbanImpl-BfOKAnJS.js +0 -194
  45. package/dist/index-CWGTi2xn.js +0 -600
@@ -0,0 +1,125 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { useState, useCallback, useMemo } from "react"
10
+ import type { KanbanColumn, ColumnWidthConfig } from "./types"
11
+
12
+ const STORAGE_KEY = "objectui:kanban-column-widths"
13
+
14
+ export interface UseColumnWidthsOptions {
15
+ /** Columns on the board */
16
+ columns: KanbanColumn[]
17
+ /** Default width in pixels applied to every column */
18
+ defaultWidth?: number
19
+ /** Minimum allowed column width in pixels */
20
+ minWidth?: number
21
+ /** Maximum allowed column width in pixels */
22
+ maxWidth?: number
23
+ /** Optional persistence key suffix (to scope per-board) */
24
+ storageKey?: string
25
+ }
26
+
27
+ export interface UseColumnWidthsReturn {
28
+ /** Get the current width for a column (in px) */
29
+ getColumnWidth: (columnId: string) => number
30
+ /** Set an override width for a column */
31
+ setColumnWidth: (columnId: string, width: number) => void
32
+ /** Reset all overrides back to default widths */
33
+ resetWidths: () => void
34
+ /** The full config (useful for serialization) */
35
+ config: ColumnWidthConfig
36
+ }
37
+
38
+ function loadPersistedWidths(key: string): Record<string, number> {
39
+ try {
40
+ const raw = localStorage.getItem(key)
41
+ if (raw) {
42
+ const parsed = JSON.parse(raw)
43
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
44
+ return parsed as Record<string, number>
45
+ }
46
+ }
47
+ } catch {
48
+ /* ignore corrupt data */
49
+ }
50
+ return {}
51
+ }
52
+
53
+ function persistWidths(key: string, overrides: Record<string, number>) {
54
+ try {
55
+ localStorage.setItem(key, JSON.stringify(overrides))
56
+ } catch {
57
+ /* quota exceeded */
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Hook for managing custom per-column widths with localStorage persistence.
63
+ * Supports per-column overrides clamped between minWidth and maxWidth.
64
+ */
65
+ export function useColumnWidths({
66
+ columns: _columns,
67
+ defaultWidth = 320,
68
+ minWidth = 200,
69
+ maxWidth = 600,
70
+ storageKey,
71
+ }: UseColumnWidthsOptions): UseColumnWidthsReturn {
72
+ const fullKey = storageKey ? `${STORAGE_KEY}:${storageKey}` : STORAGE_KEY
73
+
74
+ const [overrides, setOverrides] = useState<Record<string, number>>(() =>
75
+ loadPersistedWidths(fullKey),
76
+ )
77
+
78
+ const clamp = useCallback(
79
+ (value: number) => Math.max(minWidth, Math.min(maxWidth, value)),
80
+ [minWidth, maxWidth],
81
+ )
82
+
83
+ const getColumnWidth = useCallback(
84
+ (columnId: string): number => {
85
+ const override = overrides[columnId]
86
+ return override != null ? clamp(override) : defaultWidth
87
+ },
88
+ [overrides, defaultWidth, clamp],
89
+ )
90
+
91
+ const setColumnWidth = useCallback(
92
+ (columnId: string, width: number) => {
93
+ const clamped = clamp(width)
94
+ setOverrides(prev => {
95
+ const next = { ...prev, [columnId]: clamped }
96
+ persistWidths(fullKey, next)
97
+ return next
98
+ })
99
+ },
100
+ [clamp, fullKey],
101
+ )
102
+
103
+ const resetWidths = useCallback(() => {
104
+ setOverrides({})
105
+ try {
106
+ localStorage.removeItem(fullKey)
107
+ } catch {
108
+ /* ignore */
109
+ }
110
+ }, [fullKey])
111
+
112
+ const config: ColumnWidthConfig = useMemo(
113
+ () => ({
114
+ defaultWidth,
115
+ minWidth,
116
+ maxWidth,
117
+ overrides: { ...overrides },
118
+ }),
119
+ [defaultWidth, minWidth, maxWidth, overrides],
120
+ )
121
+
122
+ return { getColumnWidth, setColumnWidth, resetWidths, config }
123
+ }
124
+
125
+ export default useColumnWidths
@@ -0,0 +1,116 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { useState, useCallback, useMemo } from "react"
10
+
11
+ export interface Swimlane {
12
+ /** Unique identifier for the swimlane row */
13
+ id: string
14
+ /** Display label */
15
+ title: string
16
+ /** Optional list of swimlane IDs this lane accepts cards from. Omit for unrestricted. */
17
+ acceptFrom?: string[]
18
+ }
19
+
20
+ export interface CrossSwimlaneMoveEvent {
21
+ cardId: string
22
+ fromSwimlane: string
23
+ toSwimlane: string
24
+ columnId: string
25
+ }
26
+
27
+ export interface UseCrossSwimlaneOptions {
28
+ /** Swimlane definitions */
29
+ swimlanes: Swimlane[]
30
+ /** Callback executed after a successful cross-swimlane move */
31
+ onCardMove?: (event: CrossSwimlaneMoveEvent) => void
32
+ }
33
+
34
+ export interface UseCrossSwimlaneMoveReturn {
35
+ /** Execute a cross-swimlane move. Returns true if the move was allowed. */
36
+ handleCrossSwimlaneMove: (
37
+ cardId: string,
38
+ fromSwimlane: string,
39
+ toSwimlane: string,
40
+ columnId: string,
41
+ ) => boolean
42
+ /** Whether a card is currently being dragged across swimlanes */
43
+ isDraggingAcrossSwimlanes: boolean
44
+ /** Start tracking a cross-swimlane drag */
45
+ startCrossSwimlaneDrag: (fromSwimlane: string) => void
46
+ /** End the cross-swimlane drag */
47
+ endCrossSwimlaneDrag: () => void
48
+ /** Check whether a card may be moved into the target swimlane */
49
+ canMoveTo: (fromSwimlane: string, toSwimlane: string) => boolean
50
+ }
51
+
52
+ /**
53
+ * Hook for managing cross-swimlane card movements.
54
+ * Validates movement constraints (acceptFrom rules) and tracks drag state.
55
+ */
56
+ export function useCrossSwimlaneMove({
57
+ swimlanes,
58
+ onCardMove,
59
+ }: UseCrossSwimlaneOptions): UseCrossSwimlaneMoveReturn {
60
+ const [dragSource, setDragSource] = useState<string | null>(null)
61
+
62
+ const swimlaneMap = useMemo(() => {
63
+ const map = new Map<string, Swimlane>()
64
+ for (const lane of swimlanes) {
65
+ map.set(lane.id, lane)
66
+ }
67
+ return map
68
+ }, [swimlanes])
69
+
70
+ const canMoveTo = useCallback(
71
+ (fromSwimlane: string, toSwimlane: string): boolean => {
72
+ if (fromSwimlane === toSwimlane) return true
73
+ const target = swimlaneMap.get(toSwimlane)
74
+ if (!target) return false
75
+ // When acceptFrom is not specified the lane is unrestricted
76
+ if (!target.acceptFrom) return true
77
+ return target.acceptFrom.includes(fromSwimlane)
78
+ },
79
+ [swimlaneMap],
80
+ )
81
+
82
+ const handleCrossSwimlaneMove = useCallback(
83
+ (
84
+ cardId: string,
85
+ fromSwimlane: string,
86
+ toSwimlane: string,
87
+ columnId: string,
88
+ ): boolean => {
89
+ if (!canMoveTo(fromSwimlane, toSwimlane)) return false
90
+ onCardMove?.({ cardId, fromSwimlane, toSwimlane, columnId })
91
+ setDragSource(null)
92
+ return true
93
+ },
94
+ [canMoveTo, onCardMove],
95
+ )
96
+
97
+ const startCrossSwimlaneDrag = useCallback((fromSwimlane: string) => {
98
+ setDragSource(fromSwimlane)
99
+ }, [])
100
+
101
+ const endCrossSwimlaneDrag = useCallback(() => {
102
+ setDragSource(null)
103
+ }, [])
104
+
105
+ const isDraggingAcrossSwimlanes = dragSource !== null
106
+
107
+ return {
108
+ handleCrossSwimlaneMove,
109
+ isDraggingAcrossSwimlanes,
110
+ startCrossSwimlaneDrag,
111
+ endCrossSwimlaneDrag,
112
+ canMoveTo,
113
+ }
114
+ }
115
+
116
+ export default useCrossSwimlaneMove
@@ -0,0 +1,107 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { useState, useCallback } from "react"
10
+ import type { KanbanCard } from "./types"
11
+
12
+ export interface UseQuickAddReorderOptions {
13
+ /** Initial set of cards in the column */
14
+ cards: KanbanCard[]
15
+ /** Called when the user finishes reordering */
16
+ onReorderComplete?: (reorderedCards: KanbanCard[]) => void
17
+ }
18
+
19
+ export interface UseQuickAddReorderReturn {
20
+ /** Cards in their current (possibly reordered) order */
21
+ reorderedCards: KanbanCard[]
22
+ /** Move a card from one index to another within the list */
23
+ onReorder: (fromIndex: number, toIndex: number) => void
24
+ /** Whether a card is currently being dragged for reorder */
25
+ isDragging: boolean
26
+ /** Signal the start of a drag-to-reorder interaction */
27
+ startDrag: () => void
28
+ /** Signal the end of a drag-to-reorder interaction */
29
+ endDrag: () => void
30
+ /** Reset reordered state back to the source cards */
31
+ reset: () => void
32
+ }
33
+
34
+ /**
35
+ * Hook for drag-to-reorder of quick-add cards within a single column.
36
+ * Manages local ordering state and exposes drag lifecycle helpers.
37
+ */
38
+ export function useQuickAddReorder({
39
+ cards,
40
+ onReorderComplete,
41
+ }: UseQuickAddReorderOptions): UseQuickAddReorderReturn {
42
+ const [reorderedCards, setReorderedCards] = useState<KanbanCard[]>(cards)
43
+ const [isDragging, setIsDragging] = useState(false)
44
+
45
+ // Keep reorderedCards in sync when the source cards change externally
46
+ // (e.g. when a new card is added via quick-add)
47
+ if (!cardsMatch(reorderedCards, cards) && !isDragging) {
48
+ setReorderedCards(cards)
49
+ }
50
+
51
+ const reset = useCallback(() => {
52
+ setReorderedCards(cards)
53
+ setIsDragging(false)
54
+ }, [cards])
55
+
56
+ const onReorder = useCallback(
57
+ (fromIndex: number, toIndex: number) => {
58
+ setReorderedCards(prev => {
59
+ if (
60
+ fromIndex < 0 ||
61
+ toIndex < 0 ||
62
+ fromIndex >= prev.length ||
63
+ toIndex >= prev.length
64
+ ) {
65
+ return prev
66
+ }
67
+ const next = [...prev]
68
+ const [moved] = next.splice(fromIndex, 1)
69
+ next.splice(toIndex, 0, moved)
70
+ return next
71
+ })
72
+ },
73
+ [],
74
+ )
75
+
76
+ const startDrag = useCallback(() => {
77
+ setIsDragging(true)
78
+ }, [])
79
+
80
+ const endDrag = useCallback(() => {
81
+ setIsDragging(false)
82
+ setReorderedCards(current => {
83
+ onReorderComplete?.(current)
84
+ return current
85
+ })
86
+ }, [onReorderComplete])
87
+
88
+ return {
89
+ reorderedCards,
90
+ onReorder,
91
+ isDragging,
92
+ startDrag,
93
+ endDrag,
94
+ reset,
95
+ }
96
+ }
97
+
98
+ /** Shallow check that two card arrays contain the same IDs in the same order. */
99
+ function cardsMatch(a: KanbanCard[], b: KanbanCard[]): boolean {
100
+ if (a.length !== b.length) return false
101
+ for (let i = 0; i < a.length; i++) {
102
+ if (a[i].id !== b[i].id) return false
103
+ }
104
+ return true
105
+ }
106
+
107
+ export default useQuickAddReorder
@@ -1,194 +0,0 @@
1
- import { j as e } from "./index-CWGTi2xn.js";
2
- import * as h from "react";
3
- import { u as H, a as B, D as L, c as q, b as z, d as G, T as J, P as M, e as E, S as Q, v as U, C as V } from "./sortable.esm-CNNHgHk5.js";
4
- import { Badge as K, ScrollArea as W, Card as X, CardHeader as Y, CardTitle as Z, CardDescription as _, CardContent as ee } from "@object-ui/components";
5
- import { useHasDndProvider as se, useDnd as re } from "@object-ui/react";
6
- const R = (...s) => s.filter(Boolean).join(" ");
7
- function T({ card: s, onCardClick: i }) {
8
- const {
9
- attributes: x,
10
- listeners: n,
11
- setNodeRef: t,
12
- transform: d,
13
- transition: c,
14
- isDragging: C
15
- } = E({ id: s.id }), o = {
16
- transform: V.Transform.toString(d),
17
- transition: c,
18
- opacity: C ? 0.5 : void 0
19
- };
20
- return /* @__PURE__ */ e.jsx(
21
- "div",
22
- {
23
- ref: t,
24
- style: o,
25
- ...x,
26
- ...n,
27
- role: "listitem",
28
- "aria-label": s.title,
29
- onClick: () => i?.(s),
30
- children: /* @__PURE__ */ e.jsxs(X, { className: "mb-2 cursor-grab active:cursor-grabbing border-border bg-card/60 hover:border-primary/40 hover:shadow-lg hover:shadow-primary/10 transition-all duration-300 group touch-manipulation", children: [
31
- /* @__PURE__ */ e.jsxs(Y, { className: "p-2 sm:p-4", children: [
32
- /* @__PURE__ */ e.jsx(Z, { className: "text-xs sm:text-sm font-medium font-mono tracking-tight text-foreground group-hover:text-primary transition-colors", children: s.title }),
33
- s.description && /* @__PURE__ */ e.jsx(_, { className: "text-xs text-muted-foreground font-mono line-clamp-2 sm:line-clamp-none", children: s.description })
34
- ] }),
35
- s.badges && s.badges.length > 0 && /* @__PURE__ */ e.jsx(ee, { className: "p-2 sm:p-4 pt-0", children: /* @__PURE__ */ e.jsx("div", { className: "flex flex-wrap gap-1", children: s.badges.map((b, S) => /* @__PURE__ */ e.jsx(K, { variant: b.variant || "default", className: "text-xs", children: b.label }, S)) }) })
36
- ] })
37
- }
38
- );
39
- }
40
- function te({
41
- column: s,
42
- cards: i,
43
- onCardClick: x
44
- }) {
45
- const n = i || [], { setNodeRef: t } = E({
46
- id: s.id,
47
- data: {
48
- type: "column"
49
- }
50
- }), d = s.limit && n.length >= s.limit;
51
- return /* @__PURE__ */ e.jsxs(
52
- "div",
53
- {
54
- ref: t,
55
- role: "group",
56
- "aria-label": s.title,
57
- className: R(
58
- "flex flex-col w-[85vw] sm:w-80 flex-shrink-0 rounded-lg border border-border bg-card/20 backdrop-blur-sm shadow-xl snap-start",
59
- s.className
60
- ),
61
- children: [
62
- /* @__PURE__ */ e.jsx("div", { className: "p-3 sm:p-4 border-b border-border/50 bg-muted/20", children: /* @__PURE__ */ e.jsxs("div", { className: "flex items-center justify-between", children: [
63
- /* @__PURE__ */ e.jsx("h3", { id: `kanban-col-${s.id}`, className: "font-mono text-xs sm:text-sm font-semibold tracking-wider text-primary/90 uppercase truncate", children: s.title }),
64
- /* @__PURE__ */ e.jsxs("div", { className: "flex items-center gap-2", children: [
65
- /* @__PURE__ */ e.jsxs("span", { className: "font-mono text-xs text-muted-foreground", "aria-label": `${n.length} cards${s.limit ? ` of ${s.limit} maximum` : ""}`, children: [
66
- n.length,
67
- s.limit && ` / ${s.limit}`
68
- ] }),
69
- d && /* @__PURE__ */ e.jsx(K, { variant: "destructive", className: "text-xs", children: "Full" })
70
- ] })
71
- ] }) }),
72
- /* @__PURE__ */ e.jsx(W, { className: "flex-1 p-4", children: /* @__PURE__ */ e.jsx(
73
- Q,
74
- {
75
- items: n.map((c) => c.id),
76
- strategy: U,
77
- children: /* @__PURE__ */ e.jsx("div", { className: "space-y-2", role: "list", "aria-label": `${s.title} cards`, children: n.map((c) => /* @__PURE__ */ e.jsx(T, { card: c, onCardClick: x }, c.id)) })
78
- }
79
- ) })
80
- ]
81
- }
82
- );
83
- }
84
- function ae({ children: s }) {
85
- const i = re();
86
- return /* @__PURE__ */ e.jsx(e.Fragment, { children: s(i) });
87
- }
88
- function le({ columns: s, onCardMove: i, onCardClick: x, className: n }) {
89
- return se() ? /* @__PURE__ */ e.jsx(ae, { children: (d) => /* @__PURE__ */ e.jsx($, { columns: s, onCardMove: i, onCardClick: x, className: n, dnd: d }) }) : /* @__PURE__ */ e.jsx($, { columns: s, onCardMove: i, onCardClick: x, className: n, dnd: null });
90
- }
91
- function $({ columns: s, onCardMove: i, onCardClick: x, className: n, dnd: t }) {
92
- const [d, c] = h.useState(null), C = h.useMemo(() => (s || []).map((r) => ({
93
- ...r,
94
- cards: r.cards || []
95
- })), [s]), [o, b] = h.useState(C);
96
- h.useEffect(() => {
97
- b(C);
98
- }, [C]);
99
- const S = H(
100
- B(M, {
101
- activationConstraint: {
102
- distance: 5
103
- }
104
- }),
105
- B(J, {
106
- activationConstraint: {
107
- delay: 200,
108
- tolerance: 5
109
- }
110
- })
111
- ), O = (r) => {
112
- const { active: m } = r, a = A(m.id);
113
- if (c(a), t && a) {
114
- const l = w(a.id);
115
- l && t.startDrag({ id: a.id, type: "kanban-card", data: a, sourceId: l.id });
116
- }
117
- }, P = (r) => {
118
- const { active: m, over: a } = r;
119
- if (c(null), !a) {
120
- t && t.endDrag();
121
- return;
122
- }
123
- const l = m.id, v = a.id;
124
- if (l === v) {
125
- t && t.endDrag();
126
- return;
127
- }
128
- const u = w(l), p = w(v) || F(v);
129
- if (!u || !p) {
130
- t && t.endDrag();
131
- return;
132
- }
133
- if (u.id === p.id) {
134
- const g = [...u.cards], j = g.findIndex((f) => f.id === l), I = g.findIndex((f) => f.id === v), k = G(g, j, I);
135
- b(
136
- (f) => f.map(
137
- (D) => D.id === u.id ? { ...D, cards: k } : D
138
- )
139
- );
140
- } else {
141
- const g = [...u.cards], j = [...p.cards], I = g.findIndex((N) => N.id === l), f = v === p.id ? j.length : j.findIndex((N) => N.id === v), [D] = g.splice(I, 1);
142
- j.splice(f, 0, D), b(
143
- (N) => N.map((y) => y.id === u.id ? { ...y, cards: g } : y.id === p.id ? { ...y, cards: j } : y)
144
- ), i && i(l, u.id, p.id, f);
145
- }
146
- t && t.endDrag(p.id);
147
- }, A = h.useCallback(
148
- (r) => {
149
- for (const m of o) {
150
- const a = m.cards.find((l) => l.id === r);
151
- if (a) return a;
152
- }
153
- return null;
154
- },
155
- [o]
156
- ), w = h.useCallback(
157
- (r) => o.find((m) => m.cards.some((a) => a.id === r)) || null,
158
- [o]
159
- ), F = h.useCallback(
160
- (r) => o.find((m) => m.id === r) || null,
161
- [o]
162
- );
163
- return /* @__PURE__ */ e.jsxs(
164
- L,
165
- {
166
- sensors: S,
167
- collisionDetection: q,
168
- onDragStart: O,
169
- onDragEnd: P,
170
- children: [
171
- /* @__PURE__ */ e.jsxs("div", { className: "flex sm:hidden items-center justify-between px-3 pb-2 text-xs text-muted-foreground", children: [
172
- /* @__PURE__ */ e.jsxs("span", { children: [
173
- o.length,
174
- " columns"
175
- ] }),
176
- /* @__PURE__ */ e.jsx("span", { children: "← Swipe to navigate →" })
177
- ] }),
178
- /* @__PURE__ */ e.jsx("div", { className: R("flex gap-3 sm:gap-4 overflow-x-auto snap-x snap-mandatory p-2 sm:p-4 [-webkit-overflow-scrolling:touch]", n), role: "region", "aria-label": "Kanban board", children: o.map((r) => /* @__PURE__ */ e.jsx(
179
- te,
180
- {
181
- column: r,
182
- cards: r.cards,
183
- onCardClick: x
184
- },
185
- r.id
186
- )) }),
187
- /* @__PURE__ */ e.jsx(z, { children: /* @__PURE__ */ e.jsx("div", { "aria-live": "assertive", "aria-label": d ? `Dragging ${d.title}` : void 0, children: d ? /* @__PURE__ */ e.jsx(T, { card: d }) : null }) })
188
- ]
189
- }
190
- );
191
- }
192
- export {
193
- le as default
194
- };