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,312 @@
1
+ 'use client';
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useEffect, useInsertionEffect, useRef } from "react";
4
+ const LAND = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf4AP/AAAAAAAAAAAAAAAAAAAAAAAAX/z///+AAAAAAAABAAAAAAAAAAAAAYd8P///wAA+AAAAAA8AAAAAAAAAAAwAnw////4AAIAAAAAAGAAAAAAAAAAADivwAf//wAAAAADAAf/wAHYAAAAAADoi3sAP//gAAAAAMAD///sAAAAgBgACfwz/AD/+gAAAwAEHf///+/8gBAP/7/nJdjwD/+AAAH/AA7f///////f4P//////h8H/gAAAf/6//f////////Mf/////9H4D8AeAA+ev///////////AP/////4A0B4AAAD5/////////////Af3////gHgA4AAAH5///////////LwAHgH///gHkAAAAAH4/////////+CIAABAB///4D+AAAAGCx/////////4A8AAIAAf///n/gAAAOCD/////////wA4AAAAAf///n/wAAAbP///////////AgAAAAAP/////wAAADf//////////9AAAAAAAF////0YAAAB///////////9AAAAAAAD////8EAAAB///////////5AAAAAAAD////2AAAAB/f5fP//////wAAAAAAAD////gAAAAfxnwPP//////jAAAAAAAD////AAAAAPCb3/n/////+CAAAAAAAD///8AAAAAfALf/n////+ECAAAAAAAB///8AAAAAGHQP/n/////mMAAAAAAAA///8AAAAAH+Ai///////E8AAAAAAAAf//wAAAAAP/AA///////BgAAAAAAAAP//gAAAAAf/73///////gAAAAAAAAAD/AQAAAAAf////f/////gAAAAAAAAAF+AQAAAAB///+/n/////AAAAAAAAAAC+AAAAAAB///+f0H////AAAAAAAAAAAeAwAAAAD////f/B///8gAAAAAAAAAAeGEAAAAH////v+B/z/AAAAAAAAAAAAPMAgAAAD////n+A/B+gAAAAAAAAAAAD8AAAAAD////n4AeB/AgAAAAAAAAAAAPAAAAAH////3gAcAfAgAAAAAAAAAAADAAAAAD////6AAcAfggAAAAAAAAAAABDwAAAD////8wAMATAIAAAAAAAAAAAAr/AAAB/////gAKASAAAAAAAAAAAAAAH/gAAA/////gACAAAIAAAAAAAAAAAAH/8AAAaH///AAAAsGAAAAAAAAAAAAAH/+AAAAB//+AAAAUOAAAAAAAAAAAAAP/+AAAAB//8AAAAYegAAAAAAAAAAAAP//gAAAD//4AAAAMeBgAAAAAAAAAAAP//8AAAB//wAAAAGdiuAAAAAAAAAAAf///AAAA//wAAAACAQHgAAAAAAAAAAP///gAAA//wAAAABwAHwgAAAAAAAAAH///AAAA//wAAAAACIDQIAAAAAAAAAH//+AAAAf/wAAAAAAAAAAAAAAAAAAAD//+AAAA//wgAAAAABxAAAAAAAAAAAD//+AAAA//wgAAAAAPxgBAAAAAAAAAA//8AAAA//jgAAAAAf5gAAAAAAAAAAAf/8AAAA//DgAAAAAf/gAAAAAAAAAAAf/8AAAAf/DAAAAAD//4CAAAAAAAAAAf/wAAAAf/DAAAAAH//4AAAAAAAAAAAf/AAAAAf+CAAAAAH//8AAAAAAAAAAAf/AAAAAP8AAAAAAH//+AAAAAAAAAAA/+AAAAAP8AAAAAAH//+AAAAAAAAAAA/+AAAAAH4AAAAAAD//+AAAAAAAAAAA/8AAAAAHwAAAAAADwf8AAAAAAAAAAA/gAAAAAAAAAAAAACAH4AIAAAAAAAAB/wAAAAAAAAAAAAAAAD4AEAAAAAAAAB+AAAAAAAAAAAAAAAAAAAGAAAAAAAAB6AAAAAAAAAAAAAAAAAwAMAAAAAAAAA8AAAAAAAAAAAAAAAAAQAYAAAAAAAAB4AAAAAAAAAAAAAAAAAAAwAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAACAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAeAAIP+f/gAAAAAAAAAAAMAAAAAAABP/+H//////AAAAAAAAAAA+AAAAAf////8////////AAAAAAAOEAPAAAB///////////////gAAAP//T//8AAAH//////////////+AAAH/////4AAAH///////////////8AAE//////4ABw////////////////8AAAD//////gCA////////////////wAAAf/////////////////////////+A/4A///////////////////////////////////////////////////////////////////////////////////////";
5
+ const STYLE_ID = "bl-dot-globe";
6
+ const CSS = `
7
+ .bl-globe { position: relative; width: 100%; max-width: var(--bl-size); aspect-ratio: 1; margin: 0 auto; touch-action: pan-y; cursor: grab; outline: none; }
8
+ .bl-globe[data-dragging] { cursor: grabbing; }
9
+ .bl-globe:focus-visible { box-shadow: 0 0 0 3px var(--bl-focus); border-radius: 50%; }
10
+ .bl-globe canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
11
+ `;
12
+ function useStyles() {
13
+ useInsertionEffect(() => {
14
+ if (typeof document === "undefined") return;
15
+ const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
16
+ style.textContent = CSS;
17
+ if (!style.isConnected) document.head.appendChild(style);
18
+ }, []);
19
+ }
20
+ let landBits;
21
+ function isLand(lat, lon) {
22
+ if (!landBits) landBits = Uint8Array.from(atob(LAND), (c) => c.charCodeAt(0));
23
+ const col = Math.min(179, Math.max(0, Math.floor((lon + 180) / 2)));
24
+ const row = Math.min(89, Math.max(0, Math.floor((90 - lat) / 2)));
25
+ const i = row * 180 + col;
26
+ return (landBits[i >> 3] & 1 << 7 - (i & 7)) !== 0;
27
+ }
28
+ const RAD = Math.PI / 180;
29
+ const toVec = (lat, lon) => [Math.cos(lat * RAD) * Math.sin(lon * RAD), Math.sin(lat * RAD), Math.cos(lat * RAD) * Math.cos(lon * RAD)];
30
+ function landDots(count) {
31
+ const dots = [];
32
+ const golden = Math.PI * (3 - Math.sqrt(5));
33
+ for (let i = 0; i < count; i++) {
34
+ const y = 1 - 2 * (i + 0.5) / count;
35
+ const r = Math.sqrt(1 - y * y);
36
+ const x = r * Math.cos(i * golden);
37
+ const z = r * Math.sin(i * golden);
38
+ if (isLand(Math.asin(y) / RAD, Math.atan2(x, z) / RAD)) dots.push([x, y, z]);
39
+ }
40
+ return dots;
41
+ }
42
+ function arcPoints(from, to, samples = 64) {
43
+ const a = toVec(...from);
44
+ const b = toVec(...to);
45
+ const omega = Math.acos(Math.min(1, Math.max(-1, a[0] * b[0] + a[1] * b[1] + a[2] * b[2])));
46
+ const lift = Math.min(0.4, omega * 0.2);
47
+ const points = [];
48
+ for (let s = 0; s <= samples; s++) {
49
+ const t = s / samples;
50
+ const ka = omega < 1e-6 ? 1 - t : Math.sin((1 - t) * omega) / Math.sin(omega);
51
+ const kb = omega < 1e-6 ? t : Math.sin(t * omega) / Math.sin(omega);
52
+ const h = 1 + lift * Math.sin(Math.PI * t);
53
+ points.push([(a[0] * ka + b[0] * kb) * h, (a[1] * ka + b[1] * kb) * h, (a[2] * ka + b[2] * kb) * h]);
54
+ }
55
+ return points;
56
+ }
57
+ const DEFAULT_MARKERS = [
58
+ { lat: 37.77, lon: -122.42, label: "San Francisco" },
59
+ { lat: 51.51, lon: -0.13, label: "London" },
60
+ { lat: 35.68, lon: 139.69, label: "Tokyo" },
61
+ { lat: -33.87, lon: 151.21, label: "Sydney" },
62
+ { lat: -23.55, lon: -46.63, label: "S\xE3o Paulo" },
63
+ { lat: 1.35, lon: 103.82, label: "Singapore" }
64
+ ];
65
+ const DEFAULT_ARCS = [
66
+ { from: [37.77, -122.42], to: [51.51, -0.13] },
67
+ { from: [51.51, -0.13], to: [1.35, 103.82] },
68
+ { from: [1.35, 103.82], to: [35.68, 139.69] },
69
+ { from: [35.68, 139.69], to: [-33.87, 151.21] },
70
+ { from: [37.77, -122.42], to: [-23.55, -46.63] }
71
+ ];
72
+ function DotGlobe({
73
+ size = 520,
74
+ dotCount = 9e3,
75
+ dotSize = 1,
76
+ square = false,
77
+ dotColor = "#73eff7",
78
+ oceanColor = "rgba(41,54,111,0.55)",
79
+ glowColor = "rgba(115,239,247,0.25)",
80
+ arcColor = "#ffcd75",
81
+ markerColor = "#ef7d57",
82
+ markers = DEFAULT_MARKERS,
83
+ arcs = DEFAULT_ARCS,
84
+ autoRotate = true,
85
+ rotateSpeed = 8,
86
+ initialLon = -20,
87
+ initialLat = 18,
88
+ draggable = true,
89
+ label = "Interactive globe",
90
+ className = "",
91
+ style
92
+ }) {
93
+ useStyles();
94
+ const wrapRef = useRef(null);
95
+ const canvasRef = useRef(null);
96
+ const view = useRef({ yaw: -initialLon * RAD, pitch: initialLat * RAD, vy: 0, vp: 0, dragging: false });
97
+ const config = useRef(null);
98
+ config.current = { dotSize, square, dotColor, oceanColor, glowColor, arcColor, markerColor, markers, autoRotate, rotateSpeed, draggable };
99
+ const arcsKey = JSON.stringify(arcs);
100
+ useEffect(() => {
101
+ const wrap = wrapRef.current;
102
+ const canvas = canvasRef.current;
103
+ const ctx = canvas.getContext("2d");
104
+ const dots = landDots(dotCount);
105
+ const arcPaths = arcs.map((arc) => arcPoints(arc.from, arc.to));
106
+ const reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
107
+ const state = view.current;
108
+ let width = 0;
109
+ let frame = 0;
110
+ let visible = true;
111
+ let last = performance.now();
112
+ let clock = 0;
113
+ const resize = () => {
114
+ const dpr = Math.min(2, window.devicePixelRatio || 1);
115
+ width = wrap.clientWidth;
116
+ canvas.width = Math.round(width * dpr);
117
+ canvas.height = Math.round(width * dpr);
118
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
119
+ draw();
120
+ };
121
+ const project = ([x, y, z], cosY, sinY, cosP, sinP, radius, center) => {
122
+ const x1 = x * cosY + z * sinY;
123
+ const z1 = z * cosY - x * sinY;
124
+ const y2 = y * cosP - z1 * sinP;
125
+ const z2 = y * sinP + z1 * cosP;
126
+ return [center + x1 * radius, center - y2 * radius, z2, x1 * x1 + y2 * y2 > 1];
127
+ };
128
+ function draw() {
129
+ const c = config.current;
130
+ const center = width / 2;
131
+ const radius = width * 0.42;
132
+ const cosY = Math.cos(state.yaw);
133
+ const sinY = Math.sin(state.yaw);
134
+ const cosP = Math.cos(state.pitch);
135
+ const sinP = Math.sin(state.pitch);
136
+ ctx.clearRect(0, 0, width, width);
137
+ const glow = ctx.createRadialGradient(center, center, radius * 0.9, center, center, radius * 1.18);
138
+ glow.addColorStop(0, c.glowColor);
139
+ glow.addColorStop(1, "rgba(0,0,0,0)");
140
+ ctx.fillStyle = glow;
141
+ ctx.fillRect(0, 0, width, width);
142
+ ctx.beginPath();
143
+ ctx.arc(center, center, radius, 0, Math.PI * 2);
144
+ ctx.fillStyle = c.oceanColor;
145
+ ctx.fill();
146
+ const dotRadius = Math.max(0.6, radius * 68e-4 * c.dotSize);
147
+ ctx.fillStyle = c.dotColor;
148
+ for (const dot of dots) {
149
+ const [sx, sy, depth] = project(dot, cosY, sinY, cosP, sinP, radius, center);
150
+ if (depth <= 0) continue;
151
+ ctx.globalAlpha = 0.25 + depth * 0.75;
152
+ const r = dotRadius * (0.55 + depth * 0.45);
153
+ if (c.square) ctx.fillRect(sx - r, sy - r, r * 2, r * 2);
154
+ else {
155
+ ctx.beginPath();
156
+ ctx.arc(sx, sy, r, 0, Math.PI * 2);
157
+ ctx.fill();
158
+ }
159
+ }
160
+ ctx.globalAlpha = 1;
161
+ ctx.lineCap = "round";
162
+ arcPaths.forEach((path, index) => {
163
+ const projected = path.map((point) => project(point, cosY, sinY, cosP, sinP, radius, center));
164
+ const shown = (p) => p[2] > 0 || p[3];
165
+ ctx.strokeStyle = c.arcColor;
166
+ ctx.lineWidth = 1;
167
+ ctx.globalAlpha = 0.35;
168
+ ctx.beginPath();
169
+ projected.forEach((p, i) => i && shown(p) && shown(projected[i - 1]) ? ctx.lineTo(p[0], p[1]) : ctx.moveTo(p[0], p[1]));
170
+ ctx.stroke();
171
+ const head = reduced ? 1 : (clock * 0.35 + index * 0.37) % 1.4 / 1.1;
172
+ const tail = 0.28;
173
+ ctx.lineWidth = 2;
174
+ for (let i = 1; i < projected.length; i++) {
175
+ const t = i / (projected.length - 1);
176
+ if (t > head || t < head - tail || !shown(projected[i]) || !shown(projected[i - 1])) continue;
177
+ ctx.globalAlpha = 1 - (head - t) / tail;
178
+ ctx.beginPath();
179
+ ctx.moveTo(projected[i - 1][0], projected[i - 1][1]);
180
+ ctx.lineTo(projected[i][0], projected[i][1]);
181
+ ctx.stroke();
182
+ }
183
+ });
184
+ ctx.globalAlpha = 1;
185
+ for (const marker of c.markers) {
186
+ const [sx, sy, depth] = project(toVec(marker.lat, marker.lon), cosY, sinY, cosP, sinP, radius, center);
187
+ if (depth <= 0.05) continue;
188
+ const color = marker.color ?? c.markerColor;
189
+ const pulse = reduced ? 0.5 : ((clock * 0.8 + marker.lon / 90) % 1 + 1) % 1;
190
+ ctx.globalAlpha = depth * (1 - pulse);
191
+ ctx.strokeStyle = color;
192
+ ctx.lineWidth = 1.5;
193
+ ctx.beginPath();
194
+ ctx.arc(sx, sy, 3 + pulse * 12, 0, Math.PI * 2);
195
+ ctx.stroke();
196
+ ctx.globalAlpha = depth;
197
+ ctx.fillStyle = color;
198
+ ctx.beginPath();
199
+ ctx.arc(sx, sy, 3.5, 0, Math.PI * 2);
200
+ ctx.fill();
201
+ }
202
+ ctx.globalAlpha = 1;
203
+ }
204
+ const tick = (now) => {
205
+ frame = 0;
206
+ const dt = Math.min(0.05, (now - last) / 1e3);
207
+ last = now;
208
+ clock += dt;
209
+ const c = config.current;
210
+ if (!state.dragging) {
211
+ state.yaw += state.vy * dt;
212
+ state.pitch += state.vp * dt;
213
+ state.vy *= Math.pow(0.04, dt);
214
+ state.vp *= Math.pow(0.04, dt);
215
+ if (c.autoRotate && Math.abs(state.vy) < 0.2) state.yaw += c.rotateSpeed * RAD * dt;
216
+ state.pitch = Math.max(-1.2, Math.min(1.2, state.pitch));
217
+ }
218
+ draw();
219
+ if (visible) frame = requestAnimationFrame(tick);
220
+ };
221
+ const start = () => {
222
+ if (reduced || frame || !visible) return;
223
+ last = performance.now();
224
+ frame = requestAnimationFrame(tick);
225
+ };
226
+ let pointer = null;
227
+ const onDown = (event) => {
228
+ if (!config.current.draggable) return;
229
+ pointer = { id: event.pointerId, x: event.clientX, y: event.clientY, t: performance.now() };
230
+ state.dragging = true;
231
+ state.vy = 0;
232
+ state.vp = 0;
233
+ wrap.setPointerCapture(event.pointerId);
234
+ wrap.setAttribute("data-dragging", "");
235
+ };
236
+ const onMove = (event) => {
237
+ if (!pointer || event.pointerId !== pointer.id) return;
238
+ const now = performance.now();
239
+ const scale = Math.PI / Math.max(1, width);
240
+ const dx = (event.clientX - pointer.x) * scale;
241
+ const dy = (event.clientY - pointer.y) * scale;
242
+ const dt = Math.max(8e-3, (now - pointer.t) / 1e3);
243
+ state.yaw += dx;
244
+ state.pitch = Math.max(-1.2, Math.min(1.2, state.pitch + dy));
245
+ state.vy = dx / dt;
246
+ state.vp = dy / dt;
247
+ pointer = { ...pointer, x: event.clientX, y: event.clientY, t: now };
248
+ if (reduced) draw();
249
+ };
250
+ const onUp = (event) => {
251
+ if (!pointer || event.pointerId !== pointer.id) return;
252
+ if (performance.now() - pointer.t > 80) {
253
+ state.vy = 0;
254
+ state.vp = 0;
255
+ }
256
+ pointer = null;
257
+ state.dragging = false;
258
+ wrap.removeAttribute("data-dragging");
259
+ };
260
+ const onKey = (event) => {
261
+ const step = 10 * RAD;
262
+ const moves = { ArrowLeft: [step, 0], ArrowRight: [-step, 0], ArrowUp: [0, step], ArrowDown: [0, -step] };
263
+ const move = moves[event.key];
264
+ if (!move) return;
265
+ event.preventDefault();
266
+ state.yaw += move[0];
267
+ state.pitch = Math.max(-1.2, Math.min(1.2, state.pitch + move[1]));
268
+ draw();
269
+ };
270
+ const observer = new ResizeObserver(resize);
271
+ observer.observe(wrap);
272
+ const io = new IntersectionObserver(([entry]) => {
273
+ visible = entry.isIntersecting;
274
+ if (visible) start();
275
+ });
276
+ io.observe(wrap);
277
+ wrap.addEventListener("pointerdown", onDown);
278
+ wrap.addEventListener("pointermove", onMove);
279
+ wrap.addEventListener("pointerup", onUp);
280
+ wrap.addEventListener("pointercancel", onUp);
281
+ wrap.addEventListener("keydown", onKey);
282
+ resize();
283
+ start();
284
+ return () => {
285
+ cancelAnimationFrame(frame);
286
+ observer.disconnect();
287
+ io.disconnect();
288
+ wrap.removeEventListener("pointerdown", onDown);
289
+ wrap.removeEventListener("pointermove", onMove);
290
+ wrap.removeEventListener("pointerup", onUp);
291
+ wrap.removeEventListener("pointercancel", onUp);
292
+ wrap.removeEventListener("keydown", onKey);
293
+ };
294
+ }, [dotCount, arcsKey]);
295
+ const names = markers.map((m) => m.label).filter(Boolean);
296
+ return /* @__PURE__ */ jsx(
297
+ "div",
298
+ {
299
+ ref: wrapRef,
300
+ className: `bl-globe ${className}`,
301
+ role: "img",
302
+ tabIndex: 0,
303
+ "aria-label": names.length ? `${label}: ${names.join(", ")}. Drag or use arrow keys to rotate.` : `${label}. Drag or use arrow keys to rotate.`,
304
+ style: { "--bl-size": `${size}px`, "--bl-focus": dotColor, ...style },
305
+ children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, "aria-hidden": "true" })
306
+ }
307
+ );
308
+ }
309
+ export {
310
+ DotGlobe as default,
311
+ isLand
312
+ };
@@ -0,0 +1,47 @@
1
+ import { type CSSProperties } from 'react';
2
+ export declare function isLand(lat: number, lon: number): boolean;
3
+ /** [latitude, longitude] in degrees. */
4
+ export type LatLon = [number, number];
5
+ export interface MapMarker {
6
+ lat: number;
7
+ lon: number;
8
+ label?: string;
9
+ color?: string;
10
+ }
11
+ export interface MapRoute {
12
+ from: LatLon;
13
+ to: LatLon;
14
+ color?: string;
15
+ }
16
+ export interface DotMapProps {
17
+ markers?: MapMarker[];
18
+ routes?: MapRoute[];
19
+ /** Grid spacing in degrees (multiples of 2). */
20
+ dotSpacing?: number;
21
+ /** Dot size, in map units (1 unit = 1 degree). */
22
+ dotRadius?: number;
23
+ /** Square dots, for a pixel look. */
24
+ square?: boolean;
25
+ /** Hide latitudes south of 60°S. */
26
+ cropAntarctica?: boolean;
27
+ dotColor?: string;
28
+ markerColor?: string;
29
+ routeColor?: string;
30
+ labelColor?: string;
31
+ /** Outline behind labels, for contrast. */
32
+ labelHalo?: string;
33
+ /** Label size, in map units. */
34
+ labelSize?: number;
35
+ /** Labels always on, or on hover and focus only. */
36
+ showLabels?: boolean;
37
+ /** Route comet loop length, in seconds. */
38
+ routeSpeed?: number;
39
+ fontFamily?: string;
40
+ /** Makes markers buttons (click, Enter or Space). */
41
+ onMarkerSelect?: (marker: MapMarker, index: number) => void;
42
+ /** Accessible name of the map. */
43
+ label?: string;
44
+ className?: string;
45
+ style?: CSSProperties;
46
+ }
47
+ export default function DotMap({ markers, routes, dotSpacing, dotRadius, square, cropAntarctica, dotColor, markerColor, routeColor, labelColor, labelHalo, labelSize, showLabels, routeSpeed, fontFamily, onMarkerSelect, label, className, style }: DotMapProps): import("react").JSX.Element;
package/dist/DotMap.js ADDED
@@ -0,0 +1,172 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useId, useInsertionEffect, useMemo, useState } from "react";
4
+ const LAND = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf4AP/AAAAAAAAAAAAAAAAAAAAAAAAX/z///+AAAAAAAABAAAAAAAAAAAAAYd8P///wAA+AAAAAA8AAAAAAAAAAAwAnw////4AAIAAAAAAGAAAAAAAAAAADivwAf//wAAAAADAAf/wAHYAAAAAADoi3sAP//gAAAAAMAD///sAAAAgBgACfwz/AD/+gAAAwAEHf///+/8gBAP/7/nJdjwD/+AAAH/AA7f///////f4P//////h8H/gAAAf/6//f////////Mf/////9H4D8AeAA+ev///////////AP/////4A0B4AAAD5/////////////Af3////gHgA4AAAH5///////////LwAHgH///gHkAAAAAH4/////////+CIAABAB///4D+AAAAGCx/////////4A8AAIAAf///n/gAAAOCD/////////wA4AAAAAf///n/wAAAbP///////////AgAAAAAP/////wAAADf//////////9AAAAAAAF////0YAAAB///////////9AAAAAAAD////8EAAAB///////////5AAAAAAAD////2AAAAB/f5fP//////wAAAAAAAD////gAAAAfxnwPP//////jAAAAAAAD////AAAAAPCb3/n/////+CAAAAAAAD///8AAAAAfALf/n////+ECAAAAAAAB///8AAAAAGHQP/n/////mMAAAAAAAA///8AAAAAH+Ai///////E8AAAAAAAAf//wAAAAAP/AA///////BgAAAAAAAAP//gAAAAAf/73///////gAAAAAAAAAD/AQAAAAAf////f/////gAAAAAAAAAF+AQAAAAB///+/n/////AAAAAAAAAAC+AAAAAAB///+f0H////AAAAAAAAAAAeAwAAAAD////f/B///8gAAAAAAAAAAeGEAAAAH////v+B/z/AAAAAAAAAAAAPMAgAAAD////n+A/B+gAAAAAAAAAAAD8AAAAAD////n4AeB/AgAAAAAAAAAAAPAAAAAH////3gAcAfAgAAAAAAAAAAADAAAAAD////6AAcAfggAAAAAAAAAAABDwAAAD////8wAMATAIAAAAAAAAAAAAr/AAAB/////gAKASAAAAAAAAAAAAAAH/gAAA/////gACAAAIAAAAAAAAAAAAH/8AAAaH///AAAAsGAAAAAAAAAAAAAH/+AAAAB//+AAAAUOAAAAAAAAAAAAAP/+AAAAB//8AAAAYegAAAAAAAAAAAAP//gAAAD//4AAAAMeBgAAAAAAAAAAAP//8AAAB//wAAAAGdiuAAAAAAAAAAAf///AAAA//wAAAACAQHgAAAAAAAAAAP///gAAA//wAAAABwAHwgAAAAAAAAAH///AAAA//wAAAAACIDQIAAAAAAAAAH//+AAAAf/wAAAAAAAAAAAAAAAAAAAD//+AAAA//wgAAAAABxAAAAAAAAAAAD//+AAAA//wgAAAAAPxgBAAAAAAAAAA//8AAAA//jgAAAAAf5gAAAAAAAAAAAf/8AAAA//DgAAAAAf/gAAAAAAAAAAAf/8AAAAf/DAAAAAD//4CAAAAAAAAAAf/wAAAAf/DAAAAAH//4AAAAAAAAAAAf/AAAAAf+CAAAAAH//8AAAAAAAAAAAf/AAAAAP8AAAAAAH//+AAAAAAAAAAA/+AAAAAP8AAAAAAH//+AAAAAAAAAAA/+AAAAAH4AAAAAAD//+AAAAAAAAAAA/8AAAAAHwAAAAAADwf8AAAAAAAAAAA/gAAAAAAAAAAAAACAH4AIAAAAAAAAB/wAAAAAAAAAAAAAAAD4AEAAAAAAAAB+AAAAAAAAAAAAAAAAAAAGAAAAAAAAB6AAAAAAAAAAAAAAAAAwAMAAAAAAAAA8AAAAAAAAAAAAAAAAAQAYAAAAAAAAB4AAAAAAAAAAAAAAAAAAAwAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAACAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAeAAIP+f/gAAAAAAAAAAAMAAAAAAABP/+H//////AAAAAAAAAAA+AAAAAf////8////////AAAAAAAOEAPAAAB///////////////gAAAP//T//8AAAH//////////////+AAAH/////4AAAH///////////////8AAE//////4ABw////////////////8AAAD//////gCA////////////////wAAAf/////////////////////////+A/4A///////////////////////////////////////////////////////////////////////////////////////";
5
+ const STYLE_ID = "bl-dot-map";
6
+ const CSS = `
7
+ .bl-map { position: relative; width: 100%; color: var(--bl-dot); font-family: var(--bl-font, ui-sans-serif, system-ui, sans-serif); }
8
+ .bl-map svg { display: block; width: 100%; height: auto; overflow: visible; }
9
+ .bl-map__route-base { fill: none; stroke: var(--bl-route); stroke-width: 0.5; opacity: 0.35; }
10
+ .bl-map__route-comet { fill: none; stroke: var(--bl-route); stroke-width: 1; stroke-linecap: round; stroke-dasharray: 0.18 1; animation: bl-map-travel var(--bl-speed) linear infinite; }
11
+ .bl-map__pulse { fill: none; stroke: currentColor; stroke-width: 0.6; transform-box: fill-box; transform-origin: center; animation: bl-map-pulse 2.2s ease-out infinite; }
12
+ .bl-map__marker { color: var(--bl-marker); cursor: default; outline: none; }
13
+ .bl-map__marker:focus-visible .bl-map__core, .bl-map__marker:hover .bl-map__core { stroke: var(--bl-label); stroke-width: 0.6; }
14
+ .bl-map__label { fill: var(--bl-label); font-size: var(--bl-label-size); font-weight: 600; paint-order: stroke; stroke: var(--bl-label-halo); stroke-width: 1.2; stroke-linejoin: round; pointer-events: none; opacity: var(--bl-label-opacity); transition: opacity 160ms; }
15
+ .bl-map__marker:hover .bl-map__label, .bl-map__marker:focus-visible .bl-map__label { opacity: 1; }
16
+ @keyframes bl-map-travel { from { stroke-dashoffset: 0.18; } to { stroke-dashoffset: -1; } }
17
+ @keyframes bl-map-pulse { from { transform: scale(1); opacity: 0.9; } to { transform: scale(4); opacity: 0; } }
18
+ @media (prefers-reduced-motion: reduce) {
19
+ .bl-map__route-comet { animation: none; stroke-dasharray: none; opacity: 0.8; stroke-width: 0.6; }
20
+ .bl-map__pulse { animation: none; opacity: 0; }
21
+ }
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
+ let landBits;
32
+ function isLand(lat, lon) {
33
+ if (!landBits) landBits = Uint8Array.from(atob(LAND), (c) => c.charCodeAt(0));
34
+ const col = Math.min(179, Math.max(0, Math.floor((lon + 180) / 2)));
35
+ const row = Math.min(89, Math.max(0, Math.floor((90 - lat) / 2)));
36
+ const i = row * 180 + col;
37
+ return (landBits[i >> 3] & 1 << 7 - (i & 7)) !== 0;
38
+ }
39
+ const project = (lat, lon) => [lon + 180, 90 - lat];
40
+ function dotsPath(spacing, radius, square, southLimit) {
41
+ let d = "";
42
+ for (let lat = 90 - spacing / 2; lat > southLimit; lat -= spacing) {
43
+ for (let lon = -180 + spacing / 2; lon < 180; lon += spacing) {
44
+ if (!isLand(lat, lon)) continue;
45
+ const [x, y] = project(lat, lon);
46
+ d += square ? `M${(x - radius).toFixed(2)} ${(y - radius).toFixed(2)}h${radius * 2}v${radius * 2}h${-radius * 2}z` : `M${(x - radius).toFixed(2)} ${y.toFixed(2)}a${radius} ${radius} 0 1 0 ${radius * 2} 0a${radius} ${radius} 0 1 0 ${-radius * 2} 0`;
47
+ }
48
+ }
49
+ return d;
50
+ }
51
+ function routePath(from, to) {
52
+ const [x1, y1] = project(...from);
53
+ let [x2, y2] = project(...to);
54
+ if (Math.abs(x2 - x1) > 180) x2 += x2 > x1 ? -360 : 360;
55
+ const distance = Math.hypot(x2 - x1, y2 - y1);
56
+ const mx = (x1 + x2) / 2;
57
+ const my = (y1 + y2) / 2 - distance * 0.28;
58
+ return `M${x1.toFixed(2)} ${y1.toFixed(2)}Q${mx.toFixed(2)} ${my.toFixed(2)} ${x2.toFixed(2)} ${y2.toFixed(2)}`;
59
+ }
60
+ const DEFAULT_MARKERS = [
61
+ { lat: 37.77, lon: -122.42, label: "San Francisco" },
62
+ { lat: 40.71, lon: -74, label: "New York" },
63
+ { lat: 51.51, lon: -0.13, label: "London" },
64
+ { lat: 6.52, lon: 3.38, label: "Lagos" },
65
+ { lat: 19.08, lon: 72.88, label: "Mumbai" },
66
+ { lat: 35.68, lon: 139.69, label: "Tokyo" },
67
+ { lat: -33.87, lon: 151.21, label: "Sydney" },
68
+ { lat: -23.55, lon: -46.63, label: "S\xE3o Paulo" }
69
+ ];
70
+ const DEFAULT_ROUTES = [
71
+ { from: [37.77, -122.42], to: [40.71, -74] },
72
+ { from: [40.71, -74], to: [51.51, -0.13] },
73
+ { from: [51.51, -0.13], to: [6.52, 3.38] },
74
+ { from: [51.51, -0.13], to: [19.08, 72.88] },
75
+ { from: [19.08, 72.88], to: [35.68, 139.69] },
76
+ { from: [35.68, 139.69], to: [-33.87, 151.21] },
77
+ { from: [40.71, -74], to: [-23.55, -46.63] }
78
+ ];
79
+ function DotMap({
80
+ markers = DEFAULT_MARKERS,
81
+ routes = DEFAULT_ROUTES,
82
+ dotSpacing = 2,
83
+ dotRadius = 0.55,
84
+ square = false,
85
+ cropAntarctica = true,
86
+ dotColor = "rgba(148,176,194,0.55)",
87
+ markerColor = "#ef7d57",
88
+ routeColor = "#ffcd75",
89
+ labelColor = "#f4f4f4",
90
+ labelHalo = "#1a1c2c",
91
+ labelSize = 4,
92
+ showLabels = true,
93
+ routeSpeed = 3,
94
+ fontFamily,
95
+ onMarkerSelect,
96
+ label = "World map",
97
+ className = "",
98
+ style
99
+ }) {
100
+ useStyles();
101
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
102
+ const [active, setActive] = useState(null);
103
+ const southLimit = cropAntarctica ? -60 : -90;
104
+ const height = 90 - southLimit;
105
+ const land = useMemo(() => dotsPath(Math.max(2, dotSpacing), dotRadius, square, southLimit), [dotSpacing, dotRadius, square, southLimit]);
106
+ return /* @__PURE__ */ jsx(
107
+ "figure",
108
+ {
109
+ className: `bl-map ${className}`,
110
+ style: {
111
+ "--bl-dot": dotColor,
112
+ "--bl-marker": markerColor,
113
+ "--bl-route": routeColor,
114
+ "--bl-label": labelColor,
115
+ "--bl-label-halo": labelHalo,
116
+ "--bl-label-size": `${labelSize}px`,
117
+ "--bl-label-opacity": showLabels ? 1 : 0,
118
+ "--bl-speed": `${routeSpeed}s`,
119
+ ...fontFamily ? { "--bl-font": fontFamily } : null,
120
+ margin: 0,
121
+ ...style
122
+ },
123
+ children: /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 360 ${height}`, role: "group", "aria-label": label, children: [
124
+ /* @__PURE__ */ jsx("path", { d: land, fill: "currentColor", "aria-hidden": "true" }),
125
+ /* @__PURE__ */ jsx("g", { "aria-hidden": "true", children: routes.map((route, index) => {
126
+ const d = routePath(route.from, route.to);
127
+ return /* @__PURE__ */ jsxs("g", { children: [
128
+ /* @__PURE__ */ jsx("path", { className: "bl-map__route-base", d }),
129
+ /* @__PURE__ */ jsx("path", { className: "bl-map__route-comet", d, pathLength: "1", style: { animationDelay: `${-index * 0.53 % routeSpeed}s`, stroke: route.color } })
130
+ ] }, `${uid}-r${index}`);
131
+ }) }),
132
+ markers.map((marker, index) => {
133
+ const [x, y] = project(marker.lat, marker.lon);
134
+ const flip = x > 300;
135
+ return /* @__PURE__ */ jsxs(
136
+ "g",
137
+ {
138
+ className: "bl-map__marker",
139
+ style: marker.color ? { color: marker.color } : void 0,
140
+ tabIndex: 0,
141
+ role: onMarkerSelect ? "button" : "img",
142
+ "aria-label": marker.label ?? `${marker.lat}, ${marker.lon}`,
143
+ "aria-pressed": onMarkerSelect ? active === index : void 0,
144
+ onClick: () => {
145
+ setActive(index);
146
+ onMarkerSelect?.(marker, index);
147
+ },
148
+ onKeyDown: (event) => {
149
+ if (onMarkerSelect && (event.key === "Enter" || event.key === " ")) {
150
+ event.preventDefault();
151
+ setActive(index);
152
+ onMarkerSelect(marker, index);
153
+ }
154
+ },
155
+ children: [
156
+ /* @__PURE__ */ jsx("circle", { className: "bl-map__pulse", cx: x, cy: y, r: "1.6", style: { animationDelay: `${index * 0.37 % 2.2}s` } }),
157
+ /* @__PURE__ */ jsx("circle", { className: "bl-map__core", cx: x, cy: y, r: active === index ? 2.2 : 1.6, fill: "currentColor" }),
158
+ /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: "5", fill: "transparent" }),
159
+ marker.label && /* @__PURE__ */ jsx("text", { className: "bl-map__label", x: flip ? x - 3.5 : x + 3.5, y: y + labelSize * 0.35, textAnchor: flip ? "end" : "start", children: marker.label })
160
+ ]
161
+ },
162
+ `${uid}-m${index}`
163
+ );
164
+ })
165
+ ] })
166
+ }
167
+ );
168
+ }
169
+ export {
170
+ DotMap as default,
171
+ isLand
172
+ };
@@ -0,0 +1,57 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ export interface DropFile {
3
+ id: number;
4
+ file: File;
5
+ name: string;
6
+ size: number;
7
+ type: string;
8
+ /** Object URL for image thumbnails (revoked on remove and unmount). */
9
+ preview: string | null;
10
+ /** 0–1. */
11
+ progress: number;
12
+ status: 'uploading' | 'done' | 'error';
13
+ error: string | null;
14
+ }
15
+ export interface DropRejection {
16
+ file: File;
17
+ reason: string;
18
+ }
19
+ export type UploadFn = (file: File, context: {
20
+ onProgress: (fraction: number) => void;
21
+ signal: AbortSignal;
22
+ }) => unknown;
23
+ export interface DropZoneProps {
24
+ /** Same syntax as <input accept>: MIME types, wildcards like image/*, or .extensions. */
25
+ accept?: string;
26
+ multiple?: boolean;
27
+ maxFiles?: number;
28
+ /** Max size per file, in bytes. */
29
+ maxSize?: number;
30
+ /** Your uploader: call onProgress(0–1); removing the file aborts via signal. Without it, files are just collected. */
31
+ upload?: UploadFn;
32
+ onFilesChange?: (files: DropFile[]) => void;
33
+ onReject?: (rejections: DropRejection[]) => void;
34
+ title?: ReactNode;
35
+ /** Defaults to a summary of the rules. */
36
+ hint?: ReactNode;
37
+ disabled?: boolean;
38
+ /** Lean toward the cursor while files are dragged over. */
39
+ magnetic?: boolean;
40
+ width?: number;
41
+ radius?: number;
42
+ backgroundColor?: string;
43
+ /** Card color while files are dragged over. */
44
+ overColor?: string;
45
+ textColor?: string;
46
+ mutedColor?: string;
47
+ lineColor?: string;
48
+ chipColor?: string;
49
+ accentColor?: string;
50
+ successColor?: string;
51
+ errorColor?: string;
52
+ fontFamily?: string;
53
+ className?: string;
54
+ style?: CSSProperties;
55
+ }
56
+ export declare function formatBytes(bytes: number): string;
57
+ export default function DropZone({ accept, multiple, maxFiles, maxSize, upload, onFilesChange, onReject, title, hint, disabled, magnetic, width, radius, backgroundColor, overColor, textColor, mutedColor, lineColor, chipColor, accentColor, successColor, errorColor, fontFamily, className, style }: DropZoneProps): import("react").JSX.Element;