@annondeveloper/ui-kit 0.1.0 → 0.2.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.
Files changed (65) hide show
  1. package/dist/{chunk-5OKSXPWK.js → chunk-2DWZVHZS.js} +2 -2
  2. package/dist/chunk-2DWZVHZS.js.map +1 -0
  3. package/dist/form.d.ts +6 -6
  4. package/dist/form.js +1 -1
  5. package/dist/form.js.map +1 -1
  6. package/dist/index.d.ts +508 -52
  7. package/dist/index.js +2927 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/{select-nnBJUO8U.d.ts → select-B2wXqqSM.d.ts} +2 -2
  10. package/package.json +1 -1
  11. package/src/components/animated-counter.tsx +2 -1
  12. package/src/components/avatar.tsx +2 -1
  13. package/src/components/badge.tsx +3 -2
  14. package/src/components/button.tsx +3 -2
  15. package/src/components/card.tsx +13 -12
  16. package/src/components/checkbox.tsx +3 -2
  17. package/src/components/color-input.tsx +414 -0
  18. package/src/components/command-bar.tsx +434 -0
  19. package/src/components/confidence-bar.tsx +115 -0
  20. package/src/components/confirm-dialog.tsx +2 -1
  21. package/src/components/copy-block.tsx +229 -0
  22. package/src/components/data-table.tsx +2 -1
  23. package/src/components/diff-viewer.tsx +319 -0
  24. package/src/components/dropdown-menu.tsx +2 -1
  25. package/src/components/empty-state.tsx +2 -1
  26. package/src/components/filter-pill.tsx +2 -1
  27. package/src/components/form-input.tsx +5 -4
  28. package/src/components/heatmap-calendar.tsx +213 -0
  29. package/src/components/infinite-scroll.tsx +243 -0
  30. package/src/components/kanban-column.tsx +198 -0
  31. package/src/components/live-feed.tsx +220 -0
  32. package/src/components/log-viewer.tsx +2 -1
  33. package/src/components/metric-card.tsx +2 -1
  34. package/src/components/notification-stack.tsx +226 -0
  35. package/src/components/pipeline-stage.tsx +2 -1
  36. package/src/components/popover.tsx +2 -1
  37. package/src/components/port-status-grid.tsx +2 -1
  38. package/src/components/progress.tsx +2 -1
  39. package/src/components/radio-group.tsx +2 -1
  40. package/src/components/realtime-value.tsx +283 -0
  41. package/src/components/select.tsx +2 -1
  42. package/src/components/severity-timeline.tsx +2 -1
  43. package/src/components/sheet.tsx +2 -1
  44. package/src/components/skeleton.tsx +4 -3
  45. package/src/components/slider.tsx +2 -1
  46. package/src/components/smart-table.tsx +383 -0
  47. package/src/components/sortable-list.tsx +268 -0
  48. package/src/components/sparkline.tsx +2 -1
  49. package/src/components/status-badge.tsx +2 -1
  50. package/src/components/status-pulse.tsx +2 -1
  51. package/src/components/step-wizard.tsx +372 -0
  52. package/src/components/streaming-text.tsx +163 -0
  53. package/src/components/success-checkmark.tsx +2 -1
  54. package/src/components/tabs.tsx +2 -1
  55. package/src/components/threshold-gauge.tsx +2 -1
  56. package/src/components/time-range-selector.tsx +2 -1
  57. package/src/components/toast.tsx +2 -1
  58. package/src/components/toggle-switch.tsx +2 -1
  59. package/src/components/tooltip.tsx +2 -1
  60. package/src/components/truncated-text.tsx +2 -1
  61. package/src/components/typing-indicator.tsx +123 -0
  62. package/src/components/uptime-tracker.tsx +2 -1
  63. package/src/components/utilization-bar.tsx +2 -1
  64. package/src/utils.ts +1 -1
  65. package/dist/chunk-5OKSXPWK.js.map +0 -1
