@feugene/granularity 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (578) 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-0n_KGeUy.js} +4 -4
  223. package/dist/components/GrColorPicker/chunks/{GrColorPicker-CDaJRVIM.js.map → GrColorPicker-0n_KGeUy.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-B--br5so.js} +4 -4
  249. package/dist/components/GrDialogService/chunks/{GrDialogService-CHVR4sOk.js.map → GrDialogService-B--br5so.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-c53iltz0.js} +1 -1
  292. package/dist/components/GrInput/chunks/{GrInput-Bb_pxqpT.js.map → GrInput-c53iltz0.js.map} +1 -1
  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-ClTvjlFx.js} +4 -4
  298. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-jk3ylOTc.js.map → GrJsonViewer-ClTvjlFx.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-CaNTZc-v.js} +3 -3
  321. package/dist/components/GrPagination/chunks/{GrPagination-DG7kp6VL.js.map → GrPagination-CaNTZc-v.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-DMhALhSS.js} +6 -6
  331. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-DMhALhSS.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-BE1VNTiY.js} +96 -89
  351. package/dist/components/GrSelect/chunks/GrSelect-BE1VNTiY.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-KRlRMwgp.js} +1 -1
  373. package/dist/components/GrSwitch/chunks/{GrSwitch-CHiIj9GT.js.map → GrSwitch-KRlRMwgp.js.map} +1 -1
  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-Db_uKdNa.js} +18 -10
  400. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-Db_uKdNa.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 +1 -1
  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/GrRating/GrRating.vue.d.ts +1 -1
  471. package/dist/types/components/GrSegmented/GrSegmented.vue.d.ts +1 -1
  472. package/dist/types/components/GrSelect/grSelectStyles.d.ts +1 -1
  473. package/dist/types/components/GrSidebar/GrSidebar.vue.d.ts +7 -7
  474. package/dist/types/components/GrSidebar/grSidebarStyles.d.ts +4 -4
  475. package/dist/types/components/GrSlider/GrSlider.vue.d.ts +1 -1
  476. package/dist/types/components/GrSortableList/grSortableListStyles.d.ts +2 -2
  477. package/dist/types/components/GrSplitter/GrSplitter.vue.d.ts +1 -1
  478. package/dist/types/components/GrStatistic/GrStatistic.vue.d.ts +13 -13
  479. package/dist/types/components/GrSteps/grStepsStyles.d.ts +2 -2
  480. package/dist/types/components/GrSwitch/GrSwitch.vue.d.ts +1 -1
  481. package/dist/types/components/GrTabPanels/GrTabPanel.vue.d.ts +3 -3
  482. package/dist/types/components/GrTabPanels/GrTabPanels.vue.d.ts +3 -3
  483. package/dist/types/components/GrTable/GrTable.vue.d.ts +13 -13
  484. package/dist/types/components/GrTextarea/GrTextarea.vue.d.ts +1 -1
  485. package/dist/types/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
  486. package/dist/types/components/shared/classTokens.d.ts +8 -0
  487. package/dist/types/components/shared/numericStep.d.ts +14 -0
  488. package/dist/types/components/shared/overlayTransition.d.ts +33 -0
  489. package/dist/web-types.json +292 -218
  490. package/package.json +2 -2
  491. package/tokens/foundation.json +41 -1
  492. package/dist/chunks/classTokens-BzazaERD.js +0 -6
  493. package/dist/chunks/classTokens-BzazaERD.js.map +0 -1
  494. package/dist/chunks/config-9PYRZlKE.js.map +0 -1
  495. package/dist/chunks/config-B4w5gBD0.js.map +0 -1
  496. package/dist/chunks/config-BEVBsvni2.js.map +0 -1
  497. package/dist/chunks/config-BN4sXYXn.js.map +0 -1
  498. package/dist/chunks/config-BViooYNv.js.map +0 -1
  499. package/dist/chunks/config-B_qi4Fma.js.map +0 -1
  500. package/dist/chunks/config-Baj7Wvx-.js.map +0 -1
  501. package/dist/chunks/config-BbY2SKGE.js.map +0 -1
  502. package/dist/chunks/config-BfsBepkH.js.map +0 -1
  503. package/dist/chunks/config-Bpt9Xbxm.js.map +0 -1
  504. package/dist/chunks/config-BsPDQ0dS.js.map +0 -1
  505. package/dist/chunks/config-BtXRpAtq.js.map +0 -1
  506. package/dist/chunks/config-BuGlNPE92.js.map +0 -1
  507. package/dist/chunks/config-C8tVRv8i.js.map +0 -1
  508. package/dist/chunks/config-CIcynHGr.js.map +0 -1
  509. package/dist/chunks/config-CN2ULSQL.js.map +0 -1
  510. package/dist/chunks/config-CN970ZcJ.js.map +0 -1
  511. package/dist/chunks/config-COvrUfhR.js.map +0 -1
  512. package/dist/chunks/config-Cl4ST6Qd.js.map +0 -1
  513. package/dist/chunks/config-Cwd3yjRw.js.map +0 -1
  514. package/dist/chunks/config-D7zgE_Gr.js.map +0 -1
  515. package/dist/chunks/config-D94Z9po5.js.map +0 -1
  516. package/dist/chunks/config-DAX8Deh1.js.map +0 -1
  517. package/dist/chunks/config-DBmdgoVf.js.map +0 -1
  518. package/dist/chunks/config-DCFvmHEv.js.map +0 -1
  519. package/dist/chunks/config-DGWFal5h.js.map +0 -1
  520. package/dist/chunks/config-DKdlqXiY.js.map +0 -1
  521. package/dist/chunks/config-DSJY6aEl.js.map +0 -1
  522. package/dist/chunks/config-DWN4x6_e.js.map +0 -1
  523. package/dist/chunks/config-Dgj1Q6JY.js.map +0 -1
  524. package/dist/chunks/config-Dguto_6o.js.map +0 -1
  525. package/dist/chunks/config-DtgYuGUn2.js.map +0 -1
  526. package/dist/chunks/config-rkcKArZQ.js.map +0 -1
  527. package/dist/chunks/config-vSji8w90.js.map +0 -1
  528. package/dist/chunks/config-zjeAJ4ho2.js.map +0 -1
  529. package/dist/chunks/context-BfIQv7m12.js.map +0 -1
  530. package/dist/chunks/generated-CJe6hdiE.js.map +0 -1
  531. package/dist/chunks/grBadgeStyles-C1S56Alo.js.map +0 -1
  532. package/dist/chunks/grDropdownMenuStyles-D1LOkdVj.js.map +0 -1
  533. package/dist/chunks/grInputStyles-CJeMhUe5.js.map +0 -1
  534. package/dist/chunks/grKbdStyles-CeZhGQHr.js.map +0 -1
  535. package/dist/chunks/grLinkStyles-BoL8QYcV.js.map +0 -1
  536. package/dist/chunks/grPopoverStyles-Cke_BZsM.js.map +0 -1
  537. package/dist/chunks/grRadioStyles-BTjGNhtU.js.map +0 -1
  538. package/dist/chunks/grSelectStyles-eTz4uPMP.js.map +0 -1
  539. package/dist/chunks/grTextareaStyles-iOcHuVMM.js.map +0 -1
  540. package/dist/chunks/useMenuItemsFocus-OyOnIAlr.js.map +0 -1
  541. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-D9c9BNFg.js.map +0 -1
  542. package/dist/components/GrBadge/chunks/GrBadge-_9rCHuE1.js.map +0 -1
  543. package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-DDoFQscR.js.map +0 -1
  544. package/dist/components/GrButton/chunks/GrButton-DCz-Wn-V.js.map +0 -1
  545. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-BQo5VQ_c.js.map +0 -1
  546. package/dist/components/GrCard/chunks/GrCard-aQIjk6I9.js.map +0 -1
  547. package/dist/components/GrChip/chunks/GrChip-BnL2FedO.js.map +0 -1
  548. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BPsVN_XU.js.map +0 -1
  549. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-CwXNLyzk.js.map +0 -1
  550. package/dist/components/GrContextMenu/chunks/GrContextMenu-BbAJholy.js.map +0 -1
  551. package/dist/components/GrDataTable/chunks/GrDataTable-NRbrXpdx.js.map +0 -1
  552. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-1mICfrqX.js.map +0 -1
  553. package/dist/components/GrDialog/chunks/GrDialog-5Y5K87ss.js.map +0 -1
  554. package/dist/components/GrDivider/chunks/GrDivider-CAhm9jbE.js.map +0 -1
  555. package/dist/components/GrDropdown/chunks/GrDropdown-BfTmdPyh.js.map +0 -1
  556. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-l5HEqzjr.js.map +0 -1
  557. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-BGA_xNVo.js.map +0 -1
  558. package/dist/components/GrForm/chunks/GrForm-CAlJg-wb.js.map +0 -1
  559. package/dist/components/GrIcon/chunks/GrIcon-bQwtZtYT.js.map +0 -1
  560. package/dist/components/GrImageViewer/chunks/GrImageViewer-ng7el4wn.js.map +0 -1
  561. package/dist/components/GrInputTag/chunks/GrInputTag-UI8kIB_F.js.map +0 -1
  562. package/dist/components/GrLink/chunks/GrLink-BQb7U4so.js.map +0 -1
  563. package/dist/components/GrList/chunks/GrList-B5bclmHV.js.map +0 -1
  564. package/dist/components/GrNumberInput/chunks/GrNumberInput-V2mZvDDC.js.map +0 -1
  565. package/dist/components/GrPopover/chunks/GrPopover-DDChM1jD.js.map +0 -1
  566. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-BGuMUzlv.js.map +0 -1
  567. package/dist/components/GrRadio/chunks/GrRadio-BdhknbO8.js.map +0 -1
  568. package/dist/components/GrSelect/chunks/GrSelect-BA1wfX3c.js.map +0 -1
  569. package/dist/components/GrSlider/chunks/GrSlider-s99yAyz6.js.map +0 -1
  570. package/dist/components/GrStatistic/chunks/GrStatistic-BJQv5aOS.js.map +0 -1
  571. package/dist/components/GrTable/chunks/GrTable-f3qCWa99.js.map +0 -1
  572. package/dist/components/GrToaster/chunks/GrToaster-CC49q3a4.js.map +0 -1
  573. package/dist/components/GrTooltip/chunks/GrTooltip-B6AwE_UX.js.map +0 -1
  574. package/dist/components/GrTree/chunks/GrTree-J5q8aCya.js.map +0 -1
  575. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-DEfxWAc3.js.map +0 -1
  576. /package/dist/chunks/{config-BuGlNPE92.js → config-BuGlNPE9.js} +0 -0
  577. /package/dist/chunks/{config-DBmdgoVf.js → config-DBmdgoVf2.js} +0 -0
  578. /package/dist/chunks/{context-BfIQv7m12.js → context-BfIQv7m1.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"grPopoverStyles-Cke_BZsM.js","names":[],"sources":["../../src/components/GrPopover/grPopoverStyles.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type GrPopoverSize = GrComponentSize\n\n/**\n * Роль панели.\n *\n * `dialog` — произвольный интерактивный контент (значение по умолчанию).\n * Остальные нужны компонентам, которые строятся поверх этого примитива:\n * меню (`GrMenu`, `GrContextMenu`), списки, палитры. Роль задаёт потребитель,\n * потому что клавиатурный паттерн принадлежит ему, а не поповеру.\n */\nexport type GrPopoverRole = 'dialog' | 'menu' | 'listbox' | 'grid' | 'group' | 'none'\n\n/** Поле панели. `none` — поле рисует содержимое (меню, список опций). */\nexport type GrPopoverPadding = 'default' | 'none'\n\nexport const panelSizes: Record<GrPopoverSize, string> = {\n xs: 'p-2 text-[length:var(--gr-control-text-xs)]',\n sm: 'p-2.5 text-[length:var(--gr-control-text-sm)]',\n md: 'p-3 text-[length:var(--gr-control-text-md)] leading-[var(--gr-leading-sm)]',\n lg: 'p-4 text-[length:var(--gr-control-text-lg)] leading-[var(--gr-leading-base)]',\n}\n\n/**\n * `transform-origin` для scale-анимации: координаты панели уже посчитал\n * `useFloating`, здесь остаётся направление «роста» относительно триггера.\n *\n * Ключ — `resolvedPlacement`, то есть положение **после** `flip`: если снизу не\n * хватило места и панель перевернуло вверх, origin переворачивается вместе с ней.\n */\nexport const originClassByPlacement: Partial<Record<Placement, string>> = {\n 'bottom': 'origin-top',\n 'bottom-start': 'origin-top-left',\n 'bottom-end': 'origin-top-right',\n 'top': 'origin-bottom',\n 'top-start': 'origin-bottom-left',\n 'top-end': 'origin-bottom-right',\n 'left': 'origin-right',\n 'left-start': 'origin-top-right',\n 'left-end': 'origin-bottom-right',\n 'right': 'origin-left',\n 'right-start': 'origin-top-left',\n 'right-end': 'origin-bottom-left',\n}\n\nexport function grPopoverOriginClass(placement: Placement): string {\n return originClassByPlacement[placement] ?? 'origin-top'\n}\n\n/**\n * Поверхность панели. `max-w-[min(...)]` не даёт поповеру вылезти за узкий\n * экран: `useFloating` смещает панель в пределах viewport, но не сужает её.\n */\n/**\n * Кегль без поля.\n *\n * Отдельная мапа, а не `contentClass=\"p-0\"`: поле и кегль приезжают одной\n * строкой равной специфичности, и кто победит — решал бы порядок правил в\n * сгенерированном CSS. Меню внутри поповера с `p-3` — визуально не меню.\n */\nexport const panelSizesFlush: Record<GrPopoverSize, string> = {\n xs: 'text-[length:var(--gr-control-text-xs)]',\n sm: 'text-[length:var(--gr-control-text-sm)]',\n md: 'text-[length:var(--gr-control-text-md)] leading-[var(--gr-leading-sm)]',\n lg: 'text-[length:var(--gr-control-text-lg)] leading-[var(--gr-leading-base)]',\n}\n\nexport const popoverPanelBaseClass = 'rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-2)] max-w-[min(22rem,calc(100vw-1rem))] focus:outline-none'\n\nexport function grPopoverPanelClass(\n size: GrPopoverSize,\n placement: Placement,\n extra?: string,\n padding: GrPopoverPadding = 'default',\n): string {\n return [\n popoverPanelBaseClass,\n padding === 'none' ? panelSizesFlush[size] : panelSizes[size],\n grPopoverOriginClass(placement),\n extra ?? '',\n ].filter(Boolean).join(' ')\n}\n"],"mappings":";AAmBA,IAAa,aAA4C;CACvD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;;;;;;;AASA,IAAa,yBAA6D;CACxE,UAAU;CACV,gBAAgB;CAChB,cAAc;CACd,OAAO;CACP,aAAa;CACb,WAAW;CACX,QAAQ;CACR,cAAc;CACd,YAAY;CACZ,SAAS;CACT,eAAe;CACf,aAAa;AACf;AAEA,SAAgB,qBAAqB,WAA8B;CACjE,OAAO,uBAAuB,cAAc;AAC9C;;;;;;;;;;;;AAaA,IAAa,kBAAiD;CAC5D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,wBAAwB;AAErC,SAAgB,oBACd,MACA,WACA,OACA,UAA4B,WACpB;CACR,OAAO;EACL;EACA,YAAY,SAAS,gBAAgB,QAAQ,WAAW;EACxD,qBAAqB,SAAS;EAC9B,SAAS;CACX,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AAC5B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"grRadioStyles-BTjGNhtU.js","names":[],"sources":["../../src/components/GrRadio/grRadioStyles.ts"],"sourcesContent":["import {\n grButtonBaseClass,\n grButtonClass,\n sizes as grButtonSizes,\n type GrButtonSize,\n type GrButtonTone,\n type GrButtonVariant,\n} from '../GrButton/grButtonStyles'\n\nexport type GrRadioVariant = 'radiobox' | 'button'\n\nexport const grRadioEnabledClass = 'cursor-pointer'\n\n/**\n * Отключённая кнопка-радио гасится, но НЕ теряет состояние: в отключённой\n * группе всё равно должно быть видно, что выбрано. Поэтому не отдаём вид\n * `GrButton`-у (его disabled состояние-слепой), а держим свою пару.\n */\nexport const grRadioButtonDisabledClass = 'cursor-not-allowed bg-transparent text-[var(--gr-muted-fg)] border border-[var(--gr-brd)]'\nexport const grRadioButtonDisabledCheckedClass = 'cursor-not-allowed bg-[var(--gr-muted)] text-[var(--gr-fg)] border border-[var(--gr-muted-fg)]'\n\n/**\n * Отключённый переключатель гасится токенами, а не `opacity`: прозрачность\n * разбавляет выверенные на AA цвета текста.\n */\nexport const grRadioRootDisabledClass = 'cursor-not-allowed text-[var(--gr-muted-fg)]'\nexport const grRadioRootEnabledClass = 'cursor-pointer'\n/** Только для чтения: значение видно и читается, но курсор не обещает клика. */\nexport const grRadioRootReadonlyClass = 'cursor-default'\n\nexport const grRadioControlCheckedClass = 'border-[var(--gr-primary)] bg-[color-mix(in_srgb,var(--gr-primary)_10%,var(--gr-bg))]'\nexport const grRadioControlUncheckedClass = 'border-[var(--gr-brd)] bg-[var(--gr-bg)]'\nexport const grRadioControlDisabledClass = 'border-[var(--gr-brd)] bg-[var(--gr-muted)]'\nexport const grRadioControlInvalidClass = 'border-[var(--gr-danger)] bg-[var(--gr-bg)]'\n\nexport const grRadioDotCheckedClass = 'bg-[var(--gr-primary)] opacity-100 scale-100'\nexport const grRadioDotUncheckedClass = 'bg-transparent opacity-0 scale-75'\nexport const grRadioDotDisabledClass = 'bg-[var(--gr-muted-fg)] opacity-100 scale-100'\n// Базовые классы «точки» внутри radiobox — нестандартный transition. Вынесены сюда,\n// чтобы `safelist` гарантировал их присутствие в сборке даже при tree-shaking шаблона.\nexport const grRadioDotBaseClass = 'rounded-[var(--gr-radius-full)] transition-[transform,opacity,background-color] duration-[var(--gr-duration-fast)]'\n\n/**\n * Ступени `radiobox`: коробка, точка и кегли подписи с описанием.\n *\n * До этого коробка была зашита `h-4 w-4` в шаблоне, а кегли — фиксированным\n * `--gr-text-sm`: вариант `radiobox` не масштабировался вовсе, хотя `size` у\n * компонента был и работал для варианта `button`. Ступени согласованы с высотой\n * кнопки (`grButtonStyles.sizes`: 28/32/40/44) — радио стоит в тех же формах.\n */\nexport const grRadioControlSizes: Record<GrButtonSize, string> = {\n xs: 'h-3.5 w-3.5',\n sm: 'h-4 w-4',\n md: 'h-5 w-5',\n lg: 'h-6 w-6',\n}\n\nexport const grRadioDotSizes: Record<GrButtonSize, string> = {\n xs: 'h-[5px] w-[5px]',\n sm: 'h-[6px] w-[6px]',\n md: 'h-2 w-2',\n lg: 'h-2.5 w-2.5',\n}\n\nconst grRadioLabelTexts: Record<GrButtonSize, string> = {\n xs: 'text-[length:var(--gr-text-xs)] leading-[var(--gr-leading-xs)]',\n sm: 'text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)]',\n md: 'text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)]',\n lg: 'text-[length:var(--gr-text-base)] leading-[var(--gr-leading-base)]',\n}\n\nexport const grRadioDescriptionSizes: Record<GrButtonSize, string> = {\n xs: 'text-[length:var(--gr-text-2xs)] text-[var(--gr-muted-fg)]',\n sm: 'text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]',\n md: 'text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]',\n lg: 'text-[length:var(--gr-text-sm)] text-[var(--gr-muted-fg)]',\n}\n\n/** Выбранный вариант выделяется текстом, а не только точкой. */\nexport const grRadioLabelCheckedColorClass = 'text-[var(--gr-fg)] font-500'\nexport const grRadioLabelColorClass = 'text-[var(--gr-muted-fg)]'\n\nexport function grRadioLabelClassFor(size: GrButtonSize, checked: boolean): string {\n return [\n grRadioLabelTexts[size],\n checked ? grRadioLabelCheckedColorClass : grRadioLabelColorClass,\n ].join(' ')\n}\n\nexport const grRadioLabelSizes = grRadioLabelTexts\n\nexport function grRadioButtonClass(options: {\n checked: boolean\n disabled: boolean\n readonly: boolean\n size: GrButtonSize\n buttonVariant: GrButtonVariant\n buttonTone: GrButtonTone\n selectedButtonVariant: GrButtonVariant\n selectedButtonTone: GrButtonTone\n}): string {\n if (options.disabled) {\n return [\n grButtonBaseClass,\n grButtonSizes[options.size],\n options.checked ? grRadioButtonDisabledCheckedClass : grRadioButtonDisabledClass,\n ].join(' ')\n }\n\n const variant = options.checked ? options.selectedButtonVariant : options.buttonVariant\n const tone = options.checked ? options.selectedButtonTone : options.buttonTone\n\n return [\n grButtonBaseClass,\n grButtonClass({\n variant,\n tone,\n size: options.size,\n square: false,\n }),\n options.readonly ? grRadioRootReadonlyClass : grRadioEnabledClass,\n ].join(' ')\n}\n\nexport function grRadioRootClass(options: { disabled: boolean, readonly: boolean }): string {\n if (options.disabled) return grRadioRootDisabledClass\n return options.readonly ? grRadioRootReadonlyClass : grRadioRootEnabledClass\n}\n\nexport function grRadioControlClass(options: {\n checked: boolean\n disabled: boolean\n invalid: boolean\n size: GrButtonSize\n}): string {\n const state = options.disabled\n ? grRadioControlDisabledClass\n : options.invalid && !options.checked\n ? grRadioControlInvalidClass\n : options.checked ? grRadioControlCheckedClass : grRadioControlUncheckedClass\n\n return [grRadioControlSizes[options.size], state].join(' ')\n}\n\nexport function grRadioDotClass(options: { checked: boolean, disabled: boolean }): string {\n if (!options.checked)\n return grRadioDotUncheckedClass\n\n return options.disabled ? grRadioDotDisabledClass : grRadioDotCheckedClass\n}\n"],"mappings":";;AAWA,IAAa,sBAAsB;;;;;;AAOnC,IAAa,6BAA6B;AAC1C,IAAa,oCAAoC;;;;;AAMjD,IAAa,2BAA2B;AACxC,IAAa,0BAA0B;;AAEvC,IAAa,2BAA2B;AAExC,IAAa,6BAA6B;AAC1C,IAAa,+BAA+B;AAC5C,IAAa,8BAA8B;AAC3C,IAAa,6BAA6B;AAE1C,IAAa,yBAAyB;AACtC,IAAa,2BAA2B;AACxC,IAAa,0BAA0B;AAGvC,IAAa,sBAAsB;;;;;;;;;AAUnC,IAAa,sBAAoD;CAC/D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,kBAAgD;CAC3D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAM,oBAAkD;CACtD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,0BAAwD;CACnE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;AAGA,IAAa,gCAAgC;AAC7C,IAAa,yBAAyB;AAEtC,SAAgB,qBAAqB,MAAoB,SAA0B;CACjF,OAAO,CACL,kBAAkB,OAClB,UAAU,gCAAgC,sBAC5C,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAa,oBAAoB;AAEjC,SAAgB,mBAAmB,SASxB;CACT,IAAI,QAAQ,UACV,OAAO;EACL;EACA,MAAc,QAAQ;EACtB,QAAQ,UAAU,oCAAoC;CACxD,CAAC,CAAC,KAAK,GAAG;CAMZ,OAAO;EACL;EACA,cAAc;GACZ,SANY,QAAQ,UAAU,QAAQ,wBAAwB,QAAQ;GAOtE,MANS,QAAQ,UAAU,QAAQ,qBAAqB,QAAQ;GAOhE,MAAM,QAAQ;GACd,QAAQ;EACV,CAAC;EACD,QAAQ,WAAW,2BAA2B;CAChD,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,SAAgB,iBAAiB,SAA2D;CAC1F,IAAI,QAAQ,UAAU,OAAO;CAC7B,OAAO,QAAQ,WAAW,2BAA2B;AACvD;AAEA,SAAgB,oBAAoB,SAKzB;CACT,MAAM,QAAQ,QAAQ,WAClB,8BACA,QAAQ,WAAW,CAAC,QAAQ,UAC1B,6BACA,QAAQ,UAAU,6BAA6B;CAErD,OAAO,CAAC,oBAAoB,QAAQ,OAAO,KAAK,CAAC,CAAC,KAAK,GAAG;AAC5D;AAEA,SAAgB,gBAAgB,SAA0D;CACxF,IAAI,CAAC,QAAQ,SACX,OAAO;CAET,OAAO,QAAQ,WAAW,0BAA0B;AACtD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"grSelectStyles-eTz4uPMP.js","names":[],"sources":["../../src/components/GrSelect/grSelectStyles.ts"],"sourcesContent":["import type { GrComponentSize, GrControlState } from '../shared/sizes'\n\nexport type GrSelectView = 'default' | 'link'\nexport type GrSelectSize = GrComponentSize\nexport type GrSelectVariant = 'primary' | 'default' | 'muted' | 'danger'\nexport type GrSelectUnderline = 'auto' | 'always' | 'none'\nexport type GrSelectOptionsView = 'native' | 'panel'\n/**\n * Тип значения опции. Ограничен примитивами, которые переживают путь через DOM:\n * нативный `<select>` хранит в `option.value` только строку, поэтому значение\n * должно однозначно восстанавливаться из своего строкового представления.\n * Объектные модели потребовали бы отдельного key-экстрактора — см. `docs/components.md`.\n */\n/**\n * Значение опции. Объект допустим, но тогда обязателен проп `valueKey`: в DOM\n * значение живёт строкой, и без ключа объекты неотличимы друг от друга.\n */\nexport type GrSelectValue = string | number | object\nexport type GrSelectState = GrControlState\n\nexport type GrSelectOption<TValue extends GrSelectValue = string> = {\n value: TValue\n label: string\n disabled?: boolean\n}\n/** Группа опций: заголовок `label` + вложенные опции `options`. */\nexport type GrSelectOptionGroup<TValue extends GrSelectValue = string> = {\n label: string\n options: GrSelectOption<TValue>[]\n}\n/** Элемент списка опций: либо одиночная опция, либо группа опций. */\nexport type GrSelectOptionOrGroup<TValue extends GrSelectValue = string> =\n | GrSelectOption<TValue>\n | GrSelectOptionGroup<TValue>\nexport type GrSelectModelValue<TValue extends GrSelectValue = string> = TValue | TValue[]\n\nexport const defaultBaseClass = 'w-full rounded-[var(--gr-radius-control)] border bg-[var(--gr-bg)] text-[var(--gr-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]'\n\n/**\n * Цвет рамки по состоянию — та же карта, что у `GrInput` и `GrTextarea`: поля\n * одной формы обязаны показывать ошибку одинаково.\n */\nexport const borderClassByState: Record<GrSelectState, string> = {\n default: 'border-[var(--gr-brd)]',\n success: 'border-[var(--gr-success)] focus-visible:ring-[var(--gr-success)]',\n warning: 'border-[var(--gr-warning)] focus-visible:ring-[var(--gr-warning)]',\n danger: 'border-[var(--gr-danger)] focus-visible:ring-[var(--gr-danger)]',\n}\n\n/**\n * Ошибка валидации красится своей ролью, а не декоративным тоном `danger`:\n * `state=\"danger\"` — это подсветка по решению разработчика, `invalid` — вердикт\n * валидации, и тема вправе развести их по цвету.\n */\nexport const invalidBorderClass = 'border-[var(--gr-invalid-brd)] focus-visible:ring-[var(--gr-invalid-ring)]'\nexport const linkBaseClass = 'cursor-pointer inline-block w-auto align-baseline appearance-none bg-transparent border border-transparent px-0 py-0 rounded-[var(--gr-radius-control)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]'\n\n\nexport const selectSizeClassBySize: Record<GrSelectSize, string> = {\n xs: 'h-7 px-2.5 text-[length:var(--gr-control-text-xs)]',\n sm: 'h-8 px-3 text-[length:var(--gr-control-text-sm)]',\n md: 'h-10 px-3 text-[length:var(--gr-control-text-md)]',\n lg: 'h-11 px-4 text-[length:var(--gr-control-text-lg)]',\n}\n\nexport const selectLinkSizeClassBySize: Record<GrSelectSize, string> = {\n xs: 'text-[length:var(--gr-control-text-xs)]',\n sm: 'text-[length:var(--gr-control-text-sm)]',\n md: 'text-[length:var(--gr-control-text-md)]',\n lg: 'text-[length:var(--gr-control-text-lg)]',\n}\n\n/**\n * Ссылка — это ТЕКСТ на фоне страницы, поэтому опасный тон берётся из\n * `--gr-danger-text`, а не из насыщенного `--gr-danger` (последний под текст не\n * рассчитан). Hover/active выводятся подмесом `--gr-fg` — той же формулой, что\n * `GrLink`. `primary` берётся из `--gr-primary-text` по той же причине: на фоне\n * страницы насыщенный `--gr-primary` под текст не рассчитан.\n *\n * Классы записаны литералами, а не собраны интерполяцией: safelist и скан\n * UnoCSS читают исходник, и вычисленная строка для них не класс.\n */\nexport const selectLinkVariantClassByVariant: Record<GrSelectVariant, string> = {\n primary: 'text-[var(--gr-primary-text)] hover:text-[color-mix(in_srgb,var(--gr-primary-text)_92%,var(--gr-fg))] active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n default: 'text-[var(--gr-fg)] hover:text-[var(--gr-primary-text)] active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n muted: 'text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)] active:text-[var(--gr-fg)]',\n danger: 'text-[var(--gr-danger-text)] hover:text-[color-mix(in_srgb,var(--gr-danger-text)_92%,var(--gr-fg))] active:text-[color-mix(in_srgb,var(--gr-danger-text)_84%,var(--gr-fg))]',\n}\n\n/**\n * Variant-классы для видимой метки в `view=\"link\"` + `optionsView=\"native\"`.\n * Hover-цвет приходит через `peer-hover:` от прозрачного `<select>`-overlay,\n * т.к. видимая метка имеет `pointer-events-none` и не получает hover напрямую.\n */\nexport const selectLinkNativeLabelVariantClassByVariant: Record<GrSelectVariant, string> = {\n primary: 'text-[var(--gr-primary-text)] peer-hover:text-[color-mix(in_srgb,var(--gr-primary-text)_92%,var(--gr-fg))] peer-active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n default: 'text-[var(--gr-fg)] peer-hover:text-[var(--gr-primary-text)] peer-active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n muted: 'text-[var(--gr-muted-fg)] peer-hover:text-[var(--gr-fg)] peer-active:text-[var(--gr-fg)]',\n danger: 'text-[var(--gr-danger-text)] peer-hover:text-[color-mix(in_srgb,var(--gr-danger-text)_92%,var(--gr-fg))] peer-active:text-[color-mix(in_srgb,var(--gr-danger-text)_84%,var(--gr-fg))]',\n}\n\nfunction selectLinkUnderlineClass(options: { underline: GrSelectUnderline, disabled: boolean }): string {\n if (options.disabled) return 'no-underline'\n if (options.underline === 'always') return 'underline underline-offset-4'\n if (options.underline === 'none') return 'no-underline'\n return 'no-underline hover:underline hover:underline-offset-4'\n}\n\n/**\n * Underline-классы для видимой метки в `view=\"link\"` + `optionsView=\"native\"`.\n * Видимая метка имеет `pointer-events-none`, а hover получает прозрачный\n * `<select>`-overlay (peer), поэтому для `underline=\"auto\"` используем\n * `peer-hover:underline` вместо `hover:underline`.\n */\nfunction selectLinkNativeLabelUnderlineClass(options: { underline: GrSelectUnderline, disabled: boolean }): string {\n if (options.disabled) return 'no-underline'\n if (options.underline === 'always') return 'underline underline-offset-4'\n if (options.underline === 'none') return 'no-underline'\n return 'no-underline peer-hover:underline peer-hover:underline-offset-4'\n}\n\n/**\n * Классы прозрачного `<select>`-overlay для `view=\"link\"` + `optionsView=\"native\"`.\n * Нативный селект растягивается на всю обёртку, но визуально невидим — ширину обёртки\n * задаёт видимый `<span>` с выбранной меткой, благодаря чему компонент в закрытом\n * состоянии занимает столько места, сколько нужно текущей опции (а не самой длинной).\n */\nexport const grSelectLinkNativeOverlayClass = 'peer absolute inset-0 w-full h-full m-0 p-0 border-0 bg-transparent text-transparent appearance-none opacity-0 cursor-pointer disabled:cursor-not-allowed focus:outline-none'\n\n/**\n * Классы видимой метки для `view=\"link\"` + `optionsView=\"native\"` (поверх прозрачного `<select>`).\n * Получает все link-стили (size/variant/underline), а также focus-ring через `peer-focus-visible`.\n */\nexport const grSelectLinkNativeLabelBaseClass = 'pointer-events-none inline-block whitespace-nowrap align-baseline rounded-[var(--gr-radius-control)] transition-colors duration-[var(--gr-duration-fast)]'\nexport const grSelectLinkNativeLabelDisabledClass = 'cursor-not-allowed text-[var(--gr-muted-fg)] no-underline'\nexport const grSelectLinkNativeLabelFocusClass = 'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--gr-ring)]'\n\nexport function grSelectLinkNativeLabelClass(options: {\n size: GrSelectSize\n variant: GrSelectVariant\n underline: GrSelectUnderline\n disabled: boolean\n}): string {\n return [\n grSelectLinkNativeLabelBaseClass,\n selectLinkSizeClassBySize[options.size],\n selectLinkNativeLabelUnderlineClass({ underline: options.underline, disabled: options.disabled }),\n selectLinkNativeLabelVariantClassByVariant[options.variant],\n options.disabled ? grSelectLinkNativeLabelDisabledClass : '',\n grSelectLinkNativeLabelFocusClass,\n ]\n .filter(Boolean)\n .join(' ')\n}\n\nexport function grSelectClass(options: {\n view: GrSelectView\n size: GrSelectSize\n disabled: boolean\n variant: GrSelectVariant\n underline: GrSelectUnderline\n state?: GrSelectState\n invalid?: boolean\n}): string {\n if (options.view === 'link') {\n return [\n selectLinkSizeClassBySize[options.size],\n selectLinkUnderlineClass({ underline: options.underline, disabled: options.disabled }),\n selectLinkVariantClassByVariant[options.variant],\n 'disabled:cursor-not-allowed disabled:text-[var(--gr-muted-fg)] disabled:no-underline',\n ].join(' ')\n }\n\n return [\n selectSizeClassBySize[options.size],\n // `invalid` сильнее `state`: ошибка перекрывает любую другую подсветку.\n options.invalid ? invalidBorderClass : borderClassByState[options.state ?? 'default'],\n // Заблокированный контрол гасится фоном и цветом текста, а не `opacity`:\n // прозрачность разбавляет выверенные на AA токены и роняет контраст.\n 'disabled:cursor-not-allowed disabled:bg-[var(--gr-muted)] disabled:text-[var(--gr-muted-fg)]',\n ].join(' ')\n}\n\nexport function grSelectNativeClass(options: {\n view: GrSelectView\n size: GrSelectSize\n disabled: boolean\n variant: GrSelectVariant\n underline: GrSelectUnderline\n showNativeChevron: boolean\n state?: GrSelectState\n invalid?: boolean\n}): string {\n return [\n grSelectClass(options),\n options.showNativeChevron ? 'appearance-none pr-9' : '',\n ]\n .filter(Boolean)\n .join(' ')\n}\n\nexport function grSelectTriggerClass(options: {\n view: GrSelectView\n optionsView: GrSelectOptionsView\n size: GrSelectSize\n disabled: boolean\n variant: GrSelectVariant\n underline: GrSelectUnderline\n state?: GrSelectState\n invalid?: boolean\n}): string {\n if (options.optionsView !== 'panel') {\n return grSelectClass(options)\n }\n\n if (options.view === 'link') {\n return [\n grSelectClass(options),\n 'inline-flex items-center gap-1 text-left',\n ].join(' ')\n }\n\n return [\n grSelectClass(options),\n 'flex items-center justify-between text-left',\n ].join(' ')\n}\n\nexport const grSelectPanelClasses = 'rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-card)] text-[var(--gr-card-fg)] shadow-[var(--gr-shadow-2)] overflow-hidden'\n\n/** Общая подсветка наведения и активной опции панели. */\nconst selectOptionHighlight = 'bg-[color-mix(in_srgb,var(--gr-muted)_30%,transparent)]'\n\nexport const selectOptionBaseClass = 'rounded-[var(--gr-radius-md)] px-3 py-2 text-left text-[length:var(--gr-text-sm)]'\nexport const selectOptionLinkWidthClass = 'block min-w-full w-max whitespace-nowrap'\nexport const selectOptionWidthClass = 'w-full'\nexport const selectOptionEnabledClass = `hover:${selectOptionHighlight}`\nexport const selectOptionActiveClass = selectOptionHighlight\n/** Выключенная опция гасится токеном текста — по той же причине, что и контрол. */\nexport const selectOptionDisabledClass = 'cursor-not-allowed text-[var(--gr-muted-fg)]'\n\nexport function grSelectOptionClass(options: {\n view: GrSelectView\n disabled: boolean\n active: boolean\n}): string {\n return [\n selectOptionBaseClass,\n options.view === 'link' ? selectOptionLinkWidthClass : selectOptionWidthClass,\n options.disabled ? selectOptionDisabledClass : selectOptionEnabledClass,\n !options.disabled && options.active ? selectOptionActiveClass : '',\n ]\n .filter(Boolean)\n .join(' ')\n}\n"],"mappings":";AAoCA,IAAa,mBAAmB;;;;;AAMhC,IAAa,qBAAoD;CAC/D,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;AACV;;;;;;AAOA,IAAa,qBAAqB;AAClC,IAAa,gBAAgB;AAG7B,IAAa,wBAAsD;CACjE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,4BAA0D;CACrE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;;;;;;;;;;AAYA,IAAa,kCAAmE;CAC9E,SAAS;CACT,SAAS;CACT,OAAO;CACP,QAAQ;AACV;;;;;;AAOA,IAAa,6CAA8E;CACzF,SAAS;CACT,SAAS;CACT,OAAO;CACP,QAAQ;AACV;AAEA,SAAS,yBAAyB,SAAsE;CACtG,IAAI,QAAQ,UAAU,OAAO;CAC7B,IAAI,QAAQ,cAAc,UAAU,OAAO;CAC3C,IAAI,QAAQ,cAAc,QAAQ,OAAO;CACzC,OAAO;AACT;;;;;;;AAQA,SAAS,oCAAoC,SAAsE;CACjH,IAAI,QAAQ,UAAU,OAAO;CAC7B,IAAI,QAAQ,cAAc,UAAU,OAAO;CAC3C,IAAI,QAAQ,cAAc,QAAQ,OAAO;CACzC,OAAO;AACT;;;;;;;AAQA,IAAa,iCAAiC;;;;;AAM9C,IAAa,mCAAmC;AAChD,IAAa,uCAAuC;AACpD,IAAa,oCAAoC;AAEjD,SAAgB,6BAA6B,SAKlC;CACT,OAAO;EACL;EACA,0BAA0B,QAAQ;EAClC,oCAAoC;GAAE,WAAW,QAAQ;GAAW,UAAU,QAAQ;EAAS,CAAC;EAChG,2CAA2C,QAAQ;EACnD,QAAQ,WAAW,uCAAuC;EAC1D;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb;AAEA,SAAgB,cAAc,SAQnB;CACT,IAAI,QAAQ,SAAS,QACnB,OAAO;EACL,0BAA0B,QAAQ;EAClC,yBAAyB;GAAE,WAAW,QAAQ;GAAW,UAAU,QAAQ;EAAS,CAAC;EACrF,gCAAgC,QAAQ;EACxC;CACF,CAAC,CAAC,KAAK,GAAG;CAGZ,OAAO;EACL,sBAAsB,QAAQ;EAE9B,QAAQ,UAAU,qBAAqB,mBAAmB,QAAQ,SAAS;EAG3E;CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,SAAgB,oBAAoB,SASzB;CACT,OAAO,CACL,cAAc,OAAO,GACrB,QAAQ,oBAAoB,yBAAyB,EACvD,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb;AAEA,SAAgB,qBAAqB,SAS1B;CACT,IAAI,QAAQ,gBAAgB,SAC1B,OAAO,cAAc,OAAO;CAG9B,IAAI,QAAQ,SAAS,QACnB,OAAO,CACL,cAAc,OAAO,GACrB,0CACF,CAAC,CAAC,KAAK,GAAG;CAGZ,OAAO,CACL,cAAc,OAAO,GACrB,6CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAa,uBAAuB;;AAGpC,IAAM,wBAAwB;AAE9B,IAAa,wBAAwB;AACrC,IAAa,6BAA6B;AAC1C,IAAa,yBAAyB;AACtC,IAAa,2BAA2B,SAAS;AACjD,IAAa,0BAA0B;;AAEvC,IAAa,4BAA4B;AAEzC,SAAgB,oBAAoB,SAIzB;CACT,OAAO;EACL;EACA,QAAQ,SAAS,SAAS,6BAA6B;EACvD,QAAQ,WAAW,4BAA4B;EAC/C,CAAC,QAAQ,YAAY,QAAQ,SAAS,0BAA0B;CAClE,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"grTextareaStyles-iOcHuVMM.js","names":[],"sources":["../../src/components/GrTextarea/grTextareaStyles.ts"],"sourcesContent":["import type { GrComponentSize, GrControlState } from '../shared/sizes'\n\nexport type GrTextareaState = GrControlState\nexport type GrTextareaSize = GrComponentSize\n\nexport const borderClassByState: Record<GrTextareaState, string> = {\n default: 'border-[var(--gr-brd)]',\n success: 'border-[var(--gr-success)] focus-visible:ring-[var(--gr-success)]',\n warning: 'border-[var(--gr-warning)] focus-visible:ring-[var(--gr-warning)]',\n danger: 'border-[var(--gr-danger)] focus-visible:ring-[var(--gr-danger)]',\n}\n\n/**\n * Ошибка валидации красится своей ролью, а не декоративным тоном `danger`:\n * `state=\"danger\"` — это подсветка по решению разработчика, `invalid` — вердикт\n * валидации, и тема вправе развести их по цвету.\n */\nexport const invalidBorderClass = 'border-[var(--gr-invalid-brd)] focus-visible:ring-[var(--gr-invalid-ring)]'\n\n/**\n * Кегль совпадает с `GrInput` на каждой ступени: поле ввода и текстовая область\n * в одной форме обязаны читаться одинаково. Вертикальный паддинг меньше, чем у\n * `GrInput`: там он центрирует одну строку в фиксированной высоте, здесь высоту\n * задаёт `rows`.\n */\nexport const sizes: Record<GrTextareaSize, string> = {\n xs: 'px-2.5 py-1 text-[length:var(--gr-control-text-xs)]',\n sm: 'px-3 py-1.5 text-[length:var(--gr-control-text-sm)]',\n md: 'px-3 py-2 text-[length:var(--gr-control-text-md)]',\n lg: 'px-4 py-2.5 text-[length:var(--gr-control-text-lg)]',\n}\n\nexport type GrTextareaResize = 'none' | 'vertical' | 'both'\n\nexport const resizeClass: Record<GrTextareaResize, string> = {\n none: 'resize-none',\n vertical: 'resize-y',\n both: 'resize',\n}\n\nexport const enabledSurfaceClass = 'bg-[var(--gr-bg)]'\n\n/**\n * Заблокированное поле гасится фоном, а не `opacity`: прозрачность разбавляет\n * выверенные на AA токены текста. Классы взаимоисключающие — два `bg-*` одной\n * специфичности разрулил бы порядок в сгенерированном CSS.\n */\nexport const disabledSurfaceClass = 'bg-[var(--gr-muted)] text-[var(--gr-muted-fg)] cursor-not-allowed'\n\n/**\n * Ряд счётчиков под полем: слева строки, справа символы. Обе подписи — один\n * визуальный уровень, поэтому кегль и цвет у них общие (`countClass`).\n */\nexport const countRowClass = 'mt-1 flex items-baseline justify-between gap-3'\n\n/** Счётчик строк. Слева, потому что читается раньше: сначала «сколько строк», потом лимит символов. */\nexport const lineCountClass = 'text-[length:var(--gr-control-text-xs)] leading-[var(--gr-leading-xs)] text-[var(--gr-muted-fg)] tabular-nums'\n\nexport const countClass = 'mt-1 text-right text-[length:var(--gr-control-text-xs)] leading-[var(--gr-leading-xs)] text-[var(--gr-muted-fg)] tabular-nums'\n\nexport function grTextareaClass(options: { state: GrTextareaState, invalid: boolean }): string {\n return options.invalid ? invalidBorderClass : borderClassByState[options.state]\n}\n"],"mappings":";AAKA,IAAa,qBAAsD;CACjE,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;AACV;;;;;;AAOA,IAAa,qBAAqB;;;;;;;AAQlC,IAAa,QAAwC;CACnD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAIA,IAAa,cAAgD;CAC3D,MAAM;CACN,UAAU;CACV,MAAM;AACR;AAEA,IAAa,sBAAsB;;;;;;AAOnC,IAAa,uBAAuB;;;;;AAMpC,IAAa,gBAAgB;;AAG7B,IAAa,iBAAiB;AAE9B,IAAa,aAAa;AAE1B,SAAgB,gBAAgB,SAA+D;CAC7F,OAAO,QAAQ,UAAU,qBAAqB,mBAAmB,QAAQ;AAC3E"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useMenuItemsFocus-OyOnIAlr.js","names":[],"sources":["../../src/composables/internal/useTypeahead.ts","../../src/composables/internal/useMenuItemsFocus.ts"],"sourcesContent":["import { getCurrentInstance, onBeforeUnmount } from 'vue'\n\n/**\n * Буфер живёт ровно столько: дальше набранное перестаёт быть одним словом.\n * Значение из WAI-ARIA APG, оно же стояло во всех трёх прежних копиях.\n */\nexport const TYPEAHEAD_RESET_MS = 600\n\nexport interface UseTypeaheadOptions<TItem> {\n /** Кандидаты в порядке отображения. Опрашивается на каждый символ. */\n items: () => readonly TItem[]\n /** Текст кандидата. Нормализацию (`trim` + нижний регистр) делает композабл. */\n textOf: (item: TItem, index: number) => string\n /** Откуда шагать. Получает уже разрешённый набор, чтобы не опрашивать его дважды. */\n currentIndex: (items: readonly TItem[]) => number\n /** Совпадение найдено. Фокус, `activeIndex` — решает потребитель. */\n onMatch: (item: TItem, index: number) => void\n resetMs?: number\n}\n\nexport interface UseTypeaheadReturn {\n /** Обработать печатный символ. `true` — совпадение найдено. */\n type: (char: string) => boolean\n /** Пуст ли буфер: правило «`Space` при пустом буфере активирует пункт». */\n isEmpty: () => boolean\n reset: () => void\n}\n\n/**\n * Поиск по первым буквам в списке — паттерн `typeahead` из WAI-ARIA APG.\n *\n * Буфер намеренно не реактивный: на рендер он не влияет, а читается синхронно\n * внутри обработчика `keydown`.\n */\nexport function useTypeahead<TItem>(options: UseTypeaheadOptions<TItem>): UseTypeaheadReturn {\n const resetMs = options.resetMs ?? TYPEAHEAD_RESET_MS\n\n let buffer = ''\n let timer: ReturnType<typeof setTimeout> | undefined\n\n function reset(): void {\n clearTimeout(timer)\n timer = undefined\n buffer = ''\n }\n\n function type(char: string): boolean {\n clearTimeout(timer)\n timer = setTimeout(reset, resetMs)\n\n // Повтор одной буквы — это «следующий на ту же букву», а не поиск «аа».\n // Сравнение по нормализованному символу: иначе `Shift+A` и `a` считались бы\n // разными буквами, и повтор с зажатым шифтом уходил бы в поиск «Aa».\n const normalized = char.toLowerCase()\n const repeat = buffer.length === 1 && buffer === normalized\n buffer = repeat ? normalized : buffer + normalized\n\n const items = options.items()\n if (items.length === 0)\n return false\n\n const from = options.currentIndex(items)\n\n for (let step = 1; step <= items.length; step += 1) {\n const index = (from + step + items.length) % items.length\n const item = items[index]\n if (item === undefined)\n continue\n\n if (options.textOf(item, index).trim().toLowerCase().startsWith(buffer)) {\n options.onMatch(item, index)\n return true\n }\n }\n\n return false\n }\n\n // Композабл вызывается и вне компонента (тесты, чистые модули навигации),\n // поэтому уборку вешаем только когда есть на что.\n if (getCurrentInstance())\n onBeforeUnmount(reset)\n\n return {\n type,\n isEmpty: () => buffer === '',\n reset,\n }\n}\n","import { isEditableTarget } from '../../internal/keyboard'\n\nimport { getFocusableElements } from './focusables'\nimport { useTypeahead } from './useTypeahead'\n\nexport interface UseMenuItemsFocusOptions {\n /** Корень, внутри которого живут пункты. */\n container: () => HTMLElement | null\n /** Закрыть меню — `Tab` уводит фокус наружу, панель при этом закрывается. */\n close: () => void\n}\n\nexport interface UseMenuItemsFocusReturn {\n /** Пункты в порядке документа. */\n items: () => HTMLElement[]\n /** Сфокусировать по индексу; отрицательный и превышающий заворачиваются. */\n focusAt: (index: number) => void\n /** Стрелки, `Home`/`End`, `Tab`, typeahead. `true` — клавиша обработана. */\n onKeydown: (event: KeyboardEvent) => boolean\n reset: () => void\n}\n\n/**\n * Клавиатура панели-меню: кольцо фокуса плюс поиск по первым буквам.\n *\n * Пункты ищутся в DOM, а не в модели, и это осознанно: у панели бывает\n * произвольное содержимое из слота, в котором ключей нет вовсе. По той же\n * причине здесь не `useRovingFocus` — он адресуется ключами и ведёт\n * `tabindex=\"0\"` на активном элементе, а пункты меню обязаны быть `-1`.\n */\nexport function useMenuItemsFocus(options: UseMenuItemsFocusOptions): UseMenuItemsFocusReturn {\n /**\n * `tabbableOnly: false` — не оплошность: пункты меню намеренно `tabindex=\"-1\"`,\n * и список участников таб-порядка был бы пуст.\n */\n function items(): HTMLElement[] {\n return getFocusableElements(options.container(), { tabbableOnly: false })\n }\n\n function focusAt(index: number): void {\n const list = items()\n if (list.length === 0)\n return\n\n // `preventScroll` обязателен: панель уже приведена во вьюпорт позиционером,\n // поэтому прокрутка ради фокуса внутри неё паразитная. У контекстного меню\n // она к тому же порождала `scroll`, по которому меню закрывало само себя.\n list[(index + list.length) % list.length]?.focus({ preventScroll: true })\n }\n\n const typeahead = useTypeahead<HTMLElement>({\n items,\n textOf: element => element.textContent ?? '',\n currentIndex: list => list.indexOf(document.activeElement as HTMLElement),\n onMatch: element => element.focus({ preventScroll: true }),\n })\n\n function onKeydown(event: KeyboardEvent): boolean {\n const list = items()\n const currentIndex = list.indexOf(document.activeElement as HTMLElement)\n\n // Панель бывает не только меню из кнопок: с `closeOnContentClick={false}` в\n // ней живут поля и чекбоксы. Клавиши, которые сфокусированный контрол умеет\n // сам, остаются ему — иначе в поле не напечатать, а чекбокс не переключить.\n const editable = isEditableTarget(event.target)\n\n switch (event.key) {\n // Стрелки — исключение: они за меню даже в поле. `Tab` панель закрывает, и\n // без них из поля внутри панели не было бы выхода вовсе.\n case 'ArrowDown':\n event.preventDefault()\n focusAt(currentIndex + 1)\n return true\n case 'ArrowUp':\n event.preventDefault()\n focusAt(currentIndex - 1)\n return true\n case 'Home':\n if (editable)\n return false\n event.preventDefault()\n focusAt(0)\n return true\n case 'End':\n if (editable)\n return false\n event.preventDefault()\n focusAt(-1)\n return true\n case 'Tab':\n options.close()\n return true\n }\n\n if (editable)\n return false\n\n // Пробел при пустом буфере — активация сфокусированного пункта, а не поиск:\n // пункты меню это кнопки, и пробел для них родная клавиша. В буфер он входит,\n // только когда поиск уже идёт (правило typeahead из WAI-ARIA APG).\n if (event.key === ' ' && typeahead.isEmpty())\n return false\n\n // Печатный символ без модификаторов — поиск по пунктам, а не команда.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n event.preventDefault()\n typeahead.type(event.key)\n return true\n }\n\n return false\n }\n\n return { items, focusAt, onKeydown, reset: typeahead.reset }\n}\n"],"mappings":";;;;;;;;;AAkCA,SAAgB,aAAoB,SAAyD;CAC3F,MAAM,UAAU,QAAQ,WAAA;CAExB,IAAI,SAAS;CACb,IAAI;CAEJ,SAAS,QAAc;EACrB,aAAa,KAAK;EAClB,QAAQ,KAAA;EACR,SAAS;CACX;CAEA,SAAS,KAAK,MAAuB;EACnC,aAAa,KAAK;EAClB,QAAQ,WAAW,OAAO,OAAO;EAKjC,MAAM,aAAa,KAAK,YAAY;EAEpC,SADe,OAAO,WAAW,KAAK,WAAW,aAC/B,aAAa,SAAS;EAExC,MAAM,QAAQ,QAAQ,MAAM;EAC5B,IAAI,MAAM,WAAW,GACnB,OAAO;EAET,MAAM,OAAO,QAAQ,aAAa,KAAK;EAEvC,KAAK,IAAI,OAAO,GAAG,QAAQ,MAAM,QAAQ,QAAQ,GAAG;GAClD,MAAM,SAAS,OAAO,OAAO,MAAM,UAAU,MAAM;GACnD,MAAM,OAAO,MAAM;GACnB,IAAI,SAAS,KAAA,GACX;GAEF,IAAI,QAAQ,OAAO,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,MAAM,GAAG;IACvE,QAAQ,QAAQ,MAAM,KAAK;IAC3B,OAAO;GACT;EACF;EAEA,OAAO;CACT;CAIA,IAAI,mBAAmB,GACrB,gBAAgB,KAAK;CAEvB,OAAO;EACL;EACA,eAAe,WAAW;EAC1B;CACF;AACF;;;;;;;;;;;AC1DA,SAAgB,kBAAkB,SAA4D;;;;;CAK5F,SAAS,QAAuB;EAC9B,OAAO,qBAAqB,QAAQ,UAAU,GAAG,EAAE,cAAc,MAAM,CAAC;CAC1E;CAEA,SAAS,QAAQ,OAAqB;EACpC,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,WAAW,GAClB;EAKF,MAAM,QAAQ,KAAK,UAAU,KAAK,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,CAAC;CAC1E;CAEA,MAAM,YAAY,aAA0B;EAC1C;EACA,SAAQ,YAAW,QAAQ,eAAe;EAC1C,eAAc,SAAQ,KAAK,QAAQ,SAAS,aAA4B;EACxE,UAAS,YAAW,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CAC3D,CAAC;CAED,SAAS,UAAU,OAA+B;EAEhD,MAAM,eADO,MACQ,CAAA,CAAK,QAAQ,SAAS,aAA4B;EAKvE,MAAM,WAAW,iBAAiB,MAAM,MAAM;EAE9C,QAAQ,MAAM,KAAd;GAGE,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,CAAC;IACT,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,EAAE;IACV,OAAO;GACT,KAAK;IACH,QAAQ,MAAM;IACd,OAAO;EACX;EAEA,IAAI,UACF,OAAO;EAKT,IAAI,MAAM,QAAQ,OAAO,UAAU,QAAQ,GACzC,OAAO;EAGT,IAAI,MAAM,IAAI,WAAW,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,QAAQ;GAC/E,MAAM,eAAe;GACrB,UAAU,KAAK,MAAM,GAAG;GACxB,OAAO;EACT;EAEA,OAAO;CACT;CAEA,OAAO;EAAE;EAAO;EAAS;EAAW,OAAO,UAAU;CAAM;AAC7D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrAutocomplete-D9c9BNFg.js","names":[],"sources":["../../../../src/components/GrAutocomplete/GrAutocomplete.vue","../../../../src/components/GrAutocomplete/GrAutocomplete.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"TValue extends GrAutocompleteValue = string\">\nimport { computed, nextTick, onBeforeUnmount, ref, shallowRef, useId, watch } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { useGrComponentProp, useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\n\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { isComposingEvent } from '../../internal/keyboard'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useComboboxNavigation } from '../../composables/useComboboxNavigation'\nimport { filterOptions, resolveSelectedOptions } from '../shared/optionFilter'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge/grBadgeStyles'\n\nimport {\n autocompleteOptionClass,\n autocompletePanelClasses,\n autocompleteShellClass,\n autocompleteStateClass,\n type GrAutocompleteModelValue,\n type GrAutocompleteOption,\n type GrAutocompleteValue,\n type GrAutocompleteSize,\n} from './grAutocompleteStyles'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconLoaderCircle from '~icons/lucide/loader-circle'\nimport IconX from '~icons/lucide/x'\n\nexport type {\n GrAutocompleteModelValue,\n GrAutocompleteOption,\n GrAutocompleteSize,\n GrAutocompleteValue,\n} from './grAutocompleteStyles'\n\n/**\n * Публичный GR-примитив «Autocomplete / Combobox» (WAI-ARIA editable combobox).\n *\n * В отличие от `GrSelect` (select-only combobox с кнопкой-триггером), здесь\n * combobox-ом является сам текстовый `<input>`: набранный текст — это поисковый\n * запрос, а выбор опции заполняет поле. Поддерживает локальную фильтрацию,\n * удалённую (async) загрузку через событие `search` + внешний проп `loading`,\n * произвольные значения (`allowCustomValue`) и multiple с удаляемыми chips.\n */\nexport interface GrAutocompleteProps<TValue extends GrAutocompleteValue = string> {\n /** Выбранное значение (single — строка, multiple — массив строк). */\n modelValue: GrAutocompleteModelValue<TValue>\n /**\n * Доступные опции. Для локального режима — полный список (фильтруется на клиенте).\n * Для remote-режима (`filterable=false`) — список, который родитель обновляет\n * в ответ на событие `search`.\n */\n options?: GrAutocompleteOption<TValue>[]\n multiple?: boolean\n /**\n * Вид чипов выбранных значений в режиме `multiple`. Чип — это `GrBadge`, а не\n * своя плашка: на светлой теме собственная заливка чипа (примесь `--gr-muted`\n * без рамки) давала 1.02:1 к фону поля, то есть была невидима.\n */\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n disabled?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrAutocompleteSize\n placeholder?: string\n ariaLabel?: string\n /** Кнопка очистки выбранного значения/запроса. */\n clearable?: boolean\n /** Внешне управляемое состояние загрузки (для async-сценариев). */\n loading?: boolean\n /**\n * Локальная фильтрация опций по введённому запросу. Отключите (`false`) для\n * чисто удалённого поиска — тогда `options` показываются как есть, а фильтрацию\n * выполняет сервер по событию `search`.\n */\n filterable?: boolean\n /** Кастомный матчер локальной фильтрации. По умолчанию — подстрока в `label`/`value`. */\n filter?: (option: GrAutocompleteOption<TValue>, query: string) => boolean\n /**\n * Удалённая загрузка опций под управлением компонента: дебаунс, отмена\n * устаревшего запроса и `loading` берёт на себя он. Ответ на отменённый\n * запрос игнорируется — при быстром вводе в списке всегда результат\n * последнего запроса, а не того, который вернулся позже.\n *\n * `signal` пробрасывается в `fetch`. Локальная фильтрация в этом режиме\n * выключена: список фильтрует сервер. Альтернатива — событие `search`, если\n * запрос ведёт само приложение.\n */\n fetchOptions?: (query: string, signal: AbortSignal) => Promise<GrAutocompleteOption<TValue>[]>\n /** Минимальная длина запроса до эмита `search` (для дебаунса remote-загрузки). */\n minQueryLength?: number\n /** Задержка дебаунса события `search`, мс. */\n debounce?: number\n /** Разрешить ввод/коммит значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Закрывать панель после выбора (single всегда закрывает). */\n closeOnSelect?: boolean\n /** Максимальная высота панели, px. */\n dropdownMaxHeight?: number\n /**\n * Виртуализация панели: в DOM живёт только окно вокруг вьюпорта.\n *\n * Высоту окна задаёт `dropdownMaxHeight`. Включается осознанно: на сотне\n * опций выигрыша нет, а в разметке остаётся только окно — вместе с ним\n * меняется и то, что находит `querySelector` потребителя. Профильный\n * сценарий — удалённый поиск по справочнику на тысячи позиций.\n */\n virtual?: boolean\n /** i18n-тексты состояний панели / aria. */\n loadingText?: string\n noResultsText?: string\n clearLabel?: string\n /**\n * Контролируемое состояние панели (`v-model:open`). Без пропа панель ведёт\n * себя сама (uncontrolled), с ним — слушайте `update:open` и меняйте проп.\n */\n open?: boolean\n /** Имя для нативной формы: hidden input по значению модели (не по тексту запроса). */\n name?: string\n /**\n * Ширины аддонов `prefix`/`suffix` — общий контракт контролов пакета\n * (`docs/form-controls.md`).\n */\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface GrAutocompleteEmits<TValue extends GrAutocompleteValue = string> {\n (e: 'update:modelValue', value: GrAutocompleteModelValue<TValue>): void\n /** Дебаунснутый поисковый запрос — точка входа для удалённой загрузки опций. */\n (e: 'search', query: string): void\n /** Запрос `fetchOptions` завершился ошибкой (отмена устаревшего сюда не приходит). */\n (e: 'searchError', error: unknown): void\n /** Значение зафиксировано выбором или снятием опции. */\n (e: 'change', value: GrAutocompleteModelValue<TValue>): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n /** Значение снято кнопкой очистки; только при `clearable`. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrAutocompleteProps<TValue>>(),\n {\n options: undefined,\n multiple: false,\n tagTone: 'neutral',\n tagDark: false,\n // `sm`, а не `md` как у `GrInputTag`: чип живёт внутри контрола, а не в тексте.\n tagSize: 'sm',\n tagRadius: 'round',\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n placeholder: undefined,\n ariaLabel: undefined,\n clearable: undefined,\n loading: false,\n filterable: true,\n filter: undefined,\n fetchOptions: undefined,\n minQueryLength: 0,\n debounce: 250,\n allowCustomValue: false,\n closeOnSelect: true,\n dropdownMaxHeight: 280,\n virtual: false,\n loadingText: undefined,\n noResultsText: undefined,\n clearLabel: undefined,\n open: undefined,\n name: undefined,\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\n/** Ширина аддона по умолчанию — та же лестница, что у `GrInput`. */\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrAutocompleteSize, string> = {\n xs: '2rem',\n sm: '2.25rem',\n md: '2.5rem',\n lg: '3rem',\n}\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrAutocomplete' })\nconst resolvedClearable = useGrComponentProp('GrAutocomplete', 'clearable', () => props.clearable, false)\n\n/**\n * Аддоны живут флекс-соседями поля, а не поверх него: оболочка автокомплита —\n * флекс-строка с чипами, поэтому резервировать место паддингом не нужно.\n * Контракт ширин остаётся общим (`useControlAddons`).\n */\nconst { hasPrefix, hasSuffix, prefixEl, suffixEl, prefixStyle, suffixStyle } = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => '0px',\n})\n\nconst emit = defineEmits<GrAutocompleteEmits<TValue>>()\ndefineSlots<{\n /** Аддон слева от поля ввода. */\n prefix?: () => any\n /** Аддон справа, перед спиннером и крестиком. */\n suffix?: () => any\n /** Строка списка вместо подписи опции. */\n option?: (props: { option: GrAutocompleteOption<TValue>, selected: boolean }) => any\n /** Содержимое панели, пока едут опции. */\n loading?: () => any\n /** Содержимое панели, когда подходящих опций нет. */\n empty?: () => any\n}>()\n\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.autocomplete.loading', 'Loading…'))\nconst resolvedNoResultsText = computed(() => props.noResultsText ?? t('gr.autocomplete.noResults', 'No results'))\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.common.clear', 'Clear'))\n// Число уходит под двумя общепринятыми именами: `n` читают `@feugene/fint-i18n`\n// и `vue-i18n`, `count` — `i18next`. Формы для каждого языка лежат в словаре,\n// выбирает их переводчик; встроенный fallback — одна английская строка.\nconst resolvedTypeMoreText = computed(() =>\n t('gr.autocomplete.typeMore', 'Type at least {n} characters', {\n n: props.minQueryLength,\n count: props.minQueryLength,\n }),\n)\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\n// Read-only запирает контрол так же, как disabled: значение видно, но панель не\n// открывается, опции не выбираются и чипы не удаляются. Без этого `readonly`\n// оставался бы ARIA-атрибутом без поведения.\nconst locked = computed(() => isDisabled.value || isReadonly.value)\n\n// Ответ последнего `fetchOptions`. До первого ответа показываем `options` —\n// с ними компонент рисует стартовый список, не дожидаясь сервера.\nconst remoteOptions = shallowRef<GrAutocompleteOption<TValue>[]>([])\nconst remoteAnswered = ref(false)\nconst remoteLoading = ref(false)\n\nconst optionsResolved = computed<GrAutocompleteOption<TValue>[]>(() =>\n props.fetchOptions && remoteAnswered.value ? remoteOptions.value : (props.options ?? []),\n)\n\n/**\n * Подпись состава стартового списка. Сравнивать идентичность массива нельзя:\n * инлайн-литерал `:options=\"[...]\"` пересоздаётся каждым ререндером родителя —\n * в том числе тем, который вызвал сам компонент своим `update:modelValue`, —\n * и remote-результаты исчезали бы прямо посреди выбора.\n */\nfunction optionsSignature(options: GrAutocompleteOption<TValue>[] | undefined): string {\n return (options ?? []).map(o => `${String(o.value)}\\u0000${o.label}`).join('\\u0001')\n}\n\n// Родитель сменил стартовый список — он снова источник до следующего ответа\n// сервера, а летящий запрос относится к прежнему набору данных и отменяется.\nwatch(() => optionsSignature(props.options), () => {\n if (!props.fetchOptions) return\n cancelSearch()\n remoteAnswered.value = false\n remoteOptions.value = []\n})\n\nconst isLoading = computed(() => props.loading || remoteLoading.value)\n\n/** `0` — валидное значение, поэтому «пусто» проверяется явно, а не через falsy. */\nfunction isEmptyValue(value: unknown): boolean {\n return value === undefined || value === null || value === ''\n}\n\nfunction toArray(value: GrAutocompleteModelValue<TValue>): TValue[] {\n if (Array.isArray(value)) return value\n if (isEmptyValue(value)) return []\n return [value]\n}\n\nconst modelSingle = computed<TValue | ''>(() => {\n const raw = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue\n return isEmptyValue(raw) ? '' : (raw as TValue)\n})\nconst selectedValues = computed(() => (props.multiple ? toArray(props.modelValue) : (isEmptyValue(modelSingle.value) ? [] : [modelSingle.value as TValue])))\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nfunction labelFor(value: GrAutocompleteValue): string {\n return optionsResolved.value.find(o => o.value === value)?.label ?? String(value)\n}\n\n/** Опции выбранных значений (для chips в multiple). Неизвестные значения показываем как есть. */\nconst selectedOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n resolveSelectedOptions(selectedValues.value, optionsResolved.value),\n)\n\nconst singleSelectedLabel = computed(() => (props.multiple || isEmptyValue(modelSingle.value) ? '' : labelFor(modelSingle.value)))\n\n// ————— Состояние.\nconst query = ref('')\n// `dirty` — пользователь начал вводить запрос. Нужно, чтобы в single-режиме при\n// открытии заполненного поля показать весь список (а не отфильтровать по метке\n// уже выбранной опции). Сбрасывается при программной установке `query`.\nconst dirty = ref(false)\n// Имя `open` сохранено: читатели работают с computed-Ref.\nconst { open, 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 listboxEl = ref<HTMLElement | null>(null)\n\n// Панель телепортирована в `body`, то есть лежит вне корня, но для пользователя\n// она часть контрола: без неё уход фокуса в панель читался бы как `blur`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n containers: () => [panelEl.value],\n})\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\ndefineExpose({\n focus,\n blur,\n open: () => openDropdown(),\n close: () => closeDropdown(),\n})\nconst clickOutsideExclude = [() => panelEl.value]\n\nconst { floatingStyle } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: true,\n zIndexVar: '--gr-z-dropdown',\n})\n\nuseDismissible(open, closeDropdown)\n\n// ————— Фильтрация.\nconst searchQuery = computed(() => {\n // При `fetchOptions` фильтрует сервер: локальный матчер отсеял бы то, что он\n // уже прислал в ответ на этот же запрос.\n if (!props.filterable || props.fetchOptions) return ''\n // single: пока пользователь не начал вводить — показываем весь список.\n if (!props.multiple && !dirty.value) return ''\n return query.value.trim()\n})\n\nconst filteredOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n filterOptions(optionsResolved.value, searchQuery.value, props.filter),\n)\n\nconst belowMinQuery = computed(() => props.minQueryLength > 0 && query.value.trim().length < props.minQueryLength)\n\n/**\n * Опции к показу и навигации. Ниже `minQueryLength` — пусто: список ещё\n * относится к прошлому запросу, показывать его под подсказкой «введите ещё N»\n * значит дезинформировать.\n */\nconst effectiveOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n belowMinQuery.value ? [] : filteredOptions.value,\n)\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n // Кастомное значение набирается текстом — оно строковое по природе;\n // при числовом `TValue` эта ветка неприменима (см. docs/components.md).\n const v = query.value.trim() as TValue\n if (!v) return false\n if (props.multiple && selectedValues.value.includes(v)) return false\n if (!props.multiple && v === modelSingle.value) return false\n // Не предлагаем «Add», если такое значение/метка уже есть среди опций.\n return !optionsResolved.value.some(o => o.value === v || o.label === v)\n})\n\nconst showEmpty = computed(() =>\n !isLoading.value && effectiveOptions.value.length === 0 && !canAddCustom.value,\n)\n\n// ————— Панель: id/aria-activedescendant.\nconst listboxId = useId()\n\n/**\n * Id опции строится от позиции, а не от значения: значение с пробелом дало бы\n * невалидный `id`, а `aria-activedescendant` — два токена вместо одной ссылки,\n * и активная опция перестала бы объявляться.\n */\nfunction optionDomId(index: number): string {\n return `${listboxId}-opt-${index}`\n}\nconst addOptionDomId = computed(() => `${listboxId}-add`)\n\n/**\n * Виртуализация панели.\n *\n * Набор — `[«Add …»?] + filteredOptions`, тот же, по которому ходит клавиатура\n * (`navigableItems` ниже). Иначе верхняя распорка вытолкнула бы строку «Add …»\n * вниз: она отрисована внутри listbox'а первой и является полноценной опцией.\n */\n\n/** Оценка высоты опции: `py-2` вокруг строки кегля `sm`. Уточняется замером. */\nconst OPTION_SIZE_ESTIMATE = 36\n\nconst addOffset = computed(() => (canAddCustom.value ? 1 : 0))\nconst virtualCount = computed(() => effectiveOptions.value.length + addOffset.value)\n\nconst virtualizer = useVirtualList({\n container: listboxEl,\n count: () => (props.virtual ? virtualCount.value : 0),\n // Фильтрация/remote-ответ пересобирают набор — замеры прошлого невалидны.\n source: () => effectiveOptions.value,\n itemSize: OPTION_SIZE_ESTIMATE,\n // Панель скрыта `v-show`, пока закрыта, поэтому `clientHeight` контейнера —\n // ноль. Окно считается от объявленной высоты до первого настоящего замера.\n viewportSize: () => props.dropdownMaxHeight,\n})\n\n/** Виден ли «Add …»: вне виртуального окна его рисовать нельзя — он элемент набора. */\nconst showAddOption = computed(() => {\n if (!canAddCustom.value) return false\n return !props.virtual || virtualizer.range.value.start === 0\n})\n\n/** Опции к отрисовке вместе с их абсолютным индексом в `filteredOptions`. */\nconst renderedOptions = computed(() => {\n const all = effectiveOptions.value\n if (!props.virtual) return all.map((option, index) => ({ option, index }))\n\n const { start, end } = virtualizer.range.value\n const from = Math.max(0, start - addOffset.value)\n const to = Math.max(0, end - addOffset.value)\n\n return all.slice(from, to).map((option, offset) => ({ option, index: from + offset }))\n})\n\n/**\n * Размер набора и позиция в нём объявляются только при виртуализации: в обычном\n * режиме диктор выводит их из DOM, а при неполном наборе получил бы «3 из 15»\n * на списке в десять тысяч.\n */\nfunction optionSetProps(virtualIndex: number): Record<string, number> | undefined {\n if (!props.virtual) return undefined\n return { 'aria-setsize': virtualCount.value, 'aria-posinset': virtualIndex + 1 }\n}\n\nconst listboxStyle = computed(() => {\n const base: Record<string, string> = { maxHeight: `${props.dropdownMaxHeight}px` }\n if (!props.virtual) return base\n\n return {\n ...base,\n ...virtualizer.spacerStyle.value,\n }\n})\n\n/**\n * Клавиатурная навигация ходит и по опциям, и по варианту «добавить своё»:\n * иначе при непустом списке Enter всегда уходил бы в активную опцию, и\n * закоммитить произвольное значение с клавиатуры было бы нечем.\n */\ntype NavigableItem =\n | { kind: 'add' }\n | { kind: 'option', option: GrAutocompleteOption<TValue>, index: number }\n\nconst navigableItems = computed<NavigableItem[]>(() => {\n const items: NavigableItem[] = canAddCustom.value ? [{ kind: 'add' }] : []\n effectiveOptions.value.forEach((option, index) => {\n if (!option.disabled) items.push({ kind: 'option', option, index })\n })\n return items\n})\n\nfunction isSelected(value: TValue): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction navigableIndexOf(value: TValue): number {\n return navigableItems.value.findIndex(item => item.kind === 'option' && item.option.value === value)\n}\n\n/** Прокрутка к активному: сперва окно виртуального списка, затем доводка. */\nasync function scrollActiveIntoView(item: NavigableItem): Promise<void> {\n // Вне окна активной опции в DOM нет: `getElementById` вернул бы `null`,\n // прокрутка не случилась бы, а `aria-activedescendant` указал бы в пустоту.\n if (props.virtual)\n virtualizer.scrollToIndex(item.kind === 'add' ? 0 : item.index + addOffset.value)\n\n await nextTick()\n const id = item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)\n document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' })\n}\n\nconst {\n activeIndex,\n activeItem,\n activeDescendantId,\n init: initActiveIndex,\n reset: resetActive,\n handleNavigationKeys,\n} = useComboboxNavigation<NavigableItem>({\n items: () => navigableItems.value,\n open: () => open.value,\n idOf: item => (item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)),\n initialIndex: () => navigableItems.value.findIndex(item => item.kind === 'option' && isSelected(item.option.value)),\n scrollTo: item => scrollActiveIntoView(item),\n})\n\nconst activeValue = computed(() => (activeItem.value?.kind === 'option' ? activeItem.value.option.value : undefined))\n\n// ————— Открытие/закрытие.\nfunction openDropdown(): void {\n if (locked.value || open.value) return\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction focusInput(): void {\n inputEl.value?.focus()\n}\n\n// ————— Дебаунснутый поиск для remote-загрузки.\nlet searchTimer: ReturnType<typeof setTimeout> | null = null\n// Счётчик запросов: ответ, стартовавший раньше, но пришедший позже, обязан\n// проиграть последнему — иначе список показывает результат старого запроса.\nlet searchSeq = 0\nlet inflight: AbortController | null = null\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError'\n}\n\nasync function runFetch(query: string): Promise<void> {\n const fetchOptions = props.fetchOptions\n if (!fetchOptions) return\n\n inflight?.abort()\n const controller = new AbortController()\n inflight = controller\n const seq = ++searchSeq\n remoteLoading.value = true\n\n try {\n const result = await fetchOptions(query, controller.signal)\n if (seq !== searchSeq) return\n remoteOptions.value = result\n remoteAnswered.value = true\n }\n catch (error) {\n if (seq !== searchSeq || isAbortError(error)) return\n remoteOptions.value = []\n remoteAnswered.value = true\n emit('searchError', error)\n }\n finally {\n if (seq === searchSeq) remoteLoading.value = false\n }\n}\n\nfunction scheduleSearch(value: string): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = setTimeout(() => {\n if (props.minQueryLength > 0 && value.trim().length < props.minQueryLength) return\n emit('search', value.trim())\n void runFetch(value.trim())\n }, props.debounce)\n}\n\n/**\n * Снять запланированный и летящий запрос. Инкремент `searchSeq` обязателен\n * вместе со снятием `remoteLoading`: после него `finally` в `runFetch` считает\n * себя устаревшим и флаг не тронет — спиннер остался бы навсегда.\n */\nfunction cancelSearch(): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = null\n inflight?.abort()\n inflight = null\n searchSeq += 1\n remoteLoading.value = false\n}\n\nonBeforeUnmount(cancelSearch)\n\n// ————— Ввод.\nfunction onInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value\n query.value = value\n dirty.value = true\n openDropdown()\n scheduleSearch(value)\n}\n\nfunction onFocus(): void {\n if (locked.value) return\n openDropdown()\n}\n\nfunction setQuery(value: string): void {\n query.value = value\n dirty.value = false\n}\n\n// ————— Выбор значений.\nfunction selectSingle(value: TValue, label: string): void {\n emit('update:modelValue', value)\n emit('change', value as GrAutocompleteModelValue<TValue>)\n setQuery(label)\n closeDropdown()\n}\n\nfunction toggleMultiple(value: TValue): void {\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) next.splice(idx, 1)\n else next.push(value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n setQuery('')\n if (props.closeOnSelect) closeDropdown()\n else void nextTick(focusInput)\n}\n\nfunction chooseOption(option: GrAutocompleteOption<TValue>): void {\n if (locked.value || option.disabled) return\n if (props.multiple) toggleMultiple(option.value)\n else selectSingle(option.value, option.label)\n}\n\nfunction commitCustom(): void {\n if (locked.value) return\n // Кастомное значение строковое по природе — см. `canAddCustom`.\n const v = query.value.trim() as TValue\n if (!v) return\n if (props.multiple) toggleMultiple(v)\n else selectSingle(v, String(v))\n}\n\nfunction removeValue(value: TValue, focusAfter: () => void = focusInput): void {\n if (locked.value) return\n const next = selectedValues.value.filter(v => v !== value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n void nextTick(focusAfter)\n}\n\nfunction clearSelection(): void {\n if (locked.value) return\n const next = (props.multiple ? [] : '') as GrAutocompleteModelValue<TValue>\n emit('update:modelValue', next)\n emit('change', next)\n emit('clear')\n setQuery('')\n void nextTick(focusInput)\n}\n\nconst showClear = computed(() =>\n resolvedClearable.value && !locked.value && (hasSelection.value || query.value.length > 0),\n)\n\n// ————— Чипы (multiple): стрелки вместо таб-стопов.\n//\n// Крестики намеренно не табируемы: у combobox фокус живёт на `<input>`, и\n// двадцать выбранных значений не должны давать двадцать остановок Tab. Поэтому\n// вход в чипы — ArrowLeft из пустого запроса, а выход — ArrowRight/Esc/печать.\nfunction chipRemoveButtons(): HTMLButtonElement[] {\n return [...(rootEl.value?.querySelectorAll<HTMLButtonElement>('[data-gr-autocomplete-chip-remove]') ?? [])]\n}\n\nfunction focusChip(index: number): void {\n const buttons = chipRemoveButtons()\n if (!buttons.length) {\n focusInput()\n return\n }\n buttons[Math.min(Math.max(index, 0), buttons.length - 1)].focus()\n}\n\nfunction onChipKeydown(event: KeyboardEvent, index: number, value: TValue): void {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n focusChip(index - 1)\n break\n case 'ArrowRight':\n event.preventDefault()\n if (index >= chipRemoveButtons().length - 1) focusInput()\n else focusChip(index + 1)\n break\n case 'Home':\n event.preventDefault()\n focusChip(0)\n break\n case 'End':\n event.preventDefault()\n focusChip(chipRemoveButtons().length - 1)\n break\n case 'Delete':\n case 'Backspace':\n event.preventDefault()\n removeValue(value, () => focusChip(index))\n break\n case 'Escape':\n event.preventDefault()\n focusInput()\n break\n default:\n // Печатный символ возвращает в поле: фокус переезжает синхронно, до\n // вставки текста, поэтому символ попадает в запрос, а не пропадает.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) focusInput()\n }\n}\n\n// ————— Клавиатура.\nfunction onKeydown(event: KeyboardEvent): void {\n // Клавиша во время IME-композиции принадлежит композиции: Enter коммитит её,\n // Esc отменяет, стрелки ходят по кандидатам.\n if (isComposingEvent(event)) return\n if (locked.value) return\n\n // Курсор в начале пустого запроса — стрелка влево уходит к чипам, а не\n // двигает каретку, которой всё равно некуда двигаться.\n if (event.key === 'ArrowLeft' && props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n closeDropdown()\n focusChip(selectedValues.value.length - 1)\n return\n }\n\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n openDropdown()\n return\n }\n\n // Только при открытой панели: в закрытом инпуте Home/End двигают каретку.\n if (open.value && handleNavigationKeys(event)) return\n\n switch (event.key) {\n case 'Enter': {\n if (!open.value) break\n event.preventDefault()\n const item = activeItem.value\n if (item?.kind === 'option') chooseOption(item.option)\n else if (item?.kind === 'add' || canAddCustom.value) commitCustom()\n break\n }\n case 'Backspace':\n // multiple: пустой запрос + Backspace удаляет последний chip.\n if (props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n removeValue(selectedValues.value[selectedValues.value.length - 1])\n }\n break\n case 'Tab':\n closeDropdown()\n break\n }\n}\n\n// ————— Синхронизация query с выбранным значением (single).\nwatch(\n [singleSelectedLabel, () => props.multiple],\n ([label, multiple]) => {\n if (multiple) return\n // Не перетираем то, что пользователь сейчас набирает.\n if (open.value || dirty.value) return\n setQuery(label)\n },\n { immediate: true },\n)\n\nwatch(open, (isOpen) => {\n if (isOpen) {\n initActiveIndex()\n return\n }\n // Закрытие: сбрасываем активную опцию и «черновик».\n resetActive()\n if (props.multiple) {\n setQuery('')\n }\n else {\n // single: возвращаем метку выбранной опции (revert ввода без выбора).\n setQuery(singleSelectedLabel.value)\n }\n})\n\nconst ariaAutocomplete = computed(() => (props.allowCustomValue ? 'both' : 'list'))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-autocomplete\n class=\"relative w-full\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <div\n data-gr-autocomplete-shell\n :class=\"autocompleteShellClass({ size: resolvedSize, disabled: isDisabled, invalid: isInvalid })\"\n @mousedown.self.prevent=\"focusInput\"\n >\n <span\n v-if=\"hasPrefix\"\n ref=\"prefixEl\"\n data-gr-autocomplete-prefix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"prefixStyle\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <!-- Chips выбранных значений (multiple). -->\n <template v-if=\"multiple\">\n <GrBadge\n v-for=\"(option, chipIndex) in selectedOptions\"\n :key=\"option.value\"\n data-gr-autocomplete-chip\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n >\n <span class=\"inline-flex max-w-full items-center gap-1 align-middle\">\n <span class=\"truncate\">{{ option.label }}</span>\n <button\n v-if=\"!locked\"\n type=\"button\"\n data-gr-autocomplete-chip-remove\n class=\"inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-[var(--gr-radius-full)] text-current focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :aria-label=\"t('gr.autocomplete.removeValue', 'Remove {label}', { label: option.label })\"\n tabindex=\"-1\"\n @click=\"removeValue(option.value)\"\n @keydown=\"onChipKeydown($event, chipIndex, option.value)\"\n >\n <IconX class=\"block h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </span>\n </GrBadge>\n </template>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n data-gr-autocomplete-input\n data-testid=\"gr-autocomplete-input\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n role=\"combobox\"\n :value=\"query\"\n :disabled=\"isDisabled\"\n :placeholder=\"hasSelection && multiple ? undefined : placeholder\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :readonly=\"isReadonly\"\n aria-haspopup=\"listbox\"\n :aria-autocomplete=\"ariaAutocomplete\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n class=\"min-w-0 flex-1 bg-transparent text-inherit placeholder:text-[var(--gr-muted-fg)] focus:outline-none disabled:cursor-not-allowed\"\n @input=\"onInput\"\n @focus=\"onFocus\"\n @keydown=\"onKeydown\"\n >\n\n <span\n v-if=\"hasSuffix\"\n ref=\"suffixEl\"\n data-gr-autocomplete-suffix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"suffixStyle\"\n >\n <slot name=\"suffix\" />\n </span>\n\n <!-- Trailing: спиннер / очистка / шеврон. -->\n <span\n v-if=\"isLoading\"\n data-gr-autocomplete-spinner\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)]\"\n >\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n </span>\n\n <button\n v-else-if=\"showClear\"\n type=\"button\"\n data-testid=\"gr-autocomplete-clear\"\n data-gr-autocomplete-clear\n class=\"shrink-0 inline-flex h-5 w-5 items-center justify-center rounded-[var(--gr-radius-control)] text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)]\"\n :aria-label=\"resolvedClearLabel\"\n @click=\"clearSelection\"\n >\n <IconX class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <span\n v-else\n data-testid=\"gr-autocomplete-chevron\"\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)] pointer-events-none\"\n >\n <IconChevronDown class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <!-- Нативная форма: сериализуется модель, а не текст запроса. -->\n <template v-if=\"name\">\n <input\n v-for=\"value in selectedValues\"\n :key=\"`hidden-${String(value)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\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=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-autocomplete-panel\"\n v-bind=\"themeAttrs\"\n data-gr-autocomplete-panel\n data-gr-overlay-root\n :style=\"floatingStyle\"\n >\n <div :class=\"autocompletePanelClasses\">\n <!--\n Пустой listbox не рендерим: его `p-1` давал полосу пустоты над\n строкой состояния, и «ничего не найдено» выглядело прижатым к низу.\n -->\n <div\n v-if=\"showAddOption || renderedOptions.length\"\n :id=\"listboxId\"\n ref=\"listboxEl\"\n data-gr-autocomplete-listbox\n :data-gr-virtual=\"virtual ? '' : undefined\"\n class=\"p-1 overflow-auto\"\n :class=\"virtual ? 'flex flex-col' : ''\"\n :style=\"listboxStyle\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <!--\n Прямые потомки listbox — только опции: роль объявляет остальных\n недопустимыми детьми (`aria-required-children`), поэтому строки\n состояний живут ниже, вне списка. `mousedown.prevent` держит\n фокус на `<input>` — контракт combobox с `aria-activedescendant`.\n -->\n <button\n v-if=\"showAddOption\"\n :id=\"addOptionDomId\"\n type=\"button\"\n role=\"option\"\n aria-selected=\"false\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(0)\"\n data-testid=\"gr-autocomplete-add-option\"\n data-gr-autocomplete-add-option\n :class=\"autocompleteOptionClass({ disabled: false, active: activeItem?.kind === 'add' })\"\n @mousedown.prevent\n @click=\"commitCustom\"\n @mousemove=\"activeIndex = 0\"\n >\n {{ t('gr.autocomplete.addOption', 'Add \"{value}\"', { value: query.trim() }) }}\n </button>\n\n <button\n v-for=\"{ option, index: optionIndex } in renderedOptions\"\n :id=\"optionDomId(optionIndex)\"\n :key=\"option.value\"\n :ref=\"(el) => virtual && virtualizer.measure(optionIndex + addOffset, el as Element | null)\"\n data-gr-autocomplete-option\n type=\"button\"\n role=\"option\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(optionIndex + addOffset)\"\n :disabled=\"option.disabled\"\n :aria-selected=\"isSelected(option.value) ? 'true' : 'false'\"\n :aria-disabled=\"option.disabled ? 'true' : undefined\"\n :class=\"autocompleteOptionClass({\n disabled: !!option.disabled,\n active: activeValue === option.value,\n })\"\n @mousedown.prevent\n @click=\"chooseOption(option)\"\n @mousemove=\"activeIndex = navigableIndexOf(option.value)\"\n >\n <slot name=\"option\" :option=\"option\" :selected=\"isSelected(option.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span class=\"inline-block h-4 w-4 shrink-0\" aria-hidden=\"true\">\n <IconCheck v-if=\"isSelected(option.value)\" class=\"block h-4 w-4 text-[var(--gr-primary)]\" />\n </span>\n <span class=\"truncate\">{{ option.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n\n <!--\n Загрузка, «введите ещё N» и «ничего не найдено» меняются молча:\n `aria-label` на generic-элементе AT игнорируют. Один живой регион\n на все три — и вне listbox, чтобы не ломать его состав.\n -->\n <div\n v-if=\"isLoading || belowMinQuery || showEmpty\"\n data-gr-autocomplete-status\n role=\"status\"\n aria-live=\"polite\"\n :class=\"autocompleteStateClass\"\n >\n <template v-if=\"isLoading\">\n <span data-gr-autocomplete-loading class=\"flex items-center gap-2\">\n <slot name=\"loading\">\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </span>\n </template>\n <template v-else-if=\"belowMinQuery\">\n <span data-gr-autocomplete-hint>{{ resolvedTypeMoreText }}</span>\n </template>\n <template v-else>\n <span data-gr-autocomplete-empty>\n <slot name=\"empty\">\n {{ resolvedNoResultsText }}\n </slot>\n </span>\n </template>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n\n<style scoped>\n/*\n * Распорки виртуального списка.\n *\n * Не `padding` контейнера: `max-height` меряет ту же коробку, и распорка в\n * десятки тысяч пикселей раздула бы панель. Не обёртка внутри listbox'а:\n * прямыми потомками роли обязаны быть только опции (`aria-required-children`,\n * гейт — `GrAutocomplete.a11y.test.ts`). Не отступы крайних опций: при прыжке\n * прокрутки опции заменяются целиком, распорка исчезла бы вместе с ними, и\n * браузер обрезал бы `scrollTop`.\n *\n * Псевдоэлементы не узлы DOM: их не за что размонтировать, детьми listbox'а\n * они не считаются, а высота приезжает переменными в том же патче, что и опции.\n */\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=\"TValue extends GrAutocompleteValue = string\">\nimport { computed, nextTick, onBeforeUnmount, ref, shallowRef, useId, watch } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { useGrComponentProp, useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\n\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { isComposingEvent } from '../../internal/keyboard'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useComboboxNavigation } from '../../composables/useComboboxNavigation'\nimport { filterOptions, resolveSelectedOptions } from '../shared/optionFilter'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge/grBadgeStyles'\n\nimport {\n autocompleteOptionClass,\n autocompletePanelClasses,\n autocompleteShellClass,\n autocompleteStateClass,\n type GrAutocompleteModelValue,\n type GrAutocompleteOption,\n type GrAutocompleteValue,\n type GrAutocompleteSize,\n} from './grAutocompleteStyles'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconLoaderCircle from '~icons/lucide/loader-circle'\nimport IconX from '~icons/lucide/x'\n\nexport type {\n GrAutocompleteModelValue,\n GrAutocompleteOption,\n GrAutocompleteSize,\n GrAutocompleteValue,\n} from './grAutocompleteStyles'\n\n/**\n * Публичный GR-примитив «Autocomplete / Combobox» (WAI-ARIA editable combobox).\n *\n * В отличие от `GrSelect` (select-only combobox с кнопкой-триггером), здесь\n * combobox-ом является сам текстовый `<input>`: набранный текст — это поисковый\n * запрос, а выбор опции заполняет поле. Поддерживает локальную фильтрацию,\n * удалённую (async) загрузку через событие `search` + внешний проп `loading`,\n * произвольные значения (`allowCustomValue`) и multiple с удаляемыми chips.\n */\nexport interface GrAutocompleteProps<TValue extends GrAutocompleteValue = string> {\n /** Выбранное значение (single — строка, multiple — массив строк). */\n modelValue: GrAutocompleteModelValue<TValue>\n /**\n * Доступные опции. Для локального режима — полный список (фильтруется на клиенте).\n * Для remote-режима (`filterable=false`) — список, который родитель обновляет\n * в ответ на событие `search`.\n */\n options?: GrAutocompleteOption<TValue>[]\n multiple?: boolean\n /**\n * Вид чипов выбранных значений в режиме `multiple`. Чип — это `GrBadge`, а не\n * своя плашка: на светлой теме собственная заливка чипа (примесь `--gr-muted`\n * без рамки) давала 1.02:1 к фону поля, то есть была невидима.\n */\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n disabled?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrAutocompleteSize\n placeholder?: string\n ariaLabel?: string\n /** Кнопка очистки выбранного значения/запроса. */\n clearable?: boolean\n /** Внешне управляемое состояние загрузки (для async-сценариев). */\n loading?: boolean\n /**\n * Локальная фильтрация опций по введённому запросу. Отключите (`false`) для\n * чисто удалённого поиска — тогда `options` показываются как есть, а фильтрацию\n * выполняет сервер по событию `search`.\n */\n filterable?: boolean\n /** Кастомный матчер локальной фильтрации. По умолчанию — подстрока в `label`/`value`. */\n filter?: (option: GrAutocompleteOption<TValue>, query: string) => boolean\n /**\n * Удалённая загрузка опций под управлением компонента: дебаунс, отмена\n * устаревшего запроса и `loading` берёт на себя он. Ответ на отменённый\n * запрос игнорируется — при быстром вводе в списке всегда результат\n * последнего запроса, а не того, который вернулся позже.\n *\n * `signal` пробрасывается в `fetch`. Локальная фильтрация в этом режиме\n * выключена: список фильтрует сервер. Альтернатива — событие `search`, если\n * запрос ведёт само приложение.\n */\n fetchOptions?: (query: string, signal: AbortSignal) => Promise<GrAutocompleteOption<TValue>[]>\n /** Минимальная длина запроса до эмита `search` (для дебаунса remote-загрузки). */\n minQueryLength?: number\n /** Задержка дебаунса события `search`, мс. */\n debounce?: number\n /** Разрешить ввод/коммит значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Закрывать панель после выбора (single всегда закрывает). */\n closeOnSelect?: boolean\n /** Максимальная высота панели, px. */\n dropdownMaxHeight?: number\n /**\n * Виртуализация панели: в DOM живёт только окно вокруг вьюпорта.\n *\n * Высоту окна задаёт `dropdownMaxHeight`. Включается осознанно: на сотне\n * опций выигрыша нет, а в разметке остаётся только окно — вместе с ним\n * меняется и то, что находит `querySelector` потребителя. Профильный\n * сценарий — удалённый поиск по справочнику на тысячи позиций.\n */\n virtual?: boolean\n /** i18n-тексты состояний панели / aria. */\n loadingText?: string\n noResultsText?: string\n clearLabel?: string\n /**\n * Контролируемое состояние панели (`v-model:open`). Без пропа панель ведёт\n * себя сама (uncontrolled), с ним — слушайте `update:open` и меняйте проп.\n */\n open?: boolean\n /** Имя для нативной формы: hidden input по значению модели (не по тексту запроса). */\n name?: string\n /**\n * Ширины аддонов `prefix`/`suffix` — общий контракт контролов пакета\n * (`docs/form-controls.md`).\n */\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface GrAutocompleteEmits<TValue extends GrAutocompleteValue = string> {\n (e: 'update:modelValue', value: GrAutocompleteModelValue<TValue>): void\n /** Дебаунснутый поисковый запрос — точка входа для удалённой загрузки опций. */\n (e: 'search', query: string): void\n /** Запрос `fetchOptions` завершился ошибкой (отмена устаревшего сюда не приходит). */\n (e: 'searchError', error: unknown): void\n /** Значение зафиксировано выбором или снятием опции. */\n (e: 'change', value: GrAutocompleteModelValue<TValue>): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n /** Значение снято кнопкой очистки; только при `clearable`. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrAutocompleteProps<TValue>>(),\n {\n options: undefined,\n multiple: false,\n tagTone: 'neutral',\n tagDark: false,\n // `sm`, а не `md` как у `GrInputTag`: чип живёт внутри контрола, а не в тексте.\n tagSize: 'sm',\n tagRadius: 'round',\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n placeholder: undefined,\n ariaLabel: undefined,\n clearable: undefined,\n loading: false,\n filterable: true,\n filter: undefined,\n fetchOptions: undefined,\n minQueryLength: 0,\n debounce: 250,\n allowCustomValue: false,\n closeOnSelect: true,\n dropdownMaxHeight: 280,\n virtual: false,\n loadingText: undefined,\n noResultsText: undefined,\n clearLabel: undefined,\n open: undefined,\n name: undefined,\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\n/** Ширина аддона по умолчанию — та же лестница, что у `GrInput`. */\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrAutocompleteSize, string> = {\n xs: '2rem',\n sm: '2.25rem',\n md: '2.5rem',\n lg: '3rem',\n}\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrAutocomplete' })\nconst resolvedClearable = useGrComponentProp('GrAutocomplete', 'clearable', () => props.clearable, false)\n\n/**\n * Аддоны живут флекс-соседями поля, а не поверх него: оболочка автокомплита —\n * флекс-строка с чипами, поэтому резервировать место паддингом не нужно.\n * Контракт ширин остаётся общим (`useControlAddons`).\n */\nconst { hasPrefix, hasSuffix, prefixEl, suffixEl, prefixStyle, suffixStyle } = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => '0px',\n})\n\nconst emit = defineEmits<GrAutocompleteEmits<TValue>>()\ndefineSlots<{\n /** Аддон слева от поля ввода. */\n prefix?: () => any\n /** Аддон справа, перед спиннером и крестиком. */\n suffix?: () => any\n /** Строка списка вместо подписи опции. */\n option?: (props: { option: GrAutocompleteOption<TValue>, selected: boolean }) => any\n /** Содержимое панели, пока едут опции. */\n loading?: () => any\n /** Содержимое панели, когда подходящих опций нет. */\n empty?: () => any\n}>()\n\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.autocomplete.loading', 'Loading…'))\nconst resolvedNoResultsText = computed(() => props.noResultsText ?? t('gr.autocomplete.noResults', 'No results'))\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.common.clear', 'Clear'))\n// Число уходит под двумя общепринятыми именами: `n` читают `@feugene/fint-i18n`\n// и `vue-i18n`, `count` — `i18next`. Формы для каждого языка лежат в словаре,\n// выбирает их переводчик; встроенный fallback — одна английская строка.\nconst resolvedTypeMoreText = computed(() =>\n t('gr.autocomplete.typeMore', 'Type at least {n} characters', {\n n: props.minQueryLength,\n count: props.minQueryLength,\n }),\n)\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\n// Read-only запирает контрол так же, как disabled: значение видно, но панель не\n// открывается, опции не выбираются и чипы не удаляются. Без этого `readonly`\n// оставался бы ARIA-атрибутом без поведения.\nconst locked = computed(() => isDisabled.value || isReadonly.value)\n\n// Ответ последнего `fetchOptions`. До первого ответа показываем `options` —\n// с ними компонент рисует стартовый список, не дожидаясь сервера.\nconst remoteOptions = shallowRef<GrAutocompleteOption<TValue>[]>([])\nconst remoteAnswered = ref(false)\nconst remoteLoading = ref(false)\n\nconst optionsResolved = computed<GrAutocompleteOption<TValue>[]>(() =>\n props.fetchOptions && remoteAnswered.value ? remoteOptions.value : (props.options ?? []),\n)\n\n/**\n * Подпись состава стартового списка. Сравнивать идентичность массива нельзя:\n * инлайн-литерал `:options=\"[...]\"` пересоздаётся каждым ререндером родителя —\n * в том числе тем, который вызвал сам компонент своим `update:modelValue`, —\n * и remote-результаты исчезали бы прямо посреди выбора.\n */\nfunction optionsSignature(options: GrAutocompleteOption<TValue>[] | undefined): string {\n return (options ?? []).map(o => `${String(o.value)}\\u0000${o.label}`).join('\\u0001')\n}\n\n// Родитель сменил стартовый список — он снова источник до следующего ответа\n// сервера, а летящий запрос относится к прежнему набору данных и отменяется.\nwatch(() => optionsSignature(props.options), () => {\n if (!props.fetchOptions) return\n cancelSearch()\n remoteAnswered.value = false\n remoteOptions.value = []\n})\n\nconst isLoading = computed(() => props.loading || remoteLoading.value)\n\n/** `0` — валидное значение, поэтому «пусто» проверяется явно, а не через falsy. */\nfunction isEmptyValue(value: unknown): boolean {\n return value === undefined || value === null || value === ''\n}\n\nfunction toArray(value: GrAutocompleteModelValue<TValue>): TValue[] {\n if (Array.isArray(value)) return value\n if (isEmptyValue(value)) return []\n return [value]\n}\n\nconst modelSingle = computed<TValue | ''>(() => {\n const raw = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue\n return isEmptyValue(raw) ? '' : (raw as TValue)\n})\nconst selectedValues = computed(() => (props.multiple ? toArray(props.modelValue) : (isEmptyValue(modelSingle.value) ? [] : [modelSingle.value as TValue])))\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nfunction labelFor(value: GrAutocompleteValue): string {\n return optionsResolved.value.find(o => o.value === value)?.label ?? String(value)\n}\n\n/** Опции выбранных значений (для chips в multiple). Неизвестные значения показываем как есть. */\nconst selectedOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n resolveSelectedOptions(selectedValues.value, optionsResolved.value),\n)\n\nconst singleSelectedLabel = computed(() => (props.multiple || isEmptyValue(modelSingle.value) ? '' : labelFor(modelSingle.value)))\n\n// ————— Состояние.\nconst query = ref('')\n// `dirty` — пользователь начал вводить запрос. Нужно, чтобы в single-режиме при\n// открытии заполненного поля показать весь список (а не отфильтровать по метке\n// уже выбранной опции). Сбрасывается при программной установке `query`.\nconst dirty = ref(false)\n// Имя `open` сохранено: читатели работают с computed-Ref.\nconst { open, 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 listboxEl = ref<HTMLElement | null>(null)\n\n// Панель телепортирована в `body`, то есть лежит вне корня, но для пользователя\n// она часть контрола: без неё уход фокуса в панель читался бы как `blur`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n containers: () => [panelEl.value],\n})\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\ndefineExpose({\n focus,\n blur,\n open: () => openDropdown(),\n close: () => closeDropdown(),\n})\nconst clickOutsideExclude = [() => panelEl.value]\n\nconst { floatingStyle } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: true,\n zIndexVar: '--gr-z-dropdown',\n})\n\nuseDismissible(open, closeDropdown)\n\n// ————— Фильтрация.\nconst searchQuery = computed(() => {\n // При `fetchOptions` фильтрует сервер: локальный матчер отсеял бы то, что он\n // уже прислал в ответ на этот же запрос.\n if (!props.filterable || props.fetchOptions) return ''\n // single: пока пользователь не начал вводить — показываем весь список.\n if (!props.multiple && !dirty.value) return ''\n return query.value.trim()\n})\n\nconst filteredOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n filterOptions(optionsResolved.value, searchQuery.value, props.filter),\n)\n\nconst belowMinQuery = computed(() => props.minQueryLength > 0 && query.value.trim().length < props.minQueryLength)\n\n/**\n * Опции к показу и навигации. Ниже `minQueryLength` — пусто: список ещё\n * относится к прошлому запросу, показывать его под подсказкой «введите ещё N»\n * значит дезинформировать.\n */\nconst effectiveOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n belowMinQuery.value ? [] : filteredOptions.value,\n)\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n // Кастомное значение набирается текстом — оно строковое по природе;\n // при числовом `TValue` эта ветка неприменима (см. docs/components.md).\n const v = query.value.trim() as TValue\n if (!v) return false\n if (props.multiple && selectedValues.value.includes(v)) return false\n if (!props.multiple && v === modelSingle.value) return false\n // Не предлагаем «Add», если такое значение/метка уже есть среди опций.\n return !optionsResolved.value.some(o => o.value === v || o.label === v)\n})\n\nconst showEmpty = computed(() =>\n !isLoading.value && effectiveOptions.value.length === 0 && !canAddCustom.value,\n)\n\n// ————— Панель: id/aria-activedescendant.\nconst listboxId = useId()\n\n/**\n * Id опции строится от позиции, а не от значения: значение с пробелом дало бы\n * невалидный `id`, а `aria-activedescendant` — два токена вместо одной ссылки,\n * и активная опция перестала бы объявляться.\n */\nfunction optionDomId(index: number): string {\n return `${listboxId}-opt-${index}`\n}\nconst addOptionDomId = computed(() => `${listboxId}-add`)\n\n/**\n * Виртуализация панели.\n *\n * Набор — `[«Add …»?] + filteredOptions`, тот же, по которому ходит клавиатура\n * (`navigableItems` ниже). Иначе верхняя распорка вытолкнула бы строку «Add …»\n * вниз: она отрисована внутри listbox'а первой и является полноценной опцией.\n */\n\n/** Оценка высоты опции: `py-2` вокруг строки кегля `sm`. Уточняется замером. */\nconst OPTION_SIZE_ESTIMATE = 36\n\nconst addOffset = computed(() => (canAddCustom.value ? 1 : 0))\nconst virtualCount = computed(() => effectiveOptions.value.length + addOffset.value)\n\nconst virtualizer = useVirtualList({\n container: listboxEl,\n count: () => (props.virtual ? virtualCount.value : 0),\n // Фильтрация/remote-ответ пересобирают набор — замеры прошлого невалидны.\n source: () => effectiveOptions.value,\n itemSize: OPTION_SIZE_ESTIMATE,\n // Панель скрыта `v-show`, пока закрыта, поэтому `clientHeight` контейнера —\n // ноль. Окно считается от объявленной высоты до первого настоящего замера.\n viewportSize: () => props.dropdownMaxHeight,\n})\n\n/** Виден ли «Add …»: вне виртуального окна его рисовать нельзя — он элемент набора. */\nconst showAddOption = computed(() => {\n if (!canAddCustom.value) return false\n return !props.virtual || virtualizer.range.value.start === 0\n})\n\n/** Опции к отрисовке вместе с их абсолютным индексом в `filteredOptions`. */\nconst renderedOptions = computed(() => {\n const all = effectiveOptions.value\n if (!props.virtual) return all.map((option, index) => ({ option, index }))\n\n const { start, end } = virtualizer.range.value\n const from = Math.max(0, start - addOffset.value)\n const to = Math.max(0, end - addOffset.value)\n\n return all.slice(from, to).map((option, offset) => ({ option, index: from + offset }))\n})\n\n/**\n * Размер набора и позиция в нём объявляются только при виртуализации: в обычном\n * режиме диктор выводит их из DOM, а при неполном наборе получил бы «3 из 15»\n * на списке в десять тысяч.\n */\nfunction optionSetProps(virtualIndex: number): Record<string, number> | undefined {\n if (!props.virtual) return undefined\n return { 'aria-setsize': virtualCount.value, 'aria-posinset': virtualIndex + 1 }\n}\n\nconst listboxStyle = computed(() => {\n const base: Record<string, string> = { maxHeight: `${props.dropdownMaxHeight}px` }\n if (!props.virtual) return base\n\n return {\n ...base,\n ...virtualizer.spacerStyle.value,\n }\n})\n\n/**\n * Клавиатурная навигация ходит и по опциям, и по варианту «добавить своё»:\n * иначе при непустом списке Enter всегда уходил бы в активную опцию, и\n * закоммитить произвольное значение с клавиатуры было бы нечем.\n */\ntype NavigableItem =\n | { kind: 'add' }\n | { kind: 'option', option: GrAutocompleteOption<TValue>, index: number }\n\nconst navigableItems = computed<NavigableItem[]>(() => {\n const items: NavigableItem[] = canAddCustom.value ? [{ kind: 'add' }] : []\n effectiveOptions.value.forEach((option, index) => {\n if (!option.disabled) items.push({ kind: 'option', option, index })\n })\n return items\n})\n\nfunction isSelected(value: TValue): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction navigableIndexOf(value: TValue): number {\n return navigableItems.value.findIndex(item => item.kind === 'option' && item.option.value === value)\n}\n\n/** Прокрутка к активному: сперва окно виртуального списка, затем доводка. */\nasync function scrollActiveIntoView(item: NavigableItem): Promise<void> {\n // Вне окна активной опции в DOM нет: `getElementById` вернул бы `null`,\n // прокрутка не случилась бы, а `aria-activedescendant` указал бы в пустоту.\n if (props.virtual)\n virtualizer.scrollToIndex(item.kind === 'add' ? 0 : item.index + addOffset.value)\n\n await nextTick()\n const id = item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)\n document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' })\n}\n\nconst {\n activeIndex,\n activeItem,\n activeDescendantId,\n init: initActiveIndex,\n reset: resetActive,\n handleNavigationKeys,\n} = useComboboxNavigation<NavigableItem>({\n items: () => navigableItems.value,\n open: () => open.value,\n idOf: item => (item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)),\n initialIndex: () => navigableItems.value.findIndex(item => item.kind === 'option' && isSelected(item.option.value)),\n scrollTo: item => scrollActiveIntoView(item),\n})\n\nconst activeValue = computed(() => (activeItem.value?.kind === 'option' ? activeItem.value.option.value : undefined))\n\n// ————— Открытие/закрытие.\nfunction openDropdown(): void {\n if (locked.value || open.value) return\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction focusInput(): void {\n inputEl.value?.focus()\n}\n\n// ————— Дебаунснутый поиск для remote-загрузки.\nlet searchTimer: ReturnType<typeof setTimeout> | null = null\n// Счётчик запросов: ответ, стартовавший раньше, но пришедший позже, обязан\n// проиграть последнему — иначе список показывает результат старого запроса.\nlet searchSeq = 0\nlet inflight: AbortController | null = null\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError'\n}\n\nasync function runFetch(query: string): Promise<void> {\n const fetchOptions = props.fetchOptions\n if (!fetchOptions) return\n\n inflight?.abort()\n const controller = new AbortController()\n inflight = controller\n const seq = ++searchSeq\n remoteLoading.value = true\n\n try {\n const result = await fetchOptions(query, controller.signal)\n if (seq !== searchSeq) return\n remoteOptions.value = result\n remoteAnswered.value = true\n }\n catch (error) {\n if (seq !== searchSeq || isAbortError(error)) return\n remoteOptions.value = []\n remoteAnswered.value = true\n emit('searchError', error)\n }\n finally {\n if (seq === searchSeq) remoteLoading.value = false\n }\n}\n\nfunction scheduleSearch(value: string): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = setTimeout(() => {\n if (props.minQueryLength > 0 && value.trim().length < props.minQueryLength) return\n emit('search', value.trim())\n void runFetch(value.trim())\n }, props.debounce)\n}\n\n/**\n * Снять запланированный и летящий запрос. Инкремент `searchSeq` обязателен\n * вместе со снятием `remoteLoading`: после него `finally` в `runFetch` считает\n * себя устаревшим и флаг не тронет — спиннер остался бы навсегда.\n */\nfunction cancelSearch(): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = null\n inflight?.abort()\n inflight = null\n searchSeq += 1\n remoteLoading.value = false\n}\n\nonBeforeUnmount(cancelSearch)\n\n// ————— Ввод.\nfunction onInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value\n query.value = value\n dirty.value = true\n openDropdown()\n scheduleSearch(value)\n}\n\nfunction onFocus(): void {\n if (locked.value) return\n openDropdown()\n}\n\nfunction setQuery(value: string): void {\n query.value = value\n dirty.value = false\n}\n\n// ————— Выбор значений.\nfunction selectSingle(value: TValue, label: string): void {\n emit('update:modelValue', value)\n emit('change', value as GrAutocompleteModelValue<TValue>)\n setQuery(label)\n closeDropdown()\n}\n\nfunction toggleMultiple(value: TValue): void {\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) next.splice(idx, 1)\n else next.push(value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n setQuery('')\n if (props.closeOnSelect) closeDropdown()\n else void nextTick(focusInput)\n}\n\nfunction chooseOption(option: GrAutocompleteOption<TValue>): void {\n if (locked.value || option.disabled) return\n if (props.multiple) toggleMultiple(option.value)\n else selectSingle(option.value, option.label)\n}\n\nfunction commitCustom(): void {\n if (locked.value) return\n // Кастомное значение строковое по природе — см. `canAddCustom`.\n const v = query.value.trim() as TValue\n if (!v) return\n if (props.multiple) toggleMultiple(v)\n else selectSingle(v, String(v))\n}\n\nfunction removeValue(value: TValue, focusAfter: () => void = focusInput): void {\n if (locked.value) return\n const next = selectedValues.value.filter(v => v !== value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n void nextTick(focusAfter)\n}\n\nfunction clearSelection(): void {\n if (locked.value) return\n const next = (props.multiple ? [] : '') as GrAutocompleteModelValue<TValue>\n emit('update:modelValue', next)\n emit('change', next)\n emit('clear')\n setQuery('')\n void nextTick(focusInput)\n}\n\nconst showClear = computed(() =>\n resolvedClearable.value && !locked.value && (hasSelection.value || query.value.length > 0),\n)\n\n// ————— Чипы (multiple): стрелки вместо таб-стопов.\n//\n// Крестики намеренно не табируемы: у combobox фокус живёт на `<input>`, и\n// двадцать выбранных значений не должны давать двадцать остановок Tab. Поэтому\n// вход в чипы — ArrowLeft из пустого запроса, а выход — ArrowRight/Esc/печать.\nfunction chipRemoveButtons(): HTMLButtonElement[] {\n return [...(rootEl.value?.querySelectorAll<HTMLButtonElement>('[data-gr-autocomplete-chip-remove]') ?? [])]\n}\n\nfunction focusChip(index: number): void {\n const buttons = chipRemoveButtons()\n if (!buttons.length) {\n focusInput()\n return\n }\n buttons[Math.min(Math.max(index, 0), buttons.length - 1)].focus()\n}\n\nfunction onChipKeydown(event: KeyboardEvent, index: number, value: TValue): void {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n focusChip(index - 1)\n break\n case 'ArrowRight':\n event.preventDefault()\n if (index >= chipRemoveButtons().length - 1) focusInput()\n else focusChip(index + 1)\n break\n case 'Home':\n event.preventDefault()\n focusChip(0)\n break\n case 'End':\n event.preventDefault()\n focusChip(chipRemoveButtons().length - 1)\n break\n case 'Delete':\n case 'Backspace':\n event.preventDefault()\n removeValue(value, () => focusChip(index))\n break\n case 'Escape':\n event.preventDefault()\n focusInput()\n break\n default:\n // Печатный символ возвращает в поле: фокус переезжает синхронно, до\n // вставки текста, поэтому символ попадает в запрос, а не пропадает.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) focusInput()\n }\n}\n\n// ————— Клавиатура.\nfunction onKeydown(event: KeyboardEvent): void {\n // Клавиша во время IME-композиции принадлежит композиции: Enter коммитит её,\n // Esc отменяет, стрелки ходят по кандидатам.\n if (isComposingEvent(event)) return\n if (locked.value) return\n\n // Курсор в начале пустого запроса — стрелка влево уходит к чипам, а не\n // двигает каретку, которой всё равно некуда двигаться.\n if (event.key === 'ArrowLeft' && props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n closeDropdown()\n focusChip(selectedValues.value.length - 1)\n return\n }\n\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n openDropdown()\n return\n }\n\n // Только при открытой панели: в закрытом инпуте Home/End двигают каретку.\n if (open.value && handleNavigationKeys(event)) return\n\n switch (event.key) {\n case 'Enter': {\n if (!open.value) break\n event.preventDefault()\n const item = activeItem.value\n if (item?.kind === 'option') chooseOption(item.option)\n else if (item?.kind === 'add' || canAddCustom.value) commitCustom()\n break\n }\n case 'Backspace':\n // multiple: пустой запрос + Backspace удаляет последний chip.\n if (props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n removeValue(selectedValues.value[selectedValues.value.length - 1])\n }\n break\n case 'Tab':\n closeDropdown()\n break\n }\n}\n\n// ————— Синхронизация query с выбранным значением (single).\nwatch(\n [singleSelectedLabel, () => props.multiple],\n ([label, multiple]) => {\n if (multiple) return\n // Не перетираем то, что пользователь сейчас набирает.\n if (open.value || dirty.value) return\n setQuery(label)\n },\n { immediate: true },\n)\n\nwatch(open, (isOpen) => {\n if (isOpen) {\n initActiveIndex()\n return\n }\n // Закрытие: сбрасываем активную опцию и «черновик».\n resetActive()\n if (props.multiple) {\n setQuery('')\n }\n else {\n // single: возвращаем метку выбранной опции (revert ввода без выбора).\n setQuery(singleSelectedLabel.value)\n }\n})\n\nconst ariaAutocomplete = computed(() => (props.allowCustomValue ? 'both' : 'list'))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-autocomplete\n class=\"relative w-full\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <div\n data-gr-autocomplete-shell\n :class=\"autocompleteShellClass({ size: resolvedSize, disabled: isDisabled, invalid: isInvalid })\"\n @mousedown.self.prevent=\"focusInput\"\n >\n <span\n v-if=\"hasPrefix\"\n ref=\"prefixEl\"\n data-gr-autocomplete-prefix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"prefixStyle\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <!-- Chips выбранных значений (multiple). -->\n <template v-if=\"multiple\">\n <GrBadge\n v-for=\"(option, chipIndex) in selectedOptions\"\n :key=\"option.value\"\n data-gr-autocomplete-chip\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n >\n <span class=\"inline-flex max-w-full items-center gap-1 align-middle\">\n <span class=\"truncate\">{{ option.label }}</span>\n <button\n v-if=\"!locked\"\n type=\"button\"\n data-gr-autocomplete-chip-remove\n class=\"inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-[var(--gr-radius-full)] text-current focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :aria-label=\"t('gr.autocomplete.removeValue', 'Remove {label}', { label: option.label })\"\n tabindex=\"-1\"\n @click=\"removeValue(option.value)\"\n @keydown=\"onChipKeydown($event, chipIndex, option.value)\"\n >\n <IconX class=\"block h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </span>\n </GrBadge>\n </template>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n data-gr-autocomplete-input\n data-testid=\"gr-autocomplete-input\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n role=\"combobox\"\n :value=\"query\"\n :disabled=\"isDisabled\"\n :placeholder=\"hasSelection && multiple ? undefined : placeholder\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :readonly=\"isReadonly\"\n aria-haspopup=\"listbox\"\n :aria-autocomplete=\"ariaAutocomplete\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n class=\"min-w-0 flex-1 bg-transparent text-inherit placeholder:text-[var(--gr-muted-fg)] focus:outline-none disabled:cursor-not-allowed\"\n @input=\"onInput\"\n @focus=\"onFocus\"\n @keydown=\"onKeydown\"\n >\n\n <span\n v-if=\"hasSuffix\"\n ref=\"suffixEl\"\n data-gr-autocomplete-suffix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"suffixStyle\"\n >\n <slot name=\"suffix\" />\n </span>\n\n <!-- Trailing: спиннер / очистка / шеврон. -->\n <span\n v-if=\"isLoading\"\n data-gr-autocomplete-spinner\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)]\"\n >\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n </span>\n\n <button\n v-else-if=\"showClear\"\n type=\"button\"\n data-testid=\"gr-autocomplete-clear\"\n data-gr-autocomplete-clear\n class=\"shrink-0 inline-flex h-5 w-5 items-center justify-center rounded-[var(--gr-radius-control)] text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)]\"\n :aria-label=\"resolvedClearLabel\"\n @click=\"clearSelection\"\n >\n <IconX class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <span\n v-else\n data-testid=\"gr-autocomplete-chevron\"\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)] pointer-events-none\"\n >\n <IconChevronDown class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <!-- Нативная форма: сериализуется модель, а не текст запроса. -->\n <template v-if=\"name\">\n <input\n v-for=\"value in selectedValues\"\n :key=\"`hidden-${String(value)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\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=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-autocomplete-panel\"\n v-bind=\"themeAttrs\"\n data-gr-autocomplete-panel\n data-gr-overlay-root\n :style=\"floatingStyle\"\n >\n <div :class=\"autocompletePanelClasses\">\n <!--\n Пустой listbox не рендерим: его `p-1` давал полосу пустоты над\n строкой состояния, и «ничего не найдено» выглядело прижатым к низу.\n -->\n <div\n v-if=\"showAddOption || renderedOptions.length\"\n :id=\"listboxId\"\n ref=\"listboxEl\"\n data-gr-autocomplete-listbox\n :data-gr-virtual=\"virtual ? '' : undefined\"\n class=\"p-1 overflow-auto\"\n :class=\"virtual ? 'flex flex-col' : ''\"\n :style=\"listboxStyle\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <!--\n Прямые потомки listbox — только опции: роль объявляет остальных\n недопустимыми детьми (`aria-required-children`), поэтому строки\n состояний живут ниже, вне списка. `mousedown.prevent` держит\n фокус на `<input>` — контракт combobox с `aria-activedescendant`.\n -->\n <button\n v-if=\"showAddOption\"\n :id=\"addOptionDomId\"\n type=\"button\"\n role=\"option\"\n aria-selected=\"false\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(0)\"\n data-testid=\"gr-autocomplete-add-option\"\n data-gr-autocomplete-add-option\n :class=\"autocompleteOptionClass({ disabled: false, active: activeItem?.kind === 'add' })\"\n @mousedown.prevent\n @click=\"commitCustom\"\n @mousemove=\"activeIndex = 0\"\n >\n {{ t('gr.autocomplete.addOption', 'Add \"{value}\"', { value: query.trim() }) }}\n </button>\n\n <button\n v-for=\"{ option, index: optionIndex } in renderedOptions\"\n :id=\"optionDomId(optionIndex)\"\n :key=\"option.value\"\n :ref=\"(el) => virtual && virtualizer.measure(optionIndex + addOffset, el as Element | null)\"\n data-gr-autocomplete-option\n type=\"button\"\n role=\"option\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(optionIndex + addOffset)\"\n :disabled=\"option.disabled\"\n :aria-selected=\"isSelected(option.value) ? 'true' : 'false'\"\n :aria-disabled=\"option.disabled ? 'true' : undefined\"\n :class=\"autocompleteOptionClass({\n disabled: !!option.disabled,\n active: activeValue === option.value,\n })\"\n @mousedown.prevent\n @click=\"chooseOption(option)\"\n @mousemove=\"activeIndex = navigableIndexOf(option.value)\"\n >\n <slot name=\"option\" :option=\"option\" :selected=\"isSelected(option.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span class=\"inline-block h-4 w-4 shrink-0\" aria-hidden=\"true\">\n <IconCheck v-if=\"isSelected(option.value)\" class=\"block h-4 w-4 text-[var(--gr-primary)]\" />\n </span>\n <span class=\"truncate\">{{ option.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n\n <!--\n Загрузка, «введите ещё N» и «ничего не найдено» меняются молча:\n `aria-label` на generic-элементе AT игнорируют. Один живой регион\n на все три — и вне listbox, чтобы не ломать его состав.\n -->\n <div\n v-if=\"isLoading || belowMinQuery || showEmpty\"\n data-gr-autocomplete-status\n role=\"status\"\n aria-live=\"polite\"\n :class=\"autocompleteStateClass\"\n >\n <template v-if=\"isLoading\">\n <span data-gr-autocomplete-loading class=\"flex items-center gap-2\">\n <slot name=\"loading\">\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </span>\n </template>\n <template v-else-if=\"belowMinQuery\">\n <span data-gr-autocomplete-hint>{{ resolvedTypeMoreText }}</span>\n </template>\n <template v-else>\n <span data-gr-autocomplete-empty>\n <slot name=\"empty\">\n {{ resolvedNoResultsText }}\n </slot>\n </span>\n </template>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n\n<style scoped>\n/*\n * Распорки виртуального списка.\n *\n * Не `padding` контейнера: `max-height` меряет ту же коробку, и распорка в\n * десятки тысяч пикселей раздула бы панель. Не обёртка внутри listbox'а:\n * прямыми потомками роли обязаны быть только опции (`aria-required-children`,\n * гейт — `GrAutocomplete.a11y.test.ts`). Не отступы крайних опций: при прыжке\n * прокрутки опции заменяются целиком, распорка исчезла бы вместе с ними, и\n * браузер обрезал бы `scrollTop`.\n *\n * Псевдоэлементы не узлы DOM: их не за что размонтировать, детьми listbox'а\n * они не считаются, а высота приезжает переменными в том же патче, что и опции.\n */\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"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrBadge-_9rCHuE1.js","names":[],"sources":["../../../../src/components/GrBadge/GrBadge.vue","../../../../src/components/GrBadge/GrBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGrComponentProp } from '../GrConfigProvider/context'\n\nexport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from './grBadgeStyles'\n\nimport {\n grBadgeClass,\n type GrBadgeRadius,\n type GrBadgeSize,\n type GrBadgeTone,\n} from './grBadgeStyles'\n\nexport interface GrBadgeProps {\n tone?: GrBadgeTone\n dark?: boolean\n size?: GrBadgeSize\n radius?: GrBadgeRadius\n}\n\nconst props = withDefaults(\n defineProps<GrBadgeProps>(),\n {\n // `tone`/`size`/`radius` настраиваются через `GrConfigProvider`, поэтому\n // их дефолты живут в резолверах ниже, а не в `withDefaults`.\n tone: undefined,\n dark: false,\n size: undefined,\n radius: undefined,\n },\n)\n\n// Эффективные значения: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedTone = useGrComponentProp('GrBadge', 'tone', () => props.tone, 'neutral')\nconst resolvedSize = useGrComponentProp('GrBadge', 'size', () => props.size, 'sm')\nconst resolvedRadius = useGrComponentProp('GrBadge', 'radius', () => props.radius, 'round')\n\nconst className = computed(() => {\n return grBadgeClass({\n tone: resolvedTone.value,\n dark: props.dark,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n })\n})\n</script>\n\n<template>\n <span\n class=\"gr-badge inline-block border whitespace-nowrap leading-none\"\n :class=\"className\"\n >\n <span class=\"gr-badge__label\">\n <span class=\"gr-badge__text\">\n <slot />\n </span>\n </span>\n </span>\n</template>\n\n<style scoped>\n/*\n * Оптически центрируем текст по вертикали независимо от регистра,\n * не уменьшая высоту самого бейджа.\n *\n * Высоту держит обёртка `.gr-badge__label` (`min-height: 1lh` — высота\n * line-box при `leading-none`), а текст (`.gr-badge__text`) обрезается до\n * cap-height и центрируется внутри этой высоты через flex.\n *\n * Обрезка обязана жить на вложенном узле, а не на самом тексте: `text-box-trim`\n * уменьшает line-box, и повесь его прямо на текст — вместе с ним просядет\n * высота контента, а за ней и весь бейдж.\n */\n.gr-badge__label {\n display: flex;\n align-items: center;\n min-height: 1lh;\n}\n\n.gr-badge__text {\n display: block;\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n /* Шорткат для браузеров с поддержкой нового синтаксиса. */\n text-box: trim-both cap alphabetic;\n}\n</style>","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGrComponentProp } from '../GrConfigProvider/context'\n\nexport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from './grBadgeStyles'\n\nimport {\n grBadgeClass,\n type GrBadgeRadius,\n type GrBadgeSize,\n type GrBadgeTone,\n} from './grBadgeStyles'\n\nexport interface GrBadgeProps {\n tone?: GrBadgeTone\n dark?: boolean\n size?: GrBadgeSize\n radius?: GrBadgeRadius\n}\n\nconst props = withDefaults(\n defineProps<GrBadgeProps>(),\n {\n // `tone`/`size`/`radius` настраиваются через `GrConfigProvider`, поэтому\n // их дефолты живут в резолверах ниже, а не в `withDefaults`.\n tone: undefined,\n dark: false,\n size: undefined,\n radius: undefined,\n },\n)\n\n// Эффективные значения: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedTone = useGrComponentProp('GrBadge', 'tone', () => props.tone, 'neutral')\nconst resolvedSize = useGrComponentProp('GrBadge', 'size', () => props.size, 'sm')\nconst resolvedRadius = useGrComponentProp('GrBadge', 'radius', () => props.radius, 'round')\n\nconst className = computed(() => {\n return grBadgeClass({\n tone: resolvedTone.value,\n dark: props.dark,\n size: resolvedSize.value,\n radius: resolvedRadius.value,\n })\n})\n</script>\n\n<template>\n <span\n class=\"gr-badge inline-block border whitespace-nowrap leading-none\"\n :class=\"className\"\n >\n <span class=\"gr-badge__label\">\n <span class=\"gr-badge__text\">\n <slot />\n </span>\n </span>\n </span>\n</template>\n\n<style scoped>\n/*\n * Оптически центрируем текст по вертикали независимо от регистра,\n * не уменьшая высоту самого бейджа.\n *\n * Высоту держит обёртка `.gr-badge__label` (`min-height: 1lh` — высота\n * line-box при `leading-none`), а текст (`.gr-badge__text`) обрезается до\n * cap-height и центрируется внутри этой высоты через flex.\n *\n * Обрезка обязана жить на вложенном узле, а не на самом тексте: `text-box-trim`\n * уменьшает line-box, и повесь его прямо на текст — вместе с ним просядет\n * высота контента, а за ней и весь бейдж.\n */\n.gr-badge__label {\n display: flex;\n align-items: center;\n min-height: 1lh;\n}\n\n.gr-badge__text {\n display: block;\n text-box-trim: trim-both;\n text-box-edge: cap alphabetic;\n /* Шорткат для браузеров с поддержкой нового синтаксиса. */\n text-box: trim-both cap alphabetic;\n}\n</style>"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAqBA,MAAM,QAAQ;EAad,MAAM,eAAe,mBAAmB,WAAW,cAAc,MAAM,MAAM,SAAS;EACtF,MAAM,eAAe,mBAAmB,WAAW,cAAc,MAAM,MAAM,IAAI;EACjF,MAAM,iBAAiB,mBAAmB,WAAW,gBAAgB,MAAM,QAAQ,OAAO;EAE1F,MAAM,YAAY,eAAe;GAC/B,OAAO,aAAa;IAClB,MAAM,aAAa;IACnB,MAAM,MAAM;IACZ,MAAM,aAAa;IACnB,QAAQ,eAAe;GACzB,CAAC;EACH,CAAC;;uBAIC,mBASO,QAAA,EARH,OAAK,eAAA,CAAC,+DACE,UAAA,KAAS,CAAA,EAAA,GAAA,CAEnB,mBAIO,QAJP,YAIO,CAHL,mBAEO,QAFP,YAEO,CADL,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrBadgeWrap-DDoFQscR.js","names":[],"sources":["../../../../src/components/GrBadgeWrap/GrBadgeWrap.vue","../../../../src/components/GrBadgeWrap/GrBadgeWrap.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n countBaseClass,\n dotBaseClass,\n dotPlacementClass,\n formatBadgeValue,\n isBadgeWrapPulse,\n placementClass,\n rootClass,\n toneClass,\n type GrBadgeWrapPlacement,\n type GrBadgeWrapTone,\n} from './grBadgeWrapStyles'\n\nexport type { GrBadgeWrapPlacement, GrBadgeWrapTone } from './grBadgeWrapStyles'\n\n/**\n * GrBadgeWrap — обёртка, вешающая на потомка счётчик или точку-индикатор.\n *\n * Число объявляется скринридеру: «3» без единицы измерения ничего не значит,\n * поэтому рядом с бейджем идёт визуально скрытая подпись.\n */\nexport interface GrBadgeWrapProps {\n value?: string | number\n /** Точка вместо числа: чистая декорация, если не задан `ariaLabel`. */\n dot?: boolean\n /** Порог: значения больше него рисуются как «{max}+». */\n max?: number\n /** Показывать ли нулевое значение. По умолчанию ноль скрыт. */\n showZero?: boolean\n tone?: GrBadgeWrapTone\n placement?: GrBadgeWrapPlacement\n /** Подпись счётчика для скринридера. Не задана — берётся из локали. */\n ariaLabel?: string\n /** Отмечать ли появление и рост **счётчика** анимацией. Точке анимировать нечего. */\n animate?: boolean\n}\n\nconst props = withDefaults(defineProps<GrBadgeWrapProps>(), {\n value: undefined,\n dot: false,\n max: undefined,\n showZero: false,\n tone: 'danger',\n placement: 'top-right',\n ariaLabel: undefined,\n animate: false,\n})\n\nconst { t } = useGranularityTranslations()\n\nconst showCount = computed(() => {\n if (props.dot || props.value === undefined) return false\n return props.value !== 0 || props.showZero\n})\n\n/** Видимый текст: «120» при `max=99` превращается в «99+». */\nconst displayValue = computed(() => formatBadgeValue(props.value ?? '', props.max))\n\n/**\n * Озвучивается настоящее значение, а не обрезанное: «99 плюс» пользователю\n * ничего не говорит, «120 непрочитанных» — говорит.\n */\nconst countLabel = computed(() => props.ariaLabel\n ?? t('gr.badgeWrap.count', '{count} new', { count: props.value, n: props.value }))\n\nconst pulsing = ref(false)\n\n/** Значение на экране: скрытый счётчик — то же самое, что его отсутствие. */\nconst visibleValue = computed(() => showCount.value ? props.value : undefined)\n\n/**\n * Пока анимация играет, новое изменение её не перезапускает: счётчик меняется\n * пачкой (пять писем за секунду), и «поп» на каждое превратился бы в мельтешение.\n * Серия за время анимации даёт ровно один «поп».\n *\n * Флаг отпускает `animationend`, а не таймер: длительность живёт в CSS-токене,\n * и число в скрипте было бы вторым источником правды. Под\n * `prefers-reduced-motion` событие тоже приходит — глобальный кламп сжимает\n * анимацию до `0.01ms`, а не выключает её (`docs/motion.md`).\n */\nwatch(visibleValue, (next, prev) => {\n if (!props.animate || pulsing.value) return\n if (isBadgeWrapPulse(prev, next)) pulsing.value = true\n})\n\n// Ушедший с экрана счётчик `animationend` уже не пришлёт, и без сброса\n// следующее его появление осталось бы без анимации навсегда.\nwatch(showCount, (visible) => {\n if (!visible) pulsing.value = false\n})\n</script>\n\n<template>\n <span :class=\"rootClass\">\n <slot />\n\n <template v-if=\"dot\">\n <span\n data-gr-badge-wrap-dot\n :class=\"[dotBaseClass, dotPlacementClass[placement], toneClass[tone]]\"\n aria-hidden=\"true\"\n />\n <!-- Точка сама по себе смысла не несёт; подпись появляется только когда\n потребитель объяснил, о чём она. -->\n <span v-if=\"ariaLabel\" data-gr-badge-wrap-label class=\"sr-only\">{{ ariaLabel }}</span>\n </template>\n\n <template v-else-if=\"showCount\">\n <span\n data-gr-badge-wrap-count\n :data-gr-badge-wrap-pop=\"pulsing || undefined\"\n :class=\"[countBaseClass, placementClass[placement], toneClass[tone]]\"\n aria-hidden=\"true\"\n @animationend=\"pulsing = false\"\n >{{ displayValue }}</span>\n <span data-gr-badge-wrap-label class=\"sr-only\">{{ countLabel }}</span>\n </template>\n </span>\n</template>\n\n<style>\n@keyframes gr-badge-wrap-pop {\n 0% {\n transform: scale(0.4);\n }\n 60% {\n transform: scale(1.12);\n }\n 100% {\n transform: scale(1);\n }\n}\n\n/*\n * `animation-fill-mode` не задан намеренно: под `prefers-reduced-motion` анимация\n * доигрывает за `0.01ms` и возвращает элемент в исходный кадр — бейдж на месте\n * и в нужном размере. Именно поэтому компоненту хватает глобального клампа и не\n * нужен собственный reduce-блок (`docs/motion.md`).\n */\n[data-gr-badge-wrap-count][data-gr-badge-wrap-pop] {\n animation: gr-badge-wrap-pop var(--gr-duration-base) var(--gr-ease-out);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n countBaseClass,\n dotBaseClass,\n dotPlacementClass,\n formatBadgeValue,\n isBadgeWrapPulse,\n placementClass,\n rootClass,\n toneClass,\n type GrBadgeWrapPlacement,\n type GrBadgeWrapTone,\n} from './grBadgeWrapStyles'\n\nexport type { GrBadgeWrapPlacement, GrBadgeWrapTone } from './grBadgeWrapStyles'\n\n/**\n * GrBadgeWrap — обёртка, вешающая на потомка счётчик или точку-индикатор.\n *\n * Число объявляется скринридеру: «3» без единицы измерения ничего не значит,\n * поэтому рядом с бейджем идёт визуально скрытая подпись.\n */\nexport interface GrBadgeWrapProps {\n value?: string | number\n /** Точка вместо числа: чистая декорация, если не задан `ariaLabel`. */\n dot?: boolean\n /** Порог: значения больше него рисуются как «{max}+». */\n max?: number\n /** Показывать ли нулевое значение. По умолчанию ноль скрыт. */\n showZero?: boolean\n tone?: GrBadgeWrapTone\n placement?: GrBadgeWrapPlacement\n /** Подпись счётчика для скринридера. Не задана — берётся из локали. */\n ariaLabel?: string\n /** Отмечать ли появление и рост **счётчика** анимацией. Точке анимировать нечего. */\n animate?: boolean\n}\n\nconst props = withDefaults(defineProps<GrBadgeWrapProps>(), {\n value: undefined,\n dot: false,\n max: undefined,\n showZero: false,\n tone: 'danger',\n placement: 'top-right',\n ariaLabel: undefined,\n animate: false,\n})\n\nconst { t } = useGranularityTranslations()\n\nconst showCount = computed(() => {\n if (props.dot || props.value === undefined) return false\n return props.value !== 0 || props.showZero\n})\n\n/** Видимый текст: «120» при `max=99` превращается в «99+». */\nconst displayValue = computed(() => formatBadgeValue(props.value ?? '', props.max))\n\n/**\n * Озвучивается настоящее значение, а не обрезанное: «99 плюс» пользователю\n * ничего не говорит, «120 непрочитанных» — говорит.\n */\nconst countLabel = computed(() => props.ariaLabel\n ?? t('gr.badgeWrap.count', '{count} new', { count: props.value, n: props.value }))\n\nconst pulsing = ref(false)\n\n/** Значение на экране: скрытый счётчик — то же самое, что его отсутствие. */\nconst visibleValue = computed(() => showCount.value ? props.value : undefined)\n\n/**\n * Пока анимация играет, новое изменение её не перезапускает: счётчик меняется\n * пачкой (пять писем за секунду), и «поп» на каждое превратился бы в мельтешение.\n * Серия за время анимации даёт ровно один «поп».\n *\n * Флаг отпускает `animationend`, а не таймер: длительность живёт в CSS-токене,\n * и число в скрипте было бы вторым источником правды. Под\n * `prefers-reduced-motion` событие тоже приходит — глобальный кламп сжимает\n * анимацию до `0.01ms`, а не выключает её (`docs/motion.md`).\n */\nwatch(visibleValue, (next, prev) => {\n if (!props.animate || pulsing.value) return\n if (isBadgeWrapPulse(prev, next)) pulsing.value = true\n})\n\n// Ушедший с экрана счётчик `animationend` уже не пришлёт, и без сброса\n// следующее его появление осталось бы без анимации навсегда.\nwatch(showCount, (visible) => {\n if (!visible) pulsing.value = false\n})\n</script>\n\n<template>\n <span :class=\"rootClass\">\n <slot />\n\n <template v-if=\"dot\">\n <span\n data-gr-badge-wrap-dot\n :class=\"[dotBaseClass, dotPlacementClass[placement], toneClass[tone]]\"\n aria-hidden=\"true\"\n />\n <!-- Точка сама по себе смысла не несёт; подпись появляется только когда\n потребитель объяснил, о чём она. -->\n <span v-if=\"ariaLabel\" data-gr-badge-wrap-label class=\"sr-only\">{{ ariaLabel }}</span>\n </template>\n\n <template v-else-if=\"showCount\">\n <span\n data-gr-badge-wrap-count\n :data-gr-badge-wrap-pop=\"pulsing || undefined\"\n :class=\"[countBaseClass, placementClass[placement], toneClass[tone]]\"\n aria-hidden=\"true\"\n @animationend=\"pulsing = false\"\n >{{ displayValue }}</span>\n <span data-gr-badge-wrap-label class=\"sr-only\">{{ countLabel }}</span>\n </template>\n </span>\n</template>\n\n<style>\n@keyframes gr-badge-wrap-pop {\n 0% {\n transform: scale(0.4);\n }\n 60% {\n transform: scale(1.12);\n }\n 100% {\n transform: scale(1);\n }\n}\n\n/*\n * `animation-fill-mode` не задан намеренно: под `prefers-reduced-motion` анимация\n * доигрывает за `0.01ms` и возвращает элемент в исходный кадр — бейдж на месте\n * и в нужном размере. Именно поэтому компоненту хватает глобального клампа и не\n * нужен собственный reduce-блок (`docs/motion.md`).\n */\n[data-gr-badge-wrap-count][data-gr-badge-wrap-pop] {\n animation: gr-badge-wrap-pop var(--gr-duration-base) var(--gr-ease-out);\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0CA,MAAM,QAAQ;EAWd,MAAM,EAAE,MAAM,2BAA2B;EAEzC,MAAM,YAAY,eAAe;GAC/B,IAAI,MAAM,OAAO,MAAM,UAAU,KAAA,GAAW,OAAO;GACnD,OAAO,MAAM,UAAU,KAAK,MAAM;EACpC,CAAC;;EAGD,MAAM,eAAe,eAAe,iBAAiB,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC;;;;;EAMlF,MAAM,aAAa,eAAe,MAAM,aACnC,EAAE,sBAAsB,eAAe;GAAE,OAAO,MAAM;GAAO,GAAG,MAAM;EAAM,CAAC,CAAC;EAEnF,MAAM,UAAU,IAAI,KAAK;;;;;;;;;;;EAezB,MAZqB,eAAe,UAAU,QAAQ,MAAM,QAAQ,KAAA,CAY9D,IAAe,MAAM,SAAS;GAClC,IAAI,CAAC,MAAM,WAAW,QAAQ,OAAO;GACrC,IAAI,iBAAiB,MAAM,IAAI,GAAG,QAAQ,QAAQ;EACpD,CAAC;EAID,MAAM,YAAY,YAAY;GAC5B,IAAI,CAAC,SAAS,QAAQ,QAAQ;EAChC,CAAC;;uBAIC,mBAwBO,QAAA,EAxBA,OAAK,eAAE,MAAA,SAAA,CAAS,EAAA,GAAA,CACrB,WAAQ,KAAA,QAAA,SAAA,GAEQ,QAAA,OAAA,UAAA,GAAhB,mBASW,UAAA,EAAA,KAAA,EAAA,GAAA,CART,mBAIE,QAAA;IAHA,0BAAA;IACC,OAAK,eAAA;KAAG,MAAA,YAAA;KAAc,MAAA,iBAAA,CAAiB,CAAC,QAAA;KAAY,MAAA,SAAA,CAAS,CAAC,QAAA;IAAI,CAAA;IACnE,eAAY;gBAIF,QAAA,aAAA,UAAA,GAAZ,mBAAsF,QAAtF,YAAsF,gBAAnB,QAAA,SAAS,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,EAAA,KAGzD,UAAA,SAAA,UAAA,GAArB,mBASW,UAAA,EAAA,KAAA,EAAA,GAAA,CART,mBAM0B,QAAA;IALxB,4BAAA;IACC,0BAAwB,QAAA,SAAW,KAAA;IACnC,OAAK,eAAA;KAAG,MAAA,cAAA;KAAgB,MAAA,cAAA,CAAc,CAAC,QAAA;KAAY,MAAA,SAAA,CAAS,CAAC,QAAA;IAAI,CAAA;IAClE,eAAY;IACX,gBAAY,OAAA,OAAA,OAAA,MAAA,WAAE,QAAA,QAAO;sBACpB,aAAA,KAAY,GAAA,IAAA,UAAA,GAChB,mBAAsE,QAAtE,YAAsE,gBAApB,WAAA,KAAU,GAAA,CAAA,CAAA,GAAA,EAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrButton-DCz-Wn-V.js","names":[],"sources":["../../../../src/components/GrButton/GrButton.vue","../../../../src/components/GrButton/GrButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, markRaw, ref, type Component } from 'vue'\n\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrButtonGroup } from '../GrButtonGroup/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nexport type { GrButtonSize, GrButtonTone, GrButtonVariant } from './grButtonStyles'\n\nimport {\n grButtonBaseClass,\n grButtonClass,\n type GrButtonSize,\n type GrButtonTone,\n type GrButtonVariant,\n} from './grButtonStyles'\n\nexport interface GrButtonProps {\n variant?: GrButtonVariant\n tone?: GrButtonTone\n size?: GrButtonSize\n loading?: boolean\n /** i18n: что именно грузится. `aria-busy` сам по себе часть AT не объявляет. */\n loadingText?: string\n disabled?: boolean\n square?: boolean\n /** Кнопка на всю ширину контейнера. */\n block?: boolean\n type?: 'button' | 'submit' | 'reset'\n ariaLabel?: string\n /** Полиморфизм: кастомный корневой тег/компонент (например, RouterLink). */\n as?: string | Component\n /** Рендерит кнопку как `<a href>` (если не задан `as`). */\n href?: string\n target?: string\n rel?: string\n external?: boolean\n}\n\nconst props = withDefaults(\n defineProps<GrButtonProps>(),\n {\n // `variant`/`tone`/`size`/`square` настраиваются через `GrConfigProvider`,\n // поэтому их дефолты живут в резолверах ниже, а не здесь: Vue подставил бы\n // дефолт до того, как компонент успеет заглянуть в конфиг.\n variant: undefined,\n tone: undefined,\n size: undefined,\n loading: false,\n loadingText: undefined,\n disabled: false,\n square: undefined,\n block: false,\n type: 'button',\n ariaLabel: undefined,\n as: undefined,\n href: undefined,\n target: undefined,\n rel: undefined,\n external: false,\n },\n)\n\n// Эффективные значения: проп кнопки → группа → `GrConfigProvider` → дефолт.\n// Группа ближе к кнопке, чем глобальный провайдер, поэтому она подставляется\n// как «локальное» значение — и побеждает конфиг, но не собственный проп.\nconst group = useGrButtonGroup()\n\nconst resolvedSize = useGrComponentSize(() => props.size ?? group?.size.value, { component: 'GrButton' })\nconst resolvedVariant = useGrComponentProp('GrButton', 'variant', () => props.variant ?? group?.variant.value, 'primary')\nconst resolvedTone = useGrComponentProp('GrButton', 'tone', () => props.tone ?? group?.tone.value, 'primary')\nconst isSquare = useGrComponentProp('GrButton', 'square', () => props.square, false)\n\nconst { t } = useGranularityTranslations()\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.button.loading', 'Loading…'))\n\n// Полиморфный корень: `as` → `<a href>` → `<button>`.\nconst isLink = computed(() => Boolean(props.as || props.href))\nconst renderAs = computed<string | Component>(() => {\n if (props.as) return typeof props.as === 'string' ? props.as : markRaw(props.as)\n return props.href ? 'a' : 'button'\n})\n\n// Неинтерактивно при явном `disabled` ИЛИ во время `loading`.\nconst blocked = computed(() => props.disabled || props.loading)\n\n// Ключевое: `loading` НЕ ставит нативный `disabled` (элемент бы выпал из фокуса и\n// скринридер потерял бы контекст) — вместо этого `aria-disabled` + перехват клика.\n// Нативный `disabled` (только у `<button>`) оставляем лишь для явного `disabled`.\nconst nativeDisabled = computed(() => (renderAs.value === 'button' && props.disabled) ? true : undefined)\nconst ariaDisabled = computed(() => (props.loading || (isLink.value && props.disabled)) ? 'true' : undefined)\n\nconst resolvedTarget = computed(() => props.target ?? (props.external ? '_blank' : undefined))\nconst resolvedRel = computed(() => props.rel ?? (resolvedTarget.value === '_blank' ? 'noopener noreferrer' : undefined))\n\nfunction onClickCapture(e: MouseEvent): void {\n // Блокируем и дефолт (submit/навигация), и внешние обработчики (в т.ч. по Enter/Space),\n // сохраняя фокус на элементе.\n if (blocked.value) {\n e.preventDefault()\n e.stopImmediatePropagation()\n }\n}\n\n// Корень динамический (`as` принимает и тег, и компонент), поэтому ref может\n// оказаться как элементом, так и инстансом — например роутерной ссылкой.\nconst rootEl = ref<HTMLElement | { $el?: HTMLElement } | null>(null)\n\n/**\n * Фокус на кнопку из кода. Нужен там, где окно обязано открываться с фокусом\n * на конкретном действии (`GrConfirmDialog`), и потребителю не должно быть\n * важно, тегом или компонентом отрисован корень.\n */\nfunction focus(): void {\n const root = rootEl.value\n const el = root instanceof HTMLElement ? root : root?.$el\n el?.focus?.()\n}\n\nfunction blur(): void {\n const root = rootEl.value\n const el = root instanceof HTMLElement ? root : root?.$el\n el?.blur?.()\n}\n\ndefineExpose({ focus, blur })\n\nconst className = computed(() => {\n return grButtonClass({\n variant: resolvedVariant.value,\n tone: resolvedTone.value,\n size: resolvedSize.value,\n square: isSquare.value,\n disabled: props.disabled,\n block: props.block,\n })\n})\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n ref=\"rootEl\"\n data-gr-button\n :data-gr-variant=\"resolvedVariant\"\n :data-gr-tone=\"resolvedTone\"\n :type=\"renderAs === 'button' ? props.type : undefined\"\n :disabled=\"nativeDisabled\"\n :href=\"isLink && !props.disabled ? props.href : undefined\"\n :target=\"isLink && !props.disabled ? resolvedTarget : undefined\"\n :rel=\"isLink && !props.disabled ? resolvedRel : undefined\"\n :aria-busy=\"props.loading ? 'true' : undefined\"\n :aria-disabled=\"ariaDisabled\"\n :aria-label=\"props.ariaLabel\"\n :tabindex=\"isLink && props.disabled ? -1 : undefined\"\n :class=\"[grButtonBaseClass, className, blocked ? 'cursor-not-allowed' : '']\"\n @click.capture=\"onClickCapture\"\n >\n <!-- Спиннер занимает место префикса: две иконки рядом читались бы как ошибка. -->\n <IconLoader v-if=\"props.loading\" class=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <slot v-else name=\"prefix\" />\n\n <slot />\n\n <slot name=\"suffix\" />\n\n <span v-if=\"props.loading\" class=\"sr-only\">{{ resolvedLoadingText }}</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, markRaw, ref, type Component } from 'vue'\n\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrButtonGroup } from '../GrButtonGroup/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nexport type { GrButtonSize, GrButtonTone, GrButtonVariant } from './grButtonStyles'\n\nimport {\n grButtonBaseClass,\n grButtonClass,\n type GrButtonSize,\n type GrButtonTone,\n type GrButtonVariant,\n} from './grButtonStyles'\n\nexport interface GrButtonProps {\n variant?: GrButtonVariant\n tone?: GrButtonTone\n size?: GrButtonSize\n loading?: boolean\n /** i18n: что именно грузится. `aria-busy` сам по себе часть AT не объявляет. */\n loadingText?: string\n disabled?: boolean\n square?: boolean\n /** Кнопка на всю ширину контейнера. */\n block?: boolean\n type?: 'button' | 'submit' | 'reset'\n ariaLabel?: string\n /** Полиморфизм: кастомный корневой тег/компонент (например, RouterLink). */\n as?: string | Component\n /** Рендерит кнопку как `<a href>` (если не задан `as`). */\n href?: string\n target?: string\n rel?: string\n external?: boolean\n}\n\nconst props = withDefaults(\n defineProps<GrButtonProps>(),\n {\n // `variant`/`tone`/`size`/`square` настраиваются через `GrConfigProvider`,\n // поэтому их дефолты живут в резолверах ниже, а не здесь: Vue подставил бы\n // дефолт до того, как компонент успеет заглянуть в конфиг.\n variant: undefined,\n tone: undefined,\n size: undefined,\n loading: false,\n loadingText: undefined,\n disabled: false,\n square: undefined,\n block: false,\n type: 'button',\n ariaLabel: undefined,\n as: undefined,\n href: undefined,\n target: undefined,\n rel: undefined,\n external: false,\n },\n)\n\n// Эффективные значения: проп кнопки → группа → `GrConfigProvider` → дефолт.\n// Группа ближе к кнопке, чем глобальный провайдер, поэтому она подставляется\n// как «локальное» значение — и побеждает конфиг, но не собственный проп.\nconst group = useGrButtonGroup()\n\nconst resolvedSize = useGrComponentSize(() => props.size ?? group?.size.value, { component: 'GrButton' })\nconst resolvedVariant = useGrComponentProp('GrButton', 'variant', () => props.variant ?? group?.variant.value, 'primary')\nconst resolvedTone = useGrComponentProp('GrButton', 'tone', () => props.tone ?? group?.tone.value, 'primary')\nconst isSquare = useGrComponentProp('GrButton', 'square', () => props.square, false)\n\nconst { t } = useGranularityTranslations()\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.button.loading', 'Loading…'))\n\n// Полиморфный корень: `as` → `<a href>` → `<button>`.\nconst isLink = computed(() => Boolean(props.as || props.href))\nconst renderAs = computed<string | Component>(() => {\n if (props.as) return typeof props.as === 'string' ? props.as : markRaw(props.as)\n return props.href ? 'a' : 'button'\n})\n\n// Неинтерактивно при явном `disabled` ИЛИ во время `loading`.\nconst blocked = computed(() => props.disabled || props.loading)\n\n// Ключевое: `loading` НЕ ставит нативный `disabled` (элемент бы выпал из фокуса и\n// скринридер потерял бы контекст) — вместо этого `aria-disabled` + перехват клика.\n// Нативный `disabled` (только у `<button>`) оставляем лишь для явного `disabled`.\nconst nativeDisabled = computed(() => (renderAs.value === 'button' && props.disabled) ? true : undefined)\nconst ariaDisabled = computed(() => (props.loading || (isLink.value && props.disabled)) ? 'true' : undefined)\n\nconst resolvedTarget = computed(() => props.target ?? (props.external ? '_blank' : undefined))\nconst resolvedRel = computed(() => props.rel ?? (resolvedTarget.value === '_blank' ? 'noopener noreferrer' : undefined))\n\nfunction onClickCapture(e: MouseEvent): void {\n // Блокируем и дефолт (submit/навигация), и внешние обработчики (в т.ч. по Enter/Space),\n // сохраняя фокус на элементе.\n if (blocked.value) {\n e.preventDefault()\n e.stopImmediatePropagation()\n }\n}\n\n// Корень динамический (`as` принимает и тег, и компонент), поэтому ref может\n// оказаться как элементом, так и инстансом — например роутерной ссылкой.\nconst rootEl = ref<HTMLElement | { $el?: HTMLElement } | null>(null)\n\n/**\n * Фокус на кнопку из кода. Нужен там, где окно обязано открываться с фокусом\n * на конкретном действии (`GrConfirmDialog`), и потребителю не должно быть\n * важно, тегом или компонентом отрисован корень.\n */\nfunction focus(): void {\n const root = rootEl.value\n const el = root instanceof HTMLElement ? root : root?.$el\n el?.focus?.()\n}\n\nfunction blur(): void {\n const root = rootEl.value\n const el = root instanceof HTMLElement ? root : root?.$el\n el?.blur?.()\n}\n\ndefineExpose({ focus, blur })\n\nconst className = computed(() => {\n return grButtonClass({\n variant: resolvedVariant.value,\n tone: resolvedTone.value,\n size: resolvedSize.value,\n square: isSquare.value,\n disabled: props.disabled,\n block: props.block,\n })\n})\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n ref=\"rootEl\"\n data-gr-button\n :data-gr-variant=\"resolvedVariant\"\n :data-gr-tone=\"resolvedTone\"\n :type=\"renderAs === 'button' ? props.type : undefined\"\n :disabled=\"nativeDisabled\"\n :href=\"isLink && !props.disabled ? props.href : undefined\"\n :target=\"isLink && !props.disabled ? resolvedTarget : undefined\"\n :rel=\"isLink && !props.disabled ? resolvedRel : undefined\"\n :aria-busy=\"props.loading ? 'true' : undefined\"\n :aria-disabled=\"ariaDisabled\"\n :aria-label=\"props.ariaLabel\"\n :tabindex=\"isLink && props.disabled ? -1 : undefined\"\n :class=\"[grButtonBaseClass, className, blocked ? 'cursor-not-allowed' : '']\"\n @click.capture=\"onClickCapture\"\n >\n <!-- Спиннер занимает место префикса: две иконки рядом читались бы как ошибка. -->\n <IconLoader v-if=\"props.loading\" class=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <slot v-else name=\"prefix\" />\n\n <slot />\n\n <slot name=\"suffix\" />\n\n <span v-if=\"props.loading\" class=\"sr-only\">{{ resolvedLoadingText }}</span>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCA,MAAM,QAAQ;EA2Bd,MAAM,QAAQ,iBAAiB;EAE/B,MAAM,eAAe,yBAAyB,MAAM,QAAQ,OAAO,KAAK,OAAO,EAAE,WAAW,WAAW,CAAC;EACxG,MAAM,kBAAkB,mBAAmB,YAAY,iBAAiB,MAAM,WAAW,OAAO,QAAQ,OAAO,SAAS;EACxH,MAAM,eAAe,mBAAmB,YAAY,cAAc,MAAM,QAAQ,OAAO,KAAK,OAAO,SAAS;EAC5G,MAAM,WAAW,mBAAmB,YAAY,gBAAgB,MAAM,QAAQ,KAAK;EAEnF,MAAM,EAAE,MAAM,2BAA2B;EACzC,MAAM,sBAAsB,eAAe,MAAM,eAAe,EAAE,qBAAqB,UAAU,CAAC;EAGlG,MAAM,SAAS,eAAe,QAAQ,MAAM,MAAM,MAAM,IAAI,CAAC;EAC7D,MAAM,WAAW,eAAmC;GAClD,IAAI,MAAM,IAAI,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE;GAC/E,OAAO,MAAM,OAAO,MAAM;EAC5B,CAAC;EAGD,MAAM,UAAU,eAAe,MAAM,YAAY,MAAM,OAAO;EAK9D,MAAM,iBAAiB,eAAgB,SAAS,UAAU,YAAY,MAAM,WAAY,OAAO,KAAA,CAAS;EACxG,MAAM,eAAe,eAAgB,MAAM,WAAY,OAAO,SAAS,MAAM,WAAa,SAAS,KAAA,CAAS;EAE5G,MAAM,iBAAiB,eAAe,MAAM,WAAW,MAAM,WAAW,WAAW,KAAA,EAAU;EAC7F,MAAM,cAAc,eAAe,MAAM,QAAQ,eAAe,UAAU,WAAW,wBAAwB,KAAA,EAAU;EAEvH,SAAS,eAAe,GAAqB;GAG3C,IAAI,QAAQ,OAAO;IACjB,EAAE,eAAe;IACjB,EAAE,yBAAyB;GAC7B;EACF;EAIA,MAAM,SAAS,IAAgD,IAAI;;;;;;EAOnE,SAAS,QAAc;GACrB,MAAM,OAAO,OAAO;GAEpB,CADW,gBAAgB,cAAc,OAAO,MAAM,IAAA,EAClD,QAAQ;EACd;EAEA,SAAS,OAAa;GACpB,MAAM,OAAO,OAAO;GAEpB,CADW,gBAAgB,cAAc,OAAO,MAAM,IAAA,EAClD,OAAO;EACb;EAEA,SAAa;GAAE;GAAO;EAAK,CAAC;EAE5B,MAAM,YAAY,eAAe;GAC/B,OAAO,cAAc;IACnB,SAAS,gBAAgB;IACzB,MAAM,aAAa;IACnB,MAAM,aAAa;IACnB,QAAQ,SAAS;IACjB,UAAU,MAAM;IAChB,OAAO,MAAM;GACf,CAAC;EACH,CAAC;;uBAIC,YA2BY,wBA1BL,SAAA,KAAQ,GAAA;aACT;IAAJ,KAAI;IACJ,kBAAA;IACC,mBAAiB,MAAA,eAAA;IACjB,gBAAc,MAAA,YAAA;IACd,MAAM,SAAA,UAAQ,WAAgB,MAAM,OAAO,KAAA;IAC3C,UAAU,eAAA;IACV,MAAM,OAAA,SAAM,CAAK,MAAM,WAAW,MAAM,OAAO,KAAA;IAC/C,QAAQ,OAAA,SAAM,CAAK,MAAM,WAAW,eAAA,QAAiB,KAAA;IACrD,KAAK,OAAA,SAAM,CAAK,MAAM,WAAW,YAAA,QAAc,KAAA;IAC/C,aAAW,MAAM,UAAO,SAAY,KAAA;IACpC,iBAAe,aAAA;IACf,cAAY,MAAM;IAClB,UAAU,OAAA,SAAU,MAAM,WAAQ,KAAQ,KAAA;IAC1C,OAAK,eAAA;KAAG,MAAA,iBAAA;KAAmB,UAAA;KAAW,QAAA,QAAO,uBAAA;IAAA,CAAA;IAC9B;;2BAGmE;KAAjE,MAAM,WAAA,UAAA,GAAxB,YAAmF,MAAA,qBAAA,GAAA;;MAAlD,OAAM;MAAuB,eAAY;WAC1E,WAA6B,KAAA,QAAA,UAAA,CAAA,GAAA,KAAA,GAAA,KAAA,GAAA,CAAA;KAE7B,WAAQ,KAAA,QAAA,SAAA;KAER,WAAsB,KAAA,QAAA,QAAA;KAEV,MAAM,WAAA,UAAA,GAAlB,mBAA2E,QAA3E,YAA2E,gBAA7B,oBAAA,KAAmB,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrButtonGroup-BQo5VQ_c.js","names":[],"sources":["../../../../src/components/GrButtonGroup/GrButtonGroup.vue","../../../../src/components/GrButtonGroup/GrButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton/grButtonStyles'\nimport { provideGrButtonGroup } from './context'\n\nexport type GrButtonGroupOrientation = 'horizontal' | 'vertical'\n\nexport interface GrButtonGroupProps {\n /** Доступное имя группы: без него кнопки читаются как несвязанные. */\n ariaLabel?: string\n orientation?: GrButtonGroupOrientation\n /**\n * Склеивать кнопки в один блок. `false` — обычный ряд с зазором: каждая\n * кнопка сохраняет свои радиусы и границы.\n */\n attached?: boolean\n /** Оформление, общее для кнопок группы. Проп самой кнопки сильнее. */\n size?: GrButtonSize\n variant?: GrButtonVariant\n tone?: GrButtonTone\n}\n\nconst props = withDefaults(defineProps<GrButtonGroupProps>(), {\n ariaLabel: undefined,\n orientation: 'horizontal',\n attached: true,\n size: undefined,\n variant: undefined,\n tone: undefined,\n})\n\nprovideGrButtonGroup({\n size: computed(() => props.size),\n variant: computed(() => props.variant),\n tone: computed(() => props.tone),\n})\n</script>\n\n<template>\n <div\n data-gr-button-group\n class=\"inline-flex\"\n :class=\"[\n orientation === 'vertical' ? 'flex-col items-stretch' : 'items-stretch',\n attached ? '' : 'gap-2',\n ]\"\n :data-orientation=\"orientation\"\n :data-attached=\"attached ? '' : undefined\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n/*\n * Склейка держится на понятии «звено группы» — прямом потомке, который сам\n * кнопка или содержит кнопку. Обёртка (тултип, `<span v-if>`, роутерная\n * ссылка) поэтому не разрывает ряд, а не-кнопочный потомок (разделитель,\n * подпись) в склейку не попадает и скругление себе не забирает.\n *\n * Края считаются от соседства звеньев, а не от `:first-child`/`:last-child`:\n * ведущий край гасится у звена, перед которым уже было звено, ведомый — у\n * звена, за которым звено ещё будет.\n */\n[data-gr-button-group] {\n --gr-button-group-radius: var(--gr-button-radius, 0.375rem);\n}\n\n[data-gr-button-group][data-attached] [data-gr-button] {\n border-radius: var(--gr-button-group-radius);\n position: relative;\n}\n\n/* Кнопка под курсором или с фокусом — поверх соседей: иначе её граница и\n фокус-кольцо срезаются наложением. */\n[data-gr-button-group] [data-gr-button]:hover,\n[data-gr-button-group] [data-gr-button]:focus-visible {\n z-index: 1;\n}\n\n/* ————— Горизонтальная группа. */\n\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :is([data-gr-button], :has([data-gr-button])) ~ :is([data-gr-button], :has([data-gr-button])) {\n margin-inline-start: -1px;\n}\n\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :is([data-gr-button], :has([data-gr-button])) ~ [data-gr-button],\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :is([data-gr-button], :has([data-gr-button])) ~ :has([data-gr-button]) [data-gr-button] {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n}\n\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > [data-gr-button]:has(~ :is([data-gr-button], :has([data-gr-button]))),\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :has([data-gr-button]):has(~ :is([data-gr-button], :has([data-gr-button]))) [data-gr-button] {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n}\n\n/* ————— Вертикальная группа: та же логика, другая ось. */\n\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :is([data-gr-button], :has([data-gr-button])) ~ :is([data-gr-button], :has([data-gr-button])) {\n margin-block-start: -1px;\n}\n\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :is([data-gr-button], :has([data-gr-button])) ~ [data-gr-button],\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :is([data-gr-button], :has([data-gr-button])) ~ :has([data-gr-button]) [data-gr-button] {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n}\n\n[data-gr-button-group][data-attached][data-orientation='vertical'] > [data-gr-button]:has(~ :is([data-gr-button], :has([data-gr-button]))),\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :has([data-gr-button]):has(~ :is([data-gr-button], :has([data-gr-button]))) [data-gr-button] {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton/grButtonStyles'\nimport { provideGrButtonGroup } from './context'\n\nexport type GrButtonGroupOrientation = 'horizontal' | 'vertical'\n\nexport interface GrButtonGroupProps {\n /** Доступное имя группы: без него кнопки читаются как несвязанные. */\n ariaLabel?: string\n orientation?: GrButtonGroupOrientation\n /**\n * Склеивать кнопки в один блок. `false` — обычный ряд с зазором: каждая\n * кнопка сохраняет свои радиусы и границы.\n */\n attached?: boolean\n /** Оформление, общее для кнопок группы. Проп самой кнопки сильнее. */\n size?: GrButtonSize\n variant?: GrButtonVariant\n tone?: GrButtonTone\n}\n\nconst props = withDefaults(defineProps<GrButtonGroupProps>(), {\n ariaLabel: undefined,\n orientation: 'horizontal',\n attached: true,\n size: undefined,\n variant: undefined,\n tone: undefined,\n})\n\nprovideGrButtonGroup({\n size: computed(() => props.size),\n variant: computed(() => props.variant),\n tone: computed(() => props.tone),\n})\n</script>\n\n<template>\n <div\n data-gr-button-group\n class=\"inline-flex\"\n :class=\"[\n orientation === 'vertical' ? 'flex-col items-stretch' : 'items-stretch',\n attached ? '' : 'gap-2',\n ]\"\n :data-orientation=\"orientation\"\n :data-attached=\"attached ? '' : undefined\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n/*\n * Склейка держится на понятии «звено группы» — прямом потомке, который сам\n * кнопка или содержит кнопку. Обёртка (тултип, `<span v-if>`, роутерная\n * ссылка) поэтому не разрывает ряд, а не-кнопочный потомок (разделитель,\n * подпись) в склейку не попадает и скругление себе не забирает.\n *\n * Края считаются от соседства звеньев, а не от `:first-child`/`:last-child`:\n * ведущий край гасится у звена, перед которым уже было звено, ведомый — у\n * звена, за которым звено ещё будет.\n */\n[data-gr-button-group] {\n --gr-button-group-radius: var(--gr-button-radius, 0.375rem);\n}\n\n[data-gr-button-group][data-attached] [data-gr-button] {\n border-radius: var(--gr-button-group-radius);\n position: relative;\n}\n\n/* Кнопка под курсором или с фокусом — поверх соседей: иначе её граница и\n фокус-кольцо срезаются наложением. */\n[data-gr-button-group] [data-gr-button]:hover,\n[data-gr-button-group] [data-gr-button]:focus-visible {\n z-index: 1;\n}\n\n/* ————— Горизонтальная группа. */\n\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :is([data-gr-button], :has([data-gr-button])) ~ :is([data-gr-button], :has([data-gr-button])) {\n margin-inline-start: -1px;\n}\n\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :is([data-gr-button], :has([data-gr-button])) ~ [data-gr-button],\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :is([data-gr-button], :has([data-gr-button])) ~ :has([data-gr-button]) [data-gr-button] {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n}\n\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > [data-gr-button]:has(~ :is([data-gr-button], :has([data-gr-button]))),\n[data-gr-button-group][data-attached][data-orientation='horizontal'] > :has([data-gr-button]):has(~ :is([data-gr-button], :has([data-gr-button]))) [data-gr-button] {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n}\n\n/* ————— Вертикальная группа: та же логика, другая ось. */\n\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :is([data-gr-button], :has([data-gr-button])) ~ :is([data-gr-button], :has([data-gr-button])) {\n margin-block-start: -1px;\n}\n\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :is([data-gr-button], :has([data-gr-button])) ~ [data-gr-button],\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :is([data-gr-button], :has([data-gr-button])) ~ :has([data-gr-button]) [data-gr-button] {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n}\n\n[data-gr-button-group][data-attached][data-orientation='vertical'] > [data-gr-button]:has(~ :is([data-gr-button], :has([data-gr-button]))),\n[data-gr-button-group][data-attached][data-orientation='vertical'] > :has([data-gr-button]):has(~ :is([data-gr-button], :has([data-gr-button]))) [data-gr-button] {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;EAuBA,MAAM,QAAQ;EASd,qBAAqB;GACnB,MAAM,eAAe,MAAM,IAAI;GAC/B,SAAS,eAAe,MAAM,OAAO;GACrC,MAAM,eAAe,MAAM,IAAI;EACjC,CAAC;;uBAIC,mBAaM,OAAA;IAZJ,wBAAA;IACA,OAAK,eAAA,CAAC,eAAa,CACH,QAAA,gBAAW,aAAA,2BAAA,iBAAoE,QAAA,WAAQ,KAAA,OAAA,CAAA,CAAA;IAItG,oBAAkB,QAAA;IAClB,iBAAe,QAAA,WAAQ,KAAQ,KAAA;IAChC,MAAK;IACJ,cAAY,QAAA;OAEb,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,IAAA,UAAA"}