@pixpilot/shadcn-kanban 0.0.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/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/AddColumnButton.cjs +82 -0
- package/dist/AddColumnButton.js +77 -0
- package/dist/ColumnFilterButton.cjs +68 -0
- package/dist/ColumnFilterButton.js +63 -0
- package/dist/KanbanBoard.cjs +137 -0
- package/dist/KanbanBoard.d.cts +36 -0
- package/dist/KanbanBoard.d.ts +36 -0
- package/dist/KanbanBoard.js +131 -0
- package/dist/KanbanColumn.cjs +142 -0
- package/dist/KanbanColumn.js +134 -0
- package/dist/KanbanColumnCards.cjs +25 -0
- package/dist/KanbanColumnCards.js +23 -0
- package/dist/KanbanDragOverlay.cjs +42 -0
- package/dist/KanbanDragOverlay.js +37 -0
- package/dist/KanbanItem.cjs +58 -0
- package/dist/KanbanItem.js +52 -0
- package/dist/KanbanVirtualColumnCards.cjs +70 -0
- package/dist/KanbanVirtualColumnCards.js +66 -0
- package/dist/_virtual/rolldown_runtime.cjs +25 -0
- package/dist/hooks/use-kanban-board-state.cjs +43 -0
- package/dist/hooks/use-kanban-board-state.js +41 -0
- package/dist/hooks/use-kanban-card-drag.cjs +83 -0
- package/dist/hooks/use-kanban-card-drag.js +81 -0
- package/dist/hooks/use-kanban-collision-detection.cjs +70 -0
- package/dist/hooks/use-kanban-collision-detection.js +67 -0
- package/dist/hooks/use-kanban-column-reorder.cjs +31 -0
- package/dist/hooks/use-kanban-column-reorder.js +28 -0
- package/dist/hooks/use-kanban-drag.cjs +98 -0
- package/dist/hooks/use-kanban-drag.js +95 -0
- package/dist/hooks/use-kanban-filters.cjs +49 -0
- package/dist/hooks/use-kanban-filters.js +47 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/infinite-scroll/InfiniteScrollSentinel.cjs +54 -0
- package/dist/infinite-scroll/InfiniteScrollSentinel.d.cts +2 -0
- package/dist/infinite-scroll/InfiniteScrollSentinel.d.ts +2 -0
- package/dist/infinite-scroll/InfiniteScrollSentinel.js +49 -0
- package/dist/infinite-scroll/index.d.cts +3 -0
- package/dist/infinite-scroll/index.d.ts +3 -0
- package/dist/infinite-scroll/types.d.cts +29 -0
- package/dist/infinite-scroll/types.d.ts +29 -0
- package/dist/infinite-scroll/use-intersection-observer.cjs +46 -0
- package/dist/infinite-scroll/use-intersection-observer.d.cts +1 -0
- package/dist/infinite-scroll/use-intersection-observer.d.ts +1 -0
- package/dist/infinite-scroll/use-intersection-observer.js +44 -0
- package/dist/types.d.cts +251 -0
- package/dist/types.d.ts +251 -0
- package/dist/utils/apply-kanban-drop.cjs +37 -0
- package/dist/utils/apply-kanban-drop.js +35 -0
- package/dist/utils/column-sortable-id.cjs +23 -0
- package/dist/utils/column-sortable-id.js +20 -0
- package/dist/utils/is-below-over-item.cjs +17 -0
- package/dist/utils/is-below-over-item.js +16 -0
- package/dist/utils/items-order-equal.cjs +13 -0
- package/dist/utils/items-order-equal.js +12 -0
- package/dist/utils/kanban-keyboard-coordinates.cjs +64 -0
- package/dist/utils/kanban-keyboard-coordinates.js +62 -0
- package/dist/utils/move-kanban-item.cjs +28 -0
- package/dist/utils/move-kanban-item.js +27 -0
- package/dist/utils/replace-state.cjs +15 -0
- package/dist/utils/replace-state.js +14 -0
- package/dist/utils/resolve-column-id.cjs +25 -0
- package/dist/utils/resolve-column-id.js +24 -0
- package/package.json +97 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let __dnd_kit_core = require("@dnd-kit/core");
|
|
3
|
+
__dnd_kit_core = require_rolldown_runtime.__toESM(__dnd_kit_core);
|
|
4
|
+
|
|
5
|
+
//#region src/utils/kanban-keyboard-coordinates.ts
|
|
6
|
+
/** Which candidates an arrow key may move onto, relative to the dragged rect. */
|
|
7
|
+
const DIRECTION_FILTERS = {
|
|
8
|
+
[__dnd_kit_core.KeyboardCode.Down]: (collisionRect, rect) => collisionRect.top < rect.top,
|
|
9
|
+
[__dnd_kit_core.KeyboardCode.Up]: (collisionRect, rect) => collisionRect.top > rect.top,
|
|
10
|
+
[__dnd_kit_core.KeyboardCode.Left]: (collisionRect, rect) => collisionRect.left > rect.left,
|
|
11
|
+
[__dnd_kit_core.KeyboardCode.Right]: (collisionRect, rect) => collisionRect.left < rect.left
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Keyboard coordinate getter for the Kanban board, adapted from dnd-kit's
|
|
15
|
+
* multiple-containers example.
|
|
16
|
+
*
|
|
17
|
+
* The stock `sortableKeyboardCoordinates` also targets the dragged card's own
|
|
18
|
+
* column drop zone, whose left edge sits just beside the card, so arrow keys
|
|
19
|
+
* could never leave the column. Here cards target other cards and empty
|
|
20
|
+
* columns only, and column drags target other columns.
|
|
21
|
+
*/
|
|
22
|
+
const kanbanKeyboardCoordinates = (event, { context }) => {
|
|
23
|
+
const matchesDirection = DIRECTION_FILTERS[event.code];
|
|
24
|
+
if (!matchesDirection) return void 0;
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
const { active, collisionRect, droppableRects, droppableContainers } = context;
|
|
27
|
+
if (!active || !collisionRect) return void 0;
|
|
28
|
+
const draggingColumn = active.data.current?.type === "column-sortable";
|
|
29
|
+
const enabled = droppableContainers.getEnabled();
|
|
30
|
+
const columnHasCards = (columnId) => enabled.some((entry) => {
|
|
31
|
+
const data = entry.data.current;
|
|
32
|
+
return data?.type === "item" && data.item?.columnId === columnId;
|
|
33
|
+
});
|
|
34
|
+
const isCandidate = (entry) => {
|
|
35
|
+
if (entry.id === active.id) return false;
|
|
36
|
+
const type = entry.data.current?.type;
|
|
37
|
+
if (draggingColumn) return type === "column-sortable";
|
|
38
|
+
if (type === "column-sortable") return false;
|
|
39
|
+
return !(type === "column" && columnHasCards(String(entry.id)));
|
|
40
|
+
};
|
|
41
|
+
const candidates = enabled.reduce((accumulator, container) => {
|
|
42
|
+
const rect = droppableRects.get(container.id);
|
|
43
|
+
if (rect && isCandidate(container) && matchesDirection(collisionRect, rect)) accumulator.push({
|
|
44
|
+
container,
|
|
45
|
+
rect
|
|
46
|
+
});
|
|
47
|
+
return accumulator;
|
|
48
|
+
}, []);
|
|
49
|
+
const closestId = (0, __dnd_kit_core.getFirstCollision)((0, __dnd_kit_core.closestCenter)({
|
|
50
|
+
active,
|
|
51
|
+
collisionRect,
|
|
52
|
+
droppableRects,
|
|
53
|
+
droppableContainers: candidates.map(({ container }) => container),
|
|
54
|
+
pointerCoordinates: null
|
|
55
|
+
}), "id");
|
|
56
|
+
const winner = candidates.find(({ container }) => container.id === closestId);
|
|
57
|
+
return winner ? {
|
|
58
|
+
x: winner.rect.left,
|
|
59
|
+
y: winner.rect.top
|
|
60
|
+
} : void 0;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
exports.kanbanKeyboardCoordinates = kanbanKeyboardCoordinates;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { KeyboardCode, closestCenter, getFirstCollision } from "@dnd-kit/core";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/kanban-keyboard-coordinates.ts
|
|
4
|
+
/** Which candidates an arrow key may move onto, relative to the dragged rect. */
|
|
5
|
+
const DIRECTION_FILTERS = {
|
|
6
|
+
[KeyboardCode.Down]: (collisionRect, rect) => collisionRect.top < rect.top,
|
|
7
|
+
[KeyboardCode.Up]: (collisionRect, rect) => collisionRect.top > rect.top,
|
|
8
|
+
[KeyboardCode.Left]: (collisionRect, rect) => collisionRect.left > rect.left,
|
|
9
|
+
[KeyboardCode.Right]: (collisionRect, rect) => collisionRect.left < rect.left
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Keyboard coordinate getter for the Kanban board, adapted from dnd-kit's
|
|
13
|
+
* multiple-containers example.
|
|
14
|
+
*
|
|
15
|
+
* The stock `sortableKeyboardCoordinates` also targets the dragged card's own
|
|
16
|
+
* column drop zone, whose left edge sits just beside the card, so arrow keys
|
|
17
|
+
* could never leave the column. Here cards target other cards and empty
|
|
18
|
+
* columns only, and column drags target other columns.
|
|
19
|
+
*/
|
|
20
|
+
const kanbanKeyboardCoordinates = (event, { context }) => {
|
|
21
|
+
const matchesDirection = DIRECTION_FILTERS[event.code];
|
|
22
|
+
if (!matchesDirection) return void 0;
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
const { active, collisionRect, droppableRects, droppableContainers } = context;
|
|
25
|
+
if (!active || !collisionRect) return void 0;
|
|
26
|
+
const draggingColumn = active.data.current?.type === "column-sortable";
|
|
27
|
+
const enabled = droppableContainers.getEnabled();
|
|
28
|
+
const columnHasCards = (columnId) => enabled.some((entry) => {
|
|
29
|
+
const data = entry.data.current;
|
|
30
|
+
return data?.type === "item" && data.item?.columnId === columnId;
|
|
31
|
+
});
|
|
32
|
+
const isCandidate = (entry) => {
|
|
33
|
+
if (entry.id === active.id) return false;
|
|
34
|
+
const type = entry.data.current?.type;
|
|
35
|
+
if (draggingColumn) return type === "column-sortable";
|
|
36
|
+
if (type === "column-sortable") return false;
|
|
37
|
+
return !(type === "column" && columnHasCards(String(entry.id)));
|
|
38
|
+
};
|
|
39
|
+
const candidates = enabled.reduce((accumulator, container) => {
|
|
40
|
+
const rect = droppableRects.get(container.id);
|
|
41
|
+
if (rect && isCandidate(container) && matchesDirection(collisionRect, rect)) accumulator.push({
|
|
42
|
+
container,
|
|
43
|
+
rect
|
|
44
|
+
});
|
|
45
|
+
return accumulator;
|
|
46
|
+
}, []);
|
|
47
|
+
const closestId = getFirstCollision(closestCenter({
|
|
48
|
+
active,
|
|
49
|
+
collisionRect,
|
|
50
|
+
droppableRects,
|
|
51
|
+
droppableContainers: candidates.map(({ container }) => container),
|
|
52
|
+
pointerCoordinates: null
|
|
53
|
+
}), "id");
|
|
54
|
+
const winner = candidates.find(({ container }) => container.id === closestId);
|
|
55
|
+
return winner ? {
|
|
56
|
+
x: winner.rect.left,
|
|
57
|
+
y: winner.rect.top
|
|
58
|
+
} : void 0;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { kanbanKeyboardCoordinates };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/move-kanban-item.ts
|
|
3
|
+
/**
|
|
4
|
+
* Returns a new item list with one card placed relative to the current drop target.
|
|
5
|
+
* The input is never mutated, so previews and final drops can use the same drag snapshot.
|
|
6
|
+
*/
|
|
7
|
+
function moveKanbanItem({ items, activeId, overId, targetColumnId, insertAfter, overIsColumn }) {
|
|
8
|
+
const activeItem = items.find((item) => item.id === activeId);
|
|
9
|
+
if (!activeItem) return items;
|
|
10
|
+
const next = items.filter((item) => item.id !== activeId);
|
|
11
|
+
const movedItem = {
|
|
12
|
+
...activeItem,
|
|
13
|
+
columnId: targetColumnId
|
|
14
|
+
};
|
|
15
|
+
if (!overIsColumn) {
|
|
16
|
+
const overIndex = next.findIndex((item) => item.id === overId);
|
|
17
|
+
if (overIndex !== -1) {
|
|
18
|
+
next.splice(overIndex + Number(insertAfter), 0, movedItem);
|
|
19
|
+
return next;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const lastIndexInColumn = next.reduce((last, item, index) => item.columnId === targetColumnId ? index : last, -1);
|
|
23
|
+
next.splice(lastIndexInColumn === -1 ? next.length : lastIndexInColumn + 1, 0, movedItem);
|
|
24
|
+
return next;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
exports.moveKanbanItem = moveKanbanItem;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/utils/move-kanban-item.ts
|
|
2
|
+
/**
|
|
3
|
+
* Returns a new item list with one card placed relative to the current drop target.
|
|
4
|
+
* The input is never mutated, so previews and final drops can use the same drag snapshot.
|
|
5
|
+
*/
|
|
6
|
+
function moveKanbanItem({ items, activeId, overId, targetColumnId, insertAfter, overIsColumn }) {
|
|
7
|
+
const activeItem = items.find((item) => item.id === activeId);
|
|
8
|
+
if (!activeItem) return items;
|
|
9
|
+
const next = items.filter((item) => item.id !== activeId);
|
|
10
|
+
const movedItem = {
|
|
11
|
+
...activeItem,
|
|
12
|
+
columnId: targetColumnId
|
|
13
|
+
};
|
|
14
|
+
if (!overIsColumn) {
|
|
15
|
+
const overIndex = next.findIndex((item) => item.id === overId);
|
|
16
|
+
if (overIndex !== -1) {
|
|
17
|
+
next.splice(overIndex + Number(insertAfter), 0, movedItem);
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const lastIndexInColumn = next.reduce((last, item, index) => item.columnId === targetColumnId ? index : last, -1);
|
|
22
|
+
next.splice(lastIndexInColumn === -1 ? next.length : lastIndexInColumn + 1, 0, movedItem);
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { moveKanbanItem };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/replace-state.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reducer for controlled values that are replaced wholesale.
|
|
5
|
+
*
|
|
6
|
+
* Used with `useReducer` instead of `useState` so prop mirroring happens via a
|
|
7
|
+
* dispatch, which React treats as an ordinary update even when triggered from
|
|
8
|
+
* an effect during a drag.
|
|
9
|
+
*/
|
|
10
|
+
function replaceState(_current, next) {
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
exports.replaceState = replaceState;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/utils/replace-state.ts
|
|
2
|
+
/**
|
|
3
|
+
* Reducer for controlled values that are replaced wholesale.
|
|
4
|
+
*
|
|
5
|
+
* Used with `useReducer` instead of `useState` so prop mirroring happens via a
|
|
6
|
+
* dispatch, which React treats as an ordinary update even when triggered from
|
|
7
|
+
* an effect during a drag.
|
|
8
|
+
*/
|
|
9
|
+
function replaceState(_current, next) {
|
|
10
|
+
return next;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { replaceState };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const require_column_sortable_id = require('./column-sortable-id.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/utils/resolve-column-id.ts
|
|
4
|
+
/** Whether an id addresses a column as a whole rather than a single card. */
|
|
5
|
+
function isColumnDroppableId(id, columnIds) {
|
|
6
|
+
return columnIds.has(String(id)) || require_column_sortable_id.isColumnSortableId(id);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolves any droppable id — a card, a column drop zone, or a column-sortable
|
|
10
|
+
* handle — to the column it belongs to. Returns `undefined` for ids that match
|
|
11
|
+
* nothing on the board (e.g. a card that was removed mid-drag).
|
|
12
|
+
*/
|
|
13
|
+
function resolveColumnId(id, columnIds, items) {
|
|
14
|
+
const key = String(id);
|
|
15
|
+
if (columnIds.has(key)) return key;
|
|
16
|
+
if (require_column_sortable_id.isColumnSortableId(id)) {
|
|
17
|
+
const columnId = require_column_sortable_id.extractColumnId(id);
|
|
18
|
+
return columnIds.has(columnId) ? columnId : void 0;
|
|
19
|
+
}
|
|
20
|
+
return items.find((item) => item.id === id)?.columnId;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.isColumnDroppableId = isColumnDroppableId;
|
|
25
|
+
exports.resolveColumnId = resolveColumnId;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { extractColumnId, isColumnSortableId } from "./column-sortable-id.js";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/resolve-column-id.ts
|
|
4
|
+
/** Whether an id addresses a column as a whole rather than a single card. */
|
|
5
|
+
function isColumnDroppableId(id, columnIds) {
|
|
6
|
+
return columnIds.has(String(id)) || isColumnSortableId(id);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolves any droppable id — a card, a column drop zone, or a column-sortable
|
|
10
|
+
* handle — to the column it belongs to. Returns `undefined` for ids that match
|
|
11
|
+
* nothing on the board (e.g. a card that was removed mid-drag).
|
|
12
|
+
*/
|
|
13
|
+
function resolveColumnId(id, columnIds, items) {
|
|
14
|
+
const key = String(id);
|
|
15
|
+
if (columnIds.has(key)) return key;
|
|
16
|
+
if (isColumnSortableId(id)) {
|
|
17
|
+
const columnId = extractColumnId(id);
|
|
18
|
+
return columnIds.has(columnId) ? columnId : void 0;
|
|
19
|
+
}
|
|
20
|
+
return items.find((item) => item.id === id)?.columnId;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { isColumnDroppableId, resolveColumnId };
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pixpilot/shadcn-kanban",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"packageManager": "pnpm@10.33.0",
|
|
6
|
+
"description": "Accessible drag-and-drop Kanban board built with shadcn/ui and dnd-kit.",
|
|
7
|
+
"author": "m.doaie <m.doaie@hotmail.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/pixpilot/shadcn-components",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/pixpilot/shadcn-components.git",
|
|
13
|
+
"directory": "packages/shadcn-kanban"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/pixpilot/shadcn-components/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"kanban",
|
|
20
|
+
"board",
|
|
21
|
+
"drag-and-drop",
|
|
22
|
+
"dnd-kit",
|
|
23
|
+
"shadcn",
|
|
24
|
+
"react"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": "./src/index.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js",
|
|
37
|
+
"require": "./dist/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"main": "./dist/index.cjs",
|
|
41
|
+
"module": "./dist/index.js",
|
|
42
|
+
"types": "./dist/index.d.ts"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"prepublishOnly": "pnpm run clean && pnpm run build",
|
|
46
|
+
"clean": "git clean -xdf .cache .turbo dist",
|
|
47
|
+
"clean:all": "pnpm clean && git clean -xdf node_modules",
|
|
48
|
+
"build": "tsdown",
|
|
49
|
+
"build:watch": "tsdown --watch",
|
|
50
|
+
"test": "vitest --run --passWithNoTests",
|
|
51
|
+
"test:watch": "vitest --watch",
|
|
52
|
+
"test:ui": "vitest --ui --watch",
|
|
53
|
+
"test:ui:coverage": "vitest --ui --coverage --watch --hookTimeout=30000 --coverage.thresholds.lines=0 --coverage.thresholds.functions=0 --coverage.thresholds.branches=0 --coverage.thresholds.statements=0",
|
|
54
|
+
"test:coverage": "vitest --coverage",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"lint": "eslint",
|
|
57
|
+
"format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ../../.prettierignore"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"react": "^18 || ^19",
|
|
61
|
+
"react-dom": "^18 || ^19"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@dnd-kit/core": "^6.3.1",
|
|
65
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
66
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
67
|
+
"@pixpilot/shadcn-ui": "workspace:*",
|
|
68
|
+
"@tanstack/react-virtual": "^3.13.23",
|
|
69
|
+
"lucide-react": "catalog:prod"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@internal/eslint-config": "workspace:*",
|
|
73
|
+
"@internal/prettier-config": "workspace:*",
|
|
74
|
+
"@internal/tsconfig": "workspace:*",
|
|
75
|
+
"@internal/tsdown-config": "workspace:*",
|
|
76
|
+
"@internal/vitest-config": "workspace:*",
|
|
77
|
+
"@storybook/react": "catalog:dev",
|
|
78
|
+
"@testing-library/jest-dom": "catalog:dev",
|
|
79
|
+
"@testing-library/react": "catalog:dev",
|
|
80
|
+
"@types/node": "catalog:dev",
|
|
81
|
+
"@types/react": "catalog:dev",
|
|
82
|
+
"@types/react-dom": "catalog:dev",
|
|
83
|
+
"@vitest/coverage-v8": "catalog:dev",
|
|
84
|
+
"@vitest/ui": "catalog:dev",
|
|
85
|
+
"eslint": "catalog:dev",
|
|
86
|
+
"jsdom": "catalog:dev",
|
|
87
|
+
"postcss": "catalog:dev",
|
|
88
|
+
"react": "catalog:prod",
|
|
89
|
+
"react-dom": "catalog:prod",
|
|
90
|
+
"tailwindcss": "catalog:dev",
|
|
91
|
+
"tsdown": "catalog:dev",
|
|
92
|
+
"typescript": "catalog:dev",
|
|
93
|
+
"typescript-eslint": "catalog:dev",
|
|
94
|
+
"vitest": "catalog:dev"
|
|
95
|
+
},
|
|
96
|
+
"prettier": "@internal/prettier-config"
|
|
97
|
+
}
|