@particle-academy/react-fancy 2.7.0 → 2.8.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/index.cjs +275 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -13
- package/dist/index.d.ts +87 -13
- package/dist/index.js +275 -33
- package/dist/index.js.map +1 -1
- package/docs/AccordionPanel.md +6 -0
- package/docs/Kanban.md +115 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11113,35 +11113,151 @@ var KanbanColumnContext = createContext("");
|
|
|
11113
11113
|
function useKanbanColumn() {
|
|
11114
11114
|
return useContext(KanbanColumnContext);
|
|
11115
11115
|
}
|
|
11116
|
+
var DEFAULT_CARD_CLASSES = "rounded-lg border border-zinc-200 bg-white p-3 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-700 dark:bg-zinc-900";
|
|
11117
|
+
function KanbanCard({ children, id, className, unstyled }) {
|
|
11118
|
+
const { setDraggedCard, setDragSource } = useKanban();
|
|
11119
|
+
const columnId = useKanbanColumn();
|
|
11120
|
+
const handleDragStart = useCallback(() => {
|
|
11121
|
+
setDraggedCard(id);
|
|
11122
|
+
setDragSource(columnId);
|
|
11123
|
+
}, [id, columnId, setDraggedCard, setDragSource]);
|
|
11124
|
+
const handleDragEnd = useCallback(() => {
|
|
11125
|
+
setDraggedCard(null);
|
|
11126
|
+
setDragSource(null);
|
|
11127
|
+
}, [setDraggedCard, setDragSource]);
|
|
11128
|
+
return /* @__PURE__ */ jsx(
|
|
11129
|
+
"div",
|
|
11130
|
+
{
|
|
11131
|
+
"data-react-fancy-kanban-card": "",
|
|
11132
|
+
draggable: true,
|
|
11133
|
+
onDragStart: handleDragStart,
|
|
11134
|
+
onDragEnd: handleDragEnd,
|
|
11135
|
+
className: cn(
|
|
11136
|
+
// Drag affordance — kept even when unstyled so users still see grab cursors.
|
|
11137
|
+
"cursor-grab active:cursor-grabbing",
|
|
11138
|
+
!unstyled && DEFAULT_CARD_CLASSES,
|
|
11139
|
+
className
|
|
11140
|
+
),
|
|
11141
|
+
children
|
|
11142
|
+
}
|
|
11143
|
+
);
|
|
11144
|
+
}
|
|
11145
|
+
KanbanCard.displayName = "KanbanCard";
|
|
11116
11146
|
var DEFAULT_COLUMN_CLASSES = "min-h-[200px] w-72 rounded-xl bg-zinc-50 p-3 dark:bg-zinc-800/50";
|
|
11147
|
+
function countCardChildren(children) {
|
|
11148
|
+
let n = 0;
|
|
11149
|
+
Children.forEach(children, (child) => {
|
|
11150
|
+
if (!isValidElement(child)) return;
|
|
11151
|
+
if (child.type === KanbanCard) {
|
|
11152
|
+
n += 1;
|
|
11153
|
+
return;
|
|
11154
|
+
}
|
|
11155
|
+
if (child.type === Fragment$1) {
|
|
11156
|
+
n += countCardChildren(child.props.children);
|
|
11157
|
+
}
|
|
11158
|
+
});
|
|
11159
|
+
return n;
|
|
11160
|
+
}
|
|
11117
11161
|
function KanbanColumn({
|
|
11118
11162
|
children,
|
|
11119
11163
|
id,
|
|
11120
11164
|
title,
|
|
11121
11165
|
className,
|
|
11122
|
-
unstyled
|
|
11166
|
+
unstyled,
|
|
11167
|
+
wipLimit,
|
|
11168
|
+
hideWhenEmpty
|
|
11123
11169
|
}) {
|
|
11124
|
-
const { onCardMove, draggedCard, dragSource } = useKanban();
|
|
11170
|
+
const { onCardMove, draggedCard, dragSource, registerColumn } = useKanban();
|
|
11125
11171
|
const [dragOver, setDragOver] = useState(false);
|
|
11172
|
+
const [dropIndex, setDropIndex] = useState(null);
|
|
11173
|
+
const cardsRef = useRef(null);
|
|
11174
|
+
useEffect(() => registerColumn(id), [id, registerColumn]);
|
|
11175
|
+
const updateDropIndex = useCallback((clientY) => {
|
|
11176
|
+
const container = cardsRef.current;
|
|
11177
|
+
if (!container) {
|
|
11178
|
+
setDropIndex(null);
|
|
11179
|
+
return;
|
|
11180
|
+
}
|
|
11181
|
+
const cards = container.querySelectorAll(
|
|
11182
|
+
":scope > [data-react-fancy-kanban-card]"
|
|
11183
|
+
);
|
|
11184
|
+
let idx = cards.length;
|
|
11185
|
+
for (let i = 0; i < cards.length; i++) {
|
|
11186
|
+
const rect = cards[i].getBoundingClientRect();
|
|
11187
|
+
if (clientY < rect.top + rect.height / 2) {
|
|
11188
|
+
idx = i;
|
|
11189
|
+
break;
|
|
11190
|
+
}
|
|
11191
|
+
}
|
|
11192
|
+
setDropIndex(idx);
|
|
11193
|
+
}, []);
|
|
11194
|
+
const handleDragOver = useCallback(
|
|
11195
|
+
(e) => {
|
|
11196
|
+
if (!draggedCard) return;
|
|
11197
|
+
e.preventDefault();
|
|
11198
|
+
e.stopPropagation();
|
|
11199
|
+
setDragOver(true);
|
|
11200
|
+
updateDropIndex(e.clientY);
|
|
11201
|
+
},
|
|
11202
|
+
[draggedCard, updateDropIndex]
|
|
11203
|
+
);
|
|
11204
|
+
const handleDragLeave = useCallback((e) => {
|
|
11205
|
+
if (e.currentTarget.contains(e.relatedTarget)) return;
|
|
11206
|
+
setDragOver(false);
|
|
11207
|
+
setDropIndex(null);
|
|
11208
|
+
}, []);
|
|
11126
11209
|
const handleDrop = useCallback(
|
|
11127
11210
|
(e) => {
|
|
11211
|
+
if (!draggedCard) return;
|
|
11128
11212
|
e.preventDefault();
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11213
|
+
e.stopPropagation();
|
|
11214
|
+
const target = dropIndex ?? 0;
|
|
11215
|
+
if (dragSource && draggedCard) {
|
|
11216
|
+
let finalIdx = target;
|
|
11217
|
+
if (dragSource === id) {
|
|
11218
|
+
const srcIdx = findCardIndex(children, draggedCard);
|
|
11219
|
+
if (srcIdx !== -1 && target > srcIdx) {
|
|
11220
|
+
finalIdx = target - 1;
|
|
11221
|
+
}
|
|
11222
|
+
if (srcIdx === finalIdx) {
|
|
11223
|
+
setDragOver(false);
|
|
11224
|
+
setDropIndex(null);
|
|
11225
|
+
return;
|
|
11226
|
+
}
|
|
11227
|
+
}
|
|
11228
|
+
onCardMove?.(draggedCard, dragSource, id, finalIdx);
|
|
11132
11229
|
}
|
|
11230
|
+
setDragOver(false);
|
|
11231
|
+
setDropIndex(null);
|
|
11133
11232
|
},
|
|
11134
|
-
[draggedCard, dragSource, id, onCardMove]
|
|
11233
|
+
[draggedCard, dragSource, dropIndex, id, onCardMove, children]
|
|
11135
11234
|
);
|
|
11136
|
-
const
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
}
|
|
11140
|
-
|
|
11235
|
+
const cardCount = countCardChildren(children);
|
|
11236
|
+
if (hideWhenEmpty && cardCount === 0 && !draggedCard) {
|
|
11237
|
+
return null;
|
|
11238
|
+
}
|
|
11239
|
+
let cardSeen = 0;
|
|
11240
|
+
const showIndicator = draggedCard !== null && dropIndex !== null && dragOver;
|
|
11241
|
+
const renderedChildren = Children.toArray(children).map((child, i) => {
|
|
11242
|
+
const isCard = isValidElement(child) && child.type === KanbanCard;
|
|
11243
|
+
const indicator = showIndicator && isCard && cardSeen === dropIndex ? /* @__PURE__ */ jsx(DropIndicator, {}, `drop-${i}`) : null;
|
|
11244
|
+
if (isCard) cardSeen += 1;
|
|
11245
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
11246
|
+
indicator,
|
|
11247
|
+
child
|
|
11248
|
+
] }, i);
|
|
11249
|
+
});
|
|
11250
|
+
if (showIndicator && dropIndex === cardCount) {
|
|
11251
|
+
renderedChildren.push(/* @__PURE__ */ jsx(DropIndicator, {}, "drop-end"));
|
|
11252
|
+
}
|
|
11253
|
+
const overWip = wipLimit !== void 0 && cardCount > wipLimit;
|
|
11141
11254
|
return /* @__PURE__ */ jsx(KanbanColumnContext.Provider, { value: id, children: /* @__PURE__ */ jsxs(
|
|
11142
11255
|
"div",
|
|
11143
11256
|
{
|
|
11144
11257
|
"data-react-fancy-kanban-column": "",
|
|
11258
|
+
"data-column-id": id,
|
|
11259
|
+
role: "group",
|
|
11260
|
+
"aria-label": title,
|
|
11145
11261
|
onDrop: handleDrop,
|
|
11146
11262
|
onDragOver: handleDragOver,
|
|
11147
11263
|
onDragLeave: handleDragLeave,
|
|
@@ -11152,55 +11268,181 @@ function KanbanColumn({
|
|
|
11152
11268
|
className
|
|
11153
11269
|
),
|
|
11154
11270
|
children: [
|
|
11155
|
-
title && /* @__PURE__ */
|
|
11156
|
-
|
|
11271
|
+
title && /* @__PURE__ */ jsxs("h3", { className: "mb-3 flex items-center gap-2 text-sm font-semibold text-zinc-600 dark:text-zinc-400", children: [
|
|
11272
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: title }),
|
|
11273
|
+
/* @__PURE__ */ jsxs(
|
|
11274
|
+
"span",
|
|
11275
|
+
{
|
|
11276
|
+
className: cn(
|
|
11277
|
+
"rounded-full px-1.5 py-0.5 text-[10px] font-semibold",
|
|
11278
|
+
overWip ? "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300" : "bg-zinc-200 text-zinc-700 dark:bg-zinc-700 dark:text-zinc-300"
|
|
11279
|
+
),
|
|
11280
|
+
children: [
|
|
11281
|
+
cardCount,
|
|
11282
|
+
wipLimit !== void 0 ? `/${wipLimit}` : ""
|
|
11283
|
+
]
|
|
11284
|
+
}
|
|
11285
|
+
)
|
|
11286
|
+
] }),
|
|
11287
|
+
/* @__PURE__ */ jsx("div", { ref: cardsRef, className: "flex flex-1 flex-col gap-2", children: renderedChildren })
|
|
11157
11288
|
]
|
|
11158
11289
|
}
|
|
11159
11290
|
) });
|
|
11160
11291
|
}
|
|
11161
11292
|
KanbanColumn.displayName = "KanbanColumn";
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11293
|
+
function DropIndicator() {
|
|
11294
|
+
return /* @__PURE__ */ jsx(
|
|
11295
|
+
"div",
|
|
11296
|
+
{
|
|
11297
|
+
"data-react-fancy-kanban-drop-indicator": "",
|
|
11298
|
+
className: "h-0.5 -my-1 rounded-full bg-blue-500/80"
|
|
11299
|
+
}
|
|
11300
|
+
);
|
|
11301
|
+
}
|
|
11302
|
+
function findCardIndex(children, cardId) {
|
|
11303
|
+
let idx = -1;
|
|
11304
|
+
let i = 0;
|
|
11305
|
+
Children.forEach(children, (child) => {
|
|
11306
|
+
if (idx !== -1) return;
|
|
11307
|
+
if (!isValidElement(child)) return;
|
|
11308
|
+
if (child.type === KanbanCard) {
|
|
11309
|
+
if (child.props.id === cardId) {
|
|
11310
|
+
idx = i;
|
|
11311
|
+
}
|
|
11312
|
+
i += 1;
|
|
11313
|
+
}
|
|
11314
|
+
});
|
|
11315
|
+
return idx;
|
|
11316
|
+
}
|
|
11317
|
+
function KanbanColumnHandle({
|
|
11318
|
+
children,
|
|
11319
|
+
className
|
|
11320
|
+
}) {
|
|
11321
|
+
const { setDraggedColumn } = useKanban();
|
|
11165
11322
|
const columnId = useKanbanColumn();
|
|
11166
|
-
const handleDragStart = useCallback(
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11323
|
+
const handleDragStart = useCallback(
|
|
11324
|
+
(e) => {
|
|
11325
|
+
e.dataTransfer.effectAllowed = "move";
|
|
11326
|
+
e.dataTransfer.setData("text/plain", columnId);
|
|
11327
|
+
e.stopPropagation();
|
|
11328
|
+
setDraggedColumn(columnId);
|
|
11329
|
+
},
|
|
11330
|
+
[columnId, setDraggedColumn]
|
|
11331
|
+
);
|
|
11170
11332
|
const handleDragEnd = useCallback(() => {
|
|
11171
|
-
|
|
11172
|
-
|
|
11173
|
-
}, [setDraggedCard, setDragSource]);
|
|
11333
|
+
setDraggedColumn(null);
|
|
11334
|
+
}, [setDraggedColumn]);
|
|
11174
11335
|
return /* @__PURE__ */ jsx(
|
|
11175
11336
|
"div",
|
|
11176
11337
|
{
|
|
11177
|
-
"data-react-fancy-kanban-card": "",
|
|
11178
11338
|
draggable: true,
|
|
11179
11339
|
onDragStart: handleDragStart,
|
|
11180
11340
|
onDragEnd: handleDragEnd,
|
|
11341
|
+
"data-react-fancy-kanban-column-handle": "",
|
|
11181
11342
|
className: cn(
|
|
11182
|
-
|
|
11183
|
-
"cursor-grab active:cursor-grabbing",
|
|
11184
|
-
!unstyled && DEFAULT_CARD_CLASSES,
|
|
11343
|
+
"cursor-grab active:cursor-grabbing select-none",
|
|
11185
11344
|
className
|
|
11186
11345
|
),
|
|
11187
11346
|
children
|
|
11188
11347
|
}
|
|
11189
11348
|
);
|
|
11190
11349
|
}
|
|
11191
|
-
|
|
11192
|
-
function KanbanRoot({
|
|
11350
|
+
KanbanColumnHandle.displayName = "KanbanColumnHandle";
|
|
11351
|
+
function KanbanRoot({
|
|
11352
|
+
children,
|
|
11353
|
+
onCardMove,
|
|
11354
|
+
onColumnMove,
|
|
11355
|
+
className
|
|
11356
|
+
}) {
|
|
11193
11357
|
const [draggedCard, setDraggedCard] = useState(null);
|
|
11194
11358
|
const [dragSource, setDragSource] = useState(null);
|
|
11359
|
+
const [draggedColumn, setDraggedColumn] = useState(null);
|
|
11360
|
+
const orderRef = useRef([]);
|
|
11361
|
+
const [columnIds, setColumnIds] = useState([]);
|
|
11362
|
+
const registerColumn = useCallback((id) => {
|
|
11363
|
+
if (!orderRef.current.includes(id)) {
|
|
11364
|
+
orderRef.current = [...orderRef.current, id];
|
|
11365
|
+
setColumnIds(orderRef.current);
|
|
11366
|
+
}
|
|
11367
|
+
return () => {
|
|
11368
|
+
orderRef.current = orderRef.current.filter((x) => x !== id);
|
|
11369
|
+
setColumnIds(orderRef.current);
|
|
11370
|
+
};
|
|
11371
|
+
}, []);
|
|
11195
11372
|
const ctx = useMemo(
|
|
11196
|
-
() => ({
|
|
11197
|
-
|
|
11373
|
+
() => ({
|
|
11374
|
+
draggedCard,
|
|
11375
|
+
setDraggedCard,
|
|
11376
|
+
dragSource,
|
|
11377
|
+
setDragSource,
|
|
11378
|
+
draggedColumn,
|
|
11379
|
+
setDraggedColumn,
|
|
11380
|
+
onCardMove,
|
|
11381
|
+
onColumnMove,
|
|
11382
|
+
columnIds,
|
|
11383
|
+
registerColumn
|
|
11384
|
+
}),
|
|
11385
|
+
[
|
|
11386
|
+
draggedCard,
|
|
11387
|
+
dragSource,
|
|
11388
|
+
draggedColumn,
|
|
11389
|
+
onCardMove,
|
|
11390
|
+
onColumnMove,
|
|
11391
|
+
columnIds,
|
|
11392
|
+
registerColumn
|
|
11393
|
+
]
|
|
11394
|
+
);
|
|
11395
|
+
const containerRef = useRef(null);
|
|
11396
|
+
const handleDragOver = useCallback(
|
|
11397
|
+
(e) => {
|
|
11398
|
+
if (!draggedColumn) return;
|
|
11399
|
+
e.preventDefault();
|
|
11400
|
+
},
|
|
11401
|
+
[draggedColumn]
|
|
11198
11402
|
);
|
|
11199
|
-
|
|
11403
|
+
const handleDrop = useCallback(
|
|
11404
|
+
(e) => {
|
|
11405
|
+
if (!draggedColumn || !containerRef.current) return;
|
|
11406
|
+
e.preventDefault();
|
|
11407
|
+
const cols = containerRef.current.querySelectorAll(
|
|
11408
|
+
"[data-react-fancy-kanban-column]"
|
|
11409
|
+
);
|
|
11410
|
+
const x = e.clientX;
|
|
11411
|
+
let dropIdx = cols.length;
|
|
11412
|
+
for (let i = 0; i < cols.length; i++) {
|
|
11413
|
+
const rect = cols[i].getBoundingClientRect();
|
|
11414
|
+
if (x < rect.left + rect.width / 2) {
|
|
11415
|
+
dropIdx = i;
|
|
11416
|
+
break;
|
|
11417
|
+
}
|
|
11418
|
+
}
|
|
11419
|
+
const sourceIdx = columnIds.indexOf(draggedColumn);
|
|
11420
|
+
const finalIdx = sourceIdx >= 0 && dropIdx > sourceIdx ? dropIdx - 1 : dropIdx;
|
|
11421
|
+
if (finalIdx !== sourceIdx) {
|
|
11422
|
+
onColumnMove?.(draggedColumn, finalIdx);
|
|
11423
|
+
}
|
|
11424
|
+
setDraggedColumn(null);
|
|
11425
|
+
},
|
|
11426
|
+
[draggedColumn, columnIds, onColumnMove]
|
|
11427
|
+
);
|
|
11428
|
+
return /* @__PURE__ */ jsx(KanbanContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
|
|
11429
|
+
"div",
|
|
11430
|
+
{
|
|
11431
|
+
ref: containerRef,
|
|
11432
|
+
"data-react-fancy-kanban": "",
|
|
11433
|
+
onDragOver: handleDragOver,
|
|
11434
|
+
onDrop: handleDrop,
|
|
11435
|
+
role: "application",
|
|
11436
|
+
"aria-roledescription": "kanban board",
|
|
11437
|
+
className: cn("flex gap-4 overflow-x-auto p-4", className),
|
|
11438
|
+
children
|
|
11439
|
+
}
|
|
11440
|
+
) });
|
|
11200
11441
|
}
|
|
11201
11442
|
var Kanban = Object.assign(KanbanRoot, {
|
|
11202
11443
|
Column: KanbanColumn,
|
|
11203
|
-
Card: KanbanCard
|
|
11444
|
+
Card: KanbanCard,
|
|
11445
|
+
ColumnHandle: KanbanColumnHandle
|
|
11204
11446
|
});
|
|
11205
11447
|
var CanvasContext = createContext(null);
|
|
11206
11448
|
function useCanvas() {
|