gridsmith-ui 0.17.1 → 0.17.2

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.
package/dist/kanban.d.ts CHANGED
@@ -15,16 +15,31 @@ interface KanbanColumn {
15
15
  color?: string;
16
16
  }
17
17
  interface KanbanBoardProps {
18
- /** The board's cards by column. Authoritative: whenever a new array is passed the board shows it. */
18
+ /**
19
+ * The board's cards by column. Authoritative: the board shows what it is given, with one exception — a card just
20
+ * dropped stays where it was dropped until `columns` shows the move, the save fails, or `columns` changes some other
21
+ * way (see onCardMove). A re-render that passes the same cards again, or an array rebuilt with the same cards,
22
+ * never puts a dropped card back.
23
+ */
19
24
  columns: KanbanColumn[];
20
- /** The columns after a move, once on drop (Sprint 25.7, C2: it used to fire on every hover step). */
21
- onColumnsChange?: (columns: KanbanColumn[]) => void;
25
+ /**
26
+ * The columns after a move, once on drop (Sprint 25.7, C2: it used to fire on every hover step). Its return value
27
+ * counts as onCardMove's does.
28
+ */
29
+ onColumnsChange?: (columns: KanbanColumn[]) => unknown;
22
30
  /**
23
31
  * A card was dropped at a new place: its column and its index there, once on drop — a move between columns, into an
24
32
  * empty one, or a reorder within one. Save the move here (or in onColumnsChange); a board with neither saves nothing.
25
33
  * The index counts the cards the board shows: when `columns` is filtered, it is the place among the cards passed.
34
+ *
35
+ * Return the save's promise (an async function, or a mutation's mutateAsync) and the card stays where it was dropped
36
+ * while it saves, and while an earlier move of the same card still saves, whatever `columns` arrive meanwhile; when
37
+ * it rejects, the card goes back to where `columns` has it, a screen reader hears so, and the app says why (a toast
38
+ * inside the app shell). The save must settle (give a request a timeout): one that never does holds the card. A
39
+ * handler that throws refuses a move the same way (a column that is full). An app that updates `columns` at once,
40
+ * then saves, puts the card back itself when its save fails; the board still says the move was not saved.
26
41
  */
27
- onCardMove?: (card: KanbanCard, toColumnId: string, index: number) => void;
42
+ onCardMove?: (card: KanbanCard, toColumnId: string, index: number) => unknown;
28
43
  /** A board to look at, not rearrange: no dragging and no grab cursor; cards still open with onCardClick. */
29
44
  readOnly?: boolean;
30
45
  renderCard?: (card: KanbanCard) => ReactNode;
package/dist/kanban.js CHANGED
@@ -147,22 +147,46 @@ function landing(cols, cardId, overId) {
147
147
  if (!at || !overAt || overAt.col.id !== at.col.id || overAt.index === at.index) return cols;
148
148
  return cols.map((col) => col.id === at.col.id ? { ...col, cards: arrayMove(col.cards, at.index, overAt.index) } : col);
149
149
  }
150
- function rebase(next, shown, cardId) {
151
- const at = locate(shown, cardId);
152
- const from = locate(next, cardId);
153
- if (!at || !from || !next.some((col) => col.id === at.col.id)) return next;
150
+ function placementIn(cols, cardId) {
151
+ const at = locate(cols, cardId);
152
+ return at ? { cardId, toColumnId: at.col.id, beforeId: at.col.cards[at.index + 1]?.id ?? null, index: at.index } : null;
153
+ }
154
+ function place(cols, p) {
155
+ const from = locate(cols, p.cardId);
156
+ if (!from || !cols.some((col) => col.id === p.toColumnId)) return cols;
154
157
  const card = from.col.cards[from.index];
155
- const before = at.col.cards[at.index + 1]?.id;
156
- return next.map((col) => {
157
- if (col.id !== at.col.id && col.id !== from.col.id) return col;
158
- const cards = col.cards.filter((c) => c.id !== cardId);
159
- if (col.id === at.col.id) {
160
- const i = before == null ? cards.length : cards.findIndex((c) => c.id === before);
161
- cards.splice(i >= 0 ? i : Math.min(at.index, cards.length), 0, card);
158
+ return cols.map((col) => {
159
+ if (col.id !== p.toColumnId && col.id !== from.col.id) return col;
160
+ const cards = col.cards.filter((c) => c.id !== p.cardId);
161
+ if (col.id === p.toColumnId) {
162
+ const i = p.beforeId == null ? cards.length : cards.findIndex((c) => c.id === p.beforeId);
163
+ cards.splice(i >= 0 ? i : Math.min(p.index, cards.length), 0, card);
162
164
  }
163
165
  return { ...col, cards };
164
166
  });
165
167
  }
168
+ function rebase(next, shown, cardId) {
169
+ const p = placementIn(shown, cardId);
170
+ return p ? place(next, p) : next;
171
+ }
172
+ var layoutOf = (cols) => cols.map((col) => `${col.id}:${col.cards.map((c) => c.id).join(",")}`).join("|");
173
+ function isAt(cols, p) {
174
+ const at = locate(cols, p.cardId);
175
+ return at != null && at.col.id === p.toColumnId && (at.col.cards[at.index + 1]?.id ?? null) === p.beforeId;
176
+ }
177
+ function keptHolds(holds, cols) {
178
+ const layout = layoutOf(cols);
179
+ const kept = holds.filter((h) => {
180
+ if (h.inFlight > 0) return true;
181
+ if (!locate(cols, h.cardId) || !cols.some((col) => col.id === h.toColumnId)) return false;
182
+ if (isAt(cols, h.origin)) return layout === h.layout;
183
+ if (isAt(cols, h)) return false;
184
+ return h.earlier.some((p) => isAt(cols, p));
185
+ });
186
+ return kept.length === holds.length ? holds : kept;
187
+ }
188
+ var replayHolds = (cols, holds) => holds.reduce(place, cols);
189
+ var isThenable = (value) => value != null && typeof value.then === "function";
166
190
  var placeWords = (at) => `${at.col.title}, position ${at.index + 1} of ${at.col.cards.length}`;
167
191
  var MOVE_INSTRUCTIONS = "Press Space to pick up the card. Use the Left and Right arrow keys to move it between columns, and the Up and Down arrow keys to move it within a column. Press Space again to drop it, or Escape to cancel.";
168
192
  function BoardColumn({
@@ -227,8 +251,8 @@ function KanbanBoard({
227
251
  onCardClick,
228
252
  className
229
253
  }) {
230
- const [committed, setCommitted] = useState(columnsProp);
231
254
  const [seenProp, setSeenProp] = useState(columnsProp);
255
+ const [holds, setHoldsState] = useState([]);
232
256
  const [preview, setPreviewState] = useState(null);
233
257
  const previewRef = useRef(null);
234
258
  const setPreview = (next) => {
@@ -236,14 +260,29 @@ function KanbanBoard({
236
260
  setPreviewState(next);
237
261
  };
238
262
  const [activeCard, setActiveCard] = useState(null);
263
+ const holdsRef = useRef([]);
264
+ const columnsRef = useRef(columnsProp);
265
+ const activeRef = useRef(null);
266
+ const setHolds = (next) => {
267
+ holdsRef.current = next;
268
+ setHoldsState(next);
269
+ };
239
270
  if (columnsProp !== seenProp) {
240
271
  setSeenProp(columnsProp);
241
- setCommitted(columnsProp);
242
- if (preview && activeCard) setPreviewState(rebase(columnsProp, preview, activeCard.id));
272
+ const kept = keptHolds(holds, columnsProp);
273
+ if (kept !== holds) setHoldsState(kept);
274
+ if (preview && activeCard) setPreviewState(rebase(replayHolds(columnsProp, kept), preview, activeCard.id));
243
275
  }
244
276
  useLayoutEffect(() => {
245
277
  previewRef.current = preview;
246
278
  }, [preview]);
279
+ useLayoutEffect(() => {
280
+ holdsRef.current = holds;
281
+ }, [holds]);
282
+ useLayoutEffect(() => {
283
+ columnsRef.current = columnsProp;
284
+ }, [columnsProp]);
285
+ const committed = useMemo(() => replayHolds(columnsProp, holds), [columnsProp, holds]);
247
286
  const columns = preview ?? committed;
248
287
  const said = useRef(void 0);
249
288
  const lastPlace = useRef(null);
@@ -256,6 +295,22 @@ function KanbanBoard({
256
295
  return { onDragStart: take, onDragOver: take, onDragEnd: take, onDragCancel: take };
257
296
  }, []);
258
297
  const instructions = { draggable: onCardClick ? `Press Enter to open the card. ${MOVE_INSTRUCTIONS}` : MOVE_INSTRUCTIONS };
298
+ const [notice, setNotice] = useState("");
299
+ const noticeParts = useRef([]);
300
+ const say = (text) => {
301
+ noticeParts.current = [...noticeParts.current, text];
302
+ setNotice(noticeParts.current.join(" "));
303
+ };
304
+ const rootRef = useRef(null);
305
+ const refocus = useRef(null);
306
+ useLayoutEffect(() => {
307
+ const id = refocus.current;
308
+ if (id == null) return;
309
+ refocus.current = null;
310
+ const cards = rootRef.current?.querySelectorAll("[data-kanban-card]") ?? [];
311
+ [...cards].find((el) => el.getAttribute("data-kanban-card") === id)?.focus();
312
+ });
313
+ const holdKey = useRef(0);
259
314
  const sensors = useSensors(
260
315
  useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
261
316
  useSensor(KeyboardSensor, {
@@ -268,6 +323,9 @@ function KanbanBoard({
268
323
  const handleDragStart = (event) => {
269
324
  const at = locate(committed, String(event.active.id));
270
325
  if (!at) return;
326
+ noticeParts.current = [];
327
+ setNotice("");
328
+ activeRef.current = at.col.cards[at.index].id;
271
329
  setActiveCard(at.col.cards[at.index]);
272
330
  setPreview(committed);
273
331
  lastPlace.current = placeWords(at);
@@ -292,16 +350,40 @@ function KanbanBoard({
292
350
  }));
293
351
  }
294
352
  const now = overId == null ? null : locate(landing(previewRef.current ?? committed, activeId, overId), activeId);
295
- const place = now ? placeWords(now) : null;
296
- if (place === lastPlace.current) return;
297
- lastPlace.current = place;
353
+ const where = now ? placeWords(now) : null;
354
+ if (where === lastPlace.current) return;
355
+ lastPlace.current = where;
298
356
  const title = now ? now.col.cards[now.index].title : activeCard?.title;
299
- said.current = now ? `${title} is over ${place}.` : `${title} is not over a column.`;
357
+ said.current = now ? `${title} is over ${where}.` : `${title} is not over a column.`;
358
+ };
359
+ const putBack = (hold) => {
360
+ const rest = holdsRef.current.filter((h) => h.cardId !== hold.cardId);
361
+ const shown = replayHolds(columnsRef.current, rest);
362
+ const back = locate(shown, hold.cardId);
363
+ const card = back?.col.cards[back.index];
364
+ const focused = document.activeElement;
365
+ if (focused && rootRef.current?.contains(focused) && focused.closest?.("[data-kanban-card]")?.getAttribute("data-kanban-card") === hold.cardId) {
366
+ refocus.current = hold.cardId;
367
+ }
368
+ setHolds(rest);
369
+ if (previewRef.current && activeRef.current) setPreview(rebase(shown, previewRef.current, activeRef.current));
370
+ if (!back || !card) return;
371
+ const to = columnsRef.current.find((col) => col.id === hold.toColumnId)?.title;
372
+ say(isAt(columnsRef.current, hold.origin) || !to ? `Move not saved. ${card.title} is back in ${placeWords(back)}.` : `Move not saved. ${card.title} could not move to ${to}.`);
373
+ };
374
+ const settle = (cardId, key, ok) => {
375
+ const hold = holdsRef.current.find((h) => h.cardId === cardId);
376
+ if (!hold) return;
377
+ if (!ok && hold.key === key) return putBack(hold);
378
+ const inFlight = hold.inFlight - 1;
379
+ const next = holdsRef.current.map((h) => h === hold ? { ...h, inFlight, layout: inFlight > 0 ? h.layout : layoutOf(columnsRef.current) } : h);
380
+ setHolds(inFlight > 0 ? next : keptHolds(next, columnsRef.current));
300
381
  };
301
382
  const handleDragEnd = (event) => {
302
383
  const { active, over } = event;
303
384
  const activeId = String(active.id);
304
385
  const cols = previewRef.current ?? committed;
386
+ activeRef.current = null;
305
387
  setActiveCard(null);
306
388
  setPreview(null);
307
389
  const final = over ? landing(cols, activeId, String(over.id)) : committed;
@@ -312,11 +394,32 @@ function KanbanBoard({
312
394
  return;
313
395
  }
314
396
  said.current = `Dropped ${after.col.cards[after.index].title} in ${placeWords(after)}.`;
315
- setCommitted(final);
316
- onColumnsChange?.(final);
317
- onCardMove?.(after.col.cards[after.index], after.col.id, after.index);
397
+ const key = ++holdKey.current;
398
+ const prev = holdsRef.current.find((h) => h.cardId === activeId);
399
+ const hold = {
400
+ ...placementIn(final, activeId),
401
+ key,
402
+ inFlight: prev?.inFlight ?? 0,
403
+ origin: prev?.origin ?? placementIn(columnsProp, activeId) ?? placementIn(committed, activeId),
404
+ earlier: prev ? [...prev.earlier, { cardId: prev.cardId, toColumnId: prev.toColumnId, beforeId: prev.beforeId, index: prev.index }] : [],
405
+ layout: layoutOf(columnsProp)
406
+ };
407
+ setHolds([...holdsRef.current.filter((h) => h.cardId !== activeId), hold]);
408
+ const saves = [];
409
+ try {
410
+ for (const result of [onColumnsChange?.(final), onCardMove?.(after.col.cards[after.index], after.col.id, after.index)]) {
411
+ if (isThenable(result)) saves.push(result);
412
+ }
413
+ } catch (error) {
414
+ console.error("KanbanBoard: a move handler threw, so the move is refused.", error);
415
+ return putBack(hold);
416
+ }
417
+ if (!saves.length) return;
418
+ setHolds(holdsRef.current.map((h) => h.key === key ? { ...h, inFlight: h.inFlight + 1 } : h));
419
+ Promise.allSettled(saves).then((results) => settle(activeId, key, results.every((r) => r.status === "fulfilled")));
318
420
  };
319
421
  const handleDragCancel = (event) => {
422
+ activeRef.current = null;
320
423
  setActiveCard(null);
321
424
  setPreview(null);
322
425
  const at = locate(committed, String(event.active.id));
@@ -333,16 +436,19 @@ function KanbanBoard({
333
436
  onDragEnd: handleDragEnd,
334
437
  onDragCancel: handleDragCancel,
335
438
  children: [
336
- /* @__PURE__ */ jsx("div", { className: cn("flex gap-ds-4 overflow-x-auto pb-ds-2 snap-x snap-mandatory sm:snap-none", className), children: columns.map((col) => /* @__PURE__ */ jsx(BoardColumn, { col, readOnly, children: col.cards.map((card) => /* @__PURE__ */ jsx(
337
- SortableCard,
338
- {
339
- card,
340
- renderCard,
341
- onCardClick,
342
- readOnly
343
- },
344
- card.id
345
- )) }, col.id)) }),
439
+ /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("flex gap-ds-4 overflow-x-auto pb-ds-2 snap-x snap-mandatory sm:snap-none", className), children: [
440
+ columns.map((col) => /* @__PURE__ */ jsx(BoardColumn, { col, readOnly, children: col.cards.map((card) => /* @__PURE__ */ jsx(
441
+ SortableCard,
442
+ {
443
+ card,
444
+ renderCard,
445
+ onCardClick,
446
+ readOnly
447
+ },
448
+ card.id
449
+ )) }, col.id)),
450
+ /* @__PURE__ */ jsx("div", { role: "status", className: "sr-only", children: notice })
451
+ ] }),
346
452
  /* @__PURE__ */ jsx(DragOverlay, { children: activeCard && /* @__PURE__ */ jsx(OverlayCard, { card: activeCard, renderCard }) })
347
453
  ]
348
454
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../showcase/src/components/KanbanBoard.tsx"],"names":[],"mappings":"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","file":"kanban.js","sourcesContent":["import { useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport {\n DndContext,\n closestCorners,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type Announcements,\n type DragCancelEvent,\n type DragEndEvent,\n type DragOverEvent,\n DragOverlay,\n type DragStartEvent,\n type KeyboardCoordinateGetter,\n useDroppable,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n arrayMove,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n useSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../lib/icons\";\nimport { Tooltip } from \"./Tooltip\";\nimport { useTextOverflow } from \"../lib/use-text-overflow\";\nimport { Badge } from \"./Badge\";\n\nexport interface KanbanCard {\n id: string;\n title: string;\n description?: string;\n tags?: string[];\n avatar?: string;\n}\n\nexport interface KanbanColumn {\n id: string;\n title: string;\n cards: KanbanCard[];\n color?: string;\n}\n\nexport interface KanbanBoardProps {\n /** The board's cards by column. Authoritative: whenever a new array is passed the board shows it. */\n columns: KanbanColumn[];\n /** The columns after a move, once on drop (Sprint 25.7, C2: it used to fire on every hover step). */\n onColumnsChange?: (columns: KanbanColumn[]) => void;\n /**\n * A card was dropped at a new place: its column and its index there, once on drop — a move between columns, into an\n * empty one, or a reorder within one. Save the move here (or in onColumnsChange); a board with neither saves nothing.\n * The index counts the cards the board shows: when `columns` is filtered, it is the place among the cards passed.\n */\n onCardMove?: (card: KanbanCard, toColumnId: string, index: number) => void;\n /** A board to look at, not rearrange: no dragging and no grab cursor; cards still open with onCardClick. */\n readOnly?: boolean;\n renderCard?: (card: KanbanCard) => ReactNode;\n /**\n * Opens a card (a detail drawer, a route). The board's own card surface is the control: click, or Enter with the\n * card focused, opens it; Space picks the card up for a keyboard drag. Never wire a clickable element inside\n * renderCard — a role=\"button\" div fights the drag listeners and the judge cites it (Sprint 25.4 K-1).\n */\n onCardClick?: (card: KanbanCard) => void;\n className?: string;\n}\n\nfunction SortableCard({\n card,\n renderCard,\n onCardClick,\n readOnly,\n}: {\n card: KanbanCard;\n renderCard?: (card: KanbanCard) => ReactNode;\n onCardClick?: (card: KanbanCard) => void;\n readOnly?: boolean;\n}) {\n const {\n attributes,\n listeners,\n setNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id: card.id, disabled: readOnly });\n\n if (readOnly) {\n // nothing to pick up: a card that opens is a button (Enter and Space), one that does not is a plain surface\n const open = onCardClick ? () => onCardClick(card) : undefined;\n return (\n <div\n ref={setNodeRef}\n {...(open ? { role: \"button\", tabIndex: 0 } : {})}\n onClick={open}\n onKeyDown={open ? (e) => { if (e.key === \"Enter\" || e.key === \" \") { e.preventDefault(); open(); } } : undefined}\n data-kanban-card={card.id}\n className={cn(\n \"rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-button)]\",\n open ? \"cursor-pointer\" : \"cursor-default\",\n \"focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]\",\n )}\n >\n {renderCard ? renderCard(card) : <DefaultCard card={card} />}\n </div>\n );\n }\n\n const style = {\n transform: CSS.Transform.toString(transform),\n transition,\n };\n\n return (\n <div\n ref={setNodeRef}\n style={style}\n {...attributes}\n {...listeners}\n // the surface is the control (K-1): a click opens (the pointer sensor needs 5 px of movement to start a drag),\n // Enter opens, Space stays the keyboard drag's pick-up\n onClick={onCardClick ? () => onCardClick(card) : undefined}\n onKeyDown={(e) => {\n if (onCardClick && e.key === \"Enter\") { e.preventDefault(); onCardClick(card); return; }\n listeners?.onKeyDown?.(e);\n }}\n data-kanban-card={card.id}\n className={cn(\n \"rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)] p-ds-3 active:cursor-grabbing\",\n onCardClick ? \"cursor-pointer\" : \"cursor-grab\",\n \"shadow-[var(--ds-shadow-button)]\",\n \"transition-shadow duration-[var(--ds-transition-speed)]\",\n \"focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]\",\n // no scale on the card left behind: the drag overlay takes its size from it, and 2% wider made the cards\n // below count as \"to the right\" of it, so a keyboard drag's arrow keys landed on them (Sprint 25.7, C2)\n isDragging && \"opacity-30\",\n )}\n >\n {renderCard ? (\n renderCard(card)\n ) : (\n <DefaultCard card={card} />\n )}\n </div>\n );\n}\n\nfunction ClampedDescription({ children }: { children: string }) {\n const { ref, isTruncated } = useTextOverflow({ lines: 2 });\n const el = <p ref={ref} className=\"text-xs text-[var(--ds-text-secondary)] line-clamp-2\">{children}</p>;\n if (!isTruncated) return el;\n return <Tooltip content={children}>{el}</Tooltip>;\n}\n\nfunction TruncTitle({ children }: { children: string }) {\n const { ref, isTruncated } = useTextOverflow({ lines: 1 });\n const el = <p ref={ref} className=\"text-sm font-medium text-[var(--ds-text-primary)] truncate\">{children}</p>;\n if (!isTruncated) return el;\n return <Tooltip content={children}>{el}</Tooltip>;\n}\n\nfunction DefaultCard({ card }: { card: KanbanCard }) {\n return (\n <div className=\"space-y-ds-2\">\n <TruncTitle>{card.title}</TruncTitle>\n {card.description && (\n <ClampedDescription>{card.description}</ClampedDescription>\n )}\n {card.tags && card.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-ds-1\">\n {card.tags.map((tag) => (\n <Badge key={tag} variant=\"subtle\" size=\"sm\">{tag}</Badge>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction OverlayCard({\n card,\n renderCard,\n}: {\n card: KanbanCard;\n renderCard?: (card: KanbanCard) => ReactNode;\n}) {\n return (\n <div\n className={cn(\n \"rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-card)]\",\n )}\n >\n {renderCard ? renderCard(card) : <DefaultCard card={card} />}\n </div>\n );\n}\n\n/** A column's drop area has its own id, so it never collides with a card id. */\nconst DROP_PREFIX = \"kanban-column:\";\nconst columnDropId = (id: string) => `${DROP_PREFIX}${id}`;\nconst droppedColumnId = (id: string) => (id.startsWith(DROP_PREFIX) ? id.slice(DROP_PREFIX.length) : null);\n\n/**\n * Sprint 25.7, C2: a keyboard drag goes column to column. Left and Right move the card to the top of the next column's\n * list — an empty column included — and Up and Down reorder within the column (dnd-kit's own handler). dnd-kit's\n * handler alone picks the nearest drop area to the right of the card's edge, which is often a card in the same column\n * or the card's own new place, so every other arrow key did nothing.\n */\nconst boardCoordinates: KeyboardCoordinateGetter = (event, args) => {\n if (event.code !== \"ArrowLeft\" && event.code !== \"ArrowRight\") return sortableKeyboardCoordinates(event, args);\n event.preventDefault();\n const { collisionRect, droppableRects, droppableContainers } = args.context;\n if (!collisionRect) return undefined;\n const columns = droppableContainers.getEnabled()\n .filter((c) => String(c.id).startsWith(DROP_PREFIX))\n .map((c) => droppableRects.get(c.id))\n .filter((r): r is NonNullable<typeof r> => r != null)\n .sort((a, b) => a.left - b.left);\n const centre = collisionRect.left + collisionRect.width / 2;\n const current = columns.findIndex((r) => centre >= r.left && centre <= r.left + r.width);\n const next = current < 0 ? undefined : columns[current + (event.code === \"ArrowRight\" ? 1 : -1)];\n if (!next) return undefined;\n return { x: next.left + (next.width - collisionRect.width) / 2, y: next.top };\n};\n\n/** Where a card is: its column's id and its index there. */\nfunction locate(cols: KanbanColumn[], cardId: string): { col: KanbanColumn; index: number } | null {\n for (const col of cols) {\n const index = col.cards.findIndex((c) => c.id === cardId);\n if (index >= 0) return { col, index };\n }\n return null;\n}\n\n/** The columns after a drop on `overId`: within the column the preview put the card in, a drop on another card reorders it. */\nfunction landing(cols: KanbanColumn[], cardId: string, overId: string): KanbanColumn[] {\n const at = locate(cols, cardId);\n const overAt = locate(cols, overId);\n if (!at || !overAt || overAt.col.id !== at.col.id || overAt.index === at.index) return cols;\n return cols.map((col) => (col.id === at.col.id ? { ...col, cards: arrayMove(col.cards, at.index, overAt.index) } : col));\n}\n\n/**\n * A drag's move so far, re-applied to new columns: the card goes into the same column, before the same card, so a\n * change that arrives while a card is held shows at once and the drop keeps it.\n */\nfunction rebase(next: KanbanColumn[], shown: KanbanColumn[], cardId: string): KanbanColumn[] {\n const at = locate(shown, cardId);\n const from = locate(next, cardId);\n if (!at || !from || !next.some((col) => col.id === at.col.id)) return next;\n const card = from.col.cards[from.index];\n const before = at.col.cards[at.index + 1]?.id;\n return next.map((col) => {\n if (col.id !== at.col.id && col.id !== from.col.id) return col;\n const cards = col.cards.filter((c) => c.id !== cardId);\n if (col.id === at.col.id) {\n const i = before == null ? cards.length : cards.findIndex((c) => c.id === before);\n cards.splice(i >= 0 ? i : Math.min(at.index, cards.length), 0, card);\n }\n return { ...col, cards };\n });\n}\n\n/** A card's place, for the live region: \"To do, position 2 of 3\". */\nconst placeWords = (at: { col: KanbanColumn; index: number }) => `${at.col.title}, position ${at.index + 1} of ${at.col.cards.length}`;\n\n/** Read to a screen reader on every card that can be dragged (dnd-kit's aria-describedby). */\nconst MOVE_INSTRUCTIONS = \"Press Space to pick up the card. Use the Left and Right arrow keys to move it between columns, \"\n + \"and the Up and Down arrow keys to move it within a column. Press Space again to drop it, or Escape to cancel.\";\n\nfunction BoardColumn({\n col,\n readOnly,\n children,\n}: {\n col: KanbanColumn;\n readOnly?: boolean;\n children: ReactNode;\n}) {\n // Sprint 25.7, C2: the column is a drop area as well as a sortable list, so an empty column takes a card\n const { setNodeRef, isOver } = useDroppable({ id: columnDropId(col.id), disabled: readOnly });\n return (\n <div\n className={cn(\n \"flex flex-col shrink-0 w-[calc(100vw-4rem)] sm:w-[280px] snap-center\",\n \"bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-lg)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n )}\n >\n <div className=\"flex items-center justify-between px-ds-3 py-ds-2.5\">\n <div className=\"flex items-center gap-ds-2\">\n {col.color && (\n <span\n className=\"w-2.5 h-2.5 rounded-[var(--ds-radius-full)] shrink-0\"\n style={{ backgroundColor: col.color }}\n />\n )}\n <span className=\"text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]\">\n {col.title}\n </span>\n <span className=\"text-xs text-[var(--ds-text-muted)] tabular-nums\">\n {col.cards.length}\n </span>\n </div>\n <Icon name=\"plus\" size={14} className=\"text-[var(--ds-text-muted)]\" />\n </div>\n\n <SortableContext\n items={col.cards.map((c) => c.id)}\n strategy={verticalListSortingStrategy}\n id={col.id}\n >\n <div\n ref={setNodeRef}\n data-kanban-column={col.id}\n className={cn(\n \"flex flex-col gap-ds-2.5 px-ds-3 pb-ds-3 min-h-[60px] rounded-b-[var(--ds-radius-lg)] transition-colors\",\n isOver && col.cards.length === 0 && \"bg-[var(--ds-bg-surface)]/60\",\n )}\n >\n {children}\n </div>\n </SortableContext>\n </div>\n );\n}\n\nexport function KanbanBoard({\n columns: columnsProp,\n onColumnsChange,\n onCardMove,\n readOnly,\n renderCard,\n onCardClick,\n className,\n}: KanbanBoardProps) {\n // Sprint 25.7, C2: `columns` is authoritative — a new array replaces what the board shows. A move is previewed while\n // the card is dragged and committed once on drop; a cancelled drag (Escape) leaves the board as it was.\n const [committed, setCommitted] = useState<KanbanColumn[]>(columnsProp);\n const [seenProp, setSeenProp] = useState(columnsProp);\n // the preview lives in a ref as well, so a drop in the same tick as the last hover still reads it\n const [preview, setPreviewState] = useState<KanbanColumn[] | null>(null);\n const previewRef = useRef<KanbanColumn[] | null>(null);\n const setPreview = (next: KanbanColumn[] | null) => { previewRef.current = next; setPreviewState(next); };\n const [activeCard, setActiveCard] = useState<KanbanCard | null>(null);\n if (columnsProp !== seenProp) {\n setSeenProp(columnsProp);\n setCommitted(columnsProp);\n // new columns while a card is held (a refetch, another user's move): the preview is rebuilt from them\n if (preview && activeCard) setPreviewState(rebase(columnsProp, preview, activeCard.id));\n }\n // a preview rebuilt while rendering reaches the ref before any drag event can read it\n useLayoutEffect(() => { previewRef.current = preview; }, [preview]);\n const columns = preview ?? committed;\n\n // The live region speaks card and column titles, never ids. Each handler words its news in `said`, and dnd-kit\n // announces it straight after calling the handler. A move is said when the card's landing place changes.\n const said = useRef<string | undefined>(undefined);\n const lastPlace = useRef<string | null>(null);\n const announcements = useMemo<Announcements>(() => {\n const take = () => { const text = said.current; said.current = undefined; return text; };\n return { onDragStart: take, onDragOver: take, onDragEnd: take, onDragCancel: take };\n }, []);\n const instructions = { draggable: onCardClick ? `Press Enter to open the card. ${MOVE_INSTRUCTIONS}` : MOVE_INSTRUCTIONS };\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor, {\n coordinateGetter: boardCoordinates,\n // Space picks up and drops; Enter is the card's open key (onCardClick)\n keyboardCodes: { start: [\"Space\"], cancel: [\"Escape\"], end: [\"Space\"] },\n }),\n );\n // read-only: no sensors at all (an absent list would give dnd-kit's defaults)\n const noSensors = useSensors();\n\n const handleDragStart = (event: DragStartEvent) => {\n const at = locate(committed, String(event.active.id));\n if (!at) return;\n setActiveCard(at.col.cards[at.index]);\n setPreview(committed);\n lastPlace.current = placeWords(at);\n said.current = `Picked up ${at.col.cards[at.index].title} in ${lastPlace.current}.`;\n };\n\n // Across columns, the preview moves the card as it is dragged; the parent hears nothing until the drop.\n const handleDragOver = (event: DragOverEvent) => {\n const { active, over } = event;\n const activeId = String(active.id);\n const overId = over ? String(over.id) : null;\n const cols = previewRef.current ?? committed;\n const from = locate(cols, activeId);\n const toColumnId = overId == null || overId === activeId ? null : droppedColumnId(overId) ?? locate(cols, overId)?.col.id;\n if (from && toColumnId && from.col.id !== toColumnId) {\n const card = from.col.cards[from.index];\n setPreview(cols.map((col) => {\n if (col.id === from.col.id) return { ...col, cards: col.cards.filter((c) => c.id !== activeId) };\n if (col.id !== toColumnId) return col;\n const overIndex = col.cards.findIndex((c) => c.id === overId);\n const cards = [...col.cards];\n cards.splice(overIndex >= 0 ? overIndex : cards.length, 0, card);\n return { ...col, cards };\n }));\n }\n // where a drop now would put the card\n const now = overId == null ? null : locate(landing(previewRef.current ?? committed, activeId, overId), activeId);\n const place = now ? placeWords(now) : null;\n if (place === lastPlace.current) return;\n lastPlace.current = place;\n const title = now ? now.col.cards[now.index].title : activeCard?.title;\n said.current = now ? `${title} is over ${place}.` : `${title} is not over a column.`;\n };\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n const activeId = String(active.id);\n const cols = previewRef.current ?? committed;\n setActiveCard(null);\n setPreview(null);\n const final = over ? landing(cols, activeId, String(over.id)) : committed;\n const before = locate(committed, activeId);\n const after = locate(final, activeId);\n if (!before || !after || (before.col.id === after.col.id && before.index === after.index)) {\n if (before) said.current = `Dropped ${before.col.cards[before.index].title} back in ${placeWords(before)}.`;\n return;\n }\n said.current = `Dropped ${after.col.cards[after.index].title} in ${placeWords(after)}.`;\n setCommitted(final);\n onColumnsChange?.(final);\n onCardMove?.(after.col.cards[after.index], after.col.id, after.index);\n };\n\n const handleDragCancel = (event: DragCancelEvent) => {\n setActiveCard(null);\n setPreview(null);\n const at = locate(committed, String(event.active.id));\n if (at) said.current = `Move cancelled. ${at.col.cards[at.index].title} is back in ${placeWords(at)}.`;\n };\n\n return (\n <DndContext\n sensors={readOnly ? noSensors : sensors}\n accessibility={{ announcements, screenReaderInstructions: instructions }}\n collisionDetection={closestCorners}\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className={cn(\"flex gap-ds-4 overflow-x-auto pb-ds-2 snap-x snap-mandatory sm:snap-none\", className)}>\n {columns.map((col) => (\n <BoardColumn key={col.id} col={col} readOnly={readOnly}>\n {col.cards.map((card) => (\n <SortableCard\n key={card.id}\n card={card}\n renderCard={renderCard}\n onCardClick={onCardClick}\n readOnly={readOnly}\n />\n ))}\n </BoardColumn>\n ))}\n </div>\n\n <DragOverlay>\n {activeCard && <OverlayCard card={activeCard} renderCard={renderCard} />}\n </DragOverlay>\n </DndContext>\n );\n}\n"]}
1
+ {"version":3,"sources":["../../../showcase/src/components/KanbanBoard.tsx"],"names":[],"mappings":"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","file":"kanban.js","sourcesContent":["import { useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport {\n DndContext,\n closestCorners,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type Announcements,\n type DragCancelEvent,\n type DragEndEvent,\n type DragOverEvent,\n DragOverlay,\n type DragStartEvent,\n type KeyboardCoordinateGetter,\n useDroppable,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n arrayMove,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n useSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../lib/icons\";\nimport { Tooltip } from \"./Tooltip\";\nimport { useTextOverflow } from \"../lib/use-text-overflow\";\nimport { Badge } from \"./Badge\";\n\nexport interface KanbanCard {\n id: string;\n title: string;\n description?: string;\n tags?: string[];\n avatar?: string;\n}\n\nexport interface KanbanColumn {\n id: string;\n title: string;\n cards: KanbanCard[];\n color?: string;\n}\n\nexport interface KanbanBoardProps {\n /**\n * The board's cards by column. Authoritative: the board shows what it is given, with one exception — a card just\n * dropped stays where it was dropped until `columns` shows the move, the save fails, or `columns` changes some other\n * way (see onCardMove). A re-render that passes the same cards again, or an array rebuilt with the same cards,\n * never puts a dropped card back.\n */\n columns: KanbanColumn[];\n /**\n * The columns after a move, once on drop (Sprint 25.7, C2: it used to fire on every hover step). Its return value\n * counts as onCardMove's does.\n */\n onColumnsChange?: (columns: KanbanColumn[]) => unknown;\n /**\n * A card was dropped at a new place: its column and its index there, once on drop — a move between columns, into an\n * empty one, or a reorder within one. Save the move here (or in onColumnsChange); a board with neither saves nothing.\n * The index counts the cards the board shows: when `columns` is filtered, it is the place among the cards passed.\n *\n * Return the save's promise (an async function, or a mutation's mutateAsync) and the card stays where it was dropped\n * while it saves, and while an earlier move of the same card still saves, whatever `columns` arrive meanwhile; when\n * it rejects, the card goes back to where `columns` has it, a screen reader hears so, and the app says why (a toast\n * inside the app shell). The save must settle (give a request a timeout): one that never does holds the card. A\n * handler that throws refuses a move the same way (a column that is full). An app that updates `columns` at once,\n * then saves, puts the card back itself when its save fails; the board still says the move was not saved.\n */\n onCardMove?: (card: KanbanCard, toColumnId: string, index: number) => unknown;\n /** A board to look at, not rearrange: no dragging and no grab cursor; cards still open with onCardClick. */\n readOnly?: boolean;\n renderCard?: (card: KanbanCard) => ReactNode;\n /**\n * Opens a card (a detail drawer, a route). The board's own card surface is the control: click, or Enter with the\n * card focused, opens it; Space picks the card up for a keyboard drag. Never wire a clickable element inside\n * renderCard — a role=\"button\" div fights the drag listeners and the judge cites it (Sprint 25.4 K-1).\n */\n onCardClick?: (card: KanbanCard) => void;\n className?: string;\n}\n\nfunction SortableCard({\n card,\n renderCard,\n onCardClick,\n readOnly,\n}: {\n card: KanbanCard;\n renderCard?: (card: KanbanCard) => ReactNode;\n onCardClick?: (card: KanbanCard) => void;\n readOnly?: boolean;\n}) {\n const {\n attributes,\n listeners,\n setNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id: card.id, disabled: readOnly });\n\n if (readOnly) {\n // nothing to pick up: a card that opens is a button (Enter and Space), one that does not is a plain surface\n const open = onCardClick ? () => onCardClick(card) : undefined;\n return (\n <div\n ref={setNodeRef}\n {...(open ? { role: \"button\", tabIndex: 0 } : {})}\n onClick={open}\n onKeyDown={open ? (e) => { if (e.key === \"Enter\" || e.key === \" \") { e.preventDefault(); open(); } } : undefined}\n data-kanban-card={card.id}\n className={cn(\n \"rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-button)]\",\n open ? \"cursor-pointer\" : \"cursor-default\",\n \"focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]\",\n )}\n >\n {renderCard ? renderCard(card) : <DefaultCard card={card} />}\n </div>\n );\n }\n\n const style = {\n transform: CSS.Transform.toString(transform),\n transition,\n };\n\n return (\n <div\n ref={setNodeRef}\n style={style}\n {...attributes}\n {...listeners}\n // the surface is the control (K-1): a click opens (the pointer sensor needs 5 px of movement to start a drag),\n // Enter opens, Space stays the keyboard drag's pick-up\n onClick={onCardClick ? () => onCardClick(card) : undefined}\n onKeyDown={(e) => {\n if (onCardClick && e.key === \"Enter\") { e.preventDefault(); onCardClick(card); return; }\n listeners?.onKeyDown?.(e);\n }}\n data-kanban-card={card.id}\n className={cn(\n \"rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)] p-ds-3 active:cursor-grabbing\",\n onCardClick ? \"cursor-pointer\" : \"cursor-grab\",\n \"shadow-[var(--ds-shadow-button)]\",\n \"transition-shadow duration-[var(--ds-transition-speed)]\",\n \"focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]\",\n // no scale on the card left behind: the drag overlay takes its size from it, and 2% wider made the cards\n // below count as \"to the right\" of it, so a keyboard drag's arrow keys landed on them (Sprint 25.7, C2)\n isDragging && \"opacity-30\",\n )}\n >\n {renderCard ? (\n renderCard(card)\n ) : (\n <DefaultCard card={card} />\n )}\n </div>\n );\n}\n\nfunction ClampedDescription({ children }: { children: string }) {\n const { ref, isTruncated } = useTextOverflow({ lines: 2 });\n const el = <p ref={ref} className=\"text-xs text-[var(--ds-text-secondary)] line-clamp-2\">{children}</p>;\n if (!isTruncated) return el;\n return <Tooltip content={children}>{el}</Tooltip>;\n}\n\nfunction TruncTitle({ children }: { children: string }) {\n const { ref, isTruncated } = useTextOverflow({ lines: 1 });\n const el = <p ref={ref} className=\"text-sm font-medium text-[var(--ds-text-primary)] truncate\">{children}</p>;\n if (!isTruncated) return el;\n return <Tooltip content={children}>{el}</Tooltip>;\n}\n\nfunction DefaultCard({ card }: { card: KanbanCard }) {\n return (\n <div className=\"space-y-ds-2\">\n <TruncTitle>{card.title}</TruncTitle>\n {card.description && (\n <ClampedDescription>{card.description}</ClampedDescription>\n )}\n {card.tags && card.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-ds-1\">\n {card.tags.map((tag) => (\n <Badge key={tag} variant=\"subtle\" size=\"sm\">{tag}</Badge>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction OverlayCard({\n card,\n renderCard,\n}: {\n card: KanbanCard;\n renderCard?: (card: KanbanCard) => ReactNode;\n}) {\n return (\n <div\n className={cn(\n \"rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-card)]\",\n )}\n >\n {renderCard ? renderCard(card) : <DefaultCard card={card} />}\n </div>\n );\n}\n\n/** A column's drop area has its own id, so it never collides with a card id. */\nconst DROP_PREFIX = \"kanban-column:\";\nconst columnDropId = (id: string) => `${DROP_PREFIX}${id}`;\nconst droppedColumnId = (id: string) => (id.startsWith(DROP_PREFIX) ? id.slice(DROP_PREFIX.length) : null);\n\n/**\n * Sprint 25.7, C2: a keyboard drag goes column to column. Left and Right move the card to the top of the next column's\n * list — an empty column included — and Up and Down reorder within the column (dnd-kit's own handler). dnd-kit's\n * handler alone picks the nearest drop area to the right of the card's edge, which is often a card in the same column\n * or the card's own new place, so every other arrow key did nothing.\n */\nconst boardCoordinates: KeyboardCoordinateGetter = (event, args) => {\n if (event.code !== \"ArrowLeft\" && event.code !== \"ArrowRight\") return sortableKeyboardCoordinates(event, args);\n event.preventDefault();\n const { collisionRect, droppableRects, droppableContainers } = args.context;\n if (!collisionRect) return undefined;\n const columns = droppableContainers.getEnabled()\n .filter((c) => String(c.id).startsWith(DROP_PREFIX))\n .map((c) => droppableRects.get(c.id))\n .filter((r): r is NonNullable<typeof r> => r != null)\n .sort((a, b) => a.left - b.left);\n const centre = collisionRect.left + collisionRect.width / 2;\n const current = columns.findIndex((r) => centre >= r.left && centre <= r.left + r.width);\n const next = current < 0 ? undefined : columns[current + (event.code === \"ArrowRight\" ? 1 : -1)];\n if (!next) return undefined;\n return { x: next.left + (next.width - collisionRect.width) / 2, y: next.top };\n};\n\n/** Where a card is: its column's id and its index there. */\nfunction locate(cols: KanbanColumn[], cardId: string): { col: KanbanColumn; index: number } | null {\n for (const col of cols) {\n const index = col.cards.findIndex((c) => c.id === cardId);\n if (index >= 0) return { col, index };\n }\n return null;\n}\n\n/** The columns after a drop on `overId`: within the column the preview put the card in, a drop on another card reorders it. */\nfunction landing(cols: KanbanColumn[], cardId: string, overId: string): KanbanColumn[] {\n const at = locate(cols, cardId);\n const overAt = locate(cols, overId);\n if (!at || !overAt || overAt.col.id !== at.col.id || overAt.index === at.index) return cols;\n return cols.map((col) => (col.id === at.col.id ? { ...col, cards: arrayMove(col.cards, at.index, overAt.index) } : col));\n}\n\n/** A card's place, to re-apply to other columns: its column, the card after it (null when last) and its index. */\ninterface Placement {\n cardId: string;\n toColumnId: string;\n beforeId: string | null;\n index: number;\n}\n\nfunction placementIn(cols: KanbanColumn[], cardId: string): Placement | null {\n const at = locate(cols, cardId);\n return at ? { cardId, toColumnId: at.col.id, beforeId: at.col.cards[at.index + 1]?.id ?? null, index: at.index } : null;\n}\n\n/** The columns with a card put at a placement: into its column, before the same card (else at its index). */\nfunction place(cols: KanbanColumn[], p: Placement): KanbanColumn[] {\n const from = locate(cols, p.cardId);\n if (!from || !cols.some((col) => col.id === p.toColumnId)) return cols;\n const card = from.col.cards[from.index];\n return cols.map((col) => {\n if (col.id !== p.toColumnId && col.id !== from.col.id) return col;\n const cards = col.cards.filter((c) => c.id !== p.cardId);\n if (col.id === p.toColumnId) {\n const i = p.beforeId == null ? cards.length : cards.findIndex((c) => c.id === p.beforeId);\n cards.splice(i >= 0 ? i : Math.min(p.index, cards.length), 0, card);\n }\n return { ...col, cards };\n });\n}\n\n/**\n * A drag's move so far, re-applied to new columns: the card goes into the same column, before the same card, so a\n * change that arrives while a card is held shows at once and the drop keeps it.\n */\nfunction rebase(next: KanbanColumn[], shown: KanbanColumn[], cardId: string): KanbanColumn[] {\n const p = placementIn(shown, cardId);\n return p ? place(next, p) : next;\n}\n\n/**\n * A dropped card the board holds where it was dropped (Sprint 25.7, the owner's call of 26 September 2026: a card must\n * not jump back while its move saves). The pattern Atlassian's drag-and-drop guidance sets for Trello and Jira: the move\n * shows at once, before it is saved; data that arrives meanwhile has it replayed over it; a failed save lets the data\n * win, so the card goes back. One hold per card: a later move of the card replaces its place and adds its save to the\n * ones in flight.\n */\ninterface HeldMove extends Placement {\n /** The latest drop's key: only its save's failure puts the card back. */\n key: number;\n /** Saves for this card not yet settled, a replaced move's included: while one is out, nothing the columns show ends the hold. */\n inFlight: number;\n /** Where `columns` had the card before the first drop of this run of moves. */\n origin: Placement;\n /** The places of the moves this one replaced: columns showing one of them are behind, not a decision. */\n earlier: Placement[];\n /** The cards and their order in `columns` at the drop, or when its last save settled: with the card at its origin, a change ends the hold. */\n layout: string;\n}\n\n/** Which cards each column holds, in order: what a hold compares. A card's own fields changing is not a move. */\nconst layoutOf = (cols: KanbanColumn[]) => cols.map((col) => `${col.id}:${col.cards.map((c) => c.id).join(\",\")}`).join(\"|\");\n\n/** The columns show the card at a placement: in its column, before the same card (null: last). */\nfunction isAt(cols: KanbanColumn[], p: Placement): boolean {\n const at = locate(cols, p.cardId);\n return at != null && at.col.id === p.toColumnId && (at.col.cards[at.index + 1]?.id ?? null) === p.beforeId;\n}\n\n/**\n * The holds new columns keep. While a save for the card is out, every one (stale data, a card a filter hides, a card\n * that happens to sit where it was dropped). Once none is: a hold ends when its card or column is gone; with the card\n * at its origin, when the columns change some other way (the app's data has moved on — or, for a card moved back, has\n * caught up); when the columns show the card where it was dropped; or somewhere that is none of its own places (the\n * app's data decided). Columns showing one of its earlier places are behind, and the hold stays.\n */\nfunction keptHolds(holds: HeldMove[], cols: KanbanColumn[]): HeldMove[] {\n const layout = layoutOf(cols);\n const kept = holds.filter((h) => {\n if (h.inFlight > 0) return true;\n if (!locate(cols, h.cardId) || !cols.some((col) => col.id === h.toColumnId)) return false;\n // first, as a card moved back sits at its origin in data that has not caught up yet as well as in data that has\n if (isAt(cols, h.origin)) return layout === h.layout;\n if (isAt(cols, h)) return false;\n return h.earlier.some((p) => isAt(cols, p));\n });\n return kept.length === holds.length ? holds : kept;\n}\n\nconst replayHolds = (cols: KanbanColumn[], holds: HeldMove[]) => holds.reduce(place, cols);\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n value != null && typeof (value as { then?: unknown }).then === \"function\";\n\n/** A card's place, for the live region: \"To do, position 2 of 3\". */\nconst placeWords = (at: { col: KanbanColumn; index: number }) => `${at.col.title}, position ${at.index + 1} of ${at.col.cards.length}`;\n\n/** Read to a screen reader on every card that can be dragged (dnd-kit's aria-describedby). */\nconst MOVE_INSTRUCTIONS = \"Press Space to pick up the card. Use the Left and Right arrow keys to move it between columns, \"\n + \"and the Up and Down arrow keys to move it within a column. Press Space again to drop it, or Escape to cancel.\";\n\nfunction BoardColumn({\n col,\n readOnly,\n children,\n}: {\n col: KanbanColumn;\n readOnly?: boolean;\n children: ReactNode;\n}) {\n // Sprint 25.7, C2: the column is a drop area as well as a sortable list, so an empty column takes a card\n const { setNodeRef, isOver } = useDroppable({ id: columnDropId(col.id), disabled: readOnly });\n return (\n <div\n className={cn(\n \"flex flex-col shrink-0 w-[calc(100vw-4rem)] sm:w-[280px] snap-center\",\n \"bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-lg)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n )}\n >\n <div className=\"flex items-center justify-between px-ds-3 py-ds-2.5\">\n <div className=\"flex items-center gap-ds-2\">\n {col.color && (\n <span\n className=\"w-2.5 h-2.5 rounded-[var(--ds-radius-full)] shrink-0\"\n style={{ backgroundColor: col.color }}\n />\n )}\n <span className=\"text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]\">\n {col.title}\n </span>\n <span className=\"text-xs text-[var(--ds-text-muted)] tabular-nums\">\n {col.cards.length}\n </span>\n </div>\n <Icon name=\"plus\" size={14} className=\"text-[var(--ds-text-muted)]\" />\n </div>\n\n <SortableContext\n items={col.cards.map((c) => c.id)}\n strategy={verticalListSortingStrategy}\n id={col.id}\n >\n <div\n ref={setNodeRef}\n data-kanban-column={col.id}\n className={cn(\n \"flex flex-col gap-ds-2.5 px-ds-3 pb-ds-3 min-h-[60px] rounded-b-[var(--ds-radius-lg)] transition-colors\",\n isOver && col.cards.length === 0 && \"bg-[var(--ds-bg-surface)]/60\",\n )}\n >\n {children}\n </div>\n </SortableContext>\n </div>\n );\n}\n\nexport function KanbanBoard({\n columns: columnsProp,\n onColumnsChange,\n onCardMove,\n readOnly,\n renderCard,\n onCardClick,\n className,\n}: KanbanBoardProps) {\n // Sprint 25.7, C2: `columns` is authoritative — a new array replaces what the board shows. A move is previewed while\n // the card is dragged and held once dropped (HeldMove) until `columns` shows it; a cancelled drag (Escape) leaves the\n // board as it was.\n const [seenProp, setSeenProp] = useState(columnsProp);\n const [holds, setHoldsState] = useState<HeldMove[]>([]);\n // the preview lives in a ref as well, so a drop in the same tick as the last hover still reads it\n const [preview, setPreviewState] = useState<KanbanColumn[] | null>(null);\n const previewRef = useRef<KanbanColumn[] | null>(null);\n const setPreview = (next: KanbanColumn[] | null) => { previewRef.current = next; setPreviewState(next); };\n const [activeCard, setActiveCard] = useState<KanbanCard | null>(null);\n // the holds, the columns and the card being dragged, for a save that settles after this render\n const holdsRef = useRef<HeldMove[]>([]);\n const columnsRef = useRef(columnsProp);\n const activeRef = useRef<string | null>(null);\n const setHolds = (next: HeldMove[]) => { holdsRef.current = next; setHoldsState(next); };\n if (columnsProp !== seenProp) {\n setSeenProp(columnsProp);\n // new columns (a save landing, a refetch, another user's move): the holds they settle end, the rest replay over them\n const kept = keptHolds(holds, columnsProp);\n if (kept !== holds) setHoldsState(kept);\n // new columns while a card is held: the preview is rebuilt from them\n if (preview && activeCard) setPreviewState(rebase(replayHolds(columnsProp, kept), preview, activeCard.id));\n }\n // a preview or holds updated while rendering reach their refs before any drag event or settled save reads them\n useLayoutEffect(() => { previewRef.current = preview; }, [preview]);\n useLayoutEffect(() => { holdsRef.current = holds; }, [holds]);\n useLayoutEffect(() => { columnsRef.current = columnsProp; }, [columnsProp]);\n const committed = useMemo(() => replayHolds(columnsProp, holds), [columnsProp, holds]);\n const columns = preview ?? committed;\n\n // The live region speaks card and column titles, never ids. Each handler words its news in `said`, and dnd-kit\n // announces it straight after calling the handler. A move is said when the card's landing place changes.\n const said = useRef<string | undefined>(undefined);\n const lastPlace = useRef<string | null>(null);\n const announcements = useMemo<Announcements>(() => {\n const take = () => { const text = said.current; said.current = undefined; return text; };\n return { onDragStart: take, onDragOver: take, onDragEnd: take, onDragCancel: take };\n }, []);\n const instructions = { draggable: onCardClick ? `Press Enter to open the card. ${MOVE_INSTRUCTIONS}` : MOVE_INSTRUCTIONS };\n // A save that fails after the drag is over has no dnd-kit event to speak in: the board's own status line says it,\n // every failure since the last pick-up together, so two that land at once are both heard.\n const [notice, setNotice] = useState(\"\");\n const noticeParts = useRef<string[]>([]);\n const say = (text: string) => { noticeParts.current = [...noticeParts.current, text]; setNotice(noticeParts.current.join(\" \")); };\n // The card to focus once a failed save has put it back: its element is rendered anew in the column it returns to.\n const rootRef = useRef<HTMLDivElement>(null);\n const refocus = useRef<string | null>(null);\n useLayoutEffect(() => {\n const id = refocus.current;\n if (id == null) return;\n refocus.current = null;\n const cards = rootRef.current?.querySelectorAll<HTMLElement>(\"[data-kanban-card]\") ?? [];\n [...cards].find((el) => el.getAttribute(\"data-kanban-card\") === id)?.focus();\n });\n const holdKey = useRef(0);\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor, {\n coordinateGetter: boardCoordinates,\n // Space picks up and drops; Enter is the card's open key (onCardClick)\n keyboardCodes: { start: [\"Space\"], cancel: [\"Escape\"], end: [\"Space\"] },\n }),\n );\n // read-only: no sensors at all (an absent list would give dnd-kit's defaults)\n const noSensors = useSensors();\n\n const handleDragStart = (event: DragStartEvent) => {\n const at = locate(committed, String(event.active.id));\n if (!at) return;\n noticeParts.current = [];\n setNotice(\"\");\n activeRef.current = at.col.cards[at.index].id;\n setActiveCard(at.col.cards[at.index]);\n setPreview(committed);\n lastPlace.current = placeWords(at);\n said.current = `Picked up ${at.col.cards[at.index].title} in ${lastPlace.current}.`;\n };\n\n // Across columns, the preview moves the card as it is dragged; the parent hears nothing until the drop.\n const handleDragOver = (event: DragOverEvent) => {\n const { active, over } = event;\n const activeId = String(active.id);\n const overId = over ? String(over.id) : null;\n const cols = previewRef.current ?? committed;\n const from = locate(cols, activeId);\n const toColumnId = overId == null || overId === activeId ? null : droppedColumnId(overId) ?? locate(cols, overId)?.col.id;\n if (from && toColumnId && from.col.id !== toColumnId) {\n const card = from.col.cards[from.index];\n setPreview(cols.map((col) => {\n if (col.id === from.col.id) return { ...col, cards: col.cards.filter((c) => c.id !== activeId) };\n if (col.id !== toColumnId) return col;\n const overIndex = col.cards.findIndex((c) => c.id === overId);\n const cards = [...col.cards];\n cards.splice(overIndex >= 0 ? overIndex : cards.length, 0, card);\n return { ...col, cards };\n }));\n }\n // where a drop now would put the card\n const now = overId == null ? null : locate(landing(previewRef.current ?? committed, activeId, overId), activeId);\n const where = now ? placeWords(now) : null;\n if (where === lastPlace.current) return;\n lastPlace.current = where;\n const title = now ? now.col.cards[now.index].title : activeCard?.title;\n said.current = now ? `${title} is over ${where}.` : `${title} is not over a column.`;\n };\n\n /** A card's move failed: the hold ends and the card shows where `columns` has it, said on the status line. */\n const putBack = (hold: HeldMove) => {\n const rest = holdsRef.current.filter((h) => h.cardId !== hold.cardId);\n const shown = replayHolds(columnsRef.current, rest);\n const back = locate(shown, hold.cardId);\n const card = back?.col.cards[back.index];\n // focus follows the card only from this board (two boards over the same data each render it)\n const focused = document.activeElement;\n if (focused && rootRef.current?.contains(focused) && focused.closest?.(\"[data-kanban-card]\")?.getAttribute(\"data-kanban-card\") === hold.cardId) {\n refocus.current = hold.cardId;\n }\n setHolds(rest);\n // a card held mid-drag: the preview is rebuilt without the failed move, so the drop cannot carry it\n if (previewRef.current && activeRef.current) setPreview(rebase(shown, previewRef.current, activeRef.current));\n if (!back || !card) return;\n const to = columnsRef.current.find((col) => col.id === hold.toColumnId)?.title;\n say(isAt(columnsRef.current, hold.origin) || !to\n ? `Move not saved. ${card.title} is back in ${placeWords(back)}.`\n : `Move not saved. ${card.title} could not move to ${to}.`);\n };\n\n /** A save for a card has settled. The latest move's failure puts the card back; otherwise, once none is out, the hold is judged against the columns. */\n const settle = (cardId: string, key: number, ok: boolean) => {\n const hold = holdsRef.current.find((h) => h.cardId === cardId);\n if (!hold) return;\n if (!ok && hold.key === key) return putBack(hold);\n const inFlight = hold.inFlight - 1;\n const next = holdsRef.current.map((h) => (h === hold ? { ...h, inFlight, layout: inFlight > 0 ? h.layout : layoutOf(columnsRef.current) } : h));\n // the columns may already show the card elsewhere (a refetch that landed before the save resolved)\n setHolds(inFlight > 0 ? next : keptHolds(next, columnsRef.current));\n };\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n const activeId = String(active.id);\n const cols = previewRef.current ?? committed;\n activeRef.current = null;\n setActiveCard(null);\n setPreview(null);\n const final = over ? landing(cols, activeId, String(over.id)) : committed;\n const before = locate(committed, activeId);\n const after = locate(final, activeId);\n if (!before || !after || (before.col.id === after.col.id && before.index === after.index)) {\n if (before) said.current = `Dropped ${before.col.cards[before.index].title} back in ${placeWords(before)}.`;\n return;\n }\n said.current = `Dropped ${after.col.cards[after.index].title} in ${placeWords(after)}.`;\n const key = ++holdKey.current;\n // a new move of a card replaces its hold's place; the replaced move's save stays in flight\n const prev = holdsRef.current.find((h) => h.cardId === activeId);\n const hold: HeldMove = {\n ...placementIn(final, activeId)!,\n key,\n inFlight: prev?.inFlight ?? 0,\n origin: prev?.origin ?? placementIn(columnsProp, activeId) ?? placementIn(committed, activeId)!,\n earlier: prev ? [...prev.earlier, { cardId: prev.cardId, toColumnId: prev.toColumnId, beforeId: prev.beforeId, index: prev.index }] : [],\n layout: layoutOf(columnsProp),\n };\n setHolds([...holdsRef.current.filter((h) => h.cardId !== activeId), hold]);\n const saves: PromiseLike<unknown>[] = [];\n try {\n for (const result of [onColumnsChange?.(final), onCardMove?.(after.col.cards[after.index], after.col.id, after.index)]) {\n if (isThenable(result)) saves.push(result);\n }\n } catch (error) {\n // a handler that throws refuses the move, as a rejected save does; the error still reaches the console\n console.error(\"KanbanBoard: a move handler threw, so the move is refused.\", error);\n return putBack(hold);\n }\n if (!saves.length) return;\n setHolds(holdsRef.current.map((h) => (h.key === key ? { ...h, inFlight: h.inFlight + 1 } : h)));\n // every save settles before the move counts as saved; any failure refuses it\n Promise.allSettled(saves).then((results) => settle(activeId, key, results.every((r) => r.status === \"fulfilled\")));\n };\n\n const handleDragCancel = (event: DragCancelEvent) => {\n activeRef.current = null;\n setActiveCard(null);\n setPreview(null);\n const at = locate(committed, String(event.active.id));\n if (at) said.current = `Move cancelled. ${at.col.cards[at.index].title} is back in ${placeWords(at)}.`;\n };\n\n return (\n <DndContext\n sensors={readOnly ? noSensors : sensors}\n accessibility={{ announcements, screenReaderInstructions: instructions }}\n collisionDetection={closestCorners}\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div ref={rootRef} className={cn(\"flex gap-ds-4 overflow-x-auto pb-ds-2 snap-x snap-mandatory sm:snap-none\", className)}>\n {columns.map((col) => (\n <BoardColumn key={col.id} col={col} readOnly={readOnly}>\n {col.cards.map((card) => (\n <SortableCard\n key={card.id}\n card={card}\n renderCard={renderCard}\n onCardClick={onCardClick}\n readOnly={readOnly}\n />\n ))}\n </BoardColumn>\n ))}\n {/* inside the board's root, so a board in a spaced stack stays one element */}\n <div role=\"status\" className=\"sr-only\">{notice}</div>\n </div>\n\n <DragOverlay>\n {activeCard && <OverlayCard card={activeCard} renderCard={renderCard} />}\n </DragOverlay>\n </DndContext>\n );\n}\n"]}
package/dist/resizable.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useCompact, useAppShell, Button, Drawer } from './chunk-J43HCK7Q.js';
2
- import { isTabStop, tabStops, rememberFocus } from './chunk-YQABRUWD.js';
1
+ import { useCompact, useAppShell, Button, Drawer } from './chunk-CIVSNXFG.js';
2
+ import { isTabStop, tabStops, rememberFocus } from './chunk-JXANCXK6.js';
3
3
  import { SplitPaneCtx } from './chunk-TKVTZWBN.js';
4
4
  import { useBreakpoint } from './chunk-CRQ6KCRD.js';
5
5
  import './chunk-EMCSARQO.js';