gridsmith-ui 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/dist/charts.d.ts +2 -2
  3. package/dist/charts.js +2 -2
  4. package/dist/charts.js.map +1 -1
  5. package/dist/chat.js +5 -5
  6. package/dist/chat.js.map +1 -1
  7. package/dist/{chunk-64TXZ4L7.js → chunk-2ROVDVLZ.js} +3 -3
  8. package/dist/{chunk-64TXZ4L7.js.map → chunk-2ROVDVLZ.js.map} +1 -1
  9. package/dist/chunk-66JJCQU3.js +46 -0
  10. package/dist/chunk-66JJCQU3.js.map +1 -0
  11. package/dist/chunk-7RKC5C7G.js +35 -0
  12. package/dist/chunk-7RKC5C7G.js.map +1 -0
  13. package/dist/{chunk-OTCEJN64.js → chunk-BRAXJEEP.js} +7 -6
  14. package/dist/chunk-BRAXJEEP.js.map +1 -0
  15. package/dist/{chunk-IXR4UEF4.js → chunk-DQCHCLEJ.js} +9 -8
  16. package/dist/chunk-DQCHCLEJ.js.map +1 -0
  17. package/dist/{chunk-5TUVX4B2.js → chunk-EIWXVICU.js} +3 -3
  18. package/dist/{chunk-5TUVX4B2.js.map → chunk-EIWXVICU.js.map} +1 -1
  19. package/dist/{chunk-SFY2KMHG.js → chunk-FD4YED7O.js} +5 -4
  20. package/dist/chunk-FD4YED7O.js.map +1 -0
  21. package/dist/{chunk-MD2EFMES.js → chunk-GXMTS3YD.js} +3 -3
  22. package/dist/{chunk-MD2EFMES.js.map → chunk-GXMTS3YD.js.map} +1 -1
  23. package/dist/chunk-HTXP5BMM.js +38 -0
  24. package/dist/chunk-HTXP5BMM.js.map +1 -0
  25. package/dist/{chunk-R5NFCEZ3.js → chunk-OPFJEL3Z.js} +52 -24
  26. package/dist/chunk-OPFJEL3Z.js.map +1 -0
  27. package/dist/{chunk-EKBWWA6P.js → chunk-R4GXKH4U.js} +7 -7
  28. package/dist/chunk-R4GXKH4U.js.map +1 -0
  29. package/dist/{chunk-PO65I3MQ.js → chunk-TRDXWHD2.js} +6 -6
  30. package/dist/chunk-TRDXWHD2.js.map +1 -0
  31. package/dist/{chunk-2CKSSWYG.js → chunk-YUTF2X6B.js} +39 -20
  32. package/dist/chunk-YUTF2X6B.js.map +1 -0
  33. package/dist/data-grid.js +18 -11
  34. package/dist/data-grid.js.map +1 -1
  35. package/dist/editor-mentions.js +2 -2
  36. package/dist/editor.js +44 -24
  37. package/dist/editor.js.map +1 -1
  38. package/dist/index.d.ts +46 -24
  39. package/dist/index.js +404 -295
  40. package/dist/index.js.map +1 -1
  41. package/dist/kanban.js +18 -11
  42. package/dist/kanban.js.map +1 -1
  43. package/dist/resizable.js +20 -15
  44. package/dist/resizable.js.map +1 -1
  45. package/dist/roots.css +91 -15
  46. package/dist/tokens.css +111 -26
  47. package/dist/utils.d.ts +47 -5
  48. package/dist/utils.js +15 -5
  49. package/dist/utils.js.map +1 -1
  50. package/dist/virtualized.js +17 -11
  51. package/dist/virtualized.js.map +1 -1
  52. package/dist-static/tokens.css +111 -26
  53. package/package.json +2 -2
  54. package/dist/chunk-2CKSSWYG.js.map +0 -1
  55. package/dist/chunk-EKBWWA6P.js.map +0 -1
  56. package/dist/chunk-IXR4UEF4.js.map +0 -1
  57. package/dist/chunk-M2QRNBDQ.js +0 -42
  58. package/dist/chunk-M2QRNBDQ.js.map +0 -1
  59. package/dist/chunk-OTCEJN64.js.map +0 -1
  60. package/dist/chunk-PO65I3MQ.js.map +0 -1
  61. package/dist/chunk-R5NFCEZ3.js.map +0 -1
  62. package/dist/chunk-SFY2KMHG.js.map +0 -1
