@hilum/ui 3.5.1 → 3.5.2
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.d.ts +17 -1
- package/dist/index.js +70 -1
- package/dist/index.js.map +1 -1
- package/llms.txt +4 -3
- package/package.json +1 -1
- package/src/components/usage-bar.tsx +105 -0
- package/src/index.ts +1 -0
package/llms.txt
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
## Key Facts
|
|
6
6
|
|
|
7
7
|
- Package name: `@hilum/ui`
|
|
8
|
-
- Package version: 3.
|
|
9
|
-
- Component modules:
|
|
8
|
+
- Package version: 3.5.2
|
|
9
|
+
- Component modules: 100
|
|
10
10
|
- Atom components: 70
|
|
11
|
-
- Molecule components:
|
|
11
|
+
- Molecule components: 30
|
|
12
12
|
- Docs site: https://ui.hilum.dev
|
|
13
13
|
- GitHub repository: https://github.com/hilum-labs/hilum-ui
|
|
14
14
|
- Machine-readable docs index: https://ui.hilum.dev/catalog.json
|
|
@@ -153,6 +153,7 @@ Primary exports:
|
|
|
153
153
|
- Spinner (/atoms/spinner/) — Animated loading indicator for async operations.
|
|
154
154
|
- Stacked List (/molecules/stacked-list/) — Vertically stacked rows with consistent padding and optional hover behavior. Composes: Badge · Avatar.
|
|
155
155
|
- Stat Card (/molecules/stat-card/) — Metric display card with label, value, optional trend, and optional icon. Composes: Card · Badge · Icon.
|
|
156
|
+
- Usage Bar (/molecules/usage-bar/) — Compact quota, readiness, and capacity meter with inline and stacked layouts. Composes: Progress.
|
|
156
157
|
- Status Badge (/atoms/status-badge/) — Semantic badge that maps status values to labels, variants, icons, and dots.
|
|
157
158
|
- Steps (/atoms/steps/) — Multi-step progress indicator with circles, bullets, and progress variants.
|
|
158
159
|
- Switch (/atoms/switch/) — Toggle between two states, on and off.
|
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
type UsageBarTone = "primary" | "warning" | "destructive" | "success" | "muted";
|
|
5
|
+
type UsageBarLayout = "stacked" | "inline";
|
|
6
|
+
|
|
7
|
+
interface UsageBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
label?: React.ReactNode;
|
|
9
|
+
value?: number;
|
|
10
|
+
max?: number;
|
|
11
|
+
percent?: number;
|
|
12
|
+
valueLabel?: React.ReactNode;
|
|
13
|
+
showPercent?: boolean;
|
|
14
|
+
layout?: UsageBarLayout;
|
|
15
|
+
tone?: UsageBarTone;
|
|
16
|
+
barClassName?: string;
|
|
17
|
+
indicatorClassName?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const toneClassName: Record<UsageBarTone, string> = {
|
|
21
|
+
primary: "bg-primary",
|
|
22
|
+
warning: "bg-warning",
|
|
23
|
+
destructive: "bg-destructive",
|
|
24
|
+
success: "bg-success",
|
|
25
|
+
muted: "bg-muted-foreground",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function clampPercent(value: number) {
|
|
29
|
+
if (!Number.isFinite(value)) return 0;
|
|
30
|
+
return Math.min(Math.max(value, 0), 100);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function inferTone(percent: number): UsageBarTone {
|
|
34
|
+
if (percent >= 90) return "destructive";
|
|
35
|
+
if (percent >= 75) return "warning";
|
|
36
|
+
return "primary";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatUsageValue(value: number | undefined, max: number | undefined) {
|
|
40
|
+
if (typeof value !== "number" || typeof max !== "number") return undefined;
|
|
41
|
+
return `${value.toLocaleString()} / ${max.toLocaleString()}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function UsageBar({
|
|
45
|
+
label,
|
|
46
|
+
value,
|
|
47
|
+
max,
|
|
48
|
+
percent,
|
|
49
|
+
valueLabel,
|
|
50
|
+
showPercent,
|
|
51
|
+
layout = "stacked",
|
|
52
|
+
tone,
|
|
53
|
+
className,
|
|
54
|
+
barClassName,
|
|
55
|
+
indicatorClassName,
|
|
56
|
+
...props
|
|
57
|
+
}: UsageBarProps) {
|
|
58
|
+
const resolvedPercent = clampPercent(
|
|
59
|
+
typeof percent === "number" ? percent : max && max > 0 && typeof value === "number" ? (value / max) * 100 : 0,
|
|
60
|
+
);
|
|
61
|
+
const resolvedTone = tone ?? inferTone(resolvedPercent);
|
|
62
|
+
const resolvedValueLabel = valueLabel ?? formatUsageValue(value, max);
|
|
63
|
+
const percentLabel = `${Math.round(resolvedPercent)}%`;
|
|
64
|
+
|
|
65
|
+
const bar = (
|
|
66
|
+
<div
|
|
67
|
+
className={cn("h-2 w-full overflow-hidden rounded-full bg-muted", layout === "inline" && "w-28", barClassName)}
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
>
|
|
70
|
+
<div
|
|
71
|
+
className={cn("h-full rounded-full transition-all", toneClassName[resolvedTone], indicatorClassName)}
|
|
72
|
+
style={{ width: `${resolvedPercent}%` }}
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (layout === "inline") {
|
|
78
|
+
return (
|
|
79
|
+
<div className={cn("flex min-w-0 items-center gap-2", className)} {...props}>
|
|
80
|
+
{label ? <span className="caption shrink-0 text-muted-foreground">{label}</span> : null}
|
|
81
|
+
{bar}
|
|
82
|
+
{showPercent !== false ? (
|
|
83
|
+
<span className="w-10 shrink-0 text-xs tabular-nums text-muted-foreground">{percentLabel}</span>
|
|
84
|
+
) : null}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className={cn("space-y-1.5", className)} {...props}>
|
|
91
|
+
{(label || resolvedValueLabel || showPercent) && (
|
|
92
|
+
<div className="flex min-w-0 items-center justify-between gap-3 text-sm">
|
|
93
|
+
{label ? <span className="min-w-0 truncate text-muted-foreground">{label}</span> : <span />}
|
|
94
|
+
<span className="shrink-0 font-medium tabular-nums text-foreground">
|
|
95
|
+
{resolvedValueLabel ?? (showPercent ? percentLabel : null)}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
{bar}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { UsageBar };
|
|
105
|
+
export type { UsageBarProps, UsageBarLayout, UsageBarTone };
|
package/src/index.ts
CHANGED