@ceebee/ui 1.8.0 → 1.9.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.
- package/dist/client.css +281 -0
- package/dist/client.d.ts +117 -1
- package/dist/client.js +363 -1
- package/dist/styles.css +320 -0
- package/package.json +4 -1
package/dist/client.css
CHANGED
|
@@ -11,3 +11,284 @@
|
|
|
11
11
|
font-size: var(--cb-text-sm);
|
|
12
12
|
line-height: var(--cb-leading-normal);
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
/* src/data/board/board.css */
|
|
16
|
+
.cb-board {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: var(--cb-space-3);
|
|
20
|
+
font-family: var(--cb-font-sans);
|
|
21
|
+
}
|
|
22
|
+
.cb-board__surface {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: flex-start;
|
|
25
|
+
gap: var(--cb-space-3);
|
|
26
|
+
overflow-x: auto;
|
|
27
|
+
padding-bottom: var(--cb-space-2);
|
|
28
|
+
}
|
|
29
|
+
.cb-board__surface[data-lanes] {
|
|
30
|
+
overflow-x: visible;
|
|
31
|
+
}
|
|
32
|
+
.cb-board__column {
|
|
33
|
+
flex: 0 0 auto;
|
|
34
|
+
inline-size: 17rem;
|
|
35
|
+
max-inline-size: 100%;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
gap: var(--cb-space-2);
|
|
39
|
+
padding: var(--cb-space-3);
|
|
40
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
41
|
+
border-radius: var(--cb-radius-lg);
|
|
42
|
+
background: var(--cb-bg-subtle);
|
|
43
|
+
}
|
|
44
|
+
.cb-board__surface[data-lanes] .cb-board__column {
|
|
45
|
+
flex: 1 1 auto;
|
|
46
|
+
inline-size: 100%;
|
|
47
|
+
}
|
|
48
|
+
.cb-board__column[data-over] {
|
|
49
|
+
border-color: var(--cb-tone-brand);
|
|
50
|
+
background: color-mix(in oklch, var(--cb-tone-brand) 8%, var(--cb-bg-subtle));
|
|
51
|
+
}
|
|
52
|
+
.cb-board__column[data-refuses] {
|
|
53
|
+
background: var(--cb-bg);
|
|
54
|
+
border-style: dashed;
|
|
55
|
+
}
|
|
56
|
+
.cb-board__head {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: space-between;
|
|
60
|
+
gap: var(--cb-space-2);
|
|
61
|
+
}
|
|
62
|
+
.cb-board__name {
|
|
63
|
+
font-size: var(--cb-text-sm);
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
color: var(--cb-fg);
|
|
66
|
+
}
|
|
67
|
+
.cb-board__count {
|
|
68
|
+
font-size: var(--cb-text-xs);
|
|
69
|
+
color: var(--cb-fg-muted);
|
|
70
|
+
padding: 0 var(--cb-space-2);
|
|
71
|
+
border-radius: var(--cb-radius-full);
|
|
72
|
+
background: var(--cb-bg);
|
|
73
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
74
|
+
}
|
|
75
|
+
.cb-board__count[data-over-limit] {
|
|
76
|
+
color: var(--cb-tone-warning);
|
|
77
|
+
border-color: var(--cb-tone-warning);
|
|
78
|
+
}
|
|
79
|
+
.cb-board__over,
|
|
80
|
+
.cb-board__refusal {
|
|
81
|
+
margin: 0;
|
|
82
|
+
font-size: var(--cb-text-xs);
|
|
83
|
+
color: var(--cb-fg-muted);
|
|
84
|
+
}
|
|
85
|
+
.cb-board__over {
|
|
86
|
+
color: var(--cb-tone-warning);
|
|
87
|
+
}
|
|
88
|
+
.cb-board__list {
|
|
89
|
+
list-style: none;
|
|
90
|
+
margin: 0;
|
|
91
|
+
padding: 0;
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: var(--cb-space-2);
|
|
95
|
+
min-block-size: var(--cb-space-8);
|
|
96
|
+
}
|
|
97
|
+
.cb-board__card {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: var(--cb-space-1);
|
|
101
|
+
padding: var(--cb-space-3);
|
|
102
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
103
|
+
border-radius: var(--cb-radius-md);
|
|
104
|
+
background: var(--cb-bg);
|
|
105
|
+
box-shadow: var(--cb-shadow-sm);
|
|
106
|
+
cursor: grab;
|
|
107
|
+
transition: box-shadow var(--cb-duration-fast) var(--cb-ease-standard), transform var(--cb-duration-base) var(--cb-ease-standard);
|
|
108
|
+
}
|
|
109
|
+
.cb-board__card:hover {
|
|
110
|
+
box-shadow: var(--cb-shadow-md);
|
|
111
|
+
}
|
|
112
|
+
.cb-board__card:focus-visible {
|
|
113
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
114
|
+
outline-offset: var(--cb-focus-offset);
|
|
115
|
+
}
|
|
116
|
+
.cb-board__card[data-held] {
|
|
117
|
+
box-shadow: var(--cb-shadow-lg);
|
|
118
|
+
border-color: var(--cb-tone-brand);
|
|
119
|
+
}
|
|
120
|
+
.cb-board__card[data-dragging] {
|
|
121
|
+
opacity: 0.4;
|
|
122
|
+
}
|
|
123
|
+
.cb-board__card[data-disabled] {
|
|
124
|
+
cursor: not-allowed;
|
|
125
|
+
color: var(--cb-fg-muted);
|
|
126
|
+
background: var(--cb-bg-subtle);
|
|
127
|
+
box-shadow: var(--cb-shadow-none);
|
|
128
|
+
}
|
|
129
|
+
.cb-board__card--lift {
|
|
130
|
+
cursor: grabbing;
|
|
131
|
+
box-shadow: var(--cb-shadow-lg);
|
|
132
|
+
transform: rotate(1.5deg);
|
|
133
|
+
}
|
|
134
|
+
.cb-board__title {
|
|
135
|
+
font-size: var(--cb-text-sm);
|
|
136
|
+
color: var(--cb-fg);
|
|
137
|
+
line-height: var(--cb-leading-tight);
|
|
138
|
+
display: -webkit-box;
|
|
139
|
+
-webkit-box-orient: vertical;
|
|
140
|
+
-webkit-line-clamp: 2;
|
|
141
|
+
overflow: hidden;
|
|
142
|
+
}
|
|
143
|
+
.cb-board__meta {
|
|
144
|
+
font-size: var(--cb-text-xs);
|
|
145
|
+
color: var(--cb-fg-muted);
|
|
146
|
+
}
|
|
147
|
+
.cb-board__marker {
|
|
148
|
+
block-size: var(--cb-space-1);
|
|
149
|
+
border-radius: var(--cb-radius-full);
|
|
150
|
+
background: var(--cb-tone-brand);
|
|
151
|
+
list-style: none;
|
|
152
|
+
}
|
|
153
|
+
.cb-board__empty {
|
|
154
|
+
padding: var(--cb-space-4) var(--cb-space-3);
|
|
155
|
+
border: var(--cb-border-width) dashed var(--cb-border-strong);
|
|
156
|
+
border-radius: var(--cb-radius-md);
|
|
157
|
+
text-align: center;
|
|
158
|
+
font-size: var(--cb-text-xs);
|
|
159
|
+
color: var(--cb-fg-muted);
|
|
160
|
+
}
|
|
161
|
+
.cb-board__lanes {
|
|
162
|
+
display: flex;
|
|
163
|
+
gap: var(--cb-space-1);
|
|
164
|
+
overflow-x: auto;
|
|
165
|
+
}
|
|
166
|
+
.cb-board__lane {
|
|
167
|
+
display: inline-flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: var(--cb-space-2);
|
|
170
|
+
min-block-size: var(--cb-control-floor);
|
|
171
|
+
padding: 0 var(--cb-space-3);
|
|
172
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
173
|
+
border-radius: var(--cb-radius-full);
|
|
174
|
+
background: var(--cb-bg);
|
|
175
|
+
color: var(--cb-fg-muted);
|
|
176
|
+
font: inherit;
|
|
177
|
+
font-size: var(--cb-text-sm);
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
}
|
|
180
|
+
.cb-board__lane[aria-selected=true] {
|
|
181
|
+
background: color-mix(in oklch, var(--cb-tone-brand) 12%, var(--cb-bg));
|
|
182
|
+
border-color: var(--cb-tone-brand);
|
|
183
|
+
color: var(--cb-fg);
|
|
184
|
+
}
|
|
185
|
+
.cb-board__lane:focus-visible {
|
|
186
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
187
|
+
outline-offset: var(--cb-focus-offset);
|
|
188
|
+
}
|
|
189
|
+
.cb-board__undo button {
|
|
190
|
+
min-block-size: var(--cb-control-floor);
|
|
191
|
+
padding: 0 var(--cb-space-3);
|
|
192
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
193
|
+
border-radius: var(--cb-radius-md);
|
|
194
|
+
background: var(--cb-bg);
|
|
195
|
+
color: var(--cb-fg-link);
|
|
196
|
+
font: inherit;
|
|
197
|
+
font-size: var(--cb-text-sm);
|
|
198
|
+
cursor: pointer;
|
|
199
|
+
}
|
|
200
|
+
.cb-board__undo button:focus-visible {
|
|
201
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
202
|
+
outline-offset: var(--cb-focus-offset);
|
|
203
|
+
}
|
|
204
|
+
.cb-board__live {
|
|
205
|
+
position: absolute;
|
|
206
|
+
inline-size: 1px;
|
|
207
|
+
block-size: 1px;
|
|
208
|
+
margin: -1px;
|
|
209
|
+
padding: 0;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
clip-path: inset(50%);
|
|
212
|
+
white-space: nowrap;
|
|
213
|
+
}
|
|
214
|
+
.cb-board__ghost,
|
|
215
|
+
.cb-board__ghost-card {
|
|
216
|
+
border-radius: var(--cb-radius-md);
|
|
217
|
+
background: color-mix(in oklch, var(--cb-fg-subtle) 18%, transparent);
|
|
218
|
+
}
|
|
219
|
+
.cb-board__ghost--name {
|
|
220
|
+
inline-size: var(--cb-space-8);
|
|
221
|
+
block-size: var(--cb-space-3);
|
|
222
|
+
}
|
|
223
|
+
.cb-board__ghost--count {
|
|
224
|
+
inline-size: var(--cb-space-4);
|
|
225
|
+
block-size: var(--cb-space-3);
|
|
226
|
+
}
|
|
227
|
+
.cb-board__ghost-card {
|
|
228
|
+
block-size: var(--cb-space-8);
|
|
229
|
+
border: 0;
|
|
230
|
+
box-shadow: var(--cb-shadow-none);
|
|
231
|
+
}
|
|
232
|
+
@media (prefers-reduced-motion: reduce) {
|
|
233
|
+
.cb-board__card {
|
|
234
|
+
transition: none;
|
|
235
|
+
}
|
|
236
|
+
.cb-board__card--lift {
|
|
237
|
+
transform: none;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
.cb-board[data-motion=off] .cb-board__card {
|
|
241
|
+
transition: none;
|
|
242
|
+
}
|
|
243
|
+
.cb-board[data-motion=off] .cb-board__card--lift {
|
|
244
|
+
transform: none;
|
|
245
|
+
}
|
|
246
|
+
@media (forced-colors: active) {
|
|
247
|
+
.cb-board__column,
|
|
248
|
+
.cb-board__card {
|
|
249
|
+
border-color: CanvasText;
|
|
250
|
+
}
|
|
251
|
+
.cb-board__marker {
|
|
252
|
+
background: Highlight;
|
|
253
|
+
}
|
|
254
|
+
.cb-board__card[data-held] {
|
|
255
|
+
outline: var(--cb-focus-width) solid Highlight;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
.cb-board__card[data-handle] {
|
|
259
|
+
cursor: default;
|
|
260
|
+
display: grid;
|
|
261
|
+
grid-template-columns: auto 1fr;
|
|
262
|
+
grid-template-areas: "handle title" ". meta";
|
|
263
|
+
column-gap: var(--cb-space-2);
|
|
264
|
+
align-items: start;
|
|
265
|
+
}
|
|
266
|
+
.cb-board__card[data-handle] .cb-board__title {
|
|
267
|
+
grid-area: title;
|
|
268
|
+
}
|
|
269
|
+
.cb-board__card[data-handle] .cb-board__meta {
|
|
270
|
+
grid-area: meta;
|
|
271
|
+
}
|
|
272
|
+
.cb-board__handle {
|
|
273
|
+
grid-area: handle;
|
|
274
|
+
display: inline-flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
justify-content: center;
|
|
277
|
+
min-inline-size: var(--cb-space-5);
|
|
278
|
+
min-block-size: var(--cb-space-5);
|
|
279
|
+
padding: 0;
|
|
280
|
+
border: 0;
|
|
281
|
+
border-radius: var(--cb-radius-sm);
|
|
282
|
+
background: transparent;
|
|
283
|
+
color: var(--cb-fg-subtle);
|
|
284
|
+
font: inherit;
|
|
285
|
+
line-height: var(--cb-leading-tight);
|
|
286
|
+
cursor: grab;
|
|
287
|
+
}
|
|
288
|
+
.cb-board__handle:focus-visible {
|
|
289
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
290
|
+
outline-offset: var(--cb-focus-offset);
|
|
291
|
+
}
|
|
292
|
+
.cb-board__handle:disabled {
|
|
293
|
+
cursor: not-allowed;
|
|
294
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -377,6 +377,122 @@ declare const PanZoomCanvas: typeof PanZoomCanvasRoot & {
|
|
|
377
377
|
Skeleton: typeof PanZoomCanvasSkeleton;
|
|
378
378
|
};
|
|
379
379
|
|
|
380
|
+
interface BoardSkeletonProps {
|
|
381
|
+
/** How many columns to stand in for — the same shape the real board will draw. */
|
|
382
|
+
columns?: number;
|
|
383
|
+
/** Cards per column. */
|
|
384
|
+
cards?: number;
|
|
385
|
+
label?: string;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* The board's placeholder, built from the same geometry as the board itself, so the page does not
|
|
389
|
+
* reflow when the real columns arrive.
|
|
390
|
+
*/
|
|
391
|
+
declare function BoardSkeleton({ columns, cards, label }: BoardSkeletonProps): react.JSX.Element;
|
|
392
|
+
|
|
393
|
+
/** One card on the board. `disabled` refuses movement without hiding the card. */
|
|
394
|
+
interface BoardCard {
|
|
395
|
+
id: string;
|
|
396
|
+
title: ReactNode;
|
|
397
|
+
/**
|
|
398
|
+
* The card's name in an announcement and on the drag handle, when `title` is a node rather than a
|
|
399
|
+
* string. Without it a node-titled card is announced by its id, which is a uuid read aloud.
|
|
400
|
+
*/
|
|
401
|
+
label?: string;
|
|
402
|
+
/** Whatever a person triages by — an owner, an age, a due date, a blocked marker. */
|
|
403
|
+
meta?: ReactNode;
|
|
404
|
+
/** A card that cannot move: it stays visible, is not a drag source, and is skipped by the keyboard path. */
|
|
405
|
+
disabled?: boolean;
|
|
406
|
+
/** Why it cannot move, announced when a move is attempted on it. */
|
|
407
|
+
disabledReason?: string;
|
|
408
|
+
}
|
|
409
|
+
/** One column: a state of the workflow, not a category of thing. */
|
|
410
|
+
interface BoardColumn {
|
|
411
|
+
id: string;
|
|
412
|
+
name: ReactNode;
|
|
413
|
+
/**
|
|
414
|
+
* The column's accessible name, when `name` is a node rather than a string — a header carrying
|
|
415
|
+
* status tags still has to be nameable. Without it a node-named column has no accessible name.
|
|
416
|
+
*/
|
|
417
|
+
label?: string;
|
|
418
|
+
cards: BoardCard[];
|
|
419
|
+
/** A work-in-progress limit, shown beside the count; exceeding it marks the column, never blocks a move. */
|
|
420
|
+
limit?: number;
|
|
421
|
+
/** Shown when the column holds no cards. An empty column must still invite one. */
|
|
422
|
+
empty?: ReactNode;
|
|
423
|
+
/** A column that takes no cards — an end state, say. Dropping onto it is refused with `refusal`. */
|
|
424
|
+
accepts?: boolean;
|
|
425
|
+
/** Why this column refuses a card, announced and shown on an attempted drop. */
|
|
426
|
+
refusal?: string;
|
|
427
|
+
}
|
|
428
|
+
/** Where a card is, or is going. `index` is its position within the column, 0-based. */
|
|
429
|
+
interface BoardPosition {
|
|
430
|
+
columnId: string;
|
|
431
|
+
index: number;
|
|
432
|
+
}
|
|
433
|
+
/** A move the consumer is asked to validate and apply. */
|
|
434
|
+
interface BoardMove {
|
|
435
|
+
cardId: string;
|
|
436
|
+
from: BoardPosition;
|
|
437
|
+
to: BoardPosition;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* What the consumer answers with. Resolving accepts the move; rejecting — or resolving with a
|
|
441
|
+
* `refused` reason — rolls the board back and says why.
|
|
442
|
+
*/
|
|
443
|
+
type BoardMoveResult = void | {
|
|
444
|
+
refused: string;
|
|
445
|
+
};
|
|
446
|
+
/** The shape the pure logic needs: ids and order, no React. */
|
|
447
|
+
interface BoardShape {
|
|
448
|
+
id: string;
|
|
449
|
+
cards: {
|
|
450
|
+
id: string;
|
|
451
|
+
disabled?: boolean;
|
|
452
|
+
}[];
|
|
453
|
+
accepts?: boolean;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
interface BoardLabels {
|
|
457
|
+
pickUp: string;
|
|
458
|
+
dropped: (card: string, column: string) => string;
|
|
459
|
+
cancelled: string;
|
|
460
|
+
refused: (reason: string) => string;
|
|
461
|
+
undo: string;
|
|
462
|
+
/** The drag handle's accessible name, when cards carry their own actions. */
|
|
463
|
+
move: (card: string) => string;
|
|
464
|
+
undone: string;
|
|
465
|
+
lanes: string;
|
|
466
|
+
over: (count: number, limit: number) => string;
|
|
467
|
+
}
|
|
468
|
+
interface BoardProps {
|
|
469
|
+
columns: BoardColumn[];
|
|
470
|
+
/**
|
|
471
|
+
* The one path every move takes, from the pointer and from the keyboard alike. Return nothing to
|
|
472
|
+
* accept it; return `{ refused }` or reject to roll the board back and say why.
|
|
473
|
+
*/
|
|
474
|
+
onMove: (move: BoardMove) => BoardMoveResult | Promise<BoardMoveResult>;
|
|
475
|
+
/** `auto` switches to lanes on a narrow viewport; force either to prove one in a test. */
|
|
476
|
+
layout?: 'auto' | 'board' | 'lanes';
|
|
477
|
+
/** What `auto` calls narrow. */
|
|
478
|
+
phoneQuery?: string;
|
|
479
|
+
labels?: Partial<BoardLabels>;
|
|
480
|
+
motion?: boolean;
|
|
481
|
+
/**
|
|
482
|
+
* Put the grab on a handle instead of the whole card. Use it whenever a card carries its own
|
|
483
|
+
* buttons: a card that is itself a control has **presentational children**, so anything
|
|
484
|
+
* interactive inside it disappears from the accessibility tree. With a handle the card is plain
|
|
485
|
+
* markup, its buttons stay reachable, and the handle is the one thing that drags and takes the
|
|
486
|
+
* keyboard.
|
|
487
|
+
*/
|
|
488
|
+
handle?: boolean;
|
|
489
|
+
'aria-label'?: string;
|
|
490
|
+
}
|
|
491
|
+
declare function BoardRoot({ columns, onMove, layout, phoneQuery, labels, motion, handle, 'aria-label': ariaLabel, }: BoardProps): react.JSX.Element;
|
|
492
|
+
declare const Board: typeof BoardRoot & {
|
|
493
|
+
Skeleton: typeof BoardSkeleton;
|
|
494
|
+
};
|
|
495
|
+
|
|
380
496
|
interface DiagramSkeletonProps {
|
|
381
497
|
label?: string;
|
|
382
498
|
}
|
|
@@ -467,4 +583,4 @@ declare const DiagramEditor: typeof DiagramEditorRoot & {
|
|
|
467
583
|
Skeleton: typeof DiagramSkeleton;
|
|
468
584
|
};
|
|
469
585
|
|
|
470
|
-
export { type CeebeeAntStyleCache, CeebeeAntStyleProvider, type CeebeeAntStyleProviderProps, Checklist, type ChecklistProps, type ChecklistTask, type Command, CommandPalette, type CommandPaletteProps, DEFAULT_LABELS, Diagram, type DiagramEdge, DiagramEditor, type DiagramEditorProps, type DiagramNode, type DiagramPosition, type DiagramProps, type DiagramRemoval, type DiagramRenameTarget, type DiagramShape, type DiagramSkeletonProps, type DurationToken, type Labels, LabelsProvider, type LabelsProviderProps, Modal, type ModalProps, type MotionHelpers, MotionProvider, type MotionProviderProps, type MotionSettings, type NavItem, type NavSection, type PaletteCommand, PanZoomCanvas, type PanZoomCanvasProps, type PanZoomCanvasSkeletonProps, type RankedCommand, Reveal, type RevealProps, Sidebar, type SidebarProps, type SpringPreset, Stagger, type StaggerProps, StickerGroup, type StickerGroupProps, type StickerGroupSkeletonProps, type StickerItem, ThemeBridge, type ThemeBridgeProps, type ThemeChoice, ThemeProvider, type ThemeProviderProps, type ToastOptions, type ToastPosition, ToastProvider, type ToastProviderProps, TopBar, type TopBarProps, createCeebeeAntStyleCache, extractCeebeeAntStyles, filterCommands, groupCommands, rankCommand, useLabels, useMotionSettings, useTheme, useToast };
|
|
586
|
+
export { Board, type BoardCard, type BoardColumn, type BoardLabels, type BoardMove, type BoardMoveResult, type BoardPosition, type BoardProps, type BoardShape, type BoardSkeletonProps, type CeebeeAntStyleCache, CeebeeAntStyleProvider, type CeebeeAntStyleProviderProps, Checklist, type ChecklistProps, type ChecklistTask, type Command, CommandPalette, type CommandPaletteProps, DEFAULT_LABELS, Diagram, type DiagramEdge, DiagramEditor, type DiagramEditorProps, type DiagramNode, type DiagramPosition, type DiagramProps, type DiagramRemoval, type DiagramRenameTarget, type DiagramShape, type DiagramSkeletonProps, type DurationToken, type Labels, LabelsProvider, type LabelsProviderProps, Modal, type ModalProps, type MotionHelpers, MotionProvider, type MotionProviderProps, type MotionSettings, type NavItem, type NavSection, type PaletteCommand, PanZoomCanvas, type PanZoomCanvasProps, type PanZoomCanvasSkeletonProps, type RankedCommand, Reveal, type RevealProps, Sidebar, type SidebarProps, type SpringPreset, Stagger, type StaggerProps, StickerGroup, type StickerGroupProps, type StickerGroupSkeletonProps, type StickerItem, ThemeBridge, type ThemeBridgeProps, type ThemeChoice, ThemeProvider, type ThemeProviderProps, type ToastOptions, type ToastPosition, ToastProvider, type ToastProviderProps, TopBar, type TopBarProps, createCeebeeAntStyleCache, extractCeebeeAntStyles, filterCommands, groupCommands, rankCommand, useLabels, useMotionSettings, useTheme, useToast };
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Modal as Modal$1, Button, theme, ConfigProvider, Progress } from 'antd';
|
|
3
3
|
export * from 'antd';
|
|
4
|
+
import * as React from 'react';
|
|
4
5
|
import { createContext, useId, useRef, useCallback, useInsertionEffect, useLayoutEffect, useState, useEffect, useMemo, useContext, Children } from 'react';
|
|
5
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
7
|
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
|
|
@@ -13,6 +14,9 @@ import { X, Minus, Plus, RotateCcw, Minimize2, Maximize2, Search, Check, Chevron
|
|
|
13
14
|
import { Toast } from '@base-ui/react/toast';
|
|
14
15
|
import { Popover } from '@base-ui/react/popover';
|
|
15
16
|
import { AnimatePresence, motion } from 'motion/react';
|
|
17
|
+
import { useSensors, useSensor, PointerSensor, DndContext, closestCorners, DragOverlay, useDroppable } from '@dnd-kit/core';
|
|
18
|
+
import { SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
|
19
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
16
20
|
import { ReactFlow, ConnectionMode, Background, Controls, applyNodeChanges, MarkerType, Handle, Position } from '@xyflow/react';
|
|
17
21
|
|
|
18
22
|
// src/lib/cn.ts
|
|
@@ -1673,6 +1677,364 @@ function PanZoomCanvasRoot({
|
|
|
1673
1677
|
] });
|
|
1674
1678
|
}
|
|
1675
1679
|
var PanZoomCanvas = Object.assign(PanZoomCanvasRoot, { Skeleton: PanZoomCanvasSkeleton });
|
|
1680
|
+
function BoardSkeleton({ columns = 3, cards = 3, label = "Loading board" }) {
|
|
1681
|
+
return /* @__PURE__ */ jsx("div", { className: "cb-board", "aria-busy": "true", "aria-label": label, children: /* @__PURE__ */ jsx("div", { className: "cb-board__surface", children: Array.from({ length: columns }, (_, column) => /* @__PURE__ */ jsxs("section", { className: "cb-board__column", children: [
|
|
1682
|
+
/* @__PURE__ */ jsxs("header", { className: "cb-board__head", children: [
|
|
1683
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__ghost cb-board__ghost cb-board__ghost--name" }),
|
|
1684
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__ghost cb-board__ghost cb-board__ghost--count" })
|
|
1685
|
+
] }),
|
|
1686
|
+
/* @__PURE__ */ jsx("ol", { className: "cb-board__list", children: Array.from({ length: cards }, (_2, card) => /* @__PURE__ */ jsx("li", { className: "cb-board__card cb-board__ghost-card" }, card)) })
|
|
1687
|
+
] }, column)) }) });
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// src/data/board/board.math.ts
|
|
1691
|
+
function locate(columns, cardId) {
|
|
1692
|
+
for (const column of columns) {
|
|
1693
|
+
const index = column.cards.findIndex((card) => card.id === cardId);
|
|
1694
|
+
if (index !== -1) return { columnId: column.id, index };
|
|
1695
|
+
}
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
var columnOf = (columns, columnId) => columns.find((c) => c.id === columnId);
|
|
1699
|
+
function canPickUp(columns, cardId) {
|
|
1700
|
+
const at = locate(columns, cardId);
|
|
1701
|
+
if (!at) return false;
|
|
1702
|
+
return !columnOf(columns, at.columnId)?.cards.find((c) => c.id === cardId)?.disabled;
|
|
1703
|
+
}
|
|
1704
|
+
function refusalFor(columns, move) {
|
|
1705
|
+
if (!canPickUp(columns, move.cardId)) return "this card cannot be moved";
|
|
1706
|
+
const target = columnOf(columns, move.to.columnId);
|
|
1707
|
+
if (!target) return "that column is not on the board";
|
|
1708
|
+
if (target.accepts === false) return "that column does not take cards";
|
|
1709
|
+
return null;
|
|
1710
|
+
}
|
|
1711
|
+
function isNoop(move) {
|
|
1712
|
+
return move.from.columnId === move.to.columnId && move.from.index === move.to.index;
|
|
1713
|
+
}
|
|
1714
|
+
function applyMove(columns, move) {
|
|
1715
|
+
const card = columnOf(columns, move.from.columnId)?.cards[move.from.index];
|
|
1716
|
+
if (!card || card.id !== move.cardId) return [...columns];
|
|
1717
|
+
return columns.map((column) => {
|
|
1718
|
+
if (column.id === move.from.columnId && column.id === move.to.columnId) {
|
|
1719
|
+
const rest = column.cards.filter((_, i) => i !== move.from.index);
|
|
1720
|
+
rest.splice(clamp(move.to.index, rest.length), 0, card);
|
|
1721
|
+
return { ...column, cards: rest };
|
|
1722
|
+
}
|
|
1723
|
+
if (column.id === move.from.columnId) {
|
|
1724
|
+
return { ...column, cards: column.cards.filter((_, i) => i !== move.from.index) };
|
|
1725
|
+
}
|
|
1726
|
+
if (column.id === move.to.columnId) {
|
|
1727
|
+
const next = [...column.cards];
|
|
1728
|
+
next.splice(clamp(move.to.index, next.length), 0, card);
|
|
1729
|
+
return { ...column, cards: next };
|
|
1730
|
+
}
|
|
1731
|
+
return column;
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
var clamp = (value, max) => Math.max(0, Math.min(value, max));
|
|
1735
|
+
function nextTarget(columns, held, direction) {
|
|
1736
|
+
const at = columns.findIndex((c) => c.id === held.columnId);
|
|
1737
|
+
const current = columns[at];
|
|
1738
|
+
if (!current) return null;
|
|
1739
|
+
if (direction === "up" || direction === "down") {
|
|
1740
|
+
const size = current.cards.length;
|
|
1741
|
+
const index = held.index + (direction === "down" ? 1 : -1);
|
|
1742
|
+
return index < 0 || index > size - 1 ? null : { columnId: held.columnId, index };
|
|
1743
|
+
}
|
|
1744
|
+
const step = direction === "right" ? 1 : -1;
|
|
1745
|
+
for (let i = at + step; i >= 0 && i < columns.length; i += step) {
|
|
1746
|
+
const column = columns[i];
|
|
1747
|
+
if (!column || column.accepts === false) continue;
|
|
1748
|
+
return { columnId: column.id, index: clamp(held.index, column.cards.length) };
|
|
1749
|
+
}
|
|
1750
|
+
return null;
|
|
1751
|
+
}
|
|
1752
|
+
function columnLoad(column) {
|
|
1753
|
+
const count = column.cards.length;
|
|
1754
|
+
return { count, over: typeof column.limit === "number" && count > column.limit };
|
|
1755
|
+
}
|
|
1756
|
+
var DEFAULTS = {
|
|
1757
|
+
pickUp: "Picked up. Use the arrow keys to move it, Space to drop, Escape to cancel.",
|
|
1758
|
+
dropped: (card, column) => `${card} moved to ${column}.`,
|
|
1759
|
+
cancelled: "Move cancelled.",
|
|
1760
|
+
refused: (reason) => `Move refused: ${reason}`,
|
|
1761
|
+
undo: "Undo",
|
|
1762
|
+
move: (card) => `Move ${card}`,
|
|
1763
|
+
undone: "Move undone.",
|
|
1764
|
+
lanes: "Column",
|
|
1765
|
+
over: (count, limit) => `${count} of ${limit}, over the limit`
|
|
1766
|
+
};
|
|
1767
|
+
var textOf = (node, fallback) => typeof node === "string" ? node : fallback;
|
|
1768
|
+
function BoardRoot({
|
|
1769
|
+
columns,
|
|
1770
|
+
onMove,
|
|
1771
|
+
layout = "auto",
|
|
1772
|
+
phoneQuery = "(max-width: 640px)",
|
|
1773
|
+
labels,
|
|
1774
|
+
motion: motion3 = true,
|
|
1775
|
+
handle = false,
|
|
1776
|
+
"aria-label": ariaLabel = "Board"
|
|
1777
|
+
}) {
|
|
1778
|
+
const text = { ...DEFAULTS, ...labels };
|
|
1779
|
+
const [optimistic, setOptimistic] = React.useState(null);
|
|
1780
|
+
const [held, setHeld] = React.useState(null);
|
|
1781
|
+
const [dragging, setDragging] = React.useState(null);
|
|
1782
|
+
const [announcement, setAnnouncement] = React.useState("");
|
|
1783
|
+
const [undoable, setUndoable] = React.useState(null);
|
|
1784
|
+
const [lane, setLane] = React.useState(0);
|
|
1785
|
+
const view = optimistic ?? columns;
|
|
1786
|
+
React.useEffect(() => setOptimistic(null), [columns]);
|
|
1787
|
+
const narrow = useNarrow(layout === "auto" ? phoneQuery : null);
|
|
1788
|
+
const lanes = layout === "lanes" || layout === "auto" && narrow;
|
|
1789
|
+
const nameOf = (columnId) => {
|
|
1790
|
+
const column = view.find((c) => c.id === columnId);
|
|
1791
|
+
return column?.label ?? textOf(column?.name, columnId);
|
|
1792
|
+
};
|
|
1793
|
+
const commit = React.useCallback(
|
|
1794
|
+
async (move, announceAs) => {
|
|
1795
|
+
if (isNoop(move)) return;
|
|
1796
|
+
const refusal = refusalFor(view, move);
|
|
1797
|
+
if (refusal) {
|
|
1798
|
+
setAnnouncement(text.refused(refusal));
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
const before = view;
|
|
1802
|
+
setOptimistic(applyMove(view, move));
|
|
1803
|
+
setAnnouncement(announceAs ?? text.dropped(titleOf(view, move.cardId), nameOf(move.to.columnId)));
|
|
1804
|
+
try {
|
|
1805
|
+
const result = await onMove(move);
|
|
1806
|
+
if (result && "refused" in result) {
|
|
1807
|
+
setOptimistic(before);
|
|
1808
|
+
setAnnouncement(text.refused(result.refused));
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
setUndoable({ cardId: move.cardId, from: move.to, to: move.from });
|
|
1812
|
+
} catch (e) {
|
|
1813
|
+
setOptimistic(before);
|
|
1814
|
+
setAnnouncement(text.refused(e instanceof Error ? e.message : String(e)));
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
[view, onMove, text]
|
|
1818
|
+
);
|
|
1819
|
+
const onCardKeyDown = (event, cardId) => {
|
|
1820
|
+
const DIRECTIONS = {
|
|
1821
|
+
ArrowLeft: "left",
|
|
1822
|
+
ArrowRight: "right",
|
|
1823
|
+
ArrowUp: "up",
|
|
1824
|
+
ArrowDown: "down"
|
|
1825
|
+
};
|
|
1826
|
+
if (event.key === "Escape" && held) {
|
|
1827
|
+
event.preventDefault();
|
|
1828
|
+
setHeld(null);
|
|
1829
|
+
setAnnouncement(text.cancelled);
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
if (event.key === " " || event.key === "Enter") {
|
|
1833
|
+
event.preventDefault();
|
|
1834
|
+
if (!held) {
|
|
1835
|
+
if (!canPickUp(view, cardId)) {
|
|
1836
|
+
const card = view.flatMap((c) => c.cards).find((c) => c.id === cardId);
|
|
1837
|
+
setAnnouncement(text.refused(card?.disabledReason ?? "this card cannot be moved"));
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
const at = locate(view, cardId);
|
|
1841
|
+
if (at) {
|
|
1842
|
+
setHeld({ cardId, at });
|
|
1843
|
+
setAnnouncement(text.pickUp);
|
|
1844
|
+
}
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
const from = locate(view, held.cardId);
|
|
1848
|
+
if (from) void commit({ cardId: held.cardId, from, to: held.at });
|
|
1849
|
+
setHeld(null);
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
const direction = DIRECTIONS[event.key];
|
|
1853
|
+
if (!direction || !held) return;
|
|
1854
|
+
event.preventDefault();
|
|
1855
|
+
const target = nextTarget(view, held.at, direction);
|
|
1856
|
+
if (!target) return;
|
|
1857
|
+
setHeld({ ...held, at: target });
|
|
1858
|
+
setAnnouncement(`${nameOf(target.columnId)}, ${target.index + 1}`);
|
|
1859
|
+
};
|
|
1860
|
+
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
|
|
1861
|
+
const onDragEnd = (event) => {
|
|
1862
|
+
setDragging(null);
|
|
1863
|
+
const cardId = String(event.active.id);
|
|
1864
|
+
const over = event.over ? String(event.over.id) : null;
|
|
1865
|
+
if (!over) return;
|
|
1866
|
+
const from = locate(view, cardId);
|
|
1867
|
+
if (!from) return;
|
|
1868
|
+
const onColumn = view.find((c) => c.id === over);
|
|
1869
|
+
const to = onColumn ? { columnId: onColumn.id, index: onColumn.cards.length } : locate(view, over);
|
|
1870
|
+
if (to) void commit({ cardId, from, to });
|
|
1871
|
+
};
|
|
1872
|
+
const laneColumn = view[Math.min(lane, Math.max(view.length - 1, 0))];
|
|
1873
|
+
const shown = lanes ? laneColumn ? [laneColumn] : [] : view;
|
|
1874
|
+
return /* @__PURE__ */ jsxs("div", { className: "cb-board", "data-motion": motion3 ? void 0 : "off", children: [
|
|
1875
|
+
lanes ? /* @__PURE__ */ jsx("div", { className: "cb-board__lanes", role: "tablist", "aria-label": text.lanes, children: view.map((column, i) => /* @__PURE__ */ jsxs(
|
|
1876
|
+
"button",
|
|
1877
|
+
{
|
|
1878
|
+
type: "button",
|
|
1879
|
+
role: "tab",
|
|
1880
|
+
"aria-selected": i === lane,
|
|
1881
|
+
className: "cb-board__lane",
|
|
1882
|
+
onClick: () => setLane(i),
|
|
1883
|
+
children: [
|
|
1884
|
+
column.name,
|
|
1885
|
+
" ",
|
|
1886
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__count", children: column.cards.length })
|
|
1887
|
+
]
|
|
1888
|
+
},
|
|
1889
|
+
column.id
|
|
1890
|
+
)) }) : null,
|
|
1891
|
+
/* @__PURE__ */ jsxs(
|
|
1892
|
+
DndContext,
|
|
1893
|
+
{
|
|
1894
|
+
sensors,
|
|
1895
|
+
collisionDetection: closestCorners,
|
|
1896
|
+
onDragStart: (e) => setDragging(String(e.active.id)),
|
|
1897
|
+
onDragCancel: () => setDragging(null),
|
|
1898
|
+
onDragEnd,
|
|
1899
|
+
children: [
|
|
1900
|
+
/* @__PURE__ */ jsx("div", { className: "cb-board__surface", role: "group", "aria-label": ariaLabel, "data-lanes": lanes ? "" : void 0, children: shown.map((column) => /* @__PURE__ */ jsx(Column, { column, held, onCardKeyDown, labels: text, handle }, column.id)) }),
|
|
1901
|
+
/* @__PURE__ */ jsx(DragOverlay, { children: dragging ? /* @__PURE__ */ jsx("div", { className: "cb-board__card cb-board__card--lift", children: cardTitle(view, dragging) }) : null })
|
|
1902
|
+
]
|
|
1903
|
+
}
|
|
1904
|
+
),
|
|
1905
|
+
undoable ? /* @__PURE__ */ jsx("div", { className: "cb-board__undo", children: /* @__PURE__ */ jsx(
|
|
1906
|
+
"button",
|
|
1907
|
+
{
|
|
1908
|
+
type: "button",
|
|
1909
|
+
onClick: () => {
|
|
1910
|
+
const move = undoable;
|
|
1911
|
+
setUndoable(null);
|
|
1912
|
+
void commit(move, text.undone);
|
|
1913
|
+
},
|
|
1914
|
+
children: text.undo
|
|
1915
|
+
}
|
|
1916
|
+
) }) : null,
|
|
1917
|
+
/* @__PURE__ */ jsx("div", { className: "cb-board__live", role: "status", "aria-live": "polite", children: announcement })
|
|
1918
|
+
] });
|
|
1919
|
+
}
|
|
1920
|
+
var cardTitle = (columns, cardId) => columns.flatMap((c) => c.cards).find((c) => c.id === cardId)?.title ?? null;
|
|
1921
|
+
var titleOf = (columns, cardId) => {
|
|
1922
|
+
const card = columns.flatMap((c) => c.cards).find((c) => c.id === cardId);
|
|
1923
|
+
return card?.label ?? textOf(card?.title, cardId);
|
|
1924
|
+
};
|
|
1925
|
+
function Column({
|
|
1926
|
+
column,
|
|
1927
|
+
held,
|
|
1928
|
+
onCardKeyDown,
|
|
1929
|
+
labels,
|
|
1930
|
+
handle
|
|
1931
|
+
}) {
|
|
1932
|
+
const { setNodeRef, isOver } = useDroppable({ id: column.id });
|
|
1933
|
+
const load = columnLoad(column);
|
|
1934
|
+
const holding = held?.at.columnId === column.id;
|
|
1935
|
+
return /* @__PURE__ */ jsxs(
|
|
1936
|
+
"section",
|
|
1937
|
+
{
|
|
1938
|
+
ref: setNodeRef,
|
|
1939
|
+
className: "cb-board__column",
|
|
1940
|
+
"data-over": isOver ? "" : void 0,
|
|
1941
|
+
"data-refuses": column.accepts === false ? "" : void 0,
|
|
1942
|
+
"aria-label": column.label ?? (typeof column.name === "string" ? column.name : void 0),
|
|
1943
|
+
children: [
|
|
1944
|
+
/* @__PURE__ */ jsxs("header", { className: "cb-board__head", children: [
|
|
1945
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__name", children: column.name }),
|
|
1946
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__count", "data-over-limit": load.over ? "" : void 0, children: typeof column.limit === "number" ? `${load.count}/${column.limit}` : load.count })
|
|
1947
|
+
] }),
|
|
1948
|
+
/* @__PURE__ */ jsx(SortableContext, { items: column.cards.map((c) => c.id), strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsxs("ol", { className: "cb-board__list", children: [
|
|
1949
|
+
column.cards.map((card, index) => /* @__PURE__ */ jsx(
|
|
1950
|
+
Card,
|
|
1951
|
+
{
|
|
1952
|
+
card,
|
|
1953
|
+
held: held?.cardId === card.id,
|
|
1954
|
+
marker: holding && held?.at.index === index,
|
|
1955
|
+
onKeyDown: onCardKeyDown,
|
|
1956
|
+
handle,
|
|
1957
|
+
labels
|
|
1958
|
+
},
|
|
1959
|
+
card.id
|
|
1960
|
+
)),
|
|
1961
|
+
holding && held.at.index >= column.cards.length ? /* @__PURE__ */ jsx("li", { className: "cb-board__marker", "aria-hidden": true }) : null
|
|
1962
|
+
] }) }),
|
|
1963
|
+
column.cards.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cb-board__empty", children: column.empty ?? null }) : null,
|
|
1964
|
+
column.accepts === false && column.refusal ? /* @__PURE__ */ jsx("p", { className: "cb-board__refusal", children: column.refusal }) : null,
|
|
1965
|
+
load.over && typeof column.limit === "number" ? /* @__PURE__ */ jsx("p", { className: "cb-board__over", children: labels.over(load.count, column.limit) }) : null
|
|
1966
|
+
]
|
|
1967
|
+
}
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
function Card({
|
|
1971
|
+
card,
|
|
1972
|
+
held,
|
|
1973
|
+
marker,
|
|
1974
|
+
onKeyDown,
|
|
1975
|
+
handle,
|
|
1976
|
+
labels
|
|
1977
|
+
}) {
|
|
1978
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
1979
|
+
id: card.id,
|
|
1980
|
+
disabled: card.disabled
|
|
1981
|
+
});
|
|
1982
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1983
|
+
marker ? /* @__PURE__ */ jsx("li", { className: "cb-board__marker", "aria-hidden": true }) : null,
|
|
1984
|
+
/* @__PURE__ */ jsxs(
|
|
1985
|
+
"li",
|
|
1986
|
+
{
|
|
1987
|
+
ref: setNodeRef,
|
|
1988
|
+
style: { transform: CSS.Transform.toString(transform), transition },
|
|
1989
|
+
className: "cb-board__card",
|
|
1990
|
+
"data-dragging": isDragging ? "" : void 0,
|
|
1991
|
+
"data-held": held ? "" : void 0,
|
|
1992
|
+
"data-disabled": card.disabled ? "" : void 0,
|
|
1993
|
+
"data-handle": handle ? "" : void 0,
|
|
1994
|
+
...handle ? {} : {
|
|
1995
|
+
...attributes,
|
|
1996
|
+
...card.disabled ? {} : listeners,
|
|
1997
|
+
tabIndex: 0,
|
|
1998
|
+
"aria-roledescription": "draggable card",
|
|
1999
|
+
"aria-disabled": card.disabled || void 0,
|
|
2000
|
+
onKeyDown: (event) => onKeyDown(event, card.id)
|
|
2001
|
+
},
|
|
2002
|
+
children: [
|
|
2003
|
+
handle ? /* @__PURE__ */ jsx(
|
|
2004
|
+
"button",
|
|
2005
|
+
{
|
|
2006
|
+
type: "button",
|
|
2007
|
+
className: "cb-board__handle",
|
|
2008
|
+
...attributes,
|
|
2009
|
+
...card.disabled ? {} : listeners,
|
|
2010
|
+
"aria-label": labels.move(card.label ?? (typeof card.title === "string" ? card.title : card.id)),
|
|
2011
|
+
"aria-roledescription": "drag handle",
|
|
2012
|
+
"aria-disabled": card.disabled || void 0,
|
|
2013
|
+
disabled: card.disabled,
|
|
2014
|
+
onKeyDown: (event) => onKeyDown(event, card.id),
|
|
2015
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u283F" })
|
|
2016
|
+
}
|
|
2017
|
+
) : null,
|
|
2018
|
+
/* @__PURE__ */ jsx("div", { className: "cb-board__title", children: card.title }),
|
|
2019
|
+
card.meta ? /* @__PURE__ */ jsx("div", { className: "cb-board__meta", children: card.meta }) : null
|
|
2020
|
+
]
|
|
2021
|
+
}
|
|
2022
|
+
)
|
|
2023
|
+
] });
|
|
2024
|
+
}
|
|
2025
|
+
function useNarrow(query) {
|
|
2026
|
+
const [narrow, setNarrow] = React.useState(false);
|
|
2027
|
+
React.useEffect(() => {
|
|
2028
|
+
if (!query || typeof window === "undefined" || !window.matchMedia) return;
|
|
2029
|
+
const mql = window.matchMedia(query);
|
|
2030
|
+
const read = () => setNarrow(mql.matches);
|
|
2031
|
+
read();
|
|
2032
|
+
mql.addEventListener("change", read);
|
|
2033
|
+
return () => mql.removeEventListener("change", read);
|
|
2034
|
+
}, [query]);
|
|
2035
|
+
return narrow;
|
|
2036
|
+
}
|
|
2037
|
+
var Board = Object.assign(BoardRoot, { Skeleton: BoardSkeleton });
|
|
1676
2038
|
function DiagramNodeView({ data, selected }) {
|
|
1677
2039
|
return /* @__PURE__ */ jsxs(
|
|
1678
2040
|
"div",
|
|
@@ -1980,4 +2342,4 @@ function DiagramEditorRoot(props) {
|
|
|
1980
2342
|
}
|
|
1981
2343
|
var DiagramEditor = Object.assign(DiagramEditorRoot, { Skeleton: DiagramSkeleton });
|
|
1982
2344
|
|
|
1983
|
-
export { CeebeeAntStyleProvider, Checklist, CommandPalette, DEFAULT_LABELS, Diagram, DiagramEditor, LabelsProvider, Modal, MotionProvider, PanZoomCanvas, Reveal, Sidebar, Stagger, StickerGroup, ThemeBridge, ThemeProvider, ToastProvider, TopBar, createCeebeeAntStyleCache, extractCeebeeAntStyles, filterCommands, groupCommands, rankCommand, useLabels, useMotionSettings, useTheme, useToast };
|
|
2345
|
+
export { Board, CeebeeAntStyleProvider, Checklist, CommandPalette, DEFAULT_LABELS, Diagram, DiagramEditor, LabelsProvider, Modal, MotionProvider, PanZoomCanvas, Reveal, Sidebar, Stagger, StickerGroup, ThemeBridge, ThemeProvider, ToastProvider, TopBar, createCeebeeAntStyleCache, extractCeebeeAntStyles, filterCommands, groupCommands, rankCommand, useLabels, useMotionSettings, useTheme, useToast };
|
package/dist/styles.css
CHANGED
|
@@ -1360,6 +1360,326 @@ svg.react-flow__connectionline {
|
|
|
1360
1360
|
line-height: var(--cb-leading-normal);
|
|
1361
1361
|
}
|
|
1362
1362
|
|
|
1363
|
+
/* data/board/board.css */
|
|
1364
|
+
/* Board — columns of cards a person moves by pointer or by keyboard.
|
|
1365
|
+
Every value is a Token. The drag lift, the gap animation and the drop marker all have a
|
|
1366
|
+
reduced-motion rendering: transform drops, the marker stays, so a move is never invisible. */
|
|
1367
|
+
|
|
1368
|
+
.cb-board {
|
|
1369
|
+
display: flex;
|
|
1370
|
+
flex-direction: column;
|
|
1371
|
+
gap: var(--cb-space-3);
|
|
1372
|
+
font-family: var(--cb-font-sans);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
.cb-board__surface {
|
|
1376
|
+
display: flex;
|
|
1377
|
+
align-items: flex-start;
|
|
1378
|
+
gap: var(--cb-space-3);
|
|
1379
|
+
overflow-x: auto;
|
|
1380
|
+
padding-bottom: var(--cb-space-2);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.cb-board__surface[data-lanes] {
|
|
1384
|
+
overflow-x: visible;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/* ── column ── */
|
|
1388
|
+
|
|
1389
|
+
.cb-board__column {
|
|
1390
|
+
flex: 0 0 auto;
|
|
1391
|
+
inline-size: 17rem;
|
|
1392
|
+
max-inline-size: 100%;
|
|
1393
|
+
display: flex;
|
|
1394
|
+
flex-direction: column;
|
|
1395
|
+
gap: var(--cb-space-2);
|
|
1396
|
+
padding: var(--cb-space-3);
|
|
1397
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
1398
|
+
border-radius: var(--cb-radius-lg);
|
|
1399
|
+
background: var(--cb-bg-subtle);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
.cb-board__surface[data-lanes] .cb-board__column {
|
|
1403
|
+
flex: 1 1 auto;
|
|
1404
|
+
inline-size: 100%;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/* The column a pointer is over: named by a ring rather than a fill, so a tinted card keeps its tone. */
|
|
1408
|
+
.cb-board__column[data-over] {
|
|
1409
|
+
border-color: var(--cb-tone-brand);
|
|
1410
|
+
background: color-mix(in oklch, var(--cb-tone-brand) 8%, var(--cb-bg-subtle));
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/* A column that takes no cards says so before a drop is attempted. */
|
|
1414
|
+
.cb-board__column[data-refuses] {
|
|
1415
|
+
background: var(--cb-bg);
|
|
1416
|
+
border-style: dashed;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
.cb-board__head {
|
|
1420
|
+
display: flex;
|
|
1421
|
+
align-items: center;
|
|
1422
|
+
justify-content: space-between;
|
|
1423
|
+
gap: var(--cb-space-2);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.cb-board__name {
|
|
1427
|
+
font-size: var(--cb-text-sm);
|
|
1428
|
+
font-weight: 600;
|
|
1429
|
+
color: var(--cb-fg);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
.cb-board__count {
|
|
1433
|
+
font-size: var(--cb-text-xs);
|
|
1434
|
+
color: var(--cb-fg-muted);
|
|
1435
|
+
padding: 0 var(--cb-space-2);
|
|
1436
|
+
border-radius: var(--cb-radius-full);
|
|
1437
|
+
background: var(--cb-bg);
|
|
1438
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/* Over a WIP limit is marked, never blocked — the board reports, the consumer decides. */
|
|
1442
|
+
.cb-board__count[data-over-limit] {
|
|
1443
|
+
color: var(--cb-tone-warning);
|
|
1444
|
+
border-color: var(--cb-tone-warning);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
.cb-board__over,
|
|
1448
|
+
.cb-board__refusal {
|
|
1449
|
+
margin: 0;
|
|
1450
|
+
font-size: var(--cb-text-xs);
|
|
1451
|
+
color: var(--cb-fg-muted);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
.cb-board__over { color: var(--cb-tone-warning); }
|
|
1455
|
+
|
|
1456
|
+
/* ── cards ── */
|
|
1457
|
+
|
|
1458
|
+
.cb-board__list {
|
|
1459
|
+
list-style: none;
|
|
1460
|
+
margin: 0;
|
|
1461
|
+
padding: 0;
|
|
1462
|
+
display: flex;
|
|
1463
|
+
flex-direction: column;
|
|
1464
|
+
gap: var(--cb-space-2);
|
|
1465
|
+
min-block-size: var(--cb-space-8);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
.cb-board__card {
|
|
1469
|
+
display: flex;
|
|
1470
|
+
flex-direction: column;
|
|
1471
|
+
gap: var(--cb-space-1);
|
|
1472
|
+
padding: var(--cb-space-3);
|
|
1473
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
1474
|
+
border-radius: var(--cb-radius-md);
|
|
1475
|
+
background: var(--cb-bg);
|
|
1476
|
+
box-shadow: var(--cb-shadow-sm);
|
|
1477
|
+
cursor: grab;
|
|
1478
|
+
transition: box-shadow var(--cb-duration-fast) var(--cb-ease-standard),
|
|
1479
|
+
transform var(--cb-duration-base) var(--cb-ease-standard);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.cb-board__card:hover {
|
|
1483
|
+
box-shadow: var(--cb-shadow-md);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
.cb-board__card:focus-visible {
|
|
1487
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
1488
|
+
outline-offset: var(--cb-focus-offset);
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
/* Held by the keyboard: the same lift a pointer drag gives, so both paths look alike. */
|
|
1492
|
+
.cb-board__card[data-held] {
|
|
1493
|
+
box-shadow: var(--cb-shadow-lg);
|
|
1494
|
+
border-color: var(--cb-tone-brand);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
.cb-board__card[data-dragging] {
|
|
1498
|
+
opacity: 0.4;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
.cb-board__card[data-disabled] {
|
|
1502
|
+
cursor: not-allowed;
|
|
1503
|
+
color: var(--cb-fg-muted);
|
|
1504
|
+
background: var(--cb-bg-subtle);
|
|
1505
|
+
box-shadow: var(--cb-shadow-none);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/* What follows the pointer. It is out of flow, so it carries the lift on its own. */
|
|
1509
|
+
.cb-board__card--lift {
|
|
1510
|
+
cursor: grabbing;
|
|
1511
|
+
box-shadow: var(--cb-shadow-lg);
|
|
1512
|
+
transform: rotate(1.5deg);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
.cb-board__title {
|
|
1516
|
+
font-size: var(--cb-text-sm);
|
|
1517
|
+
color: var(--cb-fg);
|
|
1518
|
+
line-height: var(--cb-leading-tight);
|
|
1519
|
+
/* A long title clamps rather than stretching its own card and the cards beside it. The full name
|
|
1520
|
+
belongs in a tooltip the consumer supplies around the title. */
|
|
1521
|
+
display: -webkit-box;
|
|
1522
|
+
-webkit-box-orient: vertical;
|
|
1523
|
+
-webkit-line-clamp: 2;
|
|
1524
|
+
overflow: hidden;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
.cb-board__meta {
|
|
1528
|
+
font-size: var(--cb-text-xs);
|
|
1529
|
+
color: var(--cb-fg-muted);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/* Where the held card would land. It is the one affordance that must survive reduced motion. */
|
|
1533
|
+
.cb-board__marker {
|
|
1534
|
+
block-size: var(--cb-space-1);
|
|
1535
|
+
border-radius: var(--cb-radius-full);
|
|
1536
|
+
background: var(--cb-tone-brand);
|
|
1537
|
+
list-style: none;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
.cb-board__empty {
|
|
1541
|
+
padding: var(--cb-space-4) var(--cb-space-3);
|
|
1542
|
+
border: var(--cb-border-width) dashed var(--cb-border-strong);
|
|
1543
|
+
border-radius: var(--cb-radius-md);
|
|
1544
|
+
text-align: center;
|
|
1545
|
+
font-size: var(--cb-text-xs);
|
|
1546
|
+
color: var(--cb-fg-muted);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
/* ── lanes: the phone rendering, chosen rather than a squeezed board ── */
|
|
1550
|
+
|
|
1551
|
+
.cb-board__lanes {
|
|
1552
|
+
display: flex;
|
|
1553
|
+
gap: var(--cb-space-1);
|
|
1554
|
+
overflow-x: auto;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
.cb-board__lane {
|
|
1558
|
+
display: inline-flex;
|
|
1559
|
+
align-items: center;
|
|
1560
|
+
gap: var(--cb-space-2);
|
|
1561
|
+
min-block-size: var(--cb-control-floor);
|
|
1562
|
+
padding: 0 var(--cb-space-3);
|
|
1563
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
1564
|
+
border-radius: var(--cb-radius-full);
|
|
1565
|
+
background: var(--cb-bg);
|
|
1566
|
+
color: var(--cb-fg-muted);
|
|
1567
|
+
font: inherit;
|
|
1568
|
+
font-size: var(--cb-text-sm);
|
|
1569
|
+
cursor: pointer;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
.cb-board__lane[aria-selected='true'] {
|
|
1573
|
+
background: color-mix(in oklch, var(--cb-tone-brand) 12%, var(--cb-bg));
|
|
1574
|
+
border-color: var(--cb-tone-brand);
|
|
1575
|
+
color: var(--cb-fg);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
.cb-board__lane:focus-visible {
|
|
1579
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
1580
|
+
outline-offset: var(--cb-focus-offset);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
/* ── undo ── */
|
|
1584
|
+
|
|
1585
|
+
.cb-board__undo button {
|
|
1586
|
+
min-block-size: var(--cb-control-floor);
|
|
1587
|
+
padding: 0 var(--cb-space-3);
|
|
1588
|
+
border: var(--cb-border-width) solid var(--cb-border);
|
|
1589
|
+
border-radius: var(--cb-radius-md);
|
|
1590
|
+
background: var(--cb-bg);
|
|
1591
|
+
color: var(--cb-fg-link);
|
|
1592
|
+
font: inherit;
|
|
1593
|
+
font-size: var(--cb-text-sm);
|
|
1594
|
+
cursor: pointer;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
.cb-board__undo button:focus-visible {
|
|
1598
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
1599
|
+
outline-offset: var(--cb-focus-offset);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/* The announcement is for a screen reader, not the page. */
|
|
1603
|
+
.cb-board__live {
|
|
1604
|
+
position: absolute;
|
|
1605
|
+
inline-size: 1px;
|
|
1606
|
+
block-size: 1px;
|
|
1607
|
+
margin: -1px;
|
|
1608
|
+
padding: 0;
|
|
1609
|
+
overflow: hidden;
|
|
1610
|
+
clip-path: inset(50%);
|
|
1611
|
+
white-space: nowrap;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/* ── skeleton ── */
|
|
1615
|
+
|
|
1616
|
+
.cb-board__ghost,
|
|
1617
|
+
.cb-board__ghost-card {
|
|
1618
|
+
border-radius: var(--cb-radius-md);
|
|
1619
|
+
background: color-mix(in oklch, var(--cb-fg-subtle) 18%, transparent);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.cb-board__ghost--name { inline-size: var(--cb-space-8); block-size: var(--cb-space-3); }
|
|
1623
|
+
.cb-board__ghost--count { inline-size: var(--cb-space-4); block-size: var(--cb-space-3); }
|
|
1624
|
+
.cb-board__ghost-card { block-size: var(--cb-space-8); border: 0; box-shadow: var(--cb-shadow-none); }
|
|
1625
|
+
|
|
1626
|
+
/* ── renderings that are not decoration ── */
|
|
1627
|
+
|
|
1628
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1629
|
+
.cb-board__card { transition: none; }
|
|
1630
|
+
/* The lift's tilt is decoration; the shadow that says "this one is moving" stays. */
|
|
1631
|
+
.cb-board__card--lift { transform: none; }
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
.cb-board[data-motion='off'] .cb-board__card { transition: none; }
|
|
1635
|
+
.cb-board[data-motion='off'] .cb-board__card--lift { transform: none; }
|
|
1636
|
+
|
|
1637
|
+
@media (forced-colors: active) {
|
|
1638
|
+
.cb-board__column,
|
|
1639
|
+
.cb-board__card { border-color: CanvasText; }
|
|
1640
|
+
.cb-board__marker { background: Highlight; }
|
|
1641
|
+
.cb-board__card[data-held] { outline: var(--cb-focus-width) solid Highlight; }
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/* A card that carries its own buttons puts the grab on a handle instead of the card. A card that is
|
|
1645
|
+
itself a control has presentational children, so anything interactive inside it leaves the
|
|
1646
|
+
accessibility tree — the handle is what keeps those buttons reachable. */
|
|
1647
|
+
.cb-board__card[data-handle] {
|
|
1648
|
+
cursor: default;
|
|
1649
|
+
display: grid;
|
|
1650
|
+
grid-template-columns: auto 1fr;
|
|
1651
|
+
grid-template-areas: 'handle title' '. meta';
|
|
1652
|
+
column-gap: var(--cb-space-2);
|
|
1653
|
+
align-items: start;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
.cb-board__card[data-handle] .cb-board__title { grid-area: title; }
|
|
1657
|
+
.cb-board__card[data-handle] .cb-board__meta { grid-area: meta; }
|
|
1658
|
+
|
|
1659
|
+
.cb-board__handle {
|
|
1660
|
+
grid-area: handle;
|
|
1661
|
+
display: inline-flex;
|
|
1662
|
+
align-items: center;
|
|
1663
|
+
justify-content: center;
|
|
1664
|
+
min-inline-size: var(--cb-space-5);
|
|
1665
|
+
min-block-size: var(--cb-space-5);
|
|
1666
|
+
padding: 0;
|
|
1667
|
+
border: 0;
|
|
1668
|
+
border-radius: var(--cb-radius-sm);
|
|
1669
|
+
background: transparent;
|
|
1670
|
+
color: var(--cb-fg-subtle);
|
|
1671
|
+
font: inherit;
|
|
1672
|
+
line-height: var(--cb-leading-tight);
|
|
1673
|
+
cursor: grab;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
.cb-board__handle:focus-visible {
|
|
1677
|
+
outline: var(--cb-focus-width) solid var(--cb-tone-brand);
|
|
1678
|
+
outline-offset: var(--cb-focus-offset);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
.cb-board__handle:disabled { cursor: not-allowed; }
|
|
1682
|
+
|
|
1363
1683
|
/* data/diagram/diagram.css */
|
|
1364
1684
|
/* Diagram and DiagramEditor run on React Flow. Its base stylesheet (bundled before this file) owns layout and
|
|
1365
1685
|
reads its colours from `--xy-*` custom properties; here those are pointed at Tokens, and the node React
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ceebee/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Ceebee's design system: tokens, themed primitives, motion, and onboarding.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -55,6 +55,9 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@ant-design/cssinjs": "2.1.2",
|
|
58
|
+
"@dnd-kit/core": "6.3.1",
|
|
59
|
+
"@dnd-kit/sortable": "10.0.0",
|
|
60
|
+
"@dnd-kit/utilities": "3.2.2",
|
|
58
61
|
"@xyflow/react": "12.11.6",
|
|
59
62
|
"antd": "6.6.1",
|
|
60
63
|
"dayjs": "1.11.18"
|