@ahrowe/ui 0.14.0 → 0.14.1
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/esm/common/interactableDiv/interactableDiv.mjs +1 -1
- package/dist/esm/common/interactableDiv/interactableDiv.mjs.map +1 -1
- package/dist/esm/common/kanbanBoard/KanbanBoard.mjs +6 -1
- package/dist/esm/common/kanbanBoard/KanbanBoard.mjs.map +1 -1
- package/dist/esm/common/kanbanBoard/KanbanColumn.mjs +1 -1
- package/dist/esm/common/kanbanBoard/KanbanColumn.mjs.map +1 -1
- package/dist/esm/common/kanbanBoard/KanbanItem.mjs +1 -1
- package/dist/esm/common/kanbanBoard/KanbanItem.mjs.map +1 -1
- package/dist/esm/common/kanbanBoard/kanbanBoard.module.mjs +1 -1
- package/dist/esm/common/kanbanBoard/kanbanBoard.module.mjs.map +1 -1
- package/dist/esm/common/kanbanBoard/kanbanBoard.utils.mjs +2 -0
- package/dist/esm/common/kanbanBoard/kanbanBoard.utils.mjs.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/package/common/kanbanBoard/KanbanBoard.d.ts +1 -1
- package/dist/types/package/common/kanbanBoard/KanbanColumn.d.ts +29 -1
- package/dist/types/package/common/kanbanBoard/KanbanItem.d.ts +2 -6
- package/dist/types/package/common/kanbanBoard/kanbanBoard.types.d.ts +71 -5
- package/dist/types/package/common/kanbanBoard/kanbanBoard.utils.d.ts +60 -0
- package/dist/types/package/common/themeProvider/theme.types.d.ts +2 -0
- package/docs/KanbanBoard.md +129 -4
- package/package.json +2 -4
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { CSSProperties, ReactNode } from 'react';
|
|
2
|
-
import { SlotClassNames, SlotStyles } from '../types/slots.types';
|
|
2
|
+
import { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';
|
|
3
3
|
export interface KanbanColumnDef {
|
|
4
4
|
id: string;
|
|
5
5
|
title: string;
|
|
6
6
|
itemIds: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Optional work-in-progress limit. When set, the column header shows a `count/maxItems`
|
|
9
|
+
* badge, and the column gets a warning style once `itemIds.length` exceeds it. This is
|
|
10
|
+
* display-only — `KanbanBoard` doesn't block drops on its own. To enforce a hard limit,
|
|
11
|
+
* check it in `onChange` and skip updating your state when a drop would push a column over.
|
|
12
|
+
*/
|
|
13
|
+
maxItems?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Freezes the column: cards inside it can't be picked up, and nothing can be dropped into it.
|
|
16
|
+
* Use for a workflow rule like "cards in Done can't be moved back out."
|
|
17
|
+
*/
|
|
18
|
+
disabled?: boolean;
|
|
7
19
|
}
|
|
8
20
|
export type KanbanBoardSlots = 'dragOverlay';
|
|
9
|
-
export interface KanbanBoardProps<T = unknown> {
|
|
21
|
+
export interface KanbanBoardProps<T = unknown> extends HtmlProps {
|
|
10
22
|
/** Ordered list of column definitions. Each column owns its itemIds. */
|
|
11
23
|
columns: KanbanColumnDef[];
|
|
12
24
|
/** Flat map of all items keyed by their id. */
|
|
@@ -18,18 +30,72 @@ export interface KanbanBoardProps<T = unknown> {
|
|
|
18
30
|
* Receives the full updated columns array — replace your state with this value.
|
|
19
31
|
*/
|
|
20
32
|
onChange: (columns: KanbanColumnDef[]) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Restrict drag initiation to elements marked `data-drag-handle` within a card,
|
|
35
|
+
* instead of the whole card. Use when `renderItem` includes interactive content
|
|
36
|
+
* (buttons, inputs) that would otherwise have its clicks swallowed by the drag listener.
|
|
37
|
+
*/
|
|
38
|
+
dragHandle?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Produce an accessible label for a card, announced to screen-reader users while dragging
|
|
41
|
+
* (e.g. "Picked up <label>."). Defaults to the card's id when omitted.
|
|
42
|
+
*/
|
|
43
|
+
getItemLabel?: (item: T, id: string) => string;
|
|
44
|
+
/**
|
|
45
|
+
* Returns whether a specific card can't be picked up. Defaults to every card being draggable.
|
|
46
|
+
* Independent of a column's own `disabled` — a card can be pinned even in an otherwise
|
|
47
|
+
* unrestricted column.
|
|
48
|
+
*/
|
|
49
|
+
getItemDisabled?: (item: T, id: string) => boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Custom content shown inside a column when it has no cards, instead of leaving it blank.
|
|
52
|
+
* Receives the column definition.
|
|
53
|
+
*/
|
|
54
|
+
renderEmptyColumn?: (column: KanbanColumnDef) => ReactNode;
|
|
55
|
+
/**
|
|
56
|
+
* Custom actions rendered in a column's header, next to its title (e.g. an "add card"
|
|
57
|
+
* button, a column menu). Receives the column definition.
|
|
58
|
+
*/
|
|
59
|
+
renderColumnActions?: (column: KanbanColumnDef) => ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* Allow whole columns to be reordered by dragging their header, alongside the existing
|
|
62
|
+
* card dragging. Defaults to `false`.
|
|
63
|
+
*/
|
|
64
|
+
reorderableColumns?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Shrink a column to a narrow strip once it has no cards, instead of keeping it at the same
|
|
67
|
+
* width as every other column. Defaults to `true`. Set `false` to keep every column the same
|
|
68
|
+
* width regardless of card count. The collapsed width itself is set via the
|
|
69
|
+
* `--kanban-empty-column-width` CSS variable (falls back to `140px`) — override it theme-wide
|
|
70
|
+
* via `ThemeProvider`'s `variables`, or per board via `style`.
|
|
71
|
+
*/
|
|
72
|
+
collapseEmptyColumns?: boolean;
|
|
21
73
|
className?: string;
|
|
22
74
|
style?: CSSProperties;
|
|
23
75
|
classNames?: SlotClassNames<KanbanBoardSlots>;
|
|
24
76
|
styles?: SlotStyles<KanbanBoardSlots>;
|
|
25
77
|
}
|
|
26
|
-
export type KanbanColumnSlots = 'header' | 'items';
|
|
27
|
-
export interface KanbanColumnProps {
|
|
78
|
+
export type KanbanColumnSlots = 'header' | 'items' | 'count' | 'placeholder' | 'actions';
|
|
79
|
+
export interface KanbanColumnProps extends HtmlProps {
|
|
28
80
|
id: string;
|
|
29
81
|
title: string;
|
|
30
|
-
isOver?: boolean;
|
|
31
82
|
children?: ReactNode;
|
|
32
83
|
className?: string;
|
|
84
|
+
style?: CSSProperties;
|
|
33
85
|
classNames?: SlotClassNames<KanbanColumnSlots>;
|
|
34
86
|
styles?: SlotStyles<KanbanColumnSlots>;
|
|
35
87
|
}
|
|
88
|
+
export interface KanbanItemProps {
|
|
89
|
+
id: string;
|
|
90
|
+
dragHandle?: boolean;
|
|
91
|
+
/** Whether this card can't be picked up (column frozen, or the card itself is pinned). */
|
|
92
|
+
disabled?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether this card also can't be a drop target for other cards — i.e. other cards can't be
|
|
95
|
+
* reordered relative to it either. Only true when the *column* is frozen (nothing should be
|
|
96
|
+
* dropped into it at all); a card pinned individually via `getItemDisabled` stays droppable, so
|
|
97
|
+
* other cards can still land before/after it normally.
|
|
98
|
+
*/
|
|
99
|
+
preventAsDropTarget?: boolean;
|
|
100
|
+
children?: ReactNode;
|
|
101
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Announcements, ClientRect, CollisionDetection, KeyboardCoordinateGetter } from '@dnd-kit/core';
|
|
2
|
+
import { KanbanColumnDef } from './kanbanBoard.types';
|
|
3
|
+
export declare function columnsEqual(a: KanbanColumnDef[], b: KanbanColumnDef[]): boolean;
|
|
4
|
+
export declare function findColumnForItem(columns: KanbanColumnDef[], itemId: string): number;
|
|
5
|
+
/** Whether a drag's `active`/`over` participant is a whole column (vs. a card) — see `reorderableColumns`. */
|
|
6
|
+
export declare function isColumnDragData(data: unknown): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Reorders whole columns: moves `activeColumnId` to `overColumnId`'s position. Returns the same
|
|
9
|
+
* `columns` reference, unchanged, when either id doesn't resolve to a column or they're the same
|
|
10
|
+
* — callers can use that to skip a state update.
|
|
11
|
+
*/
|
|
12
|
+
export declare function moveColumn(columns: KanbanColumnDef[], activeColumnId: string, overColumnId: string): KanbanColumnDef[];
|
|
13
|
+
/**
|
|
14
|
+
* Collision detection for the board's shared `DndContext`, which has to pick between two very
|
|
15
|
+
* differently-scaled sets of droppables at once: individual cards / a column's own card list
|
|
16
|
+
* (small, numerous) and whole columns (large, few). Plain `closestCorners` weighs every
|
|
17
|
+
* registered droppable together regardless of what's actually being dragged, so while dragging a
|
|
18
|
+
* column it competes against every card and card-list droppable it happens to fly over too — the
|
|
19
|
+
* column-level target only wins when the pointer lands somewhere that geometrically favors it,
|
|
20
|
+
* which reads as needing to "wiggle it around" before the right drop target lights up. This
|
|
21
|
+
* restricts the candidates to only the same kind as whatever is being dragged: a column drag only
|
|
22
|
+
* considers other columns, a card drag only considers cards and card lists.
|
|
23
|
+
*/
|
|
24
|
+
export declare const kanbanCollisionDetection: CollisionDetection;
|
|
25
|
+
/** Extracts the column id from a column droppable's `column-<id>` identifier, or `null` for a card id. */
|
|
26
|
+
export declare function resolveColumnDropId(overId: string): string | null;
|
|
27
|
+
export interface DragGeometry {
|
|
28
|
+
/** The dragged card's current rect (translated by the drag delta), if measured. */
|
|
29
|
+
activeRect: ClientRect | null;
|
|
30
|
+
/** The rect of whatever `overId` refers to — a card, or a column's own droppable. */
|
|
31
|
+
overRect: ClientRect;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Computes the columns that result from dragging `activeItemId` over `overId` (a card id, or a
|
|
35
|
+
* `column-<id>` droppable id for empty column space). Returns the same `columns` reference,
|
|
36
|
+
* unchanged, when the drag doesn't resolve to a valid source/destination — callers can use that
|
|
37
|
+
* to skip a state update.
|
|
38
|
+
*
|
|
39
|
+
* `geometry` resolves cross-column drops that land on a card or gap with more than one possible
|
|
40
|
+
* position (see the two branches below for what each one decides).
|
|
41
|
+
*/
|
|
42
|
+
export declare function moveItem(columns: KanbanColumnDef[], activeItemId: string, overId: string, geometry: DragGeometry): KanbanColumnDef[];
|
|
43
|
+
/**
|
|
44
|
+
* Builds screen-reader drag announcements against the columns layout as currently understood
|
|
45
|
+
* (the live shadow layout while a drag is in progress, or the settled `columns` prop otherwise).
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildAnnouncements<T>(columns: KanbanColumnDef[], items: Record<string, T>, getItemLabel: (item: T, id: string) => string): Announcements;
|
|
48
|
+
/**
|
|
49
|
+
* Custom keyboard coordinate getter for the board's multi-column, multi-`SortableContext`
|
|
50
|
+
* layout. dnd-kit's default `sortableKeyboardCoordinates` picks the geometrically closest
|
|
51
|
+
* droppable across the *entire* board — cards and column containers alike — which is unreliable
|
|
52
|
+
* once cards live inside independent per-column `SortableContext`s: up/down and left/right end up
|
|
53
|
+
* interchangeable depending on incidental rect positions. This instead restricts up/down to
|
|
54
|
+
* reordering within the active card's own column, and left/right to moving into the adjacent
|
|
55
|
+
* column (landing on the closest card there by vertical position, or the column itself when
|
|
56
|
+
* empty). Requires each `KanbanColumn`'s `SortableContext` to be given an explicit `id` matching
|
|
57
|
+
* its column id, so a card's `sortable.containerId` can be correlated with its `column-<id>`
|
|
58
|
+
* droppable.
|
|
59
|
+
*/
|
|
60
|
+
export declare const kanbanKeyboardCoordinateGetter: KeyboardCoordinateGetter;
|
|
@@ -107,6 +107,8 @@ export interface ThemeVariables {
|
|
|
107
107
|
'--stepper-gap'?: string;
|
|
108
108
|
/** Text/check colour on a filled marker. Falls back to --text-on-primary. */
|
|
109
109
|
'--stepper-text-on-marker'?: string;
|
|
110
|
+
/** Width a column collapses to once it has no cards (see `collapseEmptyColumns`). Falls back to 140px. */
|
|
111
|
+
'--kanban-empty-column-width'?: string;
|
|
110
112
|
[key: string]: string | undefined;
|
|
111
113
|
}
|
|
112
114
|
export interface Theme {
|
package/docs/KanbanBoard.md
CHANGED
|
@@ -34,22 +34,147 @@ const items: Record<string, Task> = {
|
|
|
34
34
|
)}
|
|
35
35
|
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
36
36
|
/>
|
|
37
|
+
|
|
38
|
+
// Restrict drag initiation to a handle — needed when a card contains its own
|
|
39
|
+
// interactive controls (buttons, inputs), which would otherwise have their
|
|
40
|
+
// clicks swallowed by the drag listener on the whole card
|
|
41
|
+
<KanbanBoard
|
|
42
|
+
columns={columns}
|
|
43
|
+
items={items}
|
|
44
|
+
dragHandle
|
|
45
|
+
renderItem={(task, id) => (
|
|
46
|
+
<div className="task-card">
|
|
47
|
+
<span data-drag-handle>⠿</span>
|
|
48
|
+
<strong>{task.title}</strong>
|
|
49
|
+
<button onClick={() => deleteTask(id)}>Delete</button>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
// Work-in-progress limit — shows a "count/max" badge on the column header and a
|
|
56
|
+
// warning border once it's exceeded. Display-only: KanbanBoard doesn't block the
|
|
57
|
+
// drop itself, so enforce a hard cap (if you want one) inside onChange, by simply
|
|
58
|
+
// not calling setColumns when the destination column would go over its maxItems.
|
|
59
|
+
const columnsWithLimit: KanbanColumnDef[] = [
|
|
60
|
+
{ id: 'inprogress', title: 'In Progress', itemIds: ['task-3'], maxItems: 3 },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Accessible labels for screen-reader drag announcements — defaults to the card's
|
|
64
|
+
// id when omitted, so this is purely an upgrade for clearer narration
|
|
65
|
+
<KanbanBoard
|
|
66
|
+
columns={columns}
|
|
67
|
+
items={items}
|
|
68
|
+
getItemLabel={(task) => task.title}
|
|
69
|
+
renderItem={(task, id) => (
|
|
70
|
+
<div className="task-card">
|
|
71
|
+
<strong>{task.title}</strong>
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
// Freeze a column — cards inside can't be picked up, and nothing can be dropped
|
|
78
|
+
// into it. Use for a workflow rule like "cards in Done can't be moved back out."
|
|
79
|
+
const columnsWithLock: KanbanColumnDef[] = [
|
|
80
|
+
{ id: 'done', title: 'Done', itemIds: ['task-3'], disabled: true },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// Pin an individual card so it can't be dragged, independent of its column —
|
|
84
|
+
// e.g. a task that's locked pending approval
|
|
85
|
+
<KanbanBoard
|
|
86
|
+
columns={columns}
|
|
87
|
+
items={items}
|
|
88
|
+
getItemDisabled={(task) => task.locked === true}
|
|
89
|
+
renderItem={(task, id) => (
|
|
90
|
+
<div className="task-card">
|
|
91
|
+
<strong>{task.title}</strong>
|
|
92
|
+
{task.locked && <span>🔒</span>}
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
96
|
+
/>
|
|
97
|
+
|
|
98
|
+
// Custom content for an empty column, instead of leaving it blank
|
|
99
|
+
<KanbanBoard
|
|
100
|
+
columns={columns}
|
|
101
|
+
items={items}
|
|
102
|
+
renderItem={(task) => <div className="task-card"><strong>{task.title}</strong></div>}
|
|
103
|
+
renderEmptyColumn={(column) => <span>No tasks in {column.title}</span>}
|
|
104
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
// Custom actions in a column's header — an "add card" button, a column menu — laid
|
|
108
|
+
// out next to the title (and the maxItems count badge, when both are present)
|
|
109
|
+
<KanbanBoard
|
|
110
|
+
columns={columns}
|
|
111
|
+
items={items}
|
|
112
|
+
renderItem={(task) => <div className="task-card"><strong>{task.title}</strong></div>}
|
|
113
|
+
renderColumnActions={(column) => (
|
|
114
|
+
<button onClick={() => addTaskTo(column.id)} aria-label={`Add task to ${column.title}`}>+</button>
|
|
115
|
+
)}
|
|
116
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
117
|
+
/>
|
|
118
|
+
|
|
119
|
+
// Let whole columns be reordered by dragging their header, alongside card dragging.
|
|
120
|
+
// The same onChange receives the reordered columns array — no separate callback needed.
|
|
121
|
+
<KanbanBoard
|
|
122
|
+
columns={columns}
|
|
123
|
+
items={items}
|
|
124
|
+
renderItem={(task) => <div className="task-card"><strong>{task.title}</strong></div>}
|
|
125
|
+
reorderableColumns
|
|
126
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
127
|
+
/>
|
|
128
|
+
|
|
129
|
+
// Keep every column the same width regardless of card count — opt out of the
|
|
130
|
+
// default shrink-when-empty behaviour
|
|
131
|
+
<KanbanBoard
|
|
132
|
+
columns={columns}
|
|
133
|
+
items={items}
|
|
134
|
+
renderItem={(task) => <div className="task-card"><strong>{task.title}</strong></div>}
|
|
135
|
+
collapseEmptyColumns={false}
|
|
136
|
+
onChange={(updatedColumns) => setColumns(updatedColumns)}
|
|
137
|
+
/>
|
|
37
138
|
```
|
|
38
139
|
|
|
140
|
+
**Keyboard support:** cards are focusable and can be picked up and moved with the keyboard — Space or Enter to pick up, Space or Enter again to drop, Escape to cancel (reverts to the original position). Arrow keys move the card: Up/Down reorders within its current column; Left/Right moves it into the adjacent column, landing on the nearest card there (or the column itself, if it's empty). This uses a board-aware coordinate getter rather than `dnd-kit`'s generic `sortableKeyboardCoordinates`, which — across multiple per-column `SortableContext`s — can't reliably tell "next card in this column" from "some other column's droppable" and ends up mixing up Up/Down and Left/Right. With `reorderableColumns`, a column's header is focusable the same way — Space/Enter to pick it up, Left/Right to move it, Up/Down does nothing (columns are a single row).
|
|
141
|
+
|
|
142
|
+
**Column reordering (`reorderableColumns`):** a column's header becomes its drag handle for moving the whole column — dragging the cards area still only moves cards. This shares the same `DndContext` as card dragging (mouse, touch, and keyboard all work the same way), and commits through the same `onChange` — a column move is just a reordering of the `columns` array, same as any other change.
|
|
143
|
+
|
|
144
|
+
**Screen-reader announcements:** every pick-up, hover, drop, and cancel is announced (e.g. "Picked up card Design mockup.", "Card Design mockup is over the In Progress column."). By default cards are announced by their id — pass `getItemLabel` to announce something meaningful instead (a title, a short description).
|
|
145
|
+
|
|
39
146
|
**KanbanBoardProps:**
|
|
40
147
|
|
|
41
148
|
| Prop | Type | Description |
|
|
42
149
|
|------|------|-------------|
|
|
43
|
-
| `columns` | `KanbanColumnDef[]` | Column definitions with `id`, `title`, `itemIds` |
|
|
150
|
+
| `columns` | `KanbanColumnDef[]` | Column definitions with `id`, `title`, `itemIds`, optional `maxItems` |
|
|
44
151
|
| `items` | `Record<string, T>` | Flat map of all items keyed by id |
|
|
45
152
|
| `renderItem` | `(item: T, id: string) => ReactNode` | Render function for each item |
|
|
46
|
-
| `onChange` | `(columns: KanbanColumnDef[]) => void` | Called after
|
|
153
|
+
| `onChange` | `(columns: KanbanColumnDef[]) => void` | Called after a drop that actually changes column or order — replace state with this value |
|
|
154
|
+
| `dragHandle` | `boolean` | Restrict drag initiation to elements marked `data-drag-handle` within a card, instead of the whole card. Use when `renderItem` includes interactive content |
|
|
155
|
+
| `getItemLabel` | `(item: T, id: string) => string` | Accessible label for a card, used in screen-reader drag announcements. Defaults to the card's id |
|
|
156
|
+
| `getItemDisabled` | `(item: T, id: string) => boolean` | Returns whether a specific card can't be picked up. Defaults to every card being draggable; independent of a column's own `disabled`. A pinned card stays a valid drop target — other cards can still be reordered before/after it, unlike a card inside a fully frozen (`disabled`) column |
|
|
157
|
+
| `renderEmptyColumn` | `(column: KanbanColumnDef) => ReactNode` | Custom content shown inside a column when it has no cards, instead of leaving it blank |
|
|
158
|
+
| `renderColumnActions` | `(column: KanbanColumnDef) => ReactNode` | Custom actions rendered in a column's header, next to its title (e.g. an "add card" button, a column menu) |
|
|
159
|
+
| `reorderableColumns` | `boolean` | Allow whole columns to be reordered by dragging their header, alongside card dragging (default `false`) |
|
|
160
|
+
| `collapseEmptyColumns` | `boolean` | Shrink a column to a narrow strip once it has no cards (default `true`). Set `false` to keep every column the same width regardless of card count. The collapsed width is set via `--kanban-empty-column-width` (falls back to `140px`) — override it theme-wide via `ThemeProvider`, or per board via `style` |
|
|
161
|
+
| `id` / `role` / `aria-*` / `data-*` | | Passed through to the root element |
|
|
162
|
+
|
|
163
|
+
`KanbanColumn`'s own props accept the same `id` / `role` / `aria-*` / `data-*` pass-through on its root element.
|
|
47
164
|
|
|
48
165
|
**KanbanColumnDef:**
|
|
49
166
|
|
|
50
167
|
```ts
|
|
51
|
-
{
|
|
168
|
+
{
|
|
169
|
+
id: string;
|
|
170
|
+
title: string;
|
|
171
|
+
itemIds: string[];
|
|
172
|
+
maxItems?: number; // work-in-progress limit — display-only, see above
|
|
173
|
+
disabled?: boolean; // freezes the column — cards inside can't be dragged, nothing can drop in
|
|
174
|
+
}
|
|
52
175
|
```
|
|
53
176
|
|
|
177
|
+
**Note on item ids:** don't give a card an id starting with `column-` — the board uses that prefix internally to identify a column's own droppable (the empty space above/below its cards) versus a specific card. A colliding card id is silently misread as a column reference, and drops involving it silently no-op instead of throwing, so the bug can be easy to miss.
|
|
178
|
+
|
|
54
179
|
**Slots (KanbanBoard):** `dragOverlay`
|
|
55
|
-
**Slots (KanbanColumn):** `header` `items`
|
|
180
|
+
**Slots (KanbanColumn):** `header` `items` `count` (the `count/maxItems` badge, only rendered when `maxItems` is set) `placeholder` (the `renderEmptyColumn` wrapper, only rendered when the column has no cards and `renderEmptyColumn` is set) `actions` (the `renderColumnActions` wrapper, only rendered when `renderColumnActions` is set)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahrowe/ui",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"build:mcp": "vite build --config vite.mcp.config.ts",
|
|
56
56
|
"preview": "vite preview",
|
|
57
57
|
"gen-barrel": "tsx scripts/gen-barrel.ts",
|
|
58
|
-
"gc": "bash src/scripts/generateComponent.sh
|
|
58
|
+
"gc": "bash src/scripts/generateComponent.sh",
|
|
59
59
|
"changeset": "changeset",
|
|
60
60
|
"lint": "eslint .",
|
|
61
61
|
"lint:fix": "eslint . --fix",
|
|
@@ -84,7 +84,6 @@
|
|
|
84
84
|
"@mona-health/react-input-mask": "^3.0.3",
|
|
85
85
|
"classnames": "^2.5.1",
|
|
86
86
|
"downshift": "^9.3.3",
|
|
87
|
-
"prop-types": "^15.8.1",
|
|
88
87
|
"react-number-format": "^5.4.5",
|
|
89
88
|
"zod": "^4.4.3"
|
|
90
89
|
},
|
|
@@ -101,7 +100,6 @@
|
|
|
101
100
|
"@types/node": "^25.9.1",
|
|
102
101
|
"@types/react": "^19.2.15",
|
|
103
102
|
"@types/react-dom": "^19.2.3",
|
|
104
|
-
"@types/react-helmet": "^6.1.11",
|
|
105
103
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
106
104
|
"@vitejs/plugin-react": "^6.0.2",
|
|
107
105
|
"@vitest/ui": "^4.1.7",
|