@stll/ui 0.17.0 → 0.17.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/components/button-variants.d.ts +2 -2
- package/dist/inspector/tabs.d.ts +1 -1
- package/dist/kanban/column-band-header.d.ts +12 -7
- package/dist/kanban/column-band-header.js +25 -25
- package/dist/kanban/subgroup-board.d.ts +19 -9
- package/dist/kanban/subgroup-board.js +36 -19
- package/package.json +1 -1
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
declare const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
|
|
12
12
|
declare const buttonVariants: (props?: ({
|
|
13
|
-
size?: "
|
|
14
|
-
variant?: "
|
|
13
|
+
size?: "sm" | "default" | "lg" | "xs" | "icon" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "xl" | null | undefined;
|
|
14
|
+
variant?: "link" | "default" | "destructive" | "outline" | "destructive-outline" | "ghost" | "secondary" | null | undefined;
|
|
15
15
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
16
16
|
//#endregion
|
|
17
17
|
export { buttonAccessibleDisabledClass, buttonVariants };
|
package/dist/inspector/tabs.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Tabs } from "@base-ui/react/tabs";
|
|
|
8
8
|
*/
|
|
9
9
|
declare const InspectorTabs: ({ className, ...props }: Omit<Tabs.Root.Props, "orientation">) => React$1.JSX.Element;
|
|
10
10
|
declare const INSPECTOR_RAIL_MEDIA_QUERY: "(min-width: 48rem)";
|
|
11
|
-
declare const resolveInspectorTabOrientation: (isRailLayout: boolean) => "
|
|
11
|
+
declare const resolveInspectorTabOrientation: (isRailLayout: boolean) => "vertical" | "horizontal";
|
|
12
12
|
declare const InspectorTabList: ({ className, ...props }: Tabs.List.Props) => React$1.JSX.Element;
|
|
13
13
|
declare const InspectorTab: ({ className, ...props }: Tabs.Tab.Props) => React$1.JSX.Element;
|
|
14
14
|
declare const InspectorTabPanel: ({ className, ...props }: Tabs.Panel.Props) => React$1.JSX.Element;
|
|
@@ -7,8 +7,14 @@ type KanbanColumnBandHeaderProps = {
|
|
|
7
7
|
swatch?: ReactNode;
|
|
8
8
|
/** Short text after the name, such as the band's card count. */
|
|
9
9
|
meta?: ReactNode;
|
|
10
|
-
/**
|
|
10
|
+
/** The band's persisted state; drives the toggle, its icon, and its label. */
|
|
11
11
|
collapsed: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Render only the toggle, for the narrow slot of a folded band. Defaults to
|
|
14
|
+
* `collapsed`; a board that peeks a collapsed band open passes `false` so
|
|
15
|
+
* the caption shows the name while the toggle still offers to pin it open.
|
|
16
|
+
*/
|
|
17
|
+
compact?: boolean | undefined;
|
|
12
18
|
/** Accessible name for the toggle, such as "Collapse To do". */
|
|
13
19
|
toggleLabel: string;
|
|
14
20
|
onCollapsedChange: (collapsed: boolean) => void;
|
|
@@ -16,12 +22,11 @@ type KanbanColumnBandHeaderProps = {
|
|
|
16
22
|
actions?: ReactNode;
|
|
17
23
|
};
|
|
18
24
|
/**
|
|
19
|
-
* The band
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the
|
|
23
|
-
* `aria-expanded` for the columns it hides.
|
|
25
|
+
* The band line above a run of columns: one 28px row of toggle, swatch, name,
|
|
26
|
+
* and count, so a band costs the board a caption's height and nothing more.
|
|
27
|
+
* Folded, only the toggle remains in this line; the band's name moves down
|
|
28
|
+
* into the narrow column body, where the height is already there.
|
|
24
29
|
*/
|
|
25
|
-
declare const KanbanColumnBandHeader: ({ title, swatch, meta, collapsed, toggleLabel, onCollapsedChange, actions }: KanbanColumnBandHeaderProps) => import("react").JSX.Element;
|
|
30
|
+
declare const KanbanColumnBandHeader: ({ title, swatch, meta, collapsed, compact, toggleLabel, onCollapsedChange, actions }: KanbanColumnBandHeaderProps) => import("react").JSX.Element;
|
|
26
31
|
//#endregion
|
|
27
32
|
export { KanbanColumnBandHeader, KanbanColumnBandHeaderProps };
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import { DirectionalIcon } from "../components/directional-icon.js";
|
|
3
3
|
import { ChevronDownIcon } from "lucide-react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/kanban/column-band-header.tsx
|
|
6
6
|
/**
|
|
7
|
-
* The band
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the
|
|
11
|
-
* `aria-expanded` for the columns it hides.
|
|
7
|
+
* The band line above a run of columns: one 28px row of toggle, swatch, name,
|
|
8
|
+
* and count, so a band costs the board a caption's height and nothing more.
|
|
9
|
+
* Folded, only the toggle remains in this line; the band's name moves down
|
|
10
|
+
* into the narrow column body, where the height is already there.
|
|
12
11
|
*/
|
|
13
|
-
const KanbanColumnBandHeader = ({ title, swatch, meta, collapsed, toggleLabel, onCollapsedChange, actions }) => /* @__PURE__ */ jsxs("div", {
|
|
14
|
-
className: cn("flex items-center gap-
|
|
12
|
+
const KanbanColumnBandHeader = ({ title, swatch, meta, collapsed, compact = collapsed, toggleLabel, onCollapsedChange, actions }) => /* @__PURE__ */ jsxs("div", {
|
|
13
|
+
className: cn("flex h-7 items-center gap-1", compact ? "justify-center" : "pe-1"),
|
|
15
14
|
"data-collapsed": collapsed ? "" : void 0,
|
|
15
|
+
"data-compact": compact ? "" : void 0,
|
|
16
16
|
"data-slot": "kanban-column-band-header",
|
|
17
|
-
children: [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
18
|
+
"aria-expanded": !collapsed,
|
|
19
|
+
"aria-label": toggleLabel,
|
|
20
|
+
className: "hover:bg-muted/60 text-muted-foreground hover:text-foreground flex size-6 shrink-0 items-center justify-center rounded-md transition-[background-color]",
|
|
21
|
+
onClick: () => onCollapsedChange(!collapsed),
|
|
22
|
+
title: toggleLabel,
|
|
23
|
+
type: "button",
|
|
24
|
+
children: /* @__PURE__ */ jsx(DirectionalIcon, {
|
|
25
|
+
className: cn("size-3.5 transition-transform", collapsed && "-rotate-90"),
|
|
26
|
+
flip: collapsed,
|
|
27
|
+
icon: ChevronDownIcon
|
|
28
|
+
})
|
|
29
|
+
}), compact ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
30
|
swatch,
|
|
31
31
|
/* @__PURE__ */ jsxs("span", {
|
|
32
|
-
className:
|
|
32
|
+
className: "flex min-w-0 flex-1 items-baseline gap-1.5 truncate text-xs font-medium",
|
|
33
33
|
children: [title, meta !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
34
|
-
className: "text-muted-foreground
|
|
34
|
+
className: "text-muted-foreground font-normal tabular-nums",
|
|
35
35
|
children: meta
|
|
36
36
|
})]
|
|
37
37
|
}),
|
|
38
|
-
|
|
39
|
-
]
|
|
38
|
+
actions
|
|
39
|
+
] })]
|
|
40
40
|
});
|
|
41
41
|
//#endregion
|
|
42
42
|
export { KanbanColumnBandHeader };
|
|
@@ -23,7 +23,14 @@ type KanbanSubgroupBandHeaderContext = {
|
|
|
23
23
|
columns: KanbanBoardColumn[];
|
|
24
24
|
/** Rows across the band's columns, every lane included. */
|
|
25
25
|
count: number;
|
|
26
|
+
/** The persisted state the toggle reports and flips. */
|
|
26
27
|
collapsed: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Whether the band renders as one narrow slot right now. False while a
|
|
30
|
+
* collapsed band peeks open under the pointer, so a caption can show its
|
|
31
|
+
* name and offer to pin the band open.
|
|
32
|
+
*/
|
|
33
|
+
folded: boolean;
|
|
27
34
|
onCollapsedChange: (collapsed: boolean) => void;
|
|
28
35
|
};
|
|
29
36
|
/**
|
|
@@ -50,13 +57,14 @@ type KanbanSubgroupBoardProps<TRow> = {
|
|
|
50
57
|
renderLaneIdentity: (context: KanbanSubgroupLaneIdentityContext) => ReactNode;
|
|
51
58
|
renderCell: (context: KanbanSubgroupCellContext<TRow>) => ReactNode;
|
|
52
59
|
/**
|
|
53
|
-
* The
|
|
60
|
+
* The line above a band's columns. Defaults to `KanbanColumnBandHeader`
|
|
54
61
|
* with the band's label and count.
|
|
55
62
|
*/
|
|
56
63
|
renderBandHeader?: ((context: KanbanSubgroupBandHeaderContext) => ReactNode) | undefined;
|
|
57
64
|
/**
|
|
58
|
-
* A lane's slot while its band is collapsed. Defaults to the
|
|
59
|
-
* a host that accepts drops into a collapsed
|
|
65
|
+
* A lane's slot while its band is collapsed. Defaults to the band's name
|
|
66
|
+
* set vertically over the count; a host that accepts drops into a collapsed
|
|
67
|
+
* band renders its target here.
|
|
60
68
|
*/
|
|
61
69
|
renderCollapsedBandCell?: ((context: KanbanSubgroupCollapsedBandCellContext<TRow>) => ReactNode) | undefined;
|
|
62
70
|
/** Accessible name for a band's collapse toggle. */
|
|
@@ -74,12 +82,14 @@ type KanbanSubgroupBoardProps<TRow> = {
|
|
|
74
82
|
/**
|
|
75
83
|
* Reusable swimlane layout over the canonical two-axis Kanban matrix.
|
|
76
84
|
*
|
|
77
|
-
* Columns that carry band metadata render under a band
|
|
78
|
-
* collapsed as a run: the band folds into one narrow slot in every
|
|
79
|
-
* cells stay reachable (a host renders its drop target in them),
|
|
80
|
-
* open while a pointer rests on it, so a drag can still land on a
|
|
81
|
-
* column inside. Every row is laid out span by span with the same
|
|
82
|
-
* which is what keeps headers, counts, and cells aligned
|
|
85
|
+
* Columns that carry band metadata render under a one-line band caption and
|
|
86
|
+
* can be collapsed as a run: the band folds into one narrow slot in every
|
|
87
|
+
* row, whose cells stay reachable (a host renders its drop target in them),
|
|
88
|
+
* and peeks open while a pointer rests on it, so a drag can still land on a
|
|
89
|
+
* specific column inside. Every row is laid out span by span with the same
|
|
90
|
+
* widths, which is what keeps captions, headers, counts, and cells aligned
|
|
91
|
+
* in every state; the caption line never grows past its own height, so a
|
|
92
|
+
* folded band costs no vertical space.
|
|
83
93
|
*/
|
|
84
94
|
declare const KanbanSubgroupBoard: <TRow>({ matrix, renderColumnHeader, renderLaneIdentity, renderCell, renderBandHeader, renderCollapsedBandCell, formatBandToggleLabel, formatCount, isLaneCollapsed, onLaneCollapsedChange, isBandCollapsed, onBandCollapsedChange, footer, className }: KanbanSubgroupBoardProps<TRow>) => import("react").JSX.Element;
|
|
85
95
|
//#endregion
|
|
@@ -15,12 +15,14 @@ const KANBAN_BAND_PEEK_DELAY_MS = 400;
|
|
|
15
15
|
/**
|
|
16
16
|
* Reusable swimlane layout over the canonical two-axis Kanban matrix.
|
|
17
17
|
*
|
|
18
|
-
* Columns that carry band metadata render under a band
|
|
19
|
-
* collapsed as a run: the band folds into one narrow slot in every
|
|
20
|
-
* cells stay reachable (a host renders its drop target in them),
|
|
21
|
-
* open while a pointer rests on it, so a drag can still land on a
|
|
22
|
-
* column inside. Every row is laid out span by span with the same
|
|
23
|
-
* which is what keeps headers, counts, and cells aligned
|
|
18
|
+
* Columns that carry band metadata render under a one-line band caption and
|
|
19
|
+
* can be collapsed as a run: the band folds into one narrow slot in every
|
|
20
|
+
* row, whose cells stay reachable (a host renders its drop target in them),
|
|
21
|
+
* and peeks open while a pointer rests on it, so a drag can still land on a
|
|
22
|
+
* specific column inside. Every row is laid out span by span with the same
|
|
23
|
+
* widths, which is what keeps captions, headers, counts, and cells aligned
|
|
24
|
+
* in every state; the caption line never grows past its own height, so a
|
|
25
|
+
* folded band costs no vertical space.
|
|
24
26
|
*/
|
|
25
27
|
const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, renderCell, renderBandHeader, renderCollapsedBandCell, formatBandToggleLabel, formatCount = String, isLaneCollapsed, onLaneCollapsedChange, isBandCollapsed, onBandCollapsedChange, footer, className }) => {
|
|
26
28
|
const [collapsedLaneValues, setCollapsedLaneValues] = useState(() => /* @__PURE__ */ new Set());
|
|
@@ -73,10 +75,14 @@ const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, r
|
|
|
73
75
|
return next;
|
|
74
76
|
});
|
|
75
77
|
};
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
/** The band's persisted state: what the toggle reports and flips. */
|
|
79
|
+
const isBandCollapsedNow = (band) => isBandCollapsed ? isBandCollapsed(band) : collapsedBandIds.has(band.id);
|
|
80
|
+
/**
|
|
81
|
+
* Whether the band renders folded right now. A peek opens the layout
|
|
82
|
+
* without changing the persisted state, so a peeked band still reports
|
|
83
|
+
* itself collapsed and its toggle pins it open rather than closing it.
|
|
84
|
+
*/
|
|
85
|
+
const isBandFolded = (band) => peekingBandId !== band.id && isBandCollapsedNow(band);
|
|
80
86
|
const setBandCollapsed = (band, collapsed) => {
|
|
81
87
|
setPeekingBandId(null);
|
|
82
88
|
if (onBandCollapsedChange) {
|
|
@@ -122,17 +128,20 @@ const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, r
|
|
|
122
128
|
})
|
|
123
129
|
});
|
|
124
130
|
const bandHeader = (band, span) => {
|
|
125
|
-
const collapsed =
|
|
131
|
+
const collapsed = isBandCollapsedNow(band);
|
|
132
|
+
const folded = isBandFolded(band);
|
|
126
133
|
const context = {
|
|
127
134
|
band,
|
|
128
135
|
collapsed,
|
|
129
136
|
columns: span.columns,
|
|
130
137
|
count: span.columns.reduce((sum, column) => sum + columnCount(column), 0),
|
|
138
|
+
folded,
|
|
131
139
|
onCollapsedChange: (next) => setBandCollapsed(band, next)
|
|
132
140
|
};
|
|
133
141
|
if (renderBandHeader) return /* @__PURE__ */ jsx(Fragment, { children: renderBandHeader(context) });
|
|
134
142
|
return /* @__PURE__ */ jsx(KanbanColumnBandHeader, {
|
|
135
143
|
collapsed,
|
|
144
|
+
compact: folded,
|
|
136
145
|
meta: formatCount(context.count),
|
|
137
146
|
title: band.label,
|
|
138
147
|
toggleLabel: formatBandToggleLabel ? formatBandToggleLabel(band, collapsed) : band.label,
|
|
@@ -148,10 +157,16 @@ const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, r
|
|
|
148
157
|
count,
|
|
149
158
|
laneValue
|
|
150
159
|
}) });
|
|
151
|
-
return /* @__PURE__ */
|
|
152
|
-
className: "text-muted-foreground flex
|
|
160
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
161
|
+
className: "text-muted-foreground flex flex-col items-center gap-2 py-2 text-xs",
|
|
153
162
|
"data-kanban-collapsed-band-count": count,
|
|
154
|
-
children:
|
|
163
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
164
|
+
className: "max-h-40 truncate font-medium [writing-mode:vertical-rl]",
|
|
165
|
+
children: band.label
|
|
166
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
167
|
+
className: "tabular-nums",
|
|
168
|
+
children: formatCount(count)
|
|
169
|
+
})]
|
|
155
170
|
});
|
|
156
171
|
};
|
|
157
172
|
const foldedCount = (span, cells) => {
|
|
@@ -173,7 +188,7 @@ const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, r
|
|
|
173
188
|
/* @__PURE__ */ jsxs("div", {
|
|
174
189
|
className: "bg-background sticky top-0 z-20 pt-4 pb-3",
|
|
175
190
|
children: [hasBands ? /* @__PURE__ */ jsx("div", {
|
|
176
|
-
className: "flex gap-3 pb-
|
|
191
|
+
className: "flex items-end gap-3 pb-1.5",
|
|
177
192
|
"data-kanban-band-row": "",
|
|
178
193
|
children: spans.map((span) => {
|
|
179
194
|
const band = span.band;
|
|
@@ -183,13 +198,13 @@ const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, r
|
|
|
183
198
|
}, spanKey(span));
|
|
184
199
|
if (isBandFolded(band)) return /* @__PURE__ */ jsx(FoldedBandSlot, {
|
|
185
200
|
band,
|
|
186
|
-
className: cn("border-border/60
|
|
201
|
+
className: cn("border-border/60 border-b", KANBAN_COLLAPSED_BAND_WIDTH_CLASS),
|
|
187
202
|
onPeek: setPeekingBandId,
|
|
188
203
|
onPeekEnd: endPeek,
|
|
189
204
|
children: bandHeader(band, span)
|
|
190
205
|
}, spanKey(span));
|
|
191
206
|
return /* @__PURE__ */ jsx("div", {
|
|
192
|
-
className: "border-border/60
|
|
207
|
+
className: "border-border/60 shrink-0 border-b",
|
|
193
208
|
"data-kanban-band": band.id,
|
|
194
209
|
style: { width: `${String(spanWidth(span))}px` },
|
|
195
210
|
onPointerLeave: peekingBandId === band.id ? endPeek : void 0,
|
|
@@ -290,6 +305,8 @@ const KanbanSubgroupBoard = ({ matrix, renderColumnHeader, renderLaneIdentity, r
|
|
|
290
305
|
* The narrow slot a folded band occupies in a row. A pointer resting on it
|
|
291
306
|
* for `KANBAN_BAND_PEEK_DELAY_MS` peeks the band open; leaving ends the peek
|
|
292
307
|
* and cancels a pending one, so a drag passing over it does not unfold it.
|
|
308
|
+
* The pointer must enter the slot after it appeared: a band folded under a
|
|
309
|
+
* resting pointer does not peek straight back open.
|
|
293
310
|
*/
|
|
294
311
|
const FoldedBandSlot = ({ band, children, className, onPeek, onPeekEnd }) => {
|
|
295
312
|
const timer = useRef(null);
|
|
@@ -300,8 +317,8 @@ const FoldedBandSlot = ({ band, children, className, onPeek, onPeekEnd }) => {
|
|
|
300
317
|
className,
|
|
301
318
|
"data-kanban-band": band.id,
|
|
302
319
|
"data-kanban-band-collapsed": "",
|
|
303
|
-
|
|
304
|
-
if (timer.current !== null)
|
|
320
|
+
onPointerMove: () => {
|
|
321
|
+
if (timer.current !== null) return;
|
|
305
322
|
timer.current = setTimeout(() => {
|
|
306
323
|
timer.current = null;
|
|
307
324
|
onPeek(band.id);
|
package/package.json
CHANGED