@seekalfred/primitives 0.6.0 → 0.7.3

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 (346) hide show
  1. package/README.md +214 -15
  2. package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
  3. package/dist/activity-heatmap/activity-heatmap.js +699 -0
  4. package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
  5. package/dist/activity-heatmap/index.d.ts +2 -0
  6. package/dist/activity-heatmap/index.js +2 -0
  7. package/dist/alert/alert.d.ts +53 -4
  8. package/dist/alert/alert.js +100 -11
  9. package/dist/alert/alert.js.map +1 -1
  10. package/dist/alert/index.d.ts +2 -2
  11. package/dist/alert/index.js +2 -2
  12. package/dist/alert-dialog/alert-dialog.d.ts +58 -18
  13. package/dist/alert-dialog/alert-dialog.js +152 -9
  14. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  15. package/dist/alert-dialog/index.d.ts +2 -2
  16. package/dist/alert-dialog/index.js +2 -2
  17. package/dist/animated-counter/animated-counter.d.ts +38 -0
  18. package/dist/animated-counter/animated-counter.js +230 -0
  19. package/dist/animated-counter/animated-counter.js.map +1 -0
  20. package/dist/animated-counter/index.d.ts +2 -0
  21. package/dist/animated-counter/index.js +2 -0
  22. package/dist/announcement-bar/announcement-bar.d.ts +67 -0
  23. package/dist/announcement-bar/announcement-bar.js +364 -0
  24. package/dist/announcement-bar/announcement-bar.js.map +1 -0
  25. package/dist/announcement-bar/index.d.ts +2 -0
  26. package/dist/announcement-bar/index.js +2 -0
  27. package/dist/badge/badge.d.ts +1 -1
  28. package/dist/bar-chart/bar-chart.d.ts +51 -0
  29. package/dist/bar-chart/bar-chart.js +565 -0
  30. package/dist/bar-chart/bar-chart.js.map +1 -0
  31. package/dist/bar-chart/index.d.ts +2 -0
  32. package/dist/bar-chart/index.js +2 -0
  33. package/dist/breadcrumb/breadcrumb.d.ts +2 -2
  34. package/dist/breadcrumb/breadcrumb.js +12 -3
  35. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  36. package/dist/brush-chart/brush-chart.d.ts +64 -0
  37. package/dist/brush-chart/brush-chart.js +1056 -0
  38. package/dist/brush-chart/brush-chart.js.map +1 -0
  39. package/dist/brush-chart/index.d.ts +2 -0
  40. package/dist/brush-chart/index.js +2 -0
  41. package/dist/button/button.d.ts +1 -1
  42. package/dist/button/button.js +31 -21
  43. package/dist/button/button.js.map +1 -1
  44. package/dist/button-group/button-group.js +3 -4
  45. package/dist/button-group/button-group.js.map +1 -1
  46. package/dist/calendar/calendar.d.ts +1 -1
  47. package/dist/card/card.d.ts +22 -1
  48. package/dist/card/card.js +60 -7
  49. package/dist/card/card.js.map +1 -1
  50. package/dist/card-stack/card-stack.d.ts +54 -0
  51. package/dist/card-stack/card-stack.js +492 -0
  52. package/dist/card-stack/card-stack.js.map +1 -0
  53. package/dist/card-stack/index.d.ts +2 -0
  54. package/dist/card-stack/index.js +2 -0
  55. package/dist/carousel/carousel.d.ts +77 -0
  56. package/dist/carousel/carousel.js +415 -0
  57. package/dist/carousel/carousel.js.map +1 -0
  58. package/dist/carousel/index.d.ts +2 -0
  59. package/dist/carousel/index.js +2 -0
  60. package/dist/checkbox/checkbox.js +4 -4
  61. package/dist/checkbox/checkbox.js.map +1 -1
  62. package/dist/chip-group/chip-group.d.ts +37 -0
  63. package/dist/chip-group/chip-group.js +148 -0
  64. package/dist/chip-group/chip-group.js.map +1 -0
  65. package/dist/chip-group/index.d.ts +2 -0
  66. package/dist/chip-group/index.js +2 -0
  67. package/dist/copy-button/copy-button.d.ts +18 -2
  68. package/dist/copy-button/copy-button.js +29 -8
  69. package/dist/copy-button/copy-button.js.map +1 -1
  70. package/dist/countdown/countdown.d.ts +45 -0
  71. package/dist/countdown/countdown.js +298 -0
  72. package/dist/countdown/countdown.js.map +1 -0
  73. package/dist/countdown/index.d.ts +2 -0
  74. package/dist/countdown/index.js +2 -0
  75. package/dist/date-picker/picker-parts.js +1 -1
  76. package/dist/date-picker/picker-parts.js.map +1 -1
  77. package/dist/dialog/dialog.js +1 -1
  78. package/dist/dialog/dialog.js.map +1 -1
  79. package/dist/donut-chart/donut-chart.d.ts +86 -0
  80. package/dist/donut-chart/donut-chart.js +593 -0
  81. package/dist/donut-chart/donut-chart.js.map +1 -0
  82. package/dist/donut-chart/index.d.ts +2 -0
  83. package/dist/donut-chart/index.js +2 -0
  84. package/dist/empty-state/empty-state.d.ts +42 -10
  85. package/dist/empty-state/empty-state.js +44 -26
  86. package/dist/empty-state/empty-state.js.map +1 -1
  87. package/dist/empty-state/index.d.ts +2 -2
  88. package/dist/empty-state/index.js +2 -2
  89. package/dist/file-drop-zone/file-drop-zone.js +1 -1
  90. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  91. package/dist/filter-bar/filter-bar.d.ts +50 -26
  92. package/dist/filter-bar/filter-bar.js +317 -157
  93. package/dist/filter-bar/filter-bar.js.map +1 -1
  94. package/dist/gauge/gauge.d.ts +45 -0
  95. package/dist/gauge/gauge.js +219 -0
  96. package/dist/gauge/gauge.js.map +1 -0
  97. package/dist/gauge/index.d.ts +2 -0
  98. package/dist/gauge/index.js +2 -0
  99. package/dist/in-view-title/in-view-title.d.ts +33 -0
  100. package/dist/in-view-title/in-view-title.js +119 -0
  101. package/dist/in-view-title/in-view-title.js.map +1 -0
  102. package/dist/in-view-title/index.d.ts +2 -0
  103. package/dist/in-view-title/index.js +2 -0
  104. package/dist/index.d.ts +53 -10
  105. package/dist/index.js +52 -12
  106. package/dist/input/input.js +4 -4
  107. package/dist/input/input.js.map +1 -1
  108. package/dist/lib/cn.js +11 -1
  109. package/dist/lib/cn.js.map +1 -1
  110. package/dist/lib/field-message.js +28 -10
  111. package/dist/lib/field-message.js.map +1 -1
  112. package/dist/lib/field-shell.js +10 -3
  113. package/dist/lib/field-shell.js.map +1 -1
  114. package/dist/lib/field-surface.js +31 -9
  115. package/dist/lib/field-surface.js.map +1 -1
  116. package/dist/lib/flag-emoji.js +50 -0
  117. package/dist/lib/flag-emoji.js.map +1 -0
  118. package/dist/lib/flip.js +11 -3
  119. package/dist/lib/flip.js.map +1 -1
  120. package/dist/lib/menu-highlight.js +64 -0
  121. package/dist/lib/menu-highlight.js.map +1 -0
  122. package/dist/lib/motion.js +9 -2
  123. package/dist/lib/motion.js.map +1 -1
  124. package/dist/lib/rolling-value.js +1 -1
  125. package/dist/lib/rolling-value.js.map +1 -1
  126. package/dist/lib/spring-tween.js +500 -0
  127. package/dist/lib/spring-tween.js.map +1 -0
  128. package/dist/lib/swap-text.js +3 -1
  129. package/dist/lib/swap-text.js.map +1 -1
  130. package/dist/lib/use-copy-feedback.js +17 -3
  131. package/dist/lib/use-copy-feedback.js.map +1 -1
  132. package/dist/line-chart/index.d.ts +2 -0
  133. package/dist/line-chart/index.js +2 -0
  134. package/dist/line-chart/line-chart.d.ts +116 -0
  135. package/dist/line-chart/line-chart.js +740 -0
  136. package/dist/line-chart/line-chart.js.map +1 -0
  137. package/dist/mention-input/index.d.ts +2 -0
  138. package/dist/mention-input/index.js +2 -0
  139. package/dist/mention-input/mention-input.d.ts +98 -0
  140. package/dist/mention-input/mention-input.js +576 -0
  141. package/dist/mention-input/mention-input.js.map +1 -0
  142. package/dist/menu/menu.d.ts +2 -0
  143. package/dist/menu/menu.js +23 -12
  144. package/dist/menu/menu.js.map +1 -1
  145. package/dist/money-input/index.d.ts +2 -0
  146. package/dist/money-input/index.js +2 -0
  147. package/dist/money-input/money-input.d.ts +86 -0
  148. package/dist/money-input/money-input.js +613 -0
  149. package/dist/money-input/money-input.js.map +1 -0
  150. package/dist/morph-select/index.d.ts +2 -0
  151. package/dist/morph-select/index.js +2 -0
  152. package/dist/morph-select/morph-select.d.ts +89 -0
  153. package/dist/morph-select/morph-select.js +712 -0
  154. package/dist/morph-select/morph-select.js.map +1 -0
  155. package/dist/multi-select/index.d.ts +2 -0
  156. package/dist/multi-select/index.js +2 -0
  157. package/dist/multi-select/multi-select.d.ts +77 -0
  158. package/dist/multi-select/multi-select.js +209 -0
  159. package/dist/multi-select/multi-select.js.map +1 -0
  160. package/dist/number-field/index.d.ts +2 -0
  161. package/dist/number-field/index.js +2 -0
  162. package/dist/number-field/number-field.d.ts +42 -0
  163. package/dist/number-field/number-field.js +199 -0
  164. package/dist/number-field/number-field.js.map +1 -0
  165. package/dist/onboarding-checklist/index.d.ts +2 -0
  166. package/dist/onboarding-checklist/index.js +2 -0
  167. package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
  168. package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
  169. package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
  170. package/dist/password-field/password-field.d.ts +16 -0
  171. package/dist/password-field/password-field.js +39 -10
  172. package/dist/password-field/password-field.js.map +1 -1
  173. package/dist/phone-input/index.d.ts +3 -0
  174. package/dist/phone-input/index.js +3 -0
  175. package/dist/phone-input/phone-countries.d.ts +29 -0
  176. package/dist/phone-input/phone-countries.js +170 -0
  177. package/dist/phone-input/phone-countries.js.map +1 -0
  178. package/dist/phone-input/phone-input.d.ts +66 -0
  179. package/dist/phone-input/phone-input.js +440 -0
  180. package/dist/phone-input/phone-input.js.map +1 -0
  181. package/dist/popover/index.d.ts +2 -2
  182. package/dist/popover/index.js +2 -2
  183. package/dist/popover/popover.d.ts +51 -2
  184. package/dist/popover/popover.js +59 -8
  185. package/dist/popover/popover.js.map +1 -1
  186. package/dist/progress/progress.d.ts +16 -2
  187. package/dist/progress/progress.js +16 -9
  188. package/dist/progress/progress.js.map +1 -1
  189. package/dist/radio-group/index.d.ts +2 -2
  190. package/dist/radio-group/index.js +2 -2
  191. package/dist/radio-group/radio-group.d.ts +33 -3
  192. package/dist/radio-group/radio-group.js +110 -10
  193. package/dist/radio-group/radio-group.js.map +1 -1
  194. package/dist/reactions/index.d.ts +2 -0
  195. package/dist/reactions/index.js +2 -0
  196. package/dist/reactions/reactions.d.ts +50 -0
  197. package/dist/reactions/reactions.js +315 -0
  198. package/dist/reactions/reactions.js.map +1 -0
  199. package/dist/rich-text-editor/index.d.ts +3 -0
  200. package/dist/rich-text-editor/index.js +2 -0
  201. package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
  202. package/dist/rich-text-editor/rich-text-editor.js +909 -0
  203. package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
  204. package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
  205. package/dist/rich-text-editor/rich-text-engine.js +366 -0
  206. package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
  207. package/dist/rich-text-editor/rich-text-html.js +174 -0
  208. package/dist/rich-text-editor/rich-text-html.js.map +1 -0
  209. package/dist/rich-text-editor/rich-text-icons.js +43 -0
  210. package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
  211. package/dist/ridgeline/index.d.ts +2 -0
  212. package/dist/ridgeline/index.js +2 -0
  213. package/dist/ridgeline/ridgeline.d.ts +55 -0
  214. package/dist/ridgeline/ridgeline.js +701 -0
  215. package/dist/ridgeline/ridgeline.js.map +1 -0
  216. package/dist/scroll-area/index.d.ts +2 -2
  217. package/dist/scroll-area/index.js +2 -2
  218. package/dist/scroll-area/scroll-area.d.ts +68 -31
  219. package/dist/scroll-area/scroll-area.js +212 -62
  220. package/dist/scroll-area/scroll-area.js.map +1 -1
  221. package/dist/scrub-input/evaluate.d.ts +14 -0
  222. package/dist/scrub-input/evaluate.js +72 -0
  223. package/dist/scrub-input/evaluate.js.map +1 -0
  224. package/dist/scrub-input/index.d.ts +3 -0
  225. package/dist/scrub-input/index.js +3 -0
  226. package/dist/scrub-input/scrub-input.d.ts +74 -0
  227. package/dist/scrub-input/scrub-input.js +486 -0
  228. package/dist/scrub-input/scrub-input.js.map +1 -0
  229. package/dist/search-field/search-field.d.ts +24 -2
  230. package/dist/search-field/search-field.js +24 -9
  231. package/dist/search-field/search-field.js.map +1 -1
  232. package/dist/select/select.d.ts +3 -1
  233. package/dist/select/select.js +28 -14
  234. package/dist/select/select.js.map +1 -1
  235. package/dist/separator/separator.d.ts +22 -10
  236. package/dist/separator/separator.js +33 -11
  237. package/dist/separator/separator.js.map +1 -1
  238. package/dist/sheet/sheet.js +2 -1
  239. package/dist/sheet/sheet.js.map +1 -1
  240. package/dist/shortcut-recorder/index.d.ts +3 -0
  241. package/dist/shortcut-recorder/index.js +3 -0
  242. package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
  243. package/dist/shortcut-recorder/shortcut-model.js +206 -0
  244. package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
  245. package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
  246. package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
  247. package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
  248. package/dist/slider/index.d.ts +2 -2
  249. package/dist/slider/slider.d.ts +46 -14
  250. package/dist/slider/slider.js +151 -67
  251. package/dist/slider/slider.js.map +1 -1
  252. package/dist/slope-chart/index.d.ts +2 -0
  253. package/dist/slope-chart/index.js +2 -0
  254. package/dist/slope-chart/slope-chart.d.ts +54 -0
  255. package/dist/slope-chart/slope-chart.js +568 -0
  256. package/dist/slope-chart/slope-chart.js.map +1 -0
  257. package/dist/sparkline/index.d.ts +2 -0
  258. package/dist/sparkline/index.js +2 -0
  259. package/dist/sparkline/sparkline.d.ts +46 -0
  260. package/dist/sparkline/sparkline.js +390 -0
  261. package/dist/sparkline/sparkline.js.map +1 -0
  262. package/dist/split-bar/index.d.ts +2 -0
  263. package/dist/split-bar/index.js +2 -0
  264. package/dist/split-bar/split-bar.d.ts +62 -0
  265. package/dist/split-bar/split-bar.js +513 -0
  266. package/dist/split-bar/split-bar.js.map +1 -0
  267. package/dist/split-button/split-button.d.ts +1 -1
  268. package/dist/split-button/split-button.js +12 -11
  269. package/dist/split-button/split-button.js.map +1 -1
  270. package/dist/stat-card/index.d.ts +2 -2
  271. package/dist/stat-card/index.js +2 -2
  272. package/dist/stat-card/stat-card.d.ts +58 -5
  273. package/dist/stat-card/stat-card.js +144 -17
  274. package/dist/stat-card/stat-card.js.map +1 -1
  275. package/dist/stepper/index.d.ts +2 -0
  276. package/dist/stepper/index.js +2 -0
  277. package/dist/stepper/stepper.d.ts +51 -0
  278. package/dist/stepper/stepper.js +215 -0
  279. package/dist/stepper/stepper.js.map +1 -0
  280. package/dist/streamgraph/index.d.ts +2 -0
  281. package/dist/streamgraph/index.js +2 -0
  282. package/dist/streamgraph/streamgraph.d.ts +70 -0
  283. package/dist/streamgraph/streamgraph.js +613 -0
  284. package/dist/streamgraph/streamgraph.js.map +1 -0
  285. package/dist/styles.css +1 -1
  286. package/dist/swipe-actions/index.d.ts +2 -0
  287. package/dist/swipe-actions/index.js +2 -0
  288. package/dist/swipe-actions/swipe-actions.d.ts +67 -0
  289. package/dist/swipe-actions/swipe-actions.js +552 -0
  290. package/dist/swipe-actions/swipe-actions.js.map +1 -0
  291. package/dist/switch/switch.js +2 -2
  292. package/dist/switch/switch.js.map +1 -1
  293. package/dist/table/index.d.ts +2 -2
  294. package/dist/table/index.js +2 -2
  295. package/dist/table/table.d.ts +55 -3
  296. package/dist/table/table.js +340 -165
  297. package/dist/table/table.js.map +1 -1
  298. package/dist/tabs/tabs.js +2 -2
  299. package/dist/tabs/tabs.js.map +1 -1
  300. package/dist/tag-input/index.d.ts +2 -0
  301. package/dist/tag-input/index.js +2 -0
  302. package/dist/tag-input/tag-input.d.ts +40 -0
  303. package/dist/tag-input/tag-input.js +269 -0
  304. package/dist/tag-input/tag-input.js.map +1 -0
  305. package/dist/text-morph/index.d.ts +2 -0
  306. package/dist/text-morph/index.js +2 -0
  307. package/dist/text-morph/text-morph.d.ts +23 -0
  308. package/dist/text-morph/text-morph.js +21 -0
  309. package/dist/text-morph/text-morph.js.map +1 -0
  310. package/dist/text-reveal/index.d.ts +2 -0
  311. package/dist/text-reveal/index.js +2 -0
  312. package/dist/text-reveal/text-reveal.d.ts +27 -0
  313. package/dist/text-reveal/text-reveal.js +51 -0
  314. package/dist/text-reveal/text-reveal.js.map +1 -0
  315. package/dist/text-shimmer/index.d.ts +2 -0
  316. package/dist/text-shimmer/index.js +2 -0
  317. package/dist/text-shimmer/text-shimmer.d.ts +29 -0
  318. package/dist/text-shimmer/text-shimmer.js +29 -0
  319. package/dist/text-shimmer/text-shimmer.js.map +1 -0
  320. package/dist/time-picker/time-picker.d.ts +36 -1
  321. package/dist/time-picker/time-picker.js +35 -7
  322. package/dist/time-picker/time-picker.js.map +1 -1
  323. package/dist/toast/index.d.ts +2 -2
  324. package/dist/toast/index.js +2 -2
  325. package/dist/toast/toast.d.ts +7 -1
  326. package/dist/toast/toast.js +82 -40
  327. package/dist/toast/toast.js.map +1 -1
  328. package/dist/tooltip/tooltip.d.ts +9 -2
  329. package/dist/tooltip/tooltip.js +7 -7
  330. package/dist/tooltip/tooltip.js.map +1 -1
  331. package/dist/treemap/index.d.ts +2 -0
  332. package/dist/treemap/index.js +2 -0
  333. package/dist/treemap/treemap.d.ts +53 -0
  334. package/dist/treemap/treemap.js +699 -0
  335. package/dist/treemap/treemap.js.map +1 -0
  336. package/dist/usage-meter/index.d.ts +2 -0
  337. package/dist/usage-meter/index.js +2 -0
  338. package/dist/usage-meter/usage-meter.d.ts +80 -0
  339. package/dist/usage-meter/usage-meter.js +458 -0
  340. package/dist/usage-meter/usage-meter.js.map +1 -0
  341. package/dist/waffle-chart/index.d.ts +2 -0
  342. package/dist/waffle-chart/index.js +2 -0
  343. package/dist/waffle-chart/waffle-chart.d.ts +57 -0
  344. package/dist/waffle-chart/waffle-chart.js +426 -0
  345. package/dist/waffle-chart/waffle-chart.js.map +1 -0
  346. package/package.json +165 -3
@@ -0,0 +1,440 @@
1
+ "use client";
2
+ import { cn, cnAll } from "../lib/cn.js";
3
+ import { useIsomorphicLayoutEffect } from "../lib/motion.js";
4
+ import { SwapText } from "../lib/swap-text.js";
5
+ import { fieldSurfaceVariants } from "../lib/field-surface.js";
6
+ import { FieldShell, useFieldWiring } from "../lib/field-shell.js";
7
+ import { SelectItem, selectPopoverVariants } from "../select/select.js";
8
+ import { useFlagEmoji } from "../lib/flag-emoji.js";
9
+ import { ScrollArea } from "../scroll-area/scroll-area.js";
10
+ import { PHONE_COUNTRIES, capDigits, caretAfterDigits, flagOf, formatNational, lengthsOf, matchesCountry, onlyDigits, parsePhoneNumber, phoneCountryByIso, phoneStatus, splitTrunk, toE164 } from "./phone-countries.js";
11
+ import { forwardRef, useCallback, useImperativeHandle, useMemo, useReducer, useRef, useState } from "react";
12
+ import { Autocomplete, Button, Dialog, DialogTrigger, Group, Input, ListBox, Popover, SearchField } from "react-aria-components";
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ //#region src/phone-input/phone-input.tsx
15
+ const focusRing = "data-[focus-visible]:not-data-[invalid]:border-foreground forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-(length:--focus-ring-width) forced-colors:data-[focus-visible]:outline-offset-(--focus-ring-offset)";
16
+ const shellClass = cnAll(fieldSurfaceVariants(), "flex h-(--density-control-h-md) items-center", "has-[input:focus]:not-data-[invalid]:border-foreground", "forced-colors:has-[input:focus]:outline-solid forced-colors:has-[input:focus]:outline-(length:--focus-ring-width) forced-colors:has-[input:focus]:outline-offset-(--focus-ring-offset)", focusRing);
17
+ const triggerClass = cnAll("group/trigger m-1 inline-flex h-[calc(100%-0.5rem)] flex-none cursor-pointer items-center gap-1.5 rounded-control border-0 bg-transparent ps-2.5 pe-2 font-sans text-base font-medium tabular-nums text-foreground outline-none", "motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle", "data-[hovered]:bg-muted data-[pressed]:bg-muted data-[disabled]:cursor-not-allowed", focusRing);
18
+ /** A native `<input>` styled flat: the shell draws the surface. */
19
+ const numberClass = "relative z-10 m-0 h-full w-full min-w-0 appearance-none border-0 bg-transparent px-3 pe-10 font-sans text-base tabular-nums text-foreground outline-none selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed";
20
+ const searchClass = "relative flex h-(--density-control-h-sm) items-center gap-2 border-b-(length:--border-width) border-border px-2.5 after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-(--focus-ring-width) after:bg-transparent has-[input:focus]:after:bg-(--color-control-edge)";
21
+ const rowClass = "group/row rounded-control data-[hovered]:bg-muted data-[focus-visible]:outline-(--color-control-edge)";
22
+ const nameClass = "min-w-0 flex-1 truncate font-medium text-text-secondary group-data-[selected]/row:text-foreground group-data-[focused]/row:text-foreground group-data-[hovered]/row:text-foreground";
23
+ const flagClass = "w-6 flex-none text-center text-base leading-none";
24
+ const codeClass = "w-6 flex-none text-center text-xs font-semibold leading-none tracking-wide text-muted-foreground";
25
+ const emptyClass = "px-3 py-3 text-sm text-muted-foreground";
26
+ const SORTED = [...PHONE_COUNTRIES].sort((a, b) => a.name.localeCompare(b.name));
27
+ const textValueOf = (entry) => `${entry.name} ${entry.iso} +${entry.dial}`;
28
+ const PhoneInput = forwardRef(function PhoneInput({ label, labelHidden = false, value, defaultValue, onValueChange, country: countryProp, defaultCountry = "US", onCountryChange, countries, description, error, validate = true, flags = "auto", disabled = false, required, name, id, className, onBlur }, forwardedRef) {
29
+ const pool = useMemo(() => countries?.length ? PHONE_COUNTRIES.filter((entry) => countries.includes(entry.iso)) : PHONE_COUNTRIES, [countries]);
30
+ const list = useMemo(() => SORTED.filter((entry) => pool.includes(entry)), [pool]);
31
+ const items = useMemo(() => list.map((entry) => ({
32
+ ...entry,
33
+ id: entry.iso
34
+ })), [list]);
35
+ const canDrawFlags = useFlagEmoji();
36
+ const flagEmoji = flags === "auto" ? canDrawFlags : flags === "emoji";
37
+ const fallback = phoneCountryByIso(defaultCountry) ?? PHONE_COUNTRIES[0];
38
+ const [state, setState] = useState(() => {
39
+ const parsed = parsePhoneNumber(value ?? defaultValue ?? "", pool);
40
+ return parsed ? {
41
+ iso: parsed.country.iso,
42
+ digits: parsed.national
43
+ } : {
44
+ iso: fallback.iso,
45
+ digits: ""
46
+ };
47
+ });
48
+ const iso = countryProp ?? state.iso;
49
+ const current = phoneCountryByIso(iso) ?? fallback;
50
+ const digits = state.digits;
51
+ const e164 = toE164(current, digits);
52
+ const [seenValue, setSeenValue] = useState(value);
53
+ if (value !== seenValue) {
54
+ setSeenValue(value);
55
+ if (value !== void 0 && value !== e164) {
56
+ const parsed = parsePhoneNumber(value, pool);
57
+ setState(parsed ? {
58
+ iso: parsed.country.iso,
59
+ digits: parsed.national
60
+ } : {
61
+ iso,
62
+ digits: ""
63
+ });
64
+ }
65
+ }
66
+ const formatted = formatNational(current, digits);
67
+ const status = phoneStatus(current, digits);
68
+ const [touched, setTouched] = useState(false);
69
+ const [edited, setEdited] = useState(false);
70
+ const lengths = lengthsOf(current);
71
+ const lengthCopy = lengths.length === 1 ? `${lengths[0]}` : `${lengths.slice(0, -1).join(", ")} or ${lengths[lengths.length - 1]}`;
72
+ const builtIn = validate && touched && (status === "incomplete" || status === "too-long") ? `Numbers in ${current.name} have ${lengthCopy} digits` : void 0;
73
+ const message = error ?? builtIn;
74
+ const guide = useMemo(() => {
75
+ const [trunk, rest] = splitTrunk(current, digits);
76
+ if (rest.length >= current.example.length) return "";
77
+ const full = formatNational(current, trunk + rest + current.example.slice(rest.length));
78
+ return full.startsWith(formatted) ? full.slice(formatted.length) : "";
79
+ }, [
80
+ current,
81
+ digits,
82
+ formatted
83
+ ]);
84
+ const inputRef = useRef(null);
85
+ useImperativeHandle(forwardedRef, () => inputRef.current);
86
+ const shellRef = useRef(null);
87
+ const pendingCaret = useRef(null);
88
+ const [, rerender] = useReducer((count) => count + 1, 0);
89
+ const [announcement, setAnnouncement] = useState("");
90
+ const emit = useCallback((entry, nextDigits) => {
91
+ const nextStatus = phoneStatus(entry, nextDigits);
92
+ onValueChange?.(toE164(entry, nextDigits), {
93
+ country: entry,
94
+ formatted: formatNational(entry, nextDigits),
95
+ status: nextStatus,
96
+ valid: nextStatus === "valid"
97
+ });
98
+ }, [onValueChange]);
99
+ function setNumber(nextDigits, caretDigits, entry = current) {
100
+ const capped = capDigits(entry, nextDigits);
101
+ pendingCaret.current = caretDigits === null ? null : Math.min(caretDigits, capped.length);
102
+ if (entry.iso !== current.iso) onCountryChange?.(entry.iso);
103
+ if (capped === digits && entry.iso === current.iso) {
104
+ rerender();
105
+ return;
106
+ }
107
+ setEdited(true);
108
+ setState({
109
+ iso: entry.iso,
110
+ digits: capped
111
+ });
112
+ emit(entry, capped);
113
+ }
114
+ useIsomorphicLayoutEffect(() => {
115
+ const input = inputRef.current;
116
+ const count = pendingCaret.current;
117
+ pendingCaret.current = null;
118
+ if (!input || count === null || document.activeElement !== input) return;
119
+ const position = caretAfterDigits(input.value, count);
120
+ input.setSelectionRange(position, position);
121
+ });
122
+ function onNumberChange(input) {
123
+ const raw = input.value;
124
+ if (raw.includes("+") || /^\s*00/.test(raw) && onlyDigits(raw).length > 6) {
125
+ const parsed = parsePhoneNumber(raw.slice(Math.max(0, raw.indexOf("+"))), pool);
126
+ if (parsed?.national) {
127
+ if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);
128
+ setNumber(parsed.national, parsed.national.length, parsed.country);
129
+ return;
130
+ }
131
+ }
132
+ const caret = input.selectionStart ?? raw.length;
133
+ setNumber(onlyDigits(raw), onlyDigits(raw.slice(0, caret)).length);
134
+ }
135
+ function onNumberKeyDown(event) {
136
+ const input = event.currentTarget;
137
+ if (event.key === "+" && !event.metaKey && !event.ctrlKey) {
138
+ event.preventDefault();
139
+ setQuery("+");
140
+ setOpen(true);
141
+ return;
142
+ }
143
+ const start = input.selectionStart ?? 0;
144
+ if (start !== (input.selectionEnd ?? 0) || event.metaKey || event.ctrlKey || event.altKey) return;
145
+ if (event.key === "Backspace" && start > 0 && !/\d/.test(input.value[start - 1])) {
146
+ event.preventDefault();
147
+ const index = onlyDigits(input.value.slice(0, start)).length;
148
+ if (index > 0) setNumber(digits.slice(0, index - 1) + digits.slice(index), index - 1);
149
+ }
150
+ if (event.key === "Delete" && start < input.value.length && !/\d/.test(input.value[start])) {
151
+ event.preventDefault();
152
+ const index = onlyDigits(input.value.slice(0, start)).length;
153
+ setNumber(digits.slice(0, index) + digits.slice(index + 1), index);
154
+ }
155
+ }
156
+ function onPaste(event) {
157
+ const text = event.clipboardData.getData("text");
158
+ if (!text) return;
159
+ event.preventDefault();
160
+ const input = event.currentTarget;
161
+ const parsed = /^\s*(\+|00)/.test(text) ? parsePhoneNumber(text, pool) : null;
162
+ if (parsed) {
163
+ if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);
164
+ setNumber(parsed.national, parsed.national.length, parsed.country);
165
+ return;
166
+ }
167
+ let pasted = onlyDigits(text);
168
+ if (pasted.startsWith(current.dial) && pasted.length > capDigits(current, pasted).length) pasted = pasted.slice(current.dial.length);
169
+ if (pasted.length >= Math.min(...lengthsOf(current))) {
170
+ setNumber(pasted, pasted.length);
171
+ return;
172
+ }
173
+ const from = onlyDigits(input.value.slice(0, input.selectionStart ?? 0)).length;
174
+ const to = onlyDigits(input.value.slice(0, input.selectionEnd ?? 0)).length;
175
+ setNumber(digits.slice(0, from) + pasted + digits.slice(to), from + pasted.length);
176
+ }
177
+ const [open, setOpen] = useState(false);
178
+ const [query, setQuery] = useState("");
179
+ const openRef = useRef(false);
180
+ openRef.current = open;
181
+ function pick(key) {
182
+ const entry = list.find((item) => item.iso === key);
183
+ if (!entry) return;
184
+ if (entry.iso !== current.iso) {
185
+ const nextDigits = capDigits(entry, digits);
186
+ if (countryProp === void 0) setState({
187
+ iso: entry.iso,
188
+ digits: nextDigits
189
+ });
190
+ else setState((last) => ({
191
+ ...last,
192
+ digits: nextDigits
193
+ }));
194
+ onCountryChange?.(entry.iso);
195
+ emit(entry, nextDigits);
196
+ setEdited(true);
197
+ setAnnouncement(`${entry.name}, +${entry.dial}`);
198
+ }
199
+ setOpen(false);
200
+ requestAnimationFrame(() => {
201
+ if (!openRef.current) inputRef.current?.focus({ preventScroll: true });
202
+ });
203
+ }
204
+ const filter = useCallback((textValue, inputValue) => {
205
+ const entry = SORTED.find((item) => textValueOf(item) === textValue);
206
+ return entry ? matchesCountry(entry, inputValue) : true;
207
+ }, []);
208
+ const field = useFieldWiring({
209
+ enabled: true,
210
+ id,
211
+ describedBy: void 0,
212
+ invalid: void 0,
213
+ description,
214
+ error: message
215
+ });
216
+ const showCheck = status === "valid";
217
+ const control = /* @__PURE__ */ jsxs(Group, {
218
+ ref: shellRef,
219
+ role: "presentation",
220
+ isDisabled: disabled,
221
+ isInvalid: Boolean(message),
222
+ "data-slot": "phone-input-shell",
223
+ className: shellClass,
224
+ children: [/* @__PURE__ */ jsxs(DialogTrigger, {
225
+ isOpen: open,
226
+ onOpenChange: (next) => {
227
+ setOpen(next);
228
+ if (next) setQuery("");
229
+ },
230
+ children: [/* @__PURE__ */ jsxs(Button, {
231
+ isDisabled: disabled,
232
+ "aria-label": `Country, ${current.name} +${current.dial}`,
233
+ "data-slot": "phone-input-country",
234
+ className: triggerClass,
235
+ children: [
236
+ /* @__PURE__ */ jsx("span", {
237
+ "aria-hidden": "true",
238
+ "data-slot": "phone-input-flag",
239
+ "data-glyph": flagEmoji ? "flag" : "code",
240
+ className: flagEmoji ? flagClass : codeClass,
241
+ children: flagEmoji ? flagOf(current.iso) : current.iso
242
+ }),
243
+ /* @__PURE__ */ jsx(SwapText, {
244
+ swapKey: current.iso,
245
+ motion: "roll",
246
+ className: "inline-flex",
247
+ children: /* @__PURE__ */ jsxs("span", {
248
+ "data-slot": "phone-input-dial",
249
+ children: ["+", current.dial]
250
+ })
251
+ }),
252
+ /* @__PURE__ */ jsx("svg", {
253
+ "aria-hidden": "true",
254
+ viewBox: "0 0 16 16",
255
+ fill: "none",
256
+ stroke: "currentColor",
257
+ strokeWidth: "1.75",
258
+ strokeLinecap: "round",
259
+ strokeLinejoin: "round",
260
+ className: "size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180",
261
+ children: /* @__PURE__ */ jsx("path", { d: "m4 6 4 4 4-4" })
262
+ })
263
+ ]
264
+ }), /* @__PURE__ */ jsx(Popover, {
265
+ triggerRef: shellRef,
266
+ placement: "bottom start",
267
+ offset: 8,
268
+ "data-slot": "phone-input-popover",
269
+ className: cnAll(selectPopoverVariants({ reveal: "fade" }), "flex w-80 max-w-[calc(100vw-2rem)] flex-col overflow-hidden"),
270
+ children: /* @__PURE__ */ jsx(Dialog, {
271
+ "aria-label": "Country",
272
+ className: "flex min-h-0 flex-col gap-1 outline-none",
273
+ children: /* @__PURE__ */ jsxs(Autocomplete, {
274
+ filter,
275
+ inputValue: query,
276
+ onInputChange: setQuery,
277
+ children: [/* @__PURE__ */ jsxs(SearchField, {
278
+ "aria-label": "Search countries or calling codes",
279
+ autoFocus: true,
280
+ className: searchClass,
281
+ children: [
282
+ /* @__PURE__ */ jsxs("svg", {
283
+ "aria-hidden": "true",
284
+ viewBox: "0 0 24 24",
285
+ fill: "none",
286
+ stroke: "currentColor",
287
+ strokeWidth: 2,
288
+ strokeLinecap: "round",
289
+ strokeLinejoin: "round",
290
+ className: "size-4 flex-none text-muted-foreground",
291
+ children: [/* @__PURE__ */ jsx("circle", {
292
+ cx: "11",
293
+ cy: "11",
294
+ r: "8"
295
+ }), /* @__PURE__ */ jsx("path", { d: "m21 21-4.34-4.34" })]
296
+ }),
297
+ /* @__PURE__ */ jsx(Input, {
298
+ placeholder: "Country or code",
299
+ "data-slot": "phone-input-search",
300
+ className: "min-w-0 flex-1 appearance-none border-0 bg-transparent p-0 font-sans text-sm text-foreground outline-none placeholder:text-muted-foreground [&::-webkit-search-cancel-button]:hidden"
301
+ }),
302
+ /* @__PURE__ */ jsx("svg", {
303
+ "aria-hidden": "true",
304
+ "data-slot": "phone-input-search-chevron",
305
+ viewBox: "0 0 16 16",
306
+ fill: "none",
307
+ stroke: "currentColor",
308
+ strokeWidth: "1.75",
309
+ strokeLinecap: "round",
310
+ strokeLinejoin: "round",
311
+ className: "size-4 flex-none text-muted-foreground",
312
+ children: /* @__PURE__ */ jsx("path", { d: "m4 10 4-4 4 4" })
313
+ })
314
+ ]
315
+ }), /* @__PURE__ */ jsx(ScrollArea, {
316
+ label: "Country list",
317
+ className: "max-h-72 rounded-control",
318
+ viewportClassName: "rounded-control focus-visible:outline-(--color-control-edge)",
319
+ children: /* @__PURE__ */ jsx(ListBox, {
320
+ "aria-label": "Countries",
321
+ selectionMode: "single",
322
+ selectedKeys: [current.iso],
323
+ escapeKeyBehavior: "none",
324
+ onSelectionChange: (keys) => {
325
+ if (keys === "all") return;
326
+ pick([...keys][0] ?? current.iso);
327
+ },
328
+ items,
329
+ renderEmptyState: () => /* @__PURE__ */ jsx("p", {
330
+ className: emptyClass,
331
+ children: "No countries match"
332
+ }),
333
+ className: "outline-none",
334
+ children: (entry) => /* @__PURE__ */ jsxs(SelectItem, {
335
+ id: entry.id,
336
+ textValue: textValueOf(entry),
337
+ className: rowClass,
338
+ children: [
339
+ /* @__PURE__ */ jsx("span", {
340
+ "aria-hidden": "true",
341
+ "data-slot": "phone-input-row-flag",
342
+ "data-glyph": flagEmoji ? "flag" : "code",
343
+ className: flagEmoji ? flagClass : codeClass,
344
+ children: flagEmoji ? flagOf(entry.iso) : entry.iso
345
+ }),
346
+ /* @__PURE__ */ jsx("span", {
347
+ className: nameClass,
348
+ children: entry.name
349
+ }),
350
+ /* @__PURE__ */ jsxs("span", {
351
+ className: "flex-none text-xs tabular-nums text-muted-foreground",
352
+ children: ["+", entry.dial]
353
+ })
354
+ ]
355
+ })
356
+ })
357
+ })]
358
+ })
359
+ })
360
+ })]
361
+ }), /* @__PURE__ */ jsxs("div", {
362
+ className: "relative flex h-full min-w-0 flex-1 items-center border-s-(length:--border-width) border-border",
363
+ children: [
364
+ /* @__PURE__ */ jsxs("span", {
365
+ "aria-hidden": "true",
366
+ "data-slot": "phone-input-guide",
367
+ className: "pointer-events-none absolute inset-0 flex items-center overflow-hidden whitespace-pre px-3 font-sans text-base tabular-nums text-muted-foreground",
368
+ children: [/* @__PURE__ */ jsx("span", {
369
+ className: "invisible",
370
+ children: formatted
371
+ }), guide]
372
+ }),
373
+ /* @__PURE__ */ jsx(Input, {
374
+ ref: inputRef,
375
+ ...field.controlProps,
376
+ "data-slot": "phone-input-number",
377
+ type: "tel",
378
+ inputMode: "tel",
379
+ autoComplete: "tel",
380
+ value: formatted,
381
+ disabled,
382
+ required,
383
+ "aria-label": labelHidden ? label : void 0,
384
+ onChange: (event) => onNumberChange(event.currentTarget),
385
+ onKeyDown: onNumberKeyDown,
386
+ onPaste,
387
+ onBlur: (event) => {
388
+ setTouched(digits.length > 0);
389
+ onBlur?.(event);
390
+ },
391
+ className: numberClass
392
+ }),
393
+ showCheck ? /* @__PURE__ */ jsx("svg", {
394
+ "aria-hidden": "true",
395
+ "data-slot": "phone-input-valid",
396
+ viewBox: "0 0 16 16",
397
+ fill: "none",
398
+ stroke: "currentColor",
399
+ strokeWidth: "2",
400
+ strokeLinecap: "round",
401
+ strokeLinejoin: "round",
402
+ className: cn("pointer-events-none absolute end-3 size-4 text-success-strong", edited && "motion-safe:animate-select-check-in"),
403
+ children: /* @__PURE__ */ jsx("path", { d: "m3.5 8.5 3 3 6-6" })
404
+ }) : null
405
+ ]
406
+ })]
407
+ });
408
+ return /* @__PURE__ */ jsxs("div", {
409
+ className: cn("w-full min-w-0 font-sans", className),
410
+ "data-slot": "phone-input",
411
+ "data-country": current.iso,
412
+ children: [
413
+ /* @__PURE__ */ jsx(FieldShell, {
414
+ label: labelHidden ? null : label,
415
+ controlId: field.controlId,
416
+ description,
417
+ hintId: field.hintId,
418
+ error: message,
419
+ errorId: field.errorId,
420
+ children: control
421
+ }),
422
+ name ? /* @__PURE__ */ jsx("input", {
423
+ type: "hidden",
424
+ name,
425
+ value: e164
426
+ }) : null,
427
+ /* @__PURE__ */ jsx("span", {
428
+ className: "sr-only",
429
+ role: "status",
430
+ "aria-live": "polite",
431
+ children: announcement || (showCheck ? `Valid ${current.name} number` : "")
432
+ })
433
+ ]
434
+ });
435
+ });
436
+ PhoneInput.displayName = "PhoneInput";
437
+ //#endregion
438
+ export { PhoneInput };
439
+
440
+ //# sourceMappingURL=phone-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phone-input.js","names":["AriaGroup","AriaDialogTrigger","AriaButton","AriaPopover","AriaDialog","AriaAutocomplete","AriaSearchField","AriaInput","AriaListBox"],"sources":["../../src/phone-input/phone-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ClipboardEvent,\n type FocusEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n Autocomplete as AriaAutocomplete,\n Button as AriaButton,\n Dialog as AriaDialog,\n DialogTrigger as AriaDialogTrigger,\n Group as AriaGroup,\n Input as AriaInput,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n SearchField as AriaSearchField,\n type Key,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\nimport { useFlagEmoji } from \"../lib/flag-emoji.js\";\nimport { useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { ScrollArea } from \"../scroll-area/scroll-area.js\";\nimport { SelectItem, selectPopoverVariants } from \"../select/select.js\";\nimport {\n PHONE_COUNTRIES,\n capDigits,\n caretAfterDigits,\n flagOf,\n formatNational,\n lengthsOf,\n matchesCountry,\n onlyDigits,\n parsePhoneNumber,\n phoneCountryByIso,\n phoneStatus,\n splitTrunk,\n toE164,\n type PhoneCountry,\n type PhoneStatus,\n} from \"./phone-countries.js\";\n\n/** What `onValueChange` hands over beside the E.164 number. */\nexport interface PhoneInputDetails {\n country: PhoneCountry;\n /** The number as shown in the field, without the calling code. */\n formatted: string;\n status: PhoneStatus;\n valid: boolean;\n}\n\n/**\n * PhoneInput: a phone number with a country picker. The number formats as it is\n * typed (grouping follows the country, the caret stays on the digit it was on, a\n * faint guide shows the rest of the expected shape), a pasted or autofilled\n * international number such as \"+44 7400 123456\" picks its own country, and the\n * value comes out in E.164. The picker is a searchable list (React Aria\n * Autocomplete) that matches names, ISO codes and calling codes; typing \"+\" in\n * the number opens it with a calling code search.\n *\n * Formatting and length checks come from a compact country table in this\n * package, not a phone-number library: it knows the usual digit count and\n * grouping of about fifty countries, not which prefixes a carrier has assigned.\n * Use a full library on the server when a number must be a real, assigned line.\n */\nexport interface PhoneInputProps {\n /** What the number is for, and the field's accessible name. */\n label: string;\n /** Keep the label for assistive technology only. */\n labelHidden?: boolean;\n /** The number in E.164, such as \"+14155550132\". An empty string clears the field. */\n value?: string;\n defaultValue?: string;\n /** Fires on every edit with the E.164 number (empty when there are no digits) and its parsed details. */\n onValueChange?: (value: string, details: PhoneInputDetails) => void;\n /** ISO code of the selected country. Controlled when given. */\n country?: string;\n /** ISO code used until someone picks a country or enters an international number. @default \"US\" */\n defaultCountry?: string;\n onCountryChange?: (iso: string) => void;\n /** Limit the picker to these ISO codes. */\n countries?: string[];\n /** Helper text under the field. A string, because the copy is animated word by word. */\n description?: string;\n /** Replaces the built-in length message. */\n error?: string;\n /** Show a message after blur when the number is incomplete. @default true */\n validate?: boolean;\n /**\n * How a country is marked in the field and the list. `auto` draws the flag emoji where the platform can (Windows\n * cannot) and the two-letter ISO code where it cannot; `emoji` and `code` force one.\n * @default \"auto\"\n */\n flags?: \"auto\" | \"emoji\" | \"code\";\n disabled?: boolean;\n required?: boolean;\n /** Adds a hidden input carrying the E.164 value for native form submission. */\n name?: string;\n id?: string;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n onBlur?: (event: FocusEvent<HTMLInputElement>) => void;\n}\n\n/*\n * Focus turns the shell's own border dark (the foreground colour), as every text field does: no separate ring.\n * The outline comes back only under forced colours, where a border colour is overridden.\n */\nconst focusRing =\n \"data-[focus-visible]:not-data-[invalid]:border-foreground forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-(length:--focus-ring-width) forced-colors:data-[focus-visible]:outline-offset-(--focus-ring-offset)\";\n\nconst shellClass = cnAll(\n fieldSurfaceVariants(),\n \"flex h-(--density-control-h-md) items-center\",\n // Focus turns the box's own border dark whenever the number holds focus; no separate ring.\n \"has-[input:focus]:not-data-[invalid]:border-foreground\",\n \"forced-colors:has-[input:focus]:outline-solid forced-colors:has-[input:focus]:outline-(length:--focus-ring-width) forced-colors:has-[input:focus]:outline-offset-(--focus-ring-offset)\",\n focusRing,\n);\n\nconst triggerClass = cnAll(\n \"group/trigger m-1 inline-flex h-[calc(100%-0.5rem)] flex-none cursor-pointer items-center gap-1.5 rounded-control border-0 bg-transparent ps-2.5 pe-2 font-sans text-base font-medium tabular-nums text-foreground outline-none\",\n \"motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n \"data-[hovered]:bg-muted data-[pressed]:bg-muted data-[disabled]:cursor-not-allowed\",\n focusRing,\n);\n\n/** A native `<input>` styled flat: the shell draws the surface. */\nconst numberClass =\n \"relative z-10 m-0 h-full w-full min-w-0 appearance-none border-0 bg-transparent px-3 pe-10 font-sans text-base tabular-nums text-foreground outline-none selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed\";\n\n/*\n * The search is a flat top row of the panel, not a boxed pill: icon, text, a decorative chevron at the end and a hairline\n * divider below. Focus is a 2px neutral underline (control-edge, 3:1 on the surface) drawn over the divider, so it can\n * never read as a second ring around a border.\n */\nconst searchClass =\n \"relative flex h-(--density-control-h-sm) items-center gap-2 border-b-(length:--border-width) border-border px-2.5 after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-(--focus-ring-width) after:bg-transparent has-[input:focus]:after:bg-(--color-control-edge)\";\n\n/*\n * Rows read quietly: secondary ink for the name, a soft pill under the row the pointer or the arrows reached, and\n * full ink once a row is the chosen country or the highlighted one. The list sits in an Autocomplete, which marks the\n * keyboard row data-focused but the pointer row only data-hovered, so both states are styled. The tick sits at the end (SelectItem draws it).\n */\nconst rowClass =\n \"group/row rounded-control data-[hovered]:bg-muted data-[focus-visible]:outline-(--color-control-edge)\";\nconst nameClass =\n \"min-w-0 flex-1 truncate font-medium text-text-secondary group-data-[selected]/row:text-foreground group-data-[focused]/row:text-foreground group-data-[hovered]/row:text-foreground\";\n\n/* A flag glyph, or (where the platform draws none) the ISO code in the same slot. */\nconst flagClass = \"w-6 flex-none text-center text-base leading-none\";\nconst codeClass = \"w-6 flex-none text-center text-xs font-semibold leading-none tracking-wide text-muted-foreground\";\n\nconst emptyClass = \"px-3 py-3 text-sm text-muted-foreground\";\n\nconst SORTED = [...PHONE_COUNTRIES].sort((a, b) => a.name.localeCompare(b.name));\nconst textValueOf = (entry: PhoneCountry) => `${entry.name} ${entry.iso} +${entry.dial}`;\n\nexport const PhoneInput = forwardRef<HTMLInputElement, PhoneInputProps>(function PhoneInput(\n {\n label,\n labelHidden = false,\n value,\n defaultValue,\n onValueChange,\n country: countryProp,\n defaultCountry = \"US\",\n onCountryChange,\n countries,\n description,\n error,\n validate = true,\n flags = \"auto\",\n disabled = false,\n required,\n name,\n id,\n className,\n onBlur,\n },\n forwardedRef,\n) {\n const pool = useMemo(\n () =>\n countries?.length ? PHONE_COUNTRIES.filter((entry) => countries.includes(entry.iso)) : PHONE_COUNTRIES,\n [countries],\n );\n const list = useMemo(() => SORTED.filter((entry) => pool.includes(entry)), [pool]);\n const items = useMemo(() => list.map((entry) => ({ ...entry, id: entry.iso })), [list]);\n const canDrawFlags = useFlagEmoji();\n const flagEmoji = flags === \"auto\" ? canDrawFlags : flags === \"emoji\";\n const fallback = phoneCountryByIso(defaultCountry) ?? PHONE_COUNTRIES[0]!;\n\n const [state, setState] = useState(() => {\n const parsed = parsePhoneNumber(value ?? defaultValue ?? \"\", pool);\n return parsed\n ? { iso: parsed.country.iso, digits: parsed.national }\n : { iso: fallback.iso, digits: \"\" };\n });\n const iso = countryProp ?? state.iso;\n const current = phoneCountryByIso(iso) ?? fallback;\n const digits = state.digits;\n const e164 = toE164(current, digits);\n\n // A controlled value that differs from what the field last produced is read back in.\n const [seenValue, setSeenValue] = useState(value);\n if (value !== seenValue) {\n setSeenValue(value);\n if (value !== undefined && value !== e164) {\n const parsed = parsePhoneNumber(value, pool);\n setState(parsed ? { iso: parsed.country.iso, digits: parsed.national } : { iso, digits: \"\" });\n }\n }\n\n const formatted = formatNational(current, digits);\n const status = phoneStatus(current, digits);\n const [touched, setTouched] = useState(false);\n // The check animates in only for a number that became valid after mount.\n const [edited, setEdited] = useState(false);\n const lengths = lengthsOf(current);\n const lengthCopy =\n lengths.length === 1\n ? `${lengths[0]}`\n : `${lengths.slice(0, -1).join(\", \")} or ${lengths[lengths.length - 1]}`;\n const builtIn =\n validate && touched && (status === \"incomplete\" || status === \"too-long\")\n ? `Numbers in ${current.name} have ${lengthCopy} digits`\n : undefined;\n const message = error ?? builtIn;\n\n /* The guide: the rest of the example number in the shape this country expects, drawn faintly after what was typed. */\n const guide = useMemo(() => {\n const [trunk, rest] = splitTrunk(current, digits);\n if (rest.length >= current.example.length) return \"\";\n const full = formatNational(current, trunk + rest + current.example.slice(rest.length));\n return full.startsWith(formatted) ? full.slice(formatted.length) : \"\";\n }, [current, digits, formatted]);\n\n const inputRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(forwardedRef, () => inputRef.current as HTMLInputElement);\n const shellRef = useRef<HTMLDivElement>(null);\n const pendingCaret = useRef<number | null>(null);\n const [, rerender] = useReducer((count: number) => count + 1, 0);\n const [announcement, setAnnouncement] = useState(\"\");\n\n const emit = useCallback(\n (entry: PhoneCountry, nextDigits: string) => {\n const nextStatus = phoneStatus(entry, nextDigits);\n onValueChange?.(toE164(entry, nextDigits), {\n country: entry,\n formatted: formatNational(entry, nextDigits),\n status: nextStatus,\n valid: nextStatus === \"valid\",\n });\n },\n [onValueChange],\n );\n\n function setNumber(nextDigits: string, caretDigits: number | null, entry = current) {\n const capped = capDigits(entry, nextDigits);\n pendingCaret.current = caretDigits === null ? null : Math.min(caretDigits, capped.length);\n if (entry.iso !== current.iso) onCountryChange?.(entry.iso);\n // Nothing changed (a refused digit): React restores the old text after this handler, so the caret is placed on the next render.\n if (capped === digits && entry.iso === current.iso) {\n rerender();\n return;\n }\n setEdited(true);\n setState({ iso: entry.iso, digits: capped });\n emit(entry, capped);\n }\n\n useIsomorphicLayoutEffect(() => {\n const input = inputRef.current;\n const count = pendingCaret.current;\n pendingCaret.current = null;\n if (!input || count === null || document.activeElement !== input) return;\n const position = caretAfterDigits(input.value, count);\n input.setSelectionRange(position, position);\n });\n\n /* ---------------------------------------------- Number entry ---------------------------------------------- */\n\n function onNumberChange(input: HTMLInputElement) {\n const raw = input.value;\n // Autofill and dropped text arrive as a change: an international number chooses its own country.\n if (raw.includes(\"+\") || (/^\\s*00/.test(raw) && onlyDigits(raw).length > 6)) {\n const parsed = parsePhoneNumber(raw.slice(Math.max(0, raw.indexOf(\"+\"))), pool);\n if (parsed?.national) {\n if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);\n setNumber(parsed.national, parsed.national.length, parsed.country);\n return;\n }\n }\n const caret = input.selectionStart ?? raw.length;\n setNumber(onlyDigits(raw), onlyDigits(raw.slice(0, caret)).length);\n }\n\n function onNumberKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n if (event.key === \"+\" && !event.metaKey && !event.ctrlKey) {\n event.preventDefault();\n setQuery(\"+\");\n setOpen(true);\n return;\n }\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n if (start !== end || event.metaKey || event.ctrlKey || event.altKey) return;\n // Deleting a separator deletes the digit beside it instead of doing nothing.\n if (event.key === \"Backspace\" && start > 0 && !/\\d/.test(input.value[start - 1]!)) {\n event.preventDefault();\n const index = onlyDigits(input.value.slice(0, start)).length;\n if (index > 0) setNumber(digits.slice(0, index - 1) + digits.slice(index), index - 1);\n }\n if (event.key === \"Delete\" && start < input.value.length && !/\\d/.test(input.value[start]!)) {\n event.preventDefault();\n const index = onlyDigits(input.value.slice(0, start)).length;\n setNumber(digits.slice(0, index) + digits.slice(index + 1), index);\n }\n }\n\n function onPaste(event: ClipboardEvent<HTMLInputElement>) {\n const text = event.clipboardData.getData(\"text\");\n if (!text) return;\n event.preventDefault();\n const input = event.currentTarget;\n const parsed = /^\\s*(\\+|00)/.test(text) ? parsePhoneNumber(text, pool) : null;\n if (parsed) {\n if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);\n setNumber(parsed.national, parsed.national.length, parsed.country);\n return;\n }\n let pasted = onlyDigits(text);\n // \"1 415 555 0132\" pasted into a US field: the leading calling code goes when the number would not fit otherwise.\n if (pasted.startsWith(current.dial) && pasted.length > capDigits(current, pasted).length) {\n pasted = pasted.slice(current.dial.length);\n }\n // A whole number replaces what was there; a fragment goes in at the caret.\n if (pasted.length >= Math.min(...lengthsOf(current))) {\n setNumber(pasted, pasted.length);\n return;\n }\n const from = onlyDigits(input.value.slice(0, input.selectionStart ?? 0)).length;\n const to = onlyDigits(input.value.slice(0, input.selectionEnd ?? 0)).length;\n setNumber(digits.slice(0, from) + pasted + digits.slice(to), from + pasted.length);\n }\n\n /* ---------------------------------------------- Country picker ---------------------------------------------- */\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const openRef = useRef(false);\n openRef.current = open;\n\n function pick(key: Key | undefined) {\n const entry = list.find((item) => item.iso === key);\n if (!entry) return;\n if (entry.iso !== current.iso) {\n const nextDigits = capDigits(entry, digits);\n if (countryProp === undefined) setState({ iso: entry.iso, digits: nextDigits });\n else setState((last) => ({ ...last, digits: nextDigits }));\n onCountryChange?.(entry.iso);\n emit(entry, nextDigits);\n setEdited(true);\n setAnnouncement(`${entry.name}, +${entry.dial}`);\n }\n setOpen(false);\n // Back to the number, where the next keystroke belongs.\n requestAnimationFrame(() => {\n if (!openRef.current) inputRef.current?.focus({ preventScroll: true });\n });\n }\n\n const filter = useCallback((textValue: string, inputValue: string) => {\n const entry = SORTED.find((item) => textValueOf(item) === textValue);\n return entry ? matchesCountry(entry, inputValue) : true;\n }, []);\n\n /* ---------------------------------------------- Field wiring ---------------------------------------------- */\n\n const field = useFieldWiring({\n enabled: true,\n id,\n describedBy: undefined,\n invalid: undefined,\n description,\n error: message,\n });\n const showCheck = status === \"valid\";\n\n const control = (\n <AriaGroup\n ref={shellRef}\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={Boolean(message)}\n data-slot=\"phone-input-shell\"\n className={shellClass}\n >\n <AriaDialogTrigger\n isOpen={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setQuery(\"\");\n }}\n >\n <AriaButton\n isDisabled={disabled}\n aria-label={`Country, ${current.name} +${current.dial}`}\n data-slot=\"phone-input-country\"\n className={triggerClass}\n >\n <span\n aria-hidden=\"true\"\n data-slot=\"phone-input-flag\"\n data-glyph={flagEmoji ? \"flag\" : \"code\"}\n className={flagEmoji ? flagClass : codeClass}\n >\n {flagEmoji ? flagOf(current.iso) : current.iso}\n </span>\n <SwapText swapKey={current.iso} motion=\"roll\" className=\"inline-flex\">\n <span data-slot=\"phone-input-dial\">+{current.dial}</span>\n </SwapText>\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n <AriaPopover\n triggerRef={shellRef}\n placement=\"bottom start\"\n offset={8}\n data-slot=\"phone-input-popover\"\n className={cnAll(selectPopoverVariants({ reveal: \"fade\" }), \"flex w-80 max-w-[calc(100vw-2rem)] flex-col overflow-hidden\")}\n >\n <AriaDialog aria-label=\"Country\" className=\"flex min-h-0 flex-col gap-1 outline-none\">\n <AriaAutocomplete filter={filter} inputValue={query} onInputChange={setQuery}>\n <AriaSearchField\n aria-label=\"Search countries or calling codes\"\n autoFocus\n className={searchClass}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.34-4.34\" />\n </svg>\n <AriaInput\n placeholder=\"Country or code\"\n data-slot=\"phone-input-search\"\n className=\"min-w-0 flex-1 appearance-none border-0 bg-transparent p-0 font-sans text-sm text-foreground outline-none placeholder:text-muted-foreground [&::-webkit-search-cancel-button]:hidden\"\n />\n <svg\n aria-hidden=\"true\"\n data-slot=\"phone-input-search-chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <path d=\"m4 10 4-4 4 4\" />\n </svg>\n </AriaSearchField>\n {/* The list scrolls, and a scrolling region must be reachable by keyboard (the search field drives the options with arrow keys; this stop is for scrolling). */}\n <ScrollArea\n label=\"Country list\"\n className=\"max-h-72 rounded-control\"\n viewportClassName=\"rounded-control focus-visible:outline-(--color-control-edge)\"\n >\n <AriaListBox\n aria-label=\"Countries\"\n selectionMode=\"single\"\n selectedKeys={[current.iso]}\n // Escape closes the list instead of clearing the chosen country. Choosing the chosen country again\n // empties the selection, which is read as \"keep it\": the list just closes.\n escapeKeyBehavior=\"none\"\n onSelectionChange={(keys) => {\n if (keys === \"all\") return;\n pick([...keys][0] ?? current.iso);\n }}\n items={items}\n renderEmptyState={() => <p className={emptyClass}>No countries match</p>}\n className=\"outline-none\"\n >\n {(entry) => (\n <SelectItem id={entry.id} textValue={textValueOf(entry)} className={rowClass}>\n <span\n aria-hidden=\"true\"\n data-slot=\"phone-input-row-flag\"\n data-glyph={flagEmoji ? \"flag\" : \"code\"}\n className={flagEmoji ? flagClass : codeClass}\n >\n {flagEmoji ? flagOf(entry.iso) : entry.iso}\n </span>\n <span className={nameClass}>{entry.name}</span>\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n +{entry.dial}\n </span>\n </SelectItem>\n )}\n </AriaListBox>\n </ScrollArea>\n </AriaAutocomplete>\n </AriaDialog>\n </AriaPopover>\n </AriaDialogTrigger>\n\n <div className=\"relative flex h-full min-w-0 flex-1 items-center border-s-(length:--border-width) border-border\">\n <span\n aria-hidden=\"true\"\n data-slot=\"phone-input-guide\"\n className=\"pointer-events-none absolute inset-0 flex items-center overflow-hidden whitespace-pre px-3 font-sans text-base tabular-nums text-muted-foreground\"\n >\n <span className=\"invisible\">{formatted}</span>\n {guide}\n </span>\n <AriaInput\n ref={inputRef}\n {...field.controlProps}\n data-slot=\"phone-input-number\"\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel\"\n value={formatted}\n disabled={disabled}\n required={required}\n aria-label={labelHidden ? label : undefined}\n onChange={(event) => onNumberChange(event.currentTarget)}\n onKeyDown={onNumberKeyDown}\n onPaste={onPaste}\n onBlur={(event) => {\n setTouched(digits.length > 0);\n onBlur?.(event);\n }}\n className={numberClass}\n />\n {showCheck ? (\n <svg\n aria-hidden=\"true\"\n data-slot=\"phone-input-valid\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(\n \"pointer-events-none absolute end-3 size-4 text-success-strong\",\n edited && \"motion-safe:animate-select-check-in\",\n )}\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n ) : null}\n </div>\n </AriaGroup>\n );\n\n return (\n <div className={cn(\"w-full min-w-0 font-sans\", className)} data-slot=\"phone-input\" data-country={current.iso}>\n <FieldShell\n label={labelHidden ? null : label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={message}\n errorId={field.errorId}\n >\n {control}\n </FieldShell>\n {name ? <input type=\"hidden\" name={name} value={e164} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement || (showCheck ? `Valid ${current.name} number` : \"\")}\n </span>\n </div>\n );\n});\n\nPhoneInput.displayName = \"PhoneInput\";\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { Popover, PopoverProps, PopoverTrigger, popoverVariants } from "./popover.js";
2
- export { Popover, type PopoverProps, PopoverTrigger, popoverVariants };
1
+ import { Popover, PopoverHoverTrigger, PopoverHoverTriggerProps, PopoverProps, PopoverTrigger, popoverVariants } from "./popover.js";
2
+ export { Popover, PopoverHoverTrigger, type PopoverHoverTriggerProps, type PopoverProps, PopoverTrigger, popoverVariants };
@@ -1,2 +1,2 @@
1
- import { Popover, PopoverTrigger, popoverVariants } from "./popover.js";
2
- export { Popover, PopoverTrigger, popoverVariants };
1
+ import { Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants } from "./popover.js";
2
+ export { Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants };
@@ -1,5 +1,5 @@
1
- import { ReactNode } from "react";
2
- import { DialogTrigger as PopoverTrigger, PopoverProps } from "react-aria-components";
1
+ import { JSX, ReactNode } from "react";
2
+ import { DialogTrigger as PopoverTrigger, PopoverProps, PreviewTriggerProps } from "react-aria-components";
3
3
  //#region src/popover/popover.d.ts
4
4
  /**
5
5
  * Popover, ported from the legacy shadcn/Radix component.
@@ -51,8 +51,57 @@ interface PopoverProps$1 extends Omit<PopoverProps, "className" | "style" | "chi
51
51
  * @default "rise"
52
52
  */
53
53
  entrance?: "rise" | "none";
54
+ /**
55
+ * A heading at the top of the panel. Together with `description` and
56
+ * `actionLabel` it makes the message popover: a dark title, grey body text
57
+ * and a full-width button, with `children` rendered below them.
58
+ */
59
+ title?: ReactNode;
60
+ /** Grey body text under the title. */
61
+ description?: ReactNode;
62
+ /** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */
63
+ actionLabel?: ReactNode;
64
+ /** Runs when the action button is pressed, before the popover closes. */
65
+ onAction?: () => void;
54
66
  }
55
67
  declare const Popover$1: import("react").ForwardRefExoticComponent<PopoverProps$1 & import("react").RefAttributes<HTMLElement>>;
68
+ export interface PopoverHoverTriggerProps extends Omit<PreviewTriggerProps, "delay" | "closeDelay"> {
69
+ /** The trigger, then the `Popover`. The trigger must be focusable (a `Button` or `Link`). */
70
+ children: ReactNode;
71
+ /**
72
+ * Milliseconds the pointer rests on the trigger, or keyboard focus sits on
73
+ * it, before the card opens.
74
+ * @default 600
75
+ */
76
+ delay?: number;
77
+ /**
78
+ * Milliseconds after the pointer leaves the trigger and the card, or focus
79
+ * leaves both, before it closes. Moving onto the card keeps it open.
80
+ * @default 200
81
+ */
82
+ closeDelay?: number;
83
+ }
84
+ /**
85
+ * Opens a popover on hover instead of press: a hover card.
86
+ *
87
+ * React Aria's `PreviewTrigger`. The card opens after `delay` when the pointer
88
+ * rests on the trigger or keyboard focus reaches it, and on a long press on
89
+ * touch. It stays open while the pointer travels onto it (a safe area covers
90
+ * the gap) and closes `closeDelay` after the pointer or focus leaves both.
91
+ * Escape closes it; Tab from the trigger moves into it. It is non-modal: no
92
+ * focus trap and no underlay, and the page behind stays usable.
93
+ *
94
+ * The trigger keeps its own press action, so a Link still navigates and a
95
+ * Button still runs `onPress`. Content anyone needs must not live only in the
96
+ * card: touch without a long press never sees it.
97
+ *
98
+ * @example
99
+ * <PopoverHoverTrigger delay={300}>
100
+ * <Button variant="ghost" aria-label="3 more sources">+3</Button>
101
+ * <Popover placement="bottom start">...</Popover>
102
+ * </PopoverHoverTrigger>
103
+ */
104
+ export declare function PopoverHoverTrigger({ delay, closeDelay, ...props }: PopoverHoverTriggerProps): JSX.Element;
56
105
  //#endregion
57
106
  export { Popover$1 as Popover, PopoverProps$1 as PopoverProps, PopoverTrigger };
58
107
  //# sourceMappingURL=popover.d.ts.map