bits-lab 0.0.0-stage → 0.1.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.
Files changed (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -2
  3. package/dist/Accordion.d.ts +52 -0
  4. package/dist/Accordion.js +192 -0
  5. package/dist/AchievementToast.d.ts +36 -0
  6. package/dist/AchievementToast.js +162 -0
  7. package/dist/AgentCard.d.ts +41 -0
  8. package/dist/AgentCard.js +168 -0
  9. package/dist/ArcadeButtons.d.ts +23 -0
  10. package/dist/ArcadeButtons.js +169 -0
  11. package/dist/AuroraBands.d.ts +19 -0
  12. package/dist/AuroraBands.js +126 -0
  13. package/dist/AvatarTooltips.d.ts +32 -0
  14. package/dist/AvatarTooltips.js +121 -0
  15. package/dist/BarRace.d.ts +39 -0
  16. package/dist/BarRace.js +227 -0
  17. package/dist/BentoGrid.d.ts +50 -0
  18. package/dist/BentoGrid.js +145 -0
  19. package/dist/BootSequence.d.ts +48 -0
  20. package/dist/BootSequence.js +234 -0
  21. package/dist/BreadcrumbTrail.d.ts +31 -0
  22. package/dist/BreadcrumbTrail.js +181 -0
  23. package/dist/CRTPower.d.ts +30 -0
  24. package/dist/CRTPower.js +142 -0
  25. package/dist/CardDeck.d.ts +29 -0
  26. package/dist/CardDeck.js +154 -0
  27. package/dist/ChangelogTimeline.d.ts +35 -0
  28. package/dist/ChangelogTimeline.js +137 -0
  29. package/dist/ChapterProgress.d.ts +23 -0
  30. package/dist/ChapterProgress.js +211 -0
  31. package/dist/ChatBubbles.d.ts +42 -0
  32. package/dist/ChatBubbles.js +198 -0
  33. package/dist/ChiptuneKit.d.ts +158 -0
  34. package/dist/ChiptuneKit.js +207 -0
  35. package/dist/CipherCard.d.ts +22 -0
  36. package/dist/CipherCard.js +91 -0
  37. package/dist/CloudSky.d.ts +28 -0
  38. package/dist/CloudSky.js +222 -0
  39. package/dist/CodeRain.d.ts +24 -0
  40. package/dist/CodeRain.js +158 -0
  41. package/dist/CodeShowcase.d.ts +50 -0
  42. package/dist/CodeShowcase.js +307 -0
  43. package/dist/ColorDial.d.ts +42 -0
  44. package/dist/ColorDial.js +288 -0
  45. package/dist/CommandPalette.d.ts +59 -0
  46. package/dist/CommandPalette.js +349 -0
  47. package/dist/CompareSlider.d.ts +33 -0
  48. package/dist/CompareSlider.js +152 -0
  49. package/dist/Confetti.d.ts +61 -0
  50. package/dist/Confetti.js +176 -0
  51. package/dist/ConnectBeams.d.ts +39 -0
  52. package/dist/ConnectBeams.js +132 -0
  53. package/dist/ContextOrb.d.ts +34 -0
  54. package/dist/ContextOrb.js +269 -0
  55. package/dist/ContourLines.d.ts +20 -0
  56. package/dist/ContourLines.js +159 -0
  57. package/dist/Coverflow.d.ts +27 -0
  58. package/dist/Coverflow.js +167 -0
  59. package/dist/CursorAttractor.d.ts +21 -0
  60. package/dist/CursorAttractor.js +136 -0
  61. package/dist/CutReveal.d.ts +21 -0
  62. package/dist/CutReveal.js +116 -0
  63. package/dist/DateRange.d.ts +45 -0
  64. package/dist/DateRange.js +327 -0
  65. package/dist/DeviceFrame.d.ts +30 -0
  66. package/dist/DeviceFrame.js +145 -0
  67. package/dist/DiffReveal.d.ts +36 -0
  68. package/dist/DiffReveal.js +247 -0
  69. package/dist/DirectionHover.d.ts +15 -0
  70. package/dist/DirectionHover.js +90 -0
  71. package/dist/Discard.d.ts +18 -0
  72. package/dist/Discard.js +61 -0
  73. package/dist/DitherField.d.ts +15 -0
  74. package/dist/DitherField.js +157 -0
  75. package/dist/DotGlobe.d.ts +48 -0
  76. package/dist/DotGlobe.js +312 -0
  77. package/dist/DotMap.d.ts +47 -0
  78. package/dist/DotMap.js +172 -0
  79. package/dist/DropZone.d.ts +57 -0
  80. package/dist/DropZone.js +304 -0
  81. package/dist/DynamicIsland.d.ts +32 -0
  82. package/dist/DynamicIsland.js +92 -0
  83. package/dist/EdgeFX.d.ts +27 -0
  84. package/dist/EdgeFX.js +98 -0
  85. package/dist/ElasticString.d.ts +22 -0
  86. package/dist/ElasticString.js +127 -0
  87. package/dist/ExpandCard.d.ts +44 -0
  88. package/dist/ExpandCard.js +182 -0
  89. package/dist/FeatureTabs.d.ts +34 -0
  90. package/dist/FeatureTabs.js +169 -0
  91. package/dist/FeedList.d.ts +37 -0
  92. package/dist/FeedList.js +163 -0
  93. package/dist/FlickerGrid.d.ts +29 -0
  94. package/dist/FlickerGrid.js +156 -0
  95. package/dist/FocusGrid.d.ts +20 -0
  96. package/dist/FocusGrid.js +48 -0
  97. package/dist/FocusPull.d.ts +26 -0
  98. package/dist/FocusPull.js +106 -0
  99. package/dist/Footprints.d.ts +24 -0
  100. package/dist/Footprints.js +126 -0
  101. package/dist/FunnelFlow.d.ts +34 -0
  102. package/dist/FunnelFlow.js +267 -0
  103. package/dist/GameOver.d.ts +46 -0
  104. package/dist/GameOver.js +191 -0
  105. package/dist/GaugeMeter.d.ts +38 -0
  106. package/dist/GaugeMeter.js +195 -0
  107. package/dist/GlassPanel.d.ts +27 -0
  108. package/dist/GlassPanel.js +80 -0
  109. package/dist/GrainFX.d.ts +34 -0
  110. package/dist/GrainFX.js +122 -0
  111. package/dist/GravityBox.d.ts +30 -0
  112. package/dist/GravityBox.js +220 -0
  113. package/dist/HealthBar.d.ts +33 -0
  114. package/dist/HealthBar.js +140 -0
  115. package/dist/Heatmap.d.ts +42 -0
  116. package/dist/Heatmap.js +232 -0
  117. package/dist/HexField.d.ts +29 -0
  118. package/dist/HexField.js +188 -0
  119. package/dist/HoldToConfirm.d.ts +29 -0
  120. package/dist/HoldToConfirm.js +163 -0
  121. package/dist/HorizontalTrack.d.ts +28 -0
  122. package/dist/HorizontalTrack.js +195 -0
  123. package/dist/HotbarDock.d.ts +40 -0
  124. package/dist/HotbarDock.js +148 -0
  125. package/dist/InkText.d.ts +27 -0
  126. package/dist/InkText.js +65 -0
  127. package/dist/InlineEdit.d.ts +30 -0
  128. package/dist/InlineEdit.js +198 -0
  129. package/dist/InteractiveGrid.d.ts +25 -0
  130. package/dist/InteractiveGrid.js +194 -0
  131. package/dist/InventoryGrid.d.ts +45 -0
  132. package/dist/InventoryGrid.js +272 -0
  133. package/dist/JellyBlob.d.ts +30 -0
  134. package/dist/JellyBlob.js +209 -0
  135. package/dist/KineticHeadline.d.ts +30 -0
  136. package/dist/KineticHeadline.js +133 -0
  137. package/dist/LavaLamp.d.ts +21 -0
  138. package/dist/LavaLamp.js +133 -0
  139. package/dist/LetterSwap.d.ts +20 -0
  140. package/dist/LetterSwap.js +78 -0
  141. package/dist/LevelMap.d.ts +35 -0
  142. package/dist/LevelMap.js +189 -0
  143. package/dist/LightShafts.d.ts +22 -0
  144. package/dist/LightShafts.js +170 -0
  145. package/dist/LightboxGallery.d.ts +26 -0
  146. package/dist/LightboxGallery.js +327 -0
  147. package/dist/LinkPeek.d.ts +36 -0
  148. package/dist/LinkPeek.js +194 -0
  149. package/dist/LogoOrbit.d.ts +37 -0
  150. package/dist/LogoOrbit.js +117 -0
  151. package/dist/Marquee.d.ts +33 -0
  152. package/dist/Marquee.js +148 -0
  153. package/dist/MarqueeLine.d.ts +33 -0
  154. package/dist/MarqueeLine.js +167 -0
  155. package/dist/MasonryWall.d.ts +13 -0
  156. package/dist/MasonryWall.js +90 -0
  157. package/dist/MegaMenu.d.ts +32 -0
  158. package/dist/MegaMenu.js +280 -0
  159. package/dist/MeteorShower.d.ts +24 -0
  160. package/dist/MeteorShower.js +155 -0
  161. package/dist/Minimap.d.ts +21 -0
  162. package/dist/Minimap.js +236 -0
  163. package/dist/MobileTabBar.d.ts +38 -0
  164. package/dist/MobileTabBar.js +148 -0
  165. package/dist/MorphDialog.d.ts +34 -0
  166. package/dist/MorphDialog.js +199 -0
  167. package/dist/MorphPopover.d.ts +33 -0
  168. package/dist/MorphPopover.js +168 -0
  169. package/dist/MorphWords.d.ts +18 -0
  170. package/dist/MorphWords.js +136 -0
  171. package/dist/OTPInput.d.ts +43 -0
  172. package/dist/OTPInput.js +174 -0
  173. package/dist/OverlayKit.d.ts +97 -0
  174. package/dist/OverlayKit.js +466 -0
  175. package/dist/OverlayMenu.d.ts +32 -0
  176. package/dist/OverlayMenu.js +154 -0
  177. package/dist/ParallaxCollage.d.ts +41 -0
  178. package/dist/ParallaxCollage.js +143 -0
  179. package/dist/PasswordMeter.d.ts +44 -0
  180. package/dist/PasswordMeter.js +165 -0
  181. package/dist/PegBoard.d.ts +26 -0
  182. package/dist/PegBoard.js +184 -0
  183. package/dist/PinCard.d.ts +21 -0
  184. package/dist/PinCard.js +84 -0
  185. package/dist/PixelAvatar.d.ts +33 -0
  186. package/dist/PixelAvatar.js +144 -0
  187. package/dist/PixelButton.d.ts +20 -0
  188. package/dist/PixelButton.js +128 -0
  189. package/dist/PixelExplode.d.ts +31 -0
  190. package/dist/PixelExplode.js +164 -0
  191. package/dist/PixelLoader.d.ts +32 -0
  192. package/dist/PixelLoader.js +142 -0
  193. package/dist/PixelParallax.d.ts +41 -0
  194. package/dist/PixelParallax.js +151 -0
  195. package/dist/PixelSpinners.d.ts +21 -0
  196. package/dist/PixelSpinners.js +110 -0
  197. package/dist/PixelType.d.ts +26 -0
  198. package/dist/PixelType.js +220 -0
  199. package/dist/PointerKit.d.ts +23 -0
  200. package/dist/PointerKit.js +222 -0
  201. package/dist/Polaroids.d.ts +41 -0
  202. package/dist/Polaroids.js +293 -0
  203. package/dist/PressStart.d.ts +28 -0
  204. package/dist/PressStart.js +99 -0
  205. package/dist/PricingFlip.d.ts +52 -0
  206. package/dist/PricingFlip.js +190 -0
  207. package/dist/ProximityType.d.ts +26 -0
  208. package/dist/ProximityType.js +135 -0
  209. package/dist/QuestDialog.d.ts +50 -0
  210. package/dist/QuestDialog.js +204 -0
  211. package/dist/QuestLog.d.ts +39 -0
  212. package/dist/QuestLog.js +214 -0
  213. package/dist/RangeDual.d.ts +34 -0
  214. package/dist/RangeDual.js +238 -0
  215. package/dist/RatingPips.d.ts +25 -0
  216. package/dist/RatingPips.js +129 -0
  217. package/dist/RetroHorizon.d.ts +27 -0
  218. package/dist/RetroHorizon.js +169 -0
  219. package/dist/Reveal.d.ts +30 -0
  220. package/dist/Reveal.js +92 -0
  221. package/dist/RingProgress.d.ts +45 -0
  222. package/dist/RingProgress.js +180 -0
  223. package/dist/RollingNumber.d.ts +28 -0
  224. package/dist/RollingNumber.js +108 -0
  225. package/dist/ScanScope.d.ts +24 -0
  226. package/dist/ScanScope.js +164 -0
  227. package/dist/ScoreTicker.d.ts +32 -0
  228. package/dist/ScoreTicker.js +176 -0
  229. package/dist/ScreenScroller.d.ts +24 -0
  230. package/dist/ScreenScroller.js +138 -0
  231. package/dist/Screensaver.d.ts +31 -0
  232. package/dist/Screensaver.js +221 -0
  233. package/dist/ScrollTilt.d.ts +27 -0
  234. package/dist/ScrollTilt.js +100 -0
  235. package/dist/ScrollTypography.d.ts +26 -0
  236. package/dist/ScrollTypography.js +124 -0
  237. package/dist/ScrollZoom.d.ts +31 -0
  238. package/dist/ScrollZoom.js +123 -0
  239. package/dist/ScrubNumber.d.ts +29 -0
  240. package/dist/ScrubNumber.js +152 -0
  241. package/dist/ShadowStack.d.ts +28 -0
  242. package/dist/ShadowStack.js +116 -0
  243. package/dist/SideRail.d.ts +43 -0
  244. package/dist/SideRail.js +253 -0
  245. package/dist/SignalText.d.ts +25 -0
  246. package/dist/SignalText.js +178 -0
  247. package/dist/SkeletonShimmer.d.ts +18 -0
  248. package/dist/SkeletonShimmer.js +111 -0
  249. package/dist/SketchHighlight.d.ts +27 -0
  250. package/dist/SketchHighlight.js +180 -0
  251. package/dist/SlideTabs.d.ts +40 -0
  252. package/dist/SlideTabs.js +190 -0
  253. package/dist/SmartNavbar.d.ts +48 -0
  254. package/dist/SmartNavbar.js +188 -0
  255. package/dist/SparkChart.d.ts +41 -0
  256. package/dist/SparkChart.js +188 -0
  257. package/dist/Spoiler.d.ts +22 -0
  258. package/dist/Spoiler.js +97 -0
  259. package/dist/SpotlightTour.d.ts +38 -0
  260. package/dist/SpotlightTour.js +231 -0
  261. package/dist/SpringDrawer.d.ts +39 -0
  262. package/dist/SpringDrawer.js +360 -0
  263. package/dist/SpriteAnimator.d.ts +41 -0
  264. package/dist/SpriteAnimator.js +124 -0
  265. package/dist/StatCard.d.ts +43 -0
  266. package/dist/StatCard.js +168 -0
  267. package/dist/StepLoader.d.ts +41 -0
  268. package/dist/StepLoader.js +191 -0
  269. package/dist/StepperForm.d.ts +41 -0
  270. package/dist/StepperForm.js +206 -0
  271. package/dist/StickerBoard.d.ts +39 -0
  272. package/dist/StickerBoard.js +209 -0
  273. package/dist/StickyReveal.d.ts +42 -0
  274. package/dist/StickyReveal.js +181 -0
  275. package/dist/StoryViewer.d.ts +36 -0
  276. package/dist/StoryViewer.js +175 -0
  277. package/dist/StreamingText.d.ts +29 -0
  278. package/dist/StreamingText.js +271 -0
  279. package/dist/SuggestionChips.d.ts +32 -0
  280. package/dist/SuggestionChips.js +96 -0
  281. package/dist/SwipeActions.d.ts +30 -0
  282. package/dist/SwipeActions.js +141 -0
  283. package/dist/TagInput.d.ts +30 -0
  284. package/dist/TagInput.js +284 -0
  285. package/dist/TallyUp.d.ts +38 -0
  286. package/dist/TallyUp.js +157 -0
  287. package/dist/TerminalDemo.d.ts +63 -0
  288. package/dist/TerminalDemo.js +214 -0
  289. package/dist/TestimonialWall.d.ts +35 -0
  290. package/dist/TestimonialWall.js +131 -0
  291. package/dist/TextOnPath.d.ts +36 -0
  292. package/dist/TextOnPath.js +130 -0
  293. package/dist/ThinkingShimmer.d.ts +36 -0
  294. package/dist/ThinkingShimmer.js +132 -0
  295. package/dist/ToastStack.d.ts +62 -0
  296. package/dist/ToastStack.js +289 -0
  297. package/dist/ToggleGroup.d.ts +39 -0
  298. package/dist/ToggleGroup.js +190 -0
  299. package/dist/ToggleKit.d.ts +70 -0
  300. package/dist/ToggleKit.js +229 -0
  301. package/dist/TracingLine.d.ts +24 -0
  302. package/dist/TracingLine.js +139 -0
  303. package/dist/TradingCard.d.ts +30 -0
  304. package/dist/TradingCard.js +147 -0
  305. package/dist/VanishInput.d.ts +34 -0
  306. package/dist/VanishInput.js +190 -0
  307. package/dist/VideoHero.d.ts +32 -0
  308. package/dist/VideoHero.js +207 -0
  309. package/dist/VoiceOrb.d.ts +23 -0
  310. package/dist/VoiceOrb.js +182 -0
  311. package/dist/VoxelTiles.d.ts +38 -0
  312. package/dist/VoxelTiles.js +201 -0
  313. package/dist/WarpField.d.ts +18 -0
  314. package/dist/WarpField.js +160 -0
  315. package/dist/WebcamPixels.d.ts +38 -0
  316. package/dist/WebcamPixels.js +292 -0
  317. package/dist/WheelPicker.d.ts +29 -0
  318. package/dist/WheelPicker.js +199 -0
  319. package/dist/WobbleCard.d.ts +23 -0
  320. package/dist/WobbleCard.js +112 -0
  321. package/dist/index.d.ts +158 -0
  322. package/dist/index.js +159 -0
  323. package/package.json +58 -4
@@ -0,0 +1,124 @@
1
+ 'use client';
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
+ function SpriteAnimator({
5
+ src,
6
+ frameWidth = 16,
7
+ frameHeight = 16,
8
+ animations,
9
+ animation,
10
+ row = 0,
11
+ frames = 8,
12
+ fps = 10,
13
+ scale = 6,
14
+ loop = true,
15
+ pingPong = false,
16
+ playing = true,
17
+ flipX = false,
18
+ label = "Animated sprite",
19
+ onEnd,
20
+ className = "",
21
+ style
22
+ }) {
23
+ const canvasRef = useRef(null);
24
+ const state = useRef({ image: null, frame: 0, direction: 1, elapsed: 0 });
25
+ const onEndRef = useRef(onEnd);
26
+ onEndRef.current = onEnd;
27
+ const current = animations && animations[animation] || { row, frames, fps };
28
+ const config = { row: current.row ?? 0, frames: Math.max(1, current.frames ?? 1), fps: current.fps ?? fps, loop: current.loop ?? loop, pingPong: current.pingPong ?? pingPong };
29
+ useEffect(() => {
30
+ if (!src) return void 0;
31
+ let cancelled = false;
32
+ const image = new Image();
33
+ image.onload = () => {
34
+ if (cancelled) return;
35
+ state.current.image = image;
36
+ draw();
37
+ };
38
+ image.src = src;
39
+ return () => {
40
+ cancelled = true;
41
+ };
42
+ }, [src]);
43
+ function draw() {
44
+ const canvas = canvasRef.current;
45
+ const { image, frame } = state.current;
46
+ if (!canvas || !image) return;
47
+ const ctx = canvas.getContext("2d");
48
+ ctx.imageSmoothingEnabled = false;
49
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
50
+ ctx.save();
51
+ if (flipX) {
52
+ ctx.translate(canvas.width, 0);
53
+ ctx.scale(-1, 1);
54
+ }
55
+ ctx.drawImage(image, frame * frameWidth, config.row * frameHeight, frameWidth, frameHeight, 0, 0, canvas.width, canvas.height);
56
+ ctx.restore();
57
+ }
58
+ useEffect(() => {
59
+ state.current.frame = 0;
60
+ state.current.direction = 1;
61
+ state.current.elapsed = 0;
62
+ draw();
63
+ }, [animation, config.row, config.frames, flipX, scale]);
64
+ useEffect(() => {
65
+ const canvas = canvasRef.current;
66
+ const reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
67
+ if (!canvas || !playing || reduced || config.frames < 2) return void 0;
68
+ let visible = true;
69
+ const observer = new IntersectionObserver(([entry]) => {
70
+ visible = entry.isIntersecting;
71
+ });
72
+ observer.observe(canvas);
73
+ let frameId = 0;
74
+ let last = performance.now();
75
+ const tick = (now) => {
76
+ const s = state.current;
77
+ const dt = Math.min(250, now - last);
78
+ last = now;
79
+ if (visible && s.image) {
80
+ s.elapsed += dt;
81
+ const step = 1e3 / Math.max(1, config.fps);
82
+ while (s.elapsed >= step) {
83
+ s.elapsed -= step;
84
+ let next = s.frame + s.direction;
85
+ if (config.pingPong && (next >= config.frames || next < 0)) {
86
+ s.direction *= -1;
87
+ next = s.frame + s.direction;
88
+ }
89
+ if (!config.pingPong && next >= config.frames) {
90
+ if (!config.loop) {
91
+ onEndRef.current?.();
92
+ draw();
93
+ return;
94
+ }
95
+ next = 0;
96
+ }
97
+ s.frame = Math.max(0, Math.min(config.frames - 1, next));
98
+ }
99
+ draw();
100
+ }
101
+ frameId = requestAnimationFrame(tick);
102
+ };
103
+ frameId = requestAnimationFrame(tick);
104
+ return () => {
105
+ cancelAnimationFrame(frameId);
106
+ observer.disconnect();
107
+ };
108
+ }, [playing, config.fps, config.frames, config.row, config.loop, config.pingPong, src]);
109
+ return /* @__PURE__ */ jsx(
110
+ "canvas",
111
+ {
112
+ ref: canvasRef,
113
+ role: "img",
114
+ "aria-label": label,
115
+ width: frameWidth * scale,
116
+ height: frameHeight * scale,
117
+ className,
118
+ style: { imageRendering: "pixelated", width: frameWidth * scale, height: frameHeight * scale, maxWidth: "100%", ...style }
119
+ }
120
+ );
121
+ }
122
+ export {
123
+ SpriteAnimator as default
124
+ };
@@ -0,0 +1,43 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ export type StatFormat = 'compact' | 'currency' | 'percent' | 'number' | ((value: number) => string);
3
+ export interface StatCardProps {
4
+ label?: string;
5
+ value?: number;
6
+ /** The previous value; the delta is computed from it. */
7
+ previous?: number;
8
+ /** Or pass the delta directly (0.12 = +12%). */
9
+ delta?: number;
10
+ /** What the delta compares to. */
11
+ period?: string;
12
+ /** Whether up is good (green) or bad (red). */
13
+ goodDirection?: 'up' | 'down' | 'neutral';
14
+ format?: StatFormat;
15
+ currency?: string;
16
+ locale?: string;
17
+ /** Sparkline points (12 is ideal). */
18
+ trend?: number[];
19
+ trendLabels?: string[];
20
+ icon?: ReactNode;
21
+ /** Roll duration in ms. */
22
+ duration?: number;
23
+ width?: number;
24
+ valueSize?: number;
25
+ sparkHeight?: number;
26
+ backgroundColor?: string;
27
+ textColor?: string;
28
+ mutedColor?: string;
29
+ lineColor?: string;
30
+ trendColor?: string;
31
+ accentColor?: string;
32
+ goodColor?: string;
33
+ badColor?: string;
34
+ radius?: number;
35
+ fontFamily?: string;
36
+ className?: string;
37
+ style?: CSSProperties;
38
+ }
39
+ export declare function formatStat(value: number, format?: StatFormat, { locale, currency }?: {
40
+ locale?: string;
41
+ currency?: string;
42
+ }): string;
43
+ export default function StatCard({ label, value, previous, delta, period, goodDirection, format, currency, locale, trend, trendLabels, icon, duration, width, valueSize, sparkHeight, backgroundColor, textColor, mutedColor, lineColor, trendColor, accentColor, goodColor, badColor, radius, fontFamily, className, style }: StatCardProps): import("react").JSX.Element;
@@ -0,0 +1,168 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useInsertionEffect, useRef, useState } from "react";
4
+ const STYLE_ID = "bl-stat-card";
5
+ const CSS = `
6
+ .bl-stat { position: relative; display: grid; gap: 6px; width: 100%; max-width: var(--bl-w); box-sizing: border-box; padding: 16px 18px 14px; border-radius: var(--bl-radius); background: var(--bl-bg); color: var(--bl-ink); font-family: var(--bl-font); box-shadow: 0 0 0 1px var(--bl-line); }
7
+ .bl-stat__top { display: flex; align-items: center; gap: 8px; color: var(--bl-muted); font-size: 13px; font-weight: 600; }
8
+ .bl-stat__icon { display: grid; place-items: center; width: 22px; height: 22px; }
9
+ .bl-stat__value { font-size: var(--bl-size); font-weight: 650; line-height: 1.1; letter-spacing: -0.01em; }
10
+ .bl-stat__delta { display: flex; align-items: center; gap: 6px; font-size: 13px; }
11
+ .bl-stat__chip { display: inline-flex; align-items: center; gap: 3px; font-weight: 700; color: var(--bl-delta); }
12
+ .bl-stat__period { color: var(--bl-muted); }
13
+ .bl-stat__spark { position: relative; height: var(--bl-spark-h); margin-top: 6px; }
14
+ .bl-stat__spark svg { display: block; width: 100%; height: 100%; overflow: visible; }
15
+ .bl-stat__line { fill: none; stroke: var(--bl-trend); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
16
+ .bl-stat__area { fill: var(--bl-trend); opacity: 0.1; }
17
+ .bl-stat__cross { stroke: var(--bl-muted); stroke-width: 1; vector-effect: non-scaling-stroke; }
18
+ .bl-stat__tip { position: absolute; bottom: calc(100% + 6px); padding: 5px 8px; border-radius: 7px; background: var(--bl-ink); color: var(--bl-bg); font-size: 12px; line-height: 1.3; white-space: nowrap; pointer-events: none; transform: translateX(-50%); }
19
+ .bl-stat__tip b { display: block; font-size: 13px; }
20
+ .bl-stat__dot { position: absolute; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%; box-shadow: 0 0 0 2px var(--bl-bg); pointer-events: none; }
21
+ .bl-stat__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
22
+ `;
23
+ function useStyles() {
24
+ useInsertionEffect(() => {
25
+ if (typeof document === "undefined") return;
26
+ const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
27
+ style.textContent = CSS;
28
+ if (!style.isConnected) document.head.appendChild(style);
29
+ }, []);
30
+ }
31
+ function formatStat(value, format = "compact", { locale, currency = "USD" } = {}) {
32
+ if (typeof format === "function") return format(value);
33
+ const options = format === "currency" ? { style: "currency", currency, currencyDisplay: "narrowSymbol", notation: Math.abs(value) >= 1e4 ? "compact" : "standard", maximumFractionDigits: Math.abs(value) >= 1e4 ? 1 : 2 } : format === "percent" ? { style: "percent", maximumFractionDigits: 1 } : format === "compact" ? { notation: Math.abs(value) >= 1e4 ? "compact" : "standard", maximumFractionDigits: Math.abs(value) >= 1e4 ? 1 : 0 } : { maximumFractionDigits: 2 };
34
+ return new Intl.NumberFormat(locale, options).format(value);
35
+ }
36
+ function StatCard({
37
+ label = "Metric",
38
+ value = 0,
39
+ previous,
40
+ delta,
41
+ period = "vs last period",
42
+ goodDirection = "up",
43
+ format = "compact",
44
+ currency = "USD",
45
+ locale,
46
+ trend = [],
47
+ trendLabels,
48
+ icon,
49
+ duration = 900,
50
+ width = 300,
51
+ valueSize = 34,
52
+ sparkHeight = 44,
53
+ backgroundColor = "#1a1c2c",
54
+ textColor = "#f4f4f4",
55
+ mutedColor = "#94b0c2",
56
+ lineColor = "#333c57",
57
+ trendColor = "#566c86",
58
+ accentColor = "#3987e5",
59
+ goodColor = "#0ca30c",
60
+ badColor = "#e66767",
61
+ radius = 14,
62
+ fontFamily = "ui-sans-serif, system-ui, sans-serif",
63
+ className = "",
64
+ style
65
+ }) {
66
+ useStyles();
67
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
68
+ const [shown, setShown] = useState(value);
69
+ const shownRef = useRef(value);
70
+ const [hover, setHover] = useState(null);
71
+ const sparkRef = useRef(null);
72
+ const fmt = (v) => formatStat(v, format, { locale, currency });
73
+ useEffect(() => {
74
+ const from = shownRef.current;
75
+ if (from === value) return void 0;
76
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches || !duration) {
77
+ shownRef.current = value;
78
+ setShown(value);
79
+ return void 0;
80
+ }
81
+ const start = performance.now();
82
+ let frame = 0;
83
+ const tick = (now) => {
84
+ const p = Math.min(1, (now - start) / duration);
85
+ const eased = 1 - (1 - p) ** 3;
86
+ const v = from + (value - from) * eased;
87
+ shownRef.current = v;
88
+ setShown(v);
89
+ if (p < 1) frame = requestAnimationFrame(tick);
90
+ };
91
+ frame = requestAnimationFrame(tick);
92
+ return () => cancelAnimationFrame(frame);
93
+ }, [value, duration]);
94
+ const change = delta ?? (previous != null && previous !== 0 ? (value - previous) / Math.abs(previous) : null);
95
+ const direction = change == null || change === 0 ? "flat" : change > 0 ? "up" : "down";
96
+ const good = goodDirection === "neutral" || direction === "flat" ? null : direction === goodDirection;
97
+ const deltaColor = good == null ? mutedColor : good ? goodColor : badColor;
98
+ const deltaText = change == null ? "" : `${change > 0 ? "+" : change < 0 ? "\u2212" : ""}${new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 1 }).format(Math.abs(change))}`;
99
+ const n = trend.length;
100
+ const lo = Math.min(...trend);
101
+ const hi = Math.max(...trend);
102
+ const span = hi - lo || 1;
103
+ const pts = trend.map((v, i) => [n > 1 ? i / (n - 1) * 100 : 50, 38 - (v - lo) / span * 34]);
104
+ const line = pts.map((p, i) => `${i ? "L" : "M"}${p[0].toFixed(2)} ${p[1].toFixed(2)}`).join(" ");
105
+ const area = n > 1 ? `${line} L100 40 L0 40 Z` : "";
106
+ const at = hover ?? n - 1;
107
+ const onMove = (event) => {
108
+ const rect = sparkRef.current.getBoundingClientRect();
109
+ const x = (event.clientX - rect.left) / rect.width;
110
+ setHover(Math.max(0, Math.min(n - 1, Math.round(x * (n - 1)))));
111
+ };
112
+ const summary = n > 1 ? `Trend: from ${fmt(trend[0])} to ${fmt(trend[n - 1])}, low ${fmt(lo)}, high ${fmt(hi)}.` : "";
113
+ return /* @__PURE__ */ jsxs(
114
+ "div",
115
+ {
116
+ className: `bl-stat ${className}`,
117
+ role: "group",
118
+ "aria-labelledby": `${uid}-label`,
119
+ style: {
120
+ "--bl-w": `${width}px`,
121
+ "--bl-size": `${valueSize}px`,
122
+ "--bl-spark-h": `${sparkHeight}px`,
123
+ "--bl-bg": backgroundColor,
124
+ "--bl-ink": textColor,
125
+ "--bl-muted": mutedColor,
126
+ "--bl-line": lineColor,
127
+ "--bl-trend": trendColor,
128
+ "--bl-delta": deltaColor,
129
+ "--bl-radius": `${radius}px`,
130
+ "--bl-font": fontFamily,
131
+ ...style
132
+ },
133
+ children: [
134
+ /* @__PURE__ */ jsxs("div", { className: "bl-stat__top", children: [
135
+ icon && /* @__PURE__ */ jsx("span", { className: "bl-stat__icon", "aria-hidden": "true", children: icon }),
136
+ /* @__PURE__ */ jsx("span", { id: `${uid}-label`, children: label })
137
+ ] }),
138
+ /* @__PURE__ */ jsx("div", { className: "bl-stat__value", "aria-hidden": "true", children: fmt(shown) }),
139
+ /* @__PURE__ */ jsx("span", { className: "bl-stat__sr", children: fmt(value) }),
140
+ change != null && /* @__PURE__ */ jsxs("div", { className: "bl-stat__delta", children: [
141
+ /* @__PURE__ */ jsxs("span", { className: "bl-stat__chip", children: [
142
+ /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: direction === "up" ? /* @__PURE__ */ jsx("path", { d: "M6 10V2M2.5 5.5 6 2l3.5 3.5" }) : direction === "down" ? /* @__PURE__ */ jsx("path", { d: "M6 2v8M2.5 6.5 6 10l3.5-3.5" }) : /* @__PURE__ */ jsx("path", { d: "M2 6h8" }) }),
143
+ /* @__PURE__ */ jsx("span", { className: "bl-stat__sr", children: direction === "up" ? "Up" : direction === "down" ? "Down" : "No change" }),
144
+ deltaText
145
+ ] }),
146
+ /* @__PURE__ */ jsx("span", { className: "bl-stat__period", children: period })
147
+ ] }),
148
+ n > 1 && /* @__PURE__ */ jsxs("div", { ref: sparkRef, className: "bl-stat__spark", onPointerMove: onMove, onPointerLeave: () => setHover(null), "aria-hidden": "true", children: [
149
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 40", preserveAspectRatio: "none", children: [
150
+ /* @__PURE__ */ jsx("path", { className: "bl-stat__area", d: area }),
151
+ /* @__PURE__ */ jsx("path", { className: "bl-stat__line", d: line }),
152
+ hover != null && /* @__PURE__ */ jsx("line", { className: "bl-stat__cross", x1: pts[hover][0], x2: pts[hover][0], y1: 0, y2: 40 })
153
+ ] }),
154
+ /* @__PURE__ */ jsx("span", { className: "bl-stat__dot", style: { left: `${pts[at][0]}%`, top: `${pts[at][1] / 40 * 100}%`, background: at === n - 1 ? accentColor : textColor } }),
155
+ hover != null && /* @__PURE__ */ jsxs("span", { className: "bl-stat__tip", style: { left: `${Math.max(12, Math.min(88, pts[hover][0]))}%` }, children: [
156
+ /* @__PURE__ */ jsx("b", { children: fmt(trend[hover]) }),
157
+ trendLabels?.[hover] ?? `Point ${hover + 1} of ${n}`
158
+ ] })
159
+ ] }),
160
+ summary && /* @__PURE__ */ jsx("span", { className: "bl-stat__sr", children: summary })
161
+ ]
162
+ }
163
+ );
164
+ }
165
+ export {
166
+ StatCard as default,
167
+ formatStat
168
+ };
@@ -0,0 +1,41 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ export type StepStatus = 'running' | 'done' | 'error';
3
+ export interface LoaderStep {
4
+ id?: string;
5
+ label: string;
6
+ description?: ReactNode;
7
+ }
8
+ export interface StepLoaderProps {
9
+ steps?: LoaderStep[];
10
+ /** Index of the step in progress. */
11
+ current?: number;
12
+ status?: StepStatus;
13
+ title?: ReactNode;
14
+ open?: boolean;
15
+ /** Show as a modal overlay. */
16
+ fixed?: boolean;
17
+ onCancel?: () => void;
18
+ onDone?: () => void;
19
+ onRetry?: () => void;
20
+ cancelLabel?: string;
21
+ doneLabel?: string;
22
+ retryLabel?: string;
23
+ successMessage?: string;
24
+ errorMessage?: string;
25
+ width?: number;
26
+ maxListHeight?: number;
27
+ backgroundColor?: string;
28
+ textColor?: string;
29
+ mutedColor?: string;
30
+ trackColor?: string;
31
+ lineColor?: string;
32
+ accentColor?: string;
33
+ onAccentColor?: string;
34
+ errorColor?: string;
35
+ backdropColor?: string;
36
+ radius?: number;
37
+ fontFamily?: string;
38
+ className?: string;
39
+ style?: CSSProperties;
40
+ }
41
+ export default function StepLoader({ steps, current, status, title, open, fixed, onCancel, onDone, onRetry, cancelLabel, doneLabel, retryLabel, successMessage, errorMessage, width, maxListHeight, backgroundColor, textColor, mutedColor, trackColor, lineColor, accentColor, onAccentColor, errorColor, backdropColor, radius, fontFamily, className, style }: StepLoaderProps): import("react").JSX.Element | null;
@@ -0,0 +1,191 @@
1
+ 'use client';
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useInsertionEffect, useRef } from "react";
4
+ const STYLE_ID = "bl-step-loader";
5
+ const CSS = `
6
+ .bl-steps { position: relative; width: 100%; max-width: var(--bl-w); box-sizing: border-box; padding: 22px; border-radius: var(--bl-radius); background: var(--bl-bg); color: var(--bl-ink); font-family: var(--bl-font); box-shadow: 0 0 0 1px var(--bl-line), 0 30px 60px -30px rgba(0,0,0,0.85); }
7
+ .bl-steps-overlay { position: fixed; inset: 0; z-index: 1900; display: grid; place-items: center; padding: 16px; background: var(--bl-backdrop); animation: bl-steps-fade 200ms both; }
8
+ .bl-steps-overlay > .bl-steps { animation: bl-steps-pop 360ms cubic-bezier(.3,1.35,.5,1) both; }
9
+ .bl-steps__title { margin: 0 0 4px; font-size: 17px; font-weight: 700; }
10
+ .bl-steps__meta { display: flex; justify-content: space-between; gap: 12px; color: var(--bl-muted); font-size: 13px; }
11
+ .bl-steps__bar { height: 6px; margin: 10px 0 18px; overflow: hidden; border-radius: 999px; background: var(--bl-track); }
12
+ .bl-steps__fill { height: 100%; width: calc(var(--bl-p) * 100%); border-radius: inherit; background: var(--bl-accent); transition: width 500ms cubic-bezier(.2,.9,.25,1), background-color 200ms; }
13
+ .bl-steps[data-status="error"] .bl-steps__fill { background: var(--bl-error); }
14
+ .bl-steps__list { position: relative; display: grid; gap: 0; max-height: var(--bl-max-h); margin: 0; padding: 0; overflow-y: auto; list-style: none; }
15
+ .bl-steps__step { position: relative; display: grid; grid-template-columns: 28px 1fr; gap: 12px; padding-bottom: 16px; color: var(--bl-muted); transition: color 200ms; }
16
+ .bl-steps__step:last-child { padding-bottom: 0; }
17
+ .bl-steps__step:not(:last-child)::before { content: ""; position: absolute; left: 13px; top: 30px; bottom: 2px; width: 2px; background: var(--bl-track); }
18
+ .bl-steps__step:not(:last-child)::after { content: ""; position: absolute; left: 13px; top: 30px; bottom: 2px; width: 2px; background: var(--bl-accent); transform-origin: top; transform: scaleY(0); transition: transform 400ms cubic-bezier(.2,.9,.25,1); }
19
+ .bl-steps__step[data-state="done"]::after { transform: scaleY(1); }
20
+ .bl-steps__step[data-state="active"], .bl-steps__step[data-state="done"] { color: var(--bl-ink); }
21
+ .bl-steps__step[data-state="error"] { color: var(--bl-error); }
22
+ .bl-steps__icon { position: relative; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--bl-track); color: var(--bl-on-accent); }
23
+ .bl-steps__step[data-state="done"] .bl-steps__icon { background: var(--bl-accent); animation: bl-steps-pop-icon 360ms cubic-bezier(.3,1.6,.5,1); }
24
+ .bl-steps__step[data-state="error"] .bl-steps__icon { background: var(--bl-error); }
25
+ .bl-steps__spin { position: absolute; inset: 0; border-radius: 50%; border: 3px solid var(--bl-track); border-top-color: var(--bl-accent); animation: bl-steps-spin 800ms linear infinite; }
26
+ .bl-steps__icon path { stroke-dasharray: 20; stroke-dashoffset: 20; animation: bl-steps-draw 320ms 80ms cubic-bezier(.2,.9,.25,1) forwards; }
27
+ .bl-steps__text { display: grid; gap: 2px; padding-top: 4px; min-width: 0; }
28
+ .bl-steps__label { font-size: 15px; font-weight: 600; }
29
+ .bl-steps__desc { color: var(--bl-muted); font-size: 13px; line-height: 1.4; }
30
+ .bl-steps__actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
31
+ .bl-steps__btn { padding: 9px 16px; border: 0; border-radius: 8px; background: var(--bl-track); color: var(--bl-ink); font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
32
+ .bl-steps__btn[data-primary] { background: var(--bl-accent); color: var(--bl-on-accent); }
33
+ .bl-steps__btn:hover { filter: brightness(1.15); }
34
+ .bl-steps__btn:focus-visible { outline: 2px solid var(--bl-accent); outline-offset: 2px; }
35
+ .bl-steps__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
36
+ @keyframes bl-steps-spin { to { transform: rotate(360deg); } }
37
+ @keyframes bl-steps-draw { to { stroke-dashoffset: 0; } }
38
+ @keyframes bl-steps-pop-icon { 40% { transform: scale(1.25); } }
39
+ @keyframes bl-steps-fade { from { opacity: 0; } }
40
+ @keyframes bl-steps-pop { from { opacity: 0; transform: translateY(12px) scale(0.96); } }
41
+ @media (prefers-reduced-motion: reduce) {
42
+ .bl-steps__spin { animation-duration: 2.4s; }
43
+ .bl-steps__icon path { animation: none; stroke-dashoffset: 0; }
44
+ .bl-steps__fill, .bl-steps__step::after { transition: none; }
45
+ .bl-steps__step[data-state="done"] .bl-steps__icon, .bl-steps-overlay, .bl-steps-overlay > .bl-steps { animation: none; }
46
+ }
47
+ `;
48
+ function useStyles() {
49
+ useInsertionEffect(() => {
50
+ if (typeof document === "undefined") return;
51
+ const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
52
+ style.textContent = CSS;
53
+ if (!style.isConnected) document.head.appendChild(style);
54
+ }, []);
55
+ }
56
+ const Check = () => /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2.5 7.5 5.5 10.5 11.5 3.5" }) });
57
+ const Cross = () => /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2.5 2.5 9.5 9.5M9.5 2.5 2.5 9.5" }) });
58
+ function StepLoader({
59
+ steps = [],
60
+ current = 0,
61
+ status = "running",
62
+ title = "Working on it",
63
+ open = true,
64
+ fixed = false,
65
+ onCancel,
66
+ onDone,
67
+ onRetry,
68
+ cancelLabel = "Cancel",
69
+ doneLabel = "Done",
70
+ retryLabel = "Retry",
71
+ successMessage = "All done!",
72
+ errorMessage = "Something went wrong.",
73
+ width = 440,
74
+ maxListHeight = 320,
75
+ backgroundColor = "#1a1c2c",
76
+ textColor = "#f4f4f4",
77
+ mutedColor = "#94b0c2",
78
+ trackColor = "#333c57",
79
+ lineColor = "#333c57",
80
+ accentColor = "#38b764",
81
+ onAccentColor = "#0b0b14",
82
+ errorColor = "#b13e53",
83
+ backdropColor = "rgba(8,8,16,0.65)",
84
+ radius = 16,
85
+ fontFamily = "ui-sans-serif, system-ui, sans-serif",
86
+ className = "",
87
+ style
88
+ }) {
89
+ useStyles();
90
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
91
+ const listRef = useRef(null);
92
+ const actionRef = useRef(null);
93
+ const total = steps.length;
94
+ const finished = status === "done";
95
+ const failed = status === "error";
96
+ const index = Math.max(0, Math.min(total - 1, current));
97
+ const doneCount = finished ? total : index;
98
+ const progress = total ? doneCount / total : 0;
99
+ const stateOf = (i) => finished || i < index ? "done" : i === index ? failed ? "error" : "active" : "pending";
100
+ useEffect(() => {
101
+ const list = listRef.current;
102
+ const node = list?.children[index];
103
+ if (!list || !node) return;
104
+ const top = node.offsetTop;
105
+ const bottom = top + node.offsetHeight;
106
+ const behavior = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";
107
+ if (top < list.scrollTop) list.scrollTo({ top, behavior });
108
+ else if (bottom > list.scrollTop + list.clientHeight) list.scrollTo({ top: bottom - list.clientHeight, behavior });
109
+ }, [index]);
110
+ useEffect(() => {
111
+ if (open && fixed) actionRef.current?.focus({ preventScroll: true });
112
+ }, [open, fixed, status]);
113
+ useEffect(() => {
114
+ if (!open || !fixed) return void 0;
115
+ const onKey = (event) => {
116
+ if (event.key === "Escape" && !finished && !failed) onCancel?.();
117
+ };
118
+ window.addEventListener("keydown", onKey);
119
+ return () => window.removeEventListener("keydown", onKey);
120
+ }, [open, fixed, finished, failed, onCancel]);
121
+ if (!open) return null;
122
+ const announcement = finished ? successMessage : failed ? `${errorMessage} ${steps[index]?.label ?? ""}` : `Step ${index + 1} of ${total}: ${steps[index]?.label ?? ""}`;
123
+ const panel = /* @__PURE__ */ jsxs(
124
+ "div",
125
+ {
126
+ className: `bl-steps ${className}`,
127
+ role: fixed ? "dialog" : "region",
128
+ "aria-modal": fixed || void 0,
129
+ "aria-labelledby": `${uid}-title`,
130
+ "aria-busy": !finished && !failed,
131
+ "data-status": status,
132
+ style: {
133
+ "--bl-w": `${width}px`,
134
+ "--bl-max-h": `${maxListHeight}px`,
135
+ "--bl-p": progress,
136
+ "--bl-bg": backgroundColor,
137
+ "--bl-ink": textColor,
138
+ "--bl-muted": mutedColor,
139
+ "--bl-track": trackColor,
140
+ "--bl-line": lineColor,
141
+ "--bl-accent": accentColor,
142
+ "--bl-on-accent": onAccentColor,
143
+ "--bl-error": errorColor,
144
+ "--bl-radius": `${radius}px`,
145
+ "--bl-font": fontFamily,
146
+ ...style
147
+ },
148
+ children: [
149
+ /* @__PURE__ */ jsx("h2", { id: `${uid}-title`, className: "bl-steps__title", children: finished ? successMessage : failed ? errorMessage : title }),
150
+ /* @__PURE__ */ jsxs("div", { className: "bl-steps__meta", "aria-hidden": "true", children: [
151
+ /* @__PURE__ */ jsx("span", { children: finished ? `${total} of ${total} steps` : `Step ${index + 1} of ${total}` }),
152
+ /* @__PURE__ */ jsxs("span", { children: [
153
+ Math.round(progress * 100),
154
+ "%"
155
+ ] })
156
+ ] }),
157
+ /* @__PURE__ */ jsx("div", { className: "bl-steps__bar", role: "progressbar", "aria-label": "Overall progress", "aria-valuemin": 0, "aria-valuemax": total, "aria-valuenow": doneCount, children: /* @__PURE__ */ jsx("div", { className: "bl-steps__fill" }) }),
158
+ /* @__PURE__ */ jsx("ol", { ref: listRef, className: "bl-steps__list", children: steps.map((step, i) => {
159
+ const state = stateOf(i);
160
+ return /* @__PURE__ */ jsxs("li", { className: "bl-steps__step", "data-state": state, "aria-current": state === "active" ? "step" : void 0, children: [
161
+ /* @__PURE__ */ jsxs("span", { className: "bl-steps__icon", "aria-hidden": "true", children: [
162
+ state === "active" && /* @__PURE__ */ jsx("span", { className: "bl-steps__spin" }),
163
+ state === "done" && /* @__PURE__ */ jsx(Check, {}),
164
+ state === "error" && /* @__PURE__ */ jsx(Cross, {})
165
+ ] }),
166
+ /* @__PURE__ */ jsxs("span", { className: "bl-steps__text", children: [
167
+ /* @__PURE__ */ jsxs("span", { className: "bl-steps__label", children: [
168
+ step.label,
169
+ /* @__PURE__ */ jsx("span", { className: "bl-steps__sr", children: state === "done" ? " (done)" : state === "error" ? " (failed)" : state === "active" ? " (in progress)" : "" })
170
+ ] }),
171
+ step.description && /* @__PURE__ */ jsx("span", { className: "bl-steps__desc", children: step.description })
172
+ ] })
173
+ ] }, step.id ?? i);
174
+ }) }),
175
+ /* @__PURE__ */ jsxs("div", { className: "bl-steps__actions", children: [
176
+ finished && /* @__PURE__ */ jsx("button", { ref: actionRef, type: "button", className: "bl-steps__btn", "data-primary": "", onClick: () => onDone?.(), children: doneLabel }),
177
+ failed && /* @__PURE__ */ jsxs(Fragment, { children: [
178
+ onCancel && /* @__PURE__ */ jsx("button", { type: "button", className: "bl-steps__btn", onClick: () => onCancel(), children: cancelLabel }),
179
+ /* @__PURE__ */ jsx("button", { ref: actionRef, type: "button", className: "bl-steps__btn", "data-primary": "", onClick: () => onRetry?.(), children: retryLabel })
180
+ ] }),
181
+ !finished && !failed && onCancel && /* @__PURE__ */ jsx("button", { ref: actionRef, type: "button", className: "bl-steps__btn", onClick: () => onCancel(), children: cancelLabel })
182
+ ] }),
183
+ /* @__PURE__ */ jsx("p", { className: "bl-steps__sr", role: "status", children: announcement })
184
+ ]
185
+ }
186
+ );
187
+ return fixed ? /* @__PURE__ */ jsx("div", { className: "bl-steps-overlay", style: { "--bl-backdrop": backdropColor }, children: panel }) : panel;
188
+ }
189
+ export {
190
+ StepLoader as default
191
+ };
@@ -0,0 +1,41 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ type Verdict = true | string | void;
3
+ export interface FormStep {
4
+ id?: string;
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ content?: ReactNode | ((api: {
8
+ index: number;
9
+ go: (index: number) => void;
10
+ }) => ReactNode);
11
+ /** Runs after the native field rules; a string is shown as the error. */
12
+ validate?: (data: FormData) => Verdict | Promise<Verdict>;
13
+ }
14
+ export interface StepperFormProps {
15
+ steps?: FormStep[];
16
+ defaultStep?: number;
17
+ onStepChange?: (index: number) => void;
18
+ /** All steps' fields together. */
19
+ onComplete?: (data: FormData) => void;
20
+ doneContent?: ReactNode;
21
+ backLabel?: string;
22
+ nextLabel?: string;
23
+ finishLabel?: string;
24
+ checkingLabel?: string;
25
+ label?: string;
26
+ width?: number;
27
+ backgroundColor?: string;
28
+ textColor?: string;
29
+ mutedColor?: string;
30
+ trackColor?: string;
31
+ lineColor?: string;
32
+ accentColor?: string;
33
+ onAccentColor?: string;
34
+ errorColor?: string;
35
+ radius?: number;
36
+ fontFamily?: string;
37
+ className?: string;
38
+ style?: CSSProperties;
39
+ }
40
+ export default function StepperForm({ steps, defaultStep, onStepChange, onComplete, doneContent, backLabel, nextLabel, finishLabel, checkingLabel, label, width, backgroundColor, textColor, mutedColor, trackColor, lineColor, accentColor, onAccentColor, errorColor, radius, fontFamily, className, style }: StepperFormProps): import("react").JSX.Element;
41
+ export {};