@schandlergarcia/sf-web-components 2.4.0 → 2.5.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.
@@ -3,7 +3,7 @@ name: command-center-builder
3
3
  description: >-
4
4
  Conventions and patterns for building Command Center dashboards.
5
5
  Covers layout patterns (visualization-hero, grid), component selection
6
- (MetricCard, ChartCard, GeoMap, ListCard, ActivityCard), dark mode,
6
+ (MetricCard, ChartCard, GeoMap, ListCard, KanbanBoard, ActivityCard), dark mode,
7
7
  brand colors, styling, and wiring dashboards into the app. Use when
8
8
  scaffolding, building, or editing any dashboard page in this project.
9
9
  ---
@@ -92,7 +92,7 @@ Use these exact imports. Do NOT read component source files to discover imports
92
92
  ```tsx
93
93
  // Library components — named imports from barrel
94
94
  import {
95
- BaseCard, MetricCard, ChartCard, TableCard, ListCard, ActivityCard,
95
+ BaseCard, MetricCard, ChartCard, TableCard, ListCard, KanbanBoard, ActivityCard,
96
96
  StatusCard, WidgetCard, SectionCard, FeedPanel, CalloutCard, ActionList,
97
97
  MetricsStrip, D3Chart, D3ChartTemplates, GeoMap, Avatar, UIButton,
98
98
  UIChip, UIText, EmptyState, Spinner, ChatBar, FilterBar,
@@ -164,6 +164,7 @@ The component library (`@/components/library`) provides pre-built, themed, dark-
164
164
  | Activity feed (display only) | `ActivityCard` or `FeedPanel` | `<div>` with hand-rolled interaction rows |
165
165
  | **Activity feed (with per-item actions like "Assign to Agent")** | **Custom markup inside `BaseCard`** | **`ActivityCard` (doesn't support per-item buttons)** |
166
166
  | Data table | `TableCard` | `<table>` or custom grid of rows |
167
+ | Board / swim-lanes (cards across columns, optional DnD) | `KanbanBoard` | Stacked `ListCard`s or hand-rolled grid of columns |
167
168
  | Stats panel | `WidgetCard` or `SectionCard` | `<div className="bg-white ... p-6">` with label/value pairs |
168
169
  | Custom interactive content (expand/collapse, inline details) | `WidgetCard` (sections accept arbitrary JSX) or `ListCard` (with `onItemClick` + `itemActions`) | Hand-rolled `<div>` card with custom expand/collapse logic |
169
170
  | Status items | `StatusCard` | `<div>` with custom status badges |
@@ -3,10 +3,11 @@ name: component-library
3
3
  description: >-
4
4
  API reference for the command center component library at
5
5
  @/components/library. Documents every component (MetricCard, ChartCard,
6
- D3Chart, D3ChartTemplates, GeoMap, ListCard, ActivityCard, TableCard,
7
- StatusCard, WidgetCard, BaseCard, ChatBar, Avatar, UIChip, FilterBar),
8
- their props, data shapes, and import patterns. Use when looking up how
9
- to use a specific component or choosing which component fits a need.
6
+ D3Chart, D3ChartTemplates, GeoMap, ListCard, KanbanBoard, ActivityCard,
7
+ TableCard, StatusCard, WidgetCard, BaseCard, ChatBar, Avatar, UIChip,
8
+ FilterBar), their props, data shapes, and import patterns. Use when
9
+ looking up how to use a specific component or choosing which component
10
+ fits a need.
10
11
  ---
11
12
 
12
13
  # Component Library
@@ -64,6 +65,9 @@ import type { MetricCardProps } from "@/components/library";
64
65
  - Pass initials strings as `avatar` in ListCard items — `avatar: "MR"` is treated as an image URL and breaks. Instead, omit `avatar` to auto-generate initials from `title`/`name`, or pass a ReactNode like `<Avatar initials="MR" />`
65
66
  - Hand-roll a card container (`<div className="bg-white border rounded ...">`) when `WidgetCard` with a single section can wrap your custom content
66
67
  - Use Recharts, Chart.js, or any third-party chart library — only `D3Chart` and `GeoMap` are allowed
68
+ - Use `react-beautiful-dnd`, `react-dnd`, or any other DnD library — `KanbanBoard` is built on `@dnd-kit/*` (peer deps); do not introduce a second DnD system
69
+ - Forget `columnId` on a `KanbanBoard` card — every card MUST have `columnId` matching one of `columns[].id`, otherwise it won't render
70
+ - Wrap `KanbanBoard` in another card (`WidgetCard`, `BaseCard`) — it already renders inside its own `BaseCard`
67
71
  - Dismiss `validate:dashboard` errors as "justified" — every error has a library component fix
68
72
 
69
73
  ---
@@ -76,6 +80,7 @@ import type { MetricCardProps } from "@/components/library";
76
80
  | A row of 2–4 KPIs | Grid of `MetricCard` | `MetricsStrip` (for compact inline) |
77
81
  | A data table | `TableCard` | `<table>` HTML |
78
82
  | A scrollable item list | `ListCard` | Custom divs with `.map()` |
83
+ | A board / swim-lanes (cards in columns, optional drag-and-drop) | `KanbanBoard` | Stacked `ListCard`s or hand-rolled grid |
79
84
  | An activity log with status icons | `ActivityCard` | ListCard (no status icons) |
80
85
  | A chart (line, bar) | `ChartCard` + `D3Chart` | Raw `<svg>` |
81
86
  | A donut/pie chart | `ChartCard` + `D3Chart` + custom renderChart | Any chart library |
@@ -358,6 +363,47 @@ Status aliases: `"ok"/"healthy"/"up"` → `"operational"`, `"warn"` → `"degrad
358
363
  />
359
364
  ```
360
365
 
366
+ ### KanbanBoard
367
+ **When:** Display items as cards across labeled swim-lanes (statuses, pipeline stages, sprint board). Includes drag-and-drop powered by `@dnd-kit/*`.
368
+
369
+ **Peer deps required:** `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`. Install in your project if missing:
370
+ ```bash
371
+ npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
372
+ ```
373
+
374
+ ```jsx
375
+ <KanbanBoard
376
+ title="Sprint Board"
377
+ subtitle="Q2 · Sprint 4"
378
+ columns={[
379
+ { id: "todo", title: "To Do", limit: 5, accent: "default" },
380
+ { id: "doing", title: "In Progress", limit: 3, accent: "warning" },
381
+ { id: "done", title: "Done", accent: "success" },
382
+ ]}
383
+ cards={[
384
+ { id: "C-1", columnId: "todo", title: "Onboard Acme", subtitle: "Owner: Aria", badges: ["Priority"], status: "warning" },
385
+ { id: "C-2", columnId: "doing", title: "Renew SLA", status: "danger", badges: [{ label: "Risk", color: "danger" }] },
386
+ { id: "C-3", columnId: "done", title: "Q2 review prep", status: "success" },
387
+ ]}
388
+ isDraggable // default true
389
+ showColumnCounts // default true
390
+ showWipLimits // default true; renders "count / limit"
391
+ dense={false}
392
+ accent="default" // fallback accent
393
+ onCardClick={(card) => {/* row click */}}
394
+ onCardMove={({ cardId, fromColumnId, toColumnId, fromIndex, toIndex }) => {
395
+ // Optional. If provided, you own the state. If omitted, the board manages reorder/move internally.
396
+ }}
397
+ emptyMessage="No items"
398
+ loading={false}
399
+ error={null}
400
+ />
401
+ ```
402
+
403
+ **Card shape:** `{ id, columnId, title, subtitle?, description?, status?, accent?, avatar?, badges?, meta?, actions? }`
404
+ **Column shape:** `{ id, title, accent?, limit?, emptyMessage?, footer?, actions? }`
405
+ **Mistakes:** ❌ Missing `columnId` on a card → it won't render. ❌ Wrapping `KanbanBoard` in another card. ❌ Using a non-`@dnd-kit` DnD library alongside it.
406
+
361
407
  ---
362
408
 
363
409
  ## Chart Components
@@ -249,5 +249,93 @@ Status aliases: `"ok"/"healthy"/"up"` → `"operational"`, `"warn"` → `"degrad
249
249
  />
