gridsmith-ui 0.15.0 → 0.17.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 (51) hide show
  1. package/dist/charts.d.ts +20 -1
  2. package/dist/charts.js +64 -28
  3. package/dist/charts.js.map +1 -1
  4. package/dist/chat.js +3 -3
  5. package/dist/{chunk-X4CNZFWC.js → chunk-4LDYR7QP.js} +7 -7
  6. package/dist/chunk-4LDYR7QP.js.map +1 -0
  7. package/dist/{chunk-ATONRIEB.js → chunk-J43HCK7Q.js} +36 -13
  8. package/dist/chunk-J43HCK7Q.js.map +1 -0
  9. package/dist/{chunk-XCSTU6IJ.js → chunk-PVTGMCVR.js} +3 -3
  10. package/dist/{chunk-XCSTU6IJ.js.map → chunk-PVTGMCVR.js.map} +1 -1
  11. package/dist/{chunk-LU3YRNES.js → chunk-XGSQOMEY.js} +4 -4
  12. package/dist/{chunk-LU3YRNES.js.map → chunk-XGSQOMEY.js.map} +1 -1
  13. package/dist/{chunk-VFAKME7Q.js → chunk-YQABRUWD.js} +156 -40
  14. package/dist/chunk-YQABRUWD.js.map +1 -0
  15. package/dist/{chunk-M3AARGLI.js → chunk-Z63OSN64.js} +9 -8
  16. package/dist/chunk-Z63OSN64.js.map +1 -0
  17. package/dist/data-grid.js +1 -1
  18. package/dist/editor.js +11 -5
  19. package/dist/editor.js.map +1 -1
  20. package/dist/{feather-B65FI6CY.js → feather-2JFSFLXN.js} +3 -2
  21. package/dist/feather-2JFSFLXN.js.map +1 -0
  22. package/dist/{heroicons-DIR7PXCR.js → heroicons-4AQ4YBVG.js} +3 -2
  23. package/dist/heroicons-4AQ4YBVG.js.map +1 -0
  24. package/dist/icons.d.ts +1 -1
  25. package/dist/icons.js +1 -1
  26. package/dist/index.d.ts +528 -6
  27. package/dist/index.js +2145 -506
  28. package/dist/index.js.map +1 -1
  29. package/dist/kanban.d.ts +11 -1
  30. package/dist/kanban.js +225 -113
  31. package/dist/kanban.js.map +1 -1
  32. package/dist/{lucide-JH2R3TFV.js → lucide-RZC2FEMS.js} +3 -2
  33. package/dist/lucide-RZC2FEMS.js.map +1 -0
  34. package/dist/{phosphor-HCVUMVT5.js → phosphor-CEN25FLM.js} +3 -2
  35. package/dist/phosphor-CEN25FLM.js.map +1 -0
  36. package/dist/resizable.d.ts +11 -1
  37. package/dist/resizable.js +79 -21
  38. package/dist/resizable.js.map +1 -1
  39. package/dist/roots.css +20 -8
  40. package/dist/{tabler-HHIXSM73.js → tabler-YXXC7Y6B.js} +3 -2
  41. package/dist/tabler-YXXC7Y6B.js.map +1 -0
  42. package/package.json +1 -1
  43. package/dist/chunk-ATONRIEB.js.map +0 -1
  44. package/dist/chunk-M3AARGLI.js.map +0 -1
  45. package/dist/chunk-VFAKME7Q.js.map +0 -1
  46. package/dist/chunk-X4CNZFWC.js.map +0 -1
  47. package/dist/feather-B65FI6CY.js.map +0 -1
  48. package/dist/heroicons-DIR7PXCR.js.map +0 -1
  49. package/dist/lucide-JH2R3TFV.js.map +0 -1
  50. package/dist/phosphor-HCVUMVT5.js.map +0 -1
  51. package/dist/tabler-HHIXSM73.js.map +0 -1
package/dist/kanban.d.ts CHANGED
@@ -15,8 +15,18 @@ 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
  columns: KanbanColumn[];
20
+ /** The columns after a move, once on drop (Sprint 25.7, C2: it used to fire on every hover step). */
19
21
  onColumnsChange?: (columns: KanbanColumn[]) => void;
22
+ /**
23
+ * A card was dropped at a new place: its column and its index there, once on drop — a move between columns, into an
24
+ * empty one, or a reorder within one. Save the move here (or in onColumnsChange); a board with neither saves nothing.
25
+ * The index counts the cards the board shows: when `columns` is filtered, it is the place among the cards passed.
26
+ */
27
+ onCardMove?: (card: KanbanCard, toColumnId: string, index: number) => void;
28
+ /** A board to look at, not rearrange: no dragging and no grab cursor; cards still open with onCardClick. */
29
+ readOnly?: boolean;
20
30
  renderCard?: (card: KanbanCard) => ReactNode;