package/dist/kanban.js CHANGED
@@ -1,15 +1,16 @@
1
1
  "use client";
2
- import { useFillHeight, FillContext, useKeyboardScroll, FOCUS_INSET } from './chunk-SFY2KMHG.js';
3
- import { Badge } from './chunk-PO65I3MQ.js';
2
+ import { useFillHeight, FillContext, useKeyboardScroll } from './chunk-FD4YED7O.js';
3
+ import { Badge } from './chunk-TRDXWHD2.js';
4
4
  import { useFramesItself } from './chunk-QM4SJKWV.js';
5
+ import { useRevealFocus } from './chunk-HTXP5BMM.js';
5
6
  import './chunk-ZI3OQDET.js';
6
- import { useTextOverflow, Tooltip } from './chunk-MD2EFMES.js';
7
+ import { useTextOverflow, Tooltip } from './chunk-GXMTS3YD.js';
7
8
  import './chunk-7Q46JXSK.js';
8
9
  import { Icon } from './chunk-TJEV7YAY.js';
9
10
  import './chunk-HP7WUZPK.js';
10
11
  import './chunk-WH2FUFAC.js';
11
- import { cn } from './chunk-M2QRNBDQ.js';
12
- import { useState, useRef, useLayoutEffect, useMemo, useContext, useCallback } from 'react';
12
+ import { cn, FOCUS_INSET, FOCUS_RING } from './chunk-66JJCQU3.js';
13
+ import { useState, useRef, useLayoutEffect, useMemo, useCallback, useContext } from 'react';
13
14
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCorners, DragOverlay, useDroppable } from '@dnd-kit/core';
14
15
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable';
15
16
  import { CSS } from '@dnd-kit/utilities';
@@ -48,7 +49,7 @@ function SortableCard({
48
49
  "rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
49
50
  "bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-button)]",
50
51
  open ? "cursor-pointer" : "cursor-default",
51
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
52
+ FOCUS_RING
52
53
  ),
53
54
  children: renderCard ? renderCard(card) : /* @__PURE__ */ jsx(DefaultCard, { card })
54
55
  }
