hazo_ui 6.4.0 → 6.7.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/CHANGE_LOG.md +118 -0
- package/README.md +42 -0
- package/dist/index.cjs +262 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -2
- package/dist/index.d.ts +69 -2
- package/dist/index.js +263 -92
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/CHANGE_LOG.md
CHANGED
|
@@ -5,6 +5,124 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v6.7.0 (2026-08-02)
|
|
9
|
+
|
|
10
|
+
### Added — `data-column-key` / `data-lane-key` on the kanban DOM
|
|
11
|
+
|
|
12
|
+
`cls_hazo_kanban_column` is one class shared by every column, so a consumer
|
|
13
|
+
whose columns carry meaning — an inbox is not a done pile — had no way to
|
|
14
|
+
style one differently. The only lever available was `:nth-child`, which
|
|
15
|
+
silently mis-paints the moment columns are reordered, filtered, or a lane
|
|
16
|
+
renders a subset. These attributes make the identity the component already
|
|
17
|
+
knows about addressable from CSS:
|
|
18
|
+
|
|
19
|
+
| Element | Attributes |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `.cls_hazo_kanban_column` | `data-column-key`, `data-lane-key` |
|
|
22
|
+
| `.cls_hazo_kanban_column_head` | `data-column-key`, `data-lane-key` |
|
|
23
|
+
| `.cls_hazo_kanban_column_body` | `data-column-key`, `data-lane-key` |
|
|
24
|
+
| `.cls_hazo_kanban_card` | `data-column-key`, `data-lane-key`, `data-section-key` |
|
|
25
|
+
|
|
26
|
+
`data-lane-key` is absent (not empty) on an unlaned board, so
|
|
27
|
+
`[data-lane-key]` is a usable "is this board laned" test. Both layout trees
|
|
28
|
+
always render — pair with the ancestor `.cls_hazo_kanban_desktop` /
|
|
29
|
+
`.cls_hazo_kanban_mobile` when a rule must apply to only one.
|
|
30
|
+
|
|
31
|
+
Purely additive: no class, layout, or default paint changed.
|
|
32
|
+
|
|
33
|
+
### Added — `--hazo-kanban-card-shadow`
|
|
34
|
+
|
|
35
|
+
Cards drew `background: hsl(var(--hazo-kanban-card-bg))` with a 1px border
|
|
36
|
+
and nothing else. Under any palette where `--card` and `--background` resolve
|
|
37
|
+
to the same value (the shadcn-derived default in `hazo_theme`, in both light
|
|
38
|
+
and dark), that border is the *only* thing separating a card from the column
|
|
39
|
+
behind it, and the board reads as flat.
|
|
40
|
+
|
|
41
|
+
The card's `box-shadow` now resolves `var(--hazo-kanban-card-shadow, none)`.
|
|
42
|
+
The fallback is `none`, so a consumer that never declares the token renders
|
|
43
|
+
byte-identically to v6.6.0. Set it (e.g. to `var(--shadow-sm)`) to give cards
|
|
44
|
+
a real lift.
|
|
45
|
+
|
|
46
|
+
## v6.6.0 (2026-08-02)
|
|
47
|
+
|
|
48
|
+
### Added — consumer slots on a laned `HazoUiKanban`
|
|
49
|
+
|
|
50
|
+
v6.5.0 gave the board a second, outer axis but no way to hang anything off
|
|
51
|
+
it. A lane could be dropped into and counted, and that was all — there was no
|
|
52
|
+
supported place to put "add an item to this lane", a per-lane overflow menu,
|
|
53
|
+
or an inline composer at the foot of a column. Both slots below are additive
|
|
54
|
+
and default to rendering nothing, so existing boards are byte-identical.
|
|
55
|
+
|
|
56
|
+
- **`KanbanLane.actions?: React.ReactNode`** — rendered in the lane heading
|
|
57
|
+
between the rule and the count, and right-aligned on a `collapseWhenEmpty`
|
|
58
|
+
lane's drop strip so a lane's controls do not vanish the moment it empties.
|
|
59
|
+
No `stopPropagation` is needed on controls placed here: a lane heading is a
|
|
60
|
+
`useDroppable` node, which registers a rect but attaches no pointer
|
|
61
|
+
handlers, unlike the `useSortable` card shell.
|
|
62
|
+
- **`HazoUiKanbanProps.renderColumnFooter?: (ctx) => React.ReactNode`** —
|
|
63
|
+
rendered under a column body, below the cards or the `emptyColumn`
|
|
64
|
+
placeholder. `ctx` is `{ columnKey, laneKey }` and the function is called
|
|
65
|
+
once per rendered copy of a column, so on a laned board it fires once per
|
|
66
|
+
(lane, column) pair — enough to build a composer scoped to one lane's
|
|
67
|
+
Inbox. Return `null` for the columns that should not have one.
|
|
68
|
+
- The footer sits inside the column's droppable but **outside** its
|
|
69
|
+
`SortableContext`, so it never joins the sort order or becomes a drop
|
|
70
|
+
target of its own.
|
|
71
|
+
- The unmatched-lane row is deliberately skipped: its lane key is the
|
|
72
|
+
private `__hazo_lane_unmatched__` sentinel, never a lane the consumer
|
|
73
|
+
declared, so a footer there would both leak an internal constant and ask
|
|
74
|
+
for controls scoped to a lane that does not exist.
|
|
75
|
+
|
|
76
|
+
New class hooks: `cls_hazo_kanban_lane_actions`,
|
|
77
|
+
`cls_hazo_kanban_column_footer`. The lane strip's title is now wrapped in its
|
|
78
|
+
own `cls_hazo_kanban_lane_title` span (it was a bare text node) so the
|
|
79
|
+
right-aligned actions have something to sit beside.
|
|
80
|
+
|
|
81
|
+
See `test-app/app/kanban/` §12.
|
|
82
|
+
|
|
83
|
+
## v6.5.0 (2026-08-02)
|
|
84
|
+
|
|
85
|
+
### Added — swimlanes for `HazoUiKanban`
|
|
86
|
+
|
|
87
|
+
A board can now declare `lanes`, rendering one full-width row per lane —
|
|
88
|
+
a wide lane heading, then that lane's own copy of the whole column grid. Where
|
|
89
|
+
`sections` (v6.4.0) group *within* a column, lanes group *across* all of them,
|
|
90
|
+
so the second axis can be the row axis instead of the inner one.
|
|
91
|
+
|
|
92
|
+
- `HazoUiKanbanProps.lanes?: KanbanLane[]` — `{ key, title, accepts?,
|
|
93
|
+
collapseWhenEmpty? }`. Cards are placed by the new `KanbanItem.laneKey`.
|
|
94
|
+
Omit `lanes` and nothing changes: existing boards render exactly as before.
|
|
95
|
+
- `KanbanMoveEvent` gained `fromLane`/`toLane`; `KanbanReorderEvent` gained
|
|
96
|
+
`laneKey`. Move-vs-reorder is now decided on all three axes — same column,
|
|
97
|
+
section *and* lane is a reorder; changing any one is a move.
|
|
98
|
+
- **A lane heading is the lane-only drop target.** It carries a lane but no
|
|
99
|
+
column, read as "change the lane, keep the card's column" — the mirror of a
|
|
100
|
+
sectioned column's head ("change the column, keep the section").
|
|
101
|
+
- `collapseWhenEmpty` renders a lane with no cards as a thin full-width dashed
|
|
102
|
+
strip, which is what makes "declare every lane" affordable for a long
|
|
103
|
+
vocabulary. A card whose `laneKey` matches no declared lane renders in an
|
|
104
|
+
unlabelled trailing lane rather than disappearing.
|
|
105
|
+
- The optimistic overlay tracks the destination lane too, so a cross-lane drop
|
|
106
|
+
moves immediately instead of snapping back until the consumer's async commit
|
|
107
|
+
lands.
|
|
108
|
+
|
|
109
|
+
### Fixed — drops landed in the wrong column on tall or scrolled boards
|
|
110
|
+
|
|
111
|
+
Two long-standing defects in drop resolution, both of which lanes made
|
|
112
|
+
impossible to miss:
|
|
113
|
+
|
|
114
|
+
- **Collision detection is now pointer-first** (`pointerWithin`, falling back
|
|
115
|
+
to `rectIntersection`, then `closestCorners`). `closestCorners` alone
|
|
116
|
+
averages the distance to all four corners, so a column stretched tall by a
|
|
117
|
+
neighbour's card stack scored *worse* than a short, far-away column — a drop
|
|
118
|
+
well inside the right column could resolve to a different one entirely.
|
|
119
|
+
Keyboard drags have no pointer and take the same fallbacks as before.
|
|
120
|
+
- **Droppable ids are scoped per layout tree.** The mobile tab strip and the
|
|
121
|
+
desktop grid both always render (CSS hides one), so every column previously
|
|
122
|
+
registered twice under one id. dnd-kit keys its registry by id, so one node
|
|
123
|
+
won and collisions were measured against the CSS-hidden copy's 0×0 rect.
|
|
124
|
+
Ids now carry the tree ("m"/"d") and, on a laned board, the lane.
|
|
125
|
+
|
|
8
126
|
## v6.4.0 (2026-08-01)
|
|
9
127
|
|
|
10
128
|
### Added — in-column sections for `HazoUiKanban`
|
package/README.md
CHANGED
|
@@ -3677,6 +3677,48 @@ function Actions({ initial }) {
|
|
|
3677
3677
|
event carries `revert()` — call it when your API request fails and the
|
|
3678
3678
|
overlay snaps back to whatever `items` says.
|
|
3679
3679
|
|
|
3680
|
+
### Grouping: sections (v6.4.0) and swimlanes (v6.5.0)
|
|
3681
|
+
|
|
3682
|
+
A board can group on a second axis, either *inside* each column or *across*
|
|
3683
|
+
all of them. Both are opt-in; omit them and the board is a plain column grid.
|
|
3684
|
+
|
|
3685
|
+
```tsx
|
|
3686
|
+
// Sections: separator-headed bands inside each column, keyed by item.sectionKey
|
|
3687
|
+
<HazoUiKanban
|
|
3688
|
+
columns={COLUMNS.map(col => ({
|
|
3689
|
+
...col,
|
|
3690
|
+
sections: TAGS.map(t => ({ key: t, title: t, collapseWhenEmpty: true })),
|
|
3691
|
+
}))}
|
|
3692
|
+
items={items.map(it => ({ ...it, sectionKey: it.tag }))}
|
|
3693
|
+
onMove={(e) => save(e.itemId, { column: e.toColumn, tag: e.toSection })}
|
|
3694
|
+
renderCard={renderCard}
|
|
3695
|
+
/>
|
|
3696
|
+
|
|
3697
|
+
// Swimlanes: one full-width row per lane, each with its own column grid,
|
|
3698
|
+
// keyed by item.laneKey
|
|
3699
|
+
<HazoUiKanban
|
|
3700
|
+
columns={COLUMNS}
|
|
3701
|
+
lanes={TAGS.map(t => ({ key: t, title: t, collapseWhenEmpty: true }))}
|
|
3702
|
+
items={items.map(it => ({ ...it, laneKey: it.tag }))}
|
|
3703
|
+
onMove={(e) => save(e.itemId, { column: e.toColumn, tag: e.toLane })}
|
|
3704
|
+
renderCard={renderCard}
|
|
3705
|
+
/>
|
|
3706
|
+
```
|
|
3707
|
+
|
|
3708
|
+
| | Sections | Lanes |
|
|
3709
|
+
|---|---|---|
|
|
3710
|
+
| Declared on | `KanbanColumn.sections` | `HazoUiKanbanProps.lanes` |
|
|
3711
|
+
| Card placed by | `KanbanItem.sectionKey` | `KanbanItem.laneKey` |
|
|
3712
|
+
| Renders as | bands stacked inside one column | full-width rows, each with the whole column grid |
|
|
3713
|
+
| Move event carries | `fromSection`/`toSection` | `fromLane`/`toLane` |
|
|
3714
|
+
| "Keep the other axis" drop target | the column head | the lane heading |
|
|
3715
|
+
| `collapseWhenEmpty` | thin drop strip at the column's foot | full-width drop strip |
|
|
3716
|
+
|
|
3717
|
+
A drop expresses both axes at once, so one gesture can change status and tag
|
|
3718
|
+
together. `accepts(item)` on a column, section or lane rejects a drop into it.
|
|
3719
|
+
Cards whose key matches nothing declared render in an unlabelled trailing
|
|
3720
|
+
group (sections) or lane row (lanes) rather than disappearing.
|
|
3721
|
+
|
|
3680
3722
|
### Theming (CSS custom properties)
|
|
3681
3723
|
|
|
3682
3724
|
| Variable | Default | Purpose |
|