@feugene/granularity 0.26.0 → 0.27.1

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 (581) hide show
  1. package/dist/chunks/classTokens-8v2XC74x.js +16 -0
  2. package/dist/chunks/classTokens-8v2XC74x.js.map +1 -0
  3. package/dist/chunks/{config-Bpt9Xbxm.js → config-2ZHRsdOc.js} +2 -2
  4. package/dist/chunks/config-2ZHRsdOc.js.map +1 -0
  5. package/dist/chunks/{config-D6W1rnlp.js → config-B0uvcTPH.js} +1 -1
  6. package/dist/chunks/{config-D6W1rnlp.js.map → config-B0uvcTPH.js.map} +1 -1
  7. package/dist/chunks/{config-Bsh948nP.js → config-B3J0alyY.js} +1 -1
  8. package/dist/chunks/{config-Bsh948nP.js.map → config-B3J0alyY.js.map} +1 -1
  9. package/dist/chunks/{config-ScuvHw6k.js → config-B5SBUa6u.js} +1 -1
  10. package/dist/chunks/{config-ScuvHw6k.js.map → config-B5SBUa6u.js.map} +1 -1
  11. package/dist/chunks/{config-BeZRuonp.js → config-BDSaD-5p.js} +1 -1
  12. package/dist/chunks/{config-BeZRuonp.js.map → config-BDSaD-5p.js.map} +1 -1
  13. package/dist/chunks/{config-Dgj1Q6JY.js → config-BEO7brpO.js} +3 -3
  14. package/dist/chunks/config-BEO7brpO.js.map +1 -0
  15. package/dist/chunks/{config-B84z1aos.js → config-BHcS5Cwm.js} +1 -1
  16. package/dist/chunks/{config-B84z1aos.js.map → config-BHcS5Cwm.js.map} +1 -1
  17. package/dist/chunks/{config-DA6SnEoy.js → config-BRESvs9g.js} +2 -2
  18. package/dist/chunks/{config-DA6SnEoy.js.map → config-BRESvs9g.js.map} +1 -1
  19. package/dist/chunks/{config-CN2ULSQL.js → config-BTmfQFwi.js} +5 -5
  20. package/dist/chunks/config-BTmfQFwi.js.map +1 -0
  21. package/dist/chunks/{config-DtgYuGUn2.js → config-BTwYMQaz2.js} +5 -3
  22. package/dist/chunks/config-BTwYMQaz2.js.map +1 -0
  23. package/dist/chunks/{config-QJ54_fcM.js → config-BaWANzzB.js} +1 -1
  24. package/dist/chunks/{config-QJ54_fcM.js.map → config-BaWANzzB.js.map} +1 -1
  25. package/dist/chunks/{config-COvrUfhR.js → config-BcXX7S7B.js} +5 -5
  26. package/dist/chunks/config-BcXX7S7B.js.map +1 -0
  27. package/dist/chunks/{config-COpuWGr_.js → config-Bd7QfiGj.js} +1 -1
  28. package/dist/chunks/{config-COpuWGr_.js.map → config-Bd7QfiGj.js.map} +1 -1
  29. package/dist/chunks/{config-BDfTXHo1.js → config-BhfNgyWL.js} +2 -2
  30. package/dist/chunks/{config-BDfTXHo1.js.map → config-BhfNgyWL.js.map} +1 -1
  31. package/dist/chunks/{config-CjnbevbD.js → config-Bjn30smH.js} +1 -1
  32. package/dist/chunks/{config-CjnbevbD.js.map → config-Bjn30smH.js.map} +1 -1
  33. package/dist/chunks/{config-CN970ZcJ.js → config-BlJa1Mj9.js} +13 -13
  34. package/dist/chunks/config-BlJa1Mj9.js.map +1 -0
  35. package/dist/chunks/{config-DU2DNSRx.js → config-BoUI0t-t.js} +1 -1
  36. package/dist/chunks/{config-DU2DNSRx.js.map → config-BoUI0t-t.js.map} +1 -1
  37. package/dist/chunks/{config-BN4sXYXn.js → config-BqT6PMzO.js} +7 -14
  38. package/dist/chunks/config-BqT6PMzO.js.map +1 -0
  39. package/dist/chunks/config-BuGlNPE9.js.map +1 -0
  40. package/dist/chunks/{config-MRvhUl-O.js → config-CJzmDHfI.js} +1 -1
  41. package/dist/chunks/{config-MRvhUl-O.js.map → config-CJzmDHfI.js.map} +1 -1
  42. package/dist/chunks/{config-DGWFal5h.js → config-CLjjjPZj.js} +7 -7
  43. package/dist/chunks/config-CLjjjPZj.js.map +1 -0
  44. package/dist/chunks/{config-Cl4ST6Qd.js → config-CQLNjc9X2.js} +2 -2
  45. package/dist/chunks/config-CQLNjc9X2.js.map +1 -0
  46. package/dist/chunks/{config-vSji8w90.js → config-CWtfxkM9.js} +9 -9
  47. package/dist/chunks/config-CWtfxkM9.js.map +1 -0
  48. package/dist/chunks/{config-DKISz9gR.js → config-CWthWcWY.js} +2 -2
  49. package/dist/chunks/{config-DKISz9gR.js.map → config-CWthWcWY.js.map} +1 -1
  50. package/dist/chunks/{config-Iu684VNI.js → config-CZPDPJlg.js} +1 -1
  51. package/dist/chunks/{config-Iu684VNI.js.map → config-CZPDPJlg.js.map} +1 -1
  52. package/dist/chunks/{config-DAX8Deh1.js → config-CbEbLyDE.js} +3 -3
  53. package/dist/chunks/config-CbEbLyDE.js.map +1 -0
  54. package/dist/chunks/{config-D94Z9po5.js → config-ChPg-BQv.js} +2 -2
  55. package/dist/chunks/config-ChPg-BQv.js.map +1 -0
  56. package/dist/chunks/{config-DSJY6aEl.js → config-CjKpUcmy.js} +17 -17
  57. package/dist/chunks/config-CjKpUcmy.js.map +1 -0
  58. package/dist/chunks/{config-BIa3ilQm.js → config-CjWOTgLL.js} +4 -4
  59. package/dist/chunks/{config-BIa3ilQm.js.map → config-CjWOTgLL.js.map} +1 -1
  60. package/dist/chunks/{config-rkcKArZQ.js → config-CnYdE3HZ2.js} +5 -5
  61. package/dist/chunks/config-CnYdE3HZ2.js.map +1 -0
  62. package/dist/chunks/{config-BE6n20np.js → config-CvbVM4ep.js} +1 -1
  63. package/dist/chunks/{config-BE6n20np.js.map → config-CvbVM4ep.js.map} +1 -1
  64. package/dist/chunks/{config-9PYRZlKE.js → config-D0NzGYaE.js} +6 -6
  65. package/dist/chunks/config-D0NzGYaE.js.map +1 -0
  66. package/dist/chunks/{config-CKULiuvc.js → config-D0W8mGFD.js} +4 -4
  67. package/dist/chunks/{config-CKULiuvc.js.map → config-D0W8mGFD.js.map} +1 -1
  68. package/dist/chunks/{config-CY0_6cMi.js → config-D82r782v.js} +1 -1
  69. package/dist/chunks/{config-CY0_6cMi.js.map → config-D82r782v.js.map} +1 -1
  70. package/dist/chunks/config-DBmdgoVf2.js.map +1 -0
  71. package/dist/chunks/{config-WTNhdh6T.js → config-DCq4JySZ.js} +2 -2
  72. package/dist/chunks/{config-WTNhdh6T.js.map → config-DCq4JySZ.js.map} +1 -1
  73. package/dist/chunks/{config-C8tVRv8i.js → config-DGC00bTp.js} +13 -13
  74. package/dist/chunks/config-DGC00bTp.js.map +1 -0
  75. package/dist/chunks/{config-BsPDQ0dS.js → config-DIDz_E5m.js} +9 -9
  76. package/dist/chunks/config-DIDz_E5m.js.map +1 -0
  77. package/dist/chunks/{config-Dguto_6o.js → config-DM0lsduD.js} +6 -6
  78. package/dist/chunks/config-DM0lsduD.js.map +1 -0
  79. package/dist/chunks/{config-BfsBepkH.js → config-DNDgp9bB.js} +5 -5
  80. package/dist/chunks/config-DNDgp9bB.js.map +1 -0
  81. package/dist/chunks/{config-DpeQchsV.js → config-DO8mjeIo.js} +2 -2
  82. package/dist/chunks/{config-DpeQchsV.js.map → config-DO8mjeIo.js.map} +1 -1
  83. package/dist/chunks/{config-Baj7Wvx-.js → config-DRbojEzO.js} +4 -4
  84. package/dist/chunks/config-DRbojEzO.js.map +1 -0
  85. package/dist/chunks/{config-CQzGyi1C.js → config-DWL81-aM.js} +1 -1
  86. package/dist/chunks/{config-CQzGyi1C.js.map → config-DWL81-aM.js.map} +1 -1
  87. package/dist/chunks/{config-esdviAPL.js → config-DZCUPHPU.js} +1 -1
  88. package/dist/chunks/{config-esdviAPL.js.map → config-DZCUPHPU.js.map} +1 -1
  89. package/dist/chunks/{config-DKdlqXiY.js → config-DcfWZOi5.js} +5 -5
  90. package/dist/chunks/config-DcfWZOi5.js.map +1 -0
  91. package/dist/chunks/{config-BC8d_smz.js → config-DeyT9dJl.js} +3 -3
  92. package/dist/chunks/{config-BC8d_smz.js.map → config-DeyT9dJl.js.map} +1 -1
  93. package/dist/chunks/{config-Cwd3yjRw.js → config-DnF472Ox.js} +6 -4
  94. package/dist/chunks/config-DnF472Ox.js.map +1 -0
  95. package/dist/chunks/{config-B_qi4Fma.js → config-DolULhjJ.js} +5 -5
  96. package/dist/chunks/config-DolULhjJ.js.map +1 -0
  97. package/dist/chunks/{config-CIcynHGr.js → config-DpAYidyY2.js} +1 -1
  98. package/dist/chunks/config-DpAYidyY2.js.map +1 -0
  99. package/dist/chunks/{config-DYgfMrIF.js → config-Dpk-eVkS.js} +1 -1
  100. package/dist/chunks/{config-DYgfMrIF.js.map → config-Dpk-eVkS.js.map} +1 -1
  101. package/dist/chunks/config-Dv_r92GL.js +35 -0
  102. package/dist/chunks/config-Dv_r92GL.js.map +1 -0
  103. package/dist/chunks/{config-B4w5gBD0.js → config-DwABJHEo.js} +2 -2
  104. package/dist/chunks/config-DwABJHEo.js.map +1 -0
  105. package/dist/chunks/{config-zjeAJ4ho2.js → config-MmPym-Z72.js} +5 -5
  106. package/dist/chunks/config-MmPym-Z72.js.map +1 -0
  107. package/dist/chunks/{config-DCFvmHEv.js → config-T3-DMXYm.js} +9 -7
  108. package/dist/chunks/config-T3-DMXYm.js.map +1 -0
  109. package/dist/chunks/{config-BoNR7_PD.js → config-Yy67uWHY.js} +1 -1
  110. package/dist/chunks/{config-BoNR7_PD.js.map → config-Yy67uWHY.js.map} +1 -1
  111. package/dist/chunks/{config-BFJIxE8b.js → config-Z5LeUXys.js} +1 -1
  112. package/dist/chunks/{config-BFJIxE8b.js.map → config-Z5LeUXys.js.map} +1 -1
  113. package/dist/chunks/{config-DWN4x6_e.js → config-_QF0O1vq.js} +9 -9
  114. package/dist/chunks/config-_QF0O1vq.js.map +1 -0
  115. package/dist/chunks/{config-Cp0DE0GH.js → config-_jO3q1Le.js} +1 -1
  116. package/dist/chunks/{config-Cp0DE0GH.js.map → config-_jO3q1Le.js.map} +1 -1
  117. package/dist/chunks/{config-DGCMHPCR.js → config-g8-JGZ_Y.js} +1 -1
  118. package/dist/chunks/{config-DGCMHPCR.js.map → config-g8-JGZ_Y.js.map} +1 -1
  119. package/dist/chunks/{config-BTCOJO5z2.js → config-jyr6AIZt2.js} +3 -3
  120. package/dist/chunks/{config-BTCOJO5z2.js.map → config-jyr6AIZt2.js.map} +1 -1
  121. package/dist/chunks/{config-BtXRpAtq.js → config-mlheIMtV.js} +3 -6
  122. package/dist/chunks/config-mlheIMtV.js.map +1 -0
  123. package/dist/chunks/{config-BEVBsvni2.js → config-rLkc9Zn2.js} +1 -1
  124. package/dist/chunks/config-rLkc9Zn2.js.map +1 -0
  125. package/dist/chunks/{config-T3qdQBgU.js → config-tiUtEn4Q.js} +1 -1
  126. package/dist/chunks/{config-T3qdQBgU.js.map → config-tiUtEn4Q.js.map} +1 -1
  127. package/dist/chunks/{config-BViooYNv.js → config-uzUuosv7.js} +7 -2
  128. package/dist/chunks/config-uzUuosv7.js.map +1 -0
  129. package/dist/chunks/{config-BbY2SKGE.js → config-vYMJ3Cr1.js} +11 -9
  130. package/dist/chunks/config-vYMJ3Cr1.js.map +1 -0
  131. package/dist/chunks/context-BfIQv7m1.js.map +1 -0
  132. package/dist/chunks/{generated-CJe6hdiE.js → generated-DJ2dJ__C.js} +48 -0
  133. package/dist/chunks/generated-DJ2dJ__C.js.map +1 -0
  134. package/dist/chunks/{grBadgeStyles-C1S56Alo.js → grBadgeStyles-D8Mz73B5.js} +4 -4
  135. package/dist/chunks/grBadgeStyles-D8Mz73B5.js.map +1 -0
  136. package/dist/chunks/{config-D7zgE_Gr.js → grChipStyles-3JZApw03.js} +21 -43
  137. package/dist/chunks/grChipStyles-3JZApw03.js.map +1 -0
  138. package/dist/chunks/{grDropdownMenuStyles-D1LOkdVj.js → grDropdownMenuStyles-DG3aYHkT.js} +3 -3
  139. package/dist/chunks/grDropdownMenuStyles-DG3aYHkT.js.map +1 -0
  140. package/dist/chunks/{grIconStyles-BwJrml2L.js → grIconStyles-B6BRqUld.js} +1 -1
  141. package/dist/chunks/{grIconStyles-BwJrml2L.js.map → grIconStyles-B6BRqUld.js.map} +1 -1
  142. package/dist/chunks/{grInputStyles-CJeMhUe5.js → grInputStyles-CAk3xKIb.js} +4 -4
  143. package/dist/chunks/grInputStyles-CAk3xKIb.js.map +1 -0
  144. package/dist/chunks/{grKbdStyles-CeZhGQHr.js → grKbdStyles-DENu8pUK.js} +5 -0
  145. package/dist/chunks/grKbdStyles-DENu8pUK.js.map +1 -0
  146. package/dist/chunks/{grLinkStyles-BoL8QYcV.js → grLinkStyles-FME_qMpi.js} +4 -4
  147. package/dist/chunks/grLinkStyles-FME_qMpi.js.map +1 -0
  148. package/dist/chunks/{grLoadingStyles-DEmji2wv.js → grLoadingStyles-CnQaf5ry.js} +1 -1
  149. package/dist/chunks/{grLoadingStyles-DEmji2wv.js.map → grLoadingStyles-CnQaf5ry.js.map} +1 -1
  150. package/dist/chunks/{grPopoverStyles-Cke_BZsM.js → grPopoverStyles-Djh5RNhn.js} +4 -4
  151. package/dist/chunks/grPopoverStyles-Djh5RNhn.js.map +1 -0
  152. package/dist/chunks/{grRadioStyles-BTjGNhtU.js → grRadioStyles-D4gnU65n.js} +4 -4
  153. package/dist/chunks/grRadioStyles-D4gnU65n.js.map +1 -0
  154. package/dist/chunks/{grSelectStyles-eTz4uPMP.js → grSelectStyles-Cdtd10eY.js} +9 -9
  155. package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +1 -0
  156. package/dist/chunks/{grTableStyles-C-iaqQUN.js → grTableStyles-uuMPSOBR.js} +2 -2
  157. package/dist/chunks/{grTableStyles-C-iaqQUN.js.map → grTableStyles-uuMPSOBR.js.map} +1 -1
  158. package/dist/chunks/{grTextareaStyles-iOcHuVMM.js → grTextareaStyles-Bp35BGna.js} +4 -4
  159. package/dist/chunks/grTextareaStyles-Bp35BGna.js.map +1 -0
  160. package/dist/chunks/{granular-provider-BaHkLjFX.js → granular-provider-DvvRayEh.js} +67 -67
  161. package/dist/chunks/{granular-provider-BaHkLjFX.js.map → granular-provider-DvvRayEh.js.map} +1 -1
  162. package/dist/chunks/numericStep-Bq2lj4ew.js +23 -0
  163. package/dist/chunks/numericStep-Bq2lj4ew.js.map +1 -0
  164. package/dist/chunks/overlayTransition-0Fj5tQbq.js +36 -0
  165. package/dist/chunks/overlayTransition-0Fj5tQbq.js.map +1 -0
  166. package/dist/chunks/{safelist-CudBLyAD.js → safelist-BOq5Li7p.js} +1 -1
  167. package/dist/chunks/{safelist-CudBLyAD.js.map → safelist-BOq5Li7p.js.map} +1 -1
  168. package/dist/chunks/{safelist-DOfCQR5O.js → safelist-CtiHPgrj.js} +2 -2
  169. package/dist/chunks/{safelist-DOfCQR5O.js.map → safelist-CtiHPgrj.js.map} +1 -1
  170. package/dist/chunks/{safelist-CkF4v7DC.js → safelist-Y3zrlhOa.js} +1 -1
  171. package/dist/chunks/{safelist-CkF4v7DC.js.map → safelist-Y3zrlhOa.js.map} +1 -1
  172. package/dist/chunks/{useMenuItemsFocus-OyOnIAlr.js → useMenuItemsFocus-B6TldmGf.js} +1 -43
  173. package/dist/chunks/useMenuItemsFocus-B6TldmGf.js.map +1 -0
  174. package/dist/chunks/useTypeahead-mxPtPBf2.js +44 -0
  175. package/dist/chunks/useTypeahead-mxPtPBf2.js.map +1 -0
  176. package/dist/components/GrAlert/chunks/{GrAlert-B21QGele.js → GrAlert-FAVf-ksH.js} +1 -1
  177. package/dist/components/GrAlert/chunks/{GrAlert-B21QGele.js.map → GrAlert-FAVf-ksH.js.map} +1 -1
  178. package/dist/components/GrAlert/index.js +1 -1
  179. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-D9c9BNFg.js → GrAutocomplete-BwhUTqmG.js} +88 -81
  180. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-BwhUTqmG.js.map +1 -0
  181. package/dist/components/GrAutocomplete/index.js +2 -2
  182. package/dist/components/GrAutocomplete/styles.css +1 -1
  183. package/dist/components/GrAvatar/chunks/{GrAvatar-BbdKRX8i.js → GrAvatar-CNmLmgcr.js} +1 -1
  184. package/dist/components/GrAvatar/chunks/{GrAvatar-BbdKRX8i.js.map → GrAvatar-CNmLmgcr.js.map} +1 -1
  185. package/dist/components/GrAvatar/index.js +2 -2
  186. package/dist/components/GrBadge/chunks/{GrBadge-_9rCHuE1.js → GrBadge-BM3f9PME.js} +2 -2
  187. package/dist/components/GrBadge/chunks/GrBadge-BM3f9PME.js.map +1 -0
  188. package/dist/components/GrBadge/index.js +3 -3
  189. package/dist/components/GrBadge/styles.css +1 -1
  190. package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-DDoFQscR.js → GrBadgeWrap-B-A3PRkE.js} +1 -1
  191. package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-B-A3PRkE.js.map +1 -0
  192. package/dist/components/GrBadgeWrap/index.js +2 -2
  193. package/dist/components/GrBottomNav/chunks/{GrBottomNav-JUu4ylYv.js → GrBottomNav-wtFatLjB.js} +1 -1
  194. package/dist/components/GrBottomNav/chunks/{GrBottomNav-JUu4ylYv.js.map → GrBottomNav-wtFatLjB.js.map} +1 -1
  195. package/dist/components/GrBottomNav/index.js +2 -2
  196. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-CQ44iU2o.js → GrBreadcrumbs-jtSnMEtg.js} +2 -2
  197. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-CQ44iU2o.js.map → GrBreadcrumbs-jtSnMEtg.js.map} +1 -1
  198. package/dist/components/GrBreadcrumbs/index.js +2 -2
  199. package/dist/components/GrButton/chunks/{GrButton-DCz-Wn-V.js → GrButton-Bw2AH4uX.js} +1 -1
  200. package/dist/components/GrButton/chunks/GrButton-Bw2AH4uX.js.map +1 -0
  201. package/dist/components/GrButton/index.js +3 -3
  202. package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-BQo5VQ_c.js → GrButtonGroup-CKyiAV8K.js} +1 -1
  203. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-CKyiAV8K.js.map +1 -0
  204. package/dist/components/GrButtonGroup/index.js +3 -3
  205. package/dist/components/GrCard/chunks/{GrCard-aQIjk6I9.js → GrCard-DR3ehBt8.js} +1 -1
  206. package/dist/components/GrCard/chunks/GrCard-DR3ehBt8.js.map +1 -0
  207. package/dist/components/GrCard/index.js +2 -2
  208. package/dist/components/GrCheckbox/index.js +1 -1
  209. package/dist/components/GrCheckboxGroup/index.js +1 -1
  210. package/dist/components/GrChip/chunks/{GrChip-BnL2FedO.js → GrChip-Df4orxRH.js} +15 -4
  211. package/dist/components/GrChip/chunks/GrChip-Df4orxRH.js.map +1 -0
  212. package/dist/components/GrChip/index.js +2 -2
  213. package/dist/components/GrChipGroup/chunks/{GrChipGroup-DefmSdY9.js → GrChipGroup-C78Vcwos.js} +2 -2
  214. package/dist/components/GrChipGroup/chunks/{GrChipGroup-DefmSdY9.js.map → GrChipGroup-C78Vcwos.js.map} +1 -1
  215. package/dist/components/GrChipGroup/index.js +2 -2
  216. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-CyptiEo4.js → GrCodeBlock-Dkvkr91t.js} +2 -2
  217. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-CyptiEo4.js.map → GrCodeBlock-Dkvkr91t.js.map} +1 -1
  218. package/dist/components/GrCodeBlock/index.js +2 -2
  219. package/dist/components/GrCollapse/chunks/{GrCollapse-DUROgAgc.js → GrCollapse-CytiA2Sx.js} +3 -3
  220. package/dist/components/GrCollapse/chunks/{GrCollapse-DUROgAgc.js.map → GrCollapse-CytiA2Sx.js.map} +1 -1
  221. package/dist/components/GrCollapse/index.js +2 -2
  222. package/dist/components/GrColorPicker/chunks/{GrColorPicker-CDaJRVIM.js → GrColorPicker-CE-xtNP-.js} +4 -4
  223. package/dist/components/GrColorPicker/chunks/{GrColorPicker-CDaJRVIM.js.map → GrColorPicker-CE-xtNP-.js.map} +1 -1
  224. package/dist/components/GrColorPicker/index.js +2 -2
  225. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BPsVN_XU.js → GrCommandPalette-C5abL2Y7.js} +3 -3
  226. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-C5abL2Y7.js.map +1 -0
  227. package/dist/components/GrCommandPalette/index.js +2 -2
  228. package/dist/components/GrCommandPalette/styles.css +1 -1
  229. package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BF7R3hgg.js.map +1 -1
  230. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CwXNLyzk.js → GrConfirmDialog-B9mIU78E.js} +4 -4
  231. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-B9mIU78E.js.map +1 -0
  232. package/dist/components/GrConfirmDialog/index.js +1 -1
  233. package/dist/components/GrContextMenu/chunks/{GrContextMenu-BbAJholy.js → GrContextMenu-DDAUvrDK.js} +3 -3
  234. package/dist/components/GrContextMenu/chunks/GrContextMenu-DDAUvrDK.js.map +1 -0
  235. package/dist/components/GrContextMenu/index.js +1 -1
  236. package/dist/components/GrDataTable/chunks/{GrDataTable-NRbrXpdx.js → GrDataTable-DU00V44y.js} +14 -4
  237. package/dist/components/GrDataTable/chunks/GrDataTable-DU00V44y.js.map +1 -0
  238. package/dist/components/GrDataTable/index.js +2 -2
  239. package/dist/components/GrDelta/chunks/{GrDelta-NE5HhXoo.js → GrDelta-04j9WmEY.js} +0 -0
  240. package/dist/components/GrDelta/chunks/{GrDelta-NE5HhXoo.js.map → GrDelta-04j9WmEY.js.map} +1 -1
  241. package/dist/components/GrDelta/index.js +2 -2
  242. package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-1mICfrqX.js → GrDescriptionList-BMeCgFV3.js} +1 -1
  243. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-BMeCgFV3.js.map +1 -0
  244. package/dist/components/GrDescriptionList/index.js +2 -2
  245. package/dist/components/GrDialog/chunks/{GrDialog-5Y5K87ss.js → GrDialog-bOUDYfmh.js} +2 -2
  246. package/dist/components/GrDialog/chunks/GrDialog-bOUDYfmh.js.map +1 -0
  247. package/dist/components/GrDialog/index.js +2 -2
  248. package/dist/components/GrDialogService/chunks/{GrDialogService-CHVR4sOk.js → GrDialogService-k4SYIv64.js} +4 -4
  249. package/dist/components/GrDialogService/chunks/{GrDialogService-CHVR4sOk.js.map → GrDialogService-k4SYIv64.js.map} +1 -1
  250. package/dist/components/GrDialogService/index.js +1 -1
  251. package/dist/components/GrDivider/chunks/{GrDivider-CAhm9jbE.js → GrDivider-BXavGcMX.js} +1 -1
  252. package/dist/components/GrDivider/chunks/GrDivider-BXavGcMX.js.map +1 -0
  253. package/dist/components/GrDivider/index.js +2 -2
  254. package/dist/components/GrDrawer/chunks/{GrDrawer-CXy3-SVd.js → GrDrawer-BCreL-eq.js} +3 -3
  255. package/dist/components/GrDrawer/chunks/{GrDrawer-CXy3-SVd.js.map → GrDrawer-BCreL-eq.js.map} +1 -1
  256. package/dist/components/GrDrawer/index.js +2 -2
  257. package/dist/components/GrDropdown/chunks/{GrDropdown-BfTmdPyh.js → GrDropdown-13Wt9eB_.js} +16 -8
  258. package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js.map +1 -0
  259. package/dist/components/GrDropdown/index.js +2 -2
  260. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-l5HEqzjr.js → GrDropdownMenu-5zoOL8Oj.js} +3 -3
  261. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-5zoOL8Oj.js.map +1 -0
  262. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenuList-BGA_xNVo.js → GrDropdownMenuList-CT_1TQJo.js} +1 -1
  263. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-CT_1TQJo.js.map +1 -0
  264. package/dist/components/GrDropdownMenu/index.js +3 -3
  265. package/dist/components/GrEmptyState/chunks/{GrEmptyState-CS9do8eM.js → GrEmptyState-D4T5pzXA.js} +2 -2
  266. package/dist/components/GrEmptyState/chunks/{GrEmptyState-CS9do8eM.js.map → GrEmptyState-D4T5pzXA.js.map} +1 -1
  267. package/dist/components/GrEmptyState/index.js +2 -2
  268. package/dist/components/GrFilePreview/chunks/{GrFilePreview-D0Bt-0nn.js → GrFilePreview-C9Z7hMK8.js} +1 -1
  269. package/dist/components/GrFilePreview/chunks/{GrFilePreview-D0Bt-0nn.js.map → GrFilePreview-C9Z7hMK8.js.map} +1 -1
  270. package/dist/components/GrFilePreview/index.js +2 -2
  271. package/dist/components/GrFileUpload/chunks/{GrFileUpload-wNXzHb5N.js → GrFileUpload-FjbaYGjL.js} +2 -2
  272. package/dist/components/GrFileUpload/chunks/{GrFileUpload-wNXzHb5N.js.map → GrFileUpload-FjbaYGjL.js.map} +1 -1
  273. package/dist/components/GrFileUpload/index.js +2 -2
  274. package/dist/components/GrForm/chunks/{GrForm-CAlJg-wb.js → GrForm-CrnfCsgj.js} +8 -2
  275. package/dist/components/GrForm/chunks/GrForm-CrnfCsgj.js.map +1 -0
  276. package/dist/components/GrForm/index.js +1 -1
  277. package/dist/components/GrFormField/chunks/GrFormField-DSylU7IU.js.map +1 -1
  278. package/dist/components/GrFormField/index.js +1 -1
  279. package/dist/components/GrFormFile/chunks/{GrFormFile-DhVkB2gm.js → GrFormFile-ChdKlN83.js} +3 -3
  280. package/dist/components/GrFormFile/chunks/{GrFormFile-DhVkB2gm.js.map → GrFormFile-ChdKlN83.js.map} +1 -1
  281. package/dist/components/GrFormFile/index.js +2 -2
  282. package/dist/components/GrFormSection/chunks/{GrFormSection-CdGWvzXO.js → GrFormSection-ejieOg9H.js} +1 -1
  283. package/dist/components/GrFormSection/chunks/{GrFormSection-CdGWvzXO.js.map → GrFormSection-ejieOg9H.js.map} +1 -1
  284. package/dist/components/GrFormSection/index.js +2 -2
  285. package/dist/components/GrIcon/chunks/{GrIcon-bQwtZtYT.js → GrIcon-Bn1XEriL.js} +1 -1
  286. package/dist/components/GrIcon/chunks/GrIcon-Bn1XEriL.js.map +1 -0
  287. package/dist/components/GrIcon/index.js +2 -2
  288. package/dist/components/GrImageViewer/chunks/{GrImageViewer-ng7el4wn.js → GrImageViewer-COUcpOgR.js} +2 -2
  289. package/dist/components/GrImageViewer/chunks/GrImageViewer-COUcpOgR.js.map +1 -0
  290. package/dist/components/GrImageViewer/index.js +2 -2
  291. package/dist/components/GrInput/chunks/{GrInput-Bb_pxqpT.js → GrInput-BqqoEl1b.js} +2 -2
  292. package/dist/components/GrInput/chunks/GrInput-BqqoEl1b.js.map +1 -0
  293. package/dist/components/GrInput/index.js +2 -2
  294. package/dist/components/GrInputTag/chunks/{GrInputTag-UI8kIB_F.js → GrInputTag-LP3smqOv.js} +46 -49
  295. package/dist/components/GrInputTag/chunks/GrInputTag-LP3smqOv.js.map +1 -0
  296. package/dist/components/GrInputTag/index.js +2 -2
  297. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-jk3ylOTc.js → GrJsonViewer-BoldivFc.js} +4 -4
  298. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-jk3ylOTc.js.map → GrJsonViewer-BoldivFc.js.map} +1 -1
  299. package/dist/components/GrJsonViewer/index.js +2 -2
  300. package/dist/components/GrKbd/chunks/{GrKbd-BXbhsH0M.js → GrKbd-D2ga1H9i.js} +1 -1
  301. package/dist/components/GrKbd/chunks/{GrKbd-BXbhsH0M.js.map → GrKbd-D2ga1H9i.js.map} +1 -1
  302. package/dist/components/GrKbd/index.js +2 -2
  303. package/dist/components/GrLink/chunks/{GrLink-BQb7U4so.js → GrLink-DAGCFYO7.js} +2 -2
  304. package/dist/components/GrLink/chunks/GrLink-DAGCFYO7.js.map +1 -0
  305. package/dist/components/GrLink/index.js +2 -2
  306. package/dist/components/GrList/chunks/{GrList-B5bclmHV.js → GrList-DrXrKU37.js} +3 -3
  307. package/dist/components/GrList/chunks/GrList-DrXrKU37.js.map +1 -0
  308. package/dist/components/GrList/index.js +2 -2
  309. package/dist/components/GrList/styles.css +1 -1
  310. package/dist/components/GrLoading/chunks/{GrLoading-DeBFTvFX.js → GrLoading-DDD8WAIH.js} +2 -2
  311. package/dist/components/GrLoading/chunks/{GrLoading-DeBFTvFX.js.map → GrLoading-DDD8WAIH.js.map} +1 -1
  312. package/dist/components/GrLoading/index.js +2 -2
  313. package/dist/components/GrModal/index.js +1 -1
  314. package/dist/components/GrNavbar/chunks/{GrNavbar-DPj30sHX.js → GrNavbar-DhswlYyh.js} +3 -3
  315. package/dist/components/GrNavbar/chunks/{GrNavbar-DPj30sHX.js.map → GrNavbar-DhswlYyh.js.map} +1 -1
  316. package/dist/components/GrNavbar/index.js +2 -2
  317. package/dist/components/GrNumberInput/chunks/{GrNumberInput-V2mZvDDC.js → GrNumberInput-Daud9Jdm.js} +3 -14
  318. package/dist/components/GrNumberInput/chunks/GrNumberInput-Daud9Jdm.js.map +1 -0
  319. package/dist/components/GrNumberInput/index.js +2 -2
  320. package/dist/components/GrPagination/chunks/{GrPagination-DG7kp6VL.js → GrPagination-kQhWlfs-.js} +3 -3
  321. package/dist/components/GrPagination/chunks/{GrPagination-DG7kp6VL.js.map → GrPagination-kQhWlfs-.js.map} +1 -1
  322. package/dist/components/GrPagination/index.js +2 -2
  323. package/dist/components/GrPopover/chunks/{GrPopover-DDChM1jD.js → GrPopover-CofG-Tfe.js} +16 -8
  324. package/dist/components/GrPopover/chunks/GrPopover-CofG-Tfe.js.map +1 -0
  325. package/dist/components/GrPopover/index.js +2 -2
  326. package/dist/components/GrProgressBar/index.js +1 -1
  327. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-BeUx1g1f.js → GrProgressCircle-D3yWXYDa.js} +1 -1
  328. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-BeUx1g1f.js.map → GrProgressCircle-D3yWXYDa.js.map} +1 -1
  329. package/dist/components/GrProgressCircle/index.js +2 -2
  330. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-BGuMUzlv.js → GrPromptDialog-D1ADRFxx.js} +6 -6
  331. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-D1ADRFxx.js.map +1 -0
  332. package/dist/components/GrPromptDialog/index.js +1 -1
  333. package/dist/components/GrRadio/chunks/{GrRadio-BdhknbO8.js → GrRadio-C_rESc5g.js} +1 -1
  334. package/dist/components/GrRadio/chunks/GrRadio-C_rESc5g.js.map +1 -0
  335. package/dist/components/GrRadio/index.js +2 -2
  336. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-7w4bXIg_.js → GrRadioGroup-CdQO4G-U.js} +2 -2
  337. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-7w4bXIg_.js.map → GrRadioGroup-CdQO4G-U.js.map} +1 -1
  338. package/dist/components/GrRadioGroup/index.js +1 -1
  339. package/dist/components/GrRating/chunks/{GrRating-BSEtaibx.js → GrRating-IEiTOdnF.js} +1 -1
  340. package/dist/components/GrRating/chunks/{GrRating-BSEtaibx.js.map → GrRating-IEiTOdnF.js.map} +1 -1
  341. package/dist/components/GrRating/index.js +2 -2
  342. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DA4q90Zt.js → GrResponseErrorBanner-C_skObtv.js} +1 -1
  343. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DA4q90Zt.js.map → GrResponseErrorBanner-C_skObtv.js.map} +1 -1
  344. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-BhP-xw2P.js → GrResponseErrorBanner-DudGc9LO.js} +4 -4
  345. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-BhP-xw2P.js.map → GrResponseErrorBanner-DudGc9LO.js.map} +1 -1
  346. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  347. package/dist/components/GrSegmented/chunks/{GrSegmented-DKpo3cnC.js → GrSegmented-CB-VR-yL.js} +1 -1
  348. package/dist/components/GrSegmented/chunks/{GrSegmented-DKpo3cnC.js.map → GrSegmented-CB-VR-yL.js.map} +1 -1
  349. package/dist/components/GrSegmented/index.js +2 -2
  350. package/dist/components/GrSelect/chunks/{GrSelect-BA1wfX3c.js → GrSelect-D-KU9Lx_.js} +96 -89
  351. package/dist/components/GrSelect/chunks/GrSelect-D-KU9Lx_.js.map +1 -0
  352. package/dist/components/GrSelect/index.js +2 -2
  353. package/dist/components/GrSelect/styles.css +1 -1
  354. package/dist/components/GrSidebar/chunks/{GrSidebar-DY-LgIbS.js → GrSidebar-DxPhIiWs.js} +3 -3
  355. package/dist/components/GrSidebar/chunks/{GrSidebar-DY-LgIbS.js.map → GrSidebar-DxPhIiWs.js.map} +1 -1
  356. package/dist/components/GrSidebar/index.js +2 -2
  357. package/dist/components/GrSlider/chunks/{GrSlider-s99yAyz6.js → GrSlider-DJfeEl4h.js} +5 -4
  358. package/dist/components/GrSlider/chunks/GrSlider-DJfeEl4h.js.map +1 -0
  359. package/dist/components/GrSlider/index.js +2 -2
  360. package/dist/components/GrSortableList/chunks/{GrSortableList-BMXyxmRc.js → GrSortableList-DGs3Pi2J.js} +2 -2
  361. package/dist/components/GrSortableList/chunks/{GrSortableList-BMXyxmRc.js.map → GrSortableList-DGs3Pi2J.js.map} +1 -1
  362. package/dist/components/GrSortableList/index.js +2 -2
  363. package/dist/components/GrSplitter/chunks/{GrSplitter-B9_JrT7q.js → GrSplitter-MltjaADn.js} +1 -1
  364. package/dist/components/GrSplitter/chunks/{GrSplitter-B9_JrT7q.js.map → GrSplitter-MltjaADn.js.map} +1 -1
  365. package/dist/components/GrSplitter/index.js +2 -2
  366. package/dist/components/GrStatistic/chunks/{GrStatistic-BJQv5aOS.js → GrStatistic-qHVasK37.js} +2 -2
  367. package/dist/components/GrStatistic/chunks/GrStatistic-qHVasK37.js.map +1 -0
  368. package/dist/components/GrStatistic/index.js +2 -2
  369. package/dist/components/GrSteps/chunks/{GrSteps-Cce4rfr3.js → GrSteps-C2jxpZoN.js} +1 -1
  370. package/dist/components/GrSteps/chunks/{GrSteps-Cce4rfr3.js.map → GrSteps-C2jxpZoN.js.map} +1 -1
  371. package/dist/components/GrSteps/index.js +2 -2
  372. package/dist/components/GrSwitch/chunks/{GrSwitch-CHiIj9GT.js → GrSwitch-g_OOSkh7.js} +5 -2
  373. package/dist/components/GrSwitch/chunks/GrSwitch-g_OOSkh7.js.map +1 -0
  374. package/dist/components/GrSwitch/index.js +2 -2
  375. package/dist/components/GrTabPanels/chunks/GrTabPanels-BZQS8-JM.js.map +1 -1
  376. package/dist/components/GrTable/chunks/{GrTable-f3qCWa99.js → GrTable-Zcdz2St_.js} +1 -1
  377. package/dist/components/GrTable/chunks/GrTable-Zcdz2St_.js.map +1 -0
  378. package/dist/components/GrTable/index.js +2 -2
  379. package/dist/components/GrTabs/chunks/{GrTabs-CBHZkOFY.js → GrTabs-ugDnkk-2.js} +2 -2
  380. package/dist/components/GrTabs/chunks/{GrTabs-CBHZkOFY.js.map → GrTabs-ugDnkk-2.js.map} +1 -1
  381. package/dist/components/GrTabs/index.js +2 -2
  382. package/dist/components/GrTextarea/chunks/{GrTextarea-CNnkcIG1.js → GrTextarea-KREK75Bu.js} +1 -1
  383. package/dist/components/GrTextarea/chunks/{GrTextarea-CNnkcIG1.js.map → GrTextarea-KREK75Bu.js.map} +1 -1
  384. package/dist/components/GrTextarea/index.js +2 -2
  385. package/dist/components/GrTimeline/chunks/{GrTimeline-DIDdkTn7.js → GrTimeline-BiHpArsg.js} +1 -1
  386. package/dist/components/GrTimeline/chunks/{GrTimeline-DIDdkTn7.js.map → GrTimeline-BiHpArsg.js.map} +1 -1
  387. package/dist/components/GrTimeline/index.js +2 -2
  388. package/dist/components/GrToaster/chunks/{GrToaster-CC49q3a4.js → GrToaster-CUi3ldJ0.js} +6 -6
  389. package/dist/components/GrToaster/chunks/GrToaster-CUi3ldJ0.js.map +1 -0
  390. package/dist/components/GrToaster/index.js +2 -2
  391. package/dist/components/GrToaster/styles.css +1 -1
  392. package/dist/components/GrTooltip/chunks/{GrTooltip-B6AwE_UX.js → GrTooltip-BKFjqu0E.js} +4 -4
  393. package/dist/components/GrTooltip/chunks/GrTooltip-BKFjqu0E.js.map +1 -0
  394. package/dist/components/GrTooltip/index.js +2 -2
  395. package/dist/components/GrTree/chunks/{GrTree-J5q8aCya.js → GrTree-D5ozu6U4.js} +15 -25
  396. package/dist/components/GrTree/chunks/GrTree-D5ozu6U4.js.map +1 -0
  397. package/dist/components/GrTree/index.js +1 -1
  398. package/dist/components/GrTree/styles.css +1 -1
  399. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DEfxWAc3.js → GrTreeSelect-CS1WQWsT.js} +18 -10
  400. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-CS1WQWsT.js.map +1 -0
  401. package/dist/components/GrTreeSelect/index.js +2 -2
  402. package/dist/components/GrValue/chunks/{GrValue-CZsuZ4x-.js → GrValue-ezkQM05Q.js} +1 -1
  403. package/dist/components/GrValue/chunks/{GrValue-CZsuZ4x-.js.map → GrValue-ezkQM05Q.js.map} +1 -1
  404. package/dist/components/GrValue/index.js +2 -2
  405. package/dist/directives/loading.js +1 -1
  406. package/dist/granular-provider-node.js +1 -1
  407. package/dist/granular-provider.js +1 -1
  408. package/dist/index.js +143 -143
  409. package/dist/styles/tokens.css +11 -0
  410. package/dist/theme.js +1 -1
  411. package/dist/tokens.js +1 -1
  412. package/dist/types/components/GrAutocomplete/GrAutocomplete.vue.d.ts +4 -3
  413. package/dist/types/components/GrAutocomplete/grAutocompleteStyles.d.ts +2 -2
  414. package/dist/types/components/GrBadge/GrBadge.vue.d.ts +3 -3
  415. package/dist/types/components/GrBadgeWrap/GrBadgeWrap.vue.d.ts +3 -3
  416. package/dist/types/components/GrBadgeWrap/grBadgeWrapStyles.d.ts +1 -1
  417. package/dist/types/components/GrBottomNav/grBottomNavStyles.d.ts +1 -1
  418. package/dist/types/components/GrButton/GrButton.vue.d.ts +7 -7
  419. package/dist/types/components/GrButtonGroup/GrButtonGroup.vue.d.ts +3 -3
  420. package/dist/types/components/GrCard/GrCard.vue.d.ts +7 -9
  421. package/dist/types/components/GrCheckbox/GrCheckbox.vue.d.ts +1 -1
  422. package/dist/types/components/GrCheckboxGroup/GrCheckboxGroup.vue.d.ts +1 -1
  423. package/dist/types/components/GrChip/GrChip.vue.d.ts +19 -1
  424. package/dist/types/components/GrChip/grChipStyles.d.ts +10 -6
  425. package/dist/types/components/GrChipGroup/GrChipGroup.vue.d.ts +2 -2
  426. package/dist/types/components/GrCollapse/GrCollapseItem.vue.d.ts +9 -11
  427. package/dist/types/components/GrColorPicker/GrColorPicker.vue.d.ts +1 -1
  428. package/dist/types/components/GrCommandPalette/GrCommandPalette.vue.d.ts +13 -13
  429. package/dist/types/components/GrCommandPalette/grCommandPaletteStyles.d.ts +5 -5
  430. package/dist/types/components/GrConfigProvider/GrConfigProvider.vue.d.ts +3 -3
  431. package/dist/types/components/GrConfirmDialog/GrConfirmDialog.vue.d.ts +9 -9
  432. package/dist/types/components/GrContextMenu/GrContextMenu.vue.d.ts +9 -9
  433. package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +9 -17
  434. package/dist/types/components/GrDescriptionList/GrDescriptionList.vue.d.ts +16 -13
  435. package/dist/types/components/GrDialog/GrDialogFooter.vue.d.ts +3 -3
  436. package/dist/types/components/GrDivider/GrDivider.vue.d.ts +3 -3
  437. package/dist/types/components/GrDrawer/grDrawerStyles.d.ts +1 -1
  438. package/dist/types/components/GrDropdown/GrDropdown.vue.d.ts +16 -20
  439. package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +16 -19
  440. package/dist/types/components/GrDropdownMenu/GrDropdownMenuColumn.vue.d.ts +3 -3
  441. package/dist/types/components/GrDropdownMenu/GrDropdownMenuColumns.vue.d.ts +3 -3
  442. package/dist/types/components/GrDropdownMenu/GrDropdownMenuGroup.vue.d.ts +3 -3
  443. package/dist/types/components/GrDropdownMenu/GrDropdownMenuHeader.vue.d.ts +3 -3
  444. package/dist/types/components/GrDropdownMenu/GrDropdownMenuItem.vue.d.ts +7 -7
  445. package/dist/types/components/GrDropdownMenu/GrDropdownMenuList.vue.d.ts +3 -3
  446. package/dist/types/components/GrDropdownMenu/grDropdownMenuStyles.d.ts +3 -3
  447. package/dist/types/components/GrForm/GrForm.vue.d.ts +5 -1
  448. package/dist/types/components/GrFormField/GrFormField.vue.d.ts +11 -11
  449. package/dist/types/components/GrFormFile/GrFormFile.vue.d.ts +1 -1
  450. package/dist/types/components/GrFormSection/grFormSectionStyles.d.ts +2 -2
  451. package/dist/types/components/GrIcon/GrIcon.vue.d.ts +3 -3
  452. package/dist/types/components/GrImageViewer/GrImageViewer.vue.d.ts +6 -25
  453. package/dist/types/components/GrInput/GrInput.vue.d.ts +10 -2
  454. package/dist/types/components/GrInput/grInputStyles.d.ts +4 -4
  455. package/dist/types/components/GrInputTag/GrInputTag.vue.d.ts +2 -2
  456. package/dist/types/components/GrInputTag/grInputTagStyles.d.ts +0 -6
  457. package/dist/types/components/GrKbd/GrKbd.vue.d.ts +3 -3
  458. package/dist/types/components/GrKbd/grKbdStyles.d.ts +5 -0
  459. package/dist/types/components/GrLink/GrLink.vue.d.ts +3 -3
  460. package/dist/types/components/GrList/GrList.vue.d.ts +12 -6
  461. package/dist/types/components/GrList/GrListItem.vue.d.ts +9 -9
  462. package/dist/types/components/GrList/grListStyles.d.ts +3 -3
  463. package/dist/types/components/GrNavbar/grNavbarStyles.d.ts +1 -1
  464. package/dist/types/components/GrNumberInput/GrNumberInput.vue.d.ts +1 -1
  465. package/dist/types/components/GrNumberInput/numberInputMath.d.ts +1 -6
  466. package/dist/types/components/GrPopover/GrPopover.vue.d.ts +16 -17
  467. package/dist/types/components/GrPromptDialog/GrPromptDialog.vue.d.ts +9 -9
  468. package/dist/types/components/GrRadio/GrRadio.vue.d.ts +6 -8
  469. package/dist/types/components/GrRadioGroup/GrRadioGroup.vue.d.ts +1 -1
  470. package/dist/types/components/GrRadioGroup/index.d.ts +1 -1
  471. package/dist/types/components/GrRating/GrRating.vue.d.ts +1 -1
  472. package/dist/types/components/GrSegmented/GrSegmented.vue.d.ts +1 -1
  473. package/dist/types/components/GrSelect/grSelectStyles.d.ts +1 -1
  474. package/dist/types/components/GrSidebar/GrSidebar.vue.d.ts +7 -7
  475. package/dist/types/components/GrSidebar/grSidebarStyles.d.ts +4 -4
  476. package/dist/types/components/GrSlider/GrSlider.vue.d.ts +1 -1
  477. package/dist/types/components/GrSortableList/grSortableListStyles.d.ts +2 -2
  478. package/dist/types/components/GrSplitter/GrSplitter.vue.d.ts +1 -1
  479. package/dist/types/components/GrStatistic/GrStatistic.vue.d.ts +13 -13
  480. package/dist/types/components/GrSteps/grStepsStyles.d.ts +2 -2
  481. package/dist/types/components/GrSwitch/GrSwitch.vue.d.ts +10 -2
  482. package/dist/types/components/GrTabPanels/GrTabPanel.vue.d.ts +3 -3
  483. package/dist/types/components/GrTabPanels/GrTabPanels.vue.d.ts +3 -3
  484. package/dist/types/components/GrTable/GrTable.vue.d.ts +13 -13
  485. package/dist/types/components/GrTextarea/GrTextarea.vue.d.ts +1 -1
  486. package/dist/types/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
  487. package/dist/types/components/shared/classTokens.d.ts +8 -0
  488. package/dist/types/components/shared/numericStep.d.ts +14 -0
  489. package/dist/types/components/shared/overlayTransition.d.ts +33 -0
  490. package/dist/web-types.json +305 -229
  491. package/package.json +3 -3
  492. package/tokens/foundation.json +41 -1
  493. package/dist/chunks/classTokens-BzazaERD.js +0 -6
  494. package/dist/chunks/classTokens-BzazaERD.js.map +0 -1
  495. package/dist/chunks/config-9PYRZlKE.js.map +0 -1
  496. package/dist/chunks/config-B4w5gBD0.js.map +0 -1
  497. package/dist/chunks/config-BEVBsvni2.js.map +0 -1
  498. package/dist/chunks/config-BN4sXYXn.js.map +0 -1
  499. package/dist/chunks/config-BViooYNv.js.map +0 -1
  500. package/dist/chunks/config-B_qi4Fma.js.map +0 -1
  501. package/dist/chunks/config-Baj7Wvx-.js.map +0 -1
  502. package/dist/chunks/config-BbY2SKGE.js.map +0 -1
  503. package/dist/chunks/config-BfsBepkH.js.map +0 -1
  504. package/dist/chunks/config-Bpt9Xbxm.js.map +0 -1
  505. package/dist/chunks/config-BsPDQ0dS.js.map +0 -1
  506. package/dist/chunks/config-BtXRpAtq.js.map +0 -1
  507. package/dist/chunks/config-BuGlNPE92.js.map +0 -1
  508. package/dist/chunks/config-C8tVRv8i.js.map +0 -1
  509. package/dist/chunks/config-CIcynHGr.js.map +0 -1
  510. package/dist/chunks/config-CN2ULSQL.js.map +0 -1
  511. package/dist/chunks/config-CN970ZcJ.js.map +0 -1
  512. package/dist/chunks/config-COvrUfhR.js.map +0 -1
  513. package/dist/chunks/config-Cl4ST6Qd.js.map +0 -1
  514. package/dist/chunks/config-Cwd3yjRw.js.map +0 -1
  515. package/dist/chunks/config-D7zgE_Gr.js.map +0 -1
  516. package/dist/chunks/config-D94Z9po5.js.map +0 -1
  517. package/dist/chunks/config-DAX8Deh1.js.map +0 -1
  518. package/dist/chunks/config-DBmdgoVf.js.map +0 -1
  519. package/dist/chunks/config-DCFvmHEv.js.map +0 -1
  520. package/dist/chunks/config-DGWFal5h.js.map +0 -1
  521. package/dist/chunks/config-DKdlqXiY.js.map +0 -1
  522. package/dist/chunks/config-DSJY6aEl.js.map +0 -1
  523. package/dist/chunks/config-DWN4x6_e.js.map +0 -1
  524. package/dist/chunks/config-Dgj1Q6JY.js.map +0 -1
  525. package/dist/chunks/config-Dguto_6o.js.map +0 -1
  526. package/dist/chunks/config-DtgYuGUn2.js.map +0 -1
  527. package/dist/chunks/config-rkcKArZQ.js.map +0 -1
  528. package/dist/chunks/config-vSji8w90.js.map +0 -1
  529. package/dist/chunks/config-zjeAJ4ho2.js.map +0 -1
  530. package/dist/chunks/context-BfIQv7m12.js.map +0 -1
  531. package/dist/chunks/generated-CJe6hdiE.js.map +0 -1
  532. package/dist/chunks/grBadgeStyles-C1S56Alo.js.map +0 -1
  533. package/dist/chunks/grDropdownMenuStyles-D1LOkdVj.js.map +0 -1
  534. package/dist/chunks/grInputStyles-CJeMhUe5.js.map +0 -1
  535. package/dist/chunks/grKbdStyles-CeZhGQHr.js.map +0 -1
  536. package/dist/chunks/grLinkStyles-BoL8QYcV.js.map +0 -1
  537. package/dist/chunks/grPopoverStyles-Cke_BZsM.js.map +0 -1
  538. package/dist/chunks/grRadioStyles-BTjGNhtU.js.map +0 -1
  539. package/dist/chunks/grSelectStyles-eTz4uPMP.js.map +0 -1
  540. package/dist/chunks/grTextareaStyles-iOcHuVMM.js.map +0 -1
  541. package/dist/chunks/useMenuItemsFocus-OyOnIAlr.js.map +0 -1
  542. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-D9c9BNFg.js.map +0 -1
  543. package/dist/components/GrBadge/chunks/GrBadge-_9rCHuE1.js.map +0 -1
  544. package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-DDoFQscR.js.map +0 -1
  545. package/dist/components/GrButton/chunks/GrButton-DCz-Wn-V.js.map +0 -1
  546. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-BQo5VQ_c.js.map +0 -1
  547. package/dist/components/GrCard/chunks/GrCard-aQIjk6I9.js.map +0 -1
  548. package/dist/components/GrChip/chunks/GrChip-BnL2FedO.js.map +0 -1
  549. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BPsVN_XU.js.map +0 -1
  550. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-CwXNLyzk.js.map +0 -1
  551. package/dist/components/GrContextMenu/chunks/GrContextMenu-BbAJholy.js.map +0 -1
  552. package/dist/components/GrDataTable/chunks/GrDataTable-NRbrXpdx.js.map +0 -1
  553. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-1mICfrqX.js.map +0 -1
  554. package/dist/components/GrDialog/chunks/GrDialog-5Y5K87ss.js.map +0 -1
  555. package/dist/components/GrDivider/chunks/GrDivider-CAhm9jbE.js.map +0 -1
  556. package/dist/components/GrDropdown/chunks/GrDropdown-BfTmdPyh.js.map +0 -1
  557. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-l5HEqzjr.js.map +0 -1
  558. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-BGA_xNVo.js.map +0 -1
  559. package/dist/components/GrForm/chunks/GrForm-CAlJg-wb.js.map +0 -1
  560. package/dist/components/GrIcon/chunks/GrIcon-bQwtZtYT.js.map +0 -1
  561. package/dist/components/GrImageViewer/chunks/GrImageViewer-ng7el4wn.js.map +0 -1
  562. package/dist/components/GrInput/chunks/GrInput-Bb_pxqpT.js.map +0 -1
  563. package/dist/components/GrInputTag/chunks/GrInputTag-UI8kIB_F.js.map +0 -1
  564. package/dist/components/GrLink/chunks/GrLink-BQb7U4so.js.map +0 -1
  565. package/dist/components/GrList/chunks/GrList-B5bclmHV.js.map +0 -1
  566. package/dist/components/GrNumberInput/chunks/GrNumberInput-V2mZvDDC.js.map +0 -1
  567. package/dist/components/GrPopover/chunks/GrPopover-DDChM1jD.js.map +0 -1
  568. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-BGuMUzlv.js.map +0 -1
  569. package/dist/components/GrRadio/chunks/GrRadio-BdhknbO8.js.map +0 -1
  570. package/dist/components/GrSelect/chunks/GrSelect-BA1wfX3c.js.map +0 -1
  571. package/dist/components/GrSlider/chunks/GrSlider-s99yAyz6.js.map +0 -1
  572. package/dist/components/GrStatistic/chunks/GrStatistic-BJQv5aOS.js.map +0 -1
  573. package/dist/components/GrSwitch/chunks/GrSwitch-CHiIj9GT.js.map +0 -1
  574. package/dist/components/GrTable/chunks/GrTable-f3qCWa99.js.map +0 -1
  575. package/dist/components/GrToaster/chunks/GrToaster-CC49q3a4.js.map +0 -1
  576. package/dist/components/GrTooltip/chunks/GrTooltip-B6AwE_UX.js.map +0 -1
  577. package/dist/components/GrTree/chunks/GrTree-J5q8aCya.js.map +0 -1
  578. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-DEfxWAc3.js.map +0 -1
  579. /package/dist/chunks/{config-BuGlNPE92.js → config-BuGlNPE9.js} +0 -0
  580. /package/dist/chunks/{config-DBmdgoVf.js → config-DBmdgoVf2.js} +0 -0
  581. /package/dist/chunks/{context-BfIQv7m12.js → context-BfIQv7m1.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrList-B5bclmHV.js","names":["$slots"],"sources":["../../../../src/components/GrList/GrList.vue","../../../../src/components/GrList/GrList.vue","../../../../src/components/GrList/GrListItem.vue","../../../../src/components/GrList/GrListItem.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T = unknown\">\nimport { computed, onMounted, onUpdated, ref, useSlots, watch } from 'vue'\n\nimport GrCard from '../GrCard'\nimport type { GrCardVariant } from '../GrCard/grCardStyles'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { hasMeaningfulSlotContent } from '../shared/slotNodes'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useVirtualList, type GrVirtualAlign } from '../../composables/useVirtualList'\n\nimport { dividedClass, emptyClass, listSurfaceClass, loadingRowClass } from './grListStyles'\n\nexport interface GrListProps<TItem = unknown> {\n /**\n * Поверхность списка — вариант карточки под ним. `ghost` убирает рамку и\n * тень: список внутри уже существующей карточки не должен давать вторую.\n */\n variant?: GrCardVariant\n /** Показывать ли горизонтальные разделители между элементами (по умолчанию — да). */\n divided?: boolean\n /** Идёт загрузка: вместо пунктов — строки-скелетоны, контейнер помечен `aria-busy`. */\n loading?: boolean\n /** Сколько строк-заглушек показать при `loading`. */\n loadingRows?: number\n /**\n * Список пуст. По умолчанию определяется сам — по содержимому слота; проп\n * нужен там, где потребитель знает лучше (например, слот заполнен\n * заголовками групп, а данных в них нет).\n */\n empty?: boolean\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /**\n * Данные списка. С ними пункты рисует слот `#item`, а не слот по умолчанию, —\n * и только так список знает размер набора, то есть может его виртуализировать.\n */\n items?: TItem[]\n /** Ключ пункта для `v-for`: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Высота видимой части: контейнер становится скроллером. Число — пиксели. */\n maxHeight?: number | string\n /** Держать в DOM только окно вокруг вьюпорта. Требует `items` и `maxHeight`. */\n virtual?: boolean\n /** Оценка высоты пункта до замера. Уточняется по факту при первом рендере. */\n estimatedItemSize?: number\n}\n\nconst props = withDefaults(defineProps<GrListProps<T>>(), {\n // Дефолт живёт в `GrCard` (и его резолвере `GrConfigProvider`), поэтому здесь\n // остаётся `undefined` — иначе список перебивал бы настройку карточки.\n variant: undefined,\n divided: true,\n loading: false,\n loadingRows: 3,\n empty: undefined,\n emptyText: undefined,\n items: undefined,\n itemKey: undefined,\n maxHeight: undefined,\n virtual: false,\n // Строка обычной плотности с заголовком и описанием.\n estimatedItemSize: 56,\n})\n\nconst slots = useSlots()\nconst { t } = useGranularityTranslations()\n\nconst listEl = ref<HTMLElement | null>(null)\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.list.empty', 'Nothing here yet'))\n\nconst dataMode = computed(() => props.items !== undefined)\n\n// Пустоту видно по слоту: `v-for` по пустому массиву оставляет фрагмент без\n// узлов, `v-if` — комментарий, и ни то ни другое пунктом списка не является.\nconst isEmpty = computed(() => {\n if (props.empty !== undefined)\n return props.empty\n\n if (dataMode.value)\n return props.items!.length === 0\n\n return !hasMeaningfulSlotContent(slots.default?.() ?? [])\n})\n\nconst showItems = computed(() => !props.loading && !isEmpty.value)\n\n// Разделители нужны только между пунктами: в пустой и загрузочной ветках они\n// оставили бы висящую линию. Клип по радиусу карточки — безусловный.\nconst listClass = computed(() => [\n listSurfaceClass,\n props.divided && showItems.value ? dividedClass : '',\n].filter(Boolean).join(' '))\n\nconst loadingRowCount = computed(() => Math.max(1, Math.trunc(props.loadingRows)))\n\nconst virtualEnabled = computed(() => props.virtual && dataMode.value && props.maxHeight !== undefined)\n\nconst virtualizer = useVirtualList({\n container: listEl,\n count: () => (virtualEnabled.value && showItems.value ? props.items!.length : 0),\n // Замена массива сбрасывает замеры; append в тот же массив их сохраняет.\n source: () => props.items,\n itemSize: () => props.estimatedItemSize,\n // Первый рендер обязан совпасть с серверным: контейнера ещё нет, и окно\n // считается от объявленной высоты, а не от измеренной.\n viewportSize: () => (typeof props.maxHeight === 'number' ? props.maxHeight : undefined),\n})\n\n/** Окно к отрисовке: при выключенной виртуализации — весь набор. */\nconst renderedItems = computed(() => {\n const items = props.items ?? []\n if (!virtualEnabled.value) return items.map((item, index) => ({ item, index }))\n\n const { start, end } = virtualizer.range.value\n return items.slice(start, end).map((item, offset) => ({ item, index: start + offset }))\n})\n\nfunction keyOf(item: T, index: number): string | number {\n const key = props.itemKey\n if (typeof key === 'function') return key(item, index)\n if (typeof key === 'string') return (item as Record<string, unknown>)[key] as string | number\n\n return index\n}\n\n/**\n * ARIA-набор для пункта.\n *\n * Только в виртуальном режиме: в DOM тогда неполный набор, и без этих атрибутов\n * диктор объявил бы «7 из 20» на списке в пять тысяч. Когда набор в DOM целиком,\n * его размер и позицию браузер считает сам, а руками проставленные атрибуты\n * начинают расходиться с разметкой при любой фильтрации.\n */\nfunction ariaOf(index: number): Record<string, number> {\n if (!virtualEnabled.value) return {}\n\n return { 'aria-setsize': props.items!.length, 'aria-posinset': index + 1 }\n}\n\nconst maxHeightStyle = computed(() => {\n const value = props.maxHeight\n if (value == null) return undefined\n return typeof value === 'number' ? `${value}px` : value\n})\n\nconst listStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n const scroller = { overflowY: 'auto' as const, maxHeight: maxHeightStyle.value }\n if (!virtualEnabled.value) return scroller\n\n return { ...scroller, ...virtualizer.spacerStyle.value }\n})\n\n/**\n * Замер отрисованных пунктов.\n *\n * Идёт по детям контейнера, а не по `ref` на элементе: пункт рисует слот\n * потребителя, привязать к нему `ref` неоткуда. В виртуальном режиме прямые\n * дети контейнера — ровно окно (распорки живут псевдоэлементами и детьми не\n * считаются), поэтому сопоставление по позиции однозначно.\n */\nfunction measureRendered(): void {\n if (!virtualEnabled.value) return\n // В состояниях loading/empty дети контейнера — скелетоны и заглушки: их\n // высоты записались бы в замеры под индексами будущих строк данных.\n if (!showItems.value) return\n\n const container = listEl.value\n if (!container) return\n\n const { start } = virtualizer.range.value\n const children = container.children\n for (let offset = 0; offset < children.length; offset++) {\n virtualizer.measure(start + offset, children[offset])\n }\n}\n\nonMounted(measureRendered)\nonUpdated(measureRendered)\n\nfunction scrollToIndex(index: number, align?: GrVirtualAlign): void {\n virtualizer.scrollToIndex(index, align)\n}\n\ndefineExpose({ scrollToIndex })\n\nif (__GR_DEV__) {\n watch(\n () => [props.virtual, dataMode.value, props.maxHeight] as const,\n ([virtual, hasItems, maxHeight]) => {\n if (!virtual) return\n\n if (!hasItems) {\n console.warn(\n '[granularity] GrList: `virtual` требует `items` — пункты из слота по умолчанию '\n + 'список не считает и резать окно ему не из чего.',\n )\n }\n else if (maxHeight === undefined) {\n console.warn(\n '[granularity] GrList: `virtual` требует `maxHeight` — без ограничения высоты '\n + 'вьюпорта нет, и в окно попадает весь набор.',\n )\n }\n },\n { immediate: true },\n )\n\n watch(\n () => [dataMode.value, Boolean(slots.item)] as const,\n ([hasItems, hasSlot]) => {\n if (hasItems && !hasSlot) {\n console.warn(\n '[granularity] GrList: с `items` пункты рисует слот `#item` — без него список пуст.',\n )\n }\n },\n { immediate: true },\n )\n\n onMounted(() => {\n if (!virtualEnabled.value) return\n\n const first = listEl.value?.firstElementChild\n if (first && !first.hasAttribute('aria-posinset')) {\n console.warn(\n '[granularity] GrList: при `virtual` пункты обязаны нести `aria-setsize`/`aria-posinset` — '\n + 'пробросьте `aria` из слота `#item`: <GrListItem v-bind=\"aria\" … />. Иначе диктор '\n + 'посчитает набор по отрисованному окну.',\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n data-gr-list\n role=\"list\"\n :class=\"listClass\"\n :style=\"listStyle\"\n :tabindex=\"maxHeight === undefined ? undefined : 0\"\n :data-gr-virtual=\"virtualEnabled ? '' : undefined\"\n :aria-busy=\"loading ? 'true' : undefined\"\n >\n <template v-if=\"loading\">\n <slot name=\"loading\">\n <div\n v-for=\"row in loadingRowCount\"\n :key=\"row\"\n data-gr-list-loading-row\n :class=\"loadingRowClass\"\n >\n <GrSkeleton />\n </div>\n </slot>\n </template>\n\n <div\n v-else-if=\"isEmpty\"\n data-gr-list-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <template v-else-if=\"dataMode\">\n <template\n v-for=\"entry in renderedItems\"\n :key=\"keyOf(entry.item, entry.index)\"\n >\n <slot\n name=\"item\"\n :item=\"entry.item\"\n :index=\"entry.index\"\n :aria=\"ariaOf(entry.index)\"\n />\n </template>\n </template>\n\n <slot v-else />\n </div>\n </GrCard>\n</template>\n\n<style scoped>\n/* Срезанное сверху и снизу держат псевдоэлементы контейнера: их не за что\n размонтировать, в дереве доступности их нет, а высота приезжает переменными\n в том же патче, что и пункты. Почему не отступы контейнера и не поля крайних\n пунктов — `docs/virtual-list.md`. */\n[data-gr-virtual]::before,\n[data-gr-virtual]::after {\n content: '';\n display: block;\n flex: none;\n}\n\n[data-gr-virtual]::before {\n height: var(--gr-virtual-before, 0px);\n}\n\n[data-gr-virtual]::after {\n height: var(--gr-virtual-after, 0px);\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T = unknown\">\nimport { computed, onMounted, onUpdated, ref, useSlots, watch } from 'vue'\n\nimport GrCard from '../GrCard'\nimport type { GrCardVariant } from '../GrCard/grCardStyles'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { hasMeaningfulSlotContent } from '../shared/slotNodes'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useVirtualList, type GrVirtualAlign } from '../../composables/useVirtualList'\n\nimport { dividedClass, emptyClass, listSurfaceClass, loadingRowClass } from './grListStyles'\n\nexport interface GrListProps<TItem = unknown> {\n /**\n * Поверхность списка — вариант карточки под ним. `ghost` убирает рамку и\n * тень: список внутри уже существующей карточки не должен давать вторую.\n */\n variant?: GrCardVariant\n /** Показывать ли горизонтальные разделители между элементами (по умолчанию — да). */\n divided?: boolean\n /** Идёт загрузка: вместо пунктов — строки-скелетоны, контейнер помечен `aria-busy`. */\n loading?: boolean\n /** Сколько строк-заглушек показать при `loading`. */\n loadingRows?: number\n /**\n * Список пуст. По умолчанию определяется сам — по содержимому слота; проп\n * нужен там, где потребитель знает лучше (например, слот заполнен\n * заголовками групп, а данных в них нет).\n */\n empty?: boolean\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /**\n * Данные списка. С ними пункты рисует слот `#item`, а не слот по умолчанию, —\n * и только так список знает размер набора, то есть может его виртуализировать.\n */\n items?: TItem[]\n /** Ключ пункта для `v-for`: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Высота видимой части: контейнер становится скроллером. Число — пиксели. */\n maxHeight?: number | string\n /** Держать в DOM только окно вокруг вьюпорта. Требует `items` и `maxHeight`. */\n virtual?: boolean\n /** Оценка высоты пункта до замера. Уточняется по факту при первом рендере. */\n estimatedItemSize?: number\n}\n\nconst props = withDefaults(defineProps<GrListProps<T>>(), {\n // Дефолт живёт в `GrCard` (и его резолвере `GrConfigProvider`), поэтому здесь\n // остаётся `undefined` — иначе список перебивал бы настройку карточки.\n variant: undefined,\n divided: true,\n loading: false,\n loadingRows: 3,\n empty: undefined,\n emptyText: undefined,\n items: undefined,\n itemKey: undefined,\n maxHeight: undefined,\n virtual: false,\n // Строка обычной плотности с заголовком и описанием.\n estimatedItemSize: 56,\n})\n\nconst slots = useSlots()\nconst { t } = useGranularityTranslations()\n\nconst listEl = ref<HTMLElement | null>(null)\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.list.empty', 'Nothing here yet'))\n\nconst dataMode = computed(() => props.items !== undefined)\n\n// Пустоту видно по слоту: `v-for` по пустому массиву оставляет фрагмент без\n// узлов, `v-if` — комментарий, и ни то ни другое пунктом списка не является.\nconst isEmpty = computed(() => {\n if (props.empty !== undefined)\n return props.empty\n\n if (dataMode.value)\n return props.items!.length === 0\n\n return !hasMeaningfulSlotContent(slots.default?.() ?? [])\n})\n\nconst showItems = computed(() => !props.loading && !isEmpty.value)\n\n// Разделители нужны только между пунктами: в пустой и загрузочной ветках они\n// оставили бы висящую линию. Клип по радиусу карточки — безусловный.\nconst listClass = computed(() => [\n listSurfaceClass,\n props.divided && showItems.value ? dividedClass : '',\n].filter(Boolean).join(' '))\n\nconst loadingRowCount = computed(() => Math.max(1, Math.trunc(props.loadingRows)))\n\nconst virtualEnabled = computed(() => props.virtual && dataMode.value && props.maxHeight !== undefined)\n\nconst virtualizer = useVirtualList({\n container: listEl,\n count: () => (virtualEnabled.value && showItems.value ? props.items!.length : 0),\n // Замена массива сбрасывает замеры; append в тот же массив их сохраняет.\n source: () => props.items,\n itemSize: () => props.estimatedItemSize,\n // Первый рендер обязан совпасть с серверным: контейнера ещё нет, и окно\n // считается от объявленной высоты, а не от измеренной.\n viewportSize: () => (typeof props.maxHeight === 'number' ? props.maxHeight : undefined),\n})\n\n/** Окно к отрисовке: при выключенной виртуализации — весь набор. */\nconst renderedItems = computed(() => {\n const items = props.items ?? []\n if (!virtualEnabled.value) return items.map((item, index) => ({ item, index }))\n\n const { start, end } = virtualizer.range.value\n return items.slice(start, end).map((item, offset) => ({ item, index: start + offset }))\n})\n\nfunction keyOf(item: T, index: number): string | number {\n const key = props.itemKey\n if (typeof key === 'function') return key(item, index)\n if (typeof key === 'string') return (item as Record<string, unknown>)[key] as string | number\n\n return index\n}\n\n/**\n * ARIA-набор для пункта.\n *\n * Только в виртуальном режиме: в DOM тогда неполный набор, и без этих атрибутов\n * диктор объявил бы «7 из 20» на списке в пять тысяч. Когда набор в DOM целиком,\n * его размер и позицию браузер считает сам, а руками проставленные атрибуты\n * начинают расходиться с разметкой при любой фильтрации.\n */\nfunction ariaOf(index: number): Record<string, number> {\n if (!virtualEnabled.value) return {}\n\n return { 'aria-setsize': props.items!.length, 'aria-posinset': index + 1 }\n}\n\nconst maxHeightStyle = computed(() => {\n const value = props.maxHeight\n if (value == null) return undefined\n return typeof value === 'number' ? `${value}px` : value\n})\n\nconst listStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n const scroller = { overflowY: 'auto' as const, maxHeight: maxHeightStyle.value }\n if (!virtualEnabled.value) return scroller\n\n return { ...scroller, ...virtualizer.spacerStyle.value }\n})\n\n/**\n * Замер отрисованных пунктов.\n *\n * Идёт по детям контейнера, а не по `ref` на элементе: пункт рисует слот\n * потребителя, привязать к нему `ref` неоткуда. В виртуальном режиме прямые\n * дети контейнера — ровно окно (распорки живут псевдоэлементами и детьми не\n * считаются), поэтому сопоставление по позиции однозначно.\n */\nfunction measureRendered(): void {\n if (!virtualEnabled.value) return\n // В состояниях loading/empty дети контейнера — скелетоны и заглушки: их\n // высоты записались бы в замеры под индексами будущих строк данных.\n if (!showItems.value) return\n\n const container = listEl.value\n if (!container) return\n\n const { start } = virtualizer.range.value\n const children = container.children\n for (let offset = 0; offset < children.length; offset++) {\n virtualizer.measure(start + offset, children[offset])\n }\n}\n\nonMounted(measureRendered)\nonUpdated(measureRendered)\n\nfunction scrollToIndex(index: number, align?: GrVirtualAlign): void {\n virtualizer.scrollToIndex(index, align)\n}\n\ndefineExpose({ scrollToIndex })\n\nif (__GR_DEV__) {\n watch(\n () => [props.virtual, dataMode.value, props.maxHeight] as const,\n ([virtual, hasItems, maxHeight]) => {\n if (!virtual) return\n\n if (!hasItems) {\n console.warn(\n '[granularity] GrList: `virtual` требует `items` — пункты из слота по умолчанию '\n + 'список не считает и резать окно ему не из чего.',\n )\n }\n else if (maxHeight === undefined) {\n console.warn(\n '[granularity] GrList: `virtual` требует `maxHeight` — без ограничения высоты '\n + 'вьюпорта нет, и в окно попадает весь набор.',\n )\n }\n },\n { immediate: true },\n )\n\n watch(\n () => [dataMode.value, Boolean(slots.item)] as const,\n ([hasItems, hasSlot]) => {\n if (hasItems && !hasSlot) {\n console.warn(\n '[granularity] GrList: с `items` пункты рисует слот `#item` — без него список пуст.',\n )\n }\n },\n { immediate: true },\n )\n\n onMounted(() => {\n if (!virtualEnabled.value) return\n\n const first = listEl.value?.firstElementChild\n if (first && !first.hasAttribute('aria-posinset')) {\n console.warn(\n '[granularity] GrList: при `virtual` пункты обязаны нести `aria-setsize`/`aria-posinset` — '\n + 'пробросьте `aria` из слота `#item`: <GrListItem v-bind=\"aria\" … />. Иначе диктор '\n + 'посчитает набор по отрисованному окну.',\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n data-gr-list\n role=\"list\"\n :class=\"listClass\"\n :style=\"listStyle\"\n :tabindex=\"maxHeight === undefined ? undefined : 0\"\n :data-gr-virtual=\"virtualEnabled ? '' : undefined\"\n :aria-busy=\"loading ? 'true' : undefined\"\n >\n <template v-if=\"loading\">\n <slot name=\"loading\">\n <div\n v-for=\"row in loadingRowCount\"\n :key=\"row\"\n data-gr-list-loading-row\n :class=\"loadingRowClass\"\n >\n <GrSkeleton />\n </div>\n </slot>\n </template>\n\n <div\n v-else-if=\"isEmpty\"\n data-gr-list-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <template v-else-if=\"dataMode\">\n <template\n v-for=\"entry in renderedItems\"\n :key=\"keyOf(entry.item, entry.index)\"\n >\n <slot\n name=\"item\"\n :item=\"entry.item\"\n :index=\"entry.index\"\n :aria=\"ariaOf(entry.index)\"\n />\n </template>\n </template>\n\n <slot v-else />\n </div>\n </GrCard>\n</template>\n\n<style scoped>\n/* Срезанное сверху и снизу держат псевдоэлементы контейнера: их не за что\n размонтировать, в дереве доступности их нет, а высота приезжает переменными\n в том же патче, что и пункты. Почему не отступы контейнера и не поля крайних\n пунктов — `docs/virtual-list.md`. */\n[data-gr-virtual]::before,\n[data-gr-virtual]::after {\n content: '';\n display: block;\n flex: none;\n}\n\n[data-gr-virtual]::before {\n height: var(--gr-virtual-before, 0px);\n}\n\n[data-gr-virtual]::after {\n height: var(--gr-virtual-after, 0px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, markRaw, useSlots, watch, type Component } from 'vue'\n\nimport {\n grListItemPaddingClass,\n itemDescriptionClass,\n itemDisabledClass,\n itemHoverClass,\n itemInteractiveClass,\n itemLayoutClass,\n itemTitleClass,\n type GrListItemDensity,\n} from './grListStyles'\n\nexport type { GrListItemDensity } from './grListStyles'\n\nexport interface GrListItemProps {\n /** Заголовок строки. Если передан слот `#title`, проп игнорируется. */\n title?: string\n /** Описание под заголовком. Если передан слот `#description`, проп игнорируется. */\n description?: string\n /** Плотность вертикальных отступов: `regular` — 12px, `compact` — 8px. */\n density?: GrListItemDensity\n /** Ссылка: строка становится `<a>`. */\n href?: string\n /** Свой корневой тег строки (`RouterLink`, `Link` от Inertia). Сильнее `href`. */\n as?: string | Component\n /** Кликабельная строка без ссылки — `<button>` с событием `click`. */\n clickable?: boolean\n /** Подсветка при наведении без интерактивности. */\n hoverable?: boolean\n disabled?: boolean\n}\n\nexport interface GrListItemEmits {\n (e: 'click', event: MouseEvent): void\n}\n\nconst props = withDefaults(defineProps<GrListItemProps>(), {\n title: undefined,\n description: undefined,\n density: 'regular',\n href: undefined,\n as: undefined,\n clickable: false,\n hoverable: false,\n disabled: false,\n})\n\nconst emit = defineEmits<GrListItemEmits>()\n\nconst slots = useSlots()\n\nconst hasTitle = computed(() => !!slots.title || !!props.title)\nconst hasDescription = computed(() => !!slots.description || !!props.description)\n\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href || props.clickable))\n\n/**\n * `role=\"listitem\"` остаётся на обёртке, а строка целиком — вложенный элемент:\n * `<a role=\"listitem\">` потерял бы роль ссылки, а интерактив снаружи разорвал бы\n * связку `list` → `listitem`. Обычная строка идёт по той же схеме, чтобы\n * разметка содержимого была написана один раз, а не по копии на ветку.\n */\nconst rowTag = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'div'\n\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n\n return props.href ? 'a' : 'button'\n})\n\nconst rowClass = computed(() => [\n itemLayoutClass,\n grListItemPaddingClass(props.density),\n isInteractive.value ? `${itemInteractiveClass} ${itemHoverClass}` : '',\n !isInteractive.value && props.hoverable && !props.disabled ? itemHoverClass : '',\n props.disabled ? itemDisabledClass : '',\n].filter(Boolean).join(' '))\n\nfunction onClick(event: MouseEvent): void {\n // `disabled` сюда попадает вместе с обычной строкой: и то и другое —\n // не-интерактив, а кликов не эмитит ни один.\n if (!isInteractive.value)\n return\n\n emit('click', event)\n}\n\n/** Теги, попадающие в таб-порядок сами. `<a>` — только со ссылкой. */\nfunction isFocusableTag(tag: string): boolean {\n return tag === 'button' || (tag === 'a' && !!props.href)\n}\n\nif (__GR_DEV__) {\n watch(\n () => [isInteractive.value, props.as] as const,\n ([interactive, as]) => {\n // Компонент из `as` (`RouterLink`, Inertia `Link`) рендерит `<a>`, но узнать\n // это до рендера нельзя — предупреждаем только по явно названному тегу.\n if (!interactive || typeof as !== 'string' || isFocusableTag(as)) return\n\n console.warn(\n `[granularity] GrListItem: as=\"${as}\" не попадает в таб-порядок — строка кликается `\n + 'мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (`button`, `a` со ссылкой), '\n + 'или компонент роутера.',\n )\n },\n { immediate: true },\n )\n}\n</script>\n\n<template>\n <div\n data-gr-list-item\n role=\"listitem\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n >\n <component\n :is=\"rowTag\"\n :data-gr-list-item-action=\"isInteractive ? '' : undefined\"\n :type=\"rowTag === 'button' ? 'button' : undefined\"\n :href=\"isInteractive ? href : undefined\"\n :class=\"rowClass\"\n @click=\"onClick\"\n >\n <div v-if=\"$slots.prefix\" class=\"shrink-0\">\n <slot name=\"prefix\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div v-if=\"hasTitle\" :class=\"itemTitleClass\">\n <slot name=\"title\">\n{{ title }}\n</slot>\n </div>\n <div\n v-if=\"hasDescription\"\n :class=\"itemDescriptionClass\"\n >\n <slot name=\"description\">\n{{ description }}\n</slot>\n </div>\n </div>\n <div v-if=\"$slots.default\" class=\"shrink-0\">\n <slot />\n </div>\n </component>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, markRaw, useSlots, watch, type Component } from 'vue'\n\nimport {\n grListItemPaddingClass,\n itemDescriptionClass,\n itemDisabledClass,\n itemHoverClass,\n itemInteractiveClass,\n itemLayoutClass,\n itemTitleClass,\n type GrListItemDensity,\n} from './grListStyles'\n\nexport type { GrListItemDensity } from './grListStyles'\n\nexport interface GrListItemProps {\n /** Заголовок строки. Если передан слот `#title`, проп игнорируется. */\n title?: string\n /** Описание под заголовком. Если передан слот `#description`, проп игнорируется. */\n description?: string\n /** Плотность вертикальных отступов: `regular` — 12px, `compact` — 8px. */\n density?: GrListItemDensity\n /** Ссылка: строка становится `<a>`. */\n href?: string\n /** Свой корневой тег строки (`RouterLink`, `Link` от Inertia). Сильнее `href`. */\n as?: string | Component\n /** Кликабельная строка без ссылки — `<button>` с событием `click`. */\n clickable?: boolean\n /** Подсветка при наведении без интерактивности. */\n hoverable?: boolean\n disabled?: boolean\n}\n\nexport interface GrListItemEmits {\n (e: 'click', event: MouseEvent): void\n}\n\nconst props = withDefaults(defineProps<GrListItemProps>(), {\n title: undefined,\n description: undefined,\n density: 'regular',\n href: undefined,\n as: undefined,\n clickable: false,\n hoverable: false,\n disabled: false,\n})\n\nconst emit = defineEmits<GrListItemEmits>()\n\nconst slots = useSlots()\n\nconst hasTitle = computed(() => !!slots.title || !!props.title)\nconst hasDescription = computed(() => !!slots.description || !!props.description)\n\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href || props.clickable))\n\n/**\n * `role=\"listitem\"` остаётся на обёртке, а строка целиком — вложенный элемент:\n * `<a role=\"listitem\">` потерял бы роль ссылки, а интерактив снаружи разорвал бы\n * связку `list` → `listitem`. Обычная строка идёт по той же схеме, чтобы\n * разметка содержимого была написана один раз, а не по копии на ветку.\n */\nconst rowTag = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'div'\n\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n\n return props.href ? 'a' : 'button'\n})\n\nconst rowClass = computed(() => [\n itemLayoutClass,\n grListItemPaddingClass(props.density),\n isInteractive.value ? `${itemInteractiveClass} ${itemHoverClass}` : '',\n !isInteractive.value && props.hoverable && !props.disabled ? itemHoverClass : '',\n props.disabled ? itemDisabledClass : '',\n].filter(Boolean).join(' '))\n\nfunction onClick(event: MouseEvent): void {\n // `disabled` сюда попадает вместе с обычной строкой: и то и другое —\n // не-интерактив, а кликов не эмитит ни один.\n if (!isInteractive.value)\n return\n\n emit('click', event)\n}\n\n/** Теги, попадающие в таб-порядок сами. `<a>` — только со ссылкой. */\nfunction isFocusableTag(tag: string): boolean {\n return tag === 'button' || (tag === 'a' && !!props.href)\n}\n\nif (__GR_DEV__) {\n watch(\n () => [isInteractive.value, props.as] as const,\n ([interactive, as]) => {\n // Компонент из `as` (`RouterLink`, Inertia `Link`) рендерит `<a>`, но узнать\n // это до рендера нельзя — предупреждаем только по явно названному тегу.\n if (!interactive || typeof as !== 'string' || isFocusableTag(as)) return\n\n console.warn(\n `[granularity] GrListItem: as=\"${as}\" не попадает в таб-порядок — строка кликается `\n + 'мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (`button`, `a` со ссылкой), '\n + 'или компонент роутера.',\n )\n },\n { immediate: true },\n )\n}\n</script>\n\n<template>\n <div\n data-gr-list-item\n role=\"listitem\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n >\n <component\n :is=\"rowTag\"\n :data-gr-list-item-action=\"isInteractive ? '' : undefined\"\n :type=\"rowTag === 'button' ? 'button' : undefined\"\n :href=\"isInteractive ? href : undefined\"\n :class=\"rowClass\"\n @click=\"onClick\"\n >\n <div v-if=\"$slots.prefix\" class=\"shrink-0\">\n <slot name=\"prefix\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div v-if=\"hasTitle\" :class=\"itemTitleClass\">\n <slot name=\"title\">\n{{ title }}\n</slot>\n </div>\n <div\n v-if=\"hasDescription\"\n :class=\"itemDescriptionClass\"\n >\n <slot name=\"description\">\n{{ description }}\n</slot>\n </div>\n </div>\n <div v-if=\"$slots.default\" class=\"shrink-0\">\n <slot />\n </div>\n </component>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrNumberInput-V2mZvDDC.js","names":["$slots","$attrs"],"sources":["../../../../src/components/GrNumberInput/numberInputMath.ts","../../../../src/components/GrNumberInput/GrNumberInput.vue","../../../../src/components/GrNumberInput/GrNumberInput.vue"],"sourcesContent":["/**\n * Экспоненциальная запись разбирается отдельно: `String(1e-7)` — это `\"1e-7\"`,\n * и поиск точки насчитал бы ноль знаков, после чего округление съело бы всё\n * значение.\n */\nexport function decimalsOf(value: number): number {\n if (!Number.isFinite(value)) return 0\n\n const text = String(Math.abs(value))\n\n const exponential = text.match(/^\\d+(?:\\.(\\d+))?e-(\\d+)$/)\n if (exponential) {\n const [, fraction = '', exponent] = exponential\n return fraction.length + Number(exponent)\n }\n\n const dot = text.indexOf('.')\n return dot === -1 ? 0 : text.length - dot - 1\n}\n\n/**\n * Двоичная дробь копит ошибку, поэтому сумма округляется. Разрядность — по\n * большему из операндов: шаг `0.5` от значения `1.25` обязан дать `1.75`.\n */\nexport function addStep(current: number, step: number): number {\n const decimals = Math.max(decimalsOf(current), decimalsOf(step))\n const sum = current + step\n\n // `toFixed` за пределами 100 знаков бросает.\n return decimals > 100 ? sum : Number(sum.toFixed(decimals))\n}\n\n/**\n * Правило `GrSlider` (`bigStep` в `GrSlider.vue`): десять шагов либо десятая\n * часть диапазона — что крупнее. У поля границы необязательны, и без них\n * диапазона не существует.\n */\nexport function bigStep(step: number, min?: number, max?: number): number {\n const base = step * 10\n if (min === undefined || max === undefined) return base\n\n const span = max - min\n const tenth = Math.round((span / 10) / step) * step\n\n return Math.max(base, tenth || step)\n}\n","<script setup lang=\"ts\">\nimport type { InputHTMLAttributes } from 'vue'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { computed, onBeforeUnmount, ref } from 'vue'\n\nimport {\n clearButtonClass,\n stepperCompactClass,\n stepperWideClass,\n grNumberInputInputClass,\n grNumberInputShellClass,\n type GrNumberInputControlsDirection,\n type GrNumberInputSize,\n type GrNumberInputState,\n type GrNumberInputTextAlign,\n} from './grNumberInputStyles'\nimport { addStep, bigStep } from './numberInputMath'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconChevronLeft from '~icons/lucide/chevron-left'\nimport IconChevronRight from '~icons/lucide/chevron-right'\nimport IconChevronUp from '~icons/lucide/chevron-up'\nimport IconX from '~icons/lucide/x'\nimport { addLen } from '../../composables/internal/useAddonMeasurement'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst ADDON_PX_BY_SIZE: Record<GrNumberInputSize, number> = {\n xs: 32,\n sm: 36,\n md: 40,\n lg: 48,\n}\n\nconst BASE_PADDING_X_LEN_BY_SIZE: Record<GrNumberInputSize, string> = {\n xs: '10px',\n sm: '12px',\n md: '12px',\n lg: '16px',\n}\n\nfunction px(n: number): string {\n return `${n}px`\n}\n\nexport interface GrNumberInputProps {\n /**\n * Значение поля. `null` — пусто.\n *\n * Незавершённый ввод («-», «1,») числом не является и в модель не попадает:\n * пока он набирается, поле держит его во внутреннем черновике, а модель\n * честно говорит «числа пока нет».\n */\n modelValue: number | null\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Быстрый флаг невалидности; эквивалент `state='danger'` + `aria-invalid`. */\n invalid?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n /** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */\n required?: boolean\n /** Доступное имя вне `GrFormField`. */\n ariaLabel?: string\n state?: GrNumberInputState\n name?: string\n id?: string\n size?: GrNumberInputSize\n\n textAlign?: GrNumberInputTextAlign\n\n decimalSeparator?: string\n step?: number\n min?: number\n max?: number\n precision?: number\n\n /** Кнопка очистки значения. */\n clearable?: boolean\n /** A11y-подпись кнопки очистки. */\n clearLabel?: string\n /**\n * BCP-47 локаль для отображения значения (группировка разрядов и разделители\n * через `Intl.NumberFormat`). Работает вместе с `useGrouping`.\n */\n locale?: string\n /**\n * Группировать разряды при отображении (когда поле не в фокусе). При фокусе\n * показывается «сырое» значение для редактирования. По умолчанию выключено.\n */\n useGrouping?: boolean\n\n /** Показывать кнопки +/-. */\n controls?: boolean\n controlsDirection?: GrNumberInputControlsDirection\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: жёсткая ширина (из `*MaxWidth` →\n * `*MinWidth` → дефолт) + обрезка контента по краю (prefix — справа,\n * suffix — слева). По умолчанию аддоны растягиваются под контент.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n\n /** i18n-friendly aria-label для кнопки \"увеличить\". */\n increaseLabel?: string\n /** i18n-friendly aria-label для кнопки \"уменьшить\". */\n decreaseLabel?: string\n}\n\nexport interface GrNumberInputEmits {\n (e: 'update:modelValue', value: number | null): void\n (e: 'change', value: number | null): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n /** Значение стёрто кнопкой очистки. */\n (e: 'clear'): void\n}\n\n// Контекст `GrFormField` — fallback для id/описания/невалидности, как в `GrInput`.\nconst field = useGrFormFieldContext()\n\nconst props = withDefaults(defineProps<GrNumberInputProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: 'decimal',\n disabled: false,\n invalid: false,\n readonly: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n name: undefined,\n id: undefined,\n size: undefined,\n\n textAlign: 'left',\n\n decimalSeparator: '.',\n step: 1,\n min: undefined,\n max: undefined,\n precision: undefined,\n locale: undefined,\n useGrouping: false,\n clearable: undefined,\n clearLabel: undefined,\n\n controls: false,\n controlsDirection: 'vertical',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n\n increaseLabel: undefined,\n decreaseLabel: undefined,\n})\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrNumberInput' })\n\nconst resolvedId = computed(() => props.id ?? field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst emit = defineEmits<GrNumberInputEmits>()\ndefineSlots<{\n /** Аддон слева от поля: знак валюты, иконка. */\n prefix?: () => any\n /** Аддон справа от поля: единица измерения. */\n suffix?: () => any\n}>()\n\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\n// В фокусе поле показывает «сырое» значение: группировка мешала бы правке.\nconst isFocused = ref(false)\n\n/**\n * Строка ровно в том виде, в каком её набирают. Модель к этому моменту может\n * быть `null` («-», «1,» — не числа), и без черновика поле стирало бы под\n * пальцами то, что печатают. Коммит (`change`/`blur`) черновик снимает.\n */\nconst draft = ref<string | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\n\nconst { t, locale } = useGranularityTranslations()\n\n/**\n * Локаль форматирования: свой проп → локаль i18n-адаптера → окружение. Без\n * второго шага мультиязычное приложение передавало бы `locale` каждому полю.\n */\nconst resolvedLocale = computed(() => props.locale ?? locale.value)\nconst resolvedIncreaseLabel = computed(() => props.increaseLabel ?? t('gr.numberInput.increase', 'Increase'))\nconst resolvedDecreaseLabel = computed(() => props.decreaseLabel ?? t('gr.numberInput.decrease', 'Decrease'))\n\nconst hasHorizontalControls = computed(() => props.controls && props.controlsDirection === 'horizontal')\nconst hasVerticalControls = computed(() => props.controls && props.controlsDirection === 'vertical')\n\nconst addonPx = computed(() => ADDON_PX_BY_SIZE[resolvedSize.value])\nconst addonLen = computed(() => px(addonPx.value))\nconst basePaddingXLen = computed(() => BASE_PADDING_X_LEN_BY_SIZE[resolvedSize.value])\n\n/** Кнопки ± справа: вертикальный стек — одна колонка, горизонтальный — вторая. */\nconst rightControlsCount = computed(() =>\n (hasHorizontalControls.value ? 1 : 0) + (hasVerticalControls.value ? 1 : 0))\n\nconst {\n prefixEl,\n suffixEl,\n prefixLen,\n suffixLen,\n prefixStyle,\n suffixStyle,\n fieldPadding: inputStyle,\n} = useControlAddons(() => props, {\n defaultMinWidth: () => addonLen.value,\n paddingX: () => basePaddingXLen.value,\n leadingReserve: () => (hasHorizontalControls.value ? addonLen.value : '0px'),\n trailingReserve: () => (rightControlsCount.value > 0 ? px(addonPx.value * rightControlsCount.value) : '0px'),\n // Суффикс позиционируется абсолютно: без якоря он уехал бы от правого края.\n anchorSuffixRight: true,\n})\n\nfunction addonStyle(side: 'left' | 'right', offset: string): Record<string, string> {\n return {\n width: addonLen.value,\n [side]: offset,\n }\n}\n\nconst verticalControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\n/**\n * Крестик очистки живёт левее правой зоны (суффикс + кнопки ±) — иначе он лёг бы\n * на них. Ширины там же, где их считает поле, поэтому переиспользуем расчёт.\n */\nconst clearButtonStyle = computed(() => {\n const controls = rightControlsCount.value > 0 ? px(addonPx.value * rightControlsCount.value) : '0px'\n\n return { right: addLen(addLen(suffixLen.value, controls), '6px') }\n})\nconst horizontalLeftControlsStyle = computed(() => addonStyle('left', prefixLen.value))\nconst horizontalRightControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\nconst shellClassName = computed(() => {\n return grNumberInputShellClass({\n disabled: isDisabled.value,\n state: props.state,\n invalid: isInvalid.value,\n })\n})\n\nconst inputClassName = computed(() => {\n return grNumberInputInputClass({\n size: resolvedSize.value,\n textAlign: props.textAlign,\n })\n})\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction sanitize(raw: string): string {\n const sep = props.decimalSeparator || '.'\n\n let out = ''\n let hasSepLocal = false\n\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i]\n\n // Ведущий минус: разрешаем один в начале, чтобы можно было ввести\n // отрицательное значение с клавиатуры (границы проверит `clamp` на change).\n if (ch === '-' && out.length === 0) {\n out += '-'\n continue\n }\n\n if (isDigit(ch)) {\n out += ch\n continue\n }\n\n if ((ch === sep || ch === '.' || ch === ',') && !hasSepLocal) {\n out += sep\n hasSepLocal = true\n }\n }\n\n return out\n}\n\nfunction toNumber(value: string): number | null {\n if (value.trim() === '') return null\n const sep = props.decimalSeparator || '.'\n const normalized = sep === '.' ? value : value.replace(sep, '.')\n const n = Number(normalized)\n if (!Number.isFinite(n)) return null\n return n\n}\n\nfunction clamp(v: number): number {\n if (props.min !== undefined) v = Math.max(props.min, v)\n if (props.max !== undefined) v = Math.min(props.max, v)\n return v\n}\n\nfunction normalize(v: number): number {\n if (props.precision === undefined) return v\n const p = Math.max(0, props.precision)\n return Number(v.toFixed(p))\n}\n\nfunction format(n: number): string {\n const sep = props.decimalSeparator || '.'\n const p = props.precision\n\n // Без `precision` избегаем экспоненциальной записи (`String(1e21)` → `\"1e+21\"`),\n // форматируя через `Intl` без группировки разрядов.\n let s = p === undefined\n ? n.toLocaleString('en-US', { useGrouping: false, maximumFractionDigits: 20 })\n : n.toFixed(Math.max(0, p))\n if (sep !== '.') s = s.replace('.', sep)\n return s\n}\n\nfunction onInput(e: Event): void {\n const el = e.target as HTMLInputElement\n const raw = el.value\n const next = sanitize(raw)\n\n if (raw !== next) {\n // Сохраняем позицию каретки: считаем, сколько валидных символов было ДО\n // каретки, и ставим её после стольких же в очищенной строке — иначе при\n // редактировании середины числа каретка прыгала в конец.\n const caret = el.selectionStart ?? raw.length\n const keptBefore = sanitize(raw.slice(0, caret)).length\n el.value = next\n el.setSelectionRange(keptBefore, keptBefore)\n }\n\n draft.value = next\n\n const num = toNumber(next)\n if (num !== props.modelValue) emit('update:modelValue', num)\n}\n\n/**\n * Коммит набранного: границы и точность применяются здесь, а не на каждом\n * нажатии, — иначе `min=10` не дал бы набрать «1» как начало «15».\n */\nfunction commit(): void {\n // Без черновика коммитится сама модель: значение вне границ обязано\n // выправиться и тогда, когда его выставили снаружи, а не набрали руками.\n const num = draft.value !== null ? toNumber(draft.value) : props.modelValue\n const next = num === null ? null : clamp(normalize(num))\n\n draft.value = null\n\n if (next !== props.modelValue) emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction onChange(): void {\n // Клампинг границ (`min`/`max`) и нормализация — на `change`/`blur`, а не только\n // в кнопках: иначе ручной ввод «999» при `max=10` останется невалидным.\n commit()\n}\n\nfunction setValue(n: number): void {\n const next = clamp(normalize(n))\n // Шаг завершает набор: черновик снимается, показ считается от модели.\n draft.value = null\n emit('update:modelValue', next)\n emit('change', next)\n}\n\n/**\n * Шаг значения. Фокус не трогаем: кнопка ± обязана оставаться под фокусом,\n * иначе клавиатурный пользователь после первого Enter теряет её и повторно\n * нажать не может. Поле фокусирует тот, кто шагает от него самого.\n */\nfunction stepBy(dir: 1 | -1, step: number = props.step ?? 1): void {\n if (isDisabled.value || isReadonly.value) return\n\n const current = props.modelValue ?? 0\n setValue(addStep(current, step * dir))\n}\n\n// Интервал уже шагал: финальный `click` при отпускании — не намерение\n// пользователя сделать ещё один шаг, а хвост того же жеста.\nlet repeatFired = false\n\n/** Клик кнопки «±»: шаг, если это не хвост только что завершённого удержания. */\nfunction onStepClick(dir: 1 | -1, event: MouseEvent): void {\n // Enter/Space на кнопке дают click без указателя (`detail === 0`) — хвостом\n // жеста он быть не может. Иначе удержание, кончившееся без клика (отпускание\n // вне кнопки, `pointercancel` на таче), оставляло бы флаг взведённым, и\n // спинбаттон молча терял бы нажатие у клавиатуры и вспомогательных технологий.\n if (event.detail === 0) {\n repeatFired = false\n stepBy(dir)\n return\n }\n\n if (repeatFired) {\n repeatFired = false\n return\n }\n stepBy(dir)\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (isDisabled.value) return\n // Readonly-поле ведёт себя как текст: стрелки и Home/End отдаются нативной\n // каретке, значение не меняется.\n if (isReadonly.value) return\n\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n stepBy(1)\n break\n case 'ArrowDown':\n e.preventDefault()\n stepBy(-1)\n break\n case 'PageUp':\n e.preventDefault()\n stepBy(1, bigStep(props.step ?? 1, props.min, props.max))\n break\n case 'PageDown':\n e.preventDefault()\n stepBy(-1, bigStep(props.step ?? 1, props.min, props.max))\n break\n case 'Home':\n if (props.min !== undefined) {\n e.preventDefault()\n setValue(props.min)\n }\n break\n case 'End':\n if (props.max !== undefined) {\n e.preventDefault()\n setValue(props.max)\n }\n break\n }\n}\n\n// `aria-valuenow` — числовое значение для скринридеров; отсутствует, если поле пусто.\nconst ariaValueNow = computed(() => props.modelValue ?? undefined)\n\n// На пределе кнопка ± гаснет: активная кнопка, у которой `clamp` съедает\n// результат, читается как сломанная.\nconst numericValue = computed(() => props.modelValue)\n\nconst canIncrease = computed(() => {\n if (props.max === undefined) return true\n const current = numericValue.value\n return current === null || normalize(current) < props.max\n})\n\nconst canDecrease = computed(() => {\n if (props.min === undefined) return true\n const current = numericValue.value\n return current === null || normalize(current) > props.min\n})\n\n// Локале-зависимое отображение: при фокусе показываем «сырое» значение для\n// редактирования, иначе — сгруппированное через `Intl.NumberFormat`.\nfunction formatGrouped(num: number): string {\n const sep = props.decimalSeparator || '.'\n // formatToParts позволяет оставить локале-зависимый групповой разделитель,\n // но принудительно подставить наш десятичный разделитель (`decimalSeparator`),\n // корректно работая для любой локали (в т.ч. de-DE, где группа — '.').\n return new Intl.NumberFormat(resolvedLocale.value, {\n useGrouping: true,\n minimumFractionDigits: props.precision ?? 0,\n maximumFractionDigits: props.precision ?? 20,\n })\n .formatToParts(num)\n .map(part => (part.type === 'decimal' ? sep : part.value))\n .join('')\n}\n\n/**\n * `aria-valuetext` нужен только при группировке: без неё `aria-valuenow` уже\n * несёт то же самое число, и дублировать его текстом незачем.\n */\nconst ariaValueText = computed(() => {\n if (!props.useGrouping) return undefined\n return props.modelValue === null ? undefined : formatGrouped(props.modelValue)\n})\n\n/**\n * Что показывает поле: набираемый черновик, пока он есть, иначе — модель.\n * Группировка только вне фокуса: при правке разделители разрядов мешают.\n */\nconst displayValue = computed(() => {\n if (draft.value !== null) return draft.value\n if (props.modelValue === null) return ''\n if (props.useGrouping && !isFocused.value) return formatGrouped(props.modelValue)\n return format(props.modelValue)\n})\n\nfunction onFocus(event: FocusEvent): void {\n isFocused.value = true\n emit('focus', event)\n}\n\nfunction onBlur(event: FocusEvent): void {\n isFocused.value = false\n // `change` до `blur` доходит не всегда (значение могло вернуться к исходному),\n // а черновик обязан сняться в любом случае — иначе поле застынет на нём.\n draft.value = null\n emit('blur', event)\n stopRepeat()\n}\n\n// ————— Удержание кнопки ±: шаг повторяется, пока кнопку держат.\nconst REPEAT_DELAY_MS = 400\nconst REPEAT_INTERVAL_MS = 60\n\nlet repeatDelay: ReturnType<typeof setTimeout> | null = null\nlet repeatTimer: ReturnType<typeof setInterval> | null = null\n\nfunction stopRepeat(): void {\n if (repeatDelay !== null) {\n clearTimeout(repeatDelay)\n repeatDelay = null\n }\n if (repeatTimer !== null) {\n clearInterval(repeatTimer)\n repeatTimer = null\n }\n}\n\nfunction canStep(dir: 1 | -1): boolean {\n return dir === 1 ? canIncrease.value : canDecrease.value\n}\n\n/**\n * `pointerdown`, а не `mousedown`: на тач-устройствах второго не бывает.\n * Повтор останавливается на границе — иначе таймер крутился бы вхолостую.\n */\nfunction startRepeat(dir: 1 | -1): void {\n if (isDisabled.value || isReadonly.value) return\n\n stopRepeat()\n repeatFired = false\n repeatDelay = setTimeout(() => {\n repeatTimer = setInterval(() => {\n if (!canStep(dir)) {\n stopRepeat()\n return\n }\n repeatFired = true\n stepBy(dir)\n }, REPEAT_INTERVAL_MS)\n }, REPEAT_DELAY_MS)\n}\n\nonBeforeUnmount(stopRepeat)\n\n// ————— Очистка значения.\nconst resolvedClearable = useGrComponentProp('GrNumberInput', 'clearable', () => props.clearable, false)\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\n\nconst clearVisible = computed(() =>\n resolvedClearable.value && !isDisabled.value && !isReadonly.value && props.modelValue !== null,\n)\n\nfunction clear(): void {\n draft.value = null\n emit('update:modelValue', null)\n emit('change', null)\n emit('clear')\n focus()\n}\n</script>\n\n<template>\n <div\n data-gr-number-input\n class=\"relative w-full overflow-hidden rounded-[var(--gr-radius-control)] border bg-[var(--gr-bg)] transition-colors duration-[var(--gr-duration-fast)] focus-within:ring-2 focus-within:ring-[var(--gr-ring)]\"\n :class=\"shellClassName\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"number-input-prefix\"\n data-gr-number-input-prefix\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"name\"\n type=\"text\"\n :inputmode=\"inputmode\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"isDisabled\"\n :value=\"displayValue\"\n role=\"spinbutton\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuetext=\"ariaValueText\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-label=\"ariaLabel\"\n :readonly=\"isReadonly\"\n class=\"w-full bg-transparent text-[var(--gr-fg)] placeholder:text-[var(--gr-muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"inputClassName\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n @keydown=\"onKeydown\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"number-input-suffix\"\n data-gr-number-input-suffix\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <button\n v-if=\"clearVisible\"\n type=\"button\"\n data-gr-number-input-clear\n :class=\"clearButtonClass\"\n :style=\"clearButtonStyle\"\n :aria-label=\"resolvedClearLabel\"\n @mousedown.prevent\n @click=\"clear\"\n >\n <GrIcon :size=\"14\">\n <IconX />\n </GrIcon>\n </button>\n\n <div\n v-if=\"hasVerticalControls\"\n data-testid=\"number-input-controls-vertical\"\n data-gr-number-input-controls=\"vertical\"\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--gr-brd)]\"\n :style=\"verticalControlsStyle\"\n >\n <div class=\"flex flex-col justify-center gap-1\">\n <button\n type=\"button\"\n data-gr-number-input-increase\n :class=\"stepperCompactClass\"\n :disabled=\"isDisabled || isReadonly || !canIncrease\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronUp />\n </GrIcon>\n </button>\n <button\n type=\"button\"\n data-gr-number-input-decrease\n :class=\"stepperCompactClass\"\n :disabled=\"isDisabled || isReadonly || !canDecrease\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(-1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(-1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronDown />\n </GrIcon>\n </button>\n </div>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-left\"\n data-gr-number-input-controls=\"horizontal-left\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-r border-[var(--gr-brd)]\"\n :style=\"horizontalLeftControlsStyle\"\n >\n <button\n type=\"button\"\n data-gr-number-input-decrease\n :class=\"stepperWideClass\"\n :disabled=\"isDisabled || isReadonly || !canDecrease\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(-1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(-1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronLeft />\n </GrIcon>\n </button>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-right\"\n data-gr-number-input-controls=\"horizontal-right\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-l border-[var(--gr-brd)]\"\n :style=\"horizontalRightControlsStyle\"\n >\n <button\n type=\"button\"\n data-gr-number-input-increase\n :class=\"stepperWideClass\"\n :disabled=\"isDisabled || isReadonly || !canIncrease\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronRight />\n </GrIcon>\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { InputHTMLAttributes } from 'vue'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { computed, onBeforeUnmount, ref } from 'vue'\n\nimport {\n clearButtonClass,\n stepperCompactClass,\n stepperWideClass,\n grNumberInputInputClass,\n grNumberInputShellClass,\n type GrNumberInputControlsDirection,\n type GrNumberInputSize,\n type GrNumberInputState,\n type GrNumberInputTextAlign,\n} from './grNumberInputStyles'\nimport { addStep, bigStep } from './numberInputMath'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconChevronLeft from '~icons/lucide/chevron-left'\nimport IconChevronRight from '~icons/lucide/chevron-right'\nimport IconChevronUp from '~icons/lucide/chevron-up'\nimport IconX from '~icons/lucide/x'\nimport { addLen } from '../../composables/internal/useAddonMeasurement'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst ADDON_PX_BY_SIZE: Record<GrNumberInputSize, number> = {\n xs: 32,\n sm: 36,\n md: 40,\n lg: 48,\n}\n\nconst BASE_PADDING_X_LEN_BY_SIZE: Record<GrNumberInputSize, string> = {\n xs: '10px',\n sm: '12px',\n md: '12px',\n lg: '16px',\n}\n\nfunction px(n: number): string {\n return `${n}px`\n}\n\nexport interface GrNumberInputProps {\n /**\n * Значение поля. `null` — пусто.\n *\n * Незавершённый ввод («-», «1,») числом не является и в модель не попадает:\n * пока он набирается, поле держит его во внутреннем черновике, а модель\n * честно говорит «числа пока нет».\n */\n modelValue: number | null\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Быстрый флаг невалидности; эквивалент `state='danger'` + `aria-invalid`. */\n invalid?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n /** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */\n required?: boolean\n /** Доступное имя вне `GrFormField`. */\n ariaLabel?: string\n state?: GrNumberInputState\n name?: string\n id?: string\n size?: GrNumberInputSize\n\n textAlign?: GrNumberInputTextAlign\n\n decimalSeparator?: string\n step?: number\n min?: number\n max?: number\n precision?: number\n\n /** Кнопка очистки значения. */\n clearable?: boolean\n /** A11y-подпись кнопки очистки. */\n clearLabel?: string\n /**\n * BCP-47 локаль для отображения значения (группировка разрядов и разделители\n * через `Intl.NumberFormat`). Работает вместе с `useGrouping`.\n */\n locale?: string\n /**\n * Группировать разряды при отображении (когда поле не в фокусе). При фокусе\n * показывается «сырое» значение для редактирования. По умолчанию выключено.\n */\n useGrouping?: boolean\n\n /** Показывать кнопки +/-. */\n controls?: boolean\n controlsDirection?: GrNumberInputControlsDirection\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: жёсткая ширина (из `*MaxWidth` →\n * `*MinWidth` → дефолт) + обрезка контента по краю (prefix — справа,\n * suffix — слева). По умолчанию аддоны растягиваются под контент.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n\n /** i18n-friendly aria-label для кнопки \"увеличить\". */\n increaseLabel?: string\n /** i18n-friendly aria-label для кнопки \"уменьшить\". */\n decreaseLabel?: string\n}\n\nexport interface GrNumberInputEmits {\n (e: 'update:modelValue', value: number | null): void\n (e: 'change', value: number | null): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n /** Значение стёрто кнопкой очистки. */\n (e: 'clear'): void\n}\n\n// Контекст `GrFormField` — fallback для id/описания/невалидности, как в `GrInput`.\nconst field = useGrFormFieldContext()\n\nconst props = withDefaults(defineProps<GrNumberInputProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: 'decimal',\n disabled: false,\n invalid: false,\n readonly: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n name: undefined,\n id: undefined,\n size: undefined,\n\n textAlign: 'left',\n\n decimalSeparator: '.',\n step: 1,\n min: undefined,\n max: undefined,\n precision: undefined,\n locale: undefined,\n useGrouping: false,\n clearable: undefined,\n clearLabel: undefined,\n\n controls: false,\n controlsDirection: 'vertical',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n\n increaseLabel: undefined,\n decreaseLabel: undefined,\n})\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrNumberInput' })\n\nconst resolvedId = computed(() => props.id ?? field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst emit = defineEmits<GrNumberInputEmits>()\ndefineSlots<{\n /** Аддон слева от поля: знак валюты, иконка. */\n prefix?: () => any\n /** Аддон справа от поля: единица измерения. */\n suffix?: () => any\n}>()\n\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\n// В фокусе поле показывает «сырое» значение: группировка мешала бы правке.\nconst isFocused = ref(false)\n\n/**\n * Строка ровно в том виде, в каком её набирают. Модель к этому моменту может\n * быть `null` («-», «1,» — не числа), и без черновика поле стирало бы под\n * пальцами то, что печатают. Коммит (`change`/`blur`) черновик снимает.\n */\nconst draft = ref<string | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\n\nconst { t, locale } = useGranularityTranslations()\n\n/**\n * Локаль форматирования: свой проп → локаль i18n-адаптера → окружение. Без\n * второго шага мультиязычное приложение передавало бы `locale` каждому полю.\n */\nconst resolvedLocale = computed(() => props.locale ?? locale.value)\nconst resolvedIncreaseLabel = computed(() => props.increaseLabel ?? t('gr.numberInput.increase', 'Increase'))\nconst resolvedDecreaseLabel = computed(() => props.decreaseLabel ?? t('gr.numberInput.decrease', 'Decrease'))\n\nconst hasHorizontalControls = computed(() => props.controls && props.controlsDirection === 'horizontal')\nconst hasVerticalControls = computed(() => props.controls && props.controlsDirection === 'vertical')\n\nconst addonPx = computed(() => ADDON_PX_BY_SIZE[resolvedSize.value])\nconst addonLen = computed(() => px(addonPx.value))\nconst basePaddingXLen = computed(() => BASE_PADDING_X_LEN_BY_SIZE[resolvedSize.value])\n\n/** Кнопки ± справа: вертикальный стек — одна колонка, горизонтальный — вторая. */\nconst rightControlsCount = computed(() =>\n (hasHorizontalControls.value ? 1 : 0) + (hasVerticalControls.value ? 1 : 0))\n\nconst {\n prefixEl,\n suffixEl,\n prefixLen,\n suffixLen,\n prefixStyle,\n suffixStyle,\n fieldPadding: inputStyle,\n} = useControlAddons(() => props, {\n defaultMinWidth: () => addonLen.value,\n paddingX: () => basePaddingXLen.value,\n leadingReserve: () => (hasHorizontalControls.value ? addonLen.value : '0px'),\n trailingReserve: () => (rightControlsCount.value > 0 ? px(addonPx.value * rightControlsCount.value) : '0px'),\n // Суффикс позиционируется абсолютно: без якоря он уехал бы от правого края.\n anchorSuffixRight: true,\n})\n\nfunction addonStyle(side: 'left' | 'right', offset: string): Record<string, string> {\n return {\n width: addonLen.value,\n [side]: offset,\n }\n}\n\nconst verticalControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\n/**\n * Крестик очистки живёт левее правой зоны (суффикс + кнопки ±) — иначе он лёг бы\n * на них. Ширины там же, где их считает поле, поэтому переиспользуем расчёт.\n */\nconst clearButtonStyle = computed(() => {\n const controls = rightControlsCount.value > 0 ? px(addonPx.value * rightControlsCount.value) : '0px'\n\n return { right: addLen(addLen(suffixLen.value, controls), '6px') }\n})\nconst horizontalLeftControlsStyle = computed(() => addonStyle('left', prefixLen.value))\nconst horizontalRightControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\nconst shellClassName = computed(() => {\n return grNumberInputShellClass({\n disabled: isDisabled.value,\n state: props.state,\n invalid: isInvalid.value,\n })\n})\n\nconst inputClassName = computed(() => {\n return grNumberInputInputClass({\n size: resolvedSize.value,\n textAlign: props.textAlign,\n })\n})\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction sanitize(raw: string): string {\n const sep = props.decimalSeparator || '.'\n\n let out = ''\n let hasSepLocal = false\n\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i]\n\n // Ведущий минус: разрешаем один в начале, чтобы можно было ввести\n // отрицательное значение с клавиатуры (границы проверит `clamp` на change).\n if (ch === '-' && out.length === 0) {\n out += '-'\n continue\n }\n\n if (isDigit(ch)) {\n out += ch\n continue\n }\n\n if ((ch === sep || ch === '.' || ch === ',') && !hasSepLocal) {\n out += sep\n hasSepLocal = true\n }\n }\n\n return out\n}\n\nfunction toNumber(value: string): number | null {\n if (value.trim() === '') return null\n const sep = props.decimalSeparator || '.'\n const normalized = sep === '.' ? value : value.replace(sep, '.')\n const n = Number(normalized)\n if (!Number.isFinite(n)) return null\n return n\n}\n\nfunction clamp(v: number): number {\n if (props.min !== undefined) v = Math.max(props.min, v)\n if (props.max !== undefined) v = Math.min(props.max, v)\n return v\n}\n\nfunction normalize(v: number): number {\n if (props.precision === undefined) return v\n const p = Math.max(0, props.precision)\n return Number(v.toFixed(p))\n}\n\nfunction format(n: number): string {\n const sep = props.decimalSeparator || '.'\n const p = props.precision\n\n // Без `precision` избегаем экспоненциальной записи (`String(1e21)` → `\"1e+21\"`),\n // форматируя через `Intl` без группировки разрядов.\n let s = p === undefined\n ? n.toLocaleString('en-US', { useGrouping: false, maximumFractionDigits: 20 })\n : n.toFixed(Math.max(0, p))\n if (sep !== '.') s = s.replace('.', sep)\n return s\n}\n\nfunction onInput(e: Event): void {\n const el = e.target as HTMLInputElement\n const raw = el.value\n const next = sanitize(raw)\n\n if (raw !== next) {\n // Сохраняем позицию каретки: считаем, сколько валидных символов было ДО\n // каретки, и ставим её после стольких же в очищенной строке — иначе при\n // редактировании середины числа каретка прыгала в конец.\n const caret = el.selectionStart ?? raw.length\n const keptBefore = sanitize(raw.slice(0, caret)).length\n el.value = next\n el.setSelectionRange(keptBefore, keptBefore)\n }\n\n draft.value = next\n\n const num = toNumber(next)\n if (num !== props.modelValue) emit('update:modelValue', num)\n}\n\n/**\n * Коммит набранного: границы и точность применяются здесь, а не на каждом\n * нажатии, — иначе `min=10` не дал бы набрать «1» как начало «15».\n */\nfunction commit(): void {\n // Без черновика коммитится сама модель: значение вне границ обязано\n // выправиться и тогда, когда его выставили снаружи, а не набрали руками.\n const num = draft.value !== null ? toNumber(draft.value) : props.modelValue\n const next = num === null ? null : clamp(normalize(num))\n\n draft.value = null\n\n if (next !== props.modelValue) emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction onChange(): void {\n // Клампинг границ (`min`/`max`) и нормализация — на `change`/`blur`, а не только\n // в кнопках: иначе ручной ввод «999» при `max=10` останется невалидным.\n commit()\n}\n\nfunction setValue(n: number): void {\n const next = clamp(normalize(n))\n // Шаг завершает набор: черновик снимается, показ считается от модели.\n draft.value = null\n emit('update:modelValue', next)\n emit('change', next)\n}\n\n/**\n * Шаг значения. Фокус не трогаем: кнопка ± обязана оставаться под фокусом,\n * иначе клавиатурный пользователь после первого Enter теряет её и повторно\n * нажать не может. Поле фокусирует тот, кто шагает от него самого.\n */\nfunction stepBy(dir: 1 | -1, step: number = props.step ?? 1): void {\n if (isDisabled.value || isReadonly.value) return\n\n const current = props.modelValue ?? 0\n setValue(addStep(current, step * dir))\n}\n\n// Интервал уже шагал: финальный `click` при отпускании — не намерение\n// пользователя сделать ещё один шаг, а хвост того же жеста.\nlet repeatFired = false\n\n/** Клик кнопки «±»: шаг, если это не хвост только что завершённого удержания. */\nfunction onStepClick(dir: 1 | -1, event: MouseEvent): void {\n // Enter/Space на кнопке дают click без указателя (`detail === 0`) — хвостом\n // жеста он быть не может. Иначе удержание, кончившееся без клика (отпускание\n // вне кнопки, `pointercancel` на таче), оставляло бы флаг взведённым, и\n // спинбаттон молча терял бы нажатие у клавиатуры и вспомогательных технологий.\n if (event.detail === 0) {\n repeatFired = false\n stepBy(dir)\n return\n }\n\n if (repeatFired) {\n repeatFired = false\n return\n }\n stepBy(dir)\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (isDisabled.value) return\n // Readonly-поле ведёт себя как текст: стрелки и Home/End отдаются нативной\n // каретке, значение не меняется.\n if (isReadonly.value) return\n\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n stepBy(1)\n break\n case 'ArrowDown':\n e.preventDefault()\n stepBy(-1)\n break\n case 'PageUp':\n e.preventDefault()\n stepBy(1, bigStep(props.step ?? 1, props.min, props.max))\n break\n case 'PageDown':\n e.preventDefault()\n stepBy(-1, bigStep(props.step ?? 1, props.min, props.max))\n break\n case 'Home':\n if (props.min !== undefined) {\n e.preventDefault()\n setValue(props.min)\n }\n break\n case 'End':\n if (props.max !== undefined) {\n e.preventDefault()\n setValue(props.max)\n }\n break\n }\n}\n\n// `aria-valuenow` — числовое значение для скринридеров; отсутствует, если поле пусто.\nconst ariaValueNow = computed(() => props.modelValue ?? undefined)\n\n// На пределе кнопка ± гаснет: активная кнопка, у которой `clamp` съедает\n// результат, читается как сломанная.\nconst numericValue = computed(() => props.modelValue)\n\nconst canIncrease = computed(() => {\n if (props.max === undefined) return true\n const current = numericValue.value\n return current === null || normalize(current) < props.max\n})\n\nconst canDecrease = computed(() => {\n if (props.min === undefined) return true\n const current = numericValue.value\n return current === null || normalize(current) > props.min\n})\n\n// Локале-зависимое отображение: при фокусе показываем «сырое» значение для\n// редактирования, иначе — сгруппированное через `Intl.NumberFormat`.\nfunction formatGrouped(num: number): string {\n const sep = props.decimalSeparator || '.'\n // formatToParts позволяет оставить локале-зависимый групповой разделитель,\n // но принудительно подставить наш десятичный разделитель (`decimalSeparator`),\n // корректно работая для любой локали (в т.ч. de-DE, где группа — '.').\n return new Intl.NumberFormat(resolvedLocale.value, {\n useGrouping: true,\n minimumFractionDigits: props.precision ?? 0,\n maximumFractionDigits: props.precision ?? 20,\n })\n .formatToParts(num)\n .map(part => (part.type === 'decimal' ? sep : part.value))\n .join('')\n}\n\n/**\n * `aria-valuetext` нужен только при группировке: без неё `aria-valuenow` уже\n * несёт то же самое число, и дублировать его текстом незачем.\n */\nconst ariaValueText = computed(() => {\n if (!props.useGrouping) return undefined\n return props.modelValue === null ? undefined : formatGrouped(props.modelValue)\n})\n\n/**\n * Что показывает поле: набираемый черновик, пока он есть, иначе — модель.\n * Группировка только вне фокуса: при правке разделители разрядов мешают.\n */\nconst displayValue = computed(() => {\n if (draft.value !== null) return draft.value\n if (props.modelValue === null) return ''\n if (props.useGrouping && !isFocused.value) return formatGrouped(props.modelValue)\n return format(props.modelValue)\n})\n\nfunction onFocus(event: FocusEvent): void {\n isFocused.value = true\n emit('focus', event)\n}\n\nfunction onBlur(event: FocusEvent): void {\n isFocused.value = false\n // `change` до `blur` доходит не всегда (значение могло вернуться к исходному),\n // а черновик обязан сняться в любом случае — иначе поле застынет на нём.\n draft.value = null\n emit('blur', event)\n stopRepeat()\n}\n\n// ————— Удержание кнопки ±: шаг повторяется, пока кнопку держат.\nconst REPEAT_DELAY_MS = 400\nconst REPEAT_INTERVAL_MS = 60\n\nlet repeatDelay: ReturnType<typeof setTimeout> | null = null\nlet repeatTimer: ReturnType<typeof setInterval> | null = null\n\nfunction stopRepeat(): void {\n if (repeatDelay !== null) {\n clearTimeout(repeatDelay)\n repeatDelay = null\n }\n if (repeatTimer !== null) {\n clearInterval(repeatTimer)\n repeatTimer = null\n }\n}\n\nfunction canStep(dir: 1 | -1): boolean {\n return dir === 1 ? canIncrease.value : canDecrease.value\n}\n\n/**\n * `pointerdown`, а не `mousedown`: на тач-устройствах второго не бывает.\n * Повтор останавливается на границе — иначе таймер крутился бы вхолостую.\n */\nfunction startRepeat(dir: 1 | -1): void {\n if (isDisabled.value || isReadonly.value) return\n\n stopRepeat()\n repeatFired = false\n repeatDelay = setTimeout(() => {\n repeatTimer = setInterval(() => {\n if (!canStep(dir)) {\n stopRepeat()\n return\n }\n repeatFired = true\n stepBy(dir)\n }, REPEAT_INTERVAL_MS)\n }, REPEAT_DELAY_MS)\n}\n\nonBeforeUnmount(stopRepeat)\n\n// ————— Очистка значения.\nconst resolvedClearable = useGrComponentProp('GrNumberInput', 'clearable', () => props.clearable, false)\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\n\nconst clearVisible = computed(() =>\n resolvedClearable.value && !isDisabled.value && !isReadonly.value && props.modelValue !== null,\n)\n\nfunction clear(): void {\n draft.value = null\n emit('update:modelValue', null)\n emit('change', null)\n emit('clear')\n focus()\n}\n</script>\n\n<template>\n <div\n data-gr-number-input\n class=\"relative w-full overflow-hidden rounded-[var(--gr-radius-control)] border bg-[var(--gr-bg)] transition-colors duration-[var(--gr-duration-fast)] focus-within:ring-2 focus-within:ring-[var(--gr-ring)]\"\n :class=\"shellClassName\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"number-input-prefix\"\n data-gr-number-input-prefix\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"name\"\n type=\"text\"\n :inputmode=\"inputmode\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"isDisabled\"\n :value=\"displayValue\"\n role=\"spinbutton\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuetext=\"ariaValueText\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-label=\"ariaLabel\"\n :readonly=\"isReadonly\"\n class=\"w-full bg-transparent text-[var(--gr-fg)] placeholder:text-[var(--gr-muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"inputClassName\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n @keydown=\"onKeydown\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"number-input-suffix\"\n data-gr-number-input-suffix\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <button\n v-if=\"clearVisible\"\n type=\"button\"\n data-gr-number-input-clear\n :class=\"clearButtonClass\"\n :style=\"clearButtonStyle\"\n :aria-label=\"resolvedClearLabel\"\n @mousedown.prevent\n @click=\"clear\"\n >\n <GrIcon :size=\"14\">\n <IconX />\n </GrIcon>\n </button>\n\n <div\n v-if=\"hasVerticalControls\"\n data-testid=\"number-input-controls-vertical\"\n data-gr-number-input-controls=\"vertical\"\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--gr-brd)]\"\n :style=\"verticalControlsStyle\"\n >\n <div class=\"flex flex-col justify-center gap-1\">\n <button\n type=\"button\"\n data-gr-number-input-increase\n :class=\"stepperCompactClass\"\n :disabled=\"isDisabled || isReadonly || !canIncrease\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronUp />\n </GrIcon>\n </button>\n <button\n type=\"button\"\n data-gr-number-input-decrease\n :class=\"stepperCompactClass\"\n :disabled=\"isDisabled || isReadonly || !canDecrease\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(-1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(-1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronDown />\n </GrIcon>\n </button>\n </div>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-left\"\n data-gr-number-input-controls=\"horizontal-left\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-r border-[var(--gr-brd)]\"\n :style=\"horizontalLeftControlsStyle\"\n >\n <button\n type=\"button\"\n data-gr-number-input-decrease\n :class=\"stepperWideClass\"\n :disabled=\"isDisabled || isReadonly || !canDecrease\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(-1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(-1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronLeft />\n </GrIcon>\n </button>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-right\"\n data-gr-number-input-controls=\"horizontal-right\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-l border-[var(--gr-brd)]\"\n :style=\"horizontalRightControlsStyle\"\n >\n <button\n type=\"button\"\n data-gr-number-input-increase\n :class=\"stepperWideClass\"\n :disabled=\"isDisabled || isReadonly || !canIncrease\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @pointerdown=\"startRepeat(1)\"\n @pointerup=\"stopRepeat\"\n @pointerleave=\"stopRepeat\"\n @pointercancel=\"stopRepeat\"\n @click=\"onStepClick(1, $event)\"\n >\n <GrIcon :size=\"12\">\n <IconChevronRight />\n </GrIcon>\n </button>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrPopover-DDChM1jD.js","names":[],"sources":["../../../../src/components/GrPopover/GrPopover.vue","../../../../src/components/GrPopover/GrPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrPopover — якорный немодальный оверлей с произвольным содержимым.\n *\n * Роль «универсального поповера» до сих пор насильно исполнял `GrDropdown`, но\n * он про меню: жёстко объявляет `role=\"menu\"`, `aria-haspopup=\"menu\"` и водит\n * фокус по пунктам. Для формы, подтверждения или палитры это неверная семантика.\n *\n * Здесь наоборот: позиционирование, слой и закрытие берёт на себя примитив, а\n * клавиатурный паттерн внутри панели остаётся за потребителем — поэтому `role`\n * задаётся пропом. На этом собираются `GrMenu` / `GrContextMenu` /\n * `GrColorPicker` / Popconfirm.\n *\n * A11y:\n * - триггер получает `aria-haspopup`/`aria-expanded`/`aria-controls` через\n * `triggerProps` (биндится на реальный фокусируемый элемент в слоте);\n * - панель — `role=\"dialog\"` по умолчанию и обязательное доступное имя\n * (`ariaLabel` либо `aria-labelledby` через `labelledBy`);\n * - Esc закрывает верхний слой стека, клик вне — закрывает по точке попадания;\n * - **по умолчанию фокус-ловушки нет намеренно**: Tab обязан уводить фокус\n * наружу, иначе немодальный слой запирает пользователя на странице, которая\n * не заблокирована. Проп `modal` включает второй режим — см. ниже.\n */\nimport { computed, nextTick, ref, useId, useSlots, watch } from 'vue'\n\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useModalOverlay } from '../../composables/internal/useModalOverlay'\nimport { createFloatingAnchor, type GrFloatingAnchorRect, useFloating, type UseFloatingPlacement } from '../../composables/useFloating'\nimport { vClickOutside } from '../../directives'\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport {\n type GrPopoverPadding,\n type GrPopoverRole,\n type GrPopoverSize,\n grPopoverPanelClass,\n} from './grPopoverStyles'\n\nexport interface GrPopoverProps {\n /**\n * Открыт ли поповер. Без этого пропа компонент ведёт состояние сам\n * (uncontrolled), с ним — слушайте `update:open`.\n */\n open?: boolean\n placement?: UseFloatingPlacement\n /** Зазор между триггером и панелью, px. */\n offsetPx?: number\n size?: GrPopoverSize\n /** Роль панели. Меняется теми, кто строит поверх примитива своё меню/список. */\n role?: GrPopoverRole\n /**\n * Модальный режим: фон уходит в `inert`, Tab ходит по кругу внутри панели,\n * скролл страницы блокируется, а слой встаёт в стек модальным — как окно.\n * Нужен поповеру с формой или подтверждением внутри: без изоляции фона\n * пользователь уводит фокус на страницу, к которой поповер и относится.\n *\n * В этом режиме фокус переносится в панель независимо от `autoFocus`: фон\n * недоступен, и слой без фокуса внутри стал бы клавиатурной ловушкой.\n */\n modal?: boolean\n /** Доступное имя панели. Обязательно для `role=\"dialog\"` без видимого заголовка. */\n ariaLabel?: string\n /** `id` видимого заголовка внутри панели — альтернатива `ariaLabel`. */\n labelledBy?: string\n /**\n * `manual` — открывать только программно (через `v-model:open`). Нужно для\n * контекстного меню и подтверждений, где момент открытия решает потребитель.\n */\n trigger?: 'click' | 'manual'\n closeOnEsc?: boolean\n closeOnClickOutside?: boolean\n /** Закрывать по клику внутри панели — удобно для меню, вредно для формы. */\n closeOnContentClick?: boolean\n /**\n * Переносить фокус на панель при открытии. Именно на панель, а не на первый\n * контрол внутри: сфокусировать поле ввода за пользователя — решение\n * содержимого, а не оболочки.\n */\n autoFocus?: boolean\n /**\n * Точечное переопределение точки монтирования. По умолчанию — общий портал\n * оверлеев (`#gr-portal` либо `portalTarget` из `GrConfigProvider`).\n */\n teleportTo?: string | HTMLElement\n contentClass?: string\n disabled?: boolean\n /**\n * Якорь-прямоугольник в координатах вьюпорта вместо обёртки слота `#trigger`:\n * панель встаёт у точки курсора или у строки списка. Нужен контекстному меню,\n * у которого триггера-элемента нет вовсе.\n *\n * `triggerProps` в этом режиме некому потребить, и своих ARIA-атрибутов\n * поповер никуда не вешает: `aria-haspopup` невалиден вне интерактивного\n * элемента, а `aria-expanded` без хозяина — шум. Связь с содержимым страницы\n * объявляет тот, кто открывает.\n */\n anchor?: GrFloatingAnchorRect | null\n /** Поле панели. `none` — содержимое рисует своё (меню, список опций). */\n padding?: GrPopoverPadding\n}\n\nexport interface GrPopoverEmits {\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrPopoverProps>(), {\n open: undefined,\n placement: 'bottom-start',\n offsetPx: 8,\n size: undefined,\n role: 'dialog',\n modal: false,\n ariaLabel: undefined,\n labelledBy: undefined,\n trigger: 'click',\n closeOnEsc: true,\n closeOnClickOutside: true,\n closeOnContentClick: false,\n autoFocus: true,\n teleportTo: undefined,\n contentClass: undefined,\n disabled: false,\n anchor: undefined,\n padding: 'default',\n})\n\nconst emit = defineEmits<GrPopoverEmits>()\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrPopover' })\n\nconst { open: isOpen, setOpen } = useControlledOpen(\n () => props.open,\n next => emit('update:open', next),\n)\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst panelId = useId()\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nfunction open(): void {\n if (props.disabled) return\n setOpen(true)\n}\n\nfunction close(): void {\n setOpen(false)\n}\n\nfunction toggle(): void {\n isOpen.value ? close() : open()\n}\n\n/**\n * Якорь создаётся один раз и читает координаты геттером: `useFloating`\n * пересоздаёт подписку при смене самой ссылки, а точка меняется на каждый\n * вызов — новый объект рвал бы `autoUpdate` без нужды.\n */\nconst anchorEl = createFloatingAnchor(() => props.anchor)\n\n/** Обёртка триггера существует только вместе со слотом — см. проп `anchor`. */\nconst hasTrigger = computed(() => Boolean(slots.trigger))\n\nconst { floatingStyle, resolvedPlacement, update: updateFloatingPosition } = useFloating(\n () => (props.anchor ? anchorEl : rootEl.value),\n panelEl,\n isOpen,\n {\n placement: () => props.placement,\n offsetPx: props.offsetPx,\n // Своего слоя у поповера нет намеренно: он и дропдаун — один класс якорных\n // немодальных оверлеев и обязаны лежать на одной высоте, иначе начнут\n // перекрывать друг друга в зависимости от порядка открытия.\n zIndexVar: '--gr-z-dropdown',\n },\n)\n\nwatch(() => props.placement, () => {\n if (isOpen.value) updateFloatingPosition()\n})\n\n// Объект якоря стабилен, поэтому смену координат `useFloating` сам не заметит:\n// повторный вызов у другой точки — это пересчёт позиции, а не переоткрытие.\nwatch(() => props.anchor, () => {\n if (isOpen.value) updateFloatingPosition()\n}, { deep: true })\n\n/**\n * Слой стека оверлеев: очередь Esc общая с модалками, поэтому поповер, открытый\n * внутри модалки, по Esc закрывает себя, а не её. Возврат фокуса на триггер\n * тоже отсюда — по правилу «только если на момент закрытия фокус ещё внутри».\n *\n * Модальность включается пропом и приходит той же сборкой, что у окна и drawer'а\n * (`useModalOverlay`): ловушка фокуса, `inert` фона, блокировка скролла. При\n * `modal: false` не включается ничего из этого — дефолтный поповер немодален.\n *\n * Корень слоя и панель здесь один элемент: якорный оверлей уезжает в портал сам,\n * без обёртки.\n */\nconst { inertAttr, portalTarget, teleportEnabled, themeAttrs } = useModalOverlay(isOpen, close, {\n modal: () => props.modal,\n closeOnEscape: () => props.closeOnEsc,\n panel: panelEl,\n root: panelEl,\n teleportTo: () => props.teleportTo,\n initialFocus: () => panelEl.value,\n})\n\n// Фокус переносим после отрисовки панели: до `nextTick` её ещё нет в DOM.\n// В модальном режиме перенос обязателен: фон в `inert`, и фокус, оставленный\n// снаружи, попал бы в недоступное поддерево.\nwatch(isOpen, async (next) => {\n if (!next || !(props.autoFocus || props.modal)) return\n\n await nextTick()\n panelEl.value?.focus()\n})\n\n/** Роли, которые `aria-haspopup` действительно понимает. */\nconst HASPOPUP_ROLES = ['dialog', 'menu', 'listbox', 'grid'] as const\n\n/**\n * Пропсы для реального фокусируемого триггера: `<button v-bind=\"triggerProps\">`.\n * Клик вешается на обёртку, а ARIA — сюда, потому что `aria-expanded` обязан\n * жить на самом интерактивном элементе, а не на `div` вокруг него.\n */\nconst triggerProps = computed(() => ({\n // `aria-haspopup` принимает не любую роль: `group` в его списке нет, и\n // объявленный им триггер отдавал бы невалидный атрибут, который AT просто\n // отбрасывает. Роли вне списка сообщают о панели только через `aria-expanded`.\n 'aria-haspopup': HASPOPUP_ROLES.includes(props.role as (typeof HASPOPUP_ROLES)[number])\n ? (props.role as (typeof HASPOPUP_ROLES)[number])\n : undefined,\n 'aria-expanded': isOpen.value,\n 'aria-controls': isOpen.value ? panelId : undefined,\n 'disabled': props.disabled || undefined,\n}))\n\nfunction onTriggerClick(): void {\n if (props.trigger === 'click') toggle()\n}\n\nfunction onContentClick(): void {\n if (props.closeOnContentClick) close()\n}\n\nconst panelClasses = computed(() =>\n grPopoverPanelClass(resolvedSize.value, resolvedPlacement.value, props.contentClass, props.padding),\n)\n\ndefineExpose({ open, close, toggle })\n</script>\n\n<template>\n <div data-gr-popover>\n <div\n v-if=\"hasTrigger\"\n ref=\"rootEl\"\n v-click-outside=\"{ handler: close, enabled: isOpen && closeOnClickOutside, exclude: clickOutsideExclude }\"\n data-gr-popover-trigger\n class=\"inline-block max-w-full\"\n @click=\"onTriggerClick\"\n >\n <slot\n name=\"trigger\"\n :open=\"isOpen\"\n :toggle=\"toggle\"\n :close=\"close\"\n :trigger-props=\"triggerProps\"\n />\n </div>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <transition\n enter-active-class=\"transition ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"isOpen\"\n :id=\"panelId\"\n ref=\"panelEl\"\n v-click-outside=\"{ handler: close, enabled: isOpen && closeOnClickOutside && !hasTrigger }\"\n v-bind=\"themeAttrs\"\n data-gr-popover-panel\n data-gr-overlay-root\n :role=\"role === 'none' ? undefined : role\"\n :aria-modal=\"modal ? 'true' : undefined\"\n :inert=\"inertAttr\"\n :aria-label=\"labelledBy ? undefined : ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n tabindex=\"-1\"\n :class=\"panelClasses\"\n :style=\"floatingStyle\"\n @click=\"onContentClick\"\n >\n <slot name=\"content\" :close=\"close\" />\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrPopover — якорный немодальный оверлей с произвольным содержимым.\n *\n * Роль «универсального поповера» до сих пор насильно исполнял `GrDropdown`, но\n * он про меню: жёстко объявляет `role=\"menu\"`, `aria-haspopup=\"menu\"` и водит\n * фокус по пунктам. Для формы, подтверждения или палитры это неверная семантика.\n *\n * Здесь наоборот: позиционирование, слой и закрытие берёт на себя примитив, а\n * клавиатурный паттерн внутри панели остаётся за потребителем — поэтому `role`\n * задаётся пропом. На этом собираются `GrMenu` / `GrContextMenu` /\n * `GrColorPicker` / Popconfirm.\n *\n * A11y:\n * - триггер получает `aria-haspopup`/`aria-expanded`/`aria-controls` через\n * `triggerProps` (биндится на реальный фокусируемый элемент в слоте);\n * - панель — `role=\"dialog\"` по умолчанию и обязательное доступное имя\n * (`ariaLabel` либо `aria-labelledby` через `labelledBy`);\n * - Esc закрывает верхний слой стека, клик вне — закрывает по точке попадания;\n * - **по умолчанию фокус-ловушки нет намеренно**: Tab обязан уводить фокус\n * наружу, иначе немодальный слой запирает пользователя на странице, которая\n * не заблокирована. Проп `modal` включает второй режим — см. ниже.\n */\nimport { computed, nextTick, ref, useId, useSlots, watch } from 'vue'\n\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useModalOverlay } from '../../composables/internal/useModalOverlay'\nimport { createFloatingAnchor, type GrFloatingAnchorRect, useFloating, type UseFloatingPlacement } from '../../composables/useFloating'\nimport { vClickOutside } from '../../directives'\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport {\n type GrPopoverPadding,\n type GrPopoverRole,\n type GrPopoverSize,\n grPopoverPanelClass,\n} from './grPopoverStyles'\n\nexport interface GrPopoverProps {\n /**\n * Открыт ли поповер. Без этого пропа компонент ведёт состояние сам\n * (uncontrolled), с ним — слушайте `update:open`.\n */\n open?: boolean\n placement?: UseFloatingPlacement\n /** Зазор между триггером и панелью, px. */\n offsetPx?: number\n size?: GrPopoverSize\n /** Роль панели. Меняется теми, кто строит поверх примитива своё меню/список. */\n role?: GrPopoverRole\n /**\n * Модальный режим: фон уходит в `inert`, Tab ходит по кругу внутри панели,\n * скролл страницы блокируется, а слой встаёт в стек модальным — как окно.\n * Нужен поповеру с формой или подтверждением внутри: без изоляции фона\n * пользователь уводит фокус на страницу, к которой поповер и относится.\n *\n * В этом режиме фокус переносится в панель независимо от `autoFocus`: фон\n * недоступен, и слой без фокуса внутри стал бы клавиатурной ловушкой.\n */\n modal?: boolean\n /** Доступное имя панели. Обязательно для `role=\"dialog\"` без видимого заголовка. */\n ariaLabel?: string\n /** `id` видимого заголовка внутри панели — альтернатива `ariaLabel`. */\n labelledBy?: string\n /**\n * `manual` — открывать только программно (через `v-model:open`). Нужно для\n * контекстного меню и подтверждений, где момент открытия решает потребитель.\n */\n trigger?: 'click' | 'manual'\n closeOnEsc?: boolean\n closeOnClickOutside?: boolean\n /** Закрывать по клику внутри панели — удобно для меню, вредно для формы. */\n closeOnContentClick?: boolean\n /**\n * Переносить фокус на панель при открытии. Именно на панель, а не на первый\n * контрол внутри: сфокусировать поле ввода за пользователя — решение\n * содержимого, а не оболочки.\n */\n autoFocus?: boolean\n /**\n * Точечное переопределение точки монтирования. По умолчанию — общий портал\n * оверлеев (`#gr-portal` либо `portalTarget` из `GrConfigProvider`).\n */\n teleportTo?: string | HTMLElement\n contentClass?: string\n disabled?: boolean\n /**\n * Якорь-прямоугольник в координатах вьюпорта вместо обёртки слота `#trigger`:\n * панель встаёт у точки курсора или у строки списка. Нужен контекстному меню,\n * у которого триггера-элемента нет вовсе.\n *\n * `triggerProps` в этом режиме некому потребить, и своих ARIA-атрибутов\n * поповер никуда не вешает: `aria-haspopup` невалиден вне интерактивного\n * элемента, а `aria-expanded` без хозяина — шум. Связь с содержимым страницы\n * объявляет тот, кто открывает.\n */\n anchor?: GrFloatingAnchorRect | null\n /** Поле панели. `none` — содержимое рисует своё (меню, список опций). */\n padding?: GrPopoverPadding\n}\n\nexport interface GrPopoverEmits {\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrPopoverProps>(), {\n open: undefined,\n placement: 'bottom-start',\n offsetPx: 8,\n size: undefined,\n role: 'dialog',\n modal: false,\n ariaLabel: undefined,\n labelledBy: undefined,\n trigger: 'click',\n closeOnEsc: true,\n closeOnClickOutside: true,\n closeOnContentClick: false,\n autoFocus: true,\n teleportTo: undefined,\n contentClass: undefined,\n disabled: false,\n anchor: undefined,\n padding: 'default',\n})\n\nconst emit = defineEmits<GrPopoverEmits>()\n\nconst slots = useSlots()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrPopover' })\n\nconst { open: isOpen, setOpen } = useControlledOpen(\n () => props.open,\n next => emit('update:open', next),\n)\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst panelId = useId()\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nfunction open(): void {\n if (props.disabled) return\n setOpen(true)\n}\n\nfunction close(): void {\n setOpen(false)\n}\n\nfunction toggle(): void {\n isOpen.value ? close() : open()\n}\n\n/**\n * Якорь создаётся один раз и читает координаты геттером: `useFloating`\n * пересоздаёт подписку при смене самой ссылки, а точка меняется на каждый\n * вызов — новый объект рвал бы `autoUpdate` без нужды.\n */\nconst anchorEl = createFloatingAnchor(() => props.anchor)\n\n/** Обёртка триггера существует только вместе со слотом — см. проп `anchor`. */\nconst hasTrigger = computed(() => Boolean(slots.trigger))\n\nconst { floatingStyle, resolvedPlacement, update: updateFloatingPosition } = useFloating(\n () => (props.anchor ? anchorEl : rootEl.value),\n panelEl,\n isOpen,\n {\n placement: () => props.placement,\n offsetPx: props.offsetPx,\n // Своего слоя у поповера нет намеренно: он и дропдаун — один класс якорных\n // немодальных оверлеев и обязаны лежать на одной высоте, иначе начнут\n // перекрывать друг друга в зависимости от порядка открытия.\n zIndexVar: '--gr-z-dropdown',\n },\n)\n\nwatch(() => props.placement, () => {\n if (isOpen.value) updateFloatingPosition()\n})\n\n// Объект якоря стабилен, поэтому смену координат `useFloating` сам не заметит:\n// повторный вызов у другой точки — это пересчёт позиции, а не переоткрытие.\nwatch(() => props.anchor, () => {\n if (isOpen.value) updateFloatingPosition()\n}, { deep: true })\n\n/**\n * Слой стека оверлеев: очередь Esc общая с модалками, поэтому поповер, открытый\n * внутри модалки, по Esc закрывает себя, а не её. Возврат фокуса на триггер\n * тоже отсюда — по правилу «только если на момент закрытия фокус ещё внутри».\n *\n * Модальность включается пропом и приходит той же сборкой, что у окна и drawer'а\n * (`useModalOverlay`): ловушка фокуса, `inert` фона, блокировка скролла. При\n * `modal: false` не включается ничего из этого — дефолтный поповер немодален.\n *\n * Корень слоя и панель здесь один элемент: якорный оверлей уезжает в портал сам,\n * без обёртки.\n */\nconst { inertAttr, portalTarget, teleportEnabled, themeAttrs } = useModalOverlay(isOpen, close, {\n modal: () => props.modal,\n closeOnEscape: () => props.closeOnEsc,\n panel: panelEl,\n root: panelEl,\n teleportTo: () => props.teleportTo,\n initialFocus: () => panelEl.value,\n})\n\n// Фокус переносим после отрисовки панели: до `nextTick` её ещё нет в DOM.\n// В модальном режиме перенос обязателен: фон в `inert`, и фокус, оставленный\n// снаружи, попал бы в недоступное поддерево.\nwatch(isOpen, async (next) => {\n if (!next || !(props.autoFocus || props.modal)) return\n\n await nextTick()\n panelEl.value?.focus()\n})\n\n/** Роли, которые `aria-haspopup` действительно понимает. */\nconst HASPOPUP_ROLES = ['dialog', 'menu', 'listbox', 'grid'] as const\n\n/**\n * Пропсы для реального фокусируемого триггера: `<button v-bind=\"triggerProps\">`.\n * Клик вешается на обёртку, а ARIA — сюда, потому что `aria-expanded` обязан\n * жить на самом интерактивном элементе, а не на `div` вокруг него.\n */\nconst triggerProps = computed(() => ({\n // `aria-haspopup` принимает не любую роль: `group` в его списке нет, и\n // объявленный им триггер отдавал бы невалидный атрибут, который AT просто\n // отбрасывает. Роли вне списка сообщают о панели только через `aria-expanded`.\n 'aria-haspopup': HASPOPUP_ROLES.includes(props.role as (typeof HASPOPUP_ROLES)[number])\n ? (props.role as (typeof HASPOPUP_ROLES)[number])\n : undefined,\n 'aria-expanded': isOpen.value,\n 'aria-controls': isOpen.value ? panelId : undefined,\n 'disabled': props.disabled || undefined,\n}))\n\nfunction onTriggerClick(): void {\n if (props.trigger === 'click') toggle()\n}\n\nfunction onContentClick(): void {\n if (props.closeOnContentClick) close()\n}\n\nconst panelClasses = computed(() =>\n grPopoverPanelClass(resolvedSize.value, resolvedPlacement.value, props.contentClass, props.padding),\n)\n\ndefineExpose({ open, close, toggle })\n</script>\n\n<template>\n <div data-gr-popover>\n <div\n v-if=\"hasTrigger\"\n ref=\"rootEl\"\n v-click-outside=\"{ handler: close, enabled: isOpen && closeOnClickOutside, exclude: clickOutsideExclude }\"\n data-gr-popover-trigger\n class=\"inline-block max-w-full\"\n @click=\"onTriggerClick\"\n >\n <slot\n name=\"trigger\"\n :open=\"isOpen\"\n :toggle=\"toggle\"\n :close=\"close\"\n :trigger-props=\"triggerProps\"\n />\n </div>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <transition\n enter-active-class=\"transition ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"isOpen\"\n :id=\"panelId\"\n ref=\"panelEl\"\n v-click-outside=\"{ handler: close, enabled: isOpen && closeOnClickOutside && !hasTrigger }\"\n v-bind=\"themeAttrs\"\n data-gr-popover-panel\n data-gr-overlay-root\n :role=\"role === 'none' ? undefined : role\"\n :aria-modal=\"modal ? 'true' : undefined\"\n :inert=\"inertAttr\"\n :aria-label=\"labelledBy ? undefined : ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n tabindex=\"-1\"\n :class=\"panelClasses\"\n :style=\"floatingStyle\"\n @click=\"onContentClick\"\n >\n <slot name=\"content\" :close=\"close\" />\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrPromptDialog-BGuMUzlv.js","names":[],"sources":["../../../../src/components/GrPromptDialog/GrPromptDialog.vue","../../../../src/components/GrPromptDialog/GrPromptDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrPromptDialog — GR-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n *\n * Проверки сверх `required` описываются пропом `rules` — тем же движком, что и\n * у `GrForm`: третий частный случай валидации в пакете заводить незачем.\n */\nimport { useGrComponentProp } from '../GrConfigProvider/context'\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTextarea from '../GrTextarea/GrTextarea.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n// Именованные импорты чистых функций: разметки не тянут, поэтому в\n// `config.dependencies` не объявляются (см. правило про зависимости).\nimport { createGrFormMessageResolver, rulesForTrigger, runFieldRules } from '../GrForm/validation'\nimport type { GrFormRule, GrFormTrigger } from '../GrForm/validation'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\n/**\n * Тип поля выводим из самого `GrInput`, а не заводим ему именованный алиас:\n * алиас в пропе `GrInput` заменил бы в таблице API витрины список допустимых\n * значений на своё имя — документация стала бы беднее ради удобства обёртки.\n */\ntype GrInputType = NonNullable<InstanceType<typeof GrInput>['$props']['type']>\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\nimport type { InputHTMLAttributes } from 'vue'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /** Тип однострочного поля. В многострочном режиме не применяется. */\n inputType?: GrInputType\n /** Программная клавиатура на мобильных. */\n inputmode?: InputHTMLAttributes['inputmode']\n /** Ограничение длины; со `showCount` рисуется счётчик. */\n maxlength?: number\n showCount?: boolean\n /** Многострочный ввод: вместо `GrInput` рисуется `GrTextarea`. */\n multiline?: boolean\n /** Высота многострочного поля в строках. */\n rows?: number\n /** Автоподбор высоты многострочного поля под содержимое. */\n autosize?: boolean\n /**\n * Правила проверки значения — те же, что у `GrForm` (`required`, `type`,\n * `min`/`max`/`len`, `pattern`, свой в т.ч. асинхронный `validator`).\n * Прогоняются на blur (после первого касания) и на подтверждении.\n */\n rules?: GrFormRule | GrFormRule[]\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n /**\n * Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт\n * подтверждение или проверка `rules`. Кнопка закрытия и «Отмена» остаются:\n * окно без единого выхода — ловушка.\n */\n persistent?: boolean\n}\n\nexport interface GrPromptDialogEmits {\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}\n\nimport './defaults'\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: undefined,\n description: undefined,\n label: undefined,\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n // Дефолт `size` живёт в резолвере ниже, а не здесь: Vue подставил бы его\n // до того, как компонент заглянет в `GrConfigProvider`.\n size: undefined,\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: undefined,\n buttonSize: undefined,\n confirmText: undefined,\n cancelText: undefined,\n requiredErrorText: undefined,\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n inputType: 'text',\n inputmode: undefined,\n maxlength: undefined,\n showCount: false,\n multiline: false,\n rows: undefined,\n autosize: false,\n rules: undefined,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n persistent: false,\n})\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentProp('GrPromptDialog', 'size', () => props.size, 'md')\n\nconst emit = defineEmits<GrPromptDialogEmits>()\n\n// Дефолты берём из общего i18n-блока пакета (fallback — англ.), а не хардкодим.\nconst { t } = useGranularityTranslations()\nconst resolvedTitle = computed(() => props.title ?? t('gr.dialog.prompt.title', 'Prompt'))\nconst resolvedLabel = computed(() => props.label ?? t('gr.dialog.prompt.label', 'Value'))\nconst resolvedRequiredErrorText = computed(() => props.requiredErrorText ?? t('gr.dialog.prompt.required', 'Enter a value.'))\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? t('gr.common.close', 'Close'))\nconst resolvedConfirmText = computed(() => props.confirmText ?? t('gr.common.confirm', 'Confirm'))\nconst resolvedCancelText = computed(() => props.cancelText ?? t('gr.common.cancel', 'Cancel'))\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst textareaRef = ref<InstanceType<typeof GrTextarea> | null>(null)\n\n/** Ошибка последнего прогона `rules`; `required` считается отдельно и мгновенно. */\nconst ruleError = ref<string | undefined>(undefined)\nconst validating = ref(false)\n\n// Сторож устаревших прогонов: асинхронное правило может ответить после того,\n// как значение уже сменилось, и дописать чужую ошибку.\nlet validationRun = 0\n\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (!isOpen) return\n\n touched.value = false\n ruleError.value = undefined\n validationRun += 1\n },\n)\n\nconst resolveMessage = createGrFormMessageResolver(t)\n\nconst normalizedRules = computed<GrFormRule[]>(() => {\n if (!props.rules) return []\n return Array.isArray(props.rules) ? props.rules : [props.rules]\n})\n\nconst requiredError = computed(() => {\n if (!props.required) return undefined\n return valueModel.value.trim().length > 0 ? undefined : resolvedRequiredErrorText.value\n})\n\nconst canConfirm = computed(() => !requiredError.value)\n\nconst validationError = computed(() => {\n if (!touched.value) return undefined\n return requiredError.value ?? ruleError.value\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\n/**\n * Прогоняет `rules` для триггера. Возвращает сообщение или `undefined`.\n * `required` здесь не участвует: он синхронный и считается отдельно, чтобы\n * пустое поле блокировало кнопку без похода в асинхронный движок.\n */\nasync function runRules(trigger: GrFormTrigger): Promise<string | undefined> {\n const rules = rulesForTrigger(normalizedRules.value, trigger)\n if (!rules.length) return undefined\n\n const run = ++validationRun\n validating.value = true\n try {\n const message = await runFieldRules(valueModel.value, rules, { value: valueModel.value }, resolveMessage)\n // Ответ устарел — значение или сам диалог успели смениться.\n if (run !== validationRun) return undefined\n\n ruleError.value = message\n return message\n }\n finally {\n if (run === validationRun) validating.value = false\n }\n}\n\nfunction onBlur(): void {\n touched.value = true\n void runRules('blur')\n}\n\n// Пока подтверждение или проверка в полёте, случайное движение не должно\n// оборвать операцию.\nconst softCloseBlocked = computed(() => props.persistent && (props.confirmLoading || validating.value))\nconst resolvedCloseOnBackdrop = computed(() => (softCloseBlocked.value ? false : props.closeOnBackdrop))\nconst resolvedCloseOnEsc = computed(() => (softCloseBlocked.value ? false : props.closeOnEsc))\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction focusField(): void {\n if (props.multiline) textareaRef.value?.focus()\n else inputRef.value?.focus()\n}\n\nasync function onConfirm(): Promise<void> {\n touched.value = true\n\n if (!canConfirm.value) {\n focusField()\n return\n }\n\n if (await runRules('submit')) {\n focusField()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n\n/**\n * `Enter` подтверждает — базовое ожидание от окна, единственный смысл которого\n * ввести одно значение. В многострочном режиме `Enter` остаётся переводом\n * строки, поэтому обработчик висит только на однострочном поле.\n */\nfunction onEnter(): void {\n void onConfirm()\n}\n\n// Фокус в поле, а не на панель: диалог существует ровно ради ввода.\n//\n// Через `initialFocus` у `GrModal` это не решается: элемент рождается внутри\n// поддерева диалога, и возврат его же пропом наверх замыкает рендер в цикл\n// («Maximum recursive updates» — проверено). Поэтому фокус ставит содержимое,\n// после отрисовки (`flush: 'post'` + кадр), то есть заведомо позже, чем\n// ловушка фокуса `GrModal` переводит фокус на панель.\n// Источник — не только `modelValue`, но и само поле: содержимое панели\n// появляется на такт позже открытия, и фокус по фиксированному `nextTick`\n// приходился на момент, когда фокусировать ещё нечего.\nwatch(\n [() => props.modelValue, inputRef, textareaRef],\n () => {\n if (!props.modelValue) return\n focusField()\n },\n // `immediate`: окно могут смонтировать уже открытым — тогда смены пропа не\n // будет вовсе, и без этого фокус остался бы на панели.\n { immediate: true, flush: 'post' },\n)\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"resolvedTitle\"\n :size=\"resolvedSize\"\n :close-on-backdrop=\"resolvedCloseOnBackdrop\"\n :close-on-esc=\"resolvedCloseOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"resolvedCloseLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[length:var(--gr-control-text-md)] text-[var(--gr-muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <!-- `id` полю не задаём: `GrFormField` генерирует его сам, а поле читает\n из контекста. Литеральный id ломал два открытых диалога сразу. -->\n <GrFormField :label=\"resolvedLabel\" :error=\"fieldErrorMessage\">\n <GrTextarea\n v-if=\"multiline\"\n ref=\"textareaRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :autosize=\"autosize\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n />\n <GrInput\n v-else\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :type=\"inputType\"\n :inputmode=\"inputmode\"\n :placeholder=\"placeholder\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n @keydown.enter=\"onEnter\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"gr-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ resolvedCancelText }}\n </GrButton>\n <GrButton\n data-testid=\"gr-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading || validating\"\n :disabled=\"confirmDisabled || (touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ resolvedConfirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrPromptDialog — GR-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n *\n * Проверки сверх `required` описываются пропом `rules` — тем же движком, что и\n * у `GrForm`: третий частный случай валидации в пакете заводить незачем.\n */\nimport { useGrComponentProp } from '../GrConfigProvider/context'\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTextarea from '../GrTextarea/GrTextarea.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n// Именованные импорты чистых функций: разметки не тянут, поэтому в\n// `config.dependencies` не объявляются (см. правило про зависимости).\nimport { createGrFormMessageResolver, rulesForTrigger, runFieldRules } from '../GrForm/validation'\nimport type { GrFormRule, GrFormTrigger } from '../GrForm/validation'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\n/**\n * Тип поля выводим из самого `GrInput`, а не заводим ему именованный алиас:\n * алиас в пропе `GrInput` заменил бы в таблице API витрины список допустимых\n * значений на своё имя — документация стала бы беднее ради удобства обёртки.\n */\ntype GrInputType = NonNullable<InstanceType<typeof GrInput>['$props']['type']>\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\nimport type { InputHTMLAttributes } from 'vue'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /** Тип однострочного поля. В многострочном режиме не применяется. */\n inputType?: GrInputType\n /** Программная клавиатура на мобильных. */\n inputmode?: InputHTMLAttributes['inputmode']\n /** Ограничение длины; со `showCount` рисуется счётчик. */\n maxlength?: number\n showCount?: boolean\n /** Многострочный ввод: вместо `GrInput` рисуется `GrTextarea`. */\n multiline?: boolean\n /** Высота многострочного поля в строках. */\n rows?: number\n /** Автоподбор высоты многострочного поля под содержимое. */\n autosize?: boolean\n /**\n * Правила проверки значения — те же, что у `GrForm` (`required`, `type`,\n * `min`/`max`/`len`, `pattern`, свой в т.ч. асинхронный `validator`).\n * Прогоняются на blur (после первого касания) и на подтверждении.\n */\n rules?: GrFormRule | GrFormRule[]\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n /**\n * Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт\n * подтверждение или проверка `rules`. Кнопка закрытия и «Отмена» остаются:\n * окно без единого выхода — ловушка.\n */\n persistent?: boolean\n}\n\nexport interface GrPromptDialogEmits {\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}\n\nimport './defaults'\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: undefined,\n description: undefined,\n label: undefined,\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n // Дефолт `size` живёт в резолвере ниже, а не здесь: Vue подставил бы его\n // до того, как компонент заглянет в `GrConfigProvider`.\n size: undefined,\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: undefined,\n buttonSize: undefined,\n confirmText: undefined,\n cancelText: undefined,\n requiredErrorText: undefined,\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n inputType: 'text',\n inputmode: undefined,\n maxlength: undefined,\n showCount: false,\n multiline: false,\n rows: undefined,\n autosize: false,\n rules: undefined,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n persistent: false,\n})\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentProp('GrPromptDialog', 'size', () => props.size, 'md')\n\nconst emit = defineEmits<GrPromptDialogEmits>()\n\n// Дефолты берём из общего i18n-блока пакета (fallback — англ.), а не хардкодим.\nconst { t } = useGranularityTranslations()\nconst resolvedTitle = computed(() => props.title ?? t('gr.dialog.prompt.title', 'Prompt'))\nconst resolvedLabel = computed(() => props.label ?? t('gr.dialog.prompt.label', 'Value'))\nconst resolvedRequiredErrorText = computed(() => props.requiredErrorText ?? t('gr.dialog.prompt.required', 'Enter a value.'))\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? t('gr.common.close', 'Close'))\nconst resolvedConfirmText = computed(() => props.confirmText ?? t('gr.common.confirm', 'Confirm'))\nconst resolvedCancelText = computed(() => props.cancelText ?? t('gr.common.cancel', 'Cancel'))\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst textareaRef = ref<InstanceType<typeof GrTextarea> | null>(null)\n\n/** Ошибка последнего прогона `rules`; `required` считается отдельно и мгновенно. */\nconst ruleError = ref<string | undefined>(undefined)\nconst validating = ref(false)\n\n// Сторож устаревших прогонов: асинхронное правило может ответить после того,\n// как значение уже сменилось, и дописать чужую ошибку.\nlet validationRun = 0\n\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (!isOpen) return\n\n touched.value = false\n ruleError.value = undefined\n validationRun += 1\n },\n)\n\nconst resolveMessage = createGrFormMessageResolver(t)\n\nconst normalizedRules = computed<GrFormRule[]>(() => {\n if (!props.rules) return []\n return Array.isArray(props.rules) ? props.rules : [props.rules]\n})\n\nconst requiredError = computed(() => {\n if (!props.required) return undefined\n return valueModel.value.trim().length > 0 ? undefined : resolvedRequiredErrorText.value\n})\n\nconst canConfirm = computed(() => !requiredError.value)\n\nconst validationError = computed(() => {\n if (!touched.value) return undefined\n return requiredError.value ?? ruleError.value\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\n/**\n * Прогоняет `rules` для триггера. Возвращает сообщение или `undefined`.\n * `required` здесь не участвует: он синхронный и считается отдельно, чтобы\n * пустое поле блокировало кнопку без похода в асинхронный движок.\n */\nasync function runRules(trigger: GrFormTrigger): Promise<string | undefined> {\n const rules = rulesForTrigger(normalizedRules.value, trigger)\n if (!rules.length) return undefined\n\n const run = ++validationRun\n validating.value = true\n try {\n const message = await runFieldRules(valueModel.value, rules, { value: valueModel.value }, resolveMessage)\n // Ответ устарел — значение или сам диалог успели смениться.\n if (run !== validationRun) return undefined\n\n ruleError.value = message\n return message\n }\n finally {\n if (run === validationRun) validating.value = false\n }\n}\n\nfunction onBlur(): void {\n touched.value = true\n void runRules('blur')\n}\n\n// Пока подтверждение или проверка в полёте, случайное движение не должно\n// оборвать операцию.\nconst softCloseBlocked = computed(() => props.persistent && (props.confirmLoading || validating.value))\nconst resolvedCloseOnBackdrop = computed(() => (softCloseBlocked.value ? false : props.closeOnBackdrop))\nconst resolvedCloseOnEsc = computed(() => (softCloseBlocked.value ? false : props.closeOnEsc))\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction focusField(): void {\n if (props.multiline) textareaRef.value?.focus()\n else inputRef.value?.focus()\n}\n\nasync function onConfirm(): Promise<void> {\n touched.value = true\n\n if (!canConfirm.value) {\n focusField()\n return\n }\n\n if (await runRules('submit')) {\n focusField()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n\n/**\n * `Enter` подтверждает — базовое ожидание от окна, единственный смысл которого\n * ввести одно значение. В многострочном режиме `Enter` остаётся переводом\n * строки, поэтому обработчик висит только на однострочном поле.\n */\nfunction onEnter(): void {\n void onConfirm()\n}\n\n// Фокус в поле, а не на панель: диалог существует ровно ради ввода.\n//\n// Через `initialFocus` у `GrModal` это не решается: элемент рождается внутри\n// поддерева диалога, и возврат его же пропом наверх замыкает рендер в цикл\n// («Maximum recursive updates» — проверено). Поэтому фокус ставит содержимое,\n// после отрисовки (`flush: 'post'` + кадр), то есть заведомо позже, чем\n// ловушка фокуса `GrModal` переводит фокус на панель.\n// Источник — не только `modelValue`, но и само поле: содержимое панели\n// появляется на такт позже открытия, и фокус по фиксированному `nextTick`\n// приходился на момент, когда фокусировать ещё нечего.\nwatch(\n [() => props.modelValue, inputRef, textareaRef],\n () => {\n if (!props.modelValue) return\n focusField()\n },\n // `immediate`: окно могут смонтировать уже открытым — тогда смены пропа не\n // будет вовсе, и без этого фокус остался бы на панели.\n { immediate: true, flush: 'post' },\n)\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"resolvedTitle\"\n :size=\"resolvedSize\"\n :close-on-backdrop=\"resolvedCloseOnBackdrop\"\n :close-on-esc=\"resolvedCloseOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"resolvedCloseLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[length:var(--gr-control-text-md)] text-[var(--gr-muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <!-- `id` полю не задаём: `GrFormField` генерирует его сам, а поле читает\n из контекста. Литеральный id ломал два открытых диалога сразу. -->\n <GrFormField :label=\"resolvedLabel\" :error=\"fieldErrorMessage\">\n <GrTextarea\n v-if=\"multiline\"\n ref=\"textareaRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :autosize=\"autosize\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n />\n <GrInput\n v-else\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :type=\"inputType\"\n :inputmode=\"inputmode\"\n :placeholder=\"placeholder\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n @keydown.enter=\"onEnter\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"gr-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ resolvedCancelText }}\n </GrButton>\n <GrButton\n data-testid=\"gr-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading || validating\"\n :disabled=\"confirmDisabled || (touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ resolvedConfirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrRadio-BdhknbO8.js","names":[],"sources":["../../../../src/components/GrRadio/grRadioGroupContext.ts","../../../../src/components/GrRadio/GrRadio.vue","../../../../src/components/GrRadio/GrRadio.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey } from 'vue'\n\nimport type { GrButtonSize } from '../GrButton/grButtonStyles'\n\n/**\n * Значение переключателя. Не только строка: перечисления в реальных формах —\n * это id числом или флаг. Объекты сюда намеренно не входят: значение уходит в\n * `data-value` и в скрытый input нативной формы, а значит должно иметь\n * однозначное строковое представление.\n */\nexport type GrRadioValue = string | number | boolean\n\nexport type GrRadioEntry = {\n value: () => GrRadioValue\n disabled: () => boolean\n /**\n * Корневой узел переключателя. Геттер, а не значение: регистрация идёт в\n * `setup`, когда элемента ещё нет. Благодаря ему группа переносит фокус, не\n * обходя DOM селекторами.\n */\n el: () => HTMLElement | null\n}\n\nexport type GrRadioGroupContext = {\n modelValue: ComputedRef<GrRadioValue>\n name: ComputedRef<string | undefined>\n disabled: ComputedRef<boolean>\n /**\n * Только для чтения: выбор видно, но он не меняется. У роли `radio` нет\n * `aria-readonly` — состояние объявляет сама группа, переключателю остаётся\n * не прикидываться кликабельным.\n */\n readonly: ComputedRef<boolean>\n /** Ошибка группы: до вида переключателя она доходит только через контекст. */\n invalid: ComputedRef<boolean>\n size: ComputedRef<GrButtonSize>\n setValue: (value: GrRadioValue) => void\n /**\n * Регистрация переключателя в группе. Порядок регистрации совпадает с\n * порядком в документе (setup детей идёт сверху вниз), поэтому группа знает\n * состав и в slot-режиме, и на сервере — без обхода DOM.\n */\n register: (entry: GrRadioEntry) => () => void\n /**\n * Значение переключателя, который держит `tabindex=\"0\"`. Roving tabindex\n * WAI-ARIA: группа — одна остановка Tab, внутрь попадают стрелками. Если не\n * выбрано ничего, остановкой становится первый доступный переключатель.\n */\n rovingValue: ComputedRef<GrRadioValue | undefined>\n /**\n * Стрелки, `Home`, `End`: переносят выбор и фокус на соседний доступный\n * переключатель. Вернул `true` — клавиша обработана (с `preventDefault`).\n * Кольцо ведёт группа, а не переключатель: только она знает состав.\n */\n handleNavigationKeys: (event: KeyboardEvent) => boolean\n}\n\nexport const GR_RADIO_GROUP_CONTEXT: InjectionKey<GrRadioGroupContext> = Symbol('GR_RADIO_GROUP_CONTEXT')\n","<script setup lang=\"ts\">\nimport { computed, inject, onUnmounted, ref, useId, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDescriptionSizes,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioDotSizes,\n grRadioLabelClassFor,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT, type GrRadioValue } from './grRadioGroupContext'\n\nexport type { GrRadioValue } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name`/`invalid` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n *\n * Доступным контролом является сам элемент с `role=\"radio\"`: он держит фокус,\n * `aria-checked` и клавиатуру. Внутрь него ничего интерактивного не вкладывается —\n * `role=\"radio\"` объявляет потомков презентационными, и вложенный native `<input>`\n * (даже скрытый и с `tabindex=\"-1\"`) ломает виджет для скринридеров и падает\n * в axe на `nested-interactive`. Для отправки нативной формой рядом рендерится\n * `input[type=\"hidden\"]` — он не фокусируется и не является интерактивным.\n */\nexport interface GrRadioProps {\n value: GrRadioValue\n modelValue?: GrRadioValue\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */\n invalid?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nexport interface GrRadioEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n}\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n invalid: false,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrRadioEmits>()\n\nconst slots = useSlots()\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedReadonly = computed(() => group?.readonly.value ?? false)\n\n// «Или», а не `??`: ошибку может объявить и группа, и сам переключатель.\nconst resolvedInvalid = computed(() => props.invalid || (group?.invalid.value ?? false))\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\n// Приоритет: локальный проп → размер группы → `GrConfigProvider` → `md`.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize(\n () => props.size ?? group?.size.value,\n { component: 'GrRadio' },\n)\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\n/** В DOM значение живёт строкой: `data-value` и скрытый input иначе не умеют. */\nconst domValue = computed(() => String(props.value))\n\n// Нативная форма получает значение только от выбранного и не-disabled элемента —\n// ровно как поступил бы native radio. Без `name` отправлять нечего.\nconst submitsValue = computed(\n () => checked.value && !resolvedDisabled.value && Boolean(resolvedName.value),\n)\n\nconst descriptionId = useId()\nconst hasDescription = computed(() => Boolean(slots.description))\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass({\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n}))\nconst controlClassName = computed(() => grRadioControlClass({\n size: resolvedSize.value,\n checked: checked.value,\n disabled: resolvedDisabled.value,\n invalid: resolvedInvalid.value,\n}))\nconst dotClassName = computed(() => grRadioDotClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n}))\nconst labelClassName = computed(() => grRadioLabelClassFor(resolvedSize.value, checked.value))\nconst dotSizeClassName = computed(() => grRadioDotSizes[resolvedSize.value])\nconst descriptionClassName = computed(() => grRadioDescriptionSizes[resolvedSize.value])\n\nfunction setValue(next: GrRadioValue): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n\n// Паттерн radiogroup: группа — одна остановка Tab, внутрь попадают стрелками.\n// Вне группы (одиночный `GrRadio`) переключатель остаётся обычной остановкой.\nconst rovingTabindex = computed(() => {\n if (resolvedDisabled.value) return -1\n if (!group) return 0\n return group.rovingValue.value === props.value ? 0 : -1\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n/** Кольцо ведёт группа: только она знает состав и порядок. */\nfunction onKeydown(event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n group.handleNavigationKeys(event)\n}\n\nconst unregister = group?.register({\n value: () => props.value,\n disabled: () => resolvedDisabled.value,\n el: () => rootEl.value,\n})\n\nonUnmounted(() => unregister?.())\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :aria-describedby=\"hasDescription ? descriptionId : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n class=\"inline-flex items-start gap-2 select-none focus-visible:outline-none focus-visible:rounded-[var(--gr-radius-md)] focus-visible:shadow-[0_0_0_2px_var(--gr-ring),0_0_0_4px_var(--gr-bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"mt-0.5 shrink-0 rounded-[var(--gr-radius-full)] border flex items-center justify-center transition-colors duration-[var(--gr-duration-fast)]\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotSizeClassName, dotClassName]\"\n />\n </span>\n\n <span class=\"grid gap-0.5\">\n <span data-gr-radio-label :class=\"labelClassName\">\n <slot />\n </span>\n\n <span\n v-if=\"hasDescription\"\n :id=\"descriptionId\"\n data-gr-radio-description\n :class=\"descriptionClassName\"\n >\n <slot name=\"description\" />\n </span>\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, onUnmounted, ref, useId, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDescriptionSizes,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioDotSizes,\n grRadioLabelClassFor,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT, type GrRadioValue } from './grRadioGroupContext'\n\nexport type { GrRadioValue } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name`/`invalid` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n *\n * Доступным контролом является сам элемент с `role=\"radio\"`: он держит фокус,\n * `aria-checked` и клавиатуру. Внутрь него ничего интерактивного не вкладывается —\n * `role=\"radio\"` объявляет потомков презентационными, и вложенный native `<input>`\n * (даже скрытый и с `tabindex=\"-1\"`) ломает виджет для скринридеров и падает\n * в axe на `nested-interactive`. Для отправки нативной формой рядом рендерится\n * `input[type=\"hidden\"]` — он не фокусируется и не является интерактивным.\n */\nexport interface GrRadioProps {\n value: GrRadioValue\n modelValue?: GrRadioValue\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */\n invalid?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nexport interface GrRadioEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n}\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n invalid: false,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrRadioEmits>()\n\nconst slots = useSlots()\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedReadonly = computed(() => group?.readonly.value ?? false)\n\n// «Или», а не `??`: ошибку может объявить и группа, и сам переключатель.\nconst resolvedInvalid = computed(() => props.invalid || (group?.invalid.value ?? false))\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\n// Приоритет: локальный проп → размер группы → `GrConfigProvider` → `md`.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize(\n () => props.size ?? group?.size.value,\n { component: 'GrRadio' },\n)\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\n/** В DOM значение живёт строкой: `data-value` и скрытый input иначе не умеют. */\nconst domValue = computed(() => String(props.value))\n\n// Нативная форма получает значение только от выбранного и не-disabled элемента —\n// ровно как поступил бы native radio. Без `name` отправлять нечего.\nconst submitsValue = computed(\n () => checked.value && !resolvedDisabled.value && Boolean(resolvedName.value),\n)\n\nconst descriptionId = useId()\nconst hasDescription = computed(() => Boolean(slots.description))\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass({\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n}))\nconst controlClassName = computed(() => grRadioControlClass({\n size: resolvedSize.value,\n checked: checked.value,\n disabled: resolvedDisabled.value,\n invalid: resolvedInvalid.value,\n}))\nconst dotClassName = computed(() => grRadioDotClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n}))\nconst labelClassName = computed(() => grRadioLabelClassFor(resolvedSize.value, checked.value))\nconst dotSizeClassName = computed(() => grRadioDotSizes[resolvedSize.value])\nconst descriptionClassName = computed(() => grRadioDescriptionSizes[resolvedSize.value])\n\nfunction setValue(next: GrRadioValue): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n\n// Паттерн radiogroup: группа — одна остановка Tab, внутрь попадают стрелками.\n// Вне группы (одиночный `GrRadio`) переключатель остаётся обычной остановкой.\nconst rovingTabindex = computed(() => {\n if (resolvedDisabled.value) return -1\n if (!group) return 0\n return group.rovingValue.value === props.value ? 0 : -1\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n/** Кольцо ведёт группа: только она знает состав и порядок. */\nfunction onKeydown(event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n group.handleNavigationKeys(event)\n}\n\nconst unregister = group?.register({\n value: () => props.value,\n disabled: () => resolvedDisabled.value,\n el: () => rootEl.value,\n})\n\nonUnmounted(() => unregister?.())\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :aria-describedby=\"hasDescription ? descriptionId : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n class=\"inline-flex items-start gap-2 select-none focus-visible:outline-none focus-visible:rounded-[var(--gr-radius-md)] focus-visible:shadow-[0_0_0_2px_var(--gr-ring),0_0_0_4px_var(--gr-bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"mt-0.5 shrink-0 rounded-[var(--gr-radius-full)] border flex items-center justify-center transition-colors duration-[var(--gr-duration-fast)]\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotSizeClassName, dotClassName]\"\n />\n </span>\n\n <span class=\"grid gap-0.5\">\n <span data-gr-radio-label :class=\"labelClassName\">\n <slot />\n </span>\n\n <span\n v-if=\"hasDescription\"\n :id=\"descriptionId\"\n data-gr-radio-description\n :class=\"descriptionClassName\"\n >\n <slot name=\"description\" />\n </span>\n </span>\n </div>\n</template>\n"],"mappings":"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"}