@signalflare-ai/ui 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/CHANGELOG.md +37 -4
  2. package/README.md +1 -1
  3. package/ai/component-registry.json +1047 -183
  4. package/ai/component-registry.md +4241 -50
  5. package/ai/schemas.ts +99 -502
  6. package/dist/.build-complete +1 -1
  7. package/dist/ai/schemas.d.ts +76 -58
  8. package/dist/ai/schemas.d.ts.map +1 -1
  9. package/dist/{ai-actions-DSVeQn4e.js → ai-actions-BdUZI3Gk.js} +3 -3
  10. package/dist/{ai-actions-DSVeQn4e.js.map → ai-actions-BdUZI3Gk.js.map} +1 -1
  11. package/dist/{ai-agent-card-BXHwhWAU.js → ai-agent-card-BR2NIYhi.js} +1 -1
  12. package/dist/{ai-agent-card-BXHwhWAU.js.map → ai-agent-card-BR2NIYhi.js.map} +1 -1
  13. package/dist/{ai-approval-aa0qvjFN.js → ai-approval-Ba7mrKba.js} +2 -2
  14. package/dist/{ai-approval-aa0qvjFN.js.map → ai-approval-Ba7mrKba.js.map} +1 -1
  15. package/dist/{ai-code-block-BgtIxtZZ.js → ai-code-block-CZtoL73R.js} +3 -3
  16. package/dist/{ai-code-block-BgtIxtZZ.js.map → ai-code-block-CZtoL73R.js.map} +1 -1
  17. package/dist/ai-conversation-Cc7WlaBg.js +242 -0
  18. package/dist/ai-conversation-Cc7WlaBg.js.map +1 -0
  19. package/dist/{ai-info-banner-uFxHHwBA.js → ai-info-banner-C7EWPBj7.js} +7 -3
  20. package/dist/ai-info-banner-C7EWPBj7.js.map +1 -0
  21. package/dist/{ai-message-BjnFznXy.js → ai-message-Bp7L68U_.js} +27 -8
  22. package/dist/ai-message-Bp7L68U_.js.map +1 -0
  23. package/dist/{ai-mission-header-08__gULL.js → ai-mission-header-TiCJfTNt.js} +1 -1
  24. package/dist/{ai-mission-header-08__gULL.js.map → ai-mission-header-TiCJfTNt.js.map} +1 -1
  25. package/dist/{ai-part-group-DBtgTgAn.js → ai-part-group-DNb9I446.js} +3 -3
  26. package/dist/{ai-part-group-DBtgTgAn.js.map → ai-part-group-DNb9I446.js.map} +1 -1
  27. package/dist/{ai-prompt-input-Dy1LfxPk.js → ai-prompt-input-BVvov_KF.js} +467 -25
  28. package/dist/ai-prompt-input-BVvov_KF.js.map +1 -0
  29. package/dist/{ai-question-CHHoDJMg.js → ai-question-GPPMk7YM.js} +2 -2
  30. package/dist/{ai-question-CHHoDJMg.js.map → ai-question-GPPMk7YM.js.map} +1 -1
  31. package/dist/{ai-reasoning-CnL6ZSr5.js → ai-reasoning-_feFjk56.js} +2 -2
  32. package/dist/{ai-reasoning-CnL6ZSr5.js.map → ai-reasoning-_feFjk56.js.map} +1 -1
  33. package/dist/{ai-response-BEUg3xvd.js → ai-response-CvjV3WhV.js} +8 -3
  34. package/dist/ai-response-CvjV3WhV.js.map +1 -0
  35. package/dist/{ai-shimmer-By5_L05p.js → ai-shimmer-j6lKIrjj.js} +1 -1
  36. package/dist/{ai-shimmer-By5_L05p.js.map → ai-shimmer-j6lKIrjj.js.map} +1 -1
  37. package/dist/{ai-status-badge-BGYGWYF6.js → ai-status-badge-CSU_QOdz.js} +1 -1
  38. package/dist/{ai-status-badge-BGYGWYF6.js.map → ai-status-badge-CSU_QOdz.js.map} +1 -1
  39. package/dist/{ai-streaming-text-CMfoThV0.js → ai-streaming-text-IWW1BhvZ.js} +44 -16
  40. package/dist/ai-streaming-text-IWW1BhvZ.js.map +1 -0
  41. package/dist/{ai-subagent-DcPRqkAA.js → ai-subagent-JA4iIMW3.js} +13 -5
  42. package/dist/ai-subagent-JA4iIMW3.js.map +1 -0
  43. package/dist/{ai-suggestion-MgeCg5Ar.js → ai-suggestion-BdO6MBuH.js} +2 -2
  44. package/dist/{ai-suggestion-MgeCg5Ar.js.map → ai-suggestion-BdO6MBuH.js.map} +1 -1
  45. package/dist/{ai-task-list-Da9zIm00.js → ai-task-list-DYw4R1FA.js} +12 -5
  46. package/dist/ai-task-list-DYw4R1FA.js.map +1 -0
  47. package/dist/{ai-timeline-Cwu045IR.js → ai-timeline-C42tOUT8.js} +1 -1
  48. package/dist/{ai-timeline-Cwu045IR.js.map → ai-timeline-C42tOUT8.js.map} +1 -1
  49. package/dist/{ai-tool-Cn1O4xjP.js → ai-tool-03jOTwUI.js} +23 -10
  50. package/dist/ai-tool-03jOTwUI.js.map +1 -0
  51. package/dist/{ai-usage-bar-DjS12DMp.js → ai-usage-bar-BRf5LC_b.js} +1 -1
  52. package/dist/{ai-usage-bar-DjS12DMp.js.map → ai-usage-bar-BRf5LC_b.js.map} +1 -1
  53. package/dist/{badge-D_eaA6wv.js → badge-BheXjMc8.js} +2 -2
  54. package/dist/{badge-D_eaA6wv.js.map → badge-BheXjMc8.js.map} +1 -1
  55. package/dist/{banner-B_6oBrsu.js → banner-CcsjunJg.js} +7 -2
  56. package/dist/banner-CcsjunJg.js.map +1 -0
  57. package/dist/{breadcrumbs-BlmeYfgq.js → breadcrumbs-CouSyy3H.js} +3 -3
  58. package/dist/{breadcrumbs-BlmeYfgq.js.map → breadcrumbs-CouSyy3H.js.map} +1 -1
  59. package/dist/{button-De0267YU.js → button-CO6-qPax.js} +1 -1
  60. package/dist/{button-De0267YU.js.map → button-CO6-qPax.js.map} +1 -1
  61. package/dist/catalog.js +1 -1
  62. package/dist/{chart-BK3sVPnD.js → chart-Dg0qUeSc.js} +2 -2
  63. package/dist/{chart-BK3sVPnD.js.map → chart-Dg0qUeSc.js.map} +1 -1
  64. package/dist/{checkbox-DYhUmZNw.js → checkbox-D7p4QKsC.js} +2 -2
  65. package/dist/{checkbox-DYhUmZNw.js.map → checkbox-D7p4QKsC.js.map} +1 -1
  66. package/dist/{clipboard-text-ssybngLw.js → clipboard-text-kLaMogs3.js} +3 -3
  67. package/dist/{clipboard-text-ssybngLw.js.map → clipboard-text-kLaMogs3.js.map} +1 -1
  68. package/dist/{code-Cx-QSoOT.js → code-BN8InC0G.js} +2 -2
  69. package/dist/{code-Cx-QSoOT.js.map → code-BN8InC0G.js.map} +1 -1
  70. package/dist/{collapsible-DWsXeXmS.js → collapsible-D_ueZ0jz.js} +1 -1
  71. package/dist/{collapsible-DWsXeXmS.js.map → collapsible-D_ueZ0jz.js.map} +1 -1
  72. package/dist/{combobox-C0iW6a0r.js → combobox-B7TOK0U2.js} +3 -3
  73. package/dist/{combobox-C0iW6a0r.js.map → combobox-B7TOK0U2.js.map} +1 -1
  74. package/dist/{command-palette-DGzioeki.js → command-palette-CuNUyJca.js} +2 -2
  75. package/dist/{command-palette-DGzioeki.js.map → command-palette-CuNUyJca.js.map} +1 -1
  76. package/dist/components/ai-actions.js +1 -1
  77. package/dist/components/ai-agent-card.js +1 -1
  78. package/dist/components/ai-approval.js +1 -1
  79. package/dist/components/ai-code-block.js +1 -1
  80. package/dist/components/ai-conversation.js +2 -2
  81. package/dist/components/ai-info-banner.js +1 -1
  82. package/dist/components/ai-message.js +1 -1
  83. package/dist/components/ai-mission-header.js +1 -1
  84. package/dist/components/ai-part-group.js +1 -1
  85. package/dist/components/ai-prompt-input.js +2 -2
  86. package/dist/components/ai-question.js +1 -1
  87. package/dist/components/ai-reasoning.js +1 -1
  88. package/dist/components/ai-response.js +1 -1
  89. package/dist/components/ai-shimmer.js +1 -1
  90. package/dist/components/ai-status-badge.js +1 -1
  91. package/dist/components/ai-streaming-text.js +2 -2
  92. package/dist/components/ai-subagent.js +1 -1
  93. package/dist/components/ai-suggestion.js +1 -1
  94. package/dist/components/ai-task-list.js +1 -1
  95. package/dist/components/ai-timeline.js +1 -1
  96. package/dist/components/ai-tool.js +1 -1
  97. package/dist/components/ai-usage-bar.js +1 -1
  98. package/dist/components/badge.js +1 -1
  99. package/dist/components/banner.js +1 -1
  100. package/dist/components/breadcrumbs.js +1 -1
  101. package/dist/components/button.js +1 -1
  102. package/dist/components/chart.js +2 -2
  103. package/dist/components/checkbox.js +1 -1
  104. package/dist/components/clipboard-text.js +1 -1
  105. package/dist/components/code.js +1 -1
  106. package/dist/components/collapsible.js +1 -1
  107. package/dist/components/combobox.js +1 -1
  108. package/dist/components/command-palette.js +1 -1
  109. package/dist/components/data-grid.js +1 -1
  110. package/dist/components/date-picker.js +1 -1
  111. package/dist/components/date-range-picker.js +1 -1
  112. package/dist/components/dialog.js +1 -1
  113. package/dist/components/dropdown.js +1 -1
  114. package/dist/components/empty.js +1 -1
  115. package/dist/components/field.js +1 -1
  116. package/dist/components/filters.js +1 -1
  117. package/dist/components/flow.js +1 -1
  118. package/dist/components/grid.js +1 -1
  119. package/dist/components/input.js +2 -2
  120. package/dist/components/label.js +1 -1
  121. package/dist/components/layer-card.js +1 -1
  122. package/dist/components/loader.js +1 -1
  123. package/dist/components/menubar.js +1 -1
  124. package/dist/components/meter.js +1 -1
  125. package/dist/components/pagination.js +1 -1
  126. package/dist/components/popover.js +1 -1
  127. package/dist/components/radio.js +1 -1
  128. package/dist/components/select.js +1 -1
  129. package/dist/components/sensitive-input.js +1 -1
  130. package/dist/components/sidebar.js +1 -1
  131. package/dist/components/signalflare-ai-logo.js +1 -1
  132. package/dist/components/sparkline.js +1 -1
  133. package/dist/components/stat-card.js +1 -1
  134. package/dist/components/surface.js +1 -1
  135. package/dist/components/switch.js +1 -1
  136. package/dist/components/table.js +1 -1
  137. package/dist/components/tabs.js +1 -1
  138. package/dist/components/text-roll.js +1 -1
  139. package/dist/components/text.js +1 -1
  140. package/dist/components/theme-toggle.js +1 -1
  141. package/dist/components/toast.js +1 -1
  142. package/dist/components/tooltip.js +1 -1
  143. package/dist/components/use-agent-harness.js +1 -1
  144. package/dist/{data-grid-CG76N_hK.js → data-grid-DGHmU0w3.js} +8 -8
  145. package/dist/{data-grid-CG76N_hK.js.map → data-grid-DGHmU0w3.js.map} +1 -1
  146. package/dist/{date-picker-Dqg9L4xu.js → date-picker--ox89RBy.js} +1 -1
  147. package/dist/{date-picker-Dqg9L4xu.js.map → date-picker--ox89RBy.js.map} +1 -1
  148. package/dist/{date-range-picker-D75LLINc.js → date-range-picker-DVa7QBqE.js} +1 -1
  149. package/dist/{date-range-picker-D75LLINc.js.map → date-range-picker-DVa7QBqE.js.map} +1 -1
  150. package/dist/{dialog-CyHEQXEY.js → dialog-Bv1oSFOd.js} +2 -2
  151. package/dist/{dialog-CyHEQXEY.js.map → dialog-Bv1oSFOd.js.map} +1 -1
  152. package/dist/{dist-1-gcEL2L.js → dist-B6iWiWwp.js} +25 -25
  153. package/dist/{dist-1-gcEL2L.js.map → dist-B6iWiWwp.js.map} +1 -1
  154. package/dist/{dropdown-qnEYRFXZ.js → dropdown-B_nrGXjV.js} +2 -2
  155. package/dist/{dropdown-qnEYRFXZ.js.map → dropdown-B_nrGXjV.js.map} +1 -1
  156. package/dist/{echart-DURZEyai.js → echart-CdOUaT-r.js} +1 -1
  157. package/dist/{echart-DURZEyai.js.map → echart-CdOUaT-r.js.map} +1 -1
  158. package/dist/{empty-D2TypIId.js → empty-DZnN0zKX.js} +11 -6
  159. package/dist/empty-DZnN0zKX.js.map +1 -0
  160. package/dist/{field-Y_UK1_Cg.js → field-B_yVof52.js} +2 -2
  161. package/dist/{field-Y_UK1_Cg.js.map → field-B_yVof52.js.map} +1 -1
  162. package/dist/{filters-Bw_U6ZTx.js → filters-cpJCY21R.js} +7 -7
  163. package/dist/{filters-Bw_U6ZTx.js.map → filters-cpJCY21R.js.map} +1 -1
  164. package/dist/{flow-BRsYUCJa.js → flow-B4v198ot.js} +1 -1
  165. package/dist/{flow-BRsYUCJa.js.map → flow-B4v198ot.js.map} +1 -1
  166. package/dist/genui.js +1 -1
  167. package/dist/{grid-qUAN9hFx.js → grid-CEd64Lnh.js} +1 -1
  168. package/dist/{grid-qUAN9hFx.js.map → grid-CEd64Lnh.js.map} +1 -1
  169. package/dist/{highlight-to-react-ClEfL81q.js → highlight-to-react-D0Yav4jk.js} +1 -1
  170. package/dist/{highlight-to-react-ClEfL81q.js.map → highlight-to-react-D0Yav4jk.js.map} +1 -1
  171. package/dist/index.js +69 -69
  172. package/dist/{input-DXYUjGgD.js → input-B2bbijRh.js} +2 -2
  173. package/dist/{input-DXYUjGgD.js.map → input-B2bbijRh.js.map} +1 -1
  174. package/dist/{input-DddtBN-g.js → input-ClB_E4Lb.js} +4 -4
  175. package/dist/{input-DddtBN-g.js.map → input-ClB_E4Lb.js.map} +1 -1
  176. package/dist/{label-QtJxtJ4u.js → label-DUv_urO1.js} +2 -2
  177. package/dist/{label-QtJxtJ4u.js.map → label-DUv_urO1.js.map} +1 -1
  178. package/dist/{layer-card-BME0eljh.js → layer-card-BK7eYfwn.js} +1 -1
  179. package/dist/{layer-card-BME0eljh.js.map → layer-card-BK7eYfwn.js.map} +1 -1
  180. package/dist/layout-DJHMMap2.js +6103 -0
  181. package/dist/layout-DJHMMap2.js.map +1 -0
  182. package/dist/measured-text-BI3dTJmH.js +290 -0
  183. package/dist/measured-text-BI3dTJmH.js.map +1 -0
  184. package/dist/{menubar-C8NzAjfd.js → menubar-Cxf3xeAt.js} +2 -2
  185. package/dist/{menubar-C8NzAjfd.js.map → menubar-Cxf3xeAt.js.map} +1 -1
  186. package/dist/{meter-CpmTenEr.js → meter-BFFe9l5b.js} +1 -1
  187. package/dist/{meter-CpmTenEr.js.map → meter-BFFe9l5b.js.map} +1 -1
  188. package/dist/{pagination-BVqdlONY.js → pagination-yS372Tr4.js} +2 -2
  189. package/dist/{pagination-BVqdlONY.js.map → pagination-yS372Tr4.js.map} +1 -1
  190. package/dist/{popover-BRQZ2b6z.js → popover-SRoJaCZr.js} +1 -1
  191. package/dist/{popover-BRQZ2b6z.js.map → popover-SRoJaCZr.js.map} +1 -1
  192. package/dist/{radio-BNSwOt3B.js → radio-BcwhwYNB.js} +1 -1
  193. package/dist/{radio-BNSwOt3B.js.map → radio-BcwhwYNB.js.map} +1 -1
  194. package/dist/{select-1w2aebGQ.js → select-DMhdoHMa.js} +4 -4
  195. package/dist/{select-1w2aebGQ.js.map → select-DMhdoHMa.js.map} +1 -1
  196. package/dist/{sensitive-input-82Cez3vj.js → sensitive-input-CJUpIRal.js} +3 -3
  197. package/dist/{sensitive-input-82Cez3vj.js.map → sensitive-input-CJUpIRal.js.map} +1 -1
  198. package/dist/{sidebar-CAsCmSpM.js → sidebar-D4zrlYpn.js} +2 -2
  199. package/dist/{sidebar-CAsCmSpM.js.map → sidebar-D4zrlYpn.js.map} +1 -1
  200. package/dist/{signalflare-ai-logo-DDhxMJD6.js → signalflare-ai-logo-Bipogceq.js} +1 -1
  201. package/dist/{signalflare-ai-logo-DDhxMJD6.js.map → signalflare-ai-logo-Bipogceq.js.map} +1 -1
  202. package/dist/{skeleton-line-Do3UmGk9.js → skeleton-line-CH1-h6e2.js} +1 -1
  203. package/dist/{skeleton-line-Do3UmGk9.js.map → skeleton-line-CH1-h6e2.js.map} +1 -1
  204. package/dist/{sparkline-DdbeM4Ai.js → sparkline-DHmgj1d0.js} +2 -2
  205. package/dist/{sparkline-DdbeM4Ai.js.map → sparkline-DHmgj1d0.js.map} +1 -1
  206. package/dist/src/blocks/agent-harness/agent-harness.d.ts.map +1 -1
  207. package/dist/src/blocks/agent-harness/agent-harness.tsx +29 -5
  208. package/dist/src/components/ai-conversation/ai-conversation.d.ts +69 -37
  209. package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
  210. package/dist/src/components/ai-conversation/index.d.ts +2 -1
  211. package/dist/src/components/ai-conversation/index.d.ts.map +1 -1
  212. package/dist/src/components/ai-conversation/measurement-constants.d.ts +30 -0
  213. package/dist/src/components/ai-conversation/measurement-constants.d.ts.map +1 -0
  214. package/dist/src/components/ai-info-banner/ai-info-banner.d.ts.map +1 -1
  215. package/dist/src/components/ai-message/ai-message.d.ts +3 -0
  216. package/dist/src/components/ai-message/ai-message.d.ts.map +1 -1
  217. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts +58 -4
  218. package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  219. package/dist/src/components/ai-prompt-input/controller.d.ts +10 -2
  220. package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
  221. package/dist/src/components/ai-prompt-input/index.d.ts +2 -2
  222. package/dist/src/components/ai-prompt-input/index.d.ts.map +1 -1
  223. package/dist/src/components/ai-prompt-input/types.d.ts +16 -0
  224. package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
  225. package/dist/src/components/ai-response/ai-response.d.ts +12 -1
  226. package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
  227. package/dist/src/components/ai-streaming-text/ai-streaming-text.d.ts +27 -0
  228. package/dist/src/components/ai-streaming-text/ai-streaming-text.d.ts.map +1 -1
  229. package/dist/src/components/ai-streaming-text/index.d.ts +1 -1
  230. package/dist/src/components/ai-streaming-text/index.d.ts.map +1 -1
  231. package/dist/src/components/ai-subagent/ai-subagent.d.ts.map +1 -1
  232. package/dist/src/components/ai-task-list/ai-task-list.d.ts.map +1 -1
  233. package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
  234. package/dist/src/components/banner/banner.d.ts.map +1 -1
  235. package/dist/src/components/empty/empty.d.ts.map +1 -1
  236. package/dist/src/components/stat-card/stat-card.d.ts +5 -0
  237. package/dist/src/components/stat-card/stat-card.d.ts.map +1 -1
  238. package/dist/src/components/text/text.d.ts +35 -1
  239. package/dist/src/components/text/text.d.ts.map +1 -1
  240. package/dist/src/components/tooltip/tooltip.d.ts.map +1 -1
  241. package/dist/src/index.d.ts +3 -3
  242. package/dist/src/index.d.ts.map +1 -1
  243. package/dist/src/utils/index.d.ts +2 -0
  244. package/dist/src/utils/index.d.ts.map +1 -1
  245. package/dist/src/utils/measured-text.d.ts +40 -0
  246. package/dist/src/utils/measured-text.d.ts.map +1 -0
  247. package/dist/src/utils/use-measured-text.d.ts +59 -0
  248. package/dist/src/utils/use-measured-text.d.ts.map +1 -0
  249. package/dist/{stat-card-CEZscNh8.js → stat-card-Ew-ofzEm.js} +28 -10
  250. package/dist/stat-card-Ew-ofzEm.js.map +1 -0
  251. package/dist/styles/sf-binding.css +9 -1
  252. package/dist/styles/sf-standalone.css +2 -2
  253. package/dist/styles/shadcn.css +1 -1
  254. package/dist/styles/theme-fedramp.css +12 -3
  255. package/dist/styles/theme-minimal.css +104 -26
  256. package/dist/styles/theme-sf.css +138 -39
  257. package/dist/styles/theme-vesper.css +91 -0
  258. package/dist/{surface-BduI7Ehl.js → surface-DGwRlC0o.js} +1 -1
  259. package/dist/{surface-BduI7Ehl.js.map → surface-DGwRlC0o.js.map} +1 -1
  260. package/dist/{switch-CzZBRBL7.js → switch-BxAMfHdt.js} +2 -2
  261. package/dist/{switch-CzZBRBL7.js.map → switch-BxAMfHdt.js.map} +1 -1
  262. package/dist/{table-Rv4JMy0B.js → table-BBeAtYVZ.js} +2 -2
  263. package/dist/{table-Rv4JMy0B.js.map → table-BBeAtYVZ.js.map} +1 -1
  264. package/dist/{tabs-1cHrYoel.js → tabs-CeHu7Scn.js} +1 -1
  265. package/dist/{tabs-1cHrYoel.js.map → tabs-CeHu7Scn.js.map} +1 -1
  266. package/dist/{text-KJmGkwnf.js → text-Cqryz7rk.js} +27 -5
  267. package/dist/text-Cqryz7rk.js.map +1 -0
  268. package/dist/{text-roll-BZ3I1umc.js → text-roll-Ch52hcQj.js} +1 -1
  269. package/dist/{text-roll-BZ3I1umc.js.map → text-roll-Ch52hcQj.js.map} +1 -1
  270. package/dist/{theme-toggle-Bhu681D7.js → theme-toggle-LDfIKEqx.js} +3 -3
  271. package/dist/{theme-toggle-Bhu681D7.js.map → theme-toggle-LDfIKEqx.js.map} +1 -1
  272. package/dist/{toast-Nw28a5Cx.js → toast-CaFQNYng.js} +2 -2
  273. package/dist/{toast-Nw28a5Cx.js.map → toast-CaFQNYng.js.map} +1 -1
  274. package/dist/{tooltip-Cb7QW-7H.js → tooltip-g9lFsvcT.js} +8 -2
  275. package/dist/tooltip-g9lFsvcT.js.map +1 -0
  276. package/dist/{use-agent-harness-BMyF8pTq.js → use-agent-harness-BTcNJdw4.js} +1 -1
  277. package/dist/{use-agent-harness-BMyF8pTq.js.map → use-agent-harness-BTcNJdw4.js.map} +1 -1
  278. package/dist/utils.js +2 -1
  279. package/package.json +2 -1
  280. package/scripts/component-registry/index.ts +2 -2
  281. package/scripts/css-build.ts +1 -1
  282. package/scripts/theme-generator/config.ts +27 -141
  283. package/scripts/theme-generator/generate-css.ts +0 -1
  284. package/scripts/theme-generator/index.ts +0 -1
  285. package/dist/ai-conversation-CArP7C8K.js +0 -184
  286. package/dist/ai-conversation-CArP7C8K.js.map +0 -1
  287. package/dist/ai-info-banner-uFxHHwBA.js.map +0 -1
  288. package/dist/ai-message-BjnFznXy.js.map +0 -1
  289. package/dist/ai-prompt-input-Dy1LfxPk.js.map +0 -1
  290. package/dist/ai-response-BEUg3xvd.js.map +0 -1
  291. package/dist/ai-streaming-text-CMfoThV0.js.map +0 -1
  292. package/dist/ai-subagent-DcPRqkAA.js.map +0 -1
  293. package/dist/ai-task-list-Da9zIm00.js.map +0 -1
  294. package/dist/ai-tool-Cn1O4xjP.js.map +0 -1
  295. package/dist/banner-B_6oBrsu.js.map +0 -1
  296. package/dist/empty-D2TypIId.js.map +0 -1
  297. package/dist/stat-card-CEZscNh8.js.map +0 -1
  298. package/dist/styles/theme-blue-tint.css +0 -98
  299. package/dist/text-KJmGkwnf.js.map +0 -1
  300. package/dist/tooltip-Cb7QW-7H.js.map +0 -1
