bits-lab 0.0.0-stage → 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.
Files changed (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -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 +34 -4
@@ -0,0 +1,45 @@
1
+ import { type CSSProperties } from 'react';
2
+ export interface DateRangeValue {
3
+ start: Date | null;
4
+ end: Date | null;
5
+ }
6
+ export interface DatePreset {
7
+ label: string;
8
+ range: () => {
9
+ start: Date;
10
+ end: Date;
11
+ };
12
+ }
13
+ export interface DateRangeProps {
14
+ value?: DateRangeValue;
15
+ defaultValue?: DateRangeValue;
16
+ onValueChange?: (range: DateRangeValue) => void;
17
+ presets?: DatePreset[];
18
+ /** Months side by side (one on narrow screens). */
19
+ months?: number;
20
+ minDate?: Date;
21
+ maxDate?: Date;
22
+ /** 0 = Sunday, 1 = Monday. */
23
+ weekStartsOn?: number;
24
+ /** Intl locale for month and weekday names. */
25
+ locale?: string;
26
+ label?: string;
27
+ cellSize?: number;
28
+ /** Container width (px) below which one month shows. */
29
+ narrowAt?: number;
30
+ backgroundColor?: string;
31
+ textColor?: string;
32
+ mutedColor?: string;
33
+ dimColor?: string;
34
+ hoverColor?: string;
35
+ lineColor?: string;
36
+ accentColor?: string;
37
+ onAccentColor?: string;
38
+ bandColor?: string;
39
+ radius?: number;
40
+ fontFamily?: string;
41
+ className?: string;
42
+ style?: CSSProperties;
43
+ }
44
+ export declare const DEFAULT_PRESETS: DatePreset[];
45
+ export default function DateRange({ value, defaultValue, onValueChange, presets, months, minDate, maxDate, weekStartsOn, locale, label, cellSize, narrowAt, backgroundColor, textColor, mutedColor, dimColor, hoverColor, lineColor, accentColor, onAccentColor, bandColor, radius, fontFamily, className, style }: DateRangeProps): import("react").JSX.Element;
@@ -0,0 +1,327 @@
1
+ 'use client';
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useInsertionEffect, useLayoutEffect, useRef, useState } from "react";
4
+ const STYLE_ID = "bl-date-range";
5
+ const CSS = `
6
+ .bl-dr { display: flex; gap: 18px; width: fit-content; max-width: 100%; box-sizing: border-box; padding: 16px; 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 24px 50px -28px rgba(0,0,0,0.85); }
7
+ .bl-dr[data-narrow] { flex-direction: column; }
8
+ .bl-dr__presets { display: flex; flex-direction: column; gap: 2px; min-width: 130px; padding-right: 16px; box-shadow: 1px 0 0 var(--bl-line); }
9
+ .bl-dr[data-narrow] .bl-dr__presets { flex-direction: row; flex-wrap: wrap; min-width: 0; padding: 0 0 12px; box-shadow: 0 1px 0 var(--bl-line); }
10
+ .bl-dr__preset { padding: 7px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--bl-muted); font: inherit; font-size: 13px; text-align: left; white-space: nowrap; cursor: pointer; }
11
+ .bl-dr__preset:hover, .bl-dr__preset[aria-pressed="true"] { background: var(--bl-hover); color: var(--bl-ink); }
12
+ .bl-dr__preset:focus-visible, .bl-dr__nav:focus-visible, .bl-dr__clear:focus-visible { outline: 2px solid var(--bl-accent); outline-offset: 1px; }
13
+ .bl-dr__main { min-width: 0; }
14
+ .bl-dr__head { position: relative; display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
15
+ .bl-dr__nav { display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 8px; background: transparent; color: var(--bl-muted); cursor: pointer; }
16
+ .bl-dr__nav:hover { background: var(--bl-hover); color: var(--bl-ink); }
17
+ .bl-dr__months { display: flex; gap: 24px; }
18
+ .bl-dr__month { flex: 0 0 auto; animation: bl-dr-slide 320ms cubic-bezier(.2,.9,.25,1) both; }
19
+ .bl-dr__title { height: 32px; margin: -42px 0 10px; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; pointer-events: none; }
20
+ .bl-dr__grid { border-collapse: collapse; }
21
+ .bl-dr__grid th { width: var(--bl-cell); height: 28px; padding: 0; color: var(--bl-dim); font-size: 11px; font-weight: 600; text-transform: uppercase; }
22
+ .bl-dr__grid td { position: relative; width: var(--bl-cell); height: var(--bl-cell); padding: 0; text-align: center; }
23
+ .bl-dr__grid td[data-band]::before { content: ""; position: absolute; inset: 3px 0; background: var(--bl-band); }
24
+ .bl-dr__grid td[data-band="start"]::before { left: 50%; }
25
+ .bl-dr__grid td[data-band="end"]::before { right: 50%; }
26
+ .bl-dr__grid td[data-band="solo"]::before { display: none; }
27
+ .bl-dr__grid td[data-preview]::before { background: color-mix(in srgb, var(--bl-band) 55%, transparent); }
28
+ .bl-dr__day { position: relative; z-index: 1; width: calc(var(--bl-cell) - 4px); height: calc(var(--bl-cell) - 4px); padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; font: inherit; font-size: 13px; font-variant-numeric: tabular-nums; cursor: pointer; transition: background-color 120ms, transform 160ms cubic-bezier(.3,1.6,.5,1); }
29
+ .bl-dr__day:hover:not(:disabled) { background: var(--bl-hover); }
30
+ .bl-dr__day[data-today] { box-shadow: inset 0 0 0 1px var(--bl-dim); }
31
+ .bl-dr__day[data-edge] { background: var(--bl-accent); color: var(--bl-on-accent); font-weight: 700; animation: bl-dr-pop 300ms cubic-bezier(.3,1.6,.5,1); }
32
+ .bl-dr__day:disabled { color: var(--bl-dim); opacity: 0.4; cursor: not-allowed; text-decoration: line-through; }
33
+ .bl-dr__day:focus-visible { outline: 2px solid var(--bl-accent); outline-offset: 1px; }
34
+ .bl-dr__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 12px; padding-top: 12px; box-shadow: 0 -1px 0 var(--bl-line); font-size: 13px; }
35
+ .bl-dr__summary { color: var(--bl-muted); }
36
+ .bl-dr__summary b { color: var(--bl-ink); }
37
+ .bl-dr__clear { padding: 6px 10px; border: 0; border-radius: 8px; background: var(--bl-hover); color: var(--bl-ink); font: inherit; font-size: 13px; cursor: pointer; }
38
+ .bl-dr__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
39
+ @keyframes bl-dr-slide { from { opacity: 0; transform: translateX(calc(var(--bl-dir) * 28px)); } }
40
+ @keyframes bl-dr-pop { 40% { transform: scale(1.15); } }
41
+ @media (prefers-reduced-motion: reduce) { .bl-dr__month, .bl-dr__day[data-edge] { animation: none; } .bl-dr__day { transition: none; } }
42
+ `;
43
+ function useStyles() {
44
+ useInsertionEffect(() => {
45
+ if (typeof document === "undefined") return;
46
+ const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
47
+ style.textContent = CSS;
48
+ if (!style.isConnected) document.head.appendChild(style);
49
+ }, []);
50
+ }
51
+ const day = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
52
+ const addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
53
+ const addMonths = (d, n) => {
54
+ const target = new Date(d.getFullYear(), d.getMonth() + n, 1);
55
+ const last = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
56
+ return new Date(target.getFullYear(), target.getMonth(), Math.min(d.getDate(), last));
57
+ };
58
+ const same = (a, b) => !!a && !!b && a.getTime() === b.getTime();
59
+ const keyOf = (d) => `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
60
+ const DEFAULT_PRESETS = [
61
+ { label: "Today", range: () => ({ start: day(/* @__PURE__ */ new Date()), end: day(/* @__PURE__ */ new Date()) }) },
62
+ { label: "Last 7 days", range: () => ({ start: addDays(day(/* @__PURE__ */ new Date()), -6), end: day(/* @__PURE__ */ new Date()) }) },
63
+ { label: "Last 30 days", range: () => ({ start: addDays(day(/* @__PURE__ */ new Date()), -29), end: day(/* @__PURE__ */ new Date()) }) },
64
+ {
65
+ label: "This month",
66
+ range: () => {
67
+ const now = /* @__PURE__ */ new Date();
68
+ return { start: new Date(now.getFullYear(), now.getMonth(), 1), end: new Date(now.getFullYear(), now.getMonth() + 1, 0) };
69
+ }
70
+ },
71
+ {
72
+ label: "Next weekend",
73
+ range: () => {
74
+ const today = day(/* @__PURE__ */ new Date());
75
+ const saturday = addDays(today, (6 - today.getDay() + 7) % 7 || 7);
76
+ return { start: saturday, end: addDays(saturday, 1) };
77
+ }
78
+ }
79
+ ];
80
+ function DateRange({
81
+ value,
82
+ defaultValue,
83
+ onValueChange,
84
+ presets = DEFAULT_PRESETS,
85
+ months = 2,
86
+ minDate,
87
+ maxDate,
88
+ weekStartsOn = 1,
89
+ locale,
90
+ label = "Choose dates",
91
+ cellSize = 38,
92
+ narrowAt = 620,
93
+ backgroundColor = "#1a1c2c",
94
+ textColor = "#f4f4f4",
95
+ mutedColor = "#94b0c2",
96
+ dimColor = "#566c86",
97
+ hoverColor = "rgba(255,255,255,0.07)",
98
+ lineColor = "#333c57",
99
+ accentColor = "#73eff7",
100
+ onAccentColor = "#0b0b14",
101
+ bandColor = "rgba(115,239,247,0.16)",
102
+ radius = 16,
103
+ fontFamily = "ui-sans-serif, system-ui, sans-serif",
104
+ className = "",
105
+ style
106
+ }) {
107
+ useStyles();
108
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
109
+ const today = day(/* @__PURE__ */ new Date());
110
+ const [inner, setInner] = useState(defaultValue ?? { start: null, end: null });
111
+ const range = value ?? inner;
112
+ const start = range.start ? day(range.start) : null;
113
+ const end = range.end ? day(range.end) : null;
114
+ const [view, setView] = useState(() => new Date((start ?? today).getFullYear(), (start ?? today).getMonth(), 1));
115
+ const [dir, setDir] = useState(0);
116
+ const [focused, setFocused] = useState(start ?? today);
117
+ const [hover, setHover] = useState(null);
118
+ const [narrow, setNarrow] = useState(false);
119
+ const [room, setRoom] = useState(Infinity);
120
+ const [announce, setAnnounce] = useState("");
121
+ const rootRef = useRef(null);
122
+ const gridRef = useRef(null);
123
+ const keyboard = useRef(false);
124
+ const shown = narrow ? 1 : months;
125
+ const min = minDate ? day(minDate) : null;
126
+ const max = maxDate ? day(maxDate) : null;
127
+ const fmt = (d, opts) => new Intl.DateTimeFormat(locale, opts).format(d);
128
+ const long = (d) => fmt(d, { weekday: "long", month: "long", day: "numeric", year: "numeric" });
129
+ const short = (d) => fmt(d, { month: "short", day: "numeric", year: d.getFullYear() === today.getFullYear() ? void 0 : "numeric" });
130
+ const disabled = (d) => min && d < min || max && d > max;
131
+ useLayoutEffect(() => {
132
+ const node = rootRef.current;
133
+ const measure = () => {
134
+ const parent = node.parentElement;
135
+ const pad = parent ? parseFloat(getComputedStyle(parent).paddingLeft) + parseFloat(getComputedStyle(parent).paddingRight) : 0;
136
+ const width = parent ? parent.clientWidth - pad : 9999;
137
+ setNarrow(width < narrowAt);
138
+ setRoom(width);
139
+ };
140
+ measure();
141
+ const resize = new ResizeObserver(measure);
142
+ if (node.parentElement) resize.observe(node.parentElement);
143
+ return () => resize.disconnect();
144
+ }, [narrowAt]);
145
+ const commit = (next) => {
146
+ if (value === void 0) setInner(next);
147
+ onValueChange?.(next);
148
+ };
149
+ const goMonth = (target, direction) => {
150
+ setDir(direction);
151
+ setView(new Date(target.getFullYear(), target.getMonth(), 1));
152
+ };
153
+ const reveal = (d) => {
154
+ const first = view;
155
+ const lastShown = addMonths(view, shown - 1);
156
+ if (d < first) goMonth(d, -1);
157
+ else if (d > new Date(lastShown.getFullYear(), lastShown.getMonth() + 1, 0)) goMonth(addMonths(d, -(shown - 1)), 1);
158
+ };
159
+ const pick = (d) => {
160
+ if (disabled(d)) return;
161
+ if (!start || end) {
162
+ commit({ start: d, end: null });
163
+ setAnnounce(`Start date ${long(d)}. Now choose the end date.`);
164
+ } else {
165
+ const [a, b] = d < start ? [d, start] : [start, d];
166
+ commit({ start: a, end: b });
167
+ const nights2 = Math.round((b.getTime() - a.getTime()) / 864e5) + 1;
168
+ setAnnounce(`Selected ${long(a)} to ${long(b)}, ${nights2} ${nights2 === 1 ? "day" : "days"}.`);
169
+ }
170
+ setFocused(d);
171
+ setHover(null);
172
+ };
173
+ const onKeyDown = (event) => {
174
+ const moves = {
175
+ ArrowLeft: () => addDays(focused, -1),
176
+ ArrowRight: () => addDays(focused, 1),
177
+ ArrowUp: () => addDays(focused, -7),
178
+ ArrowDown: () => addDays(focused, 7),
179
+ Home: () => addDays(focused, -((focused.getDay() - weekStartsOn + 7) % 7)),
180
+ End: () => addDays(focused, 6 - (focused.getDay() - weekStartsOn + 7) % 7),
181
+ PageUp: () => addMonths(focused, event.shiftKey ? -12 : -1),
182
+ PageDown: () => addMonths(focused, event.shiftKey ? 12 : 1)
183
+ };
184
+ if (!moves[event.key]) return;
185
+ event.preventDefault();
186
+ const next = moves[event.key]();
187
+ keyboard.current = true;
188
+ setFocused(next);
189
+ reveal(next);
190
+ if (start && !end) setHover(next);
191
+ };
192
+ useEffect(() => {
193
+ if (!keyboard.current) return;
194
+ keyboard.current = false;
195
+ gridRef.current?.querySelector(`[data-key="${keyOf(focused)}"]`)?.focus();
196
+ }, [focused, view]);
197
+ const applyPreset = (preset) => {
198
+ const next = preset.range();
199
+ commit({ start: day(next.start), end: day(next.end) });
200
+ setFocused(day(next.start));
201
+ const target = day(next.start);
202
+ if (target.getFullYear() !== view.getFullYear() || target.getMonth() !== view.getMonth()) goMonth(target, target < view ? -1 : 1);
203
+ setAnnounce(`${preset.label}: ${long(day(next.start))} to ${long(day(next.end))}.`);
204
+ };
205
+ const bandOf = (d) => {
206
+ const previewEnd = start && !end ? hover : null;
207
+ const a = start;
208
+ const b = end ?? previewEnd;
209
+ if (!a || !b) return { band: start && same(d, start) ? "solo" : null, preview: false };
210
+ const [lo, hi] = a <= b ? [a, b] : [b, a];
211
+ if (d < lo || d > hi) return { band: null, preview: false };
212
+ const band = same(lo, hi) ? "solo" : same(d, lo) ? "start" : same(d, hi) ? "end" : "mid";
213
+ return { band, preview: !end };
214
+ };
215
+ const weekdays = Array.from({ length: 7 }, (_, i) => {
216
+ const d = new Date(2024, 0, 7 + (i + weekStartsOn) % 7);
217
+ return { short: fmt(d, { weekday: "narrow" }), long: fmt(d, { weekday: "long" }) };
218
+ });
219
+ const renderMonth = (offset) => {
220
+ const first = addMonths(view, offset);
221
+ const lead = (first.getDay() - weekStartsOn + 7) % 7;
222
+ const daysIn = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
223
+ const cells = Array.from({ length: 42 }, (_, i) => i < lead || i >= lead + daysIn ? null : new Date(first.getFullYear(), first.getMonth(), i - lead + 1));
224
+ const title = fmt(first, { month: "long", year: "numeric" });
225
+ return /* @__PURE__ */ jsxs("div", { className: "bl-dr__month", style: { "--bl-dir": dir }, children: [
226
+ /* @__PURE__ */ jsx("div", { className: "bl-dr__title", id: `${uid}-m${offset}`, "aria-hidden": "true", children: title }),
227
+ /* @__PURE__ */ jsxs("table", { className: "bl-dr__grid", role: "grid", "aria-label": title, children: [
228
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: weekdays.map((w) => /* @__PURE__ */ jsx("th", { scope: "col", abbr: w.long, children: w.short }, w.long)) }) }),
229
+ /* @__PURE__ */ jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => /* @__PURE__ */ jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((d, col) => {
230
+ if (!d) return /* @__PURE__ */ jsx("td", {}, col);
231
+ const { band, preview } = bandOf(d);
232
+ const edge = same(d, start) || same(d, end) || preview && same(d, hover);
233
+ const state = same(d, start) && same(d, end) ? ", selected" : same(d, start) ? ", start date" : same(d, end) ? ", end date" : band && !preview ? ", in range" : "";
234
+ return /* @__PURE__ */ jsx("td", { "data-band": band ?? void 0, "data-preview": band && preview || void 0, "aria-selected": !!band && !preview, children: /* @__PURE__ */ jsx(
235
+ "button",
236
+ {
237
+ type: "button",
238
+ className: "bl-dr__day",
239
+ "data-key": keyOf(d),
240
+ "data-today": same(d, today) || void 0,
241
+ "data-edge": edge || void 0,
242
+ tabIndex: same(d, focused) ? 0 : -1,
243
+ disabled: !!disabled(d),
244
+ "aria-label": `${long(d)}${same(d, today) ? ", today" : ""}${state}`,
245
+ onClick: () => pick(d),
246
+ onPointerEnter: () => start && !end && setHover(d),
247
+ onFocus: () => setFocused(d),
248
+ children: d.getDate()
249
+ }
250
+ ) }, col);
251
+ }) }, row)) })
252
+ ] })
253
+ ] }, `${first.getFullYear()}-${first.getMonth()}`);
254
+ };
255
+ const nights = start && end ? Math.round((end.getTime() - start.getTime()) / 864e5) + 1 : 0;
256
+ const cell = narrow ? Math.max(28, Math.min(cellSize, Math.floor((room - 32) / 7))) : cellSize;
257
+ const activePreset = presets.findIndex((p) => {
258
+ const r = p.range();
259
+ return same(day(r.start), start) && same(day(r.end), end);
260
+ });
261
+ return /* @__PURE__ */ jsxs(
262
+ "div",
263
+ {
264
+ ref: rootRef,
265
+ className: `bl-dr ${className}`,
266
+ role: "group",
267
+ "aria-label": label,
268
+ "data-narrow": narrow || void 0,
269
+ style: {
270
+ "--bl-cell": `${cell}px`,
271
+ "--bl-bg": backgroundColor,
272
+ "--bl-ink": textColor,
273
+ "--bl-muted": mutedColor,
274
+ "--bl-dim": dimColor,
275
+ "--bl-hover": hoverColor,
276
+ "--bl-line": lineColor,
277
+ "--bl-accent": accentColor,
278
+ "--bl-on-accent": onAccentColor,
279
+ "--bl-band": bandColor,
280
+ "--bl-radius": `${radius}px`,
281
+ "--bl-font": fontFamily,
282
+ ...style
283
+ },
284
+ children: [
285
+ presets.length > 0 && /* @__PURE__ */ jsx("div", { className: "bl-dr__presets", role: "group", "aria-label": "Presets", children: presets.map((preset, i) => /* @__PURE__ */ jsx("button", { type: "button", className: "bl-dr__preset", "aria-pressed": i === activePreset, onClick: () => applyPreset(preset), children: preset.label }, preset.label)) }),
286
+ /* @__PURE__ */ jsxs("div", { className: "bl-dr__main", children: [
287
+ /* @__PURE__ */ jsxs("div", { className: "bl-dr__head", children: [
288
+ /* @__PURE__ */ jsx("button", { type: "button", className: "bl-dr__nav", "aria-label": "Previous month", onClick: () => goMonth(addMonths(view, -1), -1), children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M8.5 3 4.5 7l4 4" }) }) }),
289
+ /* @__PURE__ */ jsx("button", { type: "button", className: "bl-dr__nav", "aria-label": "Next month", onClick: () => goMonth(addMonths(view, 1), 1), children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M5.5 3 9.5 7l-4 4" }) }) })
290
+ ] }),
291
+ /* @__PURE__ */ jsx("div", { ref: gridRef, className: "bl-dr__months", onKeyDown, onPointerLeave: () => setHover(null), children: Array.from({ length: shown }, (_, i) => renderMonth(i)) }),
292
+ /* @__PURE__ */ jsxs("div", { className: "bl-dr__foot", children: [
293
+ /* @__PURE__ */ jsx("span", { className: "bl-dr__summary", children: start && end ? /* @__PURE__ */ jsxs(Fragment, { children: [
294
+ /* @__PURE__ */ jsx("b", { children: short(start) }),
295
+ " \u2013 ",
296
+ /* @__PURE__ */ jsx("b", { children: short(end) }),
297
+ " \xB7 ",
298
+ nights,
299
+ " ",
300
+ nights === 1 ? "day" : "days"
301
+ ] }) : start ? /* @__PURE__ */ jsxs(Fragment, { children: [
302
+ /* @__PURE__ */ jsx("b", { children: short(start) }),
303
+ " \u2013 pick an end date"
304
+ ] }) : "Pick a start date" }),
305
+ start && /* @__PURE__ */ jsx(
306
+ "button",
307
+ {
308
+ type: "button",
309
+ className: "bl-dr__clear",
310
+ onClick: () => {
311
+ commit({ start: null, end: null });
312
+ setAnnounce("Dates cleared.");
313
+ },
314
+ children: "Clear"
315
+ }
316
+ )
317
+ ] })
318
+ ] }),
319
+ /* @__PURE__ */ jsx("span", { className: "bl-dr__sr", "aria-live": "polite", children: announce })
320
+ ]
321
+ }
322
+ );
323
+ }
324
+ export {
325
+ DEFAULT_PRESETS,
326
+ DateRange as default
327
+ };
@@ -0,0 +1,30 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ export type DeviceType = 'phone' | 'tablet' | 'laptop' | 'browser' | 'watch';
3
+ export type DeviceFinish = 'graphite' | 'silver' | 'midnight' | 'gold';
4
+ export interface DeviceFrameProps {
5
+ device?: DeviceType;
6
+ /** Fills the screen: an image, video or live UI. */
7
+ children?: ReactNode;
8
+ /** Frame width in px (never wider than its parent). Defaults per device. */
9
+ width?: number;
10
+ /** Preset body finish. */
11
+ finish?: DeviceFinish;
12
+ /** Custom body color; overrides the finish. */
13
+ bodyColor?: string;
14
+ /** Tablet only: rotate to 4:3. */
15
+ landscape?: boolean;
16
+ /** Browser frame address bar text. */
17
+ url?: string;
18
+ browserTheme?: 'light' | 'dark';
19
+ /** Browser page aspect ratio, e.g. "16 / 10". */
20
+ aspectRatio?: string;
21
+ /** Resting 3D tilt, in degrees. */
22
+ tilt?: number;
23
+ /** Ease the tilt toward the pointer (off under reduced motion). */
24
+ interactiveTilt?: boolean;
25
+ /** Accessible name; makes the frame a labelled figure. */
26
+ label?: string;
27
+ className?: string;
28
+ style?: CSSProperties;
29
+ }
30
+ export default function DeviceFrame({ device, children, width, finish, bodyColor, landscape, url, browserTheme, aspectRatio, tilt, interactiveTilt, label, className, style }: DeviceFrameProps): import("react").JSX.Element;
@@ -0,0 +1,145 @@
1
+ 'use client';
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useInsertionEffect, useRef } from "react";
4
+ const STYLE_ID = "bl-device-frame";
5
+ const CSS = `
6
+ .bl-dev { --bl-body: #1f2230; --bl-edge: #3a3f55; --bl-shine: rgba(255,255,255,0.14); position: relative; display: inline-block; width: var(--bl-w); max-width: 100%; transform-style: preserve-3d; transition: transform 500ms cubic-bezier(.2,.8,.2,1); }
7
+ .bl-dev__shell { position: relative; background: var(--bl-body); box-shadow: inset 0 0 0 2px var(--bl-edge), inset 0 0 0 4px rgba(0,0,0,0.35), 0 30px 60px -20px rgba(0,0,0,0.6), 0 12px 24px -12px rgba(0,0,0,0.5); }
8
+ .bl-dev__shell::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; background: linear-gradient(135deg, var(--bl-shine), transparent 40%); }
9
+ .bl-dev__screen { position: relative; overflow: hidden; background: #000; isolation: isolate; }
10
+ .bl-dev__screen > * { display: block; width: 100%; height: 100%; object-fit: cover; }
11
+
12
+ .bl-dev[data-device="phone"] .bl-dev__shell { aspect-ratio: 9 / 19; border-radius: 15% / 7%; padding: 4.5%; }
13
+ .bl-dev[data-device="phone"] .bl-dev__screen { height: 100%; border-radius: 11% / 5.2%; }
14
+ .bl-dev[data-device="phone"] .bl-dev__notch { position: absolute; z-index: 2; top: 2.2%; left: 50%; width: 30%; height: 3.2%; transform: translateX(-50%); border-radius: 999px; background: #000; }
15
+ .bl-dev[data-device="phone"] .bl-dev__btn { position: absolute; width: 1.6%; border-radius: 2px; background: var(--bl-edge); }
16
+
17
+ .bl-dev[data-device="tablet"] .bl-dev__shell { aspect-ratio: 3 / 4; border-radius: 6%; padding: 4%; }
18
+ .bl-dev[data-device="tablet"] .bl-dev__screen { height: 100%; border-radius: 2.5%; }
19
+ .bl-dev[data-device="tablet"][data-landscape] .bl-dev__shell { aspect-ratio: 4 / 3; }
20
+
21
+ .bl-dev[data-device="laptop"] .bl-dev__lid { position: relative; margin: 0 7%; padding: 2.4% 2.4% 3.2%; border-radius: 3% 3% 0 0 / 4.5% 4.5% 0 0; background: var(--bl-body); box-shadow: inset 0 0 0 2px var(--bl-edge); }
22
+ .bl-dev[data-device="laptop"] .bl-dev__screen { aspect-ratio: 16 / 10; border-radius: 0.6%; }
23
+ .bl-dev[data-device="laptop"] .bl-dev__cam { position: absolute; top: 0.9%; left: 50%; width: 0.8%; aspect-ratio: 1; border-radius: 50%; background: #0b0b14; transform: translateX(-50%); }
24
+ .bl-dev[data-device="laptop"] .bl-dev__base { position: relative; height: 0; padding-bottom: 3.6%; border-radius: 0 0 40% 40% / 0 0 100% 100%; background: linear-gradient(to bottom, var(--bl-edge), var(--bl-body)); box-shadow: 0 24px 40px -18px rgba(0,0,0,0.65); }
25
+ .bl-dev[data-device="laptop"] .bl-dev__base::before { content: ""; position: absolute; top: 0; left: 50%; width: 16%; height: 30%; border-radius: 0 0 10px 10px; background: rgba(0,0,0,0.3); transform: translateX(-50%); }
26
+
27
+ .bl-dev[data-device="browser"] .bl-dev__shell { border-radius: 12px; padding: 0; overflow: hidden; background: var(--bl-chrome); box-shadow: 0 0 0 1px rgba(0,0,0,0.25), 0 30px 60px -24px rgba(0,0,0,0.55); }
28
+ .bl-dev[data-device="browser"] .bl-dev__shell::after { display: none; }
29
+ .bl-dev__bar { display: flex; align-items: center; gap: 12px; height: 40px; padding: 0 14px; background: var(--bl-chrome); border-bottom: 1px solid var(--bl-chrome-line); }
30
+ .bl-dev__lights { display: flex; gap: 7px; }
31
+ .bl-dev__lights i { width: 11px; height: 11px; border-radius: 50%; }
32
+ .bl-dev__url { flex: 1; max-width: 60%; margin: 0 auto; padding: 5px 12px; border-radius: 7px; background: var(--bl-url); color: var(--bl-url-text); font: 500 12px/1.2 ui-sans-serif, system-ui, sans-serif; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
33
+ .bl-dev[data-device="browser"] .bl-dev__screen { aspect-ratio: var(--bl-ratio, 16 / 10); background: var(--bl-page); }
34
+
35
+ .bl-dev[data-device="watch"] .bl-dev__shell { aspect-ratio: 4 / 5; border-radius: 26%; padding: 8%; }
36
+ .bl-dev[data-device="watch"] .bl-dev__screen { height: 100%; border-radius: 22%; }
37
+ .bl-dev[data-device="watch"] .bl-dev__crown { position: absolute; top: 28%; right: -3.5%; width: 5%; height: 16%; border-radius: 3px; background: var(--bl-edge); }
38
+
39
+ @media (prefers-reduced-motion: reduce) { .bl-dev { transition: none; } }
40
+ `;
41
+ function useStyles() {
42
+ useInsertionEffect(() => {
43
+ if (typeof document === "undefined") return;
44
+ const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
45
+ style.textContent = CSS;
46
+ if (!style.isConnected) document.head.appendChild(style);
47
+ }, []);
48
+ }
49
+ const FINISHES = {
50
+ graphite: { body: "#1f2230", edge: "#3a3f55" },
51
+ silver: { body: "#d9dde4", edge: "#b4bac6" },
52
+ midnight: { body: "#141a33", edge: "#2b3566" },
53
+ gold: { body: "#e8d3ae", edge: "#c9ad7e" }
54
+ };
55
+ const BROWSER_THEMES = {
56
+ light: { chrome: "#eceef2", line: "#d8dbe2", url: "#ffffff", urlText: "#5b6170", page: "#ffffff" },
57
+ dark: { chrome: "#22252f", line: "#151720", url: "#151720", urlText: "#9aa2b4", page: "#0e1016" }
58
+ };
59
+ const DEFAULT_WIDTHS = { phone: 300, tablet: 420, laptop: 720, browser: 760, watch: 200 };
60
+ function DeviceFrame({
61
+ device = "phone",
62
+ children,
63
+ width,
64
+ finish = "graphite",
65
+ bodyColor,
66
+ landscape = false,
67
+ url = "bitslab.dev",
68
+ browserTheme = "dark",
69
+ aspectRatio,
70
+ tilt = 0,
71
+ interactiveTilt = false,
72
+ label,
73
+ className = "",
74
+ style
75
+ }) {
76
+ useStyles();
77
+ const ref = useRef(null);
78
+ const colors = FINISHES[finish] ?? FINISHES.graphite;
79
+ const chrome = BROWSER_THEMES[browserTheme] ?? BROWSER_THEMES.dark;
80
+ const baseTransform = tilt ? `perspective(1600px) rotateX(${tilt * 0.5}deg) rotateY(${-tilt}deg)` : "none";
81
+ const onPointerMove = (event) => {
82
+ if (!interactiveTilt || window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
83
+ const rect = ref.current.getBoundingClientRect();
84
+ const x = (event.clientX - rect.left) / rect.width - 0.5;
85
+ const y = (event.clientY - rect.top) / rect.height - 0.5;
86
+ ref.current.style.transform = `perspective(1600px) rotateX(${-y * 10}deg) rotateY(${x * 14}deg)`;
87
+ };
88
+ const onPointerLeave = () => {
89
+ if (ref.current) ref.current.style.transform = baseTransform;
90
+ };
91
+ const screen = /* @__PURE__ */ jsx("div", { className: "bl-dev__screen", children });
92
+ return /* @__PURE__ */ jsx(
93
+ "div",
94
+ {
95
+ ref,
96
+ className: `bl-dev ${className}`,
97
+ "data-device": device,
98
+ "data-landscape": landscape || void 0,
99
+ role: label ? "figure" : void 0,
100
+ "aria-label": label,
101
+ onPointerMove,
102
+ onPointerLeave,
103
+ style: {
104
+ "--bl-w": `${width ?? DEFAULT_WIDTHS[device] ?? 320}px`,
105
+ "--bl-body": bodyColor ?? colors.body,
106
+ "--bl-edge": colors.edge,
107
+ "--bl-chrome": chrome.chrome,
108
+ "--bl-chrome-line": chrome.line,
109
+ "--bl-url": chrome.url,
110
+ "--bl-url-text": chrome.urlText,
111
+ "--bl-page": chrome.page,
112
+ ...aspectRatio ? { "--bl-ratio": aspectRatio } : null,
113
+ transform: baseTransform,
114
+ ...style
115
+ },
116
+ children: device === "laptop" ? /* @__PURE__ */ jsxs(Fragment, { children: [
117
+ /* @__PURE__ */ jsxs("div", { className: "bl-dev__lid", children: [
118
+ /* @__PURE__ */ jsx("span", { className: "bl-dev__cam", "aria-hidden": "true" }),
119
+ screen
120
+ ] }),
121
+ /* @__PURE__ */ jsx("div", { className: "bl-dev__base", "aria-hidden": "true" })
122
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "bl-dev__shell", children: [
123
+ device === "browser" && /* @__PURE__ */ jsxs("div", { className: "bl-dev__bar", "aria-hidden": "true", children: [
124
+ /* @__PURE__ */ jsxs("span", { className: "bl-dev__lights", children: [
125
+ /* @__PURE__ */ jsx("i", { style: { background: "#ff5f57" } }),
126
+ /* @__PURE__ */ jsx("i", { style: { background: "#febc2e" } }),
127
+ /* @__PURE__ */ jsx("i", { style: { background: "#28c840" } })
128
+ ] }),
129
+ /* @__PURE__ */ jsx("span", { className: "bl-dev__url", children: url })
130
+ ] }),
131
+ device === "phone" && /* @__PURE__ */ jsxs(Fragment, { children: [
132
+ /* @__PURE__ */ jsx("span", { className: "bl-dev__notch", "aria-hidden": "true" }),
133
+ /* @__PURE__ */ jsx("span", { className: "bl-dev__btn", style: { left: "-1%", top: "18%", height: "6%" }, "aria-hidden": "true" }),
134
+ /* @__PURE__ */ jsx("span", { className: "bl-dev__btn", style: { left: "-1%", top: "27%", height: "9%" }, "aria-hidden": "true" }),
135
+ /* @__PURE__ */ jsx("span", { className: "bl-dev__btn", style: { right: "-1%", top: "24%", height: "12%" }, "aria-hidden": "true" })
136
+ ] }),
137
+ device === "watch" && /* @__PURE__ */ jsx("span", { className: "bl-dev__crown", "aria-hidden": "true" }),
138
+ screen
139
+ ] })
140
+ }
141
+ );
142
+ }
143
+ export {
144
+ DeviceFrame as default
145
+ };
@@ -0,0 +1,36 @@
1
+ import { type CSSProperties, type ElementType } from 'react';
2
+ export interface DiffOp {
3
+ type: 'same' | 'ins' | 'del';
4
+ text: string;
5
+ }
6
+ export interface DiffRevealProps {
7
+ before?: string;
8
+ after?: string;
9
+ /** Play when scrolled into view. */
10
+ autoPlay?: boolean;
11
+ /** Share that must be visible to play. */
12
+ threshold?: number;
13
+ /** Change to restart. */
14
+ replayKey?: string | number;
15
+ /** Typing speed of insertions. */
16
+ charsPerSecond?: number;
17
+ /** How long a deletion shows struck through, in ms. */
18
+ strikeDuration?: number;
19
+ /** Gap between deletions in ms. */
20
+ stagger?: number;
21
+ /** Play/replay, speed and the word count. */
22
+ showControls?: boolean;
23
+ onDone?: () => void;
24
+ as?: ElementType;
25
+ fontSize?: number;
26
+ textColor?: string;
27
+ mutedColor?: string;
28
+ chipColor?: string;
29
+ insertColor?: string;
30
+ deleteColor?: string;
31
+ fontFamily?: string;
32
+ className?: string;
33
+ style?: CSSProperties;
34
+ }
35
+ export declare function diffWords(before: string, after: string): DiffOp[];
36
+ export default function DiffReveal({ before, after, autoPlay, threshold, replayKey, charsPerSecond, strikeDuration, stagger, showControls, onDone, as, fontSize, textColor, mutedColor, chipColor, insertColor, deleteColor, fontFamily, className, style }: DiffRevealProps): import("react").JSX.Element;