@@ -0,0 +1,283 @@
1
+ 'use client'
2
+
3
+ import type React from 'react'
4
+ import { useState, useEffect, useRef, useMemo } from 'react'
5
+ import { motion, AnimatePresence, useReducedMotion } from 'framer-motion'
6
+ import { ArrowUp, ArrowDown, Minus, Wifi, WifiOff, Loader2 } from 'lucide-react'
7
+ import { cn } from '../utils'
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Types
11
+ // ---------------------------------------------------------------------------
12
+
13
+ /** Props for the RealtimeValue component. */
14
+ export interface RealtimeValueProps {
15
+ /** The current value to display. */
16
+ value: number | string
17
+ /** Optional label displayed above or beside the value. */
18
+ label?: string
19
+ /** Custom formatter for numeric values. */
20
+ format?: (v: number) => string
21
+ /** ISO timestamp or Date of the last data update. */
22
+ lastUpdated?: string | Date
23
+ /** Milliseconds after which the value is considered stale. Default 30000. */
24
+ staleAfterMs?: number
25
+ /** Connection state to the data source. */
26
+ connectionState?: 'connected' | 'reconnecting' | 'disconnected'
27
+ /** Previous numeric value for delta/change display. */
28
+ previousValue?: number
29
+ /** Whether to animate value changes. Default true. */
30
+ animate?: boolean
31
+ /** Display size variant. */
32
+ size?: 'sm' | 'md' | 'lg' | 'xl'
33
+ /** Additional class name for the root element. */
34
+ className?: string
35
+ }
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Helpers
39
+ // ---------------------------------------------------------------------------
40
+
41
+ function easeOutCubic(t: number): number {
42
+ return 1 - Math.pow(1 - t, 3)
43
+ }
44
+
45
+ function formatRelativeSeconds(ms: number): string {
46
+ const secs = Math.floor(ms / 1000)
47
+ if (secs < 1) return 'just now'
48
+ if (secs < 60) return `${secs}s ago`
49
+ if (secs < 3600) return `${Math.floor(secs / 60)}m ago`
50
+ return `${Math.floor(secs / 3600)}h ago`
51
+ }
52
+
53
+ const SIZE_CLASSES = {
54
+ sm: { value: 'text-lg', label: 'text-[11px]', delta: 'text-[10px]', dot: 'h-1.5 w-1.5', gap: 'gap-1' },
55
+ md: { value: 'text-2xl', label: 'text-xs', delta: 'text-[11px]', dot: 'h-2 w-2', gap: 'gap-1.5' },
56
+ lg: { value: 'text-3xl', label: 'text-sm', delta: 'text-xs', dot: 'h-2.5 w-2.5', gap: 'gap-2' },
57
+ xl: { value: 'text-4xl', label: 'text-base', delta: 'text-sm', dot: 'h-3 w-3', gap: 'gap-2' },
58
+ } as const
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // AnimatedNumber (internal)
62
+ // ---------------------------------------------------------------------------
63
+
64
+ function AnimatedNumber({
65
+ value,
66
+ format,
67
+ duration = 400,
68
+ animateEnabled,
69
+ reduced,
70
+ className,
71
+ }: {
72
+ value: number
73
+ format?: (v: number) => string
74
+ duration?: number
75
+ animateEnabled: boolean
76
+ reduced: boolean | null
77
+ className?: string
78
+ }): React.JSX.Element {
79
+ const prevRef = useRef(value)
80
+ const rafRef = useRef<number | null>(null)
81
+ const [displayed, setDisplayed] = useState(value)
82
+
83
+ useEffect(() => {
84
+ const from = prevRef.current
85
+ const to = value
86
+ prevRef.current = value
87
+
88
+ if (reduced || !animateEnabled || from === to) {
89
+ setDisplayed(to)
90
+ return
91
+ }
92
+
93
+ const start = performance.now()
94
+ function tick(now: number) {
95
+ const elapsed = now - start
96
+ const progress = Math.min(elapsed / duration, 1)
97
+ const eased = easeOutCubic(progress)
98
+ setDisplayed(from + (to - from) * eased)
99
+ if (progress < 1) {
100
+ rafRef.current = requestAnimationFrame(tick)
101
+ } else {
102
+ setDisplayed(to)
103
+ }
104
+ }
105
+
106
+ rafRef.current = requestAnimationFrame(tick)
107
+ return () => {
108
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current)
109
+ }
110
+ }, [value, duration, reduced, animateEnabled])
111
+
112
+ const formatted = format
113
+ ? format(displayed)
114
+ : Number.isInteger(value)
115
+ ? Math.round(displayed).toString()
116
+ : displayed.toFixed(value.toString().split('.')[1]?.length ?? 1)
117
+
118
+ return <span className={cn('tabular-nums', className)}>{formatted}</span>
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // RealtimeValue
123
+ // ---------------------------------------------------------------------------
124
+
125
+ /**
126
+ * @description A live data display component with built-in freshness tracking,
127
+ * connection state indicators, animated value transitions, delta display,
128
+ * and auto-updating relative timestamps. Designed for real-time monitoring dashboards.
129
+ */
130
+ export function RealtimeValue({
131
+ value,
132
+ label,
133
+ format,
134
+ lastUpdated,
135
+ staleAfterMs = 30000,
136
+ connectionState = 'connected',
137
+ previousValue,
138
+ animate = true,
139
+ size = 'md',
140
+ className,
141
+ }: RealtimeValueProps): React.JSX.Element {
142
+ const prefersReducedMotion = useReducedMotion()
143
+ const sizeClasses = SIZE_CLASSES[size]
144
+
145
+ // Auto-updating staleness
146
+ const [staleness, setStaleness] = useState(0)
147
+ useEffect(() => {
148
+ if (!lastUpdated) return
149
+ const getMs = () => Date.now() - new Date(lastUpdated).getTime()
150
+ setStaleness(getMs())
151
+ const interval = setInterval(() => setStaleness(getMs()), 1000)
152
+ return () => clearInterval(interval)
153
+ }, [lastUpdated])
154
+
155
+ const isStale = lastUpdated ? staleness > staleAfterMs : false
156
+ const isVeryStale = lastUpdated ? staleness > staleAfterMs * 2 : false
157
+
158
+ // Freshness dot color
159
+ const freshnessColor = useMemo(() => {
160
+ if (connectionState === 'disconnected') return 'bg-[hsl(var(--status-critical))]'
161
+ if (isVeryStale) return 'bg-[hsl(var(--status-critical))]'
162
+ if (isStale) return 'bg-[hsl(var(--status-warning))]'
163
+ return 'bg-[hsl(var(--status-ok))]'
164
+ }, [connectionState, isStale, isVeryStale])
165
+
166
+ // Delta calculation
167
+ const delta = typeof value === 'number' && previousValue !== undefined ? value - previousValue : null
168
+ const deltaSign = delta !== null ? (delta > 0 ? '+' : delta < 0 ? '' : '') : null
169
+
170
+ const isNumeric = typeof value === 'number'
171
+
172
+ return (
173
+ <div
174
+ className={cn(
175
+ 'relative inline-flex flex-col',
176
+ sizeClasses.gap,
177
+ isStale && 'opacity-60',
178
+ className,
179
+ )}
180
+ title={
181
+ lastUpdated
182
+ ? `Last updated: ${new Date(lastUpdated).toLocaleString()} (${formatRelativeSeconds(staleness)})`
183
+ : undefined
184
+ }
185
+ >
186
+ {/* Label row */}
187
+ {label && (
188
+ <div className="flex items-center gap-1.5">
189
+ <span className={cn('font-medium text-[hsl(var(--text-secondary))]', sizeClasses.label)}>
190
+ {label}
191
+ </span>
192
+ </div>
193
+ )}
194
+
195
+ {/* Value row */}
196
+ <div className="flex items-center gap-2">
197
+ {/* Freshness indicator */}
198
+ <div className="relative flex items-center justify-center">
199
+ <span className={cn('rounded-full', sizeClasses.dot, freshnessColor)} />
200
+ {connectionState === 'connected' && !isStale && (
201
+ <span
202
+ className={cn(
203
+ 'absolute rounded-full animate-ping',
204
+ sizeClasses.dot,
205
+ 'bg-[hsl(var(--status-ok)/0.5)]',
206
+ )}
207
+ style={{ animationDuration: '2s' }}
208
+ />
209
+ )}
210
+ </div>
211
+
212
+ {/* Main value */}
213
+ <div className="flex items-baseline gap-1.5">
214
+ {isNumeric ? (
215
+ <AnimatedNumber
216
+ value={value}
217
+ format={format}
218
+ animateEnabled={animate}
219
+ reduced={prefersReducedMotion}
220
+ className={cn('font-semibold text-[hsl(var(--text-primary))]', sizeClasses.value)}
221
+ />
222
+ ) : (
223
+ <span className={cn('font-semibold text-[hsl(var(--text-primary))] tabular-nums', sizeClasses.value)}>
224
+ {value}
225
+ </span>
226
+ )}
227
+
228
+ {/* Delta indicator */}
229
+ <AnimatePresence>
230
+ {delta !== null && delta !== 0 && (
231
+ <motion.span
232
+ initial={prefersReducedMotion ? undefined : { opacity: 0, x: -4 }}
233
+ animate={prefersReducedMotion ? undefined : { opacity: 1, x: 0 }}
234
+ exit={prefersReducedMotion ? undefined : { opacity: 0, x: -4 }}
235
+ transition={{ duration: 0.15 }}
236
+ className={cn(
237
+ 'inline-flex items-center gap-0.5 font-medium tabular-nums',
238
+ sizeClasses.delta,
239
+ delta > 0 ? 'text-[hsl(var(--status-ok))]' : 'text-[hsl(var(--status-critical))]',
240
+ )}
241
+ >
242
+ {delta > 0 ? <ArrowUp className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />}
243
+ {deltaSign}{format ? format(Math.abs(delta)) : Math.abs(delta).toLocaleString()}
244
+ </motion.span>
245
+ )}
246
+ {delta === 0 && (
247
+ <motion.span
248
+ initial={prefersReducedMotion ? undefined : { opacity: 0 }}
249
+ animate={prefersReducedMotion ? undefined : { opacity: 1 }}
250
+ className={cn(
251
+ 'inline-flex items-center gap-0.5 font-medium text-[hsl(var(--text-tertiary))]',
252
+ sizeClasses.delta,
253
+ )}
254
+ >
255
+ <Minus className="h-3 w-3" />
256
+ 0
257
+ </motion.span>
258
+ )}
259
+ </AnimatePresence>
260
+ </div>
261
+
262
+ {/* Connection state icon */}
263
+ {connectionState !== 'connected' && (
264
+ <span className="ml-1">
265
+ {connectionState === 'reconnecting' && (
266
+ <Loader2 className="h-4 w-4 text-[hsl(var(--status-warning))] animate-spin" />
267
+ )}
268
+ {connectionState === 'disconnected' && (
269
+ <WifiOff className="h-4 w-4 text-[hsl(var(--status-critical))]" />
270
+ )}
271
+ </span>
272
+ )}
273
+ </div>
274
+
275
+ {/* Relative timestamp */}
276
+ {lastUpdated && (
277
+ <span className={cn('text-[hsl(var(--text-tertiary))] tabular-nums', sizeClasses.delta)}>
278
+ {formatRelativeSeconds(staleness)}
279
+ </span>
280
+ )}
281
+ </div>
282
+ )
283
+ }
@@ -1,5 +1,6 @@
1
1
  'use client'