250
250
  ```
251
251
 
252
+ ### KanbanBoard
253
+ **When:** Display items as cards across labeled swim-lanes (To Do → In Progress → Done, pipeline stages, status boards). Supports drag-and-drop between columns out of the box.
254
+
255
+ **Peer deps:** `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities` are required peer dependencies. Install in your project if they're not already there:
256
+ ```bash
257
+ npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
258
+ ```
259
+
260
+ ```jsx
261
+ import { KanbanBoard } from "@/components/library";
262
+ import useDataSource from "@/components/library/data/useDataSource";
263
+
264
+ const SAMPLE_CARDS = [
265
+ { id: "C-1", columnId: "todo", title: "Onboard Acme", subtitle: "Owner: Aria", badges: ["Priority"], status: "warning" },
266
+ { id: "C-2", columnId: "doing", title: "Renew SLA", subtitle: "Owner: Devin", badges: [{ label: "Risk", color: "danger" }], status: "danger" },
267
+ { id: "C-3", columnId: "done", title: "Q2 review prep", subtitle: "Owner: Priya", status: "success" },
268
+ ];
269
+
270
+ const COLUMNS = [
271
+ { id: "todo", title: "To Do", limit: 5, accent: "default" },
272
+ { id: "doing", title: "In Progress", limit: 3, accent: "warning" }, // count > limit shows red ring on chip
273
+ { id: "done", title: "Done", accent: "success" },
274
+ ];
275
+
276
+ export default function PipelineBoard() {
277
+ const cards = useDataSource({ sample: SAMPLE_CARDS, live: [] });
278
+
279
+ return (
280
+ <KanbanBoard
281
+ title="Sprint Board"
282
+ subtitle="Q2 · Sprint 4"
283
+ columns={COLUMNS}
284
+ cards={cards}
285
+ isDraggable // enable @dnd-kit DnD (default true)
286
+ showColumnCounts // chip on each column header (default true)
287
+ showWipLimits // render "count / limit" when limit set (default true)
288
+ dense={false} // tighter card padding when true
289
+ accent="default" // fallback accent: default|primary|success|warning|danger|info
290
+ onCardClick={(card) => console.log(card.id)} // optional row click
291
+ onCardMove={({ cardId, fromColumnId, toColumnId, fromIndex, toIndex }) => {
292
+ // Optional. If provided, you own the state and must update `cards`.
293
+ // If omitted, KanbanBoard manages reorder/move internally.
294
+ }}
295
+ emptyMessage="No items" // shown in empty columns
296
+ loading={false}
297
+ error={null}
298
+ />
299
+ );
300
+ }
301
+ ```
302
+
303
+ **Card shape:**
304
+ ```js
305
+ {
306
+ id: "C-1", // required, unique
307
+ columnId: "todo", // required, must match one of columns[].id
308
+ title: "Onboard Acme", // required
309
+ subtitle: "Owner: Aria",
310
+ description: "Long-form text (clamped to 3 lines)",
311
+ status: "warning", // dot color: default|primary|success|warning|danger|info
312
+ accent: "primary", // overrides board accent for the card's left border
313
+ avatar: "/url" | <Icon /> | "AR" (auto-initials when omitted),
314
+ badges: ["Priority", { label: "Risk", color: "danger", size: "sm" }],
315
+ meta: ["Due Mon", { icon: <ClockIcon />, label: "2d" }],
316
+ actions: <UIButton size="sm">Open</UIButton>
317
+ }
318
+ ```
319
+
320
+ **Column shape:**
321
+ ```js
322
+ {
323
+ id: "todo", // required, unique
324
+ title: "To Do", // required
325
+ accent: "default" | "primary" | "success" | "warning" | "danger" | "info",
326
+ limit: 5, // WIP limit; over-limit columns get a red ring on the count chip
327
+ emptyMessage: "Nothing here yet", // overrides board-level emptyMessage
328
+ footer: <span>Capacity: 80%</span>,
329
+ actions: <UIButton size="xs">+</UIButton>
330
+ }
331
+ ```
332
+
333
+ **Mistakes:**
334
+ - ❌ Forgetting `columnId` on a card → card won't render. Every card MUST have `columnId` matching a column.
335
+ - ❌ Passing the full cards list back into a `setCards` from `onCardMove` (the callback fires on intent only — you decide if/how to update). If you want default reorder behavior, just omit `onCardMove`.
336
+ - ❌ Using `KanbanBoard` for a simple sortable list — use `ListCard` instead.
337
+ - ❌ Wrapping `KanbanBoard` in another card (`WidgetCard`, `BaseCard`) — it already renders inside a `BaseCard`.
338
+ - ❌ Installing `react-beautiful-dnd` or `react-dnd` — only `@dnd-kit/*` is supported.
339
+
252
340
  ---
253
341
 
@@ -6,6 +6,7 @@
6
6
  | A row of 2–4 KPIs | Grid of `MetricCard` | `MetricsStrip` (for compact inline) |
7
7
  | A data table | `TableCard` | `<table>` HTML |
8
8
  | A scrollable item list | `ListCard` | Custom divs with `.map()` |
9
+ | A board / swim-lanes (cards in columns) | `KanbanBoard` | Multiple stacked `ListCard`s |
9
10
  | An activity log with status icons | `ActivityCard` | ListCard (no status icons) |
10
11
  | A chart (line, bar) | `ChartCard` + `D3Chart` | Raw `<svg>` |
11
12
  | A donut/pie chart | `ChartCard` + `D3Chart` + custom renderChart | Any chart library |
package/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.5.0] - 2026-05-07
9
+
10
+ ### Added
11
+ - **`KanbanBoard` component** — board / swim-lane view for items in columns with drag-and-drop. Lives at `src/components/library/cards/KanbanBoard.jsx`, exported from the library barrel as `KanbanBoard`.
12
+ - Structured card shape: `{ id, columnId, title, subtitle?, description?, status?, accent?, avatar?, badges?, meta?, actions? }`.
13
+ - Structured column shape: `{ id, title, accent?, limit?, emptyMessage?, footer?, actions? }`.
14
+ - Drag-and-drop powered by `@dnd-kit/core` + `@dnd-kit/sortable` + `@dnd-kit/utilities` (new peer dependencies).
15
+ - Pointer + keyboard sensors for full a11y; drag overlay shows the active card; over-limit columns show a red ring on the count chip.
16
+ - `onCardMove({ cardId, fromColumnId, toColumnId, fromIndex, toIndex })` callback for state-owning parents; if omitted, the board manages reorder/move internally.
17
+ - `onCardClick(card)`, `loading`, `error`, `dense`, `accent`, `showColumnCounts`, `showWipLimits`, `emptyMessage` props.
18
+ - Renders inside its own `BaseCard` (do not wrap in another card).
19
+ - **Peer dependencies:** `@dnd-kit/core ^6.0.0`, `@dnd-kit/sortable ^8.0.0 || ^9.0.0 || ^10.0.0`, `@dnd-kit/utilities ^3.0.0`. Consuming projects must install these for `KanbanBoard` to work.
20
+ - **`vite.config.ts` externals updated** so `@dnd-kit/*` is not bundled into the library output.
21
+
22
+ ### Documentation
23
+ - Added `KanbanBoard` row to `.a4drules/skills/component-library/when-to-use.md` and the corresponding "When to Use" table in `SKILL.md`.
24
+ - Added full `KanbanBoard` API reference to `.a4drules/skills/component-library/card-components.md` (card shape, column shape, mistakes, peer-dep install hint).
25
+ - Added `KanbanBoard` API summary to `.a4drules/skills/component-library/SKILL.md` Card Components section.
26
+ - Updated `.a4drules/skills/command-center-builder/SKILL.md`: added `KanbanBoard` to the frontmatter description, the canonical import block, and the "Need / MUST use" table.
27
+ - Added Do-Not entries forbidding alternative DnD libraries (`react-beautiful-dnd`, `react-dnd`), forgetting `columnId` on a card, and double-wrapping `KanbanBoard` in another card.
28
+
29
+ ### Context
30
+ - Many command-center use cases (sprint boards, opportunity pipelines, ticket triage, approval queues) need a board layout. Without a library component, agents would either stack `ListCard`s (no DnD, no column counts/limits) or hand-roll a grid of columns (violates "every section is a library component"). `KanbanBoard` gives agents a single structured component that follows the same data-driven, sample/live-aware patterns as `ListCard` and `TableCard`.
31
+
8
32
  ## [2.4.0] - 2026-05-07
9
33
 
10
34
  ### Removed
@@ -0,0 +1,313 @@
1
+ import { jsx as t, jsxs as c } from "react/jsx-runtime";
2
+ import K, { useState as F, useMemo as re, useCallback as U } from "react";
3
+ import { useSensors as ne, useSensor as V, PointerSensor as se, KeyboardSensor as le, DndContext as ae, closestCenter as ie, DragOverlay as de, useDroppable as oe } from "@dnd-kit/core";
4
+ import { sortableKeyboardCoordinates as ce, arrayMove as ue, SortableContext as me, verticalListSortingStrategy as fe, useSortable as he } from "@dnd-kit/sortable";
5
+ import { CSS as pe } from "@dnd-kit/utilities";
6
+ import ge from "./BaseCard.js";
7
+ import N from "../ui/Text.js";
8
+ import T from "../ui/Chip.js";
9
+ const B = {
10
+ default: "bg-slate-400",
11
+ primary: "bg-brand-500",
12
+ success: "bg-emerald-500",
13
+ warning: "bg-amber-500",
14
+ danger: "bg-rose-500",
15
+ info: "bg-sky-500"
16
+ }, _ = {
17
+ default: "border-l-slate-300 dark:border-l-slate-700",
18
+ primary: "border-l-brand-500",
19
+ success: "border-l-emerald-500",
20
+ warning: "border-l-amber-500",
21
+ danger: "border-l-rose-500",
22
+ info: "border-l-sky-500"
23
+ };
24
+ function be({ badges: e }) {
25
+ return !e || e.length === 0 ? null : /* @__PURE__ */ t("div", { className: "mt-2 flex flex-wrap gap-1.5", children: e.map((r, a) => K.isValidElement(r) ? /* @__PURE__ */ t("span", { children: r }, a) : typeof r == "string" ? /* @__PURE__ */ t(T, { size: "sm", variant: "default", children: r }, a) : /* @__PURE__ */ t(
26
+ T,
27
+ {
28
+ size: r.size ?? "sm",
29
+ variant: r.variant ?? "default",
30
+ color: r.color,
31
+ children: r.label ?? r.text
32
+ },
33
+ r.id ?? a
34
+ )) });
35
+ }
36
+ function xe({ avatar: e, title: r }) {
37
+ return e ? typeof e == "string" ? /* @__PURE__ */ t(
38
+ "img",
39
+ {
40
+ src: e,
41
+ alt: "",
42
+ className: "h-7 w-7 shrink-0 rounded-full border border-slate-200 object-cover dark:border-slate-800"
43
+ }
44
+ ) : K.isValidElement(e) ? /* @__PURE__ */ t("div", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-800", children: e }) : /* @__PURE__ */ t("div", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full border border-slate-200 bg-slate-100 text-[10px] font-semibold text-slate-700 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-200", children: String(r ?? "??").slice(0, 2).toUpperCase() }) : null;
45
+ }
46
+ function M({ card: e, accent: r, dragging: a, onClick: s, dense: f }) {
47
+ const d = _[e?.accent ?? r ?? "default"] ?? _.default;
48
+ return /* @__PURE__ */ c(
49
+ s ? "button" : "div",
50
+ {
51
+ type: s ? "button" : void 0,
52
+ onClick: s ? () => s(e) : void 0,
53
+ className: [
54
+ "group block w-full rounded-xl border bg-white text-left shadow-sm transition",
55
+ "border-slate-200 dark:border-slate-800 dark:bg-slate-900",
56
+ "border-l-4",
57
+ d,
58
+ f ? "p-2.5" : "p-3",
59
+ a ? "opacity-60 ring-2 ring-brand-500" : "hover:-translate-y-[1px] hover:shadow-md"
60
+ ].filter(Boolean).join(" "),
61
+ children: [
62
+ /* @__PURE__ */ c("div", { className: "flex items-start gap-2", children: [
63
+ /* @__PURE__ */ t(xe, { avatar: e.avatar, title: e.title }),
64
+ /* @__PURE__ */ c("div", { className: "min-w-0 flex-1", children: [
65
+ /* @__PURE__ */ t(N, { as: "div", size: "sm", weight: "medium", className: "truncate", children: e.title }),
66
+ e.subtitle ? /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "mt-0.5 truncate", children: e.subtitle }) : null
67
+ ] }),
68
+ e.status ? /* @__PURE__ */ t(
69
+ "span",
70
+ {
71
+ "aria-label": String(e.status),
72
+ className: [
73
+ "mt-1.5 h-2 w-2 shrink-0 rounded-full",
74
+ B[e.status] ?? B.default
75
+ ].join(" ")
76
+ }
77
+ ) : null
78
+ ] }),
79
+ e.description ? /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "mt-2 line-clamp-3 text-left", children: e.description }) : null,
80
+ /* @__PURE__ */ t(be, { badges: e.badges }),
81
+ e.meta && e.meta.length > 0 ? /* @__PURE__ */ t("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-slate-500 dark:text-slate-400", children: e.meta.map((l, x) => K.isValidElement(l) ? /* @__PURE__ */ t("span", { children: l }, x) : typeof l == "string" ? /* @__PURE__ */ t("span", { children: l }, x) : /* @__PURE__ */ c("span", { className: "inline-flex items-center gap-1", children: [
82
+ l.icon ? /* @__PURE__ */ t("span", { className: "opacity-70", children: l.icon }) : null,
83
+ /* @__PURE__ */ t("span", { children: l.label ?? l.text })
84
+ ] }, l.id ?? x)) }) : null,
85
+ e.actions ? /* @__PURE__ */ t(
86
+ "div",
87
+ {
88
+ className: "mt-2 flex justify-end gap-1.5",
89
+ onClick: (l) => l.stopPropagation(),
90
+ children: e.actions
91
+ }
92
+ ) : null
93
+ ]
94
+ }
95
+ );
96
+ }
97
+ function ve({ card: e, accent: r, onClick: a, isDraggable: s, dense: f }) {
98
+ const d = he({
99
+ id: e.id,
100
+ data: { columnId: e.columnId, type: "card" },
101
+ disabled: !s
102
+ }), b = {
103
+ transform: pe.Translate.toString(d.transform),
104
+ transition: d.transition
105
+ };
106
+ return /* @__PURE__ */ t(
107
+ "div",
108
+ {
109
+ ref: d.setNodeRef,
110
+ style: b,
111
+ ...s ? d.attributes : {},
112
+ ...s ? d.listeners : {},
113
+ className: s ? "touch-none" : void 0,
114
+ children: /* @__PURE__ */ t(
115
+ M,
116
+ {
117
+ card: e,
118
+ accent: r,
119
+ dragging: d.isDragging,
120
+ onClick: a,
121
+ dense: f
122
+ }
123
+ )
124
+ }
125
+ );
126
+ }
127
+ function Ne({
128
+ column: e,
129
+ cards: r,
130
+ accent: a,
131
+ onCardClick: s,
132
+ isDraggable: f,
133
+ dense: d,
134
+ showCounts: b,
135
+ showLimits: l,
136
+ emptyMessage: x
137
+ }) {
138
+ const k = oe({
139
+ id: `column:${e.id}`,
140
+ data: { columnId: e.id, type: "column" }
141
+ }), y = r.length, w = e.limit != null && y > e.limit;
142
+ return /* @__PURE__ */ c("div", { className: "flex w-72 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-900/60", children: [
143
+ /* @__PURE__ */ c("div", { className: "flex items-center justify-between gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-800", children: [
144
+ /* @__PURE__ */ c("div", { className: "flex min-w-0 items-center gap-2", children: [
145
+ /* @__PURE__ */ t(
146
+ "span",
147
+ {
148
+ "aria-hidden": !0,
149
+ className: [
150
+ "h-2 w-2 shrink-0 rounded-full",
151
+ B[e.accent ?? a ?? "default"] ?? B.default
152
+ ].join(" ")
153
+ }
154
+ ),
155
+ /* @__PURE__ */ t(N, { as: "div", size: "sm", weight: "medium", className: "truncate", children: e.title }),
156
+ b ? /* @__PURE__ */ t(
157
+ T,
158
+ {
159
+ size: "sm",
160
+ variant: "default",
161
+ className: w ? "ring-1 ring-rose-500" : void 0,
162
+ children: l && e.limit != null ? `${y} / ${e.limit}` : y
163
+ }
164
+ ) : null
165
+ ] }),
166
+ e.actions ? /* @__PURE__ */ t("div", { className: "shrink-0", children: e.actions }) : null
167
+ ] }),
168
+ /* @__PURE__ */ t(
169
+ "div",
170
+ {
171
+ ref: k.setNodeRef,
172
+ className: [
173
+ "flex flex-1 flex-col gap-2 overflow-y-auto p-2",
174
+ k.isOver ? "bg-brand-50/50 dark:bg-brand-950/30" : ""
175
+ ].filter(Boolean).join(" "),
176
+ style: { minHeight: 80 },
177
+ children: /* @__PURE__ */ t(
178
+ me,
179
+ {
180
+ items: r.map((h) => h.id),
181
+ strategy: fe,
182
+ children: r.length === 0 ? /* @__PURE__ */ t("div", { className: "grid flex-1 place-items-center px-2 py-6", children: /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "text-center", children: e.emptyMessage ?? x ?? "No items" }) }) : r.map((h) => /* @__PURE__ */ t(
183
+ ve,
184
+ {
185
+ card: h,
186
+ accent: a,
187
+ onClick: s,
188
+ isDraggable: f,
189
+ dense: d
190
+ },
191
+ h.id
192
+ ))
193
+ }
194
+ )
195
+ }
196
+ ),
197
+ e.footer ? /* @__PURE__ */ t("div", { className: "border-t border-slate-200 px-3 py-2 text-xs text-slate-500 dark:border-slate-800 dark:text-slate-400", children: e.footer }) : null
198
+ ] });
199
+ }
200
+ function De({
201
+ columns: e = [],
202
+ cards: r = [],
203
+ title: a,
204
+ subtitle: s,
205
+ accent: f = "default",
206
+ isDraggable: d = !0,
207
+ dense: b = !1,
208
+ showColumnCounts: l = !0,
209
+ showWipLimits: x = !0,
210
+ emptyMessage: k = "No items",
211
+ loading: y = !1,
212
+ error: w,
213
+ actions: h,
214
+ onCardClick: P,
215
+ onCardMove: R,
216
+ className: G = "",
217
+ ...H
218
+ }) {
219
+ const [A, D] = F(null), [W, q] = F(null), u = W ?? r, J = ne(
220
+ V(se, { activationConstraint: { distance: 4 } }),
221
+ V(le, { coordinateGetter: ce })
222
+ ), E = re(() => {
223
+ const n = {};
224
+ for (const o of e) n[o.id] = [];
225
+ for (const o of u) {
226
+ const i = o.columnId;
227
+ n[i] || (n[i] = []), n[i].push(o);
228
+ }
229
+ return n;
230
+ }, [e, u]), Q = U(
231
+ (n) => u.find((i) => i.id === n)?.columnId ?? null,
232
+ [u]
233
+ ), X = U(
234
+ (n) => n ? typeof n == "string" && n.startsWith("column:") ? n.slice(7) : u.find((i) => i.id === n)?.columnId ?? null : null,
235
+ [u]
236
+ ), Y = (n) => D(n.active.id), Z = (n) => {
237
+ D(null);
238
+ const { active: o, over: i } = n;
239
+ if (!i) return;
240
+ const p = Q(o.id), m = X(i.id);
241
+ if (!p || !m) return;
242
+ const C = (E[p] ?? []).slice(), I = p === m ? C : (E[m] ?? []).slice(), S = C.findIndex((v) => v.id === o.id);
243
+ let g;
244
+ if (i.data?.current?.type === "column" || i.id === `column:${m}` ? g = I.length : (g = I.findIndex((v) => v.id === i.id), g === -1 && (g = I.length)), p === m && S === g) return;
245
+ let L;
246
+ if (p === m) {
247
+ const v = ue(C, S, g);
248
+ L = [...u.filter((z) => z.columnId !== p), ...v];
249
+ } else {
250
+ const v = { ...C[S], columnId: m }, $ = C.filter((j) => j.id !== o.id), z = I.slice();
251
+ z.splice(g, 0, v), L = [...u.filter(
252
+ (j) => j.columnId !== p && j.columnId !== m
253
+ ), ...$, ...z];
254
+ }
255
+ R ? R({
256
+ cardId: o.id,
257
+ fromColumnId: p,
258
+ toColumnId: m,
259
+ fromIndex: S,
260
+ toIndex: g
261
+ }) : q(L);
262
+ }, O = A ? u.find((n) => n.id === A) : null, ee = a || s || h ? /* @__PURE__ */ c("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
263
+ /* @__PURE__ */ c("div", { className: "min-w-0", children: [
264
+ a ? /* @__PURE__ */ t(N, { as: "div", size: "sm", weight: "medium", children: a }) : null,
265
+ s ? /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "mt-1", children: s }) : null
266
+ ] }),
267
+ h ? /* @__PURE__ */ t("div", { className: "shrink-0", children: h }) : null
268
+ ] }) : null, te = /* @__PURE__ */ t("div", { className: "flex gap-3 overflow-x-auto pb-1", children: e.map((n) => /* @__PURE__ */ t(
269
+ Ne,
270
+ {
271
+ column: n,
272
+ cards: E[n.id] ?? [],
273
+ accent: f,
274
+ onCardClick: P,
275
+ isDraggable: d,
276
+ dense: b,
277
+ showCounts: l,
278
+ showLimits: x,
279
+ emptyMessage: k
280
+ },
281
+ n.id
282
+ )) });
283
+ return /* @__PURE__ */ c(
284
+ ge,
285
+ {
286
+ padding: "default",
287
+ isLoading: y,
288
+ className: G,
289
+ ...H,
290
+ children: [
291
+ ee,
292
+ w ? /* @__PURE__ */ t("div", { className: "rounded-md border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300", children: String(w) }) : /* @__PURE__ */ c(
293
+ ae,
294
+ {
295
+ sensors: J,
296
+ collisionDetection: ie,
297
+ onDragStart: Y,
298
+ onDragEnd: Z,
299
+ onDragCancel: () => D(null),
300
+ children: [
301
+ te,
302
+ /* @__PURE__ */ t(de, { children: O ? /* @__PURE__ */ t(M, { card: O, accent: f, dragging: !0, dense: b }) : null })
303
+ ]
304
+ }
305
+ )
306
+ ]
307
+ }
308
+ );
309
+ }
310
+ export {
311
+ De as default
312
+ };
313
+ //# sourceMappingURL=KanbanBoard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KanbanBoard.js","sources":["../../../../src/components/library/cards/KanbanBoard.jsx"],"sourcesContent":["import React, { useMemo, useState, useCallback } from \"react\";\nimport {\n DndContext,\n DragOverlay,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n useDroppable,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n useSortable,\n verticalListSortingStrategy,\n sortableKeyboardCoordinates,\n arrayMove,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport BaseCard from \"./BaseCard\";\nimport UIText from \"../ui/Text\";\nimport UIChip from \"../ui/Chip\";\n\n/**\n * KanbanBoard\n *\n * A swim-lane / board view with structured cards and (optional) drag-and-drop\n * powered by @dnd-kit. Designed to live inside a CommandCenter dashboard and\n * to behave consistently with ListCard / TableCard.\n *\n * Data shapes:\n * columns: [{ id, title, accent?, limit?, footer?, emptyMessage? }]\n * cards: [{ id, columnId, title, subtitle?, description?, badges?,\n * status?, avatar?, meta?, actions?, accent? }]\n *\n * Callbacks:\n * onCardClick(card)\n * onCardMove({ cardId, fromColumnId, toColumnId, fromIndex, toIndex })\n */\n\nconst ACCENT_RING = {\n default: \"ring-slate-200 dark:ring-slate-800\",\n primary: \"ring-brand-500/40\",\n success: \"ring-emerald-500/40\",\n warning: \"ring-amber-500/40\",\n danger: \"ring-rose-500/40\",\n info: \"ring-sky-500/40\",\n};\n\nconst ACCENT_DOT = {\n default: \"bg-slate-400\",\n primary: \"bg-brand-500\",\n success: \"bg-emerald-500\",\n warning: \"bg-amber-500\",\n danger: \"bg-rose-500\",\n info: \"bg-sky-500\",\n};\n\nconst ACCENT_BORDER_LEFT = {\n default: \"border-l-slate-300 dark:border-l-slate-700\",\n primary: \"border-l-brand-500\",\n success: \"border-l-emerald-500\",\n warning: \"border-l-amber-500\",\n danger: \"border-l-rose-500\",\n info: \"border-l-sky-500\",\n};\n\nfunction CardBadges({ badges }) {\n if (!badges || badges.length === 0) return null;\n return (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {badges.map((b, i) => {\n if (React.isValidElement(b)) return <span key={i}>{b}</span>;\n if (typeof b === \"string\") {\n return (\n <UIChip key={i} size=\"sm\" variant=\"default\">\n {b}\n </UIChip>\n );\n }\n return (\n <UIChip\n key={b.id ?? i}\n size={b.size ?? \"sm\"}\n variant={b.variant ?? \"default\"}\n color={b.color}\n >\n {b.label ?? b.text}\n </UIChip>\n );\n })}\n </div>\n );\n}\n\nfunction CardAvatar({ avatar, title }) {\n if (!avatar) return null;\n if (typeof avatar === \"string\") {\n return (\n <img\n src={avatar}\n alt=\"\"\n className=\"h-7 w-7 shrink-0 rounded-full border border-slate-200 object-cover dark:border-slate-800\"\n />\n );\n }\n if (React.isValidElement(avatar)) {\n return (\n <div className=\"grid h-7 w-7 shrink-0 place-items-center rounded-full border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-800\">\n {avatar}\n </div>\n );\n }\n return (\n <div className=\"grid h-7 w-7 shrink-0 place-items-center rounded-full border border-slate-200 bg-slate-100 text-[10px] font-semibold text-slate-700 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-200\">\n {String(title ?? \"??\")\n .slice(0, 2)\n .toUpperCase()}\n </div>\n );\n}\n\nfunction KanbanCardBody({ card, accent, dragging, onClick, dense }) {\n const accentBorder =\n ACCENT_BORDER_LEFT[card?.accent ?? accent ?? \"default\"] ?? ACCENT_BORDER_LEFT.default;\n\n const Comp = onClick ? \"button\" : \"div\";\n\n return (\n <Comp\n type={onClick ? \"button\" : undefined}\n onClick={onClick ? () => onClick(card) : undefined}\n className={[\n \"group block w-full rounded-xl border bg-white text-left shadow-sm transition\",\n \"border-slate-200 dark:border-slate-800 dark:bg-slate-900\",\n \"border-l-4\",\n accentBorder,\n dense ? \"p-2.5\" : \"p-3\",\n dragging ? \"opacity-60 ring-2 ring-brand-500\" : \"hover:-translate-y-[1px] hover:shadow-md\",\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n <div className=\"flex items-start gap-2\">\n <CardAvatar avatar={card.avatar} title={card.title} />\n <div className=\"min-w-0 flex-1\">\n <UIText as=\"div\" size=\"sm\" weight=\"medium\" className=\"truncate\">\n {card.title}\n </UIText>\n {card.subtitle ? (\n <UIText as=\"div\" size=\"xs\" muted className=\"mt-0.5 truncate\">\n {card.subtitle}\n </UIText>\n ) : null}\n </div>\n {card.status ? (\n <span\n aria-label={String(card.status)}\n className={[\n \"mt-1.5 h-2 w-2 shrink-0 rounded-full\",\n ACCENT_DOT[card.status] ?? ACCENT_DOT.default,\n ].join(\" \")}\n />\n ) : null}\n </div>\n\n {card.description ? (\n <UIText as=\"div\" size=\"xs\" muted className=\"mt-2 line-clamp-3 text-left\">\n {card.description}\n </UIText>\n ) : null}\n\n <CardBadges badges={card.badges} />\n\n {card.meta && card.meta.length > 0 ? (\n <div className=\"mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-slate-500 dark:text-slate-400\">\n {card.meta.map((m, i) => {\n if (React.isValidElement(m)) return <span key={i}>{m}</span>;\n if (typeof m === \"string\") return <span key={i}>{m}</span>;\n return (\n <span key={m.id ?? i} className=\"inline-flex items-center gap-1\">\n {m.icon ? <span className=\"opacity-70\">{m.icon}</span> : null}\n <span>{m.label ?? m.text}</span>\n </span>\n );\n })}\n </div>\n ) : null}\n\n {card.actions ? (\n <div\n className=\"mt-2 flex justify-end gap-1.5\"\n onClick={(e) => e.stopPropagation()}\n >\n {card.actions}\n </div>\n ) : null}\n </Comp>\n );\n}\n\nfunction SortableKanbanCard({ card, accent, onClick, isDraggable, dense }) {\n const sortable = useSortable({\n id: card.id,\n data: { columnId: card.columnId, type: \"card\" },\n disabled: !isDraggable,\n });\n\n const style = {\n transform: CSS.Translate.toString(sortable.transform),\n transition: sortable.transition,\n };\n\n return (\n <div\n ref={sortable.setNodeRef}\n style={style}\n {...(isDraggable ? sortable.attributes : {})}\n {...(isDraggable ? sortable.listeners : {})}\n className={isDraggable ? \"touch-none\" : undefined}\n >\n <KanbanCardBody\n card={card}\n accent={accent}\n dragging={sortable.isDragging}\n onClick={onClick}\n dense={dense}\n />\n </div>\n );\n}\n\nfunction KanbanColumn({\n column,\n cards,\n accent,\n onCardClick,\n isDraggable,\n dense,\n showCounts,\n showLimits,\n emptyMessage,\n}) {\n const droppable = useDroppable({\n id: `column:${column.id}`,\n data: { columnId: column.id, type: \"column\" },\n });\n\n const count = cards.length;\n const overLimit = column.limit != null && count > column.limit;\n\n return (\n <div className=\"flex w-72 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-900/60\">\n <div className=\"flex items-center justify-between gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-800\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span\n aria-hidden\n className={[\n \"h-2 w-2 shrink-0 rounded-full\",\n ACCENT_DOT[column.accent ?? accent ?? \"default\"] ?? ACCENT_DOT.default,\n ].join(\" \")}\n />\n <UIText as=\"div\" size=\"sm\" weight=\"medium\" className=\"truncate\">\n {column.title}\n </UIText>\n {showCounts ? (\n <UIChip\n size=\"sm\"\n variant=\"default\"\n className={overLimit ? \"ring-1 ring-rose-500\" : undefined}\n >\n {showLimits && column.limit != null ? `${count} / ${column.limit}` : count}\n </UIChip>\n ) : null}\n </div>\n {column.actions ? <div className=\"shrink-0\">{column.actions}</div> : null}\n </div>\n\n <div\n ref={droppable.setNodeRef}\n className={[\n \"flex flex-1 flex-col gap-2 overflow-y-auto p-2\",\n droppable.isOver ? \"bg-brand-50/50 dark:bg-brand-950/30\" : \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n style={{ minHeight: 80 }}\n >\n <SortableContext\n items={cards.map((c) => c.id)}\n strategy={verticalListSortingStrategy}\n >\n {cards.length === 0 ? (\n <div className=\"grid flex-1 place-items-center px-2 py-6\">\n <UIText as=\"div\" size=\"xs\" muted className=\"text-center\">\n {column.emptyMessage ?? emptyMessage ?? \"No items\"}\n </UIText>\n </div>\n ) : (\n cards.map((card) => (\n <SortableKanbanCard\n key={card.id}\n card={card}\n accent={accent}\n onClick={onCardClick}\n isDraggable={isDraggable}\n dense={dense}\n />\n ))\n )}\n </SortableContext>\n </div>\n\n {column.footer ? (\n <div className=\"border-t border-slate-200 px-3 py-2 text-xs text-slate-500 dark:border-slate-800 dark:text-slate-400\">\n {column.footer}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport default function KanbanBoard({\n columns = [],\n cards = [],\n title,\n subtitle,\n accent = \"default\",\n isDraggable = true,\n dense = false,\n showColumnCounts = true,\n showWipLimits = true,\n emptyMessage = \"No items\",\n loading = false,\n error,\n actions,\n onCardClick,\n onCardMove,\n className = \"\",\n ...cardProps\n}) {\n const [activeId, setActiveId] = useState(null);\n const [internalCards, setInternalCards] = useState(null);\n\n const liveCards = internalCards ?? cards;\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })\n );\n\n const cardsByColumn = useMemo(() => {\n const map = {};\n for (const col of columns) map[col.id] = [];\n for (const c of liveCards) {\n const colId = c.columnId;\n if (!map[colId]) map[colId] = [];\n map[colId].push(c);\n }\n return map;\n }, [columns, liveCards]);\n\n const findColumnIdForCard = useCallback(\n (cardId) => {\n const card = liveCards.find((c) => c.id === cardId);\n return card?.columnId ?? null;\n },\n [liveCards]\n );\n\n const resolveColumnId = useCallback(\n (id) => {\n if (!id) return null;\n if (typeof id === \"string\" && id.startsWith(\"column:\")) return id.slice(\"column:\".length);\n const card = liveCards.find((c) => c.id === id);\n return card?.columnId ?? null;\n },\n [liveCards]\n );\n\n const handleDragStart = (event) => setActiveId(event.active.id);\n\n const handleDragEnd = (event) => {\n setActiveId(null);\n const { active, over } = event;\n if (!over) return;\n\n const fromColumnId = findColumnIdForCard(active.id);\n const toColumnId = resolveColumnId(over.id);\n if (!fromColumnId || !toColumnId) return;\n\n const fromList = (cardsByColumn[fromColumnId] ?? []).slice();\n const toList =\n fromColumnId === toColumnId ? fromList : (cardsByColumn[toColumnId] ?? []).slice();\n\n const fromIndex = fromList.findIndex((c) => c.id === active.id);\n let toIndex;\n\n if (over.data?.current?.type === \"column\" || over.id === `column:${toColumnId}`) {\n toIndex = toList.length;\n } else {\n toIndex = toList.findIndex((c) => c.id === over.id);\n if (toIndex === -1) toIndex = toList.length;\n }\n\n if (fromColumnId === toColumnId && fromIndex === toIndex) return;\n\n let nextCards;\n if (fromColumnId === toColumnId) {\n const reordered = arrayMove(fromList, fromIndex, toIndex);\n const others = liveCards.filter((c) => c.columnId !== fromColumnId);\n nextCards = [...others, ...reordered];\n } else {\n const moved = { ...fromList[fromIndex], columnId: toColumnId };\n const newFrom = fromList.filter((c) => c.id !== active.id);\n const newTo = toList.slice();\n newTo.splice(toIndex, 0, moved);\n const others = liveCards.filter(\n (c) => c.columnId !== fromColumnId && c.columnId !== toColumnId\n );\n nextCards = [...others, ...newFrom, ...newTo];\n }\n\n if (onCardMove) {\n onCardMove({\n cardId: active.id,\n fromColumnId,\n toColumnId,\n fromIndex,\n toIndex,\n });\n } else {\n setInternalCards(nextCards);\n }\n };\n\n const activeCard = activeId ? liveCards.find((c) => c.id === activeId) : null;\n\n const header =\n title || subtitle || actions ? (\n <div className=\"mb-3 flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n {title ? (\n <UIText as=\"div\" size=\"sm\" weight=\"medium\">\n {title}\n </UIText>\n ) : null}\n {subtitle ? (\n <UIText as=\"div\" size=\"xs\" muted className=\"mt-1\">\n {subtitle}\n </UIText>\n ) : null}\n </div>\n {actions ? <div className=\"shrink-0\">{actions}</div> : null}\n </div>\n ) : null;\n\n const board = (\n <div className=\"flex gap-3 overflow-x-auto pb-1\">\n {columns.map((col) => (\n <KanbanColumn\n key={col.id}\n column={col}\n cards={cardsByColumn[col.id] ?? []}\n accent={accent}\n onCardClick={onCardClick}\n isDraggable={isDraggable}\n dense={dense}\n showCounts={showColumnCounts}\n showLimits={showWipLimits}\n emptyMessage={emptyMessage}\n />\n ))}\n </div>\n );\n\n return (\n <BaseCard\n padding=\"default\"\n isLoading={loading}\n className={className}\n {...cardProps}\n >\n {header}\n {error ? (\n <div className=\"rounded-md border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300\">\n {String(error)}\n </div>\n ) : (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={() => setActiveId(null)}\n >\n {board}\n <DragOverlay>\n {activeCard ? (\n <KanbanCardBody card={activeCard} accent={accent} dragging dense={dense} />\n ) : null}\n </DragOverlay>\n </DndContext>\n )}\n </BaseCard>\n );\n}\n"],"names":["ACCENT_DOT","ACCENT_BORDER_LEFT","CardBadges","badges","jsx","b","i","React","UIChip","CardAvatar","avatar","title","KanbanCardBody","card","accent","dragging","onClick","dense","accentBorder","jsxs","UIText","m","e","SortableKanbanCard","isDraggable","sortable","useSortable","style","CSS","KanbanColumn","column","cards","onCardClick","showCounts","showLimits","emptyMessage","droppable","useDroppable","count","overLimit","SortableContext","c","verticalListSortingStrategy","KanbanBoard","columns","subtitle","showColumnCounts","showWipLimits","loading","error","actions","onCardMove","className","cardProps","activeId","setActiveId","useState","internalCards","setInternalCards","liveCards","sensors","useSensors","useSensor","PointerSensor","KeyboardSensor","sortableKeyboardCoordinates","cardsByColumn","useMemo","map","col","colId","findColumnIdForCard","useCallback","cardId","resolveColumnId","id","handleDragStart","event","handleDragEnd","active","over","fromColumnId","toColumnId","fromList","toList","fromIndex","toIndex","nextCards","reordered","arrayMove","moved","newFrom","newTo","activeCard","header","board","BaseCard","DndContext","closestCenter","DragOverlay"],"mappings":"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"}