@feugene/granularity 0.31.0 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/README.md +0 -1
  2. package/dist/chunks/adapter-CAOxmn2G.js.map +1 -1
  3. package/dist/chunks/{config-BHcS5Cwm.js → config-87fszqnD.js} +1 -1
  4. package/dist/chunks/{config-BHcS5Cwm.js.map → config-87fszqnD.js.map} +1 -1
  5. package/dist/chunks/config-A-bBiotF.js.map +1 -1
  6. package/dist/chunks/config-B3J0alyY.js.map +1 -1
  7. package/dist/chunks/config-BhfNgyWL.js.map +1 -1
  8. package/dist/chunks/config-Bjn30smH.js.map +1 -1
  9. package/dist/chunks/config-BqT6PMzO.js.map +1 -1
  10. package/dist/chunks/config-C0Ea9ePP.js.map +1 -1
  11. package/dist/chunks/config-C0Eu8jpG.js.map +1 -1
  12. package/dist/chunks/config-CDat1wXf.js.map +1 -1
  13. package/dist/chunks/config-CQLNjc9X2.js.map +1 -1
  14. package/dist/chunks/config-CbEbLyDE.js.map +1 -1
  15. package/dist/chunks/{config-jyr6AIZt2.js → config-CuQmUkMw2.js} +2 -2
  16. package/dist/chunks/{config-jyr6AIZt2.js.map → config-CuQmUkMw2.js.map} +1 -1
  17. package/dist/chunks/config-DeyT9dJl.js.map +1 -1
  18. package/dist/chunks/config-DnF472Ox.js.map +1 -1
  19. package/dist/chunks/config-DwABJHEo.js.map +1 -1
  20. package/dist/chunks/{config-tiUtEn4Q.js → config-E1cTyRVV.js} +1 -1
  21. package/dist/chunks/{config-tiUtEn4Q.js.map → config-E1cTyRVV.js.map} +1 -1
  22. package/dist/chunks/config-MmPym-Z72.js.map +1 -1
  23. package/dist/chunks/config-T3-DMXYm.js.map +1 -1
  24. package/dist/chunks/config-Yy67uWHY.js.map +1 -1
  25. package/dist/chunks/config-_jbgsFpw.js.map +1 -1
  26. package/dist/chunks/config-g8-JGZ_Y.js.map +1 -1
  27. package/dist/chunks/config-mlheIMtV.js.map +1 -1
  28. package/dist/chunks/config-rLkc9Zn2.js.map +1 -1
  29. package/dist/chunks/config-uzUuosv7.js.map +1 -1
  30. package/dist/chunks/context-CTbuwcYs.js.map +1 -1
  31. package/dist/chunks/deltaTone-B5OoS1vo.js.map +1 -1
  32. package/dist/chunks/es-Do2CNrC2.js.map +1 -1
  33. package/dist/chunks/fileValidation-D5TFBhbv.js.map +1 -1
  34. package/dist/chunks/focusables-BkzK42SQ.js.map +1 -1
  35. package/dist/chunks/{generated-DJ2dJ__C.js → generated-CSyPsWtM.js} +1 -1
  36. package/dist/chunks/{generated-DJ2dJ__C.js.map → generated-CSyPsWtM.js.map} +1 -1
  37. package/dist/chunks/grBadgeStyles-D8Mz73B5.js.map +1 -1
  38. package/dist/chunks/{grButtonStyles-B_-YRE__.js → grButtonStyles-B3wRWW_i.js} +4 -4
  39. package/dist/chunks/grButtonStyles-B3wRWW_i.js.map +1 -0
  40. package/dist/chunks/grLinkStyles-FME_qMpi.js.map +1 -1
  41. package/dist/chunks/grLoadingStyles-CnQaf5ry.js.map +1 -1
  42. package/dist/chunks/{grRadioStyles-D4gnU65n.js → grRadioStyles-DGL2QLy6.js} +1 -1
  43. package/dist/chunks/{grRadioStyles-D4gnU65n.js.map → grRadioStyles-DGL2QLy6.js.map} +1 -1
  44. package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +1 -1
  45. package/dist/chunks/{grSliderStyles-B5uNxNJq.js → grSliderStyles-kaz6VXZ3.js} +9 -1
  46. package/dist/chunks/grSliderStyles-kaz6VXZ3.js.map +1 -0
  47. package/dist/chunks/{granular-provider-yJNX1g1G.js → granular-provider-Da2CJO6j.js} +3 -3
  48. package/dist/chunks/{granular-provider-yJNX1g1G.js.map → granular-provider-Da2CJO6j.js.map} +1 -1
  49. package/dist/chunks/granularityI18n-BC5zsj_d.js.map +1 -1
  50. package/dist/chunks/icon-Dag2tIMs.js.map +1 -1
  51. package/dist/chunks/inert-DIHCCn_1.js.map +1 -1
  52. package/dist/chunks/keyboard-DE2yhz_b.js.map +1 -1
  53. package/dist/chunks/numericStep-Bq2lj4ew.js.map +1 -1
  54. package/dist/chunks/optionFilter-DYw9XPPx.js.map +1 -1
  55. package/dist/chunks/overlayStack-C8cfsqXa.js.map +1 -1
  56. package/dist/chunks/portalRoot-CVhCgdG6.js.map +1 -1
  57. package/dist/chunks/renamedProp-BwcN-SJg.js.map +1 -1
  58. package/dist/chunks/{safelist-BOq5Li7p.js → safelist-DxkMj5IW.js} +1 -1
  59. package/dist/chunks/{safelist-BOq5Li7p.js.map → safelist-DxkMj5IW.js.map} +1 -1
  60. package/dist/chunks/slotNodes-DxNXpC7v.js.map +1 -1
  61. package/dist/chunks/useControlAddons-asUr10Pf.js.map +1 -1
  62. package/dist/chunks/useControlledOpen-DV3SqLTP.js.map +1 -1
  63. package/dist/chunks/useDragSort-0l47h4yb.js.map +1 -1
  64. package/dist/chunks/useFilePreviews-DnwgdyBs.js.map +1 -1
  65. package/dist/chunks/useFocusWithin-BwHE0rzm.js.map +1 -1
  66. package/dist/chunks/useModalOverlay-Cv5hF63W.js.map +1 -1
  67. package/dist/chunks/usePortalTarget-C0p-mhaM.js.map +1 -1
  68. package/dist/chunks/validation-5HhpanPP.js.map +1 -1
  69. package/dist/components/GrAlert/chunks/GrAlert-FAVf-ksH.js.map +1 -1
  70. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-X6RlmiJQ.js → GrAutocomplete-rYEY15UI.js} +1 -1
  71. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-rYEY15UI.js.map +1 -0
  72. package/dist/components/GrAutocomplete/index.js +1 -1
  73. package/dist/components/GrAutocomplete/styles.css +1 -1
  74. package/dist/components/GrAvatar/chunks/GrAvatar-CNmLmgcr.js.map +1 -1
  75. package/dist/components/GrBadge/chunks/{GrBadge-BsoocpF-.js → GrBadge-D2MfThPz.js} +1 -1
  76. package/dist/components/GrBadge/chunks/GrBadge-D2MfThPz.js.map +1 -0
  77. package/dist/components/GrBadge/index.js +1 -1
  78. package/dist/components/GrBadge/styles.css +1 -1
  79. package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-B-A3PRkE.js.map +1 -1
  80. package/dist/components/GrBottomNav/chunks/GrBottomNav-wtFatLjB.js.map +1 -1
  81. package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-DRDNan_n.js.map +1 -1
  82. package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js → GrButton-7GP0zObj.js} +1 -1
  83. package/dist/components/GrButton/chunks/GrButton-7GP0zObj.js.map +1 -0
  84. package/dist/components/GrButton/index.js +3 -3
  85. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-CKyiAV8K.js.map +1 -1
  86. package/dist/components/GrCard/chunks/GrCard-BttXz1pQ.js.map +1 -1
  87. package/dist/components/GrCheckbox/chunks/GrCheckbox-CEcHIsXM.js.map +1 -1
  88. package/dist/components/GrCheckboxGroup/chunks/GrCheckboxGroup-BOwrVcFr.js.map +1 -1
  89. package/dist/components/GrChip/chunks/GrChip-C0vU69tf.js.map +1 -1
  90. package/dist/components/GrChipGroup/chunks/GrChipGroup-C78Vcwos.js.map +1 -1
  91. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-Dkvkr91t.js → GrCodeBlock-Dp0eEBTZ.js} +1 -1
  92. package/dist/components/GrCodeBlock/chunks/GrCodeBlock-Dp0eEBTZ.js.map +1 -0
  93. package/dist/components/GrCodeBlock/index.js +1 -1
  94. package/dist/components/GrCollapse/chunks/GrCollapse-BlW6ybdL.js.map +1 -1
  95. package/dist/components/GrColorPicker/chunks/{GrColorPicker-C7ifyQ0R.js → GrColorPicker-B20gQ6AR.js} +1 -1
  96. package/dist/components/GrColorPicker/chunks/GrColorPicker-B20gQ6AR.js.map +1 -0
  97. package/dist/components/GrColorPicker/index.js +1 -1
  98. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-C5abL2Y7.js → GrCommandPalette-Cd0Yo3Dw.js} +1 -1
  99. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-Cd0Yo3Dw.js.map +1 -0
  100. package/dist/components/GrCommandPalette/index.js +1 -1
  101. package/dist/components/GrCommandPalette/styles.css +1 -1
  102. package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BF7R3hgg.js.map +1 -1
  103. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-B9mIU78E.js → GrConfirmDialog-DOsbRhoZ.js} +3 -3
  104. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-DOsbRhoZ.js.map +1 -0
  105. package/dist/components/GrConfirmDialog/index.js +1 -1
  106. package/dist/components/GrContextMenu/chunks/GrContextMenu-_FKwQvuQ.js.map +1 -1
  107. package/dist/components/GrDataTable/chunks/{GrDataTable-C8a9_nCg.js → GrDataTable-CAGgRk27.js} +5 -3
  108. package/dist/components/GrDataTable/chunks/GrDataTable-CAGgRk27.js.map +1 -0
  109. package/dist/components/GrDataTable/index.js +1 -1
  110. package/dist/components/GrDelta/chunks/GrDelta-BZwkgQW-.js.map +1 -1
  111. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-BMeCgFV3.js.map +1 -1
  112. package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js → GrDialog-S1Vl-OhX.js} +1 -1
  113. package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js.map → GrDialog-S1Vl-OhX.js.map} +1 -1
  114. package/dist/components/GrDialog/index.js +1 -1
  115. package/dist/components/GrDialogService/chunks/{GrDialogService-6s2cQ-_6.js → GrDialogService-Cuhc4YLp.js} +4 -4
  116. package/dist/components/GrDialogService/chunks/GrDialogService-Cuhc4YLp.js.map +1 -0
  117. package/dist/components/GrDialogService/index.js +1 -1
  118. package/dist/components/GrDivider/chunks/GrDivider-BXavGcMX.js.map +1 -1
  119. package/dist/components/GrDrawer/chunks/{GrDrawer-B7PiAcbU.js → GrDrawer-C0YqVE9e.js} +1 -1
  120. package/dist/components/GrDrawer/chunks/GrDrawer-C0YqVE9e.js.map +1 -0
  121. package/dist/components/GrDrawer/index.js +1 -1
  122. package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js.map +1 -1
  123. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-GOzuAXsi.js.map +1 -1
  124. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-B33ZzwfB.js.map +1 -1
  125. package/dist/components/GrFilePreview/chunks/GrFilePreview-C9Z7hMK8.js.map +1 -1
  126. package/dist/components/GrFileUpload/chunks/GrFileUpload-NsHkRRIa.js.map +1 -1
  127. package/dist/components/GrForm/chunks/GrForm-CrnfCsgj.js.map +1 -1
  128. package/dist/components/GrFormField/chunks/GrFormField-DSylU7IU.js.map +1 -1
  129. package/dist/components/GrFormFile/chunks/{GrFormFile-CYtXU9HV.js → GrFormFile-B1jGZyIj.js} +2 -2
  130. package/dist/components/GrFormFile/chunks/GrFormFile-B1jGZyIj.js.map +1 -0
  131. package/dist/components/GrFormFile/index.js +1 -1
  132. package/dist/components/GrFormFile/styles.css +1 -1
  133. package/dist/components/GrIcon/chunks/GrIcon-Dmf0c9ba.js.map +1 -1
  134. package/dist/components/GrImageViewer/chunks/GrImageViewer-CyVN6Zpc.js.map +1 -1
  135. package/dist/components/GrInput/chunks/GrInput-BLywRZUN.js.map +1 -1
  136. package/dist/components/GrInputTag/chunks/GrInputTag-Cj4A9Xgs.js.map +1 -1
  137. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-Cy2bknHO.js → GrJsonViewer-BCZ7lE2k.js} +2 -2
  138. package/dist/components/GrJsonViewer/chunks/GrJsonViewer-BCZ7lE2k.js.map +1 -0
  139. package/dist/components/GrJsonViewer/index.js +1 -1
  140. package/dist/components/GrKbd/chunks/GrKbd-D2ga1H9i.js.map +1 -1
  141. package/dist/components/GrLink/chunks/GrLink-DYXw3W9w.js.map +1 -1
  142. package/dist/components/GrList/chunks/{GrList-DmT4qzLO.js → GrList-B_CRJB4c.js} +1 -1
  143. package/dist/components/GrList/chunks/GrList-B_CRJB4c.js.map +1 -0
  144. package/dist/components/GrList/index.js +1 -1
  145. package/dist/components/GrList/styles.css +1 -1
  146. package/dist/components/GrModal/chunks/GrModal-CCoWZuIl.js.map +1 -1
  147. package/dist/components/GrNavbar/chunks/{GrNavbar-C5EjMKaC.js → GrNavbar-_rtHKwTf.js} +1 -1
  148. package/dist/components/GrNavbar/chunks/{GrNavbar-C5EjMKaC.js.map → GrNavbar-_rtHKwTf.js.map} +1 -1
  149. package/dist/components/GrNavbar/index.js +1 -1
  150. package/dist/components/GrNumberInput/chunks/GrNumberInput-DQ59YICu.js.map +1 -1
  151. package/dist/components/GrPagination/chunks/{GrPagination-Cw3R21dp.js → GrPagination-B_kAhoAV.js} +2 -2
  152. package/dist/components/GrPagination/chunks/{GrPagination-Cw3R21dp.js.map → GrPagination-B_kAhoAV.js.map} +1 -1
  153. package/dist/components/GrPagination/index.js +1 -1
  154. package/dist/components/GrPopover/chunks/GrPopover-CofG-Tfe.js.map +1 -1
  155. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-DLcl1xiq.js → GrPromptDialog-BuJQMZLM.js} +4 -4
  156. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-BuJQMZLM.js.map +1 -0
  157. package/dist/components/GrPromptDialog/index.js +1 -1
  158. package/dist/components/GrRadio/chunks/{GrRadio-C_rESc5g.js → GrRadio-Aq3v8XgG.js} +1 -1
  159. package/dist/components/GrRadio/chunks/GrRadio-Aq3v8XgG.js.map +1 -0
  160. package/dist/components/GrRadio/index.js +2 -2
  161. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CdQO4G-U.js → GrRadioGroup-y9LVdTWh.js} +1 -1
  162. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-y9LVdTWh.js.map +1 -0
  163. package/dist/components/GrRadioGroup/index.js +1 -1
  164. package/dist/components/GrRating/chunks/GrRating-IEiTOdnF.js.map +1 -1
  165. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-C_skObtv.js → GrResponseErrorBanner-CBVRpgIG.js} +1 -1
  166. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-C_skObtv.js.map → GrResponseErrorBanner-CBVRpgIG.js.map} +1 -1
  167. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DudGc9LO.js → GrResponseErrorBanner-DMeOWlzj.js} +1 -1
  168. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DMeOWlzj.js.map +1 -0
  169. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  170. package/dist/components/GrSegmented/chunks/GrSegmented-CB-VR-yL.js.map +1 -1
  171. package/dist/components/GrSelect/chunks/{GrSelect-BiUrSSxc.js → GrSelect-gCeRFgQo.js} +1 -1
  172. package/dist/components/GrSelect/chunks/GrSelect-gCeRFgQo.js.map +1 -0
  173. package/dist/components/GrSelect/index.js +1 -1
  174. package/dist/components/GrSelect/styles.css +1 -1
  175. package/dist/components/GrSidebar/chunks/{GrSidebar-B8EQIlfW.js → GrSidebar-alkwBUzL.js} +1 -1
  176. package/dist/components/GrSidebar/chunks/GrSidebar-alkwBUzL.js.map +1 -0
  177. package/dist/components/GrSidebar/index.js +1 -1
  178. package/dist/components/GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js.map +1 -1
  179. package/dist/components/GrSlider/chunks/{GrSlider-DJfeEl4h.js → GrSlider-DTTrZTHK.js} +3 -3
  180. package/dist/components/GrSlider/chunks/GrSlider-DTTrZTHK.js.map +1 -0
  181. package/dist/components/GrSlider/index.js +2 -2
  182. package/dist/components/GrSortableList/chunks/GrSortableList-HyKX--rO.js.map +1 -1
  183. package/dist/components/GrSplitter/chunks/GrSplitter-MltjaADn.js.map +1 -1
  184. package/dist/components/GrStatistic/chunks/GrStatistic-qHVasK37.js.map +1 -1
  185. package/dist/components/GrSteps/chunks/GrSteps-C2jxpZoN.js.map +1 -1
  186. package/dist/components/GrSwitch/chunks/{GrSwitch-g_OOSkh7.js → GrSwitch-7Oe5QfQZ.js} +1 -1
  187. package/dist/components/GrSwitch/chunks/GrSwitch-7Oe5QfQZ.js.map +1 -0
  188. package/dist/components/GrSwitch/index.js +1 -1
  189. package/dist/components/GrTabPanels/chunks/GrTabPanels-BZQS8-JM.js.map +1 -1
  190. package/dist/components/GrTable/chunks/GrTable-Zcdz2St_.js.map +1 -1
  191. package/dist/components/GrTextarea/chunks/{GrTextarea-KREK75Bu.js → GrTextarea-DinXG12-.js} +10 -10
  192. package/dist/components/GrTextarea/chunks/GrTextarea-DinXG12-.js.map +1 -0
  193. package/dist/components/GrTextarea/index.js +1 -1
  194. package/dist/components/GrTimeline/chunks/GrTimeline-BiHpArsg.js.map +1 -1
  195. package/dist/components/GrToaster/chunks/{GrToaster-Dp7-4B8k.js → GrToaster-COXIBKzq.js} +2 -2
  196. package/dist/components/GrToaster/chunks/GrToaster-COXIBKzq.js.map +1 -0
  197. package/dist/components/GrToaster/index.js +1 -1
  198. package/dist/components/GrToaster/styles.css +1 -1
  199. package/dist/components/GrTooltip/chunks/GrTooltip-uYETdbCD.js.map +1 -1
  200. package/dist/components/GrTree/chunks/{GrTree-DC89I4AP.js → GrTree-Ci6CZfKR.js} +5 -5
  201. package/dist/components/GrTree/chunks/GrTree-Ci6CZfKR.js.map +1 -0
  202. package/dist/components/GrTree/index.js +1 -1
  203. package/dist/components/GrTree/styles.css +1 -1
  204. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-iJnMl2t9.js → GrTreeSelect-BHwgGJcT.js} +1 -1
  205. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-BHwgGJcT.js.map +1 -0
  206. package/dist/components/GrTreeSelect/index.js +1 -1
  207. package/dist/composables/useAnnouncer.js.map +1 -1
  208. package/dist/composables/useComboboxNavigation.js.map +1 -1
  209. package/dist/composables/useDragGesture.js.map +1 -1
  210. package/dist/composables/useFocusTrap.js.map +1 -1
  211. package/dist/composables/useOverlayLayer.js.map +1 -1
  212. package/dist/composables/useRovingFocus.js.map +1 -1
  213. package/dist/composables/useTheme.js.map +1 -1
  214. package/dist/composables/useToast.js.map +1 -1
  215. package/dist/composables/useVirtualList.js.map +1 -1
  216. package/dist/directives/autofocus.js.map +1 -1
  217. package/dist/directives/autosize.js.map +1 -1
  218. package/dist/directives/clickOutside.js.map +1 -1
  219. package/dist/directives/dropzone.js.map +1 -1
  220. package/dist/directives/hotkey.js.map +1 -1
  221. package/dist/directives/loading.js.map +1 -1
  222. package/dist/granular-provider-node.js +1 -1
  223. package/dist/granular-provider.js +1 -1
  224. package/dist/index.js +33 -33
  225. package/dist/testing.js +2 -2
  226. package/dist/testing.js.map +1 -1
  227. package/dist/theme-apply.js.map +1 -1
  228. package/dist/theme.js +1 -1
  229. package/dist/theme.js.map +1 -1
  230. package/dist/tokens.js +1 -1
  231. package/dist/types/components/GrCodeBlock/tokenizeJson.d.ts +1 -3
  232. package/dist/types/components/GrImageViewer/GrImageViewer.vue.d.ts +1 -1
  233. package/dist/types/components/GrInput/GrInput.vue.d.ts +24 -24
  234. package/dist/types/components/GrSlider/grSliderStyles.d.ts +9 -1
  235. package/dist/types/components/GrTabPanels/GrTabPanel.vue.d.ts +3 -3
  236. package/dist/vue/index.js.map +1 -1
  237. package/dist/web-types.json +1 -1
  238. package/package.json +3 -3
  239. package/dist/chunks/grButtonStyles-B_-YRE__.js.map +0 -1
  240. package/dist/chunks/grSliderStyles-B5uNxNJq.js.map +0 -1
  241. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-X6RlmiJQ.js.map +0 -1
  242. package/dist/components/GrBadge/chunks/GrBadge-BsoocpF-.js.map +0 -1
  243. package/dist/components/GrButton/chunks/GrButton-Bw2AH4uX.js.map +0 -1
  244. package/dist/components/GrCodeBlock/chunks/GrCodeBlock-Dkvkr91t.js.map +0 -1
  245. package/dist/components/GrColorPicker/chunks/GrColorPicker-C7ifyQ0R.js.map +0 -1
  246. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-C5abL2Y7.js.map +0 -1
  247. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-B9mIU78E.js.map +0 -1
  248. package/dist/components/GrDataTable/chunks/GrDataTable-C8a9_nCg.js.map +0 -1
  249. package/dist/components/GrDialogService/chunks/GrDialogService-6s2cQ-_6.js.map +0 -1
  250. package/dist/components/GrDrawer/chunks/GrDrawer-B7PiAcbU.js.map +0 -1
  251. package/dist/components/GrFormFile/chunks/GrFormFile-CYtXU9HV.js.map +0 -1
  252. package/dist/components/GrJsonViewer/chunks/GrJsonViewer-Cy2bknHO.js.map +0 -1
  253. package/dist/components/GrList/chunks/GrList-DmT4qzLO.js.map +0 -1
  254. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-DLcl1xiq.js.map +0 -1
  255. package/dist/components/GrRadio/chunks/GrRadio-C_rESc5g.js.map +0 -1
  256. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-CdQO4G-U.js.map +0 -1
  257. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DudGc9LO.js.map +0 -1
  258. package/dist/components/GrSelect/chunks/GrSelect-BiUrSSxc.js.map +0 -1
  259. package/dist/components/GrSidebar/chunks/GrSidebar-B8EQIlfW.js.map +0 -1
  260. package/dist/components/GrSlider/chunks/GrSlider-DJfeEl4h.js.map +0 -1
  261. package/dist/components/GrSwitch/chunks/GrSwitch-g_OOSkh7.js.map +0 -1
  262. package/dist/components/GrTextarea/chunks/GrTextarea-KREK75Bu.js.map +0 -1
  263. package/dist/components/GrToaster/chunks/GrToaster-Dp7-4B8k.js.map +0 -1
  264. package/dist/components/GrTree/chunks/GrTree-DC89I4AP.js.map +0 -1
  265. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-iJnMl2t9.js.map +0 -1
@@ -2,10 +2,10 @@ import type { GrComponentSize } from '../shared/sizes';
2
2
  import type { InputHTMLAttributes } from 'vue';
3
3
  export interface GrInputProps {
4
4
  /**
5
- * Значение поля. Необязательное: без `v-model` поле рисуется пустым.
6
- * Дефолт — пустая строка, а не `undefined`: длина значения читается
7
- * напрямую (`showClear`), и `undefined` уронил бы рендер.
8
- */
5
+ * Значение поля. Необязательное: без `v-model` поле рисуется пустым.
6
+ * Дефолт — пустая строка, а не `undefined`: длина значения читается
7
+ * напрямую (`showClear`), и `undefined` уронил бы рендер.
8
+ */
9
9
  modelValue?: string;
10
10
  type?: 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url';
11
11
  placeholder?: string;
@@ -32,10 +32,10 @@ export interface GrInputProps {
32
32
  /** Показывать счётчик символов (`len` или `len/maxlength`). */
33
33
  showCount?: boolean;
34
34
  /**
35
- * Фоновая работа по полю (проверка занятости логина, автосохранение):
36
- * спиннер в trailing-области + `aria-busy`. Ввод не блокируется —
37
- * для этого есть `disabled`/`readonly`.
38
- */
35
+ * Фоновая работа по полю (проверка занятости логина, автосохранение):
36
+ * спиннер в trailing-области + `aria-busy`. Ввод не блокируется —
37
+ * для этого есть `disabled`/`readonly`.
38
+ */
39
39
  loading?: boolean;
40
40
  /** Кнопка показать/скрыть пароль (только при `type="password"`). */
41
41
  passwordToggle?: boolean;
@@ -48,25 +48,25 @@ export interface GrInputProps {
48
48
  suffixMinWidth?: string;
49
49
  suffixMaxWidth?: string;
50
50
  /**
51
- * Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину
52
- * (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю
53
- * (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»
54
- * под контент (в пределах min/max), а излишек клипается оболочкой.
55
- */
51
+ * Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину
52
+ * (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю
53
+ * (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»
54
+ * под контент (в пределах min/max), а излишек клипается оболочкой.
55
+ */
56
56
  prefixFixed?: boolean;
57
57
  suffixFixed?: boolean;
58
58
  /**
59
- * Как выглядят аддоны `#prefix`/`#suffix`.
60
- *
61
- * `segment` (по умолчанию) — отдельный отсек, отрезанный рамкой и выровненный
62
- * по ступени размера: так поле с «₽» и поле с «USD» стоят в колонку.
63
- * `inline` — украшение внутри рамки: ни разделителя, ни своей ширины.
64
- *
65
- * Разница не косметическая. Поисковая строка с лупой в сегменте читается
66
- * составным элементом — полем с приклеенной кнопкой, — а не одним полем;
67
- * именно поэтому иконку внутри рамки нельзя было выразить аддоном, и
68
- * потребители отказывались от неё вовсе.
69
- */
59
+ * Как выглядят аддоны `#prefix`/`#suffix`.
60
+ *
61
+ * `segment` (по умолчанию) — отдельный отсек, отрезанный рамкой и выровненный
62
+ * по ступени размера: так поле с «₽» и поле с «USD» стоят в колонку.
63
+ * `inline` — украшение внутри рамки: ни разделителя, ни своей ширины.
64
+ *
65
+ * Разница не косметическая. Поисковая строка с лупой в сегменте читается
66
+ * составным элементом — полем с приклеенной кнопкой, — а не одним полем;
67
+ * именно поэтому иконку внутри рамки нельзя было выразить аддоном, и
68
+ * потребители отказывались от неё вовсе.
69
+ */
70
70
  addon?: 'segment' | 'inline';
71
71
  }
72
72
  export interface GrInputEmits {
@@ -26,7 +26,15 @@ export declare const sliderTrackWidthBySize: Record<GrSliderSize, string>;
26
26
  export declare const sliderTrackVerticalLengthClass = "h-[var(--gr-slider-length,10rem)]";
27
27
  export declare const sliderThumbSizeBySize: Record<GrSliderSize, string>;
28
28
  export declare const sliderPaddingBySize: Record<GrSliderSize, string>;
29
- export declare const sliderRailClass = "absolute inset-0 rounded-[var(--gr-radius-full)] bg-[var(--gr-slider-rail,color-mix(in_srgb,var(--gr-muted)_45%,transparent))]";
29
+ /**
30
+ * Дорожка — `--gr-brd`, а не полупрозрачный `--gr-muted`.
31
+ *
32
+ * Прежний дефолт (`color-mix(--gr-muted 45%, transparent)`) на светлой теме
33
+ * давал #f1f5f9 при 45 % поверх почти такой же поверхности: непройденная часть
34
+ * шкалы не читалась вовсе, и слайдер выглядел точкой на пустом месте — по нему
35
+ * нельзя было понять ни диапазон, ни положение внутри него.
36
+ */
37
+ export declare const sliderRailClass = "absolute inset-0 rounded-[var(--gr-radius-full)] bg-[var(--gr-slider-rail,var(--gr-brd))]";
30
38
  export declare const sliderFillBaseClass = "absolute rounded-[var(--gr-radius-full)]";
31
39
  /** Заливка растёт вдоль дорожки: по горизонтали — влево-вправо, по вертикали — снизу вверх. */
32
40
  export declare const sliderFillOrientationClass: Record<GrSliderOrientation, string>;
@@ -2,9 +2,9 @@ export interface GrTabPanelProps {
2
2
  value: string;
3
3
  keepAlive?: boolean;
4
4
  /**
5
- * Не монтировать содержимое, пока панель не показали впервые. Имеет смысл
6
- * вместе с `keepAlive`: без него неактивная панель и так не в DOM.
7
- */
5
+ * Не монтировать содержимое, пока панель не показали впервые. Имеет смысл
6
+ * вместе с `keepAlive`: без него неактивная панель и так не в DOM.
7
+ */
8
8
  lazy?: boolean;
9
9
  }
10
10
  type __VLS_Slots = {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/vue/plugin.ts"],"sourcesContent":["import type { App, Component, Directive, Plugin } from 'vue'\n\n/**\n * Лёгкий контейнер, который может быть зарегистрирован в Vue:\n * - SFC-компонент (Vue сам компилирует `__name` из имени файла) — достаточно просто передать сам компонент,\n * - SFC-компонент с опциональным `install` (по аналогии с Element Plus / Naive UI),\n * - объект с собственным `install`,\n * - либо объект `{ name, component }`, если имя нужно задать явно.\n */\nexport type GranularityInstallableComponent =\n | (Component & { name?: string; __name?: string; install?: (app: App) => void })\n | { install: (app: App) => void }\n | { name: string; component: Component }\n\n/**\n * Директива — либо пара `{ name, directive }`, либо объект с `install`.\n */\nexport type GranularityInstallableDirective =\n | { name: string; directive: Directive }\n | { install: (app: App) => void }\n\nexport interface CreateGranularityOptions {\n /**\n * Список компонентов для глобальной регистрации.\n * Пакет сам по себе ничего не импортирует — пользователь передаёт сюда только те компоненты,\n * которые ему действительно нужны. За счёт этого tree-shaking остаётся полным.\n */\n components?: GranularityInstallableComponent[]\n /**\n * Список директив. Передавайте ровно те, что нужны, по аналогии с компонентами.\n */\n directives?: GranularityInstallableDirective[]\n /**\n * Произвольные `provide`-значения. Ключи могут быть `symbol` или строкой.\n * Пакет не навязывает конкретные ключи (например, i18n) — выбор за пользователем.\n */\n provides?: Array<{ key: string | symbol; value: unknown }>\n /**\n * Произвольные `app.config.globalProperties` — по умолчанию ничего не ставим.\n */\n globalProperties?: Record<string, unknown>\n}\n\n/**\n * Вспомогательная обёртка: превращает произвольный компонент в `GranularityInstallableComponent`\n * с явным именем регистрации.\n *\n * ```ts\n * import { GrButton } from '@feugene/granularity/components/GrButton'\n * import { defineInstallable } from '@feugene/granularity/vue'\n *\n * const installable = defineInstallable(GrButton, 'GrButton')\n * ```\n */\nexport function defineInstallable<T extends Component>(component: T, name: string): GranularityInstallableComponent {\n return {\n install(app: App) {\n app.component(name, component)\n },\n }\n}\n\nfunction installComponent(app: App, c: GranularityInstallableComponent): void {\n if (typeof (c as any).install === 'function') {\n ;(c as any).install(app)\n return\n }\n\n // Форма `{ name, component }` — явное указание имени для произвольного компонента.\n // Допускаем как object-компоненты (SFC / options), так и functional-компоненты.\n if (\n typeof (c as any).name === 'string'\n && (c as any).component\n && (typeof (c as any).component === 'object' || typeof (c as any).component === 'function')\n ) {\n const {name, component} = c as { name: string; component: Component }\n app.component(name, component)\n return\n }\n\n // SFC: Vue 3 компилирует имя компонента из имени файла в поле `__name`\n // (например, `GrButton.vue` → `__name: 'GrButton'`). Также поддерживаем явное `name`.\n const resolvedName\n = (c as Component & { name?: string }).name\n ?? (c as { __name?: string }).__name\n if (resolvedName) {\n app.component(resolvedName, c as Component)\n return\n }\n\n // Сознательно без throw — просто сообщаем в dev-режиме.\n if (!__GR_DEV__) return\n\n console.warn('[granularity] component passed to createGranularity has no `install`, `name`, or `__name` — skipped.')\n}\n\nfunction installDirective(app: App, d: GranularityInstallableDirective): void {\n if ('install' in d && typeof d.install === 'function') {\n d.install(app)\n return\n }\n\n if ('name' in d && 'directive' in d) {\n app.directive(d.name, d.directive)\n return\n }\n\n // Сознательно без throw — согласовано с `installComponent`.\n if (!__GR_DEV__) return\n\n console.warn('[granularity] directive passed to createGranularity has no `install` or `{ name, directive }` — skipped.')\n}\n\n/**\n * Применяет опции к переданному приложению. Экспортируется отдельно от фабрики —\n * удобно, когда плагин не нужен (например, в тестах).\n */\nexport function installGranularity(app: App, options: CreateGranularityOptions = {}): void {\n for (const c of options.components ?? [])\n installComponent(app, c)\n\n for (const d of options.directives ?? [])\n installDirective(app, d)\n\n for (const {key, value} of options.provides ?? [])\n app.provide(key as any, value)\n\n if (options.globalProperties) {\n for (const [key, value] of Object.entries(options.globalProperties))\n app.config.globalProperties[key] = value\n }\n}\n\n/**\n * Создаёт Vue-плагин `{ install }`, который при `app.use(...)` зарегистрирует переданные\n * компоненты, директивы и provide-значения. Сам модуль НЕ импортирует ни компонентов,\n * ни директив из пакета — всё приходит снаружи. Это сохраняет гранулярность:\n * бандлер увидит только те символы, которые вы реально передали.\n *\n * ```ts\n * import { createApp } from 'vue'\n * import { createGranularity } from '@feugene/granularity/vue'\n * import { GrButton } from '@feugene/granularity/components/GrButton'\n * import { GrInput } from '@feugene/granularity/components/GrInput'\n * import { vHotkey } from '@feugene/granularity/directives/hotkey'\n *\n * createApp(App)\n * .use(createGranularity({\n * // Достаточно передать сами компоненты — имя будет взято из `__name` (Vue SFC)\n * // или из `name`; можно также указать явно через `{ name, component }`.\n * components: [GrButton, GrInput],\n * directives: [{ name: 'hotkey', directive: vHotkey }],\n * }))\n * .mount('#app')\n * ```\n */\nexport function createGranularity(options: CreateGranularityOptions = {}): Plugin {\n return {\n install(app: App) {\n installGranularity(app, options)\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;AAsDA,SAAgB,kBAAuC,WAAc,MAA+C;CAChH,OAAO,EACH,QAAQ,KAAU;EACd,IAAI,UAAU,MAAM,SAAS;CACjC,EACJ;AACJ;AAEA,SAAS,iBAAiB,KAAU,GAA0C;CAC1E,IAAI,OAAQ,EAAU,YAAY,YAAY;EACzC,EAAW,QAAQ,GAAG;EACvB;CACJ;CAIA,IACI,OAAQ,EAAU,SAAS,YACvB,EAAU,cACV,OAAQ,EAAU,cAAc,YAAY,OAAQ,EAAU,cAAc,aAClF;EACE,MAAM,EAAC,MAAM,cAAa;EAC1B,IAAI,UAAU,MAAM,SAAS;EAC7B;CACJ;CAIA,MAAM,eACC,EAAoC,QAC/B,EAA0B;CACtC,IAAI,cAAc;EACd,IAAI,UAAU,cAAc,CAAc;EAC1C;CACJ;CAGA,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eAAa;CAEjB,QAAQ,KAAK,sGAAsG;AACvH;AAEA,SAAS,iBAAiB,KAAU,GAA0C;CAC1E,IAAI,aAAa,KAAK,OAAO,EAAE,YAAY,YAAY;EACnD,EAAE,QAAQ,GAAG;EACb;CACJ;CAEA,IAAI,UAAU,KAAK,eAAe,GAAG;EACjC,IAAI,UAAU,EAAE,MAAM,EAAE,SAAS;EACjC;CACJ;CAGA,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eAAa;CAEjB,QAAQ,KAAK,0GAA0G;AAC3H;;;;;AAMA,SAAgB,mBAAmB,KAAU,UAAoC,CAAC,GAAS;CACvF,KAAK,MAAM,KAAK,QAAQ,cAAc,CAAC,GACnC,iBAAiB,KAAK,CAAC;CAE3B,KAAK,MAAM,KAAK,QAAQ,cAAc,CAAC,GACnC,iBAAiB,KAAK,CAAC;CAE3B,KAAK,MAAM,EAAC,KAAK,WAAU,QAAQ,YAAY,CAAC,GAC5C,IAAI,QAAQ,KAAY,KAAK;CAEjC,IAAI,QAAQ,kBACR,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,gBAAgB,GAC9D,IAAI,OAAO,iBAAiB,OAAO;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,kBAAkB,UAAoC,CAAC,GAAW;CAC9E,OAAO,EACH,QAAQ,KAAU;EACd,mBAAmB,KAAK,OAAO;CACnC,EACJ;AACJ"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/vue/plugin.ts"],"sourcesContent":["import type { App, Component, Directive, Plugin } from 'vue'\n\n/**\n * Лёгкий контейнер, который может быть зарегистрирован в Vue:\n * - SFC-компонент (Vue сам компилирует `__name` из имени файла) — достаточно просто передать сам компонент,\n * - SFC-компонент с опциональным `install` (по аналогии с Element Plus / Naive UI),\n * - объект с собственным `install`,\n * - либо объект `{ name, component }`, если имя нужно задать явно.\n */\nexport type GranularityInstallableComponent\n = | (Component & { name?: string, __name?: string, install?: (app: App) => void })\n | { install: (app: App) => void }\n | { name: string, component: Component }\n\n/**\n * Директива — либо пара `{ name, directive }`, либо объект с `install`.\n */\nexport type GranularityInstallableDirective\n = | { name: string, directive: Directive }\n | { install: (app: App) => void }\n\nexport interface CreateGranularityOptions {\n /**\n * Список компонентов для глобальной регистрации.\n * Пакет сам по себе ничего не импортирует — пользователь передаёт сюда только те компоненты,\n * которые ему действительно нужны. За счёт этого tree-shaking остаётся полным.\n */\n components?: GranularityInstallableComponent[]\n /**\n * Список директив. Передавайте ровно те, что нужны, по аналогии с компонентами.\n */\n directives?: GranularityInstallableDirective[]\n /**\n * Произвольные `provide`-значения. Ключи могут быть `symbol` или строкой.\n * Пакет не навязывает конкретные ключи (например, i18n) — выбор за пользователем.\n */\n provides?: Array<{ key: string | symbol, value: unknown }>\n /**\n * Произвольные `app.config.globalProperties` — по умолчанию ничего не ставим.\n */\n globalProperties?: Record<string, unknown>\n}\n\n/**\n * Вспомогательная обёртка: превращает произвольный компонент в `GranularityInstallableComponent`\n * с явным именем регистрации.\n *\n * ```ts\n * import { GrButton } from '@feugene/granularity/components/GrButton'\n * import { defineInstallable } from '@feugene/granularity/vue'\n *\n * const installable = defineInstallable(GrButton, 'GrButton')\n * ```\n */\nexport function defineInstallable<T extends Component>(component: T, name: string): GranularityInstallableComponent {\n return {\n install(app: App) {\n app.component(name, component)\n },\n }\n}\n\nfunction installComponent(app: App, c: GranularityInstallableComponent): void {\n if (typeof (c as any).install === 'function') {\n ;(c as any).install(app)\n return\n }\n\n // Форма `{ name, component }` — явное указание имени для произвольного компонента.\n // Допускаем как object-компоненты (SFC / options), так и functional-компоненты.\n if (\n typeof (c as any).name === 'string'\n && (c as any).component\n && (typeof (c as any).component === 'object' || typeof (c as any).component === 'function')\n ) {\n const { name, component } = c as { name: string, component: Component }\n app.component(name, component)\n return\n }\n\n // SFC: Vue 3 компилирует имя компонента из имени файла в поле `__name`\n // (например, `GrButton.vue` → `__name: 'GrButton'`). Также поддерживаем явное `name`.\n const resolvedName\n = (c as Component & { name?: string }).name\n ?? (c as { __name?: string }).__name\n if (resolvedName) {\n app.component(resolvedName, c as Component)\n return\n }\n\n // Сознательно без throw — просто сообщаем в dev-режиме.\n if (!__GR_DEV__)\n return\n\n console.warn('[granularity] component passed to createGranularity has no `install`, `name`, or `__name` — skipped.')\n}\n\nfunction installDirective(app: App, d: GranularityInstallableDirective): void {\n if ('install' in d && typeof d.install === 'function') {\n d.install(app)\n return\n }\n\n if ('name' in d && 'directive' in d) {\n app.directive(d.name, d.directive)\n return\n }\n\n // Сознательно без throw — согласовано с `installComponent`.\n if (!__GR_DEV__)\n return\n\n console.warn('[granularity] directive passed to createGranularity has no `install` or `{ name, directive }` — skipped.')\n}\n\n/**\n * Применяет опции к переданному приложению. Экспортируется отдельно от фабрики —\n * удобно, когда плагин не нужен (например, в тестах).\n */\nexport function installGranularity(app: App, options: CreateGranularityOptions = {}): void {\n for (const c of options.components ?? [])\n installComponent(app, c)\n\n for (const d of options.directives ?? [])\n installDirective(app, d)\n\n for (const { key, value } of options.provides ?? [])\n app.provide(key as any, value)\n\n if (options.globalProperties) {\n for (const [key, value] of Object.entries(options.globalProperties))\n app.config.globalProperties[key] = value\n }\n}\n\n/**\n * Создаёт Vue-плагин `{ install }`, который при `app.use(...)` зарегистрирует переданные\n * компоненты, директивы и provide-значения. Сам модуль НЕ импортирует ни компонентов,\n * ни директив из пакета — всё приходит снаружи. Это сохраняет гранулярность:\n * бандлер увидит только те символы, которые вы реально передали.\n *\n * ```ts\n * import { createApp } from 'vue'\n * import { createGranularity } from '@feugene/granularity/vue'\n * import { GrButton } from '@feugene/granularity/components/GrButton'\n * import { GrInput } from '@feugene/granularity/components/GrInput'\n * import { vHotkey } from '@feugene/granularity/directives/hotkey'\n *\n * createApp(App)\n * .use(createGranularity({\n * // Достаточно передать сами компоненты — имя будет взято из `__name` (Vue SFC)\n * // или из `name`; можно также указать явно через `{ name, component }`.\n * components: [GrButton, GrInput],\n * directives: [{ name: 'hotkey', directive: vHotkey }],\n * }))\n * .mount('#app')\n * ```\n */\nexport function createGranularity(options: CreateGranularityOptions = {}): Plugin {\n return {\n install(app: App) {\n installGranularity(app, options)\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;AAsDA,SAAgB,kBAAuC,WAAc,MAA+C;CAClH,OAAO,EACL,QAAQ,KAAU;EAChB,IAAI,UAAU,MAAM,SAAS;CAC/B,EACF;AACF;AAEA,SAAS,iBAAiB,KAAU,GAA0C;CAC5E,IAAI,OAAQ,EAAU,YAAY,YAAY;EAC3C,EAAW,QAAQ,GAAG;EACvB;CACF;CAIA,IACE,OAAQ,EAAU,SAAS,YACvB,EAAU,cACV,OAAQ,EAAU,cAAc,YAAY,OAAQ,EAAU,cAAc,aAChF;EACA,MAAM,EAAE,MAAM,cAAc;EAC5B,IAAI,UAAU,MAAM,SAAS;EAC7B;CACF;CAIA,MAAM,eACD,EAAoC,QACjC,EAA0B;CAClC,IAAI,cAAc;EAChB,IAAI,UAAU,cAAc,CAAc;EAC1C;CACF;CAGA,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eACF;CAEF,QAAQ,KAAK,sGAAsG;AACrH;AAEA,SAAS,iBAAiB,KAAU,GAA0C;CAC5E,IAAI,aAAa,KAAK,OAAO,EAAE,YAAY,YAAY;EACrD,EAAE,QAAQ,GAAG;EACb;CACF;CAEA,IAAI,UAAU,KAAK,eAAe,GAAG;EACnC,IAAI,UAAU,EAAE,MAAM,EAAE,SAAS;EACjC;CACF;CAGA,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eACF;CAEF,QAAQ,KAAK,0GAA0G;AACzH;;;;;AAMA,SAAgB,mBAAmB,KAAU,UAAoC,CAAC,GAAS;CACzF,KAAK,MAAM,KAAK,QAAQ,cAAc,CAAC,GACrC,iBAAiB,KAAK,CAAC;CAEzB,KAAK,MAAM,KAAK,QAAQ,cAAc,CAAC,GACrC,iBAAiB,KAAK,CAAC;CAEzB,KAAK,MAAM,EAAE,KAAK,WAAW,QAAQ,YAAY,CAAC,GAChD,IAAI,QAAQ,KAAY,KAAK;CAE/B,IAAI,QAAQ,kBACV,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,gBAAgB,GAChE,IAAI,OAAO,iBAAiB,OAAO;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,kBAAkB,UAAoC,CAAC,GAAW;CAChF,OAAO,EACL,QAAQ,KAAU;EAChB,mBAAmB,KAAK,OAAO;CACjC,EACF;AACF"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "@feugene/granularity",
4
- "version": "0.31.0",
4
+ "version": "0.31.1",
5
5
  "framework": "vue",
6
6
  "js-types-syntax": "typescript",
7
7
  "description-markup": "markdown",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@feugene/granularity",
3
3
  "description": "Granularity design system package with Vue 3 components, consumed via the `@feugene/unocss-preset-granular` preset.",
4
- "version": "0.31.0",
4
+ "version": "0.31.1",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "Evgeniy Fureev",
@@ -592,7 +592,7 @@
592
592
  },
593
593
  "peerDependencies": {
594
594
  "@feugene/fint-i18n": "^0.6.0",
595
- "@feugene/unocss-preset-granular": "^0.10.1",
595
+ "@feugene/unocss-preset-granular": "^0.11.0",
596
596
  "@floating-ui/dom": "^1.8.0",
597
597
  "vue": "^3.5.0"
598
598
  },
@@ -608,7 +608,7 @@
608
608
  "@antfu/eslint-config": "^9.1.0",
609
609
  "@feugene/fint-i18n": "^0.6.0",
610
610
  "@feugene/granularity-test-kit": "^0.7.0",
611
- "@feugene/unocss-preset-granular": "^0.10.1",
611
+ "@feugene/unocss-preset-granular": "^0.11.0",
612
612
  "@floating-ui/dom": "^1.8.0",
613
613
  "@iconify-json/lucide": "^1.2.117",
614
614
  "@types/node": "^25.9.5",
@@ -1 +0,0 @@
1
- {"version":3,"file":"grButtonStyles-B_-YRE__.js","names":[],"sources":["../../src/components/GrButton/grButtonStyles.ts"],"sourcesContent":["import type { GrComponentSize } from '../shared/sizes'\n\nimport type { GrTone } from '../shared/tones'\n\nexport type GrButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'ghost-border'\nexport type GrButtonTone = GrTone\nexport type GrButtonSize = GrComponentSize\n\nexport type GrButtonClassOptions = {\n variant: GrButtonVariant\n tone: GrButtonTone\n size: GrButtonSize\n square: boolean\n /** Необязательные: `grButtonClass` — публичный хелпер, его зовёт и `GrRadio`. */\n disabled?: boolean\n block?: boolean\n}\n\nexport type GrButtonToneTokens = {\n solidBackground: string\n solidBackgroundHover: string\n solidBackgroundActive: string\n solidForeground: string\n accentText: string\n softBackground: string\n softBackgroundHover: string\n softBackgroundActive: string\n softForeground: string\n border: string\n borderHover: string\n borderActive: string\n}\n\nexport const base =\n 'inline-flex items-center justify-center gap-2 select-none whitespace-nowrap rounded-[var(--gr-button-radius,0.375rem)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed'\n\n/** Кнопка на всю ширину контейнера. */\nexport const blockClass = 'w-full'\n\n/**\n * Отключённая кнопка гасится токенами, а не `opacity`: прозрачность разбавляет\n * выверенные на AA цвета, а кнопка чаще всего оказывается на цветной подложке,\n * где разбавленный текст проваливается первым.\n *\n * Классы применяются ВМЕСТО вариантных, а не поверх: два `bg-*` одной\n * специфичности разрулил бы порядок в сгенерированном CSS, а не порядок в\n * списке классов. Отсюда же и разбиение по вариантам: у прозрачных\n * (`outline`, `ghost`, `ghost-border`) заливки нет и в отключённом виде —\n * иначе стрелка пагинации превращалась бы в залитую плашку.\n */\nexport const disabledClassByVariant: Record<GrButtonVariant, string> = {\n primary: 'bg-[var(--gr-button-disabled-bg)] text-[var(--gr-button-disabled-fg)] border border-[var(--gr-button-disabled-brd)] cursor-not-allowed',\n secondary: 'bg-[var(--gr-button-disabled-bg)] text-[var(--gr-button-disabled-fg)] border border-[var(--gr-button-disabled-brd)] cursor-not-allowed',\n outline: 'bg-transparent text-[var(--gr-button-disabled-fg)] border border-[var(--gr-button-disabled-brd)] cursor-not-allowed',\n ghost: 'bg-transparent text-[var(--gr-button-disabled-fg)] cursor-not-allowed',\n 'ghost-border': 'bg-transparent text-[var(--gr-button-disabled-fg)] border border-transparent cursor-not-allowed',\n}\n\nexport const grButtonBaseClass = base\n\nexport const sizes: Record<GrButtonSize, string> = {\n xs: 'h-7 text-[length:var(--gr-text-xs)] leading-[var(--gr-leading-xs)] px-2.5',\n sm: 'h-8 text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] px-3',\n md: 'h-10 text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] px-4',\n lg: 'h-11 text-[length:var(--gr-text-base)] leading-[var(--gr-leading-base)] px-5',\n}\n\n/**\n * Квадратный режим — через `--gr-button-square-size` с размерным дефолтом в\n * fallback: потребитель задаёт переменную в своём CSS и подгоняет кнопку под\n * свою сетку (приём из `GrSlider`). Размер задаётся только здесь: инлайн-стиль\n * в SFC перекрыл бы эти классы, и переменная перестала бы что-либо значить.\n */\nexport const squareSizes: Record<GrButtonSize, string> = {\n xs: 'h-[var(--gr-button-square-size,1.75rem)] w-[var(--gr-button-square-size,1.75rem)] min-w-[var(--gr-button-square-size,1.75rem)] p-0',\n sm: 'h-[var(--gr-button-square-size,2rem)] w-[var(--gr-button-square-size,2rem)] min-w-[var(--gr-button-square-size,2rem)] p-0',\n md: 'h-[var(--gr-button-square-size,2.5rem)] w-[var(--gr-button-square-size,2.5rem)] min-w-[var(--gr-button-square-size,2.5rem)] p-0',\n lg: 'h-[var(--gr-button-square-size,2.75rem)] w-[var(--gr-button-square-size,2.75rem)] min-w-[var(--gr-button-square-size,2.75rem)] p-0',\n}\n\nexport const tones: Record<GrButtonTone, GrButtonToneTokens> = {\n primary: {\n solidBackground: 'var(--gr-button-primary-bg,var(--gr-primary))',\n solidBackgroundHover: 'var(--gr-button-primary-bg-hover,var(--gr-primary-hover))',\n solidBackgroundActive: 'var(--gr-button-primary-bg-active,var(--gr-primary-active))',\n solidForeground: 'var(--gr-button-primary-fg,var(--gr-primary-fg))',\n accentText: 'var(--gr-accent-fg)',\n softBackground: 'var(--gr-button-primary-soft-bg)',\n softBackgroundHover: 'var(--gr-button-primary-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-primary-soft-bg-active)',\n softForeground: 'var(--gr-accent-fg)',\n border: 'var(--gr-primary)',\n borderHover: 'var(--gr-primary-hover)',\n borderActive: 'var(--gr-primary-active)',\n },\n neutral: {\n solidBackground: 'var(--gr-secondary)',\n solidBackgroundHover: 'var(--gr-secondary-hover)',\n solidBackgroundActive: 'var(--gr-secondary-active)',\n solidForeground: 'var(--gr-secondary-fg)',\n accentText: 'var(--gr-fg)',\n softBackground: 'var(--gr-secondary)',\n softBackgroundHover: 'var(--gr-secondary-hover)',\n softBackgroundActive: 'var(--gr-secondary-active)',\n softForeground: 'var(--gr-secondary-fg)',\n border: 'var(--gr-brd)',\n borderHover: 'var(--gr-brd-hover,var(--gr-brd))',\n borderActive: 'var(--gr-brd-active,var(--gr-brd))',\n },\n success: {\n solidBackground: 'var(--gr-button-success-bg,var(--gr-success))',\n solidBackgroundHover: 'var(--gr-button-success-bg-hover,var(--gr-success-hover))',\n solidBackgroundActive: 'var(--gr-button-success-bg-active,var(--gr-success-active))',\n solidForeground: 'var(--gr-button-success-fg,var(--gr-success-fg,var(--gr-fg)))',\n accentText: 'var(--gr-success-text,var(--gr-success))',\n softBackground: 'var(--gr-button-success-soft-bg)',\n softBackgroundHover: 'var(--gr-button-success-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-success-soft-bg-active)',\n softForeground: 'var(--gr-success-text,var(--gr-success))',\n border: 'var(--gr-success)',\n borderHover: 'var(--gr-success-hover)',\n borderActive: 'var(--gr-success-active)',\n },\n warning: {\n solidBackground: 'var(--gr-button-warning-bg,var(--gr-warning))',\n solidBackgroundHover: 'var(--gr-button-warning-bg-hover,var(--gr-warning-hover))',\n solidBackgroundActive: 'var(--gr-button-warning-bg-active,var(--gr-warning-active))',\n solidForeground: 'var(--gr-button-warning-fg,var(--gr-warning-fg,var(--gr-fg)))',\n accentText: 'var(--gr-warning-text,var(--gr-warning))',\n softBackground: 'var(--gr-button-warning-soft-bg)',\n softBackgroundHover: 'var(--gr-button-warning-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-warning-soft-bg-active)',\n softForeground: 'var(--gr-warning-text,var(--gr-warning))',\n border: 'var(--gr-warning)',\n borderHover: 'var(--gr-warning-hover)',\n borderActive: 'var(--gr-warning-active)',\n },\n danger: {\n solidBackground: 'var(--gr-button-danger-bg,var(--gr-danger))',\n solidBackgroundHover: 'var(--gr-button-danger-bg-hover,var(--gr-danger-hover))',\n solidBackgroundActive: 'var(--gr-button-danger-bg-active,var(--gr-danger-active))',\n solidForeground: 'var(--gr-button-danger-fg,var(--gr-danger-fg,var(--gr-fg)))',\n accentText: 'var(--gr-danger-text,var(--gr-danger))',\n softBackground: 'var(--gr-button-danger-soft-bg)',\n softBackgroundHover: 'var(--gr-button-danger-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-danger-soft-bg-active)',\n softForeground: 'var(--gr-danger-text,var(--gr-danger))',\n border: 'var(--gr-danger)',\n borderHover: 'var(--gr-danger-hover)',\n borderActive: 'var(--gr-danger-active)',\n },\n info: {\n solidBackground: 'var(--gr-button-info-bg,var(--gr-info))',\n solidBackgroundHover: 'var(--gr-button-info-bg-hover,var(--gr-info-hover))',\n solidBackgroundActive: 'var(--gr-button-info-bg-active,var(--gr-info-active))',\n solidForeground: 'var(--gr-button-info-fg,var(--gr-info-fg,var(--gr-fg)))',\n accentText: 'var(--gr-info-text,var(--gr-info))',\n softBackground: 'var(--gr-button-info-soft-bg)',\n softBackgroundHover: 'var(--gr-button-info-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-info-soft-bg-active)',\n softForeground: 'var(--gr-info-text,var(--gr-info))',\n border: 'var(--gr-info)',\n borderHover: 'var(--gr-info-hover)',\n borderActive: 'var(--gr-info-active)',\n },\n slate: {\n solidBackground: 'var(--gr-button-slate-bg,var(--gr-slate))',\n solidBackgroundHover: 'var(--gr-button-slate-bg-hover,var(--gr-slate-hover))',\n solidBackgroundActive: 'var(--gr-button-slate-bg-active,var(--gr-slate-active))',\n solidForeground: 'var(--gr-button-slate-fg,var(--gr-slate-fg,var(--gr-fg)))',\n accentText: 'var(--gr-slate-text,var(--gr-slate))',\n softBackground: 'var(--gr-button-slate-soft-bg)',\n softBackgroundHover: 'var(--gr-button-slate-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-slate-soft-bg-active)',\n softForeground: 'var(--gr-slate-text,var(--gr-slate))',\n border: 'var(--gr-slate)',\n borderHover: 'var(--gr-slate-hover)',\n borderActive: 'var(--gr-slate-active)',\n },\n azure: {\n solidBackground: 'var(--gr-button-azure-bg,var(--gr-azure))',\n solidBackgroundHover: 'var(--gr-button-azure-bg-hover,var(--gr-azure-hover))',\n solidBackgroundActive: 'var(--gr-button-azure-bg-active,var(--gr-azure-active))',\n solidForeground: 'var(--gr-button-azure-fg,var(--gr-azure-fg,var(--gr-fg)))',\n accentText: 'var(--gr-azure-text,var(--gr-azure))',\n softBackground: 'var(--gr-button-azure-soft-bg)',\n softBackgroundHover: 'var(--gr-button-azure-soft-bg-hover)',\n softBackgroundActive: 'var(--gr-button-azure-soft-bg-active)',\n softForeground: 'var(--gr-azure-text,var(--gr-azure))',\n border: 'var(--gr-azure)',\n borderHover: 'var(--gr-azure-hover)',\n borderActive: 'var(--gr-azure-active)',\n },\n}\n\nfunction withVar(token: string): string {\n return `[${token}]`\n}\n\nexport function variantClass(variant: GrButtonVariant, tone: GrButtonTone): string {\n const tokens = tones[tone]\n\n if (variant === 'primary') {\n return [\n `bg-${withVar(tokens.solidBackground)}`,\n `text-${withVar(tokens.solidForeground)}`,\n `border border-${withVar(tokens.solidBackground)}`,\n `hover:bg-${withVar(tokens.solidBackgroundHover)}`,\n `hover:border-${withVar(tokens.solidBackgroundHover)}`,\n `active:bg-${withVar(tokens.solidBackgroundActive)}`,\n `hover:active:bg-${withVar(tokens.solidBackgroundActive)}`,\n `active:border-${withVar(tokens.solidBackgroundActive)}`,\n `hover:active:border-${withVar(tokens.solidBackgroundActive)}`,\n ].join(' ')\n }\n\n if (variant === 'secondary') {\n return [\n `bg-${withVar(tokens.softBackground)}`,\n `text-${withVar(tokens.softForeground)}`,\n `border border-${withVar(tokens.border)}`,\n `hover:bg-${withVar(tokens.softBackgroundHover)}`,\n `hover:border-${withVar(tokens.borderHover)}`,\n `active:bg-${withVar(tokens.softBackgroundActive)}`,\n `hover:active:bg-${withVar(tokens.softBackgroundActive)}`,\n `active:border-${withVar(tokens.borderActive)}`,\n `hover:active:border-${withVar(tokens.borderActive)}`,\n ].join(' ')\n }\n\n if (variant === 'outline') {\n return [\n 'bg-transparent',\n `text-${withVar(tokens.accentText)}`,\n `border border-${withVar(tokens.border)}`,\n `hover:bg-${withVar(tokens.softBackgroundHover)}`,\n `hover:border-${withVar(tokens.borderHover)}`,\n `active:bg-${withVar(tokens.softBackgroundActive)}`,\n `hover:active:bg-${withVar(tokens.softBackgroundActive)}`,\n `active:border-${withVar(tokens.borderActive)}`,\n `hover:active:border-${withVar(tokens.borderActive)}`,\n ].join(' ')\n }\n\n if (variant === 'ghost') {\n return [\n 'bg-transparent',\n `text-${withVar(tokens.accentText)}`,\n `hover:bg-${withVar(tokens.softBackgroundHover)}`,\n `active:bg-${withVar(tokens.softBackgroundActive)}`,\n `hover:active:bg-${withVar(tokens.softBackgroundActive)}`,\n ].join(' ')\n }\n\n return [\n 'bg-transparent',\n `text-${withVar(tokens.accentText)}`,\n 'border border-transparent',\n `hover:bg-${withVar(tokens.softBackgroundHover)}`,\n `hover:border-${withVar(tokens.borderHover)}`,\n `active:bg-${withVar(tokens.softBackgroundActive)}`,\n `hover:active:bg-${withVar(tokens.softBackgroundActive)}`,\n `active:border-${withVar(tokens.borderActive)}`,\n `hover:active:border-${withVar(tokens.borderActive)}`,\n ].join(' ')\n}\n\nexport function grButtonClass(options: GrButtonClassOptions): string {\n return [\n options.square ? squareSizes[options.size] : sizes[options.size],\n options.block ? blockClass : '',\n // `loading` сюда не входит: у него свой спиннер, и выглядеть отключённым он\n // не должен.\n options.disabled ? disabledClassByVariant[options.variant] : variantClass(options.variant, options.tone),\n ].filter(Boolean).join(' ')\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"grSliderStyles-B5uNxNJq.js","names":[],"sources":["../../src/components/GrSlider/grSliderStyles.ts"],"sourcesContent":["import type { GrComponentSize } from '../shared/sizes'\n\nexport type GrSliderSize = GrComponentSize\n/** Значение: число (single) или кортеж `[lo, hi]` (range при `range=true`). */\nexport type GrSliderModelValue = number | [number, number]\n/** Метка деления: словарь `value → label` или просто массив значений (label = значение). */\nexport type GrSliderMarks = Record<number, string> | number[]\n\n/** Ориентация дорожки. В вертикальной минимум внизу — как на регуляторе громкости. */\nexport const GR_SLIDER_ORIENTATIONS = ['horizontal', 'vertical'] as const\nexport type GrSliderOrientation = typeof GR_SLIDER_ORIENTATIONS[number]\n\n/**\n * Кастомизация через CSS-переменные (можно задать на самом слайдере или любом\n * предке — inline `style`, класс, тема). Значение по умолчанию — второй аргумент\n * `var(--name, default)`, поэтому переопределять можно точечно:\n *\n * - `--gr-slider-rail` — цвет фона дорожки (неактивная часть).\n * - `--gr-slider-fill` — цвет активной (заполненной) части.\n * - `--gr-slider-thumb-bg` — заливка бегунка.\n * - `--gr-slider-thumb-border` — цвет окантовки бегунка (по умолчанию = fill).\n * - `--gr-slider-thumb-size` — диаметр бегунка (по умолчанию — из `size`).\n * - `--gr-slider-track-height` — толщина дорожки (по умолчанию — из `size`).\n * - `--gr-slider-length` — длина вертикальной дорожки (по умолчанию `10rem`).\n */\n\n// Высота дорожки (rail/fill) по размеру — дефолт для `--gr-slider-track-height`.\nexport const sliderTrackHeightBySize: Record<GrSliderSize, string> = {\n xs: 'h-[var(--gr-slider-track-height,0.1875rem)]',\n sm: 'h-[var(--gr-slider-track-height,0.25rem)]',\n md: 'h-[var(--gr-slider-track-height,0.375rem)]',\n lg: 'h-[var(--gr-slider-track-height,0.5rem)]',\n}\n\n/** В вертикали та же толщина задаёт ширину, а длину берёт `--gr-slider-length`. */\nexport const sliderTrackWidthBySize: Record<GrSliderSize, string> = {\n xs: 'w-[var(--gr-slider-track-height,0.1875rem)]',\n sm: 'w-[var(--gr-slider-track-height,0.25rem)]',\n md: 'w-[var(--gr-slider-track-height,0.375rem)]',\n lg: 'w-[var(--gr-slider-track-height,0.5rem)]',\n}\n\nexport const sliderTrackVerticalLengthClass = 'h-[var(--gr-slider-length,10rem)]'\n\n// Размер «бегунка» (thumb) по размеру — дефолт для `--gr-slider-thumb-size`.\nexport const sliderThumbSizeBySize: Record<GrSliderSize, string> = {\n xs: 'h-[var(--gr-slider-thumb-size,0.75rem)] w-[var(--gr-slider-thumb-size,0.75rem)]',\n sm: 'h-[var(--gr-slider-thumb-size,0.875rem)] w-[var(--gr-slider-thumb-size,0.875rem)]',\n md: 'h-[var(--gr-slider-thumb-size,1rem)] w-[var(--gr-slider-thumb-size,1rem)]',\n lg: 'h-[var(--gr-slider-thumb-size,1.25rem)] w-[var(--gr-slider-thumb-size,1.25rem)]',\n}\n\n// Вертикальный отступ обёртки, чтобы thumb не обрезался краями дорожки.\nexport const sliderPaddingBySize: Record<GrSliderSize, string> = {\n xs: 'py-1',\n sm: 'py-1.5',\n md: 'py-2',\n lg: 'py-2.5',\n}\n\nexport const sliderRailClass = 'absolute inset-0 rounded-[var(--gr-radius-full)] bg-[var(--gr-slider-rail,color-mix(in_srgb,var(--gr-muted)_45%,transparent))]'\n\nexport const sliderFillBaseClass = 'absolute rounded-[var(--gr-radius-full)]'\n\n/** Заливка растёт вдоль дорожки: по горизонтали — влево-вправо, по вертикали — снизу вверх. */\nexport const sliderFillOrientationClass: Record<GrSliderOrientation, string> = {\n horizontal: 'top-0 bottom-0',\n vertical: 'left-0 right-0',\n}\n\n/**\n * Недоступный слайдер гасится токенами, а не `opacity`: прозрачность разбавляет\n * выверенные на AA токены и роняет контраст подписей меток.\n */\nexport const sliderFillDisabledClass = 'bg-[var(--gr-disabled-fg)]'\nexport const sliderFillEnabledClass = 'bg-[var(--gr-slider-fill,var(--gr-primary))]'\n\nexport function sliderFillClass(disabled: boolean): string {\n return [sliderFillBaseClass, disabled ? sliderFillDisabledClass : sliderFillEnabledClass].join(' ')\n}\n\n// Окантовка бегунка: цветной border (по умолчанию = fill) + тонкая контрастная\n// обводка (`ring`), чтобы бегунок не сливался ни с фоном страницы, ни с заливкой.\nexport const sliderThumbBaseClass = 'absolute rounded-[var(--gr-radius-full)] border-2 border-[var(--gr-slider-thumb-border,var(--gr-slider-fill,var(--gr-primary)))] bg-[var(--gr-slider-thumb-bg,var(--gr-bg))] ring-1 ring-[color-mix(in_srgb,var(--gr-fg)_22%,transparent)] shadow-[var(--gr-shadow-1)] transition-[box-shadow,transform] duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] focus-visible:ring-offset-1 focus-visible:ring-offset-[var(--gr-bg)]'\n\n/** Бегунок центрируется поперёк дорожки, а вдоль неё его ведёт inline-стиль. */\nexport const sliderThumbOrientationClass: Record<GrSliderOrientation, string> = {\n horizontal: 'top-1/2 -translate-x-1/2 -translate-y-1/2',\n vertical: 'left-1/2 -translate-x-1/2 translate-y-1/2',\n}\n\nexport const sliderThumbDisabledClass = 'cursor-not-allowed border-[var(--gr-disabled-fg)] bg-[var(--gr-disabled-bg)]'\nexport const sliderThumbEnabledClass = 'cursor-grab active:cursor-grabbing hover:scale-110'\n\nexport function sliderThumbClass(options: {\n size: GrSliderSize\n disabled: boolean\n orientation?: GrSliderOrientation\n}): string {\n return [\n sliderThumbBaseClass,\n sliderThumbOrientationClass[options.orientation ?? 'horizontal'],\n sliderThumbSizeBySize[options.size],\n options.disabled ? sliderThumbDisabledClass : sliderThumbEnabledClass,\n ].join(' ')\n}\n\nexport const sliderTooltipBaseClass = 'pointer-events-none absolute whitespace-nowrap rounded-[var(--gr-radius-control)] bg-[var(--gr-fg)] px-1.5 py-0.5 text-[length:var(--gr-text-2xs)] font-medium leading-tight text-[var(--gr-bg)] shadow-[var(--gr-shadow-2)]'\n\n/** В вертикали подсказка уходит вбок: над бегунком она легла бы на дорожку. */\nexport const sliderTooltipOrientationClass: Record<GrSliderOrientation, string> = {\n horizontal: 'bottom-full left-1/2 mb-2 -translate-x-1/2',\n vertical: 'right-full top-1/2 mr-2 -translate-y-1/2',\n}\n\nexport function sliderTooltipClass(orientation: GrSliderOrientation = 'horizontal'): string {\n return [sliderTooltipBaseClass, sliderTooltipOrientationClass[orientation]].join(' ')\n}\n\nexport const sliderMarkTickBaseClass = 'absolute h-1.5 w-1.5 rounded-[var(--gr-radius-full)] bg-[var(--gr-bg)] ring-1 ring-[var(--gr-brd)]'\n\nexport const sliderMarkTickOrientationClass: Record<GrSliderOrientation, string> = {\n horizontal: 'top-1/2 -translate-x-1/2 -translate-y-1/2',\n vertical: 'left-1/2 -translate-x-1/2 translate-y-1/2',\n}\n\nexport function sliderMarkTickClass(orientation: GrSliderOrientation = 'horizontal'): string {\n return [sliderMarkTickBaseClass, sliderMarkTickOrientationClass[orientation]].join(' ')\n}\n// Подписи меток уводим под дорожку (`top-full` + отступ, ниже бегунка) и запрещаем\n// перенос (`whitespace-nowrap`) — иначе крайняя подпись рвётся по буквам.\nexport const sliderMarkLabelClass = 'absolute whitespace-nowrap text-[length:var(--gr-text-2xs)] leading-none text-[var(--gr-muted-fg)]'\n\nexport const sliderMarkLabelDisabledClass = 'text-[var(--gr-disabled-fg)]'\n\n/** Вертикальные подписи уходят вправо от дорожки: под ней им места нет. */\nexport const sliderMarkLabelVerticalClass = 'left-full ml-2.5 translate-y-1/2'\nexport const sliderMarkLabelHorizontalClass = 'top-full mt-2.5'\n\n// Выравнивание подписи по позиции: центр для внутренних меток, а крайние (0% и\n// 100%) прижимаем внутрь, чтобы они не вылезали за края дорожки и не обрезались.\nexport function sliderMarkLabelClassFor(\n pct: number,\n options: { orientation?: GrSliderOrientation, disabled?: boolean } = {},\n): string {\n const orientation = options.orientation ?? 'horizontal'\n const tone = options.disabled ? sliderMarkLabelDisabledClass : ''\n\n if (orientation === 'vertical')\n return [sliderMarkLabelClass, sliderMarkLabelVerticalClass, tone].filter(Boolean).join(' ')\n\n const align = pct <= 0.5 ? 'translate-x-0' : pct >= 99.5 ? '-translate-x-full' : '-translate-x-1/2'\n return [sliderMarkLabelClass, sliderMarkLabelHorizontalClass, align, tone].filter(Boolean).join(' ')\n}\n\nexport const sliderRootBaseClass = 'relative select-none'\n\nexport const sliderRootOrientationClass: Record<GrSliderOrientation, string> = {\n horizontal: 'w-full',\n vertical: 'inline-flex',\n}\n\n/** Горизонтальный отступ обёртки вертикали — та же роль, что у `py-*` у горизонтали. */\nexport const sliderPaddingVerticalBySize: Record<GrSliderSize, string> = {\n xs: 'px-1',\n sm: 'px-1.5',\n md: 'px-2',\n lg: 'px-2.5',\n}\n\nexport function sliderRootClass(options: {\n size: GrSliderSize\n disabled: boolean\n hasMarks?: boolean\n orientation?: GrSliderOrientation\n}): string {\n const orientation = options.orientation ?? 'horizontal'\n const vertical = orientation === 'vertical'\n\n return [\n sliderRootBaseClass,\n sliderRootOrientationClass[orientation],\n vertical ? sliderPaddingVerticalBySize[options.size] : sliderPaddingBySize[options.size],\n // Резервируем место под подписи меток, чтобы они не наезжали на соседний контент.\n options.hasMarks ? (vertical ? 'mr-10' : 'mb-7') : '',\n ]\n .filter(Boolean)\n .join(' ')\n}\n"],"mappings":";;;;;;;;;;;;;;AA2BA,IAAa,0BAAwD;CACnE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;AAGA,IAAa,yBAAuD;CAClE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,iCAAiC;AAG9C,IAAa,wBAAsD;CACjE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAGA,IAAa,sBAAoD;CAC/D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,kBAAkB;AAE/B,IAAa,sBAAsB;;AAGnC,IAAa,6BAAkE;CAC7E,YAAY;CACZ,UAAU;AACZ;;;;;AAMA,IAAa,0BAA0B;AACvC,IAAa,yBAAyB;AAEtC,SAAgB,gBAAgB,UAA2B;CACzD,OAAO,CAAC,qBAAqB,WAAW,0BAA0B,sBAAsB,CAAC,CAAC,KAAK,GAAG;AACpG;AAIA,IAAa,uBAAuB;;AAGpC,IAAa,8BAAmE;CAC9E,YAAY;CACZ,UAAU;AACZ;AAEA,IAAa,2BAA2B;AACxC,IAAa,0BAA0B;AAEvC,SAAgB,iBAAiB,SAItB;CACT,OAAO;EACL;EACA,4BAA4B,QAAQ,eAAe;EACnD,sBAAsB,QAAQ;EAC9B,QAAQ,WAAW,2BAA2B;CAChD,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAa,yBAAyB;;AAGtC,IAAa,gCAAqE;CAChF,YAAY;CACZ,UAAU;AACZ;AAEA,SAAgB,mBAAmB,cAAmC,cAAsB;CAC1F,OAAO,CAAC,wBAAwB,8BAA8B,YAAY,CAAC,CAAC,KAAK,GAAG;AACtF;AAEA,IAAa,0BAA0B;AAEvC,IAAa,iCAAsE;CACjF,YAAY;CACZ,UAAU;AACZ;AAEA,SAAgB,oBAAoB,cAAmC,cAAsB;CAC3F,OAAO,CAAC,yBAAyB,+BAA+B,YAAY,CAAC,CAAC,KAAK,GAAG;AACxF;AAGA,IAAa,uBAAuB;AAEpC,IAAa,+BAA+B;;AAG5C,IAAa,+BAA+B;AAC5C,IAAa,iCAAiC;AAI9C,SAAgB,wBACd,KACA,UAAqE,CAAC,GAC9D;CACR,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,OAAO,QAAQ,WAAW,+BAA+B;CAE/D,IAAI,gBAAgB,YAClB,OAAO;EAAC;EAAsB;EAA8B;CAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAG5F,OAAO;EAAC;EAAsB;EADhB,OAAO,KAAM,kBAAkB,OAAO,OAAO,sBAAsB;EACZ;CAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AACrG;AAEA,IAAa,sBAAsB;AAEnC,IAAa,6BAAkE;CAC7E,YAAY;CACZ,UAAU;AACZ;;AAGA,IAAa,8BAA4D;CACvE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,SAAgB,gBAAgB,SAKrB;CACT,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,WAAW,gBAAgB;CAEjC,OAAO;EACL;EACA,2BAA2B;EAC3B,WAAW,4BAA4B,QAAQ,QAAQ,oBAAoB,QAAQ;EAEnF,QAAQ,WAAY,WAAW,UAAU,SAAU;CACrD,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrAutocomplete-X6RlmiJQ.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 { useRovingFocus } from '../../composables/useRovingFocus'\nimport { filterOptions, resolveSelectedOptions } from '../shared/optionFilter'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge/grBadgeStyles'\nimport GrChip from '../GrChip/GrChip.vue'\nimport { chipSizeForBadgeScale } from '../GrChip/grChipStyles'\nimport { panelPopTransition } from '../shared/overlayTransition'\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`. Рисует их `GrChip`, но\n * шкала пропа осталась бейджевой: `tagSize` публичен, и его значения обязаны\n * означать тот же кегль, что и раньше. Перевод ступеней — в\n * `chipSizeForBadgeScale`.\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/печать.\n//\n// Отсюда же единственное отличие от `GrInputTag`: `tabindexFor` здесь не\n// используется. Кольцо нужно как движок навигации, а таб-стоп у ряда чипов\n// отсутствует вовсе — он принадлежит полю ввода.\nconst chipRefs = ref<Array<{ removeEl: HTMLButtonElement | null } | null>>([])\n\nfunction setChipRef(index: number) {\n return (el: unknown): void => {\n chipRefs.value[index] = el as { removeEl: HTMLButtonElement | null } | null\n }\n}\n\n/**\n * Проп `tagSize` объявлен по шкале бейджа и остаётся публичным контрактом:\n * дефолт `sm` обязан означать для потребителя тот же кегль, что и раньше.\n */\nconst chipSize = computed(() => chipSizeForBadgeScale(props.tagSize))\n\n// Адресация индексом, а не значением: значения дженерик-типа, и `TValue`\n// сравнивался бы по ссылке там, где опции пересоздаются.\nconst chipIndexes = computed(() => selectedOptions.value.map((_, index) => index))\n\nconst chipRoving = useRovingFocus<number>({\n items: () => chipIndexes.value,\n elementFor: index => chipRefs.value[index]?.removeEl,\n orientation: () => 'horizontal',\n // Ряд чипов — не кольцо: за правым краем стоит поле ввода, за левым ничего.\n wrap: () => false,\n onOverflow: (edge) => {\n if (edge === 'end') focusInput()\n return true\n },\n // Удаление чипа перерисовывает ряд: без ожидания фокус уехал бы на узел,\n // которого уже нет.\n beforeFocus: () => nextTick(),\n})\n\nfunction focusChip(index: number): void {\n const last = selectedOptions.value.length - 1\n if (last < 0) {\n focusInput()\n return\n }\n\n void chipRoving.focusKey(Math.min(Math.max(index, 0), last))\n}\n\nfunction onChipKeydown(event: KeyboardEvent, index: number, value: TValue): void {\n if (chipRoving.handleNavigationKeys(event)) return\n\n switch (event.key) {\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 <GrChip\n v-for=\"(option, chipIndex) in selectedOptions\"\n :key=\"option.value\"\n :ref=\"setChipRef(chipIndex)\"\n data-gr-autocomplete-chip\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"chipSize\"\n :radius=\"tagRadius\"\n :closable=\"!locked\"\n :remove-label=\"t('gr.autocomplete.removeValue', 'Remove {label}', { label: option.label })\"\n :remove-tabindex=\"-1\"\n @remove=\"removeValue(option.value)\"\n @keydown=\"onChipKeydown($event, chipIndex, option.value)\"\n >\n <span class=\"truncate\">{{ option.label }}</span>\n </GrChip>\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=\"panelPopTransition.enter\"\n :enter-from-class=\"panelPopTransition.enterFrom\"\n :enter-to-class=\"panelPopTransition.enterTo\"\n :leave-active-class=\"panelPopTransition.leave\"\n :leave-from-class=\"panelPopTransition.leaveFrom\"\n :leave-to-class=\"panelPopTransition.leaveTo\"\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-text)]\" />\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 { useRovingFocus } from '../../composables/useRovingFocus'\nimport { filterOptions, resolveSelectedOptions } from '../shared/optionFilter'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge/grBadgeStyles'\nimport GrChip from '../GrChip/GrChip.vue'\nimport { chipSizeForBadgeScale } from '../GrChip/grChipStyles'\nimport { panelPopTransition } from '../shared/overlayTransition'\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`. Рисует их `GrChip`, но\n * шкала пропа осталась бейджевой: `tagSize` публичен, и его значения обязаны\n * означать тот же кегль, что и раньше. Перевод ступеней — в\n * `chipSizeForBadgeScale`.\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/печать.\n//\n// Отсюда же единственное отличие от `GrInputTag`: `tabindexFor` здесь не\n// используется. Кольцо нужно как движок навигации, а таб-стоп у ряда чипов\n// отсутствует вовсе — он принадлежит полю ввода.\nconst chipRefs = ref<Array<{ removeEl: HTMLButtonElement | null } | null>>([])\n\nfunction setChipRef(index: number) {\n return (el: unknown): void => {\n chipRefs.value[index] = el as { removeEl: HTMLButtonElement | null } | null\n }\n}\n\n/**\n * Проп `tagSize` объявлен по шкале бейджа и остаётся публичным контрактом:\n * дефолт `sm` обязан означать для потребителя тот же кегль, что и раньше.\n */\nconst chipSize = computed(() => chipSizeForBadgeScale(props.tagSize))\n\n// Адресация индексом, а не значением: значения дженерик-типа, и `TValue`\n// сравнивался бы по ссылке там, где опции пересоздаются.\nconst chipIndexes = computed(() => selectedOptions.value.map((_, index) => index))\n\nconst chipRoving = useRovingFocus<number>({\n items: () => chipIndexes.value,\n elementFor: index => chipRefs.value[index]?.removeEl,\n orientation: () => 'horizontal',\n // Ряд чипов — не кольцо: за правым краем стоит поле ввода, за левым ничего.\n wrap: () => false,\n onOverflow: (edge) => {\n if (edge === 'end') focusInput()\n return true\n },\n // Удаление чипа перерисовывает ряд: без ожидания фокус уехал бы на узел,\n // которого уже нет.\n beforeFocus: () => nextTick(),\n})\n\nfunction focusChip(index: number): void {\n const last = selectedOptions.value.length - 1\n if (last < 0) {\n focusInput()\n return\n }\n\n void chipRoving.focusKey(Math.min(Math.max(index, 0), last))\n}\n\nfunction onChipKeydown(event: KeyboardEvent, index: number, value: TValue): void {\n if (chipRoving.handleNavigationKeys(event)) return\n\n switch (event.key) {\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 <GrChip\n v-for=\"(option, chipIndex) in selectedOptions\"\n :key=\"option.value\"\n :ref=\"setChipRef(chipIndex)\"\n data-gr-autocomplete-chip\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"chipSize\"\n :radius=\"tagRadius\"\n :closable=\"!locked\"\n :remove-label=\"t('gr.autocomplete.removeValue', 'Remove {label}', { label: option.label })\"\n :remove-tabindex=\"-1\"\n @remove=\"removeValue(option.value)\"\n @keydown=\"onChipKeydown($event, chipIndex, option.value)\"\n >\n <span class=\"truncate\">{{ option.label }}</span>\n </GrChip>\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=\"panelPopTransition.enter\"\n :enter-from-class=\"panelPopTransition.enterFrom\"\n :enter-to-class=\"panelPopTransition.enterTo\"\n :leave-active-class=\"panelPopTransition.leave\"\n :leave-from-class=\"panelPopTransition.leaveFrom\"\n :leave-to-class=\"panelPopTransition.leaveTo\"\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-text)]\" />\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAicA,IAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAvR7B,MAAM,QAAQ;;EA2Cd,MAAM,0BAA8D;GAClE,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EAGA,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,iBAAiB,CAAC;EACzF,MAAM,oBAAoB,mBAAmB,kBAAkB,mBAAmB,MAAM,WAAW,KAAK;;;;;;EAOxG,MAAM,EAAE,WAAW,WAAW,UAAU,UAAU,aAAa,gBAAgB,uBAAuB,OAAO;GAC3G,uBAAuB,wBAAwB,aAAa;GAC5D,gBAAgB;EAClB,CAAC;EAED,MAAM,OAAO;EAeb,MAAM,EAAE,MAAM,2BAA2B;EAEzC,MAAM,sBAAsB,eAAe,MAAM,eAAe,EAAE,2BAA2B,UAAU,CAAC;EACxG,MAAM,wBAAwB,eAAe,MAAM,iBAAiB,EAAE,6BAA6B,YAAY,CAAC;EAChH,MAAM,qBAAqB,eAAe,MAAM,cAAc,EAAE,mBAAmB,OAAO,CAAC;EAI3F,MAAM,uBAAuB,eAC3B,EAAE,4BAA4B,gCAAgC;GAC5D,GAAG,MAAM;GACT,OAAO,MAAM;EACf,CAAC,CACH;EAGA,MAAM,QAAQ,sBAAsB;EACpC,MAAM,aAAa,eAAe,OAAO,GAAG,KAAK;EACjD,MAAM,EACJ,UAAU,YACV,SAAS,WAAW,UAAU,YAAY,UAAU,eAAe,uBAAuB,KAAK;EACjG,MAAM,cAAc,eAAe,OAAO,cAAc,KAAK;EAK7D,MAAM,SAAS,eAAe,WAAW,SAAS,WAAW,KAAK;EAIlE,MAAM,gBAAgB,WAA2C,CAAC,CAAC;EACnE,MAAM,iBAAiB,IAAI,KAAK;EAChC,MAAM,gBAAgB,IAAI,KAAK;EAE/B,MAAM,kBAAkB,eACtB,MAAM,gBAAgB,eAAe,QAAQ,cAAc,QAAS,MAAM,WAAW,CAAC,CACxF;;;;;;;EAQA,SAAS,iBAAiB,SAA6D;GACrF,QAAQ,WAAW,CAAC,EAAA,CAAG,KAAI,MAAK,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,GAAQ;EACrF;EAIA,YAAY,iBAAiB,MAAM,OAAO,SAAS;GACjD,IAAI,CAAC,MAAM,cAAc;GACzB,aAAa;GACb,eAAe,QAAQ;GACvB,cAAc,QAAQ,CAAC;EACzB,CAAC;EAED,MAAM,YAAY,eAAe,MAAM,WAAW,cAAc,KAAK;;EAGrE,SAAS,aAAa,OAAyB;GAC7C,OAAO,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU;EAC5D;EAEA,SAAS,QAAQ,OAAmD;GAClE,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO;GACjC,IAAI,aAAa,KAAK,GAAG,OAAO,CAAC;GACjC,OAAO,CAAC,KAAK;EACf;EAEA,MAAM,cAAc,eAA4B;GAC9C,MAAM,MAAM,MAAM,QAAQ,MAAM,UAAU,IAAI,MAAM,WAAW,KAAK,MAAM;GAC1E,OAAO,aAAa,GAAG,IAAI,KAAM;EACnC,CAAC;EACD,MAAM,iBAAiB,eAAgB,MAAM,WAAW,QAAQ,MAAM,UAAU,IAAK,aAAa,YAAY,KAAK,IAAI,CAAC,IAAI,CAAC,YAAY,KAAe,CAAG;EAC3J,MAAM,eAAe,eAAe,eAAe,MAAM,SAAS,CAAC;EAEnE,SAAS,SAAS,OAAoC;GACpD,OAAO,gBAAgB,MAAM,MAAK,MAAK,EAAE,UAAU,KAAK,CAAC,EAAE,SAAS,OAAO,KAAK;EAClF;;EAGA,MAAM,kBAAkB,eACtB,uBAAuB,eAAe,OAAO,gBAAgB,KAAK,CACpE;EAEA,MAAM,sBAAsB,eAAgB,MAAM,YAAY,aAAa,YAAY,KAAK,IAAI,KAAK,SAAS,YAAY,KAAK,CAAE;EAGjI,MAAM,QAAQ,IAAI,EAAE;EAIpB,MAAM,QAAQ,IAAI,KAAK;EAEvB,MAAM,EAAE,MAAM,YAAY,wBAClB,MAAM,OACZ,SAAQ,KAAK,eAAe,IAAI,CAClC;EAEA,MAAM,SAAS,IAAwB,IAAI;EAC3C,MAAM,UAAU,IAAwB,IAAI;EAC5C,MAAM,YAAY,IAAwB,IAAI;EAI9C,MAAM,EAAE,WAAW,eAAe,eAAe,QAAQ;GACvD,QAAO,UAAS,KAAK,SAAS,KAAK;GACnC,QAAO,UAAS,KAAK,QAAQ,KAAK;GAClC,kBAAkB,CAAC,QAAQ,KAAK;EAClC,CAAC;EACD,MAAM,UAAU,IAA6B,IAAI;EAEjD,SAAS,QAAc;GACrB,QAAQ,OAAO,MAAM;EACvB;EAEA,SAAS,OAAa;GACpB,QAAQ,OAAO,KAAK;EACtB;EAEA,SAAa;GACX;GACA;GACA,YAAY,aAAa;GACzB,aAAa,cAAc;EAC7B,CAAC;EACD,MAAM,sBAAsB,OAAO,QAAQ,KAAK;EAEhD,MAAM,EAAE,kBAAkB,YAAY,QAAQ,SAAS,MAAM;GAC3D,WAAW;GACX,YAAY;GACZ,WAAW;EACb,CAAC;EAED,eAAe,MAAM,aAAa;EAGlC,MAAM,cAAc,eAAe;GAGjC,IAAI,CAAC,MAAM,cAAc,MAAM,cAAc,OAAO;GAEpD,IAAI,CAAC,MAAM,YAAY,CAAC,MAAM,OAAO,OAAO;GAC5C,OAAO,MAAM,MAAM,KAAK;EAC1B,CAAC;EAED,MAAM,kBAAkB,eACtB,cAAc,gBAAgB,OAAO,YAAY,OAAO,MAAM,MAAM,CACtE;EAEA,MAAM,gBAAgB,eAAe,MAAM,iBAAiB,KAAK,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,MAAM,cAAc;;;;;;EAOjH,MAAM,mBAAmB,eACvB,cAAc,QAAQ,CAAC,IAAI,gBAAgB,KAC7C;EAEA,MAAM,eAAe,eAAe;GAClC,IAAI,CAAC,MAAM,kBAAkB,OAAO;GAGpC,MAAM,IAAI,MAAM,MAAM,KAAK;GAC3B,IAAI,CAAC,GAAG,OAAO;GACf,IAAI,MAAM,YAAY,eAAe,MAAM,SAAS,CAAC,GAAG,OAAO;GAC/D,IAAI,CAAC,MAAM,YAAY,MAAM,YAAY,OAAO,OAAO;GAEvD,OAAO,CAAC,gBAAgB,MAAM,MAAK,MAAK,EAAE,UAAU,KAAK,EAAE,UAAU,CAAC;EACxE,CAAC;EAED,MAAM,YAAY,eAChB,CAAC,UAAU,SAAS,iBAAiB,MAAM,WAAW,KAAK,CAAC,aAAa,KAC3E;EAGA,MAAM,YAAY,MAAM;;;;;;EAOxB,SAAS,YAAY,OAAuB;GAC1C,OAAO,GAAG,UAAU,OAAO;EAC7B;EACA,MAAM,iBAAiB,eAAe,GAAG,UAAU,KAAK;;;;;;;;;EAaxD,MAAM,YAAY,eAAgB,aAAa,QAAQ,IAAI,CAAE;EAC7D,MAAM,eAAe,eAAe,iBAAiB,MAAM,SAAS,UAAU,KAAK;EAEnF,MAAM,cAAc,eAAe;GACjC,WAAW;GACX,aAAc,MAAM,UAAU,aAAa,QAAQ;GAEnD,cAAc,iBAAiB;GAC/B,UAAU;GAGV,oBAAoB,MAAM;EAC5B,CAAC;;EAGD,MAAM,gBAAgB,eAAe;GACnC,IAAI,CAAC,aAAa,OAAO,OAAO;GAChC,OAAO,CAAC,MAAM,WAAW,YAAY,MAAM,MAAM,UAAU;EAC7D,CAAC;;EAGD,MAAM,kBAAkB,eAAe;GACrC,MAAM,MAAM,iBAAiB;GAC7B,IAAI,CAAC,MAAM,SAAS,OAAO,IAAI,KAAK,QAAQ,WAAW;IAAE;IAAQ;GAAM,EAAE;GAEzE,MAAM,EAAE,OAAO,QAAQ,YAAY,MAAM;GACzC,MAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,UAAU,KAAK;GAChD,MAAM,KAAK,KAAK,IAAI,GAAG,MAAM,UAAU,KAAK;GAE5C,OAAO,IAAI,MAAM,MAAM,EAAE,CAAC,CAAC,KAAK,QAAQ,YAAY;IAAE;IAAQ,OAAO,OAAO;GAAO,EAAE;EACvF,CAAC;;;;;;EAOD,SAAS,eAAe,cAA0D;GAChF,IAAI,CAAC,MAAM,SAAS,OAAO,KAAA;GAC3B,OAAO;IAAE,gBAAgB,aAAa;IAAO,iBAAiB,eAAe;GAAE;EACjF;EAEA,MAAM,eAAe,eAAe;GAClC,MAAM,OAA+B,EAAE,WAAW,GAAG,MAAM,kBAAkB,IAAI;GACjF,IAAI,CAAC,MAAM,SAAS,OAAO;GAE3B,OAAO;IACL,GAAG;IACH,GAAG,YAAY,YAAY;GAC7B;EACF,CAAC;;;;;;EAWD,MAAM,iBAAiB,eAAgC;GACrD,MAAM,QAAyB,aAAa,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC;GACzE,iBAAiB,MAAM,SAAS,QAAQ,UAAU;IAChD,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK;KAAE,MAAM;KAAU;KAAQ;IAAM,CAAC;GACpE,CAAC;GACD,OAAO;EACT,CAAC;EAED,SAAS,WAAW,OAAwB;GAC1C,OAAO,eAAe,MAAM,SAAS,KAAK;EAC5C;EAEA,SAAS,iBAAiB,OAAuB;GAC/C,OAAO,eAAe,MAAM,WAAU,SAAQ,KAAK,SAAS,YAAY,KAAK,OAAO,UAAU,KAAK;EACrG;;EAGA,eAAe,qBAAqB,MAAoC;GAGtE,IAAI,MAAM,SACR,YAAY,cAAc,KAAK,SAAS,QAAQ,IAAI,KAAK,QAAQ,UAAU,KAAK;GAElF,MAAM,SAAS;GACf,MAAM,KAAK,KAAK,SAAS,QAAQ,eAAe,QAAQ,YAAY,KAAK,KAAK;GAC9E,SAAS,eAAe,EAAE,CAAC,EAAE,iBAAiB,EAAE,OAAO,UAAU,CAAC;EACpE;EAEA,MAAM,EACJ,aACA,YACA,oBACA,MAAM,iBACN,OAAO,aACP,yBACE,sBAAqC;GACvC,aAAa,eAAe;GAC5B,YAAY,KAAK;GACjB,OAAM,SAAS,KAAK,SAAS,QAAQ,eAAe,QAAQ,YAAY,KAAK,KAAK;GAClF,oBAAoB,eAAe,MAAM,WAAU,SAAQ,KAAK,SAAS,YAAY,WAAW,KAAK,OAAO,KAAK,CAAC;GAClH,WAAU,SAAQ,qBAAqB,IAAI;EAC7C,CAAC;EAED,MAAM,cAAc,eAAgB,WAAW,OAAO,SAAS,WAAW,WAAW,MAAM,OAAO,QAAQ,KAAA,CAAU;EAGpH,SAAS,eAAqB;GAC5B,IAAI,OAAO,SAAS,KAAK,OAAO;GAChC,QAAQ,IAAI;EACd;EAEA,SAAS,gBAAsB;GAC7B,QAAQ,KAAK;EACf;EAEA,SAAS,aAAmB;GAC1B,QAAQ,OAAO,MAAM;EACvB;EAGA,IAAI,cAAoD;EAGxD,IAAI,YAAY;EAChB,IAAI,WAAmC;EAEvC,SAAS,aAAa,OAAyB;GAC7C,OAAO,iBAAiB,SAAS,MAAM,SAAS;EAClD;EAEA,eAAe,SAAS,OAA8B;GACpD,MAAM,eAAe,MAAM;GAC3B,IAAI,CAAC,cAAc;GAEnB,UAAU,MAAM;GAChB,MAAM,aAAa,IAAI,gBAAgB;GACvC,WAAW;GACX,MAAM,MAAM,EAAE;GACd,cAAc,QAAQ;GAEtB,IAAI;IACF,MAAM,SAAS,MAAM,aAAa,OAAO,WAAW,MAAM;IAC1D,IAAI,QAAQ,WAAW;IACvB,cAAc,QAAQ;IACtB,eAAe,QAAQ;GACzB,SACO,OAAO;IACZ,IAAI,QAAQ,aAAa,aAAa,KAAK,GAAG;IAC9C,cAAc,QAAQ,CAAC;IACvB,eAAe,QAAQ;IACvB,KAAK,eAAe,KAAK;GAC3B,UACQ;IACN,IAAI,QAAQ,WAAW,cAAc,QAAQ;GAC/C;EACF;EAEA,SAAS,eAAe,OAAqB;GAC3C,IAAI,aAAa,aAAa,WAAW;GACzC,cAAc,iBAAiB;IAC7B,IAAI,MAAM,iBAAiB,KAAK,MAAM,KAAK,CAAC,CAAC,SAAS,MAAM,gBAAgB;IAC5E,KAAK,UAAU,MAAM,KAAK,CAAC;IAC3B,SAAc,MAAM,KAAK,CAAC;GAC5B,GAAG,MAAM,QAAQ;EACnB;;;;;;EAOA,SAAS,eAAqB;GAC5B,IAAI,aAAa,aAAa,WAAW;GACzC,cAAc;GACd,UAAU,MAAM;GAChB,WAAW;GACX,aAAa;GACb,cAAc,QAAQ;EACxB;EAEA,gBAAgB,YAAY;EAG5B,SAAS,QAAQ,OAAoB;GACnC,MAAM,QAAS,MAAM,OAA4B;GACjD,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,aAAa;GACb,eAAe,KAAK;EACtB;EAEA,SAAS,UAAgB;GACvB,IAAI,OAAO,OAAO;GAClB,aAAa;EACf;EAEA,SAAS,SAAS,OAAqB;GACrC,MAAM,QAAQ;GACd,MAAM,QAAQ;EAChB;EAGA,SAAS,aAAa,OAAe,OAAqB;GACxD,KAAK,qBAAqB,KAAK;GAC/B,KAAK,UAAU,KAAyC;GACxD,SAAS,KAAK;GACd,cAAc;EAChB;EAEA,SAAS,eAAe,OAAqB;GAC3C,MAAM,OAAO,eAAe,MAAM,MAAM;GACxC,MAAM,MAAM,KAAK,QAAQ,KAAK;GAC9B,IAAI,OAAO,GAAG,KAAK,OAAO,KAAK,CAAC;QAC3B,KAAK,KAAK,KAAK;GACpB,KAAK,qBAAqB,IAAI;GAC9B,KAAK,UAAU,IAAwC;GACvD,SAAS,EAAE;GACX,IAAI,MAAM,eAAe,cAAc;QAClC,SAAc,UAAU;EAC/B;EAEA,SAAS,aAAa,QAA4C;GAChE,IAAI,OAAO,SAAS,OAAO,UAAU;GACrC,IAAI,MAAM,UAAU,eAAe,OAAO,KAAK;QAC1C,aAAa,OAAO,OAAO,OAAO,KAAK;EAC9C;EAEA,SAAS,eAAqB;GAC5B,IAAI,OAAO,OAAO;GAElB,MAAM,IAAI,MAAM,MAAM,KAAK;GAC3B,IAAI,CAAC,GAAG;GACR,IAAI,MAAM,UAAU,eAAe,CAAC;QAC/B,aAAa,GAAG,OAAO,CAAC,CAAC;EAChC;EAEA,SAAS,YAAY,OAAe,aAAyB,YAAkB;GAC7E,IAAI,OAAO,OAAO;GAClB,MAAM,OAAO,eAAe,MAAM,QAAO,MAAK,MAAM,KAAK;GACzD,KAAK,qBAAqB,IAAI;GAC9B,KAAK,UAAU,IAAwC;GACvD,SAAc,UAAU;EAC1B;EAEA,SAAS,iBAAuB;GAC9B,IAAI,OAAO,OAAO;GAClB,MAAM,OAAQ,MAAM,WAAW,CAAC,IAAI;GACpC,KAAK,qBAAqB,IAAI;GAC9B,KAAK,UAAU,IAAI;GACnB,KAAK,OAAO;GACZ,SAAS,EAAE;GACX,SAAc,UAAU;EAC1B;EAEA,MAAM,YAAY,eAChB,kBAAkB,SAAS,CAAC,OAAO,UAAU,aAAa,SAAS,MAAM,MAAM,SAAS,EAC1F;EAWA,MAAM,WAAW,IAA0D,CAAC,CAAC;EAE7E,SAAS,WAAW,OAAe;GACjC,QAAQ,OAAsB;IAC5B,SAAS,MAAM,SAAS;GAC1B;EACF;;;;;EAMA,MAAM,WAAW,eAAe,sBAAsB,MAAM,OAAO,CAAC;EAIpE,MAAM,cAAc,eAAe,gBAAgB,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC;EAEjF,MAAM,aAAa,eAAuB;GACxC,aAAa,YAAY;GACzB,aAAY,UAAS,SAAS,MAAM,MAAM,EAAE;GAC5C,mBAAmB;GAEnB,YAAY;GACZ,aAAa,SAAS;IACpB,IAAI,SAAS,OAAO,WAAW;IAC/B,OAAO;GACT;GAGA,mBAAmB,SAAS;EAC9B,CAAC;EAED,SAAS,UAAU,OAAqB;GACtC,MAAM,OAAO,gBAAgB,MAAM,SAAS;GAC5C,IAAI,OAAO,GAAG;IACZ,WAAW;IACX;GACF;GAEA,WAAgB,SAAS,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,IAAI,CAAC;EAC7D;EAEA,SAAS,cAAc,OAAsB,OAAe,OAAqB;GAC/E,IAAI,WAAW,qBAAqB,KAAK,GAAG;GAE5C,QAAQ,MAAM,KAAd;IACE,KAAK;IACL,KAAK;KACH,MAAM,eAAe;KACrB,YAAY,aAAa,UAAU,KAAK,CAAC;KACzC;IACF,KAAK;KACH,MAAM,eAAe;KACrB,WAAW;KACX;IACF,SAGE,IAAI,MAAM,IAAI,WAAW,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,QAAQ,WAAW;GAChG;EACF;EAGA,SAAS,UAAU,OAA4B;GAG7C,IAAI,iBAAiB,KAAK,GAAG;GAC7B,IAAI,OAAO,OAAO;GAIlB,IAAI,MAAM,QAAQ,eAAe,MAAM,YAAY,MAAM,UAAU,MAAM,eAAe,MAAM,QAAQ;IACpG,MAAM,eAAe;IACrB,cAAc;IACd,UAAU,eAAe,MAAM,SAAS,CAAC;IACzC;GACF;GAEA,IAAI,CAAC,KAAK,SAAS,CAAC,aAAa,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,GAAG;IAC/D,MAAM,eAAe;IACrB,aAAa;IACb;GACF;GAGA,IAAI,KAAK,SAAS,qBAAqB,KAAK,GAAG;GAE/C,QAAQ,MAAM,KAAd;IACE,KAAK,SAAS;KACZ,IAAI,CAAC,KAAK,OAAO;KACjB,MAAM,eAAe;KACrB,MAAM,OAAO,WAAW;KACxB,IAAI,MAAM,SAAS,UAAU,aAAa,KAAK,MAAM;UAChD,IAAI,MAAM,SAAS,SAAS,aAAa,OAAO,aAAa;KAClE;IACF;IACA,KAAK;KAEH,IAAI,MAAM,YAAY,MAAM,UAAU,MAAM,eAAe,MAAM,QAAQ;MACvE,MAAM,eAAe;MACrB,YAAY,eAAe,MAAM,eAAe,MAAM,SAAS,EAAE;KACnE;KACA;IACF,KAAK;KACH,cAAc;KACd;GACJ;EACF;EAGA,MACE,CAAC,2BAA2B,MAAM,QAAQ,IACzC,CAAC,OAAO,cAAc;GACrB,IAAI,UAAU;GAEd,IAAI,KAAK,SAAS,MAAM,OAAO;GAC/B,SAAS,KAAK;EAChB,GACA,EAAE,WAAW,KAAK,CACpB;EAEA,MAAM,OAAO,WAAW;GACtB,IAAI,QAAQ;IACV,gBAAgB;IAChB;GACF;GAEA,YAAY;GACZ,IAAI,MAAM,UACR,SAAS,EAAE;QAIX,SAAS,oBAAoB,KAAK;EAEtC,CAAC;EAED,MAAM,mBAAmB,eAAgB,MAAM,mBAAmB,SAAS,MAAO;EAIlF,MAAM,EAAE,QAAQ,cAAc,SAAS,oBAAoB,gBAAgB;EAM3E,MAAM,aAAa,gBAAgB;;uCAIjC,mBA0PM,OAAA;aAzPA;IAAJ,KAAI;IAEJ,wBAAA;IACA,OAAM;IACJ,WAAO,OAAA,OAAA,OAAA,MAAA,GAAA,SAAE,MAAA,SAAA,KAAA,MAAA,SAAA,CAAA,CAAA,GAAA,IAAA;IACV,YAAQ,OAAA,OAAA,OAAA,MAAA,GAAA,SAAE,MAAA,UAAA,KAAA,MAAA,UAAA,CAAA,CAAA,GAAA,IAAA;;IAEX,mBAuGM,OAAA;KAtGJ,8BAAA;KACC,OAAK,eAAE,MAAA,sBAAA,CAAsB,CAAA;MAAA,MAAS,MAAA,YAAA;MAAY,UAAY,MAAA,UAAA;MAAU,SAAW,MAAA,SAAA;KAAS,CAAA,CAAA;KAC5F,aAAS,cAAe,YAAU,CAAA,QAAA,SAAA,CAAA;;KAG3B,MAAA,SAAA,KAAA,UAAA,GADR,mBAQO,QAAA;;eAND;MAAJ,KAAI;MACJ,+BAAA;MACA,OAAM;MACL,OAAK,eAAE,MAAA,WAAA,CAAW;SAEnB,WAAsB,KAAA,QAAA,UAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAIR,QAAA,YAAA,UAAA,IAAA,GACd,mBAgBS,UAAA,EAAA,KAAA,EAAA,GAAA,WAfuB,gBAAA,QAAtB,QAAQ,cAAS;0BAD3B,YAgBS,gBAAA;OAdN,KAAK,OAAO;;OACZ,KAAK,WAAW,SAAS;OAC1B,6BAAA;OACC,MAAM,QAAA;OACN,MAAM,QAAA;OACN,MAAM,SAAA;OACN,QAAQ,QAAA;OACR,UAAQ,CAAG,OAAA;OACX,gBAAc,MAAA,CAAA,CAAC,CAAA,+BAAA,kBAAA,EAAA,OAA2D,OAAO,MAAK,CAAA;OACtF,mBAAiB;OACjB,WAAM,WAAE,YAAY,OAAO,KAAK;OAChC,YAAO,WAAE,cAAc,QAAQ,WAAW,OAAO,KAAK;;8BAEP,CAAhD,mBAAgD,QAAhD,YAAgD,gBAAtB,OAAO,KAAK,GAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;KAI1C,mBA2BC,SAAA;MA1BE,IAAI,WAAA;eACD;MAAJ,KAAI;MACJ,8BAAA;MACA,eAAY;MACZ,MAAK;MACL,cAAa;MACb,YAAW;MACX,MAAK;MACJ,OAAO,MAAA;MACP,UAAU,MAAA,UAAA;MACV,aAAa,aAAA,SAAgB,QAAA,WAAW,KAAA,IAAY,QAAA;MACpD,cAAY,QAAA;MACZ,gBAAc,MAAA,SAAA,IAAS,SAAY,KAAA;MACnC,oBAAkB,YAAA;MAClB,iBAAe,MAAA,UAAA,IAAU,SAAY,KAAA;MACrC,iBAAe,MAAA,UAAA,IAAU,SAAY,KAAA;MACrC,UAAU,MAAA,UAAA;MACX,iBAAc;MACb,qBAAmB,iBAAA;MACnB,iBAAe,MAAA,IAAA,IAAO,MAAA,SAAA,IAAY,KAAA;MAClC,yBAAuB,MAAA,kBAAA;MACvB,iBAAe,MAAA,IAAA,IAAI,SAAA;MACpB,OAAM;MACE;MACA;MACE;;KAIJ,MAAA,SAAA,KAAA,UAAA,GADR,mBAQO,QAAA;;eAND;MAAJ,KAAI;MACJ,+BAAA;MACA,OAAM;MACL,OAAK,eAAE,MAAA,WAAA,CAAW;SAEnB,WAAsB,KAAA,QAAA,UAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAKhB,UAAA,SAAA,UAAA,GADR,mBAMO,QANP,YAMO,CADL,YAA0E,MAAA,qBAAA,GAAA;MAAxD,OAAM;MAA6B,eAAY;aAItD,UAAA,SAAA,UAAA,GADb,mBAUS,UAAA;;MARP,MAAK;MACL,eAAY;MACZ,8BAAA;MACA,OAAM;MACL,cAAY,mBAAA;MACZ,SAAO;SAER,YAAkD,MAAA,SAAA,GAAA;MAA3C,OAAM;MAAgB,eAAY;0CAG3C,mBAMO,QANP,YAMO,CADL,YAA4D,MAAA,oBAAA,GAAA;MAA3C,OAAM;MAAgB,eAAY;;;IAKvC,QAAA,QAAA,UAAA,IAAA,GACd,mBAMC,UAAA,EAAA,KAAA,EAAA,GAAA,WALiB,eAAA,QAAT,UAAK;yBADd,mBAMC,SAAA;MAJE,KAAG,UAAY,OAAO,KAAK;MAC5B,MAAK;MACJ,MAAM,QAAA;MACN,OAAO,OAAO,KAAK;;;kBAIxB,YA6HW,UAAA;KA7HA,IAAI,MAAA,YAAA;KAAe,UAAQ,CAAG,MAAA,eAAA;QACvC,YA2Ha,YAAA;KA1HV,sBAAoB,MAAA,kBAAA,CAAkB,CAAC;KACvC,oBAAkB,MAAA,kBAAA,CAAkB,CAAC;KACrC,kBAAgB,MAAA,kBAAA,CAAkB,CAAC;KACnC,sBAAoB,MAAA,kBAAA,CAAkB,CAAC;KACvC,oBAAkB,MAAA,kBAAA,CAAkB,CAAC;KACrC,kBAAgB,MAAA,kBAAA,CAAkB,CAAC;;4BAoH9B,CAAA,eAlHN,mBAkHM,OAlHN,WAkHM;eAhHA;MAAJ,KAAI;MACJ,eAAY;QACJ,MAAA,UAAA,GAAU;MAClB,8BAAA;MACA,wBAAA;MACC,OAAO,MAAA,aAAA;UAER,mBAwGM,OAAA,EAxGA,OAAK,eAAE,MAAA,wBAAA,CAAwB,EAAA,GAAA,CAM3B,cAAA,SAAiB,gBAAA,MAAgB,UAAA,UAAA,GADzC,mBAkEM,OAAA;;MAhEH,IAAI,MAAA,SAAA;eACD;MAAJ,KAAI;MACJ,gCAAA;MACC,mBAAiB,QAAA,UAAO,KAAQ,KAAA;MACjC,OAAK,eAAA,CAAC,qBACE,QAAA,UAAO,kBAAA,EAAA,CAAA;MACd,OAAK,eAAE,aAAA,KAAY;MACpB,MAAK;MACJ,wBAAsB,QAAA,WAAQ,SAAY,KAAA;SASnC,cAAA,SAAA,UAAA,GADR,mBAgBS,UAhBT,WAgBS;;MAdN,IAAI,eAAA;MACL,MAAK;MACL,MAAK;MACL,iBAAc;MACd,UAAS;QACD,eAAc,CAAA,GAAA;MACtB,eAAY;MACZ,mCAAA;MACC,OAAO,MAAA,uBAAA,CAAuB,CAAA;OAAA,UAAA;OAAA,QAA4B,MAAA,UAAA,CAAU,EAAE,SAAI;MAAA,CAAA;MAC1E,aAAS,OAAA,OAAA,OAAA,KAAA,oBAAV,CAAA,GAAkB,CAAA,SAAA,CAAA;MACjB,SAAO;MACP,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,YAAA,QAAW;yBAEpB,MAAA,CAAA,CAAC,CAAA,6BAAA,mBAAA,EAAA,OAAwD,MAAA,MAAM,KAAI,EAAA,CAAA,CAAA,GAAA,IAAA,UAAA,KAAA,mBAAA,IAAA,IAAA,IAAA,UAAA,IAAA,GAGxE,mBA6BS,UAAA,MAAA,WA5BkC,gBAAA,QAAe,EAA/C,QAAM,OAAS,kBAAW;0BADrC,mBA6BS,UA7BT,WA6BS;OA3BN,IAAI,YAAY,WAAW;OAC3B,KAAK,OAAO;;OACZ,MAAM,OAAO,QAAA,WAAW,MAAA,WAAA,CAAW,CAAC,QAAQ,cAAc,UAAA,OAAW,EAAE;OACxE,+BAAA;OACA,MAAK;OACL,MAAK;OACL,UAAS;4BACD,eAAe,cAAc,UAAA,KAAS,GAAA;OAC7C,UAAU,OAAO;OACjB,iBAAe,WAAW,OAAO,KAAK,IAAA,SAAA;OACtC,iBAAe,OAAO,WAAQ,SAAY,KAAA;OAC1C,OAAO,MAAA,uBAAA,CAAuB,CAAA;oBAAiC,OAAO;gBAAoC,YAAA,UAAgB,OAAO;;OAIjI,aAAS,OAAA,OAAA,OAAA,KAAA,oBAAV,CAAA,GAAkB,CAAA,SAAA,CAAA;OACjB,UAAK,WAAE,aAAa,MAAM;OAC1B,cAAS,WAAE,YAAA,QAAc,iBAAiB,OAAO,KAAK;WAEvD,WAOO,KAAA,QAAA,UAAA;OAPsB;OAAS,UAAU,WAAW,OAAO,KAAK;eAOhE,CANL,mBAKO,QALP,aAKO,CAJL,mBAEO,QAFP,aAEO,CADY,WAAW,OAAO,KAAK,KAAA,UAAA,GAAxC,YAAiG,MAAA,aAAA,GAAA;;OAAtD,OAAM;4CAEnD,mBAAgD,QAAhD,aAAgD,gBAAtB,OAAO,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,IAAA,CAAA,GAAA,IAAA,UAAA;kEAYtC,UAAA,SAAa,cAAA,SAAiB,UAAA,SAAA,UAAA,GADtC,mBAyBM,OAAA;;MAvBJ,+BAAA;MACA,MAAK;MACL,aAAU;MACT,OAAK,eAAE,MAAA,sBAAA,CAAsB;SAEd,UAAA,SAAA,UAAA,GACd,mBAKO,QALP,aAKO,CAJL,WAGO,KAAA,QAAA,WAAA,CAAA,SAAA,CAFL,YAA0E,MAAA,qBAAA,GAAA;MAAxD,OAAM;MAA6B,eAAY;SACjE,mBAAsC,QAAA,MAAA,gBAA7B,oBAAA,KAAmB,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA,CAAA,KAIb,cAAA,SAAA,UAAA,GACnB,mBAAiE,QAAjE,aAAiE,gBAA9B,qBAAA,KAAoB,GAAA,CAAA,MAAA,UAAA,GAGvD,mBAIO,QAJP,aAIO,CAHL,WAEO,KAAA,QAAA,SAAA,CAAA,SAAA,CAAA,gBAAA,gBADF,sBAAA,KAAqB,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA,CAAA,EAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA,CAAA,GAAA,EAAA,GAAA,CAAA,CAAA,OA3G1B,MAAA,IAAA,CAAI,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;aApIU;IAAa,SAAW,MAAA,IAAA;IAAI,SAAW;GAAmB,CAAA,CAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrBadge-BsoocpF-.js","names":[],"sources":["../../../../src/components/GrBadge/GrBadge.vue","../../../../src/components/GrBadge/GrBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nimport { useGrComponentProp } from '../GrConfigProvider/context'\n\nexport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from './grBadgeStyles'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\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\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrBadge', useAttrs(), { variant: 'tone' })\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\ndefineSlots<{\n /** Содержимое метки. */\n default?: () => any\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, useAttrs } from 'vue'\n\nimport { useGrComponentProp } from '../GrConfigProvider/context'\n\nexport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from './grBadgeStyles'\n\nimport { warnRenamedProp } from '../shared/renamedProp'\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\n// `variant` переименован в `tone`: незнакомый атрибут Vue сажает на корневой\n// узел, поэтому старое имя не роняет ни типы, ни рантайм — компонент молча\n// рисуется дефолтным тоном.\nwarnRenamedProp('GrBadge', useAttrs(), { variant: 'tone' })\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\ndefineSlots<{\n /** Содержимое метки. */\n default?: () => any\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":";;;;;;;;;;;;;;;;;;;;;;EAuBA,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;EAK1F,gBAAgB,WAAW,SAAS,GAAG,EAAE,SAAS,OAAO,CAAC;EAE1D,MAAM,YAAY,eAAe;GAC/B,OAAO,aAAa;IAClB,MAAM,aAAa;IACnB,MAAM,MAAM;IACZ,MAAM,aAAa;IACnB,QAAQ,eAAe;GACzB,CAAC;EACH,CAAC;;uBAUC,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"}