21
31
  /**
22
32
  * Opens a card (a detail drawer, a route). The board's own card surface is the control: click, or Enter with the
@@ -26,6 +36,6 @@ interface KanbanBoardProps {
26
36
  onCardClick?: (card: KanbanCard) => void;
27
37
  className?: string;
28
38
  }
29
- declare function KanbanBoard({ columns: controlledCols, onColumnsChange, renderCard, onCardClick, className, }: KanbanBoardProps): react_jsx_runtime.JSX.Element;
39
+ declare function KanbanBoard({ columns: columnsProp, onColumnsChange, onCardMove, readOnly, renderCard, onCardClick, className, }: KanbanBoardProps): react_jsx_runtime.JSX.Element;
30
40
 
31
41
  export { KanbanBoard };
package/dist/kanban.js CHANGED
@@ -1,19 +1,20 @@
1
- import { Badge } from './chunk-XCSTU6IJ.js';
1
+ import { Badge } from './chunk-PVTGMCVR.js';
2
2
  import { useTextOverflow, Tooltip } from './chunk-EMCSARQO.js';
3
3
  import './chunk-7Q46JXSK.js';
4
4
  import './chunk-WH2FUFAC.js';
5
- import { Icon } from './chunk-X4CNZFWC.js';
5
+ import { Icon } from './chunk-4LDYR7QP.js';
6
6
  import { cn } from './chunk-4KVFH5KC.js';
7
- import { useState } from 'react';
8
- import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCorners, DragOverlay } from '@dnd-kit/core';
9
- import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
7
+ import { useState, useRef, useLayoutEffect, useMemo } from 'react';
8
+ import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCorners, DragOverlay, useDroppable } from '@dnd-kit/core';
9
+ import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable';
10
10
  import { CSS } from '@dnd-kit/utilities';
11
11
  import { jsxs, jsx } from 'react/jsx-runtime';
12
12
 
13
13
  function SortableCard({
14
14
  card,
15
15
  renderCard,
16
- onCardClick
16
+ onCardClick,
17
+ readOnly
17
18
  }) {
18
19
  const {
19
20
  attributes,
@@ -22,7 +23,32 @@ function SortableCard({
22
23
  transform,
23
24
  transition,
24
25
  isDragging
25
- } = useSortable({ id: card.id });
26
+ } = useSortable({ id: card.id, disabled: readOnly });
27
+ if (readOnly) {
28
+ const open = onCardClick ? () => onCardClick(card) : void 0;
29
+ return /* @__PURE__ */ jsx(
30
+ "div",
31
+ {
32
+ ref: setNodeRef,
33
+ ...open ? { role: "button", tabIndex: 0 } : {},
34
+ onClick: open,
35
+ onKeyDown: open ? (e) => {
36
+ if (e.key === "Enter" || e.key === " ") {
37
+ e.preventDefault();
38
+ open();
39
+ }
40
+ } : void 0,
41
+ "data-kanban-card": card.id,
42
+ className: cn(
43
+ "rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
44
+ "bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-button)]",
45
+ open ? "cursor-pointer" : "cursor-default",
46
+ "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
47
+ ),
48
+ children: renderCard ? renderCard(card) : /* @__PURE__ */ jsx(DefaultCard, { card })
49
+ }
50
+ );
51
+ }
26
52
  const style = {
27
53
  transform: CSS.Transform.toString(transform),
28
54
  transition
@@ -51,7 +77,9 @@ function SortableCard({
51
77
  "shadow-[var(--ds-shadow-button)]",
52
78
  "transition-shadow duration-[var(--ds-transition-speed)]",
53
79
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
54
- isDragging && "opacity-30 scale-[1.02]"
80
+ // no scale on the card left behind: the drag overlay takes its size from it, and 2% wider made the cards
81
+ // below count as "to the right" of it, so a keyboard drag's arrow keys landed on them (Sprint 25.7, C2)
82
+ isDragging && "opacity-30"
55
83
  ),
56
84
  children: renderCard ? renderCard(card) : /* @__PURE__ */ jsx(DefaultCard, { card })
57
85
  }
@@ -85,152 +113,236 @@ function OverlayCard({
85
113
  {
86
114
  className: cn(
87
115
  "rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
88
- "bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-card)]",
89
- "rotate-2"
116
+ "bg-[var(--ds-bg-surface)] p-ds-3 shadow-[var(--ds-shadow-card)]"
90
117
  ),
91
118
  children: renderCard ? renderCard(card) : /* @__PURE__ */ jsx(DefaultCard, { card })
92
119
  }
93
120
  );
94
121
  }
122
+ var DROP_PREFIX = "kanban-column:";
123
+ var columnDropId = (id) => `${DROP_PREFIX}${id}`;
124
+ var droppedColumnId = (id) => id.startsWith(DROP_PREFIX) ? id.slice(DROP_PREFIX.length) : null;
125
+ var boardCoordinates = (event, args) => {
126
+ if (event.code !== "ArrowLeft" && event.code !== "ArrowRight") return sortableKeyboardCoordinates(event, args);
127
+ event.preventDefault();
128
+ const { collisionRect, droppableRects, droppableContainers } = args.context;
129
+ if (!collisionRect) return void 0;
130
+ const columns = droppableContainers.getEnabled().filter((c) => String(c.id).startsWith(DROP_PREFIX)).map((c) => droppableRects.get(c.id)).filter((r) => r != null).sort((a, b) => a.left - b.left);
131
+ const centre = collisionRect.left + collisionRect.width / 2;
132
+ const current = columns.findIndex((r) => centre >= r.left && centre <= r.left + r.width);
133
+ const next = current < 0 ? void 0 : columns[current + (event.code === "ArrowRight" ? 1 : -1)];
134
+ if (!next) return void 0;
135
+ return { x: next.left + (next.width - collisionRect.width) / 2, y: next.top };
136
+ };
137
+ function locate(cols, cardId) {
138
+ for (const col of cols) {
139
+ const index = col.cards.findIndex((c) => c.id === cardId);
140
+ if (index >= 0) return { col, index };
141
+ }
142
+ return null;
143
+ }
144
+ function landing(cols, cardId, overId) {
145
+ const at = locate(cols, cardId);
146
+ const overAt = locate(cols, overId);
147
+ if (!at || !overAt || overAt.col.id !== at.col.id || overAt.index === at.index) return cols;
148
+ return cols.map((col) => col.id === at.col.id ? { ...col, cards: arrayMove(col.cards, at.index, overAt.index) } : col);
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;
154
+ 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);
162
+ }
163
+ return { ...col, cards };
164
+ });
165
+ }
166
+ var placeWords = (at) => `${at.col.title}, position ${at.index + 1} of ${at.col.cards.length}`;
167
+ 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
+ function BoardColumn({
169
+ col,
170
+ readOnly,
171
+ children
172
+ }) {
173
+ const { setNodeRef, isOver } = useDroppable({ id: columnDropId(col.id), disabled: readOnly });
174
+ return /* @__PURE__ */ jsxs(
175
+ "div",
176
+ {
177
+ className: cn(
178
+ "flex flex-col shrink-0 w-[calc(100vw-4rem)] sm:w-[280px] snap-center",
179
+ "bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-lg)]",
180
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
181
+ ),
182
+ children: [
183
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-ds-3 py-ds-2.5", children: [
184
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
185
+ col.color && /* @__PURE__ */ jsx(
186
+ "span",
187
+ {
188
+ className: "w-2.5 h-2.5 rounded-[var(--ds-radius-full)] shrink-0",
189
+ style: { backgroundColor: col.color }
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: col.title }),
193
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--ds-text-muted)] tabular-nums", children: col.cards.length })
194
+ ] }),
195
+ /* @__PURE__ */ jsx(Icon, { name: "plus", size: 14, className: "text-[var(--ds-text-muted)]" })
196
+ ] }),
197
+ /* @__PURE__ */ jsx(
198
+ SortableContext,
199
+ {
200
+ items: col.cards.map((c) => c.id),
201
+ strategy: verticalListSortingStrategy,
202
+ id: col.id,
203
+ children: /* @__PURE__ */ jsx(
204
+ "div",
205
+ {
206
+ ref: setNodeRef,
207
+ "data-kanban-column": col.id,
208
+ className: cn(
209
+ "flex flex-col gap-ds-2.5 px-ds-3 pb-ds-3 min-h-[60px] rounded-b-[var(--ds-radius-lg)] transition-colors",
210
+ isOver && col.cards.length === 0 && "bg-[var(--ds-bg-surface)]/60"
211
+ ),
212
+ children
213
+ }
214
+ )
215
+ }
216
+ )
217
+ ]
218
+ }
219
+ );
220
+ }
95
221
  function KanbanBoard({
96
- columns: controlledCols,
222
+ columns: columnsProp,
97
223
  onColumnsChange,
224
+ onCardMove,
225
+ readOnly,
98
226
  renderCard,
99
227
  onCardClick,
100
228
  className
101
229
  }) {
102
- const [internalCols, setInternalCols] = useState(controlledCols);
103
- const columns = onColumnsChange ? controlledCols : internalCols;
104
- const setCols = (next) => {
105
- if (typeof next === "function") {
106
- const updated = next(columns);
107
- if (onColumnsChange) onColumnsChange(updated);
108
- else setInternalCols(updated);
109
- } else {
110
- if (onColumnsChange) onColumnsChange(next);
111
- else setInternalCols(next);
112
- }
230
+ const [committed, setCommitted] = useState(columnsProp);
231
+ const [seenProp, setSeenProp] = useState(columnsProp);
232
+ const [preview, setPreviewState] = useState(null);
233
+ const previewRef = useRef(null);
234
+ const setPreview = (next) => {
235
+ previewRef.current = next;
236
+ setPreviewState(next);
113
237
  };
114
238
  const [activeCard, setActiveCard] = useState(null);
239
+ if (columnsProp !== seenProp) {
240
+ setSeenProp(columnsProp);
241
+ setCommitted(columnsProp);
242
+ if (preview && activeCard) setPreviewState(rebase(columnsProp, preview, activeCard.id));
243
+ }
244
+ useLayoutEffect(() => {
245
+ previewRef.current = preview;
246
+ }, [preview]);
247
+ const columns = preview ?? committed;
248
+ const said = useRef(void 0);
249
+ const lastPlace = useRef(null);
250
+ const announcements = useMemo(() => {
251
+ const take = () => {
252
+ const text = said.current;
253
+ said.current = void 0;
254
+ return text;
255
+ };
256
+ return { onDragStart: take, onDragOver: take, onDragEnd: take, onDragCancel: take };
257
+ }, []);
258
+ const instructions = { draggable: onCardClick ? `Press Enter to open the card. ${MOVE_INSTRUCTIONS}` : MOVE_INSTRUCTIONS };
115
259
  const sensors = useSensors(
116
260
  useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
117
261
  useSensor(KeyboardSensor, {
118
- coordinateGetter: sortableKeyboardCoordinates,
262
+ coordinateGetter: boardCoordinates,
119
263
  // Space picks up and drops; Enter is the card's open key (onCardClick)
120
264
  keyboardCodes: { start: ["Space"], cancel: ["Escape"], end: ["Space"] }
121
265
  })
122
266
  );
123
- const findColumnByCardId = (cardId) => columns.find((col) => col.cards.some((c) => c.id === cardId));
267
+ const noSensors = useSensors();
124
268
  const handleDragStart = (event) => {
125
- const col = findColumnByCardId(String(event.active.id));
126
- const card = col?.cards.find((c) => c.id === String(event.active.id));
127
- if (card) setActiveCard(card);
269
+ const at = locate(committed, String(event.active.id));
270
+ if (!at) return;
271
+ setActiveCard(at.col.cards[at.index]);
272
+ setPreview(committed);
273
+ lastPlace.current = placeWords(at);
274
+ said.current = `Picked up ${at.col.cards[at.index].title} in ${lastPlace.current}.`;
128
275
  };
129
276
  const handleDragOver = (event) => {
130
277
  const { active, over } = event;
131
- if (!over) return;
132
278
  const activeId = String(active.id);
133
- const overId = String(over.id);
134
- if (activeId === overId) return;
135
- const activeCol = findColumnByCardId(activeId);
136
- const overCol = findColumnByCardId(overId) ?? columns.find((c) => c.id === overId);
137
- if (!activeCol || !overCol || activeCol.id === overCol.id) return;
138
- setCols(
139
- (prev) => prev.map((col) => {
140
- if (col.id === activeCol.id) {
141
- return { ...col, cards: col.cards.filter((c) => c.id !== activeId) };
142
- }
143
- if (col.id === overCol.id) {
144
- const card = activeCol.cards.find((c) => c.id === activeId);
145
- if (!card) return col;
146
- const overIdx = col.cards.findIndex((c) => c.id === overId);
147
- const newCards = [...col.cards];
148
- if (overIdx >= 0) {
149
- newCards.splice(overIdx, 0, card);
150
- } else {
151
- newCards.push(card);
152
- }
153
- return { ...col, cards: newCards };
154
- }
155
- return col;
156
- })
157
- );
279
+ const overId = over ? String(over.id) : null;
280
+ const cols = previewRef.current ?? committed;
281
+ const from = locate(cols, activeId);
282
+ const toColumnId = overId == null || overId === activeId ? null : droppedColumnId(overId) ?? locate(cols, overId)?.col.id;
283
+ if (from && toColumnId && from.col.id !== toColumnId) {
284
+ const card = from.col.cards[from.index];
285
+ setPreview(cols.map((col) => {
286
+ if (col.id === from.col.id) return { ...col, cards: col.cards.filter((c) => c.id !== activeId) };
287
+ if (col.id !== toColumnId) return col;
288
+ const overIndex = col.cards.findIndex((c) => c.id === overId);
289
+ const cards = [...col.cards];
290
+ cards.splice(overIndex >= 0 ? overIndex : cards.length, 0, card);
291
+ return { ...col, cards };
292
+ }));
293
+ }
294
+ 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;
298
+ 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.`;
158
300
  };
159
301
  const handleDragEnd = (event) => {
160
302
  const { active, over } = event;
161
- setActiveCard(null);
162
- if (!over || active.id === over.id) return;
163
303
  const activeId = String(active.id);
164
- const overId = String(over.id);
165
- const col = findColumnByCardId(activeId);
166
- if (!col) return;
167
- const oldIdx = col.cards.findIndex((c) => c.id === activeId);
168
- const newIdx = col.cards.findIndex((c) => c.id === overId);
169
- if (oldIdx >= 0 && newIdx >= 0 && oldIdx !== newIdx) {
170
- setCols(
171
- (prev) => prev.map((c) => {
172
- if (c.id !== col.id) return c;
173
- const newCards = [...c.cards];
174
- const [moved] = newCards.splice(oldIdx, 1);
175
- newCards.splice(newIdx, 0, moved);
176
- return { ...c, cards: newCards };
177
- })
178
- );
304
+ const cols = previewRef.current ?? committed;
305
+ setActiveCard(null);
306
+ setPreview(null);
307
+ const final = over ? landing(cols, activeId, String(over.id)) : committed;
308
+ const before = locate(committed, activeId);
309
+ const after = locate(final, activeId);
310
+ if (!before || !after || before.col.id === after.col.id && before.index === after.index) {
311
+ if (before) said.current = `Dropped ${before.col.cards[before.index].title} back in ${placeWords(before)}.`;
312
+ return;
179
313
  }
314
+ 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);
318
+ };
319
+ const handleDragCancel = (event) => {
320
+ setActiveCard(null);
321
+ setPreview(null);
322
+ const at = locate(committed, String(event.active.id));
323
+ if (at) said.current = `Move cancelled. ${at.col.cards[at.index].title} is back in ${placeWords(at)}.`;
180
324
  };
181
325
  return /* @__PURE__ */ jsxs(
182
326
  DndContext,
183
327
  {
184
- sensors,
328
+ sensors: readOnly ? noSensors : sensors,
329
+ accessibility: { announcements, screenReaderInstructions: instructions },
185
330
  collisionDetection: closestCorners,
186
331
  onDragStart: handleDragStart,
187
332
  onDragOver: handleDragOver,
188
333
  onDragEnd: handleDragEnd,
334
+ onDragCancel: handleDragCancel,
189
335
  children: [
190
- /* @__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__ */ jsxs(
191
- "div",
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,
192
338
  {
193
- className: cn(
194
- "flex flex-col shrink-0 w-[calc(100vw-4rem)] sm:w-[280px] snap-center",
195
- "bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-lg)]",
196
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
197
- ),
198
- children: [
199
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-ds-3 py-ds-2.5", children: [
200
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
201
- col.color && /* @__PURE__ */ jsx(
202
- "span",
203
- {
204
- className: "w-2.5 h-2.5 rounded-[var(--ds-radius-full)] shrink-0",
205
- style: { backgroundColor: col.color }
206
- }
207
- ),
208
- /* @__PURE__ */ jsx("span", { className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: col.title }),
209
- /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--ds-text-muted)] tabular-nums", children: col.cards.length })
210
- ] }),
211
- /* @__PURE__ */ jsx(Icon, { name: "plus", size: 14, className: "text-[var(--ds-text-muted)]" })
212
- ] }),
213
- /* @__PURE__ */ jsx(
214
- SortableContext,
215
- {
216
- items: col.cards.map((c) => c.id),
217
- strategy: verticalListSortingStrategy,
218
- id: col.id,
219
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-ds-2.5 px-ds-3 pb-ds-3 min-h-[60px]", children: col.cards.map((card) => /* @__PURE__ */ jsx(
220
- SortableCard,
221
- {
222
- card,
223
- renderCard,
224
- onCardClick
225
- },
226
- card.id
227
- )) })
228
- }
229
- )
230
- ]
339
+ card,
340
+ renderCard,
341
+ onCardClick,
342
+ readOnly
231
343
  },
232
- col.id
233
- )) }),
344
+ card.id
345
+ )) }, col.id)) }),
234
346
  /* @__PURE__ */ jsx(DragOverlay, { children: activeCard && /* @__PURE__ */ jsx(OverlayCard, { card: activeCard, renderCard }) })
235
347
  ]
236
348
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../showcase/src/components/KanbanBoard.tsx"],"names":[],"mappings":"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","file":"kanban.js","sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport {\n DndContext,\n closestCorners,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n type DragOverEvent,\n DragOverlay,\n type DragStartEvent,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\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 columns: KanbanColumn[];\n onColumnsChange?: (columns: KanbanColumn[]) => void;\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}: {\n card: KanbanCard;\n renderCard?: (card: KanbanCard) => ReactNode;\n onCardClick?: (card: KanbanCard) => void;\n}) {\n const {\n attributes,\n listeners,\n setNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id: card.id });\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 isDragging && \"opacity-30 scale-[1.02]\",\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 \"rotate-2\",\n )}\n >\n {renderCard ? renderCard(card) : <DefaultCard card={card} />}\n </div>\n );\n}\n\nexport function KanbanBoard({\n columns: controlledCols,\n onColumnsChange,\n renderCard,\n onCardClick,\n className,\n}: KanbanBoardProps) {\n const [internalCols, setInternalCols] = useState<KanbanColumn[]>(controlledCols);\n const columns = onColumnsChange ? controlledCols : internalCols;\n const setCols = (next: KanbanColumn[] | ((prev: KanbanColumn[]) => KanbanColumn[])) => {\n if (typeof next === \"function\") {\n const updated = next(columns);\n if (onColumnsChange) onColumnsChange(updated);\n else setInternalCols(updated);\n } else {\n if (onColumnsChange) onColumnsChange(next);\n else setInternalCols(next);\n }\n };\n\n const [activeCard, setActiveCard] = useState<KanbanCard | null>(null);\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\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\n const findColumnByCardId = (cardId: string) =>\n columns.find((col) => col.cards.some((c) => c.id === cardId));\n\n const handleDragStart = (event: DragStartEvent) => {\n const col = findColumnByCardId(String(event.active.id));\n const card = col?.cards.find((c) => c.id === String(event.active.id));\n if (card) setActiveCard(card);\n };\n\n const handleDragOver = (event: DragOverEvent) => {\n const { active, over } = event;\n if (!over) return;\n\n const activeId = String(active.id);\n const overId = String(over.id);\n if (activeId === overId) return;\n\n const activeCol = findColumnByCardId(activeId);\n const overCol =\n findColumnByCardId(overId) ?? columns.find((c) => c.id === overId);\n\n if (!activeCol || !overCol || activeCol.id === overCol.id) return;\n\n setCols((prev) =>\n prev.map((col) => {\n if (col.id === activeCol.id) {\n return { ...col, cards: col.cards.filter((c) => c.id !== activeId) };\n }\n if (col.id === overCol.id) {\n const card = activeCol.cards.find((c) => c.id === activeId);\n if (!card) return col;\n const overIdx = col.cards.findIndex((c) => c.id === overId);\n const newCards = [...col.cards];\n if (overIdx >= 0) {\n newCards.splice(overIdx, 0, card);\n } else {\n newCards.push(card);\n }\n return { ...col, cards: newCards };\n }\n return col;\n }),\n );\n };\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n setActiveCard(null);\n if (!over || active.id === over.id) return;\n\n const activeId = String(active.id);\n const overId = String(over.id);\n\n const col = findColumnByCardId(activeId);\n if (!col) return;\n\n const oldIdx = col.cards.findIndex((c) => c.id === activeId);\n const newIdx = col.cards.findIndex((c) => c.id === overId);\n\n if (oldIdx >= 0 && newIdx >= 0 && oldIdx !== newIdx) {\n setCols((prev) =>\n prev.map((c) => {\n if (c.id !== col.id) return c;\n const newCards = [...c.cards];\n const [moved] = newCards.splice(oldIdx, 1);\n newCards.splice(newIdx, 0, moved);\n return { ...c, cards: newCards };\n }),\n );\n }\n };\n\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCorners}\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={handleDragEnd}\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 <div\n key={col.id}\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 className=\"flex flex-col gap-ds-2.5 px-ds-3 pb-ds-3 min-h-[60px]\">\n {col.cards.map((card) => (\n <SortableCard\n key={card.id}\n card={card}\n renderCard={renderCard}\n onCardClick={onCardClick}\n />\n ))}\n </div>\n </SortableContext>\n </div>\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 /** 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"]}
@@ -40,6 +40,7 @@ var CURATED = {
40
40
  "type": "Type",
41
41
  "chevron-up": "ChevronUp",
42
42
  "chevron-down": "ChevronDown",
43
+ "chevron-left": "ChevronLeft",
43
44
  "trash": "Trash2",
44
45
  "undo": "Undo2",
45
46
  "redo": "Redo2",
@@ -72,5 +73,5 @@ async function loadExtended() {
72
73
  }
73
74
 
74
75
  export { loadCurated, loadExtended };
75
- //# sourceMappingURL=lucide-JH2R3TFV.js.map
76
- //# sourceMappingURL=lucide-JH2R3TFV.js.map
76
+ //# sourceMappingURL=lucide-RZC2FEMS.js.map
77
+ //# sourceMappingURL=lucide-RZC2FEMS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/icon-packs/lucide.ts"],"names":[],"mappings":";;;AAgBA,IAAM,OAAA,GAAkC;AAAA,EACtC,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,cAAA;AAAA,EACV,OAAA,EAAS,WAAA;AAAA,EACT,UAAA,EAAY,UAAA;AAAA,EACZ,eAAA,EAAiB,cAAA;AAAA,EACjB,OAAA,EAAS,GAAA;AAAA,EACT,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,QAAA;AAAA,EACV,MAAA,EAAQ,MAAA;AAAA,EACR,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,OAAA;AAAA,EACT,SAAA,EAAW,eAAA;AAAA,EACX,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,aAAA;AAAA,EACT,OAAA,EAAS,OAAA;AAAA,EACT,UAAA,EAAY,cAAA;AAAA,EACZ,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,QAAA;AAAA,EACV,WAAA,EAAa,WAAA;AAAA,EACb,eAAA,EAAiB,eAAA;AAAA,EACjB,YAAA,EAAc,WAAA;AAAA,EACd,cAAA,EAAgB,aAAA;AAAA,EAChB,aAAA,EAAe,YAAA;AAAA,EACf,MAAA,EAAQ,MAAA;AAAA,EACR,cAAA,EAAgB,aAAA;AAAA,EAChB,aAAA,EAAe,YAAA;AAAA,EACf,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,OAAA;AAAA,EACT,OAAA,EAAS,OAAA;AAAA,EACT,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,OAAA;AAAA,EACT,OAAA,EAAS,OAAA;AAAA,EACT,aAAA,EAAe,aAAA;AAAA,EACf,WAAA,EAAa,WAAA;AAAA,EACb,WAAA,EAAa,aAAA;AAAA,EACb,MAAA,EAAQ,MAAA;AAAA,EACR,YAAA,EAAc,WAAA;AAAA,EACd,cAAA,EAAgB,aAAA;AAAA,EAChB,cAAA,EAAgB,aAAA;AAAA,EAChB,OAAA,EAAS,QAAA;AAAA,EACT,MAAA,EAAQ,OAAA;AAAA,EACR,MAAA,EAAQ,OAAA;AAAA,EACR,YAAA,EAAc,WAAA;AAAA,EACd,eAAA,EAAiB,cAAA;AAAA,EACjB,KAAA,EAAO,KAAA;AAAA,EACP,WAAA,EAAa,WAAA;AAAA,EACb,MAAA,EAAQ,MAAA;AAAA,EACR,SAAA,EAAW,QAAA;AAAA,EACX,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ,MAAA;AAAA,EACR,iBAAA,EAAmB,gBAAA;AAAA,EACnB,MAAA,EAAQ,MAAA;AAAA,EACR,UAAA,EAAY,UAAA;AAAA,EACZ,UAAA,EAAY,UAAA;AAAA,EACZ,QAAA,EAAU,QAAA;AAAA,EACV,UAAA,EAAY;AACd,CAAA;AAEA,eAAe,IAAA,GAAyC;AACtD,EAAA,OAAQ,MAAM,OAAO,cAAc,CAAA;AACrC;AAEA,eAAsB,WAAA,GAAgC;AACpD,EAAA,OAAO,WAAA,CAAY,MAAM,IAAA,EAAK,EAAG,OAAO,CAAA;AAC1C;AAEA,eAAsB,YAAA,GAAiC;AACrD,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,EAAK;AACvB,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,GAAA,EAAK,EAAE,CAAA;AACzC,EAAA,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,WAAA,CAAY,GAAA,EAAK,OAAO,CAAC,CAAA;AACjD,EAAA,OAAO,QAAA;AACT","file":"lucide-RZC2FEMS.js","sourcesContent":["import type { IconMap } from './types'\nimport { buildExtendedMap, pickCurated } from './utils'\n\n/**\n * Lucide icons.\n *\n * The pack is reached only through the dynamic import in peer() — never a\n * static named import. A static import of an optional peer makes a consumer's\n * production build fail on Vite 8 when the peer is not installed: Vite\n * substitutes an export-less stub and the bundler treats every missing name as\n * fatal (Sprint 22 bench finding 1, 13 Sep 2026; showcase/src/test/\n * vite8-optional-peers-build.test.ts guards it). The curated names are strings\n * for the same reason; icon-completeness.test.tsx proves every one resolves in\n * the installed pack. If the pack is missing, peer() rejects and loadPack in\n * icons.tsx reports which package to install.\n */\nconst CURATED: Record<string, string> = {\n \"home\": \"Home\",\n \"folder\": \"FolderKanban\",\n \"chart\": \"BarChart3\",\n \"settings\": \"Settings\",\n \"chevron-right\": \"ChevronRight\",\n \"close\": \"X\",\n \"plus\": \"Plus\",\n \"search\": \"Search\",\n \"bell\": \"Bell\",\n \"user\": \"User\",\n \"check\": \"Check\",\n \"warning\": \"AlertTriangle\",\n \"info\": \"Info\",\n \"error\": \"AlertCircle\",\n \"clock\": \"Clock\",\n \"calendar\": \"CalendarDays\",\n \"bold\": \"Bold\",\n \"italic\": \"Italic\",\n \"underline\": \"Underline\",\n \"strikethrough\": \"Strikethrough\",\n \"align-left\": \"AlignLeft\",\n \"align-center\": \"AlignCenter\",\n \"align-right\": \"AlignRight\",\n \"list\": \"List\",\n \"list-ordered\": \"ListOrdered\",\n \"list-checks\": \"ListChecks\",\n \"link\": \"Link\",\n \"image\": \"Image\",\n \"table\": \"Table\",\n \"code\": \"Code\",\n \"quote\": \"Quote\",\n \"minus\": \"Minus\",\n \"superscript\": \"Superscript\",\n \"subscript\": \"Subscript\",\n \"highlight\": \"Highlighter\",\n \"type\": \"Type\",\n \"chevron-up\": \"ChevronUp\",\n \"chevron-down\": \"ChevronDown\",\n \"chevron-left\": \"ChevronLeft\",\n \"trash\": \"Trash2\",\n \"undo\": \"Undo2\",\n \"redo\": \"Redo2\",\n \"arrow-left\": \"ArrowLeft\",\n \"grip-vertical\": \"GripVertical\",\n \"mic\": \"Mic\",\n \"paperclip\": \"Paperclip\",\n \"send\": \"Send\",\n \"log-out\": \"LogOut\",\n \"edit\": \"Pencil\",\n \"menu\": \"Menu\",\n \"more-horizontal\": \"MoreHorizontal\",\n \"star\": \"Star\",\n \"bookmark\": \"Bookmark\",\n \"download\": \"Download\",\n \"upload\": \"Upload\",\n \"sparkles\": \"Sparkles\",\n}\n\nasync function peer(): Promise<Record<string, unknown>> {\n return (await import(\"lucide-react\")) as unknown as Record<string, unknown>\n}\n\nexport async function loadCurated(): Promise<IconMap> {\n return pickCurated(await peer(), CURATED)\n}\n\nexport async function loadExtended(): Promise<IconMap> {\n const mod = await peer()\n const extended = buildExtendedMap(mod, {})\n Object.assign(extended, pickCurated(mod, CURATED))\n return extended\n}\n"]}
@@ -40,6 +40,7 @@ var CURATED = {
40
40
  "type": "TextT",
41
41
  "chevron-up": "CaretUp",
42
42
  "chevron-down": "CaretDown",
43
+ "chevron-left": "CaretLeft",
43
44
  "trash": "Trash",
44
45
  "undo": "ArrowCounterClockwise",
45
46
  "redo": "ArrowClockwise",
@@ -72,5 +73,5 @@ async function loadExtended() {
72
73
  }
73
74
 
74
75
  export { loadCurated, loadExtended };
75
- //# sourceMappingURL=phosphor-HCVUMVT5.js.map
76
- //# sourceMappingURL=phosphor-HCVUMVT5.js.map
76
+ //# sourceMappingURL=phosphor-CEN25FLM.js.map
77
+ //# sourceMappingURL=phosphor-CEN25FLM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/icon-packs/phosphor.ts"],"names":[],"mappings":";;;AAgBA,IAAM,OAAA,GAAkC;AAAA,EACtC,MAAA,EAAQ,OAAA;AAAA,EACR,QAAA,EAAU,cAAA;AAAA,EACV,OAAA,EAAS,UAAA;AAAA,EACT,UAAA,EAAY,MAAA;AAAA,EACZ,eAAA,EAAiB,YAAA;AAAA,EACjB,OAAA,EAAS,GAAA;AAAA,EACT,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,iBAAA;AAAA,EACV,MAAA,EAAQ,MAAA;AAAA,EACR,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,OAAA;AAAA,EACT,SAAA,EAAW,SAAA;AAAA,EACX,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,eAAA;AAAA,EACT,OAAA,EAAS,OAAA;AAAA,EACT,UAAA,EAAY,eAAA;AAAA,EACZ,MAAA,EAAQ,OAAA;AAAA,EACR,QAAA,EAAU,YAAA;AAAA,EACV,WAAA,EAAa,eAAA;AAAA,EACb,eAAA,EAAiB,mBAAA;AAAA,EACjB,YAAA,EAAc,eAAA;AAAA,EACd,cAAA,EAAgB,iBAAA;AAAA,EAChB,aAAA,EAAe,gBAAA;AAAA,EACf,MAAA,EAAQ,aAAA;AAAA,EACR,cAAA,EAAgB,aAAA;AAAA,EAChB,aAAA,EAAe,YAAA;AAAA,EACf,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,OAAA;AAAA,EACT,OAAA,EAAS,OAAA;AAAA,EACT,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,QAAA;AAAA,EACT,OAAA,EAAS,OAAA;AAAA,EACT,aAAA,EAAe,iBAAA;AAAA,EACf,WAAA,EAAa,eAAA;AAAA,EACb,WAAA,EAAa,aAAA;AAAA,EACb,MAAA,EAAQ,OAAA;AAAA,EACR,YAAA,EAAc,SAAA;AAAA,EACd,cAAA,EAAgB,WAAA;AAAA,EAChB,cAAA,EAAgB,WAAA;AAAA,EAChB,OAAA,EAAS,OAAA;AAAA,EACT,MAAA,EAAQ,uBAAA;AAAA,EACR,MAAA,EAAQ,gBAAA;AAAA,EACR,YAAA,EAAc,WAAA;AAAA,EACd,eAAA,EAAiB,iBAAA;AAAA,EACjB,KAAA,EAAO,YAAA;AAAA,EACP,WAAA,EAAa,WAAA;AAAA,EACb,MAAA,EAAQ,gBAAA;AAAA,EACR,SAAA,EAAW,SAAA;AAAA,EACX,MAAA,EAAQ,cAAA;AAAA,EACR,MAAA,EAAQ,MAAA;AAAA,EACR,iBAAA,EAAmB,WAAA;AAAA,EACnB,MAAA,EAAQ,MAAA;AAAA,EACR,UAAA,EAAY,gBAAA;AAAA,EACZ,UAAA,EAAY,gBAAA;AAAA,EACZ,QAAA,EAAU,cAAA;AAAA,EACV,UAAA,EAAY;AACd,CAAA;AAEA,eAAe,IAAA,GAAyC;AACtD,EAAA,OAAQ,MAAM,OAAO,uBAAuB,CAAA;AAC9C;AAEA,eAAsB,WAAA,GAAgC;AACpD,EAAA,OAAO,WAAA,CAAY,MAAM,IAAA,EAAK,EAAG,OAAO,CAAA;AAC1C;AAEA,eAAsB,YAAA,GAAiC;AACrD,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,EAAK;AACvB,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,GAAA,EAAK,EAAE,CAAA;AACzC,EAAA,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,WAAA,CAAY,GAAA,EAAK,OAAO,CAAC,CAAA;AACjD,EAAA,OAAO,QAAA;AACT","file":"phosphor-CEN25FLM.js","sourcesContent":["import type { IconMap } from './types'\nimport { buildExtendedMap, pickCurated } from './utils'\n\n/**\n * Phosphor icons.\n *\n * The pack is reached only through the dynamic import in peer() — never a\n * static named import. A static import of an optional peer makes a consumer's\n * production build fail on Vite 8 when the peer is not installed: Vite\n * substitutes an export-less stub and the bundler treats every missing name as\n * fatal (Sprint 22 bench finding 1, 13 Sep 2026; showcase/src/test/\n * vite8-optional-peers-build.test.ts guards it). The curated names are strings\n * for the same reason; icon-completeness.test.tsx proves every one resolves in\n * the installed pack. If the pack is missing, peer() rejects and loadPack in\n * icons.tsx reports which package to install.\n */\nconst CURATED: Record<string, string> = {\n \"home\": \"House\",\n \"folder\": \"FolderSimple\",\n \"chart\": \"ChartBar\",\n \"settings\": \"Gear\",\n \"chevron-right\": \"CaretRight\",\n \"close\": \"X\",\n \"plus\": \"Plus\",\n \"search\": \"MagnifyingGlass\",\n \"bell\": \"Bell\",\n \"user\": \"User\",\n \"check\": \"Check\",\n \"warning\": \"Warning\",\n \"info\": \"Info\",\n \"error\": \"WarningCircle\",\n \"clock\": \"Clock\",\n \"calendar\": \"CalendarBlank\",\n \"bold\": \"TextB\",\n \"italic\": \"TextItalic\",\n \"underline\": \"TextUnderline\",\n \"strikethrough\": \"TextStrikethrough\",\n \"align-left\": \"TextAlignLeft\",\n \"align-center\": \"TextAlignCenter\",\n \"align-right\": \"TextAlignRight\",\n \"list\": \"ListBullets\",\n \"list-ordered\": \"ListNumbers\",\n \"list-checks\": \"ListChecks\",\n \"link\": \"Link\",\n \"image\": \"Image\",\n \"table\": \"Table\",\n \"code\": \"Code\",\n \"quote\": \"Quotes\",\n \"minus\": \"Minus\",\n \"superscript\": \"TextSuperscript\",\n \"subscript\": \"TextSubscript\",\n \"highlight\": \"Highlighter\",\n \"type\": \"TextT\",\n \"chevron-up\": \"CaretUp\",\n \"chevron-down\": \"CaretDown\",\n \"chevron-left\": \"CaretLeft\",\n \"trash\": \"Trash\",\n \"undo\": \"ArrowCounterClockwise\",\n \"redo\": \"ArrowClockwise\",\n \"arrow-left\": \"ArrowLeft\",\n \"grip-vertical\": \"DotsSixVertical\",\n \"mic\": \"Microphone\",\n \"paperclip\": \"Paperclip\",\n \"send\": \"PaperPlaneTilt\",\n \"log-out\": \"SignOut\",\n \"edit\": \"PencilSimple\",\n \"menu\": \"List\",\n \"more-horizontal\": \"DotsThree\",\n \"star\": \"Star\",\n \"bookmark\": \"BookmarkSimple\",\n \"download\": \"DownloadSimple\",\n \"upload\": \"UploadSimple\",\n \"sparkles\": \"Sparkle\",\n}\n\nasync function peer(): Promise<Record<string, unknown>> {\n return (await import(\"@phosphor-icons/react\")) as unknown as Record<string, unknown>\n}\n\nexport async function loadCurated(): Promise<IconMap> {\n return pickCurated(await peer(), CURATED)\n}\n\nexport async function loadExtended(): Promise<IconMap> {\n const mod = await peer()\n const extended = buildExtendedMap(mod, {})\n Object.assign(extended, pickCurated(mod, CURATED))\n return extended\n}\n"]}