2
2
 
3
+ import type React from 'react'
3
4
  import * as RadixSelect from '@radix-ui/react-select'
4
5
  import { ChevronDown, Check } from 'lucide-react'
5
6
  import { cn } from '../utils'
@@ -29,7 +30,7 @@ export interface SelectProps {
29
30
  */
30
31
  export function Select({
31
32
  value, onValueChange, options, placeholder, className, disabled,
32
- }: SelectProps) {
33
+ }: SelectProps): React.JSX.Element {
33
34
  return (
34
35
  <RadixSelect.Root value={value} onValueChange={onValueChange} disabled={disabled}>
35
36
  <RadixSelect.Trigger
@@ -1,5 +1,6 @@
1
1
  'use client'
2
2
 
3
+ import type React from 'react'
3
4
  import { useRef } from 'react'
4
5
  import { motion, AnimatePresence, useReducedMotion } from 'framer-motion'
5
6
  import { cn } from '../utils'
@@ -58,7 +59,7 @@ export function SeverityTimeline({
58
59
  maxVisible = 20,
59
60
  onEventClick,
60
61
  className,
61
- }: SeverityTimelineProps) {
62
+ }: SeverityTimelineProps): React.JSX.Element | null {
62
63
  const reduced = useReducedMotion()
63
64
  const scrollRef = useRef<HTMLDivElement>(null)
64
65
  const visible = events.slice(0, maxVisible)
@@ -1,5 +1,6 @@
1
1
  'use client'
2
2
 
3
+ import type React from 'react'
3
4
  import { useEffect, useCallback, useRef, type ReactNode } from 'react'
4
5
  import { motion, AnimatePresence, useReducedMotion } from 'framer-motion'
5
6
  import { X } from 'lucide-react'
@@ -52,7 +53,7 @@ export function Sheet({
52
53
  width = 'max-w-md',
53
54
  children,
54
55
  className,
55
- }: SheetProps) {
56
+ }: SheetProps): React.JSX.Element {
56
57
  const prefersReducedMotion = useReducedMotion()
57
58
  const panelRef = useRef<HTMLDivElement>(null)
58
59
 
@@ -1,12 +1,13 @@
1
1
  'use client'
2
2
 
3
+ import type React from 'react'
3
4
  import { cn } from '../utils'
4
5
 
5
6
  /**
6
7
  * @description A shimmer skeleton loader block. Requires the `skeleton-shimmer` CSS class
7
8
  * from theme.css to animate.
8
9
  */
9
- export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
10
+ export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element {
10
11
  return (
11
12
  <div
12
13
  className={cn('skeleton-shimmer rounded-md', className)}
@@ -25,7 +26,7 @@ export interface SkeletonTextProps {
25
26
  * @description A multi-line skeleton text placeholder. The last line renders shorter
26
27
  * for a natural paragraph appearance.
27
28
  */
28
- export function SkeletonText({ lines = 1, className }: SkeletonTextProps) {
29
+ export function SkeletonText({ lines = 1, className }: SkeletonTextProps): React.JSX.Element {
29
30
  return (
30
31
  <div className={cn('space-y-2', className)}>
31
32
  {Array.from({ length: lines }).map((_, i) => (
@@ -41,7 +42,7 @@ export function SkeletonText({ lines = 1, className }: SkeletonTextProps) {
41
42
  /**
42
43
  * @description A card-shaped skeleton placeholder with header area and content bars.
43
44
  */
44
- export function SkeletonCard({ className }: { className?: string }) {
45
+ export function SkeletonCard({ className }: { className?: string }): React.JSX.Element {
45
46
  return (
46
47
  <div className={cn('rounded-lg border border-[hsl(var(--border-subtle))] bg-[hsl(var(--bg-surface))] p-4 space-y-3', className)}>
47
48
  <div className="flex items-center gap-3">
@@ -1,5 +1,6 @@
1
1
  'use client'
2
2
 
3
+ import type React from 'react'
3
4
  import { useCallback, useRef, useState, type KeyboardEvent, type MouseEvent, type TouchEvent } from 'react'
4
5
  import { cn } from '../utils'
5
6
 
@@ -36,7 +37,7 @@ export function Slider({
36
37
  label,
37
38
  showValue = false,
38
39
  className,
39
- }: SliderProps) {
40
+ }: SliderProps): React.JSX.Element {
40
41
  const trackRef = useRef<HTMLDivElement>(null)
41
42
  const [isDragging, setIsDragging] = useState(false)
42
43
  const [isHovering, setIsHovering] = useState(false)