@@ -81,7 +82,7 @@ function SortableCard({
81
82
  onCardClick ? "cursor-pointer" : "cursor-grab",
82
83
  "shadow-[var(--ds-shadow-button)]",
83
84
  "transition-shadow duration-[var(--ds-transition-speed)]",
84
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
85
+ FOCUS_RING,
85
86
  // no scale on the card left behind: the drag overlay takes its size from it, and 2% wider made the cards
86
87
  // below count as "to the right" of it, so a keyboard drag's arrow keys landed on them (Sprint 25.7, C2)
87
88
  isDragging && "opacity-30"
@@ -244,9 +245,9 @@ function BoardColumn({
244
245
  "data-kanban-column": col.id,
245
246
  className: cn(
246
247
  "flex flex-col gap-ds-2.5 px-ds-3 pb-ds-3 min-h-[60px] rounded-b-[var(--ds-radius-lg)] transition-colors",
247
- // scrolling, it keeps room above the first card for its focus ring (drawn where the header's padding was) and
248
- // brings a card focused by keyboard in with room around it
249
- filled && cn("flex-1 min-h-0 overflow-y-auto -mt-ds-1 pt-ds-1 scroll-py-ds-2", FOCUS_INSET),
248
+ // scrolling, it keeps room above the first card for its focus outline (drawn where the header's padding was) and
249
+ // brings a card focused by keyboard in with room around it: the outline's reach, 4 px, never a theme space (P3)
250
+ filled && cn("flex-1 min-h-0 overflow-y-auto -mt-[4px] pt-[4px] scroll-py-ds-2", FOCUS_INSET),
250
251
  isOver && col.cards.length === 0 && "bg-[var(--ds-bg-surface)]/60"
251
252
  ),
252
253
  children
@@ -319,6 +320,12 @@ function KanbanBoard({
319
320
  setNotice(noticeParts.current.join(" "));
320
321
  };
321
322
  const rootRef = useRef(null);
323
+ const [board, setBoard] = useState(null);
324
+ const setRoot = useCallback((node) => {
325
+ rootRef.current = node;
326
+ setBoard(node);
327
+ }, []);
328
+ useRevealFocus(board);
322
329
  const fill = useFillHeight(rootRef);
323
330
  const inPane = useContext(FillContext);
324
331
  const filled = fill != null || inPane;
@@ -456,7 +463,7 @@ function KanbanBoard({
456
463
  onDragEnd: handleDragEnd,
457
464
  onDragCancel: handleDragCancel,
458
465
  children: [
459
- /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-fill": fill != null ? "" : void 0, className: cn("relative flex gap-ds-4 overflow-x-auto pb-ds-2 snap-x snap-mandatory sm:snap-none", inPane && "h-full min-h-0", className), style: fill != null ? { height: fill } : void 0, children: [
466
+ /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-fill": fill != null ? "" : void 0, className: cn("relative flex gap-ds-4 overflow-x-auto pb-ds-2 scroll-px-[4px] snap-x snap-mandatory sm:snap-none", inPane && "h-full min-h-0", className), style: fill != null ? { height: fill } : void 0, children: [
460
467
  columns.map((col) => /* @__PURE__ */ jsx(BoardColumn, { col, readOnly, filled, children: col.cards.map((card) => /* @__PURE__ */ jsx(
461
468
  SortableCard,
462
469
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../showcase/src/components/KanbanBoard.tsx"],"names":[],"mappings":"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","file":"kanban.js","sourcesContent":["import { useCallback, useContext, 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\";\nimport { useFramesItself } from \"../lib/surface-context\";\nimport { FillContext, useFillHeight } from \"../lib/use-fill-height\";\nimport { FOCUS_INSET, useKeyboardScroll } from \"../lib/use-keyboard-scroll\";\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 filled,\n children,\n}: {\n col: KanbanColumn;\n readOnly?: boolean;\n /** The board has a height of its own (the page's, or its pane's): the column takes it and scrolls its cards. */\n filled?: 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 // Sprint 25.16 A42: a column that scrolls its cards, with no card to Tab to (a read-only board), is a Tab stop itself\n const [cards, setCardsEl] = useState<HTMLDivElement | null>(null);\n const setCards = useCallback((node: HTMLDivElement | null) => { setNodeRef(node); setCardsEl(node); }, [setNodeRef]);\n useKeyboardScroll(cards, col.title, !!filled);\n return (\n <div\n className={cn(\n \"flex flex-col shrink-0 w-[calc(100vw-4rem)] sm:w-[280px] snap-center\",\n filled && \"min-h-0\",\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={setCards}\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 // scrolling, it keeps room above the first card for its focus ring (drawn where the header's padding was) and\n // brings a card focused by keyboard in with room around it\n filled && cn(\"flex-1 min-h-0 overflow-y-auto -mt-ds-1 pt-ds-1 scroll-py-ds-2\", FOCUS_INSET),\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.14 A44: a board frames itself, so a SplitView's list pane draws nothing behind it\n useFramesItself();\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 // Sprint 25.16 A42 (P31): a board that is its page's last content takes the height under the header, and one in a\n // SplitView's pane takes the pane's; each column then scrolls its own cards, and the page or the pane doesn't\n const fill = useFillHeight(rootRef);\n const inPane = useContext(FillContext);\n const filled = fill != null || inPane;\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} data-fill={fill != null ? \"\" : undefined} className={cn(\"relative flex gap-ds-4 overflow-x-auto pb-ds-2 snap-x snap-mandatory sm:snap-none\", inPane && \"h-full min-h-0\", className)} style={fill != null ? { height: fill } : undefined}>\n {columns.map((col) => (\n <BoardColumn key={col.id} col={col} readOnly={readOnly} filled={filled}>\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"]}
1
+ {"version":3,"sources":["../../../showcase/src/components/KanbanBoard.tsx"],"names":[],"mappings":"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","file":"kanban.js","sourcesContent":["import { useCallback, useContext, 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, FOCUS_INSET, FOCUS_RING } from \"../lib/utils\";\nimport { Icon } from \"../lib/icons\";\nimport { Tooltip } from \"./Tooltip\";\nimport { useTextOverflow } from \"../lib/use-text-overflow\";\nimport { Badge } from \"./Badge\";\nimport { useFramesItself } from \"../lib/surface-context\";\nimport { FillContext, useFillHeight } from \"../lib/use-fill-height\";\nimport { useKeyboardScroll } from \"../lib/use-keyboard-scroll\";\nimport { useRevealFocus } from \"../lib/reveal-focus\";\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_RING,\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_RING,\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 filled,\n children,\n}: {\n col: KanbanColumn;\n readOnly?: boolean;\n /** The board has a height of its own (the page's, or its pane's): the column takes it and scrolls its cards. */\n filled?: 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 // Sprint 25.16 A42: a column that scrolls its cards, with no card to Tab to (a read-only board), is a Tab stop itself\n const [cards, setCardsEl] = useState<HTMLDivElement | null>(null);\n const setCards = useCallback((node: HTMLDivElement | null) => { setNodeRef(node); setCardsEl(node); }, [setNodeRef]);\n useKeyboardScroll(cards, col.title, !!filled);\n return (\n <div\n className={cn(\n \"flex flex-col shrink-0 w-[calc(100vw-4rem)] sm:w-[280px] snap-center\",\n filled && \"min-h-0\",\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={setCards}\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 // scrolling, it keeps room above the first card for its focus outline (drawn where the header's padding was) and\n // brings a card focused by keyboard in with room around it: the outline's reach, 4 px, never a theme space (P3)\n filled && cn(\"flex-1 min-h-0 overflow-y-auto -mt-[4px] pt-[4px] scroll-py-ds-2\", FOCUS_INSET),\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.14 A44: a board frames itself, so a SplitView's list pane draws nothing behind it\n useFramesItself();\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 // the board scrolls sideways: a card the keyboard reaches at its edge comes in whole, with its outline (Sprint 30, P3)\n const [board, setBoard] = useState<HTMLDivElement | null>(null);\n const setRoot = useCallback((node: HTMLDivElement | null) => { rootRef.current = node; setBoard(node); }, []);\n useRevealFocus(board);\n // Sprint 25.16 A42 (P31): a board that is its page's last content takes the height under the header, and one in a\n // SplitView's pane takes the pane's; each column then scrolls its own cards, and the page or the pane doesn't\n const fill = useFillHeight(rootRef);\n const inPane = useContext(FillContext);\n const filled = fill != null || inPane;\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={setRoot} data-fill={fill != null ? \"\" : undefined} className={cn(\"relative flex gap-ds-4 overflow-x-auto pb-ds-2 scroll-px-[4px] snap-x snap-mandatory sm:snap-none\", inPane && \"h-full min-h-0\", className)} style={fill != null ? { height: fill } : undefined}>\n {columns.map((col) => (\n <BoardColumn key={col.id} col={col} readOnly={readOnly} filled={filled}>\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,18 +1,19 @@
1
1
  "use client";
2
- import { useCompact, useElementWidth, useBelow, crumbGoes, useAppShell, Breadcrumbs, Drawer } from './chunk-R5NFCEZ3.js';
3
- import { useFillHeight, useKeyboardScroll, FillContext, FOCUS_INSET } from './chunk-SFY2KMHG.js';
4
- import { Button } from './chunk-EKBWWA6P.js';
2
+ import { useCompact, useElementWidth, useBelow, crumbGoes, useAppShell, Breadcrumbs, Drawer } from './chunk-OPFJEL3Z.js';
3
+ import { useFillHeight, useKeyboardScroll, FillContext } from './chunk-FD4YED7O.js';
4
+ import { Button } from './chunk-R4GXKH4U.js';
5
5
  import './chunk-OYNUU346.js';
6
6
  import { useBreakpoint } from './chunk-CRQ6KCRD.js';
7
7
  import './chunk-XVB55M3N.js';
8
8
  import { SurfaceBoundary } from './chunk-QM4SJKWV.js';
9
+ import './chunk-HTXP5BMM.js';
9
10
  import { isTabStop, tabStops, rememberFocus } from './chunk-ZI3OQDET.js';
10
- import './chunk-MD2EFMES.js';
11
+ import './chunk-GXMTS3YD.js';
11
12
  import './chunk-7Q46JXSK.js';
12
13
  import { Icon } from './chunk-TJEV7YAY.js';
13
14
  import './chunk-HP7WUZPK.js';
14
15
  import './chunk-WH2FUFAC.js';
15
- import { cn } from './chunk-M2QRNBDQ.js';
16
+ import { cn, GROUP_FOCUS_RING, FOCUS_INSET } from './chunk-66JJCQU3.js';
16
17
  import { Group, Panel, Separator } from 'react-resizable-panels';
17
18
  import { createContext, useContext, useRef, useState, useCallback, useLayoutEffect } from 'react';
18
19
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -96,12 +97,14 @@ function ResizableHandle({
96
97
  const isMobile = useContext(MobileCtx);
97
98
  const isVertical = direction === "vertical";
98
99
  const gripRef = useRef(null);
99
- useGripInView(gripRef, !!withHandle && !isVertical);
100
+ useGripInView(gripRef, !isVertical);
100
101
  return /* @__PURE__ */ jsx(
101
102
  Separator,
102
103
  {
103
104
  className: cn(
104
- "relative flex items-center justify-center bg-[var(--ds-border-default)]",
105
+ // a group: its keyboard focus is drawn round its grip (Sprint 30), as a full-length line's outline would be cut off
106
+ // at both its ends by the panels it divides; the line itself shows no outline of its own
107
+ "group relative flex items-center justify-center bg-[var(--ds-border-default)] outline-none",
105
108
  isVertical ? cn(
106
109
  "h-[var(--ds-border-width)] w-full after:absolute after:left-0 after:right-0 after:content-['']",
107
110
  isMobile ? "after:-top-3 after:-bottom-3" : "after:-top-1 after:-bottom-1"
@@ -109,18 +112,20 @@ function ResizableHandle({
109
112
  "w-[var(--ds-border-width)] after:absolute after:inset-y-0 after:content-['']",
110
113
  isMobile ? "after:-left-3 after:-right-3" : "after:-left-1 after:-right-1"
111
114
  ),
112
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
113
115
  "[&[data-resize-handle-state=drag]]:bg-[var(--ds-accent-primary)]",
114
116
  "[&[data-resize-handle-state=hover]]:bg-[var(--ds-accent-primary)]",
115
117
  className
116
118
  ),
117
119
  ...props,
118
- children: withHandle && /* @__PURE__ */ jsx(
120
+ children: /* @__PURE__ */ jsx(
119
121
  "div",
120
122
  {
121
123
  ref: gripRef,
124
+ "aria-hidden": "true",
122
125
  className: cn(
123
- "z-10 flex items-center justify-center rounded-[var(--ds-radius-sm)]",
126
+ "z-10 items-center justify-center rounded-[var(--ds-radius-sm)]",
127
+ withHandle ? "flex" : "hidden group-focus-visible:flex",
128
+ GROUP_FOCUS_RING,
124
129
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
125
130
  "bg-[var(--ds-bg-surface)]",
126
131
  isVertical ? isMobile ? "h-4 w-8" : "h-3 w-4" : isMobile ? "h-8 w-4" : "h-4 w-3"
@@ -258,9 +263,9 @@ function SplitView({
258
263
  path ? /* @__PURE__ */ jsx(Breadcrumbs, { items: path, maxItems: 3, "aria-label": "Path", className: "min-w-0" }) : /* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "arrow-left", size: 14 }), onClick: onDetailClose, children: backLabel }),
259
264
  phoneAside && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "info", size: 14 }), onClick: () => setAsideWantsOpen(true), "aria-expanded": asideWantsOpen, children: asideTitle })
260
265
  ] }),
261
- /* @__PURE__ */ jsx("div", { ref: setStackedDetailScroll, className: cn("flex-1 min-h-0 overflow-y-auto", FOCUS_INSET), children: detailContent })
266
+ /* @__PURE__ */ jsx("div", { ref: setStackedDetailScroll, className: cn("flex-1 min-h-0 overflow-y-auto scroll-p-[4px]", FOCUS_INSET), children: detailContent })
262
267
  ] }),
263
- /* @__PURE__ */ jsx("div", { ref: setStackedListScroll, className: cn("flex-1 min-h-0 overflow-y-auto", FOCUS_INSET), hidden: showDetail2 || void 0, children: listContent }),
268
+ /* @__PURE__ */ jsx("div", { ref: setStackedListScroll, className: cn("flex-1 min-h-0 overflow-y-auto scroll-p-[4px]", FOCUS_INSET), hidden: showDetail2 || void 0, children: listContent }),
264
269
  phoneAside && /* @__PURE__ */ jsx(Drawer, { open: asideWantsOpen, onClose: () => setAsideWantsOpen(false), title: asideTitle, position: "right", children: aside })
265
270
  ] }) });
266
271
  }
@@ -286,19 +291,19 @@ function SplitView({
286
291
  if (hiding && layout.detail != null && layout.list != null) setListPct(layout.list);
287
292
  },
288
293
  children: [
289
- /* @__PURE__ */ jsx(ResizablePanel, { id: "list", defaultSize: `${hiding ? detailOpen ? listPct : 100 : listWidth}%`, minSize: `${listMinWidth}%`, children: /* @__PURE__ */ jsx("div", { ref: setListScroll, "data-pane": "list", className: cn("h-full min-h-0 overflow-y-auto", FOCUS_INSET), onFocus: hiding ? (e) => {
294
+ /* @__PURE__ */ jsx(ResizablePanel, { id: "list", defaultSize: `${hiding ? detailOpen ? listPct : 100 : listWidth}%`, minSize: `${listMinWidth}%`, children: /* @__PURE__ */ jsx("div", { ref: setListScroll, "data-pane": "list", className: cn("h-full min-h-0 overflow-y-auto scroll-p-[4px]", FOCUS_INSET), onFocus: hiding ? (e) => {
290
295
  lastInList.current = e.target;
291
296
  } : void 0, children: listContent }) }),
292
297
  showDetail && /* @__PURE__ */ jsxs(Fragment, { children: [
293
298
  /* @__PURE__ */ jsx(ResizableHandle, { withHandle: true }),
294
299
  /* @__PURE__ */ jsx(ResizablePanel, { id: "detail", minSize: "30%", children: /* @__PURE__ */ jsxs("div", { ref: paneRef, "data-pane": "detail", className: cn("h-full min-h-0 flex flex-col", itemSurface), onKeyDown: hiding ? onPaneKeyDown : void 0, children: [
295
300
  paneBar,
296
- /* @__PURE__ */ jsx("div", { ref: setDetailScroll, className: cn("flex-1 min-h-0 overflow-y-auto", FOCUS_INSET), children: detailContent })
301
+ /* @__PURE__ */ jsx("div", { ref: setDetailScroll, className: cn("flex-1 min-h-0 overflow-y-auto scroll-p-[4px]", FOCUS_INSET), children: detailContent })
297
302
  ] }) })
298
303
  ] }),
299
304
  showAside && !asideCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
300
305
  /* @__PURE__ */ jsx(ResizableHandle, { withHandle: true }),
301
- /* @__PURE__ */ jsx(ResizablePanel, { id: "aside", defaultSize: `${asideWidth}%`, minSize: "15%", children: /* @__PURE__ */ jsx("div", { ref: setAsideScroll, "data-pane": "aside", className: cn("h-full min-h-0 overflow-y-auto py-ds-5 px-[var(--ds-page-gutter)]", itemSurface, FOCUS_INSET), children: /* @__PURE__ */ jsx(SurfaceBoundary, { pane: "aside", children: aside }) }) })
306
+ /* @__PURE__ */ jsx(ResizablePanel, { id: "aside", defaultSize: `${asideWidth}%`, minSize: "15%", children: /* @__PURE__ */ jsx("div", { ref: setAsideScroll, "data-pane": "aside", className: cn("h-full min-h-0 overflow-y-auto py-ds-5 px-[var(--ds-page-gutter)] scroll-p-[4px]", itemSurface, FOCUS_INSET), children: /* @__PURE__ */ jsx(SurfaceBoundary, { pane: "aside", children: aside }) }) })
302
307
  ] })
303
308
  ]
304
309
  }