@@ -0,0 +1,290 @@
1
+ "use client";
2
+ import { t as cn } from "./cn-YROP2_ox.js";
3
+ import { a as prepare, i as measureNaturalWidth, o as prepareWithSegments, r as measureLineStats, t as layout } from "./layout-DJHMMap2.js";
4
+ import { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/utils/use-measured-text.ts
7
+ var preparationCache = /* @__PURE__ */ new Map();
8
+ var fontsReadyPromise = null;
9
+ function getFontsReady() {
10
+ if (fontsReadyPromise) return fontsReadyPromise;
11
+ if (typeof document === "undefined" || !document.fonts?.ready) {
12
+ fontsReadyPromise = Promise.resolve();
13
+ return fontsReadyPromise;
14
+ }
15
+ fontsReadyPromise = document.fonts.ready.then(() => void 0);
16
+ return fontsReadyPromise;
17
+ }
18
+ function makeCacheKey(text, font, letterSpacing, whiteSpace, wordBreak) {
19
+ return `${text}|${font}|${letterSpacing}|${whiteSpace}|${wordBreak}`;
20
+ }
21
+ function getPrepared(text, font, options) {
22
+ const key = makeCacheKey(text, font, options.letterSpacing, options.whiteSpace, options.wordBreak);
23
+ const cached = preparationCache.get(key);
24
+ if (cached) return cached;
25
+ const entry = {
26
+ prepared: prepare(text, font, {
27
+ letterSpacing: options.letterSpacing,
28
+ whiteSpace: options.whiteSpace,
29
+ wordBreak: options.wordBreak
30
+ }),
31
+ preparedWithSegments: prepareWithSegments(text, font, {
32
+ letterSpacing: options.letterSpacing,
33
+ whiteSpace: options.whiteSpace,
34
+ wordBreak: options.wordBreak
35
+ })
36
+ };
37
+ preparationCache.set(key, entry);
38
+ return entry;
39
+ }
40
+ var isSSR = typeof window === "undefined";
41
+ var useIsomorphicLayoutEffect = isSSR ? useEffect : useLayoutEffect;
42
+ /**
43
+ * Measures text layout using @chenglou/pretext without DOM reflow.
44
+ *
45
+ * - Caches `prepare()` results keyed by text + font config (do not re-prepare on resize).
46
+ * - Waits for `document.fonts.ready` before first measurement.
47
+ * - Uses ResizeObserver (rAF-throttled) to recompute layout on container resize.
48
+ * - SSR-safe: returns `ready: false` initially; measures post-hydration.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * const { ref, measurement, ready } = useMeasuredText(
53
+ * "Hello world",
54
+ * { mode: "balance" }
55
+ * );
56
+ * // ready === false on SSR
57
+ * // measurement.balancedWidth available after hydration + fonts load
58
+ * ```
59
+ */
60
+ function useMeasuredText(text, options = {}) {
61
+ const { mode = "natural", lineHeight: lineHeightProp, letterSpacing: letterSpacingProp, whiteSpace = "normal", wordBreak = "normal", reserveLines } = options;
62
+ const ref = useRef(null);
63
+ const [ready, setReady] = useState(false);
64
+ const [measurement, setMeasurement] = useState(null);
65
+ const [resolvedFont, setResolvedFont] = useState("16px sans-serif");
66
+ const [resolvedLineHeight, setResolvedLineHeight] = useState(20);
67
+ const [resolvedLetterSpacing, setResolvedLetterSpacing] = useState(0);
68
+ const readComputedStyles = useCallback(() => {
69
+ const el = ref.current;
70
+ if (!el) return;
71
+ const computed = window.getComputedStyle(el);
72
+ const font = computed.font;
73
+ const lh = parseFloat(computed.lineHeight);
74
+ const ls = parseFloat(computed.letterSpacing) || 0;
75
+ setResolvedFont(font || "16px sans-serif");
76
+ setResolvedLineHeight(Number.isNaN(lh) ? 20 : lh);
77
+ setResolvedLetterSpacing(Number.isNaN(ls) ? 0 : ls);
78
+ if (process.env.NODE_ENV === "development") {
79
+ if (computed.fontFamily?.includes("system-ui")) console.warn("[useMeasuredText] Font family \"system-ui\" detected on element. This is unsafe for layout accuracy on macOS. Use an explicit font family (e.g., Inter, Helvetica Neue).");
80
+ }
81
+ }, []);
82
+ const measure = useCallback(() => {
83
+ if (isSSR) return;
84
+ if (typeof globalThis !== "undefined" && globalThis.__SF_PRETEXT_DISABLED__) return;
85
+ if (!ref.current) return;
86
+ const containerWidth = ref.current.getBoundingClientRect().width;
87
+ if (containerWidth <= 0) return;
88
+ const font = resolvedFont;
89
+ const lh = lineHeightProp ?? resolvedLineHeight;
90
+ const ls = letterSpacingProp ?? resolvedLetterSpacing;
91
+ try {
92
+ const { prepared, preparedWithSegments } = getPrepared(text, font, {
93
+ letterSpacing: ls,
94
+ whiteSpace,
95
+ wordBreak
96
+ });
97
+ let height = 0;
98
+ let lineCount = 0;
99
+ let maxLineWidth = 0;
100
+ let balancedWidth = 0;
101
+ if (mode === "reserve" && typeof reserveLines === "number") {
102
+ maxLineWidth = measureLineStats(preparedWithSegments, containerWidth).maxLineWidth;
103
+ lineCount = reserveLines;
104
+ height = reserveLines * lh;
105
+ balancedWidth = maxLineWidth;
106
+ } else if (mode === "shrink") {
107
+ maxLineWidth = measureNaturalWidth(preparedWithSegments);
108
+ balancedWidth = maxLineWidth;
109
+ const result = layout(prepared, maxLineWidth, lh);
110
+ height = result.height;
111
+ lineCount = result.lineCount;
112
+ } else if (mode === "balance") {
113
+ const naturalResult = layout(prepared, containerWidth, lh);
114
+ const naturalLineCount = naturalResult.lineCount;
115
+ let low = 0;
116
+ let high = containerWidth;
117
+ balancedWidth = containerWidth;
118
+ for (let i = 0; i < 12; i++) {
119
+ const mid = (low + high) / 2;
120
+ const stats = measureLineStats(preparedWithSegments, mid);
121
+ if (stats.lineCount <= naturalLineCount && stats.lineCount > 0) {
122
+ balancedWidth = mid;
123
+ high = mid;
124
+ } else low = mid;
125
+ }
126
+ const result = layout(prepared, balancedWidth, lh);
127
+ height = result.height;
128
+ lineCount = result.lineCount;
129
+ maxLineWidth = naturalResult.lineCount > 0 ? measureLineStats(preparedWithSegments, containerWidth).maxLineWidth : 0;
130
+ } else {
131
+ const result = layout(prepared, containerWidth, lh);
132
+ height = result.height;
133
+ lineCount = result.lineCount;
134
+ maxLineWidth = measureLineStats(preparedWithSegments, containerWidth).maxLineWidth;
135
+ balancedWidth = maxLineWidth;
136
+ }
137
+ setMeasurement({
138
+ height,
139
+ lineCount,
140
+ maxLineWidth,
141
+ balancedWidth
142
+ });
143
+ setReady(true);
144
+ } catch (err) {
145
+ if (process.env.NODE_ENV === "development") console.warn("[useMeasuredText] Measurement failed:", err);
146
+ }
147
+ }, [
148
+ text,
149
+ mode,
150
+ resolvedFont,
151
+ resolvedLineHeight,
152
+ resolvedLetterSpacing,
153
+ whiteSpace,
154
+ wordBreak,
155
+ reserveLines,
156
+ lineHeightProp,
157
+ letterSpacingProp
158
+ ]);
159
+ useIsomorphicLayoutEffect(() => {
160
+ if (isSSR) return;
161
+ if (mode === "natural") {
162
+ setReady(true);
163
+ return;
164
+ }
165
+ let cancelled = false;
166
+ const runMeasurement = async () => {
167
+ await getFontsReady();
168
+ if (cancelled) return;
169
+ readComputedStyles();
170
+ requestAnimationFrame(() => {
171
+ if (!cancelled) measure();
172
+ });
173
+ };
174
+ runMeasurement();
175
+ return () => {
176
+ cancelled = true;
177
+ };
178
+ }, [
179
+ measure,
180
+ mode,
181
+ readComputedStyles
182
+ ]);
183
+ useEffect(() => {
184
+ if (isSSR) return;
185
+ if (mode === "natural") return;
186
+ const el = ref.current;
187
+ if (!el) return;
188
+ let rafId = null;
189
+ let pending = false;
190
+ const handleResize = () => {
191
+ if (pending) return;
192
+ pending = true;
193
+ rafId = requestAnimationFrame(() => {
194
+ pending = false;
195
+ readComputedStyles();
196
+ measure();
197
+ });
198
+ };
199
+ const observer = new ResizeObserver(handleResize);
200
+ observer.observe(el);
201
+ return () => {
202
+ observer.disconnect();
203
+ if (rafId !== null) cancelAnimationFrame(rafId);
204
+ };
205
+ }, [
206
+ measure,
207
+ mode,
208
+ readComputedStyles
209
+ ]);
210
+ return {
211
+ ref,
212
+ measurement,
213
+ ready
214
+ };
215
+ }
216
+ //#endregion
217
+ //#region src/utils/measured-text.tsx
218
+ /**
219
+ * Internal component that renders text with pretext-driven layout.
220
+ *
221
+ * This is NOT exported publicly. It powers the public `Text` component.
222
+ *
223
+ * Strategies:
224
+ * - `balance`: finds smallest maxWidth that doesn't increase line count
225
+ * - `shrink`: hugs the widest natural line (maxWidth = maxLineWidth)
226
+ * - `reserve`: sets minHeight based on line count × lineHeight
227
+ * - `natural` / `false`: no measurement, renders as normal inline element
228
+ *
229
+ * SSR-safe: renders with CSS fallbacks initially; upgrades to measured
230
+ * values after hydration without layout mismatch.
231
+ */
232
+ var MeasuredText = forwardRef(function MeasuredText({ children, as: Component = "span", wrap = "natural", reserveLines, whiteSpace = "normal", wordBreak = "normal", className, style, ...props }, forwardedRef) {
233
+ const text = useMemo(() => {
234
+ if (typeof children === "string") return children;
235
+ if (typeof children === "number") return String(children);
236
+ return "";
237
+ }, [children]);
238
+ const mode = wrap === false ? "natural" : wrap;
239
+ const { ref, measurement, ready } = useMeasuredText(text, {
240
+ mode: mode !== "natural" && text.length > 0 ? mode : "natural",
241
+ whiteSpace,
242
+ wordBreak,
243
+ reserveLines
244
+ });
245
+ const setRefs = (el) => {
246
+ ref.current = el;
247
+ if (typeof forwardedRef === "function") forwardedRef(el);
248
+ else if (forwardedRef) forwardedRef.current = el;
249
+ };
250
+ const layoutStyles = useMemo(() => {
251
+ if (!ready || !measurement) {
252
+ if (mode === "balance") return { textWrap: "balance" };
253
+ if (mode === "reserve" && typeof reserveLines === "number") return { minHeight: `${reserveLines * 1.5}em` };
254
+ return {};
255
+ }
256
+ if (mode === "balance") return {
257
+ maxWidth: measurement.balancedWidth,
258
+ transition: "max-width 120ms ease-out"
259
+ };
260
+ if (mode === "shrink") return {
261
+ maxWidth: measurement.maxLineWidth,
262
+ transition: "max-width 120ms ease-out"
263
+ };
264
+ if (mode === "reserve") return {
265
+ minHeight: measurement.height,
266
+ transition: "min-height 120ms ease-out"
267
+ };
268
+ return {};
269
+ }, [
270
+ mode,
271
+ measurement,
272
+ ready,
273
+ reserveLines
274
+ ]);
275
+ return /* @__PURE__ */ jsx(Component, {
276
+ ref: setRefs,
277
+ className: cn(mode !== "natural" && "inline-block", className),
278
+ style: {
279
+ ...layoutStyles,
280
+ ...style
281
+ },
282
+ ...props,
283
+ children
284
+ });
285
+ });
286
+ MeasuredText.displayName = "MeasuredText";
287
+ //#endregion
288
+ export { useMeasuredText as n, MeasuredText as t };
289
+
290
+ //# sourceMappingURL=measured-text-BI3dTJmH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measured-text-BI3dTJmH.js","names":[],"sources":["../src/utils/use-measured-text.ts","../src/utils/measured-text.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n prepare,\n prepareWithSegments,\n layout,\n measureLineStats,\n measureNaturalWidth,\n type PreparedText,\n type PreparedTextWithSegments,\n} from \"@chenglou/pretext\";\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type MeasuredTextMode = \"natural\" | \"balance\" | \"shrink\" | \"reserve\";\n\nexport interface TextMeasurement {\n /** Total height in pixels */\n height: number;\n /** Number of wrapped lines */\n lineCount: number;\n /** Width of the widest line */\n maxLineWidth: number;\n /**\n * For balance mode: the smallest maxWidth that doesn't increase line count\n * vs natural wrapping. For other modes, same as maxLineWidth.\n */\n balancedWidth: number;\n}\n\nexport interface UseMeasuredTextOptions {\n /** Measurement strategy */\n mode?: MeasuredTextMode;\n /** Expected line height in pixels (syncs with CSS line-height). Auto-detected if omitted. */\n lineHeight?: number;\n /** CSS letter-spacing in pixels. Auto-detected if omitted. */\n letterSpacing?: number;\n /** CSS white-space behavior. @default \"normal\" */\n whiteSpace?: \"normal\" | \"pre-wrap\";\n /** CSS word-break behavior. @default \"normal\" */\n wordBreak?: \"normal\" | \"keep-all\";\n /**\n * For reserve mode: pre-reserve this many lines of height before content arrives.\n * Overrides measurement-based line count when provided.\n */\n reserveLines?: number;\n}\n\nexport interface UseMeasuredTextResult {\n /** Ref to attach to the measured element */\n ref: React.RefObject<HTMLElement | null>;\n /** Measurement result (null when not ready or disabled) */\n measurement: TextMeasurement | null;\n /** Whether measurement is ready (false during SSR and initial paint) */\n ready: boolean;\n}\n\n// ─── Module-level cache ──────────────────────────────────────────────────────\n\ntype CacheKey = string;\n\ninterface CachedPrepared {\n prepared: PreparedText;\n preparedWithSegments: PreparedTextWithSegments;\n}\n\nconst preparationCache = new Map<CacheKey, CachedPrepared>();\n\nlet fontsReadyPromise: Promise<void> | null = null;\n\nfunction getFontsReady(): Promise<void> {\n if (fontsReadyPromise) return fontsReadyPromise;\n if (typeof document === \"undefined\" || !document.fonts?.ready) {\n fontsReadyPromise = Promise.resolve();\n return fontsReadyPromise;\n }\n fontsReadyPromise = document.fonts.ready.then(() => undefined);\n return fontsReadyPromise;\n}\n\nfunction makeCacheKey(\n text: string,\n font: string,\n letterSpacing: number,\n whiteSpace: string,\n wordBreak: string\n): CacheKey {\n return `${text}|${font}|${letterSpacing}|${whiteSpace}|${wordBreak}`;\n}\n\nfunction getPrepared(text: string, font: string, options: {\n letterSpacing: number;\n whiteSpace: \"normal\" | \"pre-wrap\";\n wordBreak: \"normal\" | \"keep-all\";\n}): CachedPrepared {\n const key = makeCacheKey(\n text,\n font,\n options.letterSpacing,\n options.whiteSpace,\n options.wordBreak\n );\n\n const cached = preparationCache.get(key);\n if (cached) return cached;\n\n const prepared = prepare(text, font, {\n letterSpacing: options.letterSpacing,\n whiteSpace: options.whiteSpace,\n wordBreak: options.wordBreak,\n });\n\n const preparedWithSegments = prepareWithSegments(text, font, {\n letterSpacing: options.letterSpacing,\n whiteSpace: options.whiteSpace,\n wordBreak: options.wordBreak,\n });\n\n const entry: CachedPrepared = { prepared, preparedWithSegments };\n preparationCache.set(key, entry);\n return entry;\n}\n\n// ─── Hook ────────────────────────────────────────────────────────────────────\n\nconst isSSR = typeof window === \"undefined\";\nconst useIsomorphicLayoutEffect = isSSR ? useEffect : useLayoutEffect;\n\n/**\n * Measures text layout using @chenglou/pretext without DOM reflow.\n *\n * - Caches `prepare()` results keyed by text + font config (do not re-prepare on resize).\n * - Waits for `document.fonts.ready` before first measurement.\n * - Uses ResizeObserver (rAF-throttled) to recompute layout on container resize.\n * - SSR-safe: returns `ready: false` initially; measures post-hydration.\n *\n * @example\n * ```tsx\n * const { ref, measurement, ready } = useMeasuredText(\n * \"Hello world\",\n * { mode: \"balance\" }\n * );\n * // ready === false on SSR\n * // measurement.balancedWidth available after hydration + fonts load\n * ```\n */\nexport function useMeasuredText(\n text: string,\n options: UseMeasuredTextOptions = {}\n): UseMeasuredTextResult {\n const {\n mode = \"natural\",\n lineHeight: lineHeightProp,\n letterSpacing: letterSpacingProp,\n whiteSpace = \"normal\",\n wordBreak = \"normal\",\n reserveLines,\n } = options;\n\n const ref = useRef<HTMLElement>(null);\n const [ready, setReady] = useState(false);\n const [measurement, setMeasurement] = useState<TextMeasurement | null>(null);\n\n // Resolved styles from computed CSS (synced with DOM)\n const [resolvedFont, setResolvedFont] = useState<string>(\"16px sans-serif\");\n const [resolvedLineHeight, setResolvedLineHeight] = useState<number>(20);\n const [resolvedLetterSpacing, setResolvedLetterSpacing] = useState<number>(0);\n\n // Read computed styles from the DOM element\n const readComputedStyles = useCallback(() => {\n const el = ref.current;\n if (!el) return;\n\n const computed = window.getComputedStyle(el);\n const font = computed.font;\n const lh = parseFloat(computed.lineHeight);\n const ls = parseFloat(computed.letterSpacing) || 0;\n\n setResolvedFont(font || \"16px sans-serif\");\n setResolvedLineHeight(Number.isNaN(lh) ? 20 : lh);\n setResolvedLetterSpacing(Number.isNaN(ls) ? 0 : ls);\n\n // Dev-only warning for system-ui (unsafe on macOS per pretext README)\n if (process.env.NODE_ENV === \"development\") {\n const fontFamily = computed.fontFamily;\n if (fontFamily?.includes(\"system-ui\")) {\n // eslint-disable-next-line no-console\n console.warn(\n `[useMeasuredText] Font family \"system-ui\" detected on element. ` +\n `This is unsafe for layout accuracy on macOS. ` +\n `Use an explicit font family (e.g., Inter, Helvetica Neue).`\n );\n }\n }\n }, []);\n\n // Main measurement effect\n const measure = useCallback(() => {\n if (isSSR) return;\n if (typeof globalThis !== \"undefined\" && (globalThis as { __SF_PRETEXT_DISABLED__?: boolean }).__SF_PRETEXT_DISABLED__) {\n return;\n }\n if (!ref.current) return;\n\n const el = ref.current;\n const rect = el.getBoundingClientRect();\n const containerWidth = rect.width;\n if (containerWidth <= 0) return;\n\n const font = resolvedFont;\n const lh = lineHeightProp ?? resolvedLineHeight;\n const ls = letterSpacingProp ?? resolvedLetterSpacing;\n\n try {\n const { prepared, preparedWithSegments } = getPrepared(text, font, {\n letterSpacing: ls,\n whiteSpace,\n wordBreak,\n });\n\n // Mode-specific measurement\n let height = 0;\n let lineCount = 0;\n let maxLineWidth = 0;\n let balancedWidth = 0;\n\n if (mode === \"reserve\" && typeof reserveLines === \"number\") {\n // Use reserved line count for height, but still measure width\n const stats = measureLineStats(preparedWithSegments, containerWidth);\n maxLineWidth = stats.maxLineWidth;\n lineCount = reserveLines;\n height = reserveLines * lh;\n balancedWidth = maxLineWidth;\n } else if (mode === \"shrink\") {\n // Measure natural width (widest unwrapped line)\n maxLineWidth = measureNaturalWidth(preparedWithSegments);\n balancedWidth = maxLineWidth;\n // Re-layout at that width to get accurate line count/height\n const result = layout(prepared, maxLineWidth, lh);\n height = result.height;\n lineCount = result.lineCount;\n } else if (mode === \"balance\") {\n // Find balanced width: smallest width that doesn't add lines\n const naturalResult = layout(prepared, containerWidth, lh);\n const naturalLineCount = naturalResult.lineCount;\n\n // Binary search for smallest width that keeps same line count\n let low = 0;\n let high = containerWidth;\n balancedWidth = containerWidth;\n\n for (let i = 0; i < 12; i++) {\n // 12 iterations = ~0.02% precision\n const mid = (low + high) / 2;\n const stats = measureLineStats(preparedWithSegments, mid);\n if (stats.lineCount <= naturalLineCount && stats.lineCount > 0) {\n balancedWidth = mid;\n high = mid;\n } else {\n low = mid;\n }\n }\n\n // Final measurement at balanced width\n const result = layout(prepared, balancedWidth, lh);\n height = result.height;\n lineCount = result.lineCount;\n maxLineWidth = naturalResult.lineCount > 0\n ? measureLineStats(preparedWithSegments, containerWidth).maxLineWidth\n : 0;\n } else {\n // natural or default\n const result = layout(prepared, containerWidth, lh);\n height = result.height;\n lineCount = result.lineCount;\n maxLineWidth = measureLineStats(preparedWithSegments, containerWidth).maxLineWidth;\n balancedWidth = maxLineWidth;\n }\n\n setMeasurement({ height, lineCount, maxLineWidth, balancedWidth });\n setReady(true);\n } catch (err) {\n // Measurement failed (e.g., canvas unavailable) — keep CSS fallback behavior\n if (process.env.NODE_ENV === \"development\") {\n // eslint-disable-next-line no-console\n console.warn(\"[useMeasuredText] Measurement failed:\", err);\n }\n }\n }, [text, mode, resolvedFont, resolvedLineHeight, resolvedLetterSpacing, whiteSpace, wordBreak, reserveLines, lineHeightProp, letterSpacingProp]);\n\n // Initial measurement after fonts load and layout settles\n useIsomorphicLayoutEffect(() => {\n if (isSSR) return;\n if (mode === \"natural\") {\n // No measurement needed for natural mode\n setReady(true);\n return;\n }\n\n let cancelled = false;\n\n const runMeasurement = async () => {\n await getFontsReady();\n if (cancelled) return;\n\n // Read computed styles first\n readComputedStyles();\n\n // Measure on next frame to ensure layout settled\n requestAnimationFrame(() => {\n if (!cancelled) measure();\n });\n };\n\n runMeasurement();\n\n return () => {\n cancelled = true;\n };\n }, [measure, mode, readComputedStyles]);\n\n // ResizeObserver with rAF throttling\n useEffect(() => {\n if (isSSR) return;\n if (mode === \"natural\") return;\n\n const el = ref.current;\n if (!el) return;\n\n let rafId: number | null = null;\n let pending = false;\n\n const handleResize = () => {\n if (pending) return;\n pending = true;\n\n rafId = requestAnimationFrame(() => {\n pending = false;\n readComputedStyles();\n measure();\n });\n };\n\n const observer = new ResizeObserver(handleResize);\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n if (rafId !== null) cancelAnimationFrame(rafId);\n };\n }, [measure, mode, readComputedStyles]);\n\n return { ref, measurement, ready };\n}\n","\"use client\";\n\nimport type { ElementType, ReactNode, CSSProperties } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\nimport { cn } from \"./cn\";\nimport {\n useMeasuredText,\n type MeasuredTextMode,\n} from \"./use-measured-text\";\n\n// Re-export the type for consumers\nexport type { MeasuredTextMode } from \"./use-measured-text\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface MeasuredTextProps {\n /** Text content to measure and render */\n children: ReactNode;\n /** HTML element to render as. @default \"span\" */\n as?: ElementType;\n /** Measurement/wrapping strategy */\n wrap?: MeasuredTextMode | false;\n /**\n * For reserve mode: pre-reserve this many lines.\n * If omitted in reserve mode, measures children to determine line count.\n */\n reserveLines?: number;\n /** CSS white-space behavior. @default \"normal\" */\n whiteSpace?: \"normal\" | \"pre-wrap\";\n /** CSS word-break behavior. @default \"normal\" */\n wordBreak?: \"normal\" | \"keep-all\";\n /** Additional CSS classes */\n className?: string;\n /** Inline styles (merged with measured styles) */\n style?: CSSProperties;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * Internal component that renders text with pretext-driven layout.\n *\n * This is NOT exported publicly. It powers the public `Text` component.\n *\n * Strategies:\n * - `balance`: finds smallest maxWidth that doesn't increase line count\n * - `shrink`: hugs the widest natural line (maxWidth = maxLineWidth)\n * - `reserve`: sets minHeight based on line count × lineHeight\n * - `natural` / `false`: no measurement, renders as normal inline element\n *\n * SSR-safe: renders with CSS fallbacks initially; upgrades to measured\n * values after hydration without layout mismatch.\n */\nexport const MeasuredText = forwardRef<HTMLElement, MeasuredTextProps>(\n function MeasuredText(\n {\n children,\n as: Component = \"span\",\n wrap = \"natural\",\n reserveLines,\n whiteSpace = \"normal\",\n wordBreak = \"normal\",\n className,\n style,\n ...props\n },\n forwardedRef\n ) {\n // Extract text content for measurement (simple stringification for now)\n const text = useMemo(() => {\n if (typeof children === \"string\") return children;\n if (typeof children === \"number\") return String(children);\n // For complex children, we can't measure accurately — fall back to natural\n return \"\";\n }, [children]);\n\n const mode = wrap === false ? \"natural\" : wrap;\n const shouldMeasure = mode !== \"natural\" && text.length > 0;\n\n const { ref, measurement, ready } = useMeasuredText(text, {\n mode: shouldMeasure ? mode : \"natural\",\n whiteSpace,\n wordBreak,\n reserveLines,\n });\n\n // Merge refs\n const setRefs = (el: HTMLElement | null) => {\n (ref as React.MutableRefObject<HTMLElement | null>).current = el;\n if (typeof forwardedRef === \"function\") {\n forwardedRef(el);\n } else if (forwardedRef) {\n (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = el;\n }\n };\n\n // Compute layout styles based on mode and measurement state\n const layoutStyles = useMemo<CSSProperties>(() => {\n if (!ready || !measurement) {\n // Pre-measurement: CSS fallbacks\n if (mode === \"balance\") {\n return { textWrap: \"balance\" as const };\n }\n if (mode === \"reserve\" && typeof reserveLines === \"number\") {\n // Heuristic: assume 1.5em line height for SSR reserve\n return { minHeight: `${reserveLines * 1.5}em` };\n }\n return {};\n }\n\n // Post-measurement: apply precise values\n if (mode === \"balance\") {\n return {\n maxWidth: measurement.balancedWidth,\n // Smooth transition when measurement settles\n transition: \"max-width 120ms ease-out\",\n };\n }\n\n if (mode === \"shrink\") {\n return {\n maxWidth: measurement.maxLineWidth,\n transition: \"max-width 120ms ease-out\",\n };\n }\n\n if (mode === \"reserve\") {\n return {\n minHeight: measurement.height,\n transition: \"min-height 120ms ease-out\",\n };\n }\n\n return {};\n }, [mode, measurement, ready, reserveLines]);\n\n return (\n <Component\n ref={setRefs}\n className={cn(\n // Base display mode for measurement to work correctly\n mode !== \"natural\" && \"inline-block\",\n className\n )}\n style={{\n ...layoutStyles,\n ...style,\n }}\n {...props}\n >\n {children}\n </Component>\n );\n }\n);\n\nMeasuredText.displayName = \"MeasuredText\";\n"],"mappings":";;;;;;AAyEA,IAAM,mCAAmB,IAAI,KAA+B;AAE5D,IAAI,oBAA0C;AAE9C,SAAS,gBAA+B;AACtC,KAAI,kBAAmB,QAAO;AAC9B,KAAI,OAAO,aAAa,eAAe,CAAC,SAAS,OAAO,OAAO;AAC7D,sBAAoB,QAAQ,SAAS;AACrC,SAAO;;AAET,qBAAoB,SAAS,MAAM,MAAM,WAAW,KAAA,EAAU;AAC9D,QAAO;;AAGT,SAAS,aACP,MACA,MACA,eACA,YACA,WACU;AACV,QAAO,GAAG,KAAK,GAAG,KAAK,GAAG,cAAc,GAAG,WAAW,GAAG;;AAG3D,SAAS,YAAY,MAAc,MAAc,SAI9B;CACjB,MAAM,MAAM,aACV,MACA,MACA,QAAQ,eACR,QAAQ,YACR,QAAQ,UACT;CAED,MAAM,SAAS,iBAAiB,IAAI,IAAI;AACxC,KAAI,OAAQ,QAAO;CAcnB,MAAM,QAAwB;EAAE,UAZf,QAAQ,MAAM,MAAM;GACnC,eAAe,QAAQ;GACvB,YAAY,QAAQ;GACpB,WAAW,QAAQ;GACpB,CAAC;EAQwC,sBANb,oBAAoB,MAAM,MAAM;GAC3D,eAAe,QAAQ;GACvB,YAAY,QAAQ;GACpB,WAAW,QAAQ;GACpB,CAAC;EAE8D;AAChE,kBAAiB,IAAI,KAAK,MAAM;AAChC,QAAO;;AAKT,IAAM,QAAQ,OAAO,WAAW;AAChC,IAAM,4BAA4B,QAAQ,YAAY;;;;;;;;;;;;;;;;;;;AAoBtD,SAAgB,gBACd,MACA,UAAkC,EAAE,EACb;CACvB,MAAM,EACJ,OAAO,WACP,YAAY,gBACZ,eAAe,mBACf,aAAa,UACb,YAAY,UACZ,iBACE;CAEJ,MAAM,MAAM,OAAoB,KAAK;CACrC,MAAM,CAAC,OAAO,YAAY,SAAS,MAAM;CACzC,MAAM,CAAC,aAAa,kBAAkB,SAAiC,KAAK;CAG5E,MAAM,CAAC,cAAc,mBAAmB,SAAiB,kBAAkB;CAC3E,MAAM,CAAC,oBAAoB,yBAAyB,SAAiB,GAAG;CACxE,MAAM,CAAC,uBAAuB,4BAA4B,SAAiB,EAAE;CAG7E,MAAM,qBAAqB,kBAAkB;EAC3C,MAAM,KAAK,IAAI;AACf,MAAI,CAAC,GAAI;EAET,MAAM,WAAW,OAAO,iBAAiB,GAAG;EAC5C,MAAM,OAAO,SAAS;EACtB,MAAM,KAAK,WAAW,SAAS,WAAW;EAC1C,MAAM,KAAK,WAAW,SAAS,cAAc,IAAI;AAEjD,kBAAgB,QAAQ,kBAAkB;AAC1C,wBAAsB,OAAO,MAAM,GAAG,GAAG,KAAK,GAAG;AACjD,2BAAyB,OAAO,MAAM,GAAG,GAAG,IAAI,GAAG;AAGnD,MAAA,QAAA,IAAA,aAA6B;OACR,SAAS,YACZ,SAAS,YAAY,CAEnC,SAAQ,KACN,2KAGD;;IAGJ,EAAE,CAAC;CAGN,MAAM,UAAU,kBAAkB;AAChC,MAAI,MAAO;AACX,MAAI,OAAO,eAAe,eAAgB,WAAqD,wBAC7F;AAEF,MAAI,CAAC,IAAI,QAAS;EAIlB,MAAM,iBAFK,IAAI,QACC,uBAAuB,CACX;AAC5B,MAAI,kBAAkB,EAAG;EAEzB,MAAM,OAAO;EACb,MAAM,KAAK,kBAAkB;EAC7B,MAAM,KAAK,qBAAqB;AAEhC,MAAI;GACF,MAAM,EAAE,UAAU,yBAAyB,YAAY,MAAM,MAAM;IACjE,eAAe;IACf;IACA;IACD,CAAC;GAGF,IAAI,SAAS;GACb,IAAI,YAAY;GAChB,IAAI,eAAe;GACnB,IAAI,gBAAgB;AAEpB,OAAI,SAAS,aAAa,OAAO,iBAAiB,UAAU;AAG1D,mBADc,iBAAiB,sBAAsB,eAAe,CAC/C;AACrB,gBAAY;AACZ,aAAS,eAAe;AACxB,oBAAgB;cACP,SAAS,UAAU;AAE5B,mBAAe,oBAAoB,qBAAqB;AACxD,oBAAgB;IAEhB,MAAM,SAAS,OAAO,UAAU,cAAc,GAAG;AACjD,aAAS,OAAO;AAChB,gBAAY,OAAO;cACV,SAAS,WAAW;IAE7B,MAAM,gBAAgB,OAAO,UAAU,gBAAgB,GAAG;IAC1D,MAAM,mBAAmB,cAAc;IAGvC,IAAI,MAAM;IACV,IAAI,OAAO;AACX,oBAAgB;AAEhB,SAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;KAE3B,MAAM,OAAO,MAAM,QAAQ;KAC3B,MAAM,QAAQ,iBAAiB,sBAAsB,IAAI;AACzD,SAAI,MAAM,aAAa,oBAAoB,MAAM,YAAY,GAAG;AAC9D,sBAAgB;AAChB,aAAO;WAEP,OAAM;;IAKV,MAAM,SAAS,OAAO,UAAU,eAAe,GAAG;AAClD,aAAS,OAAO;AAChB,gBAAY,OAAO;AACnB,mBAAe,cAAc,YAAY,IACrC,iBAAiB,sBAAsB,eAAe,CAAC,eACvD;UACC;IAEL,MAAM,SAAS,OAAO,UAAU,gBAAgB,GAAG;AACnD,aAAS,OAAO;AAChB,gBAAY,OAAO;AACnB,mBAAe,iBAAiB,sBAAsB,eAAe,CAAC;AACtE,oBAAgB;;AAGlB,kBAAe;IAAE;IAAQ;IAAW;IAAc;IAAe,CAAC;AAClE,YAAS,KAAK;WACP,KAAK;AAEZ,OAAA,QAAA,IAAA,aAA6B,cAE3B,SAAQ,KAAK,yCAAyC,IAAI;;IAG7D;EAAC;EAAM;EAAM;EAAc;EAAoB;EAAuB;EAAY;EAAW;EAAc;EAAgB;EAAkB,CAAC;AAGjJ,iCAAgC;AAC9B,MAAI,MAAO;AACX,MAAI,SAAS,WAAW;AAEtB,YAAS,KAAK;AACd;;EAGF,IAAI,YAAY;EAEhB,MAAM,iBAAiB,YAAY;AACjC,SAAM,eAAe;AACrB,OAAI,UAAW;AAGf,uBAAoB;AAGpB,+BAA4B;AAC1B,QAAI,CAAC,UAAW,UAAS;KACzB;;AAGJ,kBAAgB;AAEhB,eAAa;AACX,eAAY;;IAEb;EAAC;EAAS;EAAM;EAAmB,CAAC;AAGvC,iBAAgB;AACd,MAAI,MAAO;AACX,MAAI,SAAS,UAAW;EAExB,MAAM,KAAK,IAAI;AACf,MAAI,CAAC,GAAI;EAET,IAAI,QAAuB;EAC3B,IAAI,UAAU;EAEd,MAAM,qBAAqB;AACzB,OAAI,QAAS;AACb,aAAU;AAEV,WAAQ,4BAA4B;AAClC,cAAU;AACV,wBAAoB;AACpB,aAAS;KACT;;EAGJ,MAAM,WAAW,IAAI,eAAe,aAAa;AACjD,WAAS,QAAQ,GAAG;AAEpB,eAAa;AACX,YAAS,YAAY;AACrB,OAAI,UAAU,KAAM,sBAAqB,MAAM;;IAEhD;EAAC;EAAS;EAAM;EAAmB,CAAC;AAEvC,QAAO;EAAE;EAAK;EAAa;EAAO;;;;;;;;;;;;;;;;;;AClTpC,IAAa,eAAe,WAC1B,SAAS,aACP,EACE,UACA,IAAI,YAAY,QAChB,OAAO,WACP,cACA,aAAa,UACb,YAAY,UACZ,WACA,OACA,GAAG,SAEL,cACA;CAEA,MAAM,OAAO,cAAc;AACzB,MAAI,OAAO,aAAa,SAAU,QAAO;AACzC,MAAI,OAAO,aAAa,SAAU,QAAO,OAAO,SAAS;AAEzD,SAAO;IACN,CAAC,SAAS,CAAC;CAEd,MAAM,OAAO,SAAS,QAAQ,YAAY;CAG1C,MAAM,EAAE,KAAK,aAAa,UAAU,gBAAgB,MAAM;EACxD,MAHoB,SAAS,aAAa,KAAK,SAAS,IAGlC,OAAO;EAC7B;EACA;EACA;EACD,CAAC;CAGF,MAAM,WAAW,OAA2B;AACzC,MAAmD,UAAU;AAC9D,MAAI,OAAO,iBAAiB,WAC1B,cAAa,GAAG;WACP,aACR,cAA4D,UAAU;;CAK3E,MAAM,eAAe,cAA6B;AAChD,MAAI,CAAC,SAAS,CAAC,aAAa;AAE1B,OAAI,SAAS,UACX,QAAO,EAAE,UAAU,WAAoB;AAEzC,OAAI,SAAS,aAAa,OAAO,iBAAiB,SAEhD,QAAO,EAAE,WAAW,GAAG,eAAe,IAAI,KAAK;AAEjD,UAAO,EAAE;;AAIX,MAAI,SAAS,UACX,QAAO;GACL,UAAU,YAAY;GAEtB,YAAY;GACb;AAGH,MAAI,SAAS,SACX,QAAO;GACL,UAAU,YAAY;GACtB,YAAY;GACb;AAGH,MAAI,SAAS,UACX,QAAO;GACL,WAAW,YAAY;GACvB,YAAY;GACb;AAGH,SAAO,EAAE;IACR;EAAC;EAAM;EAAa;EAAO;EAAa,CAAC;AAE5C,QACE,oBAAC,WAAD;EACE,KAAK;EACL,WAAW,GAET,SAAS,aAAa,gBACtB,UACD;EACD,OAAO;GACL,GAAG;GACH,GAAG;GACJ;EACD,GAAI;EAEH;EACS,CAAA;EAGjB;AAED,aAAa,cAAc"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { t as cn } from "./cn-YROP2_ox.js";
3
- import { t as Tooltip } from "./tooltip-Cb7QW-7H.js";
3
+ import { t as Tooltip } from "./tooltip-g9lFsvcT.js";
4
4
  import { useEffect, useRef } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { IconContext } from "@phosphor-icons/react";
@@ -99,4 +99,4 @@ var MenuBar = ({ className, isActive, options, optionIds = false }) => {
99
99
  //#endregion
100
100
  export { useMenuNavigation as n, MenuBar as t };
101
101
 
102
- //# sourceMappingURL=menubar-C8NzAjfd.js.map
102
+ //# sourceMappingURL=menubar-Cxf3xeAt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"menubar-C8NzAjfd.js","names":[],"sources":["../src/components/menubar/use-menu-navigation.ts","../src/components/menubar/menubar.tsx"],"sourcesContent":["import React, { useEffect, useRef } from \"react\";\n\ntype UseMenuNavigationProps = {\n menuRef: React.RefObject<HTMLElement | null>;\n direction?: \"horizontal\" | \"vertical\"; // Default: horizontal\n};\n\nexport const useMenuNavigation = ({\n menuRef,\n direction = \"horizontal\",\n}: UseMenuNavigationProps) => {\n const activeElementRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!menuRef.current) return;\n\n const focusableElements = Array.from(\n menuRef.current.querySelectorAll(\n 'a, button, input, textarea, select, details, [tabindex]:not([tabindex=\"-1\"])'\n )\n ) as HTMLElement[];\n\n if (focusableElements.length === 0) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!activeElementRef.current) return;\n\n const currentIndex = focusableElements.indexOf(activeElementRef.current);\n let nextIndex = currentIndex;\n\n const isHorizontal = direction === \"horizontal\";\n const forwardKey = isHorizontal ? \"ArrowRight\" : \"ArrowDown\";\n const backwardKey = isHorizontal ? \"ArrowLeft\" : \"ArrowUp\";\n\n if (e.key === forwardKey) {\n e.preventDefault();\n nextIndex = (currentIndex + 1) % focusableElements.length;\n } else if (e.key === backwardKey) {\n e.preventDefault();\n nextIndex =\n (currentIndex - 1 + focusableElements.length) %\n focusableElements.length;\n } else {\n return;\n }\n\n const nextElement = focusableElements[nextIndex];\n activeElementRef.current = nextElement;\n nextElement.focus();\n };\n\n const addKeyListener = () =>\n document.addEventListener(\"keydown\", handleKeyDown);\n const removeKeyListener = () =>\n document.removeEventListener(\"keydown\", handleKeyDown);\n\n const handleFocusIn = () => {\n activeElementRef.current = document.activeElement as HTMLElement;\n addKeyListener();\n };\n\n const handleFocusOut = () => {\n activeElementRef.current = null;\n removeKeyListener();\n };\n\n menuRef.current.addEventListener(\"focusin\", handleFocusIn);\n menuRef.current.addEventListener(\"focusout\", handleFocusOut);\n\n return () => {\n menuRef.current?.removeEventListener(\"focusin\", handleFocusIn);\n menuRef.current?.removeEventListener(\"focusout\", handleFocusOut);\n removeKeyListener();\n };\n }, [menuRef, direction]);\n};\n","import { IconContext } from \"@phosphor-icons/react\";\nimport React, { useRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Tooltip } from \"../tooltip\";\nimport { useMenuNavigation } from \"./use-menu-navigation\";\n\n/** MenuBar variant definitions (currently empty, reserved for future additions). */\nexport const SF_MENUBAR_VARIANTS = {\n // MenuBar currently has no variant options but structure is ready for future additions\n} as const;\n\nexport const SF_MENUBAR_DEFAULT_VARIANTS = {} as const;\n\n// Derived types from SF_MENUBAR_VARIANTS\nexport interface SFMenuBarVariantsProps {}\n\nexport function menuBarVariants(_props: SFMenuBarVariantsProps = {}) {\n return cn(\n // Base styles\n \"flex rounded-lg border border-sf-fill bg-sf-fill pl-px shadow-xs transition-colors\"\n );\n}\n\n/** Props for an individual menu option within a MenuBar. */\ntype MenuOptionProps = {\n /** Icon element (typically from `@phosphor-icons/react`) rendered at 18px */\n icon: React.ReactNode;\n /** Unique identifier for the option (used when `optionIds` is true) */\n id?: number | string;\n /** Currently active value from the parent MenuBar */\n isActive?: number | boolean | string | undefined;\n /** Callback when this option is clicked */\n onClick: () => void;\n /** Tooltip text shown on hover */\n tooltip: string;\n};\n\nconst MenuOption = ({\n icon,\n id,\n isActive,\n onClick,\n tooltip,\n}: MenuOptionProps) => {\n return (\n <Tooltip content={tooltip}>\n <button\n className={cn(\n \"focus:inset-ring-focus relative -ml-px flex h-full w-11 cursor-pointer items-center justify-center rounded-md border-none bg-sf-fill first:rounded-l-lg last:rounded-r-lg transition-colors focus:z-10 focus:outline-none focus-visible:z-10 focus-visible:inset-ring-[0.5]\",\n {\n \"z-20 bg-sf-base shadow-xs transition-colors\": isActive === id,\n }\n )}\n onClick={onClick}\n >\n <IconContext.Provider value={{ size: 18 }} {...({} as any)}>\n {icon}\n </IconContext.Provider>\n </button>\n </Tooltip>\n );\n};\n\n/**\n * MenuBar component props.\n *\n * Horizontal icon-button toolbar with keyboard navigation and tooltip labels.\n *\n * @example\n * ```tsx\n * <MenuBar\n * isActive={activeIndex}\n * options={[\n * { icon: <ListIcon />, tooltip: \"List view\", onClick: () => setView(\"list\") },\n * { icon: <GridFourIcon />, tooltip: \"Grid view\", onClick: () => setView(\"grid\") },\n * ]}\n * />\n * ```\n */\ntype MenuBarProps = {\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** The currently active option value — matched against option index or `id`. */\n isActive: number | boolean | string | undefined;\n /** Array of menu option configurations. */\n options: MenuOptionProps[];\n /** When true, each option's `id` field is used for matching instead of its array index. */\n optionIds?: boolean;\n};\n\n/**\n * MenuBar — horizontal icon-button toolbar with keyboard arrow-key navigation.\n *\n * Each option renders as a `<button>` with a Tooltip. The active option is\n * visually highlighted with an elevated background.\n *\n * @example\n * ```tsx\n * <MenuBar\n * isActive={0}\n * options={[\n * { icon: <ListIcon />, tooltip: \"List\", onClick: () => {} },\n * { icon: <GridFourIcon />, tooltip: \"Grid\", onClick: () => {} },\n * ]}\n * />\n * ```\n */\nexport const MenuBar = ({\n className,\n isActive,\n options,\n optionIds = false, // if option needs an extra unique ID\n}: MenuBarProps) => {\n const menuRef = useRef<HTMLElement | null>(null);\n\n useMenuNavigation({ menuRef, direction: \"horizontal\" });\n\n return (\n <nav\n className={cn(\n \"flex rounded-lg border border-sf-fill bg-sf-fill pl-px shadow-xs transition-colors\",\n className\n )}\n ref={menuRef}\n >\n {options.map((option, index) => (\n <MenuOption\n key={index}\n {...option}\n isActive={isActive}\n id={optionIds ? option.id : index}\n />\n ))}\n </nav>\n );\n};\n"],"mappings":";;;;;;;AAOA,IAAa,qBAAqB,EAChC,SACA,YAAY,mBACgB;CAC5B,MAAM,mBAAmB,OAA2B,KAAK;AAEzD,iBAAgB;AACd,MAAI,CAAC,QAAQ,QAAS;EAEtB,MAAM,oBAAoB,MAAM,KAC9B,QAAQ,QAAQ,iBACd,iFACD,CACF;AAED,MAAI,kBAAkB,WAAW,EAAG;EAEpC,MAAM,iBAAiB,MAAqB;AAC1C,OAAI,CAAC,iBAAiB,QAAS;GAE/B,MAAM,eAAe,kBAAkB,QAAQ,iBAAiB,QAAQ;GACxE,IAAI,YAAY;GAEhB,MAAM,eAAe,cAAc;GACnC,MAAM,aAAa,eAAe,eAAe;GACjD,MAAM,cAAc,eAAe,cAAc;AAEjD,OAAI,EAAE,QAAQ,YAAY;AACxB,MAAE,gBAAgB;AAClB,iBAAa,eAAe,KAAK,kBAAkB;cAC1C,EAAE,QAAQ,aAAa;AAChC,MAAE,gBAAgB;AAClB,iBACG,eAAe,IAAI,kBAAkB,UACtC,kBAAkB;SAEpB;GAGF,MAAM,cAAc,kBAAkB;AACtC,oBAAiB,UAAU;AAC3B,eAAY,OAAO;;EAGrB,MAAM,uBACJ,SAAS,iBAAiB,WAAW,cAAc;EACrD,MAAM,0BACJ,SAAS,oBAAoB,WAAW,cAAc;EAExD,MAAM,sBAAsB;AAC1B,oBAAiB,UAAU,SAAS;AACpC,mBAAgB;;EAGlB,MAAM,uBAAuB;AAC3B,oBAAiB,UAAU;AAC3B,sBAAmB;;AAGrB,UAAQ,QAAQ,iBAAiB,WAAW,cAAc;AAC1D,UAAQ,QAAQ,iBAAiB,YAAY,eAAe;AAE5D,eAAa;AACX,WAAQ,SAAS,oBAAoB,WAAW,cAAc;AAC9D,WAAQ,SAAS,oBAAoB,YAAY,eAAe;AAChE,sBAAmB;;IAEpB,CAAC,SAAS,UAAU,CAAC;;;;ACpC1B,IAAM,cAAc,EAClB,MACA,IACA,UACA,SACA,cACqB;AACrB,QACE,oBAAC,SAAD;EAAS,SAAS;YAChB,oBAAC,UAAD;GACE,WAAW,GACT,+QACA,EACE,+CAA+C,aAAa,IAC7D,CACF;GACQ;aAET,oBAAC,YAAY,UAAb;IAAsB,OAAO,EAAE,MAAM,IAAI;cACtC;IACoB,CAAA;GAChB,CAAA;EACD,CAAA;;;;;;;;;;;;;;;;;;;AAgDd,IAAa,WAAW,EACtB,WACA,UACA,SACA,YAAY,YACM;CAClB,MAAM,UAAU,OAA2B,KAAK;AAEhD,mBAAkB;EAAE;EAAS,WAAW;EAAc,CAAC;AAEvD,QACE,oBAAC,OAAD;EACE,WAAW,GACT,sFACA,UACD;EACD,KAAK;YAEJ,QAAQ,KAAK,QAAQ,UACpB,oBAAC,YAAD;GAEE,GAAI;GACM;GACV,IAAI,YAAY,OAAO,KAAK;GAC5B,EAJK,MAIL,CACF;EACE,CAAA"}
1
+ {"version":3,"file":"menubar-Cxf3xeAt.js","names":[],"sources":["../src/components/menubar/use-menu-navigation.ts","../src/components/menubar/menubar.tsx"],"sourcesContent":["import React, { useEffect, useRef } from \"react\";\n\ntype UseMenuNavigationProps = {\n menuRef: React.RefObject<HTMLElement | null>;\n direction?: \"horizontal\" | \"vertical\"; // Default: horizontal\n};\n\nexport const useMenuNavigation = ({\n menuRef,\n direction = \"horizontal\",\n}: UseMenuNavigationProps) => {\n const activeElementRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!menuRef.current) return;\n\n const focusableElements = Array.from(\n menuRef.current.querySelectorAll(\n 'a, button, input, textarea, select, details, [tabindex]:not([tabindex=\"-1\"])'\n )\n ) as HTMLElement[];\n\n if (focusableElements.length === 0) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!activeElementRef.current) return;\n\n const currentIndex = focusableElements.indexOf(activeElementRef.current);\n let nextIndex = currentIndex;\n\n const isHorizontal = direction === \"horizontal\";\n const forwardKey = isHorizontal ? \"ArrowRight\" : \"ArrowDown\";\n const backwardKey = isHorizontal ? \"ArrowLeft\" : \"ArrowUp\";\n\n if (e.key === forwardKey) {\n e.preventDefault();\n nextIndex = (currentIndex + 1) % focusableElements.length;\n } else if (e.key === backwardKey) {\n e.preventDefault();\n nextIndex =\n (currentIndex - 1 + focusableElements.length) %\n focusableElements.length;\n } else {\n return;\n }\n\n const nextElement = focusableElements[nextIndex];\n activeElementRef.current = nextElement;\n nextElement.focus();\n };\n\n const addKeyListener = () =>\n document.addEventListener(\"keydown\", handleKeyDown);\n const removeKeyListener = () =>\n document.removeEventListener(\"keydown\", handleKeyDown);\n\n const handleFocusIn = () => {\n activeElementRef.current = document.activeElement as HTMLElement;\n addKeyListener();\n };\n\n const handleFocusOut = () => {\n activeElementRef.current = null;\n removeKeyListener();\n };\n\n menuRef.current.addEventListener(\"focusin\", handleFocusIn);\n menuRef.current.addEventListener(\"focusout\", handleFocusOut);\n\n return () => {\n menuRef.current?.removeEventListener(\"focusin\", handleFocusIn);\n menuRef.current?.removeEventListener(\"focusout\", handleFocusOut);\n removeKeyListener();\n };\n }, [menuRef, direction]);\n};\n","import { IconContext } from \"@phosphor-icons/react\";\nimport React, { useRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Tooltip } from \"../tooltip\";\nimport { useMenuNavigation } from \"./use-menu-navigation\";\n\n/** MenuBar variant definitions (currently empty, reserved for future additions). */\nexport const SF_MENUBAR_VARIANTS = {\n // MenuBar currently has no variant options but structure is ready for future additions\n} as const;\n\nexport const SF_MENUBAR_DEFAULT_VARIANTS = {} as const;\n\n// Derived types from SF_MENUBAR_VARIANTS\nexport interface SFMenuBarVariantsProps {}\n\nexport function menuBarVariants(_props: SFMenuBarVariantsProps = {}) {\n return cn(\n // Base styles\n \"flex rounded-lg border border-sf-fill bg-sf-fill pl-px shadow-xs transition-colors\"\n );\n}\n\n/** Props for an individual menu option within a MenuBar. */\ntype MenuOptionProps = {\n /** Icon element (typically from `@phosphor-icons/react`) rendered at 18px */\n icon: React.ReactNode;\n /** Unique identifier for the option (used when `optionIds` is true) */\n id?: number | string;\n /** Currently active value from the parent MenuBar */\n isActive?: number | boolean | string | undefined;\n /** Callback when this option is clicked */\n onClick: () => void;\n /** Tooltip text shown on hover */\n tooltip: string;\n};\n\nconst MenuOption = ({\n icon,\n id,\n isActive,\n onClick,\n tooltip,\n}: MenuOptionProps) => {\n return (\n <Tooltip content={tooltip}>\n <button\n className={cn(\n \"focus:inset-ring-focus relative -ml-px flex h-full w-11 cursor-pointer items-center justify-center rounded-md border-none bg-sf-fill first:rounded-l-lg last:rounded-r-lg transition-colors focus:z-10 focus:outline-none focus-visible:z-10 focus-visible:inset-ring-[0.5]\",\n {\n \"z-20 bg-sf-base shadow-xs transition-colors\": isActive === id,\n }\n )}\n onClick={onClick}\n >\n <IconContext.Provider value={{ size: 18 }} {...({} as any)}>\n {icon}\n </IconContext.Provider>\n </button>\n </Tooltip>\n );\n};\n\n/**\n * MenuBar component props.\n *\n * Horizontal icon-button toolbar with keyboard navigation and tooltip labels.\n *\n * @example\n * ```tsx\n * <MenuBar\n * isActive={activeIndex}\n * options={[\n * { icon: <ListIcon />, tooltip: \"List view\", onClick: () => setView(\"list\") },\n * { icon: <GridFourIcon />, tooltip: \"Grid view\", onClick: () => setView(\"grid\") },\n * ]}\n * />\n * ```\n */\ntype MenuBarProps = {\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** The currently active option value — matched against option index or `id`. */\n isActive: number | boolean | string | undefined;\n /** Array of menu option configurations. */\n options: MenuOptionProps[];\n /** When true, each option's `id` field is used for matching instead of its array index. */\n optionIds?: boolean;\n};\n\n/**\n * MenuBar — horizontal icon-button toolbar with keyboard arrow-key navigation.\n *\n * Each option renders as a `<button>` with a Tooltip. The active option is\n * visually highlighted with an elevated background.\n *\n * @example\n * ```tsx\n * <MenuBar\n * isActive={0}\n * options={[\n * { icon: <ListIcon />, tooltip: \"List\", onClick: () => {} },\n * { icon: <GridFourIcon />, tooltip: \"Grid\", onClick: () => {} },\n * ]}\n * />\n * ```\n */\nexport const MenuBar = ({\n className,\n isActive,\n options,\n optionIds = false, // if option needs an extra unique ID\n}: MenuBarProps) => {\n const menuRef = useRef<HTMLElement | null>(null);\n\n useMenuNavigation({ menuRef, direction: \"horizontal\" });\n\n return (\n <nav\n className={cn(\n \"flex rounded-lg border border-sf-fill bg-sf-fill pl-px shadow-xs transition-colors\",\n className\n )}\n ref={menuRef}\n >\n {options.map((option, index) => (\n <MenuOption\n key={index}\n {...option}\n isActive={isActive}\n id={optionIds ? option.id : index}\n />\n ))}\n </nav>\n );\n};\n"],"mappings":";;;;;;;AAOA,IAAa,qBAAqB,EAChC,SACA,YAAY,mBACgB;CAC5B,MAAM,mBAAmB,OAA2B,KAAK;AAEzD,iBAAgB;AACd,MAAI,CAAC,QAAQ,QAAS;EAEtB,MAAM,oBAAoB,MAAM,KAC9B,QAAQ,QAAQ,iBACd,iFACD,CACF;AAED,MAAI,kBAAkB,WAAW,EAAG;EAEpC,MAAM,iBAAiB,MAAqB;AAC1C,OAAI,CAAC,iBAAiB,QAAS;GAE/B,MAAM,eAAe,kBAAkB,QAAQ,iBAAiB,QAAQ;GACxE,IAAI,YAAY;GAEhB,MAAM,eAAe,cAAc;GACnC,MAAM,aAAa,eAAe,eAAe;GACjD,MAAM,cAAc,eAAe,cAAc;AAEjD,OAAI,EAAE,QAAQ,YAAY;AACxB,MAAE,gBAAgB;AAClB,iBAAa,eAAe,KAAK,kBAAkB;cAC1C,EAAE,QAAQ,aAAa;AAChC,MAAE,gBAAgB;AAClB,iBACG,eAAe,IAAI,kBAAkB,UACtC,kBAAkB;SAEpB;GAGF,MAAM,cAAc,kBAAkB;AACtC,oBAAiB,UAAU;AAC3B,eAAY,OAAO;;EAGrB,MAAM,uBACJ,SAAS,iBAAiB,WAAW,cAAc;EACrD,MAAM,0BACJ,SAAS,oBAAoB,WAAW,cAAc;EAExD,MAAM,sBAAsB;AAC1B,oBAAiB,UAAU,SAAS;AACpC,mBAAgB;;EAGlB,MAAM,uBAAuB;AAC3B,oBAAiB,UAAU;AAC3B,sBAAmB;;AAGrB,UAAQ,QAAQ,iBAAiB,WAAW,cAAc;AAC1D,UAAQ,QAAQ,iBAAiB,YAAY,eAAe;AAE5D,eAAa;AACX,WAAQ,SAAS,oBAAoB,WAAW,cAAc;AAC9D,WAAQ,SAAS,oBAAoB,YAAY,eAAe;AAChE,sBAAmB;;IAEpB,CAAC,SAAS,UAAU,CAAC;;;;ACpC1B,IAAM,cAAc,EAClB,MACA,IACA,UACA,SACA,cACqB;AACrB,QACE,oBAAC,SAAD;EAAS,SAAS;YAChB,oBAAC,UAAD;GACE,WAAW,GACT,+QACA,EACE,+CAA+C,aAAa,IAC7D,CACF;GACQ;aAET,oBAAC,YAAY,UAAb;IAAsB,OAAO,EAAE,MAAM,IAAI;cACtC;IACoB,CAAA;GAChB,CAAA;EACD,CAAA;;;;;;;;;;;;;;;;;;;AAgDd,IAAa,WAAW,EACtB,WACA,UACA,SACA,YAAY,YACM;CAClB,MAAM,UAAU,OAA2B,KAAK;AAEhD,mBAAkB;EAAE;EAAS,WAAW;EAAc,CAAC;AAEvD,QACE,oBAAC,OAAD;EACE,WAAW,GACT,sFACA,UACD;EACD,KAAK;YAEJ,QAAQ,KAAK,QAAQ,UACpB,oBAAC,YAAD;GAEE,GAAI;GACM;GACV,IAAI,YAAY,OAAO,KAAK;GAC5B,EAJK,MAIL,CACF;EACE,CAAA"}
@@ -34,4 +34,4 @@ function Meter$1({ value, customValue, label, showValue = true, className, track
34
34
  //#endregion
35
35
  export { Meter$1 as t };
36
36
 
37
- //# sourceMappingURL=meter-CpmTenEr.js.map
37
+ //# sourceMappingURL=meter-BFFe9l5b.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"meter-CpmTenEr.js","names":[],"sources":["../src/components/meter/meter.tsx"],"sourcesContent":["import { Meter as BaseMeter } from \"@base-ui/react/meter\";\nimport { type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Meter variant definitions (currently empty, reserved for future additions). */\nexport const SF_METER_VARIANTS = {\n // Meter currently has no variant options but structure is ready for future additions\n} as const;\n\nexport const SF_METER_DEFAULT_VARIANTS = {} as const;\n\n// Derived types from SF_METER_VARIANTS\nexport interface SFMeterVariantsProps {}\n\nexport function meterVariants(_props: SFMeterVariantsProps = {}) {\n return cn(\n // Base styles\n \"flex w-full flex-col gap-2\"\n );\n}\n\ntype RootProps = ComponentPropsWithoutRef<typeof BaseMeter.Root>;\n\n/**\n * Meter component props.\n *\n * @example\n * ```tsx\n * <Meter label=\"Storage used\" value={65} />\n * <Meter label=\"API requests\" value={75} customValue=\"750 / 1,000\" />\n * ```\n */\nexport interface MeterProps extends RootProps, SFMeterVariantsProps {\n /** Custom formatted value text (e.g. \"750 / 1,000\") displayed instead of percentage. */\n customValue?: string;\n /** Label text displayed above the meter track. */\n label: string;\n /**\n * Whether to display the percentage value next to the label.\n * @default true\n */\n showValue?: boolean;\n /** Additional CSS classes for the track (background bar). */\n trackClassName?: string;\n /** Additional CSS classes for the indicator (filled bar). */\n indicatorClassName?: string;\n}\n\n/**\n * Progress bar showing a measured value within a known range (e.g. quota usage).\n *\n * @example\n * ```tsx\n * <Meter label=\"Storage\" value={65} />\n * ```\n */\nexport function Meter({\n value,\n customValue,\n label,\n showValue = true,\n className,\n trackClassName,\n indicatorClassName,\n ...props\n}: MeterProps) {\n return (\n <BaseMeter.Root\n value={value}\n {...props}\n className={cn(\"flex w-full flex-col gap-2\", className)}\n >\n <div className=\"flex items-center justify-between gap-4\">\n <BaseMeter.Label className=\"text-xs text-sf-strong\">\n {label}\n </BaseMeter.Label>\n {customValue ? (\n <span className=\"text-sm font-medium text-sf-default tabular-nums\">\n {customValue}\n </span>\n ) : (\n <>\n {showValue && (\n <BaseMeter.Value className=\"text-sm font-medium text-sf-default tabular-nums\" />\n )}\n </>\n )}\n </div>\n <BaseMeter.Track\n className={cn(\n \"relative h-2 w-full overflow-hidden rounded-full bg-sf-fill\",\n trackClassName\n )}\n >\n <BaseMeter.Indicator\n className={cn(\n \"absolute inset-y-0 left-0 rounded-full bg-linear-to-r from-sf-brand via-sf-brand to-sf-brand transition-[width] duration-300 ease-out\",\n indicatorClassName\n )}\n />\n </BaseMeter.Track>\n </BaseMeter.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAyDA,SAAgB,QAAM,EACpB,OACA,aACA,OACA,YAAY,MACZ,WACA,gBACA,oBACA,GAAG,SACU;AACb,QACE,qBAAC,MAAU,MAAX;EACS;EACP,GAAI;EACJ,WAAW,GAAG,8BAA8B,UAAU;YAHxD,CAKE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,MAAU,OAAX;IAAiB,WAAU;cACxB;IACe,CAAA,EACjB,cACC,oBAAC,QAAD;IAAM,WAAU;cACb;IACI,CAAA,GAEP,oBAAA,UAAA,EAAA,UACG,aACC,oBAAC,MAAU,OAAX,EAAiB,WAAU,oDAAqD,CAAA,EAEjF,CAAA,CAED;MACN,oBAAC,MAAU,OAAX;GACE,WAAW,GACT,+DACA,eACD;aAED,oBAAC,MAAU,WAAX,EACE,WAAW,GACT,yIACA,mBACD,EACD,CAAA;GACc,CAAA,CACH"}
1
+ {"version":3,"file":"meter-BFFe9l5b.js","names":[],"sources":["../src/components/meter/meter.tsx"],"sourcesContent":["import { Meter as BaseMeter } from \"@base-ui/react/meter\";\nimport { type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Meter variant definitions (currently empty, reserved for future additions). */\nexport const SF_METER_VARIANTS = {\n // Meter currently has no variant options but structure is ready for future additions\n} as const;\n\nexport const SF_METER_DEFAULT_VARIANTS = {} as const;\n\n// Derived types from SF_METER_VARIANTS\nexport interface SFMeterVariantsProps {}\n\nexport function meterVariants(_props: SFMeterVariantsProps = {}) {\n return cn(\n // Base styles\n \"flex w-full flex-col gap-2\"\n );\n}\n\ntype RootProps = ComponentPropsWithoutRef<typeof BaseMeter.Root>;\n\n/**\n * Meter component props.\n *\n * @example\n * ```tsx\n * <Meter label=\"Storage used\" value={65} />\n * <Meter label=\"API requests\" value={75} customValue=\"750 / 1,000\" />\n * ```\n */\nexport interface MeterProps extends RootProps, SFMeterVariantsProps {\n /** Custom formatted value text (e.g. \"750 / 1,000\") displayed instead of percentage. */\n customValue?: string;\n /** Label text displayed above the meter track. */\n label: string;\n /**\n * Whether to display the percentage value next to the label.\n * @default true\n */\n showValue?: boolean;\n /** Additional CSS classes for the track (background bar). */\n trackClassName?: string;\n /** Additional CSS classes for the indicator (filled bar). */\n indicatorClassName?: string;\n}\n\n/**\n * Progress bar showing a measured value within a known range (e.g. quota usage).\n *\n * @example\n * ```tsx\n * <Meter label=\"Storage\" value={65} />\n * ```\n */\nexport function Meter({\n value,\n customValue,\n label,\n showValue = true,\n className,\n trackClassName,\n indicatorClassName,\n ...props\n}: MeterProps) {\n return (\n <BaseMeter.Root\n value={value}\n {...props}\n className={cn(\"flex w-full flex-col gap-2\", className)}\n >\n <div className=\"flex items-center justify-between gap-4\">\n <BaseMeter.Label className=\"text-xs text-sf-strong\">\n {label}\n </BaseMeter.Label>\n {customValue ? (\n <span className=\"text-sm font-medium text-sf-default tabular-nums\">\n {customValue}\n </span>\n ) : (\n <>\n {showValue && (\n <BaseMeter.Value className=\"text-sm font-medium text-sf-default tabular-nums\" />\n )}\n </>\n )}\n </div>\n <BaseMeter.Track\n className={cn(\n \"relative h-2 w-full overflow-hidden rounded-full bg-sf-fill\",\n trackClassName\n )}\n >\n <BaseMeter.Indicator\n className={cn(\n \"absolute inset-y-0 left-0 rounded-full bg-linear-to-r from-sf-brand via-sf-brand to-sf-brand transition-[width] duration-300 ease-out\",\n indicatorClassName\n )}\n />\n </BaseMeter.Track>\n </BaseMeter.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAyDA,SAAgB,QAAM,EACpB,OACA,aACA,OACA,YAAY,MACZ,WACA,gBACA,oBACA,GAAG,SACU;AACb,QACE,qBAAC,MAAU,MAAX;EACS;EACP,GAAI;EACJ,WAAW,GAAG,8BAA8B,UAAU;YAHxD,CAKE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,MAAU,OAAX;IAAiB,WAAU;cACxB;IACe,CAAA,EACjB,cACC,oBAAC,QAAD;IAAM,WAAU;cACb;IACI,CAAA,GAEP,oBAAA,UAAA,EAAA,UACG,aACC,oBAAC,MAAU,OAAX,EAAiB,WAAU,oDAAqD,CAAA,EAEjF,CAAA,CAED;MACN,oBAAC,MAAU,OAAX;GACE,WAAW,GACT,+DACA,eACD;aAED,oBAAC,MAAU,WAAX,EACE,WAAW,GACT,yIACA,mBACD,EACD,CAAA;GACc,CAAA,CACH"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as InputGroup } from "./input-DddtBN-g.js";
2
+ import { t as InputGroup } from "./input-ClB_E4Lb.js";
3
3
  import { useEffect, useMemo, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { CaretDoubleLeftIcon, CaretDoubleRightIcon, CaretLeftIcon, CaretRightIcon } from "@phosphor-icons/react";
@@ -114,4 +114,4 @@ function Pagination({ page = 1, perPage, totalCount, setPage, text, controls = S
114
114
  //#endregion
115
115
  export { Pagination as t };
116
116
 
117
- //# sourceMappingURL=pagination-BVqdlONY.js.map
117
+ //# sourceMappingURL=pagination-yS372Tr4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination-BVqdlONY.js","names":[],"sources":["../src/components/pagination/pagination.tsx"],"sourcesContent":["import {\n CaretDoubleLeftIcon,\n CaretDoubleRightIcon,\n CaretLeftIcon,\n CaretRightIcon,\n} from \"@phosphor-icons/react\";\nimport { useEffect, useMemo, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { InputGroup } from \"../input\";\n\n/** Pagination controls variant definitions. */\nexport const SF_PAGINATION_VARIANTS = {\n controls: {\n full: {\n classes: \"\",\n description:\n \"Full pagination controls with first, previous, page input, next, and last buttons\",\n },\n simple: {\n classes: \"\",\n description:\n \"Simple pagination controls with only previous and next buttons\",\n },\n },\n} as const;\n\nexport type SFPaginationControls = keyof typeof SF_PAGINATION_VARIANTS.controls;\n\nexport const SF_PAGINATION_DEFAULT_VARIANTS = {\n controls: \"full\",\n} as const;\n\nexport interface SFPaginationVariantsProps {\n controls?: SFPaginationControls;\n}\n\nexport function paginationVariants({\n controls = SF_PAGINATION_DEFAULT_VARIANTS.controls,\n}: SFPaginationVariantsProps = {}) {\n return cn(\n \"flex items-center justify-between gap-2\",\n SF_PAGINATION_VARIANTS.controls[controls].classes\n );\n}\n\n/**\n * Pagination component props.\n *\n * @example\n * ```tsx\n * <Pagination page={page} setPage={setPage} perPage={10} totalCount={100} />\n * <Pagination page={page} setPage={setPage} perPage={10} totalCount={100} controls=\"simple\" />\n * ```\n */\nexport interface PaginationProps extends SFPaginationVariantsProps {\n /** Callback fired when the current page changes. */\n setPage: (page: number) => void;\n /**\n * Current page number (1-indexed).\n * @default 1\n */\n page?: number;\n /** Number of items displayed per page. */\n perPage?: number;\n /** Total number of items across all pages. */\n totalCount?: number;\n /** Method to provide custom pagination text */\n text?: (props: {\n page?: number;\n perPage?: number;\n totalCount?: number;\n pageShowingRange: string;\n }) => React.ReactNode;\n}\n\n/**\n * Page navigation controls with page count display.\n *\n * @example\n * ```tsx\n * <Pagination page={page} setPage={setPage} perPage={10} totalCount={100} />\n * ```\n */\nexport function Pagination({\n page = 1,\n perPage,\n totalCount,\n setPage,\n text,\n controls = SF_PAGINATION_DEFAULT_VARIANTS.controls,\n}: PaginationProps) {\n const [editingPage, setEditingPage] = useState<number>(1);\n\n // Value of the input as its being modified to display in the input, eventually syncs with `pagination.page`\n useEffect(() => {\n setEditingPage(page);\n }, [page]);\n\n const pageShowingRange = useMemo(() => {\n let lower = page * (perPage ?? 1) - (perPage ?? 0) + 1;\n let upper = Math.min(page * (perPage ?? 0), totalCount ?? 0);\n\n if (Number.isNaN(lower)) lower = 0;\n if (Number.isNaN(upper)) upper = 0;\n\n return `${lower}-${upper}`;\n }, [page, perPage, totalCount]);\n\n const maxPage = useMemo(() => {\n return Math.ceil((totalCount ?? 1) / (perPage ?? 1));\n }, [totalCount, perPage]);\n\n const getPaginationText = () => {\n if (text) {\n return text({ page, perPage, totalCount, pageShowingRange });\n } else if (totalCount && totalCount > 0) {\n return `Showing ${pageShowingRange} of ${totalCount}`;\n }\n return null;\n };\n\n return (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"grow text-sm text-sf-strong\">{getPaginationText()}</div>\n <div>\n <InputGroup focusMode=\"individual\">\n {controls === \"full\" && (\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"First page\"\n disabled={page <= 1}\n onClick={() => {\n setPage(1);\n setEditingPage(1);\n }}\n >\n <CaretDoubleLeftIcon size={16} />\n </InputGroup.Button>\n )}\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"Previous page\"\n disabled={page <= 1}\n onClick={() => {\n const previousPage = Math.max(page - 1, 1);\n setPage(previousPage);\n setEditingPage(previousPage);\n }}\n >\n <CaretLeftIcon size={16} />\n </InputGroup.Button>\n {controls === \"full\" && (\n <InputGroup.Input\n style={{ width: 50 }}\n className=\"text-center\"\n aria-label=\"Page number\"\n value={editingPage}\n onValueChange={(value: string) => {\n setEditingPage(Number(value));\n }}\n onBlur={() => {\n let number = Math.max(editingPage, 1);\n number = Math.min(number, maxPage);\n setPage(number);\n setEditingPage(number);\n }}\n />\n )}\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"Next page\"\n disabled={page === maxPage}\n onClick={() => {\n const nextPage = Math.min(page + 1, maxPage);\n setPage(nextPage);\n setEditingPage(nextPage);\n }}\n >\n <CaretRightIcon size={16} />\n </InputGroup.Button>\n {controls === \"full\" && (\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"Last page\"\n disabled={page === maxPage}\n onClick={() => {\n setPage(maxPage);\n setEditingPage(maxPage);\n }}\n >\n <CaretDoubleRightIcon size={16} />\n </InputGroup.Button>\n )}\n </InputGroup>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AA6BA,IAAa,iCAAiC,EAC5C,UAAU,QACX;;;;;;;;;AAqDD,SAAgB,WAAW,EACzB,OAAO,GACP,SACA,YACA,SACA,MACA,WAAW,+BAA+B,YACxB;CAClB,MAAM,CAAC,aAAa,kBAAkB,SAAiB,EAAE;AAGzD,iBAAgB;AACd,iBAAe,KAAK;IACnB,CAAC,KAAK,CAAC;CAEV,MAAM,mBAAmB,cAAc;EACrC,IAAI,QAAQ,QAAQ,WAAW,MAAM,WAAW,KAAK;EACrD,IAAI,QAAQ,KAAK,IAAI,QAAQ,WAAW,IAAI,cAAc,EAAE;AAE5D,MAAI,OAAO,MAAM,MAAM,CAAE,SAAQ;AACjC,MAAI,OAAO,MAAM,MAAM,CAAE,SAAQ;AAEjC,SAAO,GAAG,MAAM,GAAG;IAClB;EAAC;EAAM;EAAS;EAAW,CAAC;CAE/B,MAAM,UAAU,cAAc;AAC5B,SAAO,KAAK,MAAM,cAAc,MAAM,WAAW,GAAG;IACnD,CAAC,YAAY,QAAQ,CAAC;CAEzB,MAAM,0BAA0B;AAC9B,MAAI,KACF,QAAO,KAAK;GAAE;GAAM;GAAS;GAAY;GAAkB,CAAC;WACnD,cAAc,aAAa,EACpC,QAAO,WAAW,iBAAiB,MAAM;AAE3C,SAAO;;AAGT,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,OAAD;GAAK,WAAU;aAA+B,mBAAmB;GAAO,CAAA,EACxE,oBAAC,OAAD,EAAA,UACE,qBAAC,YAAD;GAAY,WAAU;aAAtB;IACG,aAAa,UACZ,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,QAAQ;KAClB,eAAe;AACb,cAAQ,EAAE;AACV,qBAAe,EAAE;;eAGnB,oBAAC,qBAAD,EAAqB,MAAM,IAAM,CAAA;KACf,CAAA;IAEtB,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,QAAQ;KAClB,eAAe;MACb,MAAM,eAAe,KAAK,IAAI,OAAO,GAAG,EAAE;AAC1C,cAAQ,aAAa;AACrB,qBAAe,aAAa;;eAG9B,oBAAC,eAAD,EAAe,MAAM,IAAM,CAAA;KACT,CAAA;IACnB,aAAa,UACZ,oBAAC,WAAW,OAAZ;KACE,OAAO,EAAE,OAAO,IAAI;KACpB,WAAU;KACV,cAAW;KACX,OAAO;KACP,gBAAgB,UAAkB;AAChC,qBAAe,OAAO,MAAM,CAAC;;KAE/B,cAAc;MACZ,IAAI,SAAS,KAAK,IAAI,aAAa,EAAE;AACrC,eAAS,KAAK,IAAI,QAAQ,QAAQ;AAClC,cAAQ,OAAO;AACf,qBAAe,OAAO;;KAExB,CAAA;IAEJ,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,SAAS;KACnB,eAAe;MACb,MAAM,WAAW,KAAK,IAAI,OAAO,GAAG,QAAQ;AAC5C,cAAQ,SAAS;AACjB,qBAAe,SAAS;;eAG1B,oBAAC,gBAAD,EAAgB,MAAM,IAAM,CAAA;KACV,CAAA;IACnB,aAAa,UACZ,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,SAAS;KACnB,eAAe;AACb,cAAQ,QAAQ;AAChB,qBAAe,QAAQ;;eAGzB,oBAAC,sBAAD,EAAsB,MAAM,IAAM,CAAA;KAChB,CAAA;IAEX;MACT,CAAA,CACF"}
1
+ {"version":3,"file":"pagination-yS372Tr4.js","names":[],"sources":["../src/components/pagination/pagination.tsx"],"sourcesContent":["import {\n CaretDoubleLeftIcon,\n CaretDoubleRightIcon,\n CaretLeftIcon,\n CaretRightIcon,\n} from \"@phosphor-icons/react\";\nimport { useEffect, useMemo, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { InputGroup } from \"../input\";\n\n/** Pagination controls variant definitions. */\nexport const SF_PAGINATION_VARIANTS = {\n controls: {\n full: {\n classes: \"\",\n description:\n \"Full pagination controls with first, previous, page input, next, and last buttons\",\n },\n simple: {\n classes: \"\",\n description:\n \"Simple pagination controls with only previous and next buttons\",\n },\n },\n} as const;\n\nexport type SFPaginationControls = keyof typeof SF_PAGINATION_VARIANTS.controls;\n\nexport const SF_PAGINATION_DEFAULT_VARIANTS = {\n controls: \"full\",\n} as const;\n\nexport interface SFPaginationVariantsProps {\n controls?: SFPaginationControls;\n}\n\nexport function paginationVariants({\n controls = SF_PAGINATION_DEFAULT_VARIANTS.controls,\n}: SFPaginationVariantsProps = {}) {\n return cn(\n \"flex items-center justify-between gap-2\",\n SF_PAGINATION_VARIANTS.controls[controls].classes\n );\n}\n\n/**\n * Pagination component props.\n *\n * @example\n * ```tsx\n * <Pagination page={page} setPage={setPage} perPage={10} totalCount={100} />\n * <Pagination page={page} setPage={setPage} perPage={10} totalCount={100} controls=\"simple\" />\n * ```\n */\nexport interface PaginationProps extends SFPaginationVariantsProps {\n /** Callback fired when the current page changes. */\n setPage: (page: number) => void;\n /**\n * Current page number (1-indexed).\n * @default 1\n */\n page?: number;\n /** Number of items displayed per page. */\n perPage?: number;\n /** Total number of items across all pages. */\n totalCount?: number;\n /** Method to provide custom pagination text */\n text?: (props: {\n page?: number;\n perPage?: number;\n totalCount?: number;\n pageShowingRange: string;\n }) => React.ReactNode;\n}\n\n/**\n * Page navigation controls with page count display.\n *\n * @example\n * ```tsx\n * <Pagination page={page} setPage={setPage} perPage={10} totalCount={100} />\n * ```\n */\nexport function Pagination({\n page = 1,\n perPage,\n totalCount,\n setPage,\n text,\n controls = SF_PAGINATION_DEFAULT_VARIANTS.controls,\n}: PaginationProps) {\n const [editingPage, setEditingPage] = useState<number>(1);\n\n // Value of the input as its being modified to display in the input, eventually syncs with `pagination.page`\n useEffect(() => {\n setEditingPage(page);\n }, [page]);\n\n const pageShowingRange = useMemo(() => {\n let lower = page * (perPage ?? 1) - (perPage ?? 0) + 1;\n let upper = Math.min(page * (perPage ?? 0), totalCount ?? 0);\n\n if (Number.isNaN(lower)) lower = 0;\n if (Number.isNaN(upper)) upper = 0;\n\n return `${lower}-${upper}`;\n }, [page, perPage, totalCount]);\n\n const maxPage = useMemo(() => {\n return Math.ceil((totalCount ?? 1) / (perPage ?? 1));\n }, [totalCount, perPage]);\n\n const getPaginationText = () => {\n if (text) {\n return text({ page, perPage, totalCount, pageShowingRange });\n } else if (totalCount && totalCount > 0) {\n return `Showing ${pageShowingRange} of ${totalCount}`;\n }\n return null;\n };\n\n return (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"grow text-sm text-sf-strong\">{getPaginationText()}</div>\n <div>\n <InputGroup focusMode=\"individual\">\n {controls === \"full\" && (\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"First page\"\n disabled={page <= 1}\n onClick={() => {\n setPage(1);\n setEditingPage(1);\n }}\n >\n <CaretDoubleLeftIcon size={16} />\n </InputGroup.Button>\n )}\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"Previous page\"\n disabled={page <= 1}\n onClick={() => {\n const previousPage = Math.max(page - 1, 1);\n setPage(previousPage);\n setEditingPage(previousPage);\n }}\n >\n <CaretLeftIcon size={16} />\n </InputGroup.Button>\n {controls === \"full\" && (\n <InputGroup.Input\n style={{ width: 50 }}\n className=\"text-center\"\n aria-label=\"Page number\"\n value={editingPage}\n onValueChange={(value: string) => {\n setEditingPage(Number(value));\n }}\n onBlur={() => {\n let number = Math.max(editingPage, 1);\n number = Math.min(number, maxPage);\n setPage(number);\n setEditingPage(number);\n }}\n />\n )}\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"Next page\"\n disabled={page === maxPage}\n onClick={() => {\n const nextPage = Math.min(page + 1, maxPage);\n setPage(nextPage);\n setEditingPage(nextPage);\n }}\n >\n <CaretRightIcon size={16} />\n </InputGroup.Button>\n {controls === \"full\" && (\n <InputGroup.Button\n variant=\"secondary\"\n aria-label=\"Last page\"\n disabled={page === maxPage}\n onClick={() => {\n setPage(maxPage);\n setEditingPage(maxPage);\n }}\n >\n <CaretDoubleRightIcon size={16} />\n </InputGroup.Button>\n )}\n </InputGroup>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AA6BA,IAAa,iCAAiC,EAC5C,UAAU,QACX;;;;;;;;;AAqDD,SAAgB,WAAW,EACzB,OAAO,GACP,SACA,YACA,SACA,MACA,WAAW,+BAA+B,YACxB;CAClB,MAAM,CAAC,aAAa,kBAAkB,SAAiB,EAAE;AAGzD,iBAAgB;AACd,iBAAe,KAAK;IACnB,CAAC,KAAK,CAAC;CAEV,MAAM,mBAAmB,cAAc;EACrC,IAAI,QAAQ,QAAQ,WAAW,MAAM,WAAW,KAAK;EACrD,IAAI,QAAQ,KAAK,IAAI,QAAQ,WAAW,IAAI,cAAc,EAAE;AAE5D,MAAI,OAAO,MAAM,MAAM,CAAE,SAAQ;AACjC,MAAI,OAAO,MAAM,MAAM,CAAE,SAAQ;AAEjC,SAAO,GAAG,MAAM,GAAG;IAClB;EAAC;EAAM;EAAS;EAAW,CAAC;CAE/B,MAAM,UAAU,cAAc;AAC5B,SAAO,KAAK,MAAM,cAAc,MAAM,WAAW,GAAG;IACnD,CAAC,YAAY,QAAQ,CAAC;CAEzB,MAAM,0BAA0B;AAC9B,MAAI,KACF,QAAO,KAAK;GAAE;GAAM;GAAS;GAAY;GAAkB,CAAC;WACnD,cAAc,aAAa,EACpC,QAAO,WAAW,iBAAiB,MAAM;AAE3C,SAAO;;AAGT,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,OAAD;GAAK,WAAU;aAA+B,mBAAmB;GAAO,CAAA,EACxE,oBAAC,OAAD,EAAA,UACE,qBAAC,YAAD;GAAY,WAAU;aAAtB;IACG,aAAa,UACZ,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,QAAQ;KAClB,eAAe;AACb,cAAQ,EAAE;AACV,qBAAe,EAAE;;eAGnB,oBAAC,qBAAD,EAAqB,MAAM,IAAM,CAAA;KACf,CAAA;IAEtB,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,QAAQ;KAClB,eAAe;MACb,MAAM,eAAe,KAAK,IAAI,OAAO,GAAG,EAAE;AAC1C,cAAQ,aAAa;AACrB,qBAAe,aAAa;;eAG9B,oBAAC,eAAD,EAAe,MAAM,IAAM,CAAA;KACT,CAAA;IACnB,aAAa,UACZ,oBAAC,WAAW,OAAZ;KACE,OAAO,EAAE,OAAO,IAAI;KACpB,WAAU;KACV,cAAW;KACX,OAAO;KACP,gBAAgB,UAAkB;AAChC,qBAAe,OAAO,MAAM,CAAC;;KAE/B,cAAc;MACZ,IAAI,SAAS,KAAK,IAAI,aAAa,EAAE;AACrC,eAAS,KAAK,IAAI,QAAQ,QAAQ;AAClC,cAAQ,OAAO;AACf,qBAAe,OAAO;;KAExB,CAAA;IAEJ,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,SAAS;KACnB,eAAe;MACb,MAAM,WAAW,KAAK,IAAI,OAAO,GAAG,QAAQ;AAC5C,cAAQ,SAAS;AACjB,qBAAe,SAAS;;eAG1B,oBAAC,gBAAD,EAAgB,MAAM,IAAM,CAAA;KACV,CAAA;IACnB,aAAa,UACZ,oBAAC,WAAW,QAAZ;KACE,SAAQ;KACR,cAAW;KACX,UAAU,SAAS;KACnB,eAAe;AACb,cAAQ,QAAQ;AAChB,qBAAe,QAAQ;;eAGzB,oBAAC,sBAAD,EAAsB,MAAM,IAAM,CAAA;KAChB,CAAA;IAEX;MACT,CAAA,CACF"}
@@ -144,4 +144,4 @@ var Popover$1 = Object.assign(PopoverRoot, {
144
144
  //#endregion
145
145
  export { PopoverRoot as a, SF_POPOVER_DEFAULT_VARIANTS as c, PopoverDescription as i, SF_POPOVER_VARIANTS as l, PopoverClose as n, PopoverTitle as o, PopoverContent as r, PopoverTrigger as s, Popover$1 as t };
146
146
 
147
- //# sourceMappingURL=popover-BRQZ2b6z.js.map
147
+ //# sourceMappingURL=popover-SRoJaCZr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"popover-BRQZ2b6z.js","names":[],"sources":["../src/components/popover/popover.tsx"],"sourcesContent":["import { Popover as PopoverBase } from \"@base-ui/react/popover\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Popover side variant definitions mapping positions to their Tailwind classes. */\nexport const SF_POPOVER_VARIANTS = {\n side: {\n top: {\n classes: \"\",\n description: \"Popover appears above the trigger\",\n },\n bottom: {\n classes: \"\",\n description: \"Popover appears below the trigger\",\n },\n left: {\n classes: \"\",\n description: \"Popover appears to the left of the trigger\",\n },\n right: {\n classes: \"\",\n description: \"Popover appears to the right of the trigger\",\n },\n },\n} as const;\n\nexport const SF_POPOVER_DEFAULT_VARIANTS = {\n side: \"bottom\",\n} as const;\n\n// Derived types from SF_POPOVER_VARIANTS\nexport type SFPopoverSide = keyof typeof SF_POPOVER_VARIANTS.side;\n\nexport interface SFPopoverVariantsProps {\n /**\n * Which side of the trigger the popover appears on.\n * - `\"top\"` — Above the trigger\n * - `\"bottom\"` — Below the trigger\n * - `\"left\"` — Left of the trigger\n * - `\"right\"` — Right of the trigger\n * @default \"bottom\"\n */\n side?: SFPopoverSide;\n}\n\n// ============================================================================\n// Popover Root\n// ============================================================================\n\ntype BasePopoverRootProps = ComponentPropsWithoutRef<typeof PopoverBase.Root>;\n\nexport type PopoverRootProps = BasePopoverRootProps;\n\nfunction PopoverRoot({ children, ...props }: PopoverRootProps) {\n return <PopoverBase.Root {...props}>{children}</PopoverBase.Root>;\n}\n\nPopoverRoot.displayName = \"Popover\";\n\n// ============================================================================\n// Popover Trigger\n// ============================================================================\n\ntype BasePopoverTriggerProps = ComponentPropsWithoutRef<\n typeof PopoverBase.Trigger\n>;\n\nexport type PopoverTriggerProps = BasePopoverTriggerProps & {\n /** When true, the trigger element will be the child element */\n asChild?: boolean;\n};\n\nfunction PopoverTrigger({\n children,\n className,\n asChild,\n ...props\n}: PopoverTriggerProps) {\n return (\n <PopoverBase.Trigger\n className={className}\n render={\n asChild ? (children as BasePopoverTriggerProps[\"render\"]) : undefined\n }\n {...props}\n >\n {asChild ? undefined : children}\n </PopoverBase.Trigger>\n );\n}\n\nPopoverTrigger.displayName = \"Popover.Trigger\";\n\n// ============================================================================\n// Popover Content\n// ============================================================================\n\n/** Alignment options for popover positioning */\ntype PopoverAlign = \"start\" | \"center\" | \"end\";\n\n/**\n * Popover content panel props.\n *\n * @example\n * ```tsx\n * <Popover.Content side=\"top\" align=\"start\" sideOffset={12}>\n * <p>Popover body</p>\n * </Popover.Content>\n * ```\n */\nexport type PopoverContentProps = SFPopoverVariantsProps & {\n /**\n * How to align the popover relative to the trigger.\n * @default \"center\"\n */\n align?: PopoverAlign;\n /**\n * Distance between the trigger and the popover in pixels.\n * @default 8\n */\n sideOffset?: number;\n /**\n * Additional offset along the alignment axis in pixels.\n * @default 0\n */\n alignOffset?: number;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** Content to render inside the popover. */\n children?: ReactNode;\n};\n\nfunction PopoverContent({\n children,\n side = SF_POPOVER_DEFAULT_VARIANTS.side,\n align = \"center\",\n sideOffset = 8,\n alignOffset = 0,\n className,\n}: PopoverContentProps) {\n return (\n <PopoverBase.Portal>\n <PopoverBase.Positioner\n align={align}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <PopoverBase.Popup\n className={cn(\n \"flex origin-(--transform-origin) flex-col rounded-lg bg-sf-elevated px-4 py-3 text-sm text-sf-default\",\n \"shadow-lg shadow-sf-tip-shadow outline outline-sf-fill\",\n \"transition-[transform,scale,opacity] duration-150\",\n \"data-starting-style:scale-90 data-starting-style:opacity-0\",\n \"data-ending-style:scale-90 data-ending-style:opacity-0\",\n \"data-instant:duration-0\",\n \"sf-popover-popup\",\n className\n )}\n >\n <PopoverBase.Arrow\n className={cn(\n \"flex\",\n \"data-[side=bottom]:-top-2\",\n \"data-[side=left]:right-[-13px] data-[side=left]:rotate-90\",\n \"data-[side=right]:left-[-13px] data-[side=right]:-rotate-90\",\n \"data-[side=top]:-bottom-2 data-[side=top]:rotate-180\"\n )}\n >\n <ArrowSvg />\n </PopoverBase.Arrow>\n {children}\n </PopoverBase.Popup>\n </PopoverBase.Positioner>\n </PopoverBase.Portal>\n );\n}\n\nPopoverContent.displayName = \"Popover.Content\";\n\n// ============================================================================\n// Popover Title\n// ============================================================================\n\ntype BasePopoverTitleProps = ComponentPropsWithoutRef<typeof PopoverBase.Title>;\n\nexport type PopoverTitleProps = BasePopoverTitleProps;\n\nfunction PopoverTitle({ className, ...props }: PopoverTitleProps) {\n return (\n <PopoverBase.Title\n className={cn(\"m-0 text-base leading-6 font-medium\", className)}\n {...props}\n />\n );\n}\n\nPopoverTitle.displayName = \"Popover.Title\";\n\n// ============================================================================\n// Popover Description\n// ============================================================================\n\ntype BasePopoverDescriptionProps = ComponentPropsWithoutRef<\n typeof PopoverBase.Description\n>;\n\nexport type PopoverDescriptionProps = BasePopoverDescriptionProps;\n\nfunction PopoverDescription({ className, ...props }: PopoverDescriptionProps) {\n return (\n <PopoverBase.Description\n className={cn(\"m-0 text-base leading-6 text-sf-subtle\", className)}\n {...props}\n />\n );\n}\n\nPopoverDescription.displayName = \"Popover.Description\";\n\n// ============================================================================\n// Popover Close\n// ============================================================================\n\ntype BasePopoverCloseProps = ComponentPropsWithoutRef<typeof PopoverBase.Close>;\n\nexport type PopoverCloseProps = BasePopoverCloseProps & {\n /** When true, the close element will be the child element */\n asChild?: boolean;\n};\n\nfunction PopoverClose({\n children,\n className,\n asChild,\n ...props\n}: PopoverCloseProps) {\n return (\n <PopoverBase.Close\n className={className}\n render={\n asChild ? (children as BasePopoverCloseProps[\"render\"]) : undefined\n }\n {...props}\n >\n {asChild ? undefined : children}\n </PopoverBase.Close>\n );\n}\n\nPopoverClose.displayName = \"Popover.Close\";\n\n// ============================================================================\n// Arrow SVG\n// ============================================================================\n\n/**\n * Arrow SVG with three paths for proper border rendering in both light and dark modes.\n * This approach matches Base UI's popover/tooltip implementation.\n *\n * The three paths are:\n * 1. ArrowFill - The main arrow body, matches popover background\n * 2. ArrowOuterStroke - Border visible in light mode only (transparent in dark)\n * 3. ArrowInnerStroke - Border visible in dark mode only (transparent in light)\n *\n * This is necessary because the outer and inner stroke paths have different geometries,\n * and using both ensures the arrow border aligns perfectly with the popover's outline\n * in both color modes.\n *\n * @see https://base-ui.com/react/components/popover\n */\nfunction ArrowSvg(props: React.ComponentProps<\"svg\">) {\n return (\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\" fill=\"none\" {...props}>\n <path\n d=\"M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z\"\n className=\"fill-sf-elevated\"\n />\n <path\n d=\"M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z\"\n className=\"fill-sf-tip-shadow\"\n />\n <path\n d=\"M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z\"\n className=\"fill-sf-tip-stroke\"\n />\n </svg>\n );\n}\n\n// ============================================================================\n// Compound Component Export\n// ============================================================================\n\n/**\n * Popover component for displaying accessible popup content anchored to a trigger.\n *\n * @example\n * ```tsx\n * <Popover>\n * <Popover.Trigger asChild>\n * <Button>Open</Button>\n * </Popover.Trigger>\n * <Popover.Content>\n * <Popover.Title>Notifications</Popover.Title>\n * <Popover.Description>You are all caught up!</Popover.Description>\n * </Popover.Content>\n * </Popover>\n * ```\n *\n * @see https://base-ui.com/react/components/popover\n */\nexport const Popover = Object.assign(PopoverRoot, {\n Trigger: PopoverTrigger,\n Content: PopoverContent,\n Title: PopoverTitle,\n Description: PopoverDescription,\n Close: PopoverClose,\n});\n\n// Export sub-components for direct access and type inference\nexport {\n PopoverRoot,\n PopoverTrigger,\n PopoverContent,\n PopoverTitle,\n PopoverDescription,\n PopoverClose,\n};\n"],"mappings":";;;;;;AAMA,IAAa,sBAAsB,EACjC,MAAM;CACJ,KAAK;EACH,SAAS;EACT,aAAa;EACd;CACD,QAAQ;EACN,SAAS;EACT,aAAa;EACd;CACD,MAAM;EACJ,SAAS;EACT,aAAa;EACd;CACD,OAAO;EACL,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,8BAA8B,EACzC,MAAM,UACP;AAyBD,SAAS,YAAY,EAAE,UAAU,GAAG,SAA2B;AAC7D,QAAO,oBAAC,QAAY,MAAb;EAAkB,GAAI;EAAQ;EAA4B,CAAA;;AAGnE,YAAY,cAAc;AAe1B,SAAS,eAAe,EACtB,UACA,WACA,SACA,GAAG,SACmB;AACtB,QACE,oBAAC,QAAY,SAAb;EACa;EACX,QACE,UAAW,WAAiD,KAAA;EAE9D,GAAI;YAEH,UAAU,KAAA,IAAY;EACH,CAAA;;AAI1B,eAAe,cAAc;AAyC7B,SAAS,eAAe,EACtB,UACA,OAAO,4BAA4B,MACnC,QAAQ,UACR,aAAa,GACb,cAAc,GACd,aACsB;AACtB,QACE,oBAAC,QAAY,QAAb,EAAA,UACE,oBAAC,QAAY,YAAb;EACS;EACM;EACP;EACM;YAEZ,qBAAC,QAAY,OAAb;GACE,WAAW,GACT,yGACA,0DACA,qDACA,8DACA,0DACA,2BACA,oBACA,UACD;aAVH,CAYE,oBAAC,QAAY,OAAb;IACE,WAAW,GACT,QACA,6BACA,6DACA,+DACA,uDACD;cAED,oBAAC,UAAD,EAAY,CAAA;IACM,CAAA,EACnB,SACiB;;EACG,CAAA,EACN,CAAA;;AAIzB,eAAe,cAAc;AAU7B,SAAS,aAAa,EAAE,WAAW,GAAG,SAA4B;AAChE,QACE,oBAAC,QAAY,OAAb;EACE,WAAW,GAAG,uCAAuC,UAAU;EAC/D,GAAI;EACJ,CAAA;;AAIN,aAAa,cAAc;AAY3B,SAAS,mBAAmB,EAAE,WAAW,GAAG,SAAkC;AAC5E,QACE,oBAAC,QAAY,aAAb;EACE,WAAW,GAAG,0CAA0C,UAAU;EAClE,GAAI;EACJ,CAAA;;AAIN,mBAAmB,cAAc;AAajC,SAAS,aAAa,EACpB,UACA,WACA,SACA,GAAG,SACiB;AACpB,QACE,oBAAC,QAAY,OAAb;EACa;EACX,QACE,UAAW,WAA+C,KAAA;EAE5D,GAAI;YAEH,UAAU,KAAA,IAAY;EACL,CAAA;;AAIxB,aAAa,cAAc;;;;;;;;;;;;;;;;AAqB3B,SAAS,SAAS,OAAoC;AACpD,QACE,qBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EAAO,GAAI;YAAhE;GACE,oBAAC,QAAD;IACE,GAAE;IACF,WAAU;IACV,CAAA;GACF,oBAAC,QAAD;IACE,GAAE;IACF,WAAU;IACV,CAAA;GACF,oBAAC,QAAD;IACE,GAAE;IACF,WAAU;IACV,CAAA;GACE;;;;;;;;;;;;;;;;;;;;;AA0BV,IAAa,YAAU,OAAO,OAAO,aAAa;CAChD,SAAS;CACT,SAAS;CACT,OAAO;CACP,aAAa;CACb,OAAO;CACR,CAAC"}
1
+ {"version":3,"file":"popover-SRoJaCZr.js","names":[],"sources":["../src/components/popover/popover.tsx"],"sourcesContent":["import { Popover as PopoverBase } from \"@base-ui/react/popover\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Popover side variant definitions mapping positions to their Tailwind classes. */\nexport const SF_POPOVER_VARIANTS = {\n side: {\n top: {\n classes: \"\",\n description: \"Popover appears above the trigger\",\n },\n bottom: {\n classes: \"\",\n description: \"Popover appears below the trigger\",\n },\n left: {\n classes: \"\",\n description: \"Popover appears to the left of the trigger\",\n },\n right: {\n classes: \"\",\n description: \"Popover appears to the right of the trigger\",\n },\n },\n} as const;\n\nexport const SF_POPOVER_DEFAULT_VARIANTS = {\n side: \"bottom\",\n} as const;\n\n// Derived types from SF_POPOVER_VARIANTS\nexport type SFPopoverSide = keyof typeof SF_POPOVER_VARIANTS.side;\n\nexport interface SFPopoverVariantsProps {\n /**\n * Which side of the trigger the popover appears on.\n * - `\"top\"` — Above the trigger\n * - `\"bottom\"` — Below the trigger\n * - `\"left\"` — Left of the trigger\n * - `\"right\"` — Right of the trigger\n * @default \"bottom\"\n */\n side?: SFPopoverSide;\n}\n\n// ============================================================================\n// Popover Root\n// ============================================================================\n\ntype BasePopoverRootProps = ComponentPropsWithoutRef<typeof PopoverBase.Root>;\n\nexport type PopoverRootProps = BasePopoverRootProps;\n\nfunction PopoverRoot({ children, ...props }: PopoverRootProps) {\n return <PopoverBase.Root {...props}>{children}</PopoverBase.Root>;\n}\n\nPopoverRoot.displayName = \"Popover\";\n\n// ============================================================================\n// Popover Trigger\n// ============================================================================\n\ntype BasePopoverTriggerProps = ComponentPropsWithoutRef<\n typeof PopoverBase.Trigger\n>;\n\nexport type PopoverTriggerProps = BasePopoverTriggerProps & {\n /** When true, the trigger element will be the child element */\n asChild?: boolean;\n};\n\nfunction PopoverTrigger({\n children,\n className,\n asChild,\n ...props\n}: PopoverTriggerProps) {\n return (\n <PopoverBase.Trigger\n className={className}\n render={\n asChild ? (children as BasePopoverTriggerProps[\"render\"]) : undefined\n }\n {...props}\n >\n {asChild ? undefined : children}\n </PopoverBase.Trigger>\n );\n}\n\nPopoverTrigger.displayName = \"Popover.Trigger\";\n\n// ============================================================================\n// Popover Content\n// ============================================================================\n\n/** Alignment options for popover positioning */\ntype PopoverAlign = \"start\" | \"center\" | \"end\";\n\n/**\n * Popover content panel props.\n *\n * @example\n * ```tsx\n * <Popover.Content side=\"top\" align=\"start\" sideOffset={12}>\n * <p>Popover body</p>\n * </Popover.Content>\n * ```\n */\nexport type PopoverContentProps = SFPopoverVariantsProps & {\n /**\n * How to align the popover relative to the trigger.\n * @default \"center\"\n */\n align?: PopoverAlign;\n /**\n * Distance between the trigger and the popover in pixels.\n * @default 8\n */\n sideOffset?: number;\n /**\n * Additional offset along the alignment axis in pixels.\n * @default 0\n */\n alignOffset?: number;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** Content to render inside the popover. */\n children?: ReactNode;\n};\n\nfunction PopoverContent({\n children,\n side = SF_POPOVER_DEFAULT_VARIANTS.side,\n align = \"center\",\n sideOffset = 8,\n alignOffset = 0,\n className,\n}: PopoverContentProps) {\n return (\n <PopoverBase.Portal>\n <PopoverBase.Positioner\n align={align}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <PopoverBase.Popup\n className={cn(\n \"flex origin-(--transform-origin) flex-col rounded-lg bg-sf-elevated px-4 py-3 text-sm text-sf-default\",\n \"shadow-lg shadow-sf-tip-shadow outline outline-sf-fill\",\n \"transition-[transform,scale,opacity] duration-150\",\n \"data-starting-style:scale-90 data-starting-style:opacity-0\",\n \"data-ending-style:scale-90 data-ending-style:opacity-0\",\n \"data-instant:duration-0\",\n \"sf-popover-popup\",\n className\n )}\n >\n <PopoverBase.Arrow\n className={cn(\n \"flex\",\n \"data-[side=bottom]:-top-2\",\n \"data-[side=left]:right-[-13px] data-[side=left]:rotate-90\",\n \"data-[side=right]:left-[-13px] data-[side=right]:-rotate-90\",\n \"data-[side=top]:-bottom-2 data-[side=top]:rotate-180\"\n )}\n >\n <ArrowSvg />\n </PopoverBase.Arrow>\n {children}\n </PopoverBase.Popup>\n </PopoverBase.Positioner>\n </PopoverBase.Portal>\n );\n}\n\nPopoverContent.displayName = \"Popover.Content\";\n\n// ============================================================================\n// Popover Title\n// ============================================================================\n\ntype BasePopoverTitleProps = ComponentPropsWithoutRef<typeof PopoverBase.Title>;\n\nexport type PopoverTitleProps = BasePopoverTitleProps;\n\nfunction PopoverTitle({ className, ...props }: PopoverTitleProps) {\n return (\n <PopoverBase.Title\n className={cn(\"m-0 text-base leading-6 font-medium\", className)}\n {...props}\n />\n );\n}\n\nPopoverTitle.displayName = \"Popover.Title\";\n\n// ============================================================================\n// Popover Description\n// ============================================================================\n\ntype BasePopoverDescriptionProps = ComponentPropsWithoutRef<\n typeof PopoverBase.Description\n>;\n\nexport type PopoverDescriptionProps = BasePopoverDescriptionProps;\n\nfunction PopoverDescription({ className, ...props }: PopoverDescriptionProps) {\n return (\n <PopoverBase.Description\n className={cn(\"m-0 text-base leading-6 text-sf-subtle\", className)}\n {...props}\n />\n );\n}\n\nPopoverDescription.displayName = \"Popover.Description\";\n\n// ============================================================================\n// Popover Close\n// ============================================================================\n\ntype BasePopoverCloseProps = ComponentPropsWithoutRef<typeof PopoverBase.Close>;\n\nexport type PopoverCloseProps = BasePopoverCloseProps & {\n /** When true, the close element will be the child element */\n asChild?: boolean;\n};\n\nfunction PopoverClose({\n children,\n className,\n asChild,\n ...props\n}: PopoverCloseProps) {\n return (\n <PopoverBase.Close\n className={className}\n render={\n asChild ? (children as BasePopoverCloseProps[\"render\"]) : undefined\n }\n {...props}\n >\n {asChild ? undefined : children}\n </PopoverBase.Close>\n );\n}\n\nPopoverClose.displayName = \"Popover.Close\";\n\n// ============================================================================\n// Arrow SVG\n// ============================================================================\n\n/**\n * Arrow SVG with three paths for proper border rendering in both light and dark modes.\n * This approach matches Base UI's popover/tooltip implementation.\n *\n * The three paths are:\n * 1. ArrowFill - The main arrow body, matches popover background\n * 2. ArrowOuterStroke - Border visible in light mode only (transparent in dark)\n * 3. ArrowInnerStroke - Border visible in dark mode only (transparent in light)\n *\n * This is necessary because the outer and inner stroke paths have different geometries,\n * and using both ensures the arrow border aligns perfectly with the popover's outline\n * in both color modes.\n *\n * @see https://base-ui.com/react/components/popover\n */\nfunction ArrowSvg(props: React.ComponentProps<\"svg\">) {\n return (\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\" fill=\"none\" {...props}>\n <path\n d=\"M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z\"\n className=\"fill-sf-elevated\"\n />\n <path\n d=\"M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z\"\n className=\"fill-sf-tip-shadow\"\n />\n <path\n d=\"M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z\"\n className=\"fill-sf-tip-stroke\"\n />\n </svg>\n );\n}\n\n// ============================================================================\n// Compound Component Export\n// ============================================================================\n\n/**\n * Popover component for displaying accessible popup content anchored to a trigger.\n *\n * @example\n * ```tsx\n * <Popover>\n * <Popover.Trigger asChild>\n * <Button>Open</Button>\n * </Popover.Trigger>\n * <Popover.Content>\n * <Popover.Title>Notifications</Popover.Title>\n * <Popover.Description>You are all caught up!</Popover.Description>\n * </Popover.Content>\n * </Popover>\n * ```\n *\n * @see https://base-ui.com/react/components/popover\n */\nexport const Popover = Object.assign(PopoverRoot, {\n Trigger: PopoverTrigger,\n Content: PopoverContent,\n Title: PopoverTitle,\n Description: PopoverDescription,\n Close: PopoverClose,\n});\n\n// Export sub-components for direct access and type inference\nexport {\n PopoverRoot,\n PopoverTrigger,\n PopoverContent,\n PopoverTitle,\n PopoverDescription,\n PopoverClose,\n};\n"],"mappings":";;;;;;AAMA,IAAa,sBAAsB,EACjC,MAAM;CACJ,KAAK;EACH,SAAS;EACT,aAAa;EACd;CACD,QAAQ;EACN,SAAS;EACT,aAAa;EACd;CACD,MAAM;EACJ,SAAS;EACT,aAAa;EACd;CACD,OAAO;EACL,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,8BAA8B,EACzC,MAAM,UACP;AAyBD,SAAS,YAAY,EAAE,UAAU,GAAG,SAA2B;AAC7D,QAAO,oBAAC,QAAY,MAAb;EAAkB,GAAI;EAAQ;EAA4B,CAAA;;AAGnE,YAAY,cAAc;AAe1B,SAAS,eAAe,EACtB,UACA,WACA,SACA,GAAG,SACmB;AACtB,QACE,oBAAC,QAAY,SAAb;EACa;EACX,QACE,UAAW,WAAiD,KAAA;EAE9D,GAAI;YAEH,UAAU,KAAA,IAAY;EACH,CAAA;;AAI1B,eAAe,cAAc;AAyC7B,SAAS,eAAe,EACtB,UACA,OAAO,4BAA4B,MACnC,QAAQ,UACR,aAAa,GACb,cAAc,GACd,aACsB;AACtB,QACE,oBAAC,QAAY,QAAb,EAAA,UACE,oBAAC,QAAY,YAAb;EACS;EACM;EACP;EACM;YAEZ,qBAAC,QAAY,OAAb;GACE,WAAW,GACT,yGACA,0DACA,qDACA,8DACA,0DACA,2BACA,oBACA,UACD;aAVH,CAYE,oBAAC,QAAY,OAAb;IACE,WAAW,GACT,QACA,6BACA,6DACA,+DACA,uDACD;cAED,oBAAC,UAAD,EAAY,CAAA;IACM,CAAA,EACnB,SACiB;;EACG,CAAA,EACN,CAAA;;AAIzB,eAAe,cAAc;AAU7B,SAAS,aAAa,EAAE,WAAW,GAAG,SAA4B;AAChE,QACE,oBAAC,QAAY,OAAb;EACE,WAAW,GAAG,uCAAuC,UAAU;EAC/D,GAAI;EACJ,CAAA;;AAIN,aAAa,cAAc;AAY3B,SAAS,mBAAmB,EAAE,WAAW,GAAG,SAAkC;AAC5E,QACE,oBAAC,QAAY,aAAb;EACE,WAAW,GAAG,0CAA0C,UAAU;EAClE,GAAI;EACJ,CAAA;;AAIN,mBAAmB,cAAc;AAajC,SAAS,aAAa,EACpB,UACA,WACA,SACA,GAAG,SACiB;AACpB,QACE,oBAAC,QAAY,OAAb;EACa;EACX,QACE,UAAW,WAA+C,KAAA;EAE5D,GAAI;YAEH,UAAU,KAAA,IAAY;EACL,CAAA;;AAIxB,aAAa,cAAc;;;;;;;;;;;;;;;;AAqB3B,SAAS,SAAS,OAAoC;AACpD,QACE,qBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EAAO,GAAI;YAAhE;GACE,oBAAC,QAAD;IACE,GAAE;IACF,WAAU;IACV,CAAA;GACF,oBAAC,QAAD;IACE,GAAE;IACF,WAAU;IACV,CAAA;GACF,oBAAC,QAAD;IACE,GAAE;IACF,WAAU;IACV,CAAA;GACE;;;;;;;;;;;;;;;;;;;;;AA0BV,IAAa,YAAU,OAAO,OAAO,aAAa;CAChD,SAAS;CACT,SAAS;CACT,OAAO;CACP,aAAa;CACb,OAAO;CACR,CAAC"}
@@ -98,4 +98,4 @@ var Radio$1 = {
98
98
  //#endregion
99
99
  export { radioVariants as a, SF_RADIO_VARIANTS as i, RadioGroup$1 as n, SF_RADIO_DEFAULT_VARIANTS as r, Radio$1 as t };
100
100
 
101
- //# sourceMappingURL=radio-BNSwOt3B.js.map
101
+ //# sourceMappingURL=radio-BcwhwYNB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"radio-BNSwOt3B.js","names":[],"sources":["../src/components/radio/radio.tsx"],"sourcesContent":["import { Fieldset } from \"@base-ui/react/fieldset\";\nimport { Radio as BaseRadio } from \"@base-ui/react/radio\";\nimport { RadioGroup as BaseRadioGroup } from \"@base-ui/react/radio-group\";\nimport { forwardRef, createContext, useContext, type ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Radio variant definitions mapping variant names to their Tailwind classes. */\nexport const SF_RADIO_VARIANTS = {\n variant: {\n default: {\n classes: \"ring-sf-interact\",\n description: \"Default radio appearance\",\n },\n error: {\n classes: \"ring-destructive\",\n description: \"Error state for validation failures\",\n },\n },\n} as const;\n\nexport const SF_RADIO_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\n// Derived types from SF_RADIO_VARIANTS\nexport type SFRadioVariant = keyof typeof SF_RADIO_VARIANTS.variant;\n\nexport interface SFRadioVariantsProps {\n /**\n * Visual variant.\n * - `\"default\"` — Standard radio appearance\n * - `\"error\"` — Error state for validation failures\n * @default \"default\"\n */\n variant?: SFRadioVariant;\n}\n\nexport function radioVariants({\n variant = SF_RADIO_DEFAULT_VARIANTS.variant,\n}: SFRadioVariantsProps = {}) {\n return cn(SF_RADIO_VARIANTS.variant[variant].classes);\n}\n\n// Legacy type alias for backwards compatibility\nexport type RadioVariant = SFRadioVariant;\n\n/** Position of the radio control relative to its label */\nexport type RadioControlPosition = \"start\" | \"end\";\n\n// Context for passing controlPosition from Group to Items\nconst RadioGroupContext = createContext<{\n controlPosition: RadioControlPosition;\n}>({\n controlPosition: \"start\",\n});\n\n/**\n * Radio group component props (with built-in Fieldset and RadioGroup)\n *\n * @example\n * // Basic usage\n * ```tsx\n * <Radio.Group legend=\"Notification preference\" defaultValue=\"email\">\n * <Radio.Item label=\"Email\" value=\"email\" />\n * <Radio.Item label=\"SMS\" value=\"sms\" />\n * <Radio.Item label=\"Push\" value=\"push\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // Horizontal layout\n * ```tsx\n * <Radio.Group legend=\"Size\" orientation=\"horizontal\" defaultValue=\"md\">\n * <Radio.Item label=\"Small\" value=\"sm\" />\n * <Radio.Item label=\"Medium\" value=\"md\" />\n * <Radio.Item label=\"Large\" value=\"lg\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // With error and description\n * ```tsx\n * <Radio.Group\n * legend=\"Payment method\"\n * error=\"Please select a payment method\"\n * description=\"Choose how you'd like to pay\"\n * >\n * <Radio.Item label=\"Credit Card\" value=\"card\" />\n * <Radio.Item label=\"PayPal\" value=\"paypal\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // Controlled\n * ```tsx\n * const [value, setValue] = useState(\"email\");\n * <Radio.Group legend=\"Contact\" value={value} onValueChange={setValue}>\n * <Radio.Item label=\"Email\" value=\"email\" />\n * <Radio.Item label=\"Phone\" value=\"phone\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // Label before radio (controlPosition=\"end\")\n * ```tsx\n * <Radio.Group legend=\"Options\" controlPosition=\"end\" defaultValue=\"a\">\n * <Radio.Item label=\"Option A\" value=\"a\" />\n * <Radio.Item label=\"Option B\" value=\"b\" />\n * </Radio.Group>\n * ```\n */\nexport interface RadioGroupProps {\n /** Legend text for the group (required for accessibility) */\n legend: string;\n /** Child Radio.Item components */\n children: ReactNode;\n /** Layout direction of the radio items */\n orientation?: \"vertical\" | \"horizontal\";\n /** Error message for the group */\n error?: string;\n /** Helper text for the group */\n description?: ReactNode;\n /** Value of the radio that should be initially selected (uncontrolled) */\n defaultValue?: string;\n /** Value of the radio that should be selected (controlled) */\n value?: string;\n /** Event handler called when radio value changes */\n onValueChange?: (value: string) => void;\n /** Whether all radios in the group are disabled */\n disabled?: boolean;\n /** Position of radio control relative to label: \"start\" (default) puts radio before label, \"end\" puts label before radio */\n controlPosition?: RadioControlPosition;\n /** Form submission name for the radio group */\n name?: string;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Individual radio item within a group\n *\n * @example\n * ```tsx\n * <Radio.Item label=\"Option A\" value=\"a\" />\n * ```\n *\n * @example\n * // Disabled item\n * ```tsx\n * <Radio.Item label=\"Unavailable\" value=\"unavailable\" disabled />\n * ```\n */\nexport type RadioItemProps = {\n /** Visual variant: \"default\" or \"error\" for validation failures */\n variant?: RadioVariant;\n /** Label text displayed next to radio (required) */\n label: string;\n /** Value of the radio (required) */\n value: string;\n /** Additional CSS classes for the label wrapper */\n className?: string;\n /** Whether the radio is disabled */\n disabled?: boolean;\n};\n\n// Radio.Item for use within Radio.Group\nconst RadioItem = forwardRef<HTMLButtonElement, RadioItemProps>(\n ({ className, disabled, variant = \"default\", label, value }, ref) => {\n const { controlPosition } = useContext(RadioGroupContext);\n\n return (\n <label\n className={cn(\n \"group relative inline-flex items-center gap-2\",\n // \"start\" (default): radio before label\n // \"end\": label before radio using flex-row-reverse\n controlPosition === \"end\" && \"flex-row-reverse justify-end\",\n disabled ? \"cursor-not-allowed opacity-50\" : \"cursor-pointer\",\n className\n )}\n >\n <BaseRadio.Root\n ref={ref}\n value={value}\n disabled={disabled}\n className={cn(\n \"flex h-4 w-4 items-center justify-center rounded-full border-0 bg-sf-base ring\",\n variant === \"error\" ? \"ring-sf-danger\" : \"ring-sf-interact\",\n !disabled &&\n \"group-hover:ring-sf-ring focus-visible:ring-sf-ring focus-visible:outline-offset-3\",\n \"data-checked:bg-sf-contrast\"\n )}\n >\n <BaseRadio.Indicator className=\"flex items-center justify-center\">\n <span className=\"h-2 w-2 rounded-full bg-sf-base\" />\n </BaseRadio.Indicator>\n </BaseRadio.Root>\n <span className=\"text-base font-medium text-sf-default\">{label}</span>\n </label>\n );\n }\n);\n\nRadioItem.displayName = \"Radio.Item\";\n\n// Radio.Group with built-in Fieldset and RadioGroup\nfunction RadioGroup({\n legend,\n children,\n orientation = \"vertical\",\n error,\n description,\n defaultValue,\n value,\n onValueChange,\n disabled,\n controlPosition = \"start\",\n name,\n className,\n}: RadioGroupProps) {\n return (\n <RadioGroupContext.Provider value={{ controlPosition }}>\n <BaseRadioGroup\n defaultValue={defaultValue}\n value={value}\n onValueChange={(newValue) => onValueChange?.(newValue as string)}\n disabled={disabled}\n name={name}\n >\n <Fieldset.Root\n className={cn(\n \"flex flex-col gap-4 rounded-lg border border-sf-line p-4\",\n className\n )}\n >\n <Fieldset.Legend className=\"text-lg font-medium text-sf-default\">\n {legend}\n </Fieldset.Legend>\n <div\n className={cn(\n \"flex gap-2\",\n orientation === \"vertical\" ? \"flex-col\" : \"flex-row flex-wrap\"\n )}\n >\n {children}\n </div>\n {error && <p className=\"text-sm text-sf-danger\">{error}</p>}\n {description && (\n <p className=\"text-sm text-sf-subtle\">{description}</p>\n )}\n </Fieldset.Root>\n </BaseRadioGroup>\n </RadioGroupContext.Provider>\n );\n}\n\nRadioGroup.displayName = \"Radio.Group\";\n\n// Export RadioGroup directly for external usage\nexport { RadioGroup };\n\n/**\n * Radio — radio button group for single-select choices.\n *\n * Compound component: `Radio.Group` (with built-in Fieldset) and `Radio.Item`.\n * Built on `@base-ui/react/radio-group` + `@base-ui/react/radio`.\n *\n * @example\n * ```tsx\n * <Radio.Group legend=\"Notification preference\" defaultValue=\"email\">\n * <Radio.Item label=\"Email\" value=\"email\" />\n * <Radio.Item label=\"SMS\" value=\"sms\" />\n * <Radio.Item label=\"Push\" value=\"push\" />\n * </Radio.Group>\n * ```\n */\nexport const Radio = {\n Item: RadioItem,\n Group: RadioGroup,\n};\n"],"mappings":";;;;;;;;;AAQA,IAAa,oBAAoB,EAC/B,SAAS;CACP,SAAS;EACP,SAAS;EACT,aAAa;EACd;CACD,OAAO;EACL,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,4BAA4B,EACvC,SAAS,WACV;AAeD,SAAgB,cAAc,EAC5B,UAAU,0BAA0B,YACZ,EAAE,EAAE;AAC5B,QAAO,GAAG,kBAAkB,QAAQ,SAAS,QAAQ;;AAUvD,IAAM,oBAAoB,cAEvB,EACD,iBAAiB,SAClB,CAAC;AAgHF,IAAM,YAAY,YACf,EAAE,WAAW,UAAU,UAAU,WAAW,OAAO,SAAS,QAAQ;CACnE,MAAM,EAAE,oBAAoB,WAAW,kBAAkB;AAEzD,QACE,qBAAC,SAAD;EACE,WAAW,GACT,iDAGA,oBAAoB,SAAS,gCAC7B,WAAW,kCAAkC,kBAC7C,UACD;YARH,CAUE,oBAAC,MAAU,MAAX;GACO;GACE;GACG;GACV,WAAW,GACT,kFACA,YAAY,UAAU,mBAAmB,oBACzC,CAAC,YACC,sFACF,8BACD;aAED,oBAAC,MAAU,WAAX;IAAqB,WAAU;cAC7B,oBAAC,QAAD,EAAM,WAAU,mCAAoC,CAAA;IAChC,CAAA;GACP,CAAA,EACjB,oBAAC,QAAD;GAAM,WAAU;aAAyC;GAAa,CAAA,CAChE;;EAGb;AAED,UAAU,cAAc;AAGxB,SAAS,aAAW,EAClB,QACA,UACA,cAAc,YACd,OACA,aACA,cACA,OACA,eACA,UACA,kBAAkB,SAClB,MACA,aACkB;AAClB,QACE,oBAAC,kBAAkB,UAAnB;EAA4B,OAAO,EAAE,iBAAiB;YACpD,oBAAC,YAAD;GACgB;GACP;GACP,gBAAgB,aAAa,gBAAgB,SAAmB;GACtD;GACJ;aAEN,qBAAC,SAAS,MAAV;IACE,WAAW,GACT,4DACA,UACD;cAJH;KAME,oBAAC,SAAS,QAAV;MAAiB,WAAU;gBACxB;MACe,CAAA;KAClB,oBAAC,OAAD;MACE,WAAW,GACT,cACA,gBAAgB,aAAa,aAAa,qBAC3C;MAEA;MACG,CAAA;KACL,SAAS,oBAAC,KAAD;MAAG,WAAU;gBAA0B;MAAU,CAAA;KAC1D,eACC,oBAAC,KAAD;MAAG,WAAU;gBAA0B;MAAgB,CAAA;KAE3C;;GACD,CAAA;EACU,CAAA;;AAIjC,aAAW,cAAc;;;;;;;;;;;;;;;;AAoBzB,IAAa,UAAQ;CACnB,MAAM;CACN,OAAO;CACR"}
1
+ {"version":3,"file":"radio-BcwhwYNB.js","names":[],"sources":["../src/components/radio/radio.tsx"],"sourcesContent":["import { Fieldset } from \"@base-ui/react/fieldset\";\nimport { Radio as BaseRadio } from \"@base-ui/react/radio\";\nimport { RadioGroup as BaseRadioGroup } from \"@base-ui/react/radio-group\";\nimport { forwardRef, createContext, useContext, type ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** Radio variant definitions mapping variant names to their Tailwind classes. */\nexport const SF_RADIO_VARIANTS = {\n variant: {\n default: {\n classes: \"ring-sf-interact\",\n description: \"Default radio appearance\",\n },\n error: {\n classes: \"ring-destructive\",\n description: \"Error state for validation failures\",\n },\n },\n} as const;\n\nexport const SF_RADIO_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\n// Derived types from SF_RADIO_VARIANTS\nexport type SFRadioVariant = keyof typeof SF_RADIO_VARIANTS.variant;\n\nexport interface SFRadioVariantsProps {\n /**\n * Visual variant.\n * - `\"default\"` — Standard radio appearance\n * - `\"error\"` — Error state for validation failures\n * @default \"default\"\n */\n variant?: SFRadioVariant;\n}\n\nexport function radioVariants({\n variant = SF_RADIO_DEFAULT_VARIANTS.variant,\n}: SFRadioVariantsProps = {}) {\n return cn(SF_RADIO_VARIANTS.variant[variant].classes);\n}\n\n// Legacy type alias for backwards compatibility\nexport type RadioVariant = SFRadioVariant;\n\n/** Position of the radio control relative to its label */\nexport type RadioControlPosition = \"start\" | \"end\";\n\n// Context for passing controlPosition from Group to Items\nconst RadioGroupContext = createContext<{\n controlPosition: RadioControlPosition;\n}>({\n controlPosition: \"start\",\n});\n\n/**\n * Radio group component props (with built-in Fieldset and RadioGroup)\n *\n * @example\n * // Basic usage\n * ```tsx\n * <Radio.Group legend=\"Notification preference\" defaultValue=\"email\">\n * <Radio.Item label=\"Email\" value=\"email\" />\n * <Radio.Item label=\"SMS\" value=\"sms\" />\n * <Radio.Item label=\"Push\" value=\"push\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // Horizontal layout\n * ```tsx\n * <Radio.Group legend=\"Size\" orientation=\"horizontal\" defaultValue=\"md\">\n * <Radio.Item label=\"Small\" value=\"sm\" />\n * <Radio.Item label=\"Medium\" value=\"md\" />\n * <Radio.Item label=\"Large\" value=\"lg\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // With error and description\n * ```tsx\n * <Radio.Group\n * legend=\"Payment method\"\n * error=\"Please select a payment method\"\n * description=\"Choose how you'd like to pay\"\n * >\n * <Radio.Item label=\"Credit Card\" value=\"card\" />\n * <Radio.Item label=\"PayPal\" value=\"paypal\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // Controlled\n * ```tsx\n * const [value, setValue] = useState(\"email\");\n * <Radio.Group legend=\"Contact\" value={value} onValueChange={setValue}>\n * <Radio.Item label=\"Email\" value=\"email\" />\n * <Radio.Item label=\"Phone\" value=\"phone\" />\n * </Radio.Group>\n * ```\n *\n * @example\n * // Label before radio (controlPosition=\"end\")\n * ```tsx\n * <Radio.Group legend=\"Options\" controlPosition=\"end\" defaultValue=\"a\">\n * <Radio.Item label=\"Option A\" value=\"a\" />\n * <Radio.Item label=\"Option B\" value=\"b\" />\n * </Radio.Group>\n * ```\n */\nexport interface RadioGroupProps {\n /** Legend text for the group (required for accessibility) */\n legend: string;\n /** Child Radio.Item components */\n children: ReactNode;\n /** Layout direction of the radio items */\n orientation?: \"vertical\" | \"horizontal\";\n /** Error message for the group */\n error?: string;\n /** Helper text for the group */\n description?: ReactNode;\n /** Value of the radio that should be initially selected (uncontrolled) */\n defaultValue?: string;\n /** Value of the radio that should be selected (controlled) */\n value?: string;\n /** Event handler called when radio value changes */\n onValueChange?: (value: string) => void;\n /** Whether all radios in the group are disabled */\n disabled?: boolean;\n /** Position of radio control relative to label: \"start\" (default) puts radio before label, \"end\" puts label before radio */\n controlPosition?: RadioControlPosition;\n /** Form submission name for the radio group */\n name?: string;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Individual radio item within a group\n *\n * @example\n * ```tsx\n * <Radio.Item label=\"Option A\" value=\"a\" />\n * ```\n *\n * @example\n * // Disabled item\n * ```tsx\n * <Radio.Item label=\"Unavailable\" value=\"unavailable\" disabled />\n * ```\n */\nexport type RadioItemProps = {\n /** Visual variant: \"default\" or \"error\" for validation failures */\n variant?: RadioVariant;\n /** Label text displayed next to radio (required) */\n label: string;\n /** Value of the radio (required) */\n value: string;\n /** Additional CSS classes for the label wrapper */\n className?: string;\n /** Whether the radio is disabled */\n disabled?: boolean;\n};\n\n// Radio.Item for use within Radio.Group\nconst RadioItem = forwardRef<HTMLButtonElement, RadioItemProps>(\n ({ className, disabled, variant = \"default\", label, value }, ref) => {\n const { controlPosition } = useContext(RadioGroupContext);\n\n return (\n <label\n className={cn(\n \"group relative inline-flex items-center gap-2\",\n // \"start\" (default): radio before label\n // \"end\": label before radio using flex-row-reverse\n controlPosition === \"end\" && \"flex-row-reverse justify-end\",\n disabled ? \"cursor-not-allowed opacity-50\" : \"cursor-pointer\",\n className\n )}\n >\n <BaseRadio.Root\n ref={ref}\n value={value}\n disabled={disabled}\n className={cn(\n \"flex h-4 w-4 items-center justify-center rounded-full border-0 bg-sf-base ring\",\n variant === \"error\" ? \"ring-sf-danger\" : \"ring-sf-interact\",\n !disabled &&\n \"group-hover:ring-sf-ring focus-visible:ring-sf-ring focus-visible:outline-offset-3\",\n \"data-checked:bg-sf-contrast\"\n )}\n >\n <BaseRadio.Indicator className=\"flex items-center justify-center\">\n <span className=\"h-2 w-2 rounded-full bg-sf-base\" />\n </BaseRadio.Indicator>\n </BaseRadio.Root>\n <span className=\"text-base font-medium text-sf-default\">{label}</span>\n </label>\n );\n }\n);\n\nRadioItem.displayName = \"Radio.Item\";\n\n// Radio.Group with built-in Fieldset and RadioGroup\nfunction RadioGroup({\n legend,\n children,\n orientation = \"vertical\",\n error,\n description,\n defaultValue,\n value,\n onValueChange,\n disabled,\n controlPosition = \"start\",\n name,\n className,\n}: RadioGroupProps) {\n return (\n <RadioGroupContext.Provider value={{ controlPosition }}>\n <BaseRadioGroup\n defaultValue={defaultValue}\n value={value}\n onValueChange={(newValue) => onValueChange?.(newValue as string)}\n disabled={disabled}\n name={name}\n >\n <Fieldset.Root\n className={cn(\n \"flex flex-col gap-4 rounded-lg border border-sf-line p-4\",\n className\n )}\n >\n <Fieldset.Legend className=\"text-lg font-medium text-sf-default\">\n {legend}\n </Fieldset.Legend>\n <div\n className={cn(\n \"flex gap-2\",\n orientation === \"vertical\" ? \"flex-col\" : \"flex-row flex-wrap\"\n )}\n >\n {children}\n </div>\n {error && <p className=\"text-sm text-sf-danger\">{error}</p>}\n {description && (\n <p className=\"text-sm text-sf-subtle\">{description}</p>\n )}\n </Fieldset.Root>\n </BaseRadioGroup>\n </RadioGroupContext.Provider>\n );\n}\n\nRadioGroup.displayName = \"Radio.Group\";\n\n// Export RadioGroup directly for external usage\nexport { RadioGroup };\n\n/**\n * Radio — radio button group for single-select choices.\n *\n * Compound component: `Radio.Group` (with built-in Fieldset) and `Radio.Item`.\n * Built on `@base-ui/react/radio-group` + `@base-ui/react/radio`.\n *\n * @example\n * ```tsx\n * <Radio.Group legend=\"Notification preference\" defaultValue=\"email\">\n * <Radio.Item label=\"Email\" value=\"email\" />\n * <Radio.Item label=\"SMS\" value=\"sms\" />\n * <Radio.Item label=\"Push\" value=\"push\" />\n * </Radio.Group>\n * ```\n */\nexport const Radio = {\n Item: RadioItem,\n Group: RadioGroup,\n};\n"],"mappings":";;;;;;;;;AAQA,IAAa,oBAAoB,EAC/B,SAAS;CACP,SAAS;EACP,SAAS;EACT,aAAa;EACd;CACD,OAAO;EACL,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,4BAA4B,EACvC,SAAS,WACV;AAeD,SAAgB,cAAc,EAC5B,UAAU,0BAA0B,YACZ,EAAE,EAAE;AAC5B,QAAO,GAAG,kBAAkB,QAAQ,SAAS,QAAQ;;AAUvD,IAAM,oBAAoB,cAEvB,EACD,iBAAiB,SAClB,CAAC;AAgHF,IAAM,YAAY,YACf,EAAE,WAAW,UAAU,UAAU,WAAW,OAAO,SAAS,QAAQ;CACnE,MAAM,EAAE,oBAAoB,WAAW,kBAAkB;AAEzD,QACE,qBAAC,SAAD;EACE,WAAW,GACT,iDAGA,oBAAoB,SAAS,gCAC7B,WAAW,kCAAkC,kBAC7C,UACD;YARH,CAUE,oBAAC,MAAU,MAAX;GACO;GACE;GACG;GACV,WAAW,GACT,kFACA,YAAY,UAAU,mBAAmB,oBACzC,CAAC,YACC,sFACF,8BACD;aAED,oBAAC,MAAU,WAAX;IAAqB,WAAU;cAC7B,oBAAC,QAAD,EAAM,WAAU,mCAAoC,CAAA;IAChC,CAAA;GACP,CAAA,EACjB,oBAAC,QAAD;GAAM,WAAU;aAAyC;GAAa,CAAA,CAChE;;EAGb;AAED,UAAU,cAAc;AAGxB,SAAS,aAAW,EAClB,QACA,UACA,cAAc,YACd,OACA,aACA,cACA,OACA,eACA,UACA,kBAAkB,SAClB,MACA,aACkB;AAClB,QACE,oBAAC,kBAAkB,UAAnB;EAA4B,OAAO,EAAE,iBAAiB;YACpD,oBAAC,YAAD;GACgB;GACP;GACP,gBAAgB,aAAa,gBAAgB,SAAmB;GACtD;GACJ;aAEN,qBAAC,SAAS,MAAV;IACE,WAAW,GACT,4DACA,UACD;cAJH;KAME,oBAAC,SAAS,QAAV;MAAiB,WAAU;gBACxB;MACe,CAAA;KAClB,oBAAC,OAAD;MACE,WAAW,GACT,cACA,gBAAgB,aAAa,aAAa,qBAC3C;MAEA;MACG,CAAA;KACL,SAAS,oBAAC,KAAD;MAAG,WAAU;gBAA0B;MAAU,CAAA;KAC1D,eACC,oBAAC,KAAD;MAAG,WAAU;gBAA0B;MAAgB,CAAA;KAE3C;;GACD,CAAA;EACU,CAAA;;AAIjC,aAAW,cAAc;;;;;;;;;;;;;;;;AAoBzB,IAAa,UAAQ;CACnB,MAAM;CACN,OAAO;CACR"}