@annondeveloper/ui-kit 0.1.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 +215 -0
- package/dist/chunk-5OKSXPWK.js +270 -0
- package/dist/chunk-5OKSXPWK.js.map +1 -0
- package/dist/cli/index.js +430 -0
- package/dist/form.d.ts +65 -0
- package/dist/form.js +148 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +942 -0
- package/dist/index.js +2812 -0
- package/dist/index.js.map +1 -0
- package/dist/select-nnBJUO8U.d.ts +26 -0
- package/package.json +114 -0
- package/src/components/animated-counter.stories.tsx +68 -0
- package/src/components/animated-counter.tsx +85 -0
- package/src/components/avatar.tsx +106 -0
- package/src/components/badge.stories.tsx +70 -0
- package/src/components/badge.tsx +97 -0
- package/src/components/button.stories.tsx +101 -0
- package/src/components/button.tsx +67 -0
- package/src/components/card.tsx +128 -0
- package/src/components/checkbox.stories.tsx +64 -0
- package/src/components/checkbox.tsx +58 -0
- package/src/components/confirm-dialog.stories.tsx +96 -0
- package/src/components/confirm-dialog.tsx +145 -0
- package/src/components/data-table.stories.tsx +125 -0
- package/src/components/data-table.tsx +791 -0
- package/src/components/dropdown-menu.tsx +111 -0
- package/src/components/empty-state.stories.tsx +42 -0
- package/src/components/empty-state.tsx +43 -0
- package/src/components/filter-pill.stories.tsx +71 -0
- package/src/components/filter-pill.tsx +45 -0
- package/src/components/form-input.stories.tsx +91 -0
- package/src/components/form-input.tsx +77 -0
- package/src/components/log-viewer.tsx +212 -0
- package/src/components/metric-card.tsx +141 -0
- package/src/components/pipeline-stage.tsx +134 -0
- package/src/components/popover.tsx +72 -0
- package/src/components/port-status-grid.tsx +102 -0
- package/src/components/progress.tsx +128 -0
- package/src/components/radio-group.tsx +162 -0
- package/src/components/select.stories.tsx +52 -0
- package/src/components/select.tsx +92 -0
- package/src/components/severity-timeline.tsx +125 -0
- package/src/components/sheet.tsx +164 -0
- package/src/components/skeleton.stories.tsx +64 -0
- package/src/components/skeleton.tsx +62 -0
- package/src/components/slider.tsx +208 -0
- package/src/components/sparkline.tsx +104 -0
- package/src/components/status-badge.stories.tsx +84 -0
- package/src/components/status-badge.tsx +71 -0
- package/src/components/status-pulse.stories.tsx +56 -0
- package/src/components/status-pulse.tsx +78 -0
- package/src/components/success-checkmark.stories.tsx +67 -0
- package/src/components/success-checkmark.tsx +53 -0
- package/src/components/tabs.tsx +177 -0
- package/src/components/threshold-gauge.tsx +149 -0
- package/src/components/time-range-selector.tsx +86 -0
- package/src/components/toast.stories.tsx +70 -0
- package/src/components/toast.tsx +48 -0
- package/src/components/toggle-switch.stories.tsx +66 -0
- package/src/components/toggle-switch.tsx +51 -0
- package/src/components/tooltip.tsx +62 -0
- package/src/components/truncated-text.stories.tsx +56 -0
- package/src/components/truncated-text.tsx +80 -0
- package/src/components/uptime-tracker.tsx +138 -0
- package/src/components/utilization-bar.tsx +103 -0
- package/src/theme.css +178 -0
- package/src/utils.ts +123 -0
package/src/theme.css
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @annondeveloper/ui-kit — Theme Tokens
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* 1. Import this file in your app's global CSS:
|
|
6
|
+
* @import '@annondeveloper/ui-kit/theme.css';
|
|
7
|
+
* 2. All components use `hsl(var(--token-name))` for colors.
|
|
8
|
+
* 3. Dark mode is the default (:root). Add class "light" to <html> for light mode.
|
|
9
|
+
* 4. Override any token by redefining the custom property in your own CSS.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ── Dark mode (default) ─────────────────────────────────────────────────── */
|
|
13
|
+
:root {
|
|
14
|
+
color-scheme: dark;
|
|
15
|
+
|
|
16
|
+
--bg-base: 222 47% 7%;
|
|
17
|
+
--bg-surface: 222 40% 10%;
|
|
18
|
+
--bg-elevated: 222 35% 14%;
|
|
19
|
+
--bg-overlay: 222 30% 18%;
|
|
20
|
+
|
|
21
|
+
--border-subtle: 222 30% 20%;
|
|
22
|
+
--border-default: 222 25% 25%;
|
|
23
|
+
--border-strong: 222 20% 35%;
|
|
24
|
+
|
|
25
|
+
--text-primary: 210 40% 95%;
|
|
26
|
+
--text-secondary: 210 20% 70%;
|
|
27
|
+
--text-tertiary: 210 15% 50%;
|
|
28
|
+
--text-disabled: 210 10% 35%;
|
|
29
|
+
|
|
30
|
+
--brand-primary: 217 91% 60%;
|
|
31
|
+
--brand-secondary: 258 80% 65%;
|
|
32
|
+
--text-on-brand: 0 0% 100%;
|
|
33
|
+
|
|
34
|
+
--status-ok: 142 71% 45%;
|
|
35
|
+
--status-warning: 38 92% 50%;
|
|
36
|
+
--status-critical: 0 84% 60%;
|
|
37
|
+
--status-unknown: 220 15% 50%;
|
|
38
|
+
--status-maintenance: 258 60% 65%;
|
|
39
|
+
|
|
40
|
+
--util-low: 142 71% 45%;
|
|
41
|
+
--util-medium: 38 92% 50%;
|
|
42
|
+
--util-high: 0 84% 60%;
|
|
43
|
+
|
|
44
|
+
--chart-1: 217 91% 60%;
|
|
45
|
+
--chart-2: 142 71% 45%;
|
|
46
|
+
--chart-3: 38 92% 50%;
|
|
47
|
+
--chart-4: 258 80% 65%;
|
|
48
|
+
--chart-5: 16 86% 58%;
|
|
49
|
+
--chart-6: 186 87% 47%;
|
|
50
|
+
--chart-7: 316 72% 63%;
|
|
51
|
+
--chart-8: 60 80% 55%;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* ── Light mode ──────────────────────────────────────────────────────────── */
|
|
55
|
+
html.light {
|
|
56
|
+
color-scheme: light;
|
|
57
|
+
|
|
58
|
+
--bg-base: 210 40% 98%;
|
|
59
|
+
--bg-surface: 0 0% 100%;
|
|
60
|
+
--bg-elevated: 210 40% 96%;
|
|
61
|
+
--bg-overlay: 210 30% 92%;
|
|
62
|
+
|
|
63
|
+
--border-subtle: 210 20% 88%;
|
|
64
|
+
--border-default: 210 20% 80%;
|
|
65
|
+
--border-strong: 210 20% 65%;
|
|
66
|
+
|
|
67
|
+
--text-primary: 222 47% 11%;
|
|
68
|
+
--text-secondary: 222 25% 35%;
|
|
69
|
+
--text-tertiary: 222 15% 55%;
|
|
70
|
+
--text-disabled: 222 10% 75%;
|
|
71
|
+
|
|
72
|
+
--brand-primary: 217 91% 50%;
|
|
73
|
+
--brand-secondary: 258 80% 58%;
|
|
74
|
+
--text-on-brand: 0 0% 100%;
|
|
75
|
+
|
|
76
|
+
--status-ok: 142 71% 38%;
|
|
77
|
+
--status-warning: 38 92% 42%;
|
|
78
|
+
--status-critical: 0 84% 52%;
|
|
79
|
+
--status-unknown: 220 15% 55%;
|
|
80
|
+
--status-maintenance: 258 60% 58%;
|
|
81
|
+
|
|
82
|
+
--util-low: 142 71% 38%;
|
|
83
|
+
--util-medium: 38 92% 42%;
|
|
84
|
+
--util-high: 0 84% 52%;
|
|
85
|
+
|
|
86
|
+
--chart-1: 217 91% 50%;
|
|
87
|
+
--chart-2: 142 71% 38%;
|
|
88
|
+
--chart-3: 38 92% 42%;
|
|
89
|
+
--chart-4: 258 80% 58%;
|
|
90
|
+
--chart-5: 0 84% 52%;
|
|
91
|
+
--chart-6: 199 89% 42%;
|
|
92
|
+
--chart-7: 316 70% 50%;
|
|
93
|
+
--chart-8: 162 60% 38%;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ── Typography scale ────────────────────────────────────────────────────── */
|
|
97
|
+
.text-display { font-size: 2.25rem; font-weight: 700; letter-spacing: -0.02em; text-wrap: balance; }
|
|
98
|
+
.text-heading-1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.01em; text-wrap: balance; }
|
|
99
|
+
.text-heading-2 { font-size: 1.25rem; font-weight: 600; text-wrap: balance; }
|
|
100
|
+
.text-heading-3 { font-size: 1rem; font-weight: 600; text-wrap: balance; }
|
|
101
|
+
.text-body { font-size: 0.875rem; font-weight: 400; }
|
|
102
|
+
.text-small { font-size: 0.75rem; font-weight: 400; }
|
|
103
|
+
.text-label { font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
104
|
+
|
|
105
|
+
/* ── Animations ──────────────────────────────────────────────────────────── */
|
|
106
|
+
@keyframes pulse-ring {
|
|
107
|
+
0% { transform: scale(1); opacity: 0.6; }
|
|
108
|
+
70% { transform: scale(1.8); opacity: 0; }
|
|
109
|
+
100% { transform: scale(1.8); opacity: 0; }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@keyframes shimmer {
|
|
113
|
+
0% { background-position: -200% 0; }
|
|
114
|
+
100% { background-position: 200% 0; }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.animate-pulse-ring {
|
|
118
|
+
animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.animate-shimmer {
|
|
122
|
+
background: linear-gradient(
|
|
123
|
+
90deg,
|
|
124
|
+
hsl(var(--bg-elevated)) 25%,
|
|
125
|
+
hsl(var(--bg-overlay)) 50%,
|
|
126
|
+
hsl(var(--bg-elevated)) 75%
|
|
127
|
+
);
|
|
128
|
+
background-size: 200% 100%;
|
|
129
|
+
animation: shimmer 1.5s infinite;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.skeleton-shimmer {
|
|
133
|
+
background: linear-gradient(
|
|
134
|
+
90deg,
|
|
135
|
+
hsl(var(--bg-elevated)) 0%,
|
|
136
|
+
hsl(var(--bg-overlay)) 50%,
|
|
137
|
+
hsl(var(--bg-elevated)) 100%
|
|
138
|
+
);
|
|
139
|
+
background-size: 200% 100%;
|
|
140
|
+
animation: shimmer 1.5s ease-in-out infinite;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ── Toast (Sonner) overrides ────────────────────────────────────────────── */
|
|
144
|
+
.toast-success [data-icon] svg { color: hsl(var(--status-ok)); }
|
|
145
|
+
.toast-error [data-icon] svg { color: hsl(var(--status-critical)); }
|
|
146
|
+
.toast-warning [data-icon] svg { color: hsl(var(--status-warning)); }
|
|
147
|
+
.toast-info [data-icon] svg { color: hsl(var(--brand-primary)); }
|
|
148
|
+
|
|
149
|
+
[data-sonner-toaster] [data-button] {
|
|
150
|
+
background: hsl(var(--brand-primary));
|
|
151
|
+
color: hsl(var(--text-primary));
|
|
152
|
+
border-radius: 0.5rem;
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
font-size: 0.8125rem;
|
|
155
|
+
padding: 0.25rem 0.625rem;
|
|
156
|
+
}
|
|
157
|
+
[data-sonner-toaster] [data-button]:hover {
|
|
158
|
+
background: hsl(var(--brand-secondary));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
[data-sonner-toaster] [data-close-button] {
|
|
162
|
+
background: hsl(var(--bg-surface));
|
|
163
|
+
border: 1px solid hsl(var(--border-subtle));
|
|
164
|
+
color: hsl(var(--text-secondary));
|
|
165
|
+
}
|
|
166
|
+
[data-sonner-toaster] [data-close-button]:hover {
|
|
167
|
+
background: hsl(var(--bg-overlay));
|
|
168
|
+
color: hsl(var(--text-primary));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* ── Reduced motion ──────────────────────────────────────────────────────── */
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.animate-pulse-ring,
|
|
174
|
+
.animate-shimmer,
|
|
175
|
+
.skeleton-shimmer {
|
|
176
|
+
animation: none;
|
|
177
|
+
}
|
|
178
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from 'clsx'
|
|
2
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
|
+
|
|
4
|
+
/** Merge Tailwind CSS class names with conflict resolution. */
|
|
5
|
+
export function cn(...inputs: ClassValue[]) {
|
|
6
|
+
return twMerge(clsx(inputs))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Format bits/sec into human-readable string (clamps negative to 0). */
|
|
10
|
+
export function fmtBps(bps: number): string {
|
|
11
|
+
const v = Math.max(0, bps)
|
|
12
|
+
if (v >= 1e12) return `${(v / 1e12).toFixed(1)} Tbps`
|
|
13
|
+
if (v >= 1e9) return `${(v / 1e9).toFixed(1)} Gbps`
|
|
14
|
+
if (v >= 1e6) return `${(v / 1e6).toFixed(1)} Mbps`
|
|
15
|
+
if (v >= 1e3) return `${(v / 1e3).toFixed(1)} Kbps`
|
|
16
|
+
return `${v.toFixed(0)} bps`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Format link speed for interface display (e.g. 1000000000 -> "1G"). */
|
|
20
|
+
export function fmtSpeed(bps: number): string {
|
|
21
|
+
if (bps >= 1e12) return `${(bps / 1e12).toFixed(0)}T`
|
|
22
|
+
if (bps >= 1e9) return `${(bps / 1e9).toFixed(0)}G`
|
|
23
|
+
if (bps >= 1e6) return `${(bps / 1e6).toFixed(0)}M`
|
|
24
|
+
if (bps >= 1e3) return `${(bps / 1e3).toFixed(0)}K`
|
|
25
|
+
return `${bps.toFixed(0)}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Format utilization as percentage of link speed. */
|
|
29
|
+
export function fmtUtil(bps: number, speedBps: number): string {
|
|
30
|
+
if (speedBps <= 0) return '\u2014'
|
|
31
|
+
const pct = (Math.max(0, bps) / speedBps) * 100
|
|
32
|
+
if (pct < 0.01) return '0%'
|
|
33
|
+
if (pct < 1) return `${pct.toFixed(2)}%`
|
|
34
|
+
if (pct < 10) return `${pct.toFixed(1)}%`
|
|
35
|
+
return `${pct.toFixed(0)}%`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Format bytes into human-readable string. */
|
|
39
|
+
export function fmtBytes(bytes: number): string {
|
|
40
|
+
if (bytes >= 1e12) return `${(bytes / 1e12).toFixed(1)} TB`
|
|
41
|
+
if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)} GB`
|
|
42
|
+
if (bytes >= 1e6) return `${(bytes / 1e6).toFixed(1)} MB`
|
|
43
|
+
if (bytes >= 1e3) return `${(bytes / 1e3).toFixed(1)} KB`
|
|
44
|
+
return `${bytes.toFixed(0)} B`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Format percent. */
|
|
48
|
+
export function fmtPct(v: number, decimals = 1): string {
|
|
49
|
+
return `${v.toFixed(decimals)}%`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Format duration in seconds to human-readable uptime string. */
|
|
53
|
+
export function fmtUptime(secs: number): string {
|
|
54
|
+
if (secs < 60) return `${secs}s`
|
|
55
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m`
|
|
56
|
+
if (secs < 86400) return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`
|
|
57
|
+
return `${Math.floor(secs / 86400)}d ${Math.floor((secs % 86400) / 3600)}h`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Format ISO timestamp to relative time ("3m ago"). */
|
|
61
|
+
export function fmtRelative(isoStr: string): string {
|
|
62
|
+
const diff = (Date.now() - new Date(isoStr).getTime()) / 1000
|
|
63
|
+
if (diff < 60) return 'just now'
|
|
64
|
+
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`
|
|
65
|
+
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`
|
|
66
|
+
return `${Math.floor(diff / 86400)}d ago`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Format large numbers compactly (e.g. 480933305 -> "480.9M", 1234 -> "1.2K"). */
|
|
70
|
+
export function fmtCompact(n: number): string {
|
|
71
|
+
const abs = Math.abs(n)
|
|
72
|
+
const sign = n < 0 ? '-' : ''
|
|
73
|
+
if (abs >= 1e12) return `${sign}${(abs / 1e12).toFixed(1)}T`
|
|
74
|
+
if (abs >= 1e9) return `${sign}${(abs / 1e9).toFixed(1)}B`
|
|
75
|
+
if (abs >= 1e6) return `${sign}${(abs / 1e6).toFixed(1)}M`
|
|
76
|
+
if (abs >= 1e3) return `${sign}${(abs / 1e3).toFixed(1)}K`
|
|
77
|
+
if (abs >= 1) return `${sign}${abs.toFixed(0)}`
|
|
78
|
+
if (abs >= 0.01) return `${sign}${abs.toFixed(2)}`
|
|
79
|
+
return `${sign}${abs.toFixed(0)}`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Format seconds duration compactly (e.g. for replication lag display). */
|
|
83
|
+
export function fmtDuration(secs: number): string {
|
|
84
|
+
if (secs < 0.001) return '0s'
|
|
85
|
+
if (secs < 1) return `${(secs * 1000).toFixed(0)}ms`
|
|
86
|
+
if (secs < 60) return `${secs.toFixed(1)}s`
|
|
87
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m ${Math.floor(secs % 60)}s`
|
|
88
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Strip CIDR suffix from IP address (e.g. "10.0.0.1/32" -> "10.0.0.1"). */
|
|
92
|
+
export function stripCidr(ip: string): string {
|
|
93
|
+
return ip.replace(/\/\d+$/, '')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Clamp a number between min and max. */
|
|
97
|
+
export function clamp(v: number, min: number, max: number): number {
|
|
98
|
+
return Math.min(Math.max(v, min), max)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Color thresholds for utilization percentage mapping. */
|
|
102
|
+
export interface UtilColorMap {
|
|
103
|
+
high: { threshold: number; className: string }
|
|
104
|
+
medium: { threshold: number; className: string }
|
|
105
|
+
low: { className: string }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Default utilization color map using CSS custom property token classes. */
|
|
109
|
+
export const defaultUtilColorMap: UtilColorMap = {
|
|
110
|
+
high: { threshold: 80, className: 'text-[hsl(var(--status-critical))]' },
|
|
111
|
+
medium: { threshold: 60, className: 'text-[hsl(var(--status-warning))]' },
|
|
112
|
+
low: { className: 'text-[hsl(var(--status-ok))]' },
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get utilization color class from value 0-100.
|
|
117
|
+
* Accepts an optional color map to override the default thresholds and classes.
|
|
118
|
+
*/
|
|
119
|
+
export function utilColor(pct: number, colorMap: UtilColorMap = defaultUtilColorMap): string {
|
|
120
|
+
if (pct >= colorMap.high.threshold) return colorMap.high.className
|
|
121
|
+
if (pct >= colorMap.medium.threshold) return colorMap.medium.className
|
|
122
|
+
return colorMap.low.className
|
|
123
|
+
}
|