@feugene/granularity 0.16.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/dist/chunks/chevron-left-D8AUBAM7.js +23 -0
  2. package/dist/chunks/chevron-left-D8AUBAM7.js.map +1 -0
  3. package/dist/chunks/{chevron-right-BfqMVpbB.js → chevron-right-C6Z01h0X.js} +1 -22
  4. package/dist/chunks/chevron-right-C6Z01h0X.js.map +1 -0
  5. package/dist/chunks/config-8Pcutu1j2.js.map +1 -0
  6. package/dist/chunks/{config-K0C2tcLB.js → config-9PYRZlKE.js} +1 -1
  7. package/dist/chunks/{config-K0C2tcLB.js.map → config-9PYRZlKE.js.map} +1 -1
  8. package/dist/chunks/{config-Zc_bUvZY2.js → config-BDfTXHo12.js} +1 -1
  9. package/dist/chunks/{config-Zc_bUvZY2.js.map → config-BDfTXHo12.js.map} +1 -1
  10. package/dist/chunks/config-BE6n20np2.js.map +1 -0
  11. package/dist/chunks/config-BJ7Bv_G2.js +148 -0
  12. package/dist/chunks/config-BJ7Bv_G2.js.map +1 -0
  13. package/dist/chunks/config-BKvX0q102.js.map +1 -0
  14. package/dist/chunks/{config-CpwwkFBO.js → config-Baj7Wvx-.js} +1 -1
  15. package/dist/chunks/{config-CpwwkFBO.js.map → config-Baj7Wvx-.js.map} +1 -1
  16. package/dist/chunks/{config-D8tSR2uS.js → config-Be9k9bOf.js} +2 -8
  17. package/dist/chunks/config-Be9k9bOf.js.map +1 -0
  18. package/dist/chunks/config-BoNR7_PD2.js.map +1 -0
  19. package/dist/chunks/{config-DxCTVBfx.js → config-BsPDQ0dS.js} +2 -2
  20. package/dist/chunks/config-BsPDQ0dS.js.map +1 -0
  21. package/dist/chunks/{config-DpqOnJiz2.js → config-CDat1wXf2.js} +1 -1
  22. package/dist/chunks/config-CDat1wXf2.js.map +1 -0
  23. package/dist/chunks/config-CN2ULSQL.js.map +1 -0
  24. package/dist/chunks/{config-Cw6TDzWv2.js → config-COpuWGr_.js} +1 -1
  25. package/dist/chunks/config-COpuWGr_.js.map +1 -0
  26. package/dist/chunks/config-CY0_6cMi2.js.map +1 -0
  27. package/dist/chunks/{config-B-g6lT19.js → config-DA6SnEoy.js} +5 -2
  28. package/dist/chunks/config-DA6SnEoy.js.map +1 -0
  29. package/dist/chunks/config-DAX8Deh1.js +68 -0
  30. package/dist/chunks/config-DAX8Deh1.js.map +1 -0
  31. package/dist/chunks/config-DKdlqXiY.js.map +1 -1
  32. package/dist/chunks/{config-CvC0sX8p.js → config-MRvhUl-O.js} +1 -1
  33. package/dist/chunks/{config-CvC0sX8p.js.map → config-MRvhUl-O.js.map} +1 -1
  34. package/dist/chunks/config-Rw9OyW702.js.map +1 -0
  35. package/dist/chunks/config-ScuvHw6k2.js +42 -0
  36. package/dist/chunks/config-ScuvHw6k2.js.map +1 -0
  37. package/dist/chunks/config-T3qdQBgU2.js.map +1 -0
  38. package/dist/chunks/config-eXB87DOi.js.map +1 -0
  39. package/dist/chunks/context-BSYR7RZs.js.map +1 -1
  40. package/dist/chunks/{en-B3xMeswz.js → en-COdzxgAX.js} +30 -5
  41. package/dist/chunks/en-COdzxgAX.js.map +1 -0
  42. package/dist/chunks/{es-CA-mkU--.js → es-ClqJ-4hr.js} +30 -5
  43. package/dist/chunks/es-ClqJ-4hr.js.map +1 -0
  44. package/dist/chunks/{es-Bv8vvqdT.js → es-DGMHwknJ.js} +3 -3
  45. package/dist/chunks/{es-Bv8vvqdT.js.map → es-DGMHwknJ.js.map} +1 -1
  46. package/dist/chunks/generated-BRkbhyxk.js +3270 -0
  47. package/dist/chunks/generated-BRkbhyxk.js.map +1 -0
  48. package/dist/chunks/grKbdStyles-CeZhGQHr.js +43 -0
  49. package/dist/chunks/grKbdStyles-CeZhGQHr.js.map +1 -0
  50. package/dist/chunks/{grTextareaStyles-CWI2SqC8.js → grTextareaStyles-iOcHuVMM.js} +2 -2
  51. package/dist/chunks/{grTextareaStyles-CWI2SqC8.js.map → grTextareaStyles-iOcHuVMM.js.map} +1 -1
  52. package/dist/chunks/{granular-provider-CtrXGa7c.js → granular-provider-C6uAujdJ.js} +23 -21
  53. package/dist/chunks/granular-provider-C6uAujdJ.js.map +1 -0
  54. package/dist/chunks/grip-vertical-lRha2S7q.js +16 -0
  55. package/dist/chunks/grip-vertical-lRha2S7q.js.map +1 -0
  56. package/dist/chunks/icon-Dag2tIMs.js +21 -0
  57. package/dist/chunks/icon-Dag2tIMs.js.map +1 -0
  58. package/dist/chunks/{keyboard-zYvvyHd4.js → keyboard-DE2yhz_b.js} +10 -1
  59. package/dist/chunks/keyboard-DE2yhz_b.js.map +1 -0
  60. package/dist/chunks/{optionFilter-BVY988pC.js → optionFilter-DYw9XPPx.js} +1 -71
  61. package/dist/chunks/optionFilter-DYw9XPPx.js.map +1 -0
  62. package/dist/chunks/{useOverlayLayer-BupuP_hs.js → overlayStack-C8cfsqXa.js} +11 -61
  63. package/dist/chunks/overlayStack-C8cfsqXa.js.map +1 -0
  64. package/dist/chunks/{portalRoot-CWD4zHBm.js → portalRoot-CVhCgdG6.js} +10 -1
  65. package/dist/chunks/{portalRoot-CWD4zHBm.js.map → portalRoot-CVhCgdG6.js.map} +1 -1
  66. package/dist/chunks/{ru-COWxgh2A.js → ru-9z8ix45c.js} +30 -5
  67. package/dist/chunks/ru-9z8ix45c.js.map +1 -0
  68. package/dist/chunks/useDragSort-0l47h4yb.js +265 -0
  69. package/dist/chunks/useDragSort-0l47h4yb.js.map +1 -0
  70. package/dist/chunks/{useModalOverlay-DCM4d3yA.js → useModalOverlay-DrQVYwwA.js} +2 -2
  71. package/dist/chunks/{useModalOverlay-DCM4d3yA.js.map → useModalOverlay-DrQVYwwA.js.map} +1 -1
  72. package/dist/chunks/{usePortalTarget-RyzEgNTb.js → usePortalTarget-Cn-Wiz5d.js} +1 -1
  73. package/dist/chunks/{usePortalTarget-RyzEgNTb.js.map → usePortalTarget-Cn-Wiz5d.js.map} +1 -1
  74. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CQ9T7Lly.js → GrAutocomplete-gY4Xopza.js} +35 -26
  75. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-gY4Xopza.js.map +1 -0
  76. package/dist/components/GrAutocomplete/index.js +1 -1
  77. package/dist/components/GrAutocomplete/styles.css +1 -1
  78. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-BCNGDS6k.js → GrBreadcrumbs-DS_-PBVt.js} +8 -7
  79. package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-DS_-PBVt.js.map +1 -0
  80. package/dist/components/GrBreadcrumbs/index.js +1 -1
  81. package/dist/components/GrCollapse/chunks/{GrCollapse-B2Fc6A9r.js.map → GrCollapse-Kk0DE8tb.js.map} +1 -1
  82. package/dist/components/GrCollapse/index.js +1 -1
  83. package/dist/components/GrColorPicker/chunks/{GrColorPicker-bnYBRGk8.js → GrColorPicker-CkBTzkqn.js} +4 -4
  84. package/dist/components/GrColorPicker/chunks/{GrColorPicker-bnYBRGk8.js.map → GrColorPicker-CkBTzkqn.js.map} +1 -1
  85. package/dist/components/GrColorPicker/index.js +2 -2
  86. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-MWDYDPNC.js → GrCommandPalette-BugTlGdj.js} +41 -16
  87. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BugTlGdj.js.map +1 -0
  88. package/dist/components/GrCommandPalette/index.js +2 -2
  89. package/dist/components/GrCommandPalette/styles.css +1 -1
  90. package/dist/components/GrConfigProvider/index.js +2 -2
  91. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DaQmlp1_.js → GrConfirmDialog-Ds5T0GiR.js} +1 -1
  92. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DaQmlp1_.js.map → GrConfirmDialog-Ds5T0GiR.js.map} +1 -1
  93. package/dist/components/GrConfirmDialog/index.js +1 -1
  94. package/dist/components/GrDataTable/chunks/{GrDataTable-DVFho9Cj.js → GrDataTable-CYe2A2WA.js} +498 -48
  95. package/dist/components/GrDataTable/chunks/GrDataTable-CYe2A2WA.js.map +1 -0
  96. package/dist/components/GrDataTable/index.js +2 -2
  97. package/dist/components/GrDialog/chunks/{GrDialog-AE1gIZuz.js → GrDialog-C3tYaA8v.js} +1 -1
  98. package/dist/components/GrDialog/chunks/{GrDialog-AE1gIZuz.js.map → GrDialog-C3tYaA8v.js.map} +1 -1
  99. package/dist/components/GrDialog/index.js +1 -1
  100. package/dist/components/GrDialogService/chunks/{GrDialogService-B4DYWDkA.js → GrDialogService-CTBiiJDI.js} +3 -3
  101. package/dist/components/GrDialogService/chunks/{GrDialogService-B4DYWDkA.js.map → GrDialogService-CTBiiJDI.js.map} +1 -1
  102. package/dist/components/GrDialogService/index.js +1 -1
  103. package/dist/components/GrDivider/chunks/{GrDivider-B2Xwc_QO.js → GrDivider-xxq1_fIl.js} +1 -1
  104. package/dist/components/GrDivider/chunks/{GrDivider-B2Xwc_QO.js.map → GrDivider-xxq1_fIl.js.map} +1 -1
  105. package/dist/components/GrDivider/index.js +2 -2
  106. package/dist/components/GrDrawer/chunks/{GrDrawer-DQwi6aRw.js → GrDrawer-BSSuiNHU.js} +1 -1
  107. package/dist/components/GrDrawer/chunks/{GrDrawer-DQwi6aRw.js.map → GrDrawer-BSSuiNHU.js.map} +1 -1
  108. package/dist/components/GrDrawer/index.js +1 -1
  109. package/dist/components/GrDropdown/chunks/{GrDropdown-BLng6V1m.js → GrDropdown-Bh6UK2M5.js} +3 -3
  110. package/dist/components/GrDropdown/chunks/{GrDropdown-BLng6V1m.js.map → GrDropdown-Bh6UK2M5.js.map} +1 -1
  111. package/dist/components/GrDropdown/index.js +1 -1
  112. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BImk2Vs7.js → GrDropdownMenu-LyfEFUbg.js} +2 -2
  113. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BImk2Vs7.js.map → GrDropdownMenu-LyfEFUbg.js.map} +1 -1
  114. package/dist/components/GrDropdownMenu/index.js +2 -2
  115. package/dist/components/GrFileUpload/chunks/{GrFileUpload-D0B-BEsd.js → GrFileUpload-DuDzC43S.js} +2 -2
  116. package/dist/components/GrFileUpload/chunks/{GrFileUpload-D0B-BEsd.js.map → GrFileUpload-DuDzC43S.js.map} +1 -1
  117. package/dist/components/GrFileUpload/index.js +1 -1
  118. package/dist/components/GrImageViewer/chunks/{GrImageViewer-BeCnE8XQ.js → GrImageViewer-DfD4euH5.js} +48 -23
  119. package/dist/components/GrImageViewer/chunks/GrImageViewer-DfD4euH5.js.map +1 -0
  120. package/dist/components/GrImageViewer/index.js +1 -1
  121. package/dist/components/GrInput/chunks/{GrInput-DD2gYdqS.js → GrInput-2wd7Crs0.js} +1 -1
  122. package/dist/components/GrInput/chunks/GrInput-2wd7Crs0.js.map +1 -0
  123. package/dist/components/GrInput/index.js +1 -1
  124. package/dist/components/GrInputTag/chunks/{GrInputTag-DmNunixz.js → GrInputTag-CyV2eY7Z.js} +20 -33
  125. package/dist/components/GrInputTag/chunks/GrInputTag-CyV2eY7Z.js.map +1 -0
  126. package/dist/components/GrInputTag/index.js +1 -1
  127. package/dist/components/GrKbd/chunks/{GrKbd-DRFPvW3G.js → GrKbd-DU2lBkRV.js} +218 -52
  128. package/dist/components/GrKbd/chunks/GrKbd-DU2lBkRV.js.map +1 -0
  129. package/dist/components/GrKbd/index.js +3 -3
  130. package/dist/components/GrModal/chunks/{GrModal-B8I78T0N.js → GrModal-CbMMr6W1.js} +1 -1
  131. package/dist/components/GrModal/chunks/{GrModal-B8I78T0N.js.map → GrModal-CbMMr6W1.js.map} +1 -1
  132. package/dist/components/GrModal/index.js +1 -1
  133. package/dist/components/GrNumberInput/chunks/{GrNumberInput-CZOyPz4J.js → GrNumberInput-B5CHZDBj.js} +2 -1
  134. package/dist/components/GrNumberInput/chunks/{GrNumberInput-CZOyPz4J.js.map → GrNumberInput-B5CHZDBj.js.map} +1 -1
  135. package/dist/components/GrNumberInput/index.js +1 -1
  136. package/dist/components/GrPagination/chunks/{GrPagination-B6N0QZBf.js → GrPagination-BM4FrD6Q.js} +4 -4
  137. package/dist/components/GrPagination/chunks/GrPagination-BM4FrD6Q.js.map +1 -0
  138. package/dist/components/GrPagination/index.js +1 -1
  139. package/dist/components/GrPopover/chunks/{GrPopover-B1eRCOoO.js → GrPopover-DMH4Jjst.js} +1 -1
  140. package/dist/components/GrPopover/chunks/{GrPopover-B1eRCOoO.js.map → GrPopover-DMH4Jjst.js.map} +1 -1
  141. package/dist/components/GrPopover/index.js +1 -1
  142. package/dist/components/GrProgressBar/chunks/{GrProgressBar-BiHnaitu.js → GrProgressBar-xwbNM9QB.js} +1 -1
  143. package/dist/components/GrProgressBar/chunks/GrProgressBar-xwbNM9QB.js.map +1 -0
  144. package/dist/components/GrProgressBar/index.js +1 -1
  145. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-CTogjcHz.js → GrProgressCircle-B8olXRo5.js} +1 -1
  146. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-CTogjcHz.js.map → GrProgressCircle-B8olXRo5.js.map} +1 -1
  147. package/dist/components/GrProgressCircle/index.js +2 -2
  148. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ct174NVD.js → GrPromptDialog-Pwr9NPxK.js} +3 -3
  149. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ct174NVD.js.map → GrPromptDialog-Pwr9NPxK.js.map} +1 -1
  150. package/dist/components/GrPromptDialog/index.js +1 -1
  151. package/dist/components/GrRadio/chunks/{GrRadio-DksDro_U.js → GrRadio-DWiRaMzt.js} +8 -32
  152. package/dist/components/GrRadio/chunks/GrRadio-DWiRaMzt.js.map +1 -0
  153. package/dist/components/GrRadio/index.js +1 -1
  154. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BOIhejE6.js → GrRadioGroup-DIuBw0wk.js} +34 -27
  155. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-DIuBw0wk.js.map +1 -0
  156. package/dist/components/GrRadioGroup/index.js +1 -1
  157. package/dist/components/GrRating/chunks/{GrRating-JrHXz0Rv.js → GrRating-DVFH3lYu.js} +10 -9
  158. package/dist/components/GrRating/chunks/GrRating-DVFH3lYu.js.map +1 -0
  159. package/dist/components/GrRating/index.js +2 -2
  160. package/dist/components/GrSegmented/chunks/{GrSegmented-BQIlH5KI.js → GrSegmented-CQw3ijqa.js} +47 -65
  161. package/dist/components/GrSegmented/chunks/GrSegmented-CQw3ijqa.js.map +1 -0
  162. package/dist/components/GrSegmented/index.js +1 -1
  163. package/dist/components/GrSelect/chunks/{GrSelect-aqkPCqjd.js → GrSelect-B-Voni7D.js} +69 -55
  164. package/dist/components/GrSelect/chunks/GrSelect-B-Voni7D.js.map +1 -0
  165. package/dist/components/GrSelect/index.js +1 -1
  166. package/dist/components/GrSelect/styles.css +1 -1
  167. package/dist/components/GrSidebar/chunks/{GrSidebar-DRGnnViw.js → GrSidebar-i7AxjhY_.js} +3 -2
  168. package/dist/components/GrSidebar/chunks/{GrSidebar-DRGnnViw.js.map → GrSidebar-i7AxjhY_.js.map} +1 -1
  169. package/dist/components/GrSidebar/index.js +2 -2
  170. package/dist/components/GrSkeleton/index.js +1 -1
  171. package/dist/components/GrSlider/chunks/{GrSlider-DkU9IQSX.js → GrSlider-CyhVsNKn.js} +21 -34
  172. package/dist/components/GrSlider/chunks/GrSlider-CyhVsNKn.js.map +1 -0
  173. package/dist/components/GrSlider/index.js +2 -2
  174. package/dist/components/GrSortableList/chunks/GrSortableList-XB7mXM1W.js +277 -0
  175. package/dist/components/GrSortableList/chunks/GrSortableList-XB7mXM1W.js.map +1 -0
  176. package/dist/components/GrSortableList/index.js +3 -0
  177. package/dist/components/GrSplitter/chunks/{GrSplitter-7To8jbJ8.js → GrSplitter-BzxVQwbY.js} +17 -37
  178. package/dist/components/GrSplitter/chunks/GrSplitter-BzxVQwbY.js.map +1 -0
  179. package/dist/components/GrSplitter/index.js +2 -2
  180. package/dist/components/GrStatistic/chunks/{GrStatistic-CttyoYPT.js → GrStatistic-DOsM6s5V.js} +54 -7
  181. package/dist/components/GrStatistic/chunks/GrStatistic-DOsM6s5V.js.map +1 -0
  182. package/dist/components/GrStatistic/index.js +2 -2
  183. package/dist/components/GrSwitch/chunks/{GrSwitch-B9TYbiih.js → GrSwitch-DpyvVrZG.js} +1 -1
  184. package/dist/components/GrSwitch/chunks/{GrSwitch-B9TYbiih.js.map → GrSwitch-DpyvVrZG.js.map} +1 -1
  185. package/dist/components/GrSwitch/index.js +2 -2
  186. package/dist/components/GrTabPanels/index.js +1 -1
  187. package/dist/components/GrTable/chunks/{GrTable-BB-mI_NM.js → GrTable-BfqgwETQ.js} +7 -1
  188. package/dist/components/GrTable/chunks/GrTable-BfqgwETQ.js.map +1 -0
  189. package/dist/components/GrTable/index.js +1 -1
  190. package/dist/components/GrTabs/chunks/{GrTabs-B8CDq2D9.js → GrTabs-f3-oQenu.js} +55 -65
  191. package/dist/components/GrTabs/chunks/GrTabs-f3-oQenu.js.map +1 -0
  192. package/dist/components/GrTabs/index.js +1 -1
  193. package/dist/components/GrTextarea/chunks/{GrTextarea-DUh5Tlo4.js → GrTextarea-DSvd1nSy.js} +1 -1
  194. package/dist/components/GrTextarea/chunks/{GrTextarea-DUh5Tlo4.js.map → GrTextarea-DSvd1nSy.js.map} +1 -1
  195. package/dist/components/GrTextarea/index.js +2 -2
  196. package/dist/components/GrTimeline/chunks/{GrTimeline-CE9b3s18.js → GrTimeline-B-h2Yanw.js} +1 -1
  197. package/dist/components/GrTimeline/chunks/{GrTimeline-CE9b3s18.js.map → GrTimeline-B-h2Yanw.js.map} +1 -1
  198. package/dist/components/GrTimeline/index.js +2 -2
  199. package/dist/components/GrToaster/chunks/{GrToaster-BKQy3Srm.js → GrToaster-Dqja0Lf9.js} +114 -13
  200. package/dist/components/GrToaster/chunks/GrToaster-Dqja0Lf9.js.map +1 -0
  201. package/dist/components/GrToaster/index.js +2 -2
  202. package/dist/components/GrToaster/styles.css +1 -1
  203. package/dist/components/GrTooltip/chunks/{GrTooltip-DEbejfhA.js → GrTooltip-Cn08heWz.js} +2 -2
  204. package/dist/components/GrTooltip/chunks/{GrTooltip-DEbejfhA.js.map → GrTooltip-Cn08heWz.js.map} +1 -1
  205. package/dist/components/GrTooltip/index.js +2 -2
  206. package/dist/components/GrTree/chunks/{GrTree-wfdbvygw.js → GrTree-BR9a2pay.js} +148 -111
  207. package/dist/components/GrTree/chunks/GrTree-BR9a2pay.js.map +1 -0
  208. package/dist/components/GrTree/index.js +2 -2
  209. package/dist/components/GrTree/styles.css +1 -1
  210. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-lLWmAMtI.js → GrTreeSelect--muUeB0v.js} +28 -14
  211. package/dist/components/GrTreeSelect/chunks/GrTreeSelect--muUeB0v.js.map +1 -0
  212. package/dist/components/GrTreeSelect/index.js +1 -1
  213. package/dist/composables/useAnnouncer.js +1 -1
  214. package/dist/composables/useComboboxNavigation.js +71 -0
  215. package/dist/composables/useComboboxNavigation.js.map +1 -0
  216. package/dist/composables/useDismissible.js +1 -1
  217. package/dist/composables/useDragGesture.js +55 -0
  218. package/dist/composables/useDragGesture.js.map +1 -0
  219. package/dist/composables/useDragSort.js +2 -0
  220. package/dist/composables/useFloating.js +22 -1
  221. package/dist/composables/useFloating.js.map +1 -1
  222. package/dist/composables/useGrComponentConfig.js +2 -0
  223. package/dist/composables/useGranularityTranslations.js +2 -0
  224. package/dist/composables/useOverlayLayer.js +64 -1
  225. package/dist/composables/useOverlayLayer.js.map +1 -0
  226. package/dist/composables/usePortalTarget.js +1 -1
  227. package/dist/composables/useRovingFocus.js +150 -0
  228. package/dist/composables/useRovingFocus.js.map +1 -0
  229. package/dist/directives/hotkey.js +13 -7
  230. package/dist/directives/hotkey.js.map +1 -1
  231. package/dist/directives/loading.js +1 -1
  232. package/dist/granular-provider-node.js +1 -1
  233. package/dist/granular-provider.js +1 -1
  234. package/dist/i18n/all.js +1 -1
  235. package/dist/i18n/index.js +1 -1
  236. package/dist/index.js +77 -68
  237. package/dist/styles/themes/dark.css +6 -1
  238. package/dist/testing.js +199 -0
  239. package/dist/testing.js.map +1 -0
  240. package/dist/theme-apply.js +33 -0
  241. package/dist/theme-apply.js.map +1 -0
  242. package/dist/theme.js +501 -0
  243. package/dist/theme.js.map +1 -0
  244. package/dist/tokens.js +1 -3131
  245. package/dist/types/src/components/GrAlert/defaults.d.ts +1 -1
  246. package/dist/types/src/components/GrAutocomplete/defaults.d.ts +1 -1
  247. package/dist/types/src/components/GrAvatar/defaults.d.ts +1 -1
  248. package/dist/types/src/components/GrBadge/defaults.d.ts +1 -1
  249. package/dist/types/src/components/GrBottomNav/defaults.d.ts +1 -1
  250. package/dist/types/src/components/GrBreadcrumbs/defaults.d.ts +1 -1
  251. package/dist/types/src/components/GrBreadcrumbs/grBreadcrumbsStyles.d.ts +5 -2
  252. package/dist/types/src/components/GrButton/defaults.d.ts +1 -1
  253. package/dist/types/src/components/GrCard/defaults.d.ts +1 -1
  254. package/dist/types/src/components/GrCheckbox/defaults.d.ts +1 -1
  255. package/dist/types/src/components/GrCheckboxGroup/defaults.d.ts +1 -1
  256. package/dist/types/src/components/GrCollapse/defaults.d.ts +1 -1
  257. package/dist/types/src/components/GrColorPicker/defaults.d.ts +1 -1
  258. package/dist/types/src/components/GrColorPicker/grColorPickerStyles.d.ts +2 -2
  259. package/dist/types/src/components/GrCommandPalette/GrCommandPalette.vue.d.ts +6 -6
  260. package/dist/types/src/components/GrCommandPalette/defaults.d.ts +1 -1
  261. package/dist/types/src/components/GrCommandPalette/filtering.d.ts +6 -2
  262. package/dist/types/src/components/GrConfigProvider/context.d.ts +6 -22
  263. package/dist/types/src/components/GrConfigProvider/index.d.ts +1 -1
  264. package/dist/types/src/components/GrConfirmDialog/defaults.d.ts +1 -1
  265. package/dist/types/src/components/GrDataTable/GrDataTable.vue.d.ts +53 -0
  266. package/dist/types/src/components/GrDataTable/defaults.d.ts +1 -1
  267. package/dist/types/src/components/GrDataTable/grDataTableStyles.d.ts +38 -0
  268. package/dist/types/src/components/GrDialog/defaults.d.ts +1 -1
  269. package/dist/types/src/components/GrDivider/defaults.d.ts +1 -1
  270. package/dist/types/src/components/GrDrawer/defaults.d.ts +1 -1
  271. package/dist/types/src/components/GrEmptyState/defaults.d.ts +1 -1
  272. package/dist/types/src/components/GrFileUpload/defaults.d.ts +1 -1
  273. package/dist/types/src/components/GrFormField/defaults.d.ts +1 -1
  274. package/dist/types/src/components/GrFormFile/defaults.d.ts +1 -1
  275. package/dist/types/src/components/GrFormSection/defaults.d.ts +1 -1
  276. package/dist/types/src/components/GrIcon/defaults.d.ts +1 -1
  277. package/dist/types/src/components/GrImageViewer/composables/usePointerGestures.d.ts +1 -0
  278. package/dist/types/src/components/GrImageViewer/composables/useZoomPan.d.ts +1 -2
  279. package/dist/types/src/components/GrInput/defaults.d.ts +1 -1
  280. package/dist/types/src/components/GrInputTag/defaults.d.ts +1 -1
  281. package/dist/types/src/components/GrKbd/GrKbd.vue.d.ts +18 -3
  282. package/dist/types/src/components/GrKbd/defaults.d.ts +1 -1
  283. package/dist/types/src/components/GrKbd/grKbdStyles.d.ts +20 -3
  284. package/dist/types/src/components/GrKbd/index.d.ts +6 -0
  285. package/dist/types/src/components/GrLink/defaults.d.ts +1 -1
  286. package/dist/types/src/components/GrModal/defaults.d.ts +1 -1
  287. package/dist/types/src/components/GrNumberInput/defaults.d.ts +1 -1
  288. package/dist/types/src/components/GrPagination/defaults.d.ts +1 -1
  289. package/dist/types/src/components/GrPopover/GrPopover.vue.d.ts +1 -1
  290. package/dist/types/src/components/GrPopover/defaults.d.ts +1 -1
  291. package/dist/types/src/components/GrProgressBar/defaults.d.ts +1 -1
  292. package/dist/types/src/components/GrProgressCircle/defaults.d.ts +1 -1
  293. package/dist/types/src/components/GrProgressCircle/grProgressCircleStyles.d.ts +1 -1
  294. package/dist/types/src/components/GrPromptDialog/defaults.d.ts +1 -1
  295. package/dist/types/src/components/GrRadio/defaults.d.ts +1 -1
  296. package/dist/types/src/components/GrRadio/grRadioGroupContext.d.ts +12 -4
  297. package/dist/types/src/components/GrRadioGroup/defaults.d.ts +1 -1
  298. package/dist/types/src/components/GrRating/GrRating.vue.d.ts +9 -4
  299. package/dist/types/src/components/GrRating/defaults.d.ts +1 -1
  300. package/dist/types/src/components/GrSegmented/defaults.d.ts +1 -1
  301. package/dist/types/src/components/GrSelect/defaults.d.ts +1 -1
  302. package/dist/types/src/components/GrSlider/defaults.d.ts +1 -1
  303. package/dist/types/src/components/GrSortableList/GrSortableList.vue.d.ts +68 -0
  304. package/dist/types/src/components/GrSortableList/config.d.ts +1 -0
  305. package/dist/types/src/components/GrSortableList/grSortableListStyles.d.ts +37 -0
  306. package/dist/types/src/components/GrSortableList/index.d.ts +9 -0
  307. package/dist/types/src/components/GrSortableList/safelist.d.ts +1 -0
  308. package/dist/types/src/components/GrSplitter/defaults.d.ts +1 -1
  309. package/dist/types/src/components/GrStatistic/GrStatistic.vue.d.ts +12 -9
  310. package/dist/types/src/components/GrStatistic/defaults.d.ts +1 -1
  311. package/dist/types/src/components/GrStatistic/grStatisticStyles.d.ts +1 -2
  312. package/dist/types/src/components/GrSwitch/defaults.d.ts +1 -1
  313. package/dist/types/src/components/GrTable/GrTable.vue.d.ts +13 -0
  314. package/dist/types/src/components/GrTable/defaults.d.ts +1 -1
  315. package/dist/types/src/components/GrTabs/GrTabs.vue.d.ts +6 -2
  316. package/dist/types/src/components/GrTabs/defaults.d.ts +1 -1
  317. package/dist/types/src/components/GrTextarea/defaults.d.ts +1 -1
  318. package/dist/types/src/components/GrTextarea/grTextareaStyles.d.ts +2 -2
  319. package/dist/types/src/components/GrTimeline/defaults.d.ts +1 -1
  320. package/dist/types/src/components/GrTimeline/grTimelineStyles.d.ts +1 -1
  321. package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +8 -0
  322. package/dist/types/src/components/GrToaster/grToasterStyles.d.ts +27 -0
  323. package/dist/types/src/components/GrTooltip/defaults.d.ts +1 -1
  324. package/dist/types/src/components/GrTree/defaults.d.ts +1 -1
  325. package/dist/types/src/components/GrTree/grTreeInteractionContext.d.ts +0 -1
  326. package/dist/types/src/components/GrTree/grTreeProps.d.ts +11 -4
  327. package/dist/types/src/components/GrTree/safelist.d.ts +1 -1
  328. package/dist/types/src/components/GrTreeSelect/defaults.d.ts +1 -1
  329. package/dist/types/src/components/shared/hotkey.d.ts +7 -15
  330. package/dist/types/src/components/shared/hotkeyTokens.d.ts +37 -0
  331. package/dist/types/src/components/shared/icon.d.ts +13 -0
  332. package/dist/types/src/composables/internal/dragSortGeometry.d.ts +59 -0
  333. package/dist/types/src/composables/internal/overlayStack.d.ts +11 -0
  334. package/dist/types/src/composables/useDragGesture.d.ts +63 -0
  335. package/dist/types/src/composables/useDragSort.d.ts +80 -0
  336. package/dist/types/src/composables/useFloating.d.ts +5 -1
  337. package/dist/types/src/composables/useGrComponentConfig.d.ts +41 -0
  338. package/dist/types/src/composables/useGranularityTranslations.d.ts +13 -0
  339. package/dist/types/src/composables/useRovingFocus.d.ts +108 -0
  340. package/dist/types/src/granular-provider/shared.d.ts +1 -0
  341. package/dist/types/src/i18n/messages/en.d.ts +48 -0
  342. package/dist/types/src/i18n/messages/es.d.ts +48 -0
  343. package/dist/types/src/i18n/messages/ru.d.ts +48 -0
  344. package/dist/types/src/index.d.ts +12 -0
  345. package/dist/types/src/internal/keyboard.d.ts +6 -0
  346. package/dist/types/src/testing/announcer.d.ts +10 -0
  347. package/dist/types/src/testing/context.d.ts +36 -0
  348. package/dist/types/src/testing/env.d.ts +25 -0
  349. package/dist/types/src/testing/geometry.d.ts +23 -0
  350. package/dist/types/src/testing/index.d.ts +12 -0
  351. package/dist/types/src/testing/pointer.d.ts +31 -0
  352. package/dist/types/src/theme/apply.d.ts +27 -0
  353. package/dist/types/src/theme/color.d.ts +35 -0
  354. package/dist/types/src/theme/extendTheme.d.ts +63 -0
  355. package/dist/types/src/theme/index.d.ts +22 -0
  356. package/dist/types/src/theme/roles.d.ts +16 -0
  357. package/dist/types/src/theme/tone.d.ts +32 -0
  358. package/dist/types/src/theme/validate.d.ts +16 -0
  359. package/dist/types/src/tokens/types.d.ts +8 -0
  360. package/dist/web-types.json +214 -13
  361. package/package.json +47 -4
  362. package/tokens/themes/dark.json +2 -1
  363. package/dist/chunks/chevron-right-BfqMVpbB.js.map +0 -1
  364. package/dist/chunks/config-8Pcutu1j.js.map +0 -1
  365. package/dist/chunks/config-B-g6lT19.js.map +0 -1
  366. package/dist/chunks/config-BE6n20np.js.map +0 -1
  367. package/dist/chunks/config-BKvX0q10.js.map +0 -1
  368. package/dist/chunks/config-BNfQwyL4.js +0 -97
  369. package/dist/chunks/config-BNfQwyL4.js.map +0 -1
  370. package/dist/chunks/config-BoNR7_PD.js.map +0 -1
  371. package/dist/chunks/config-CN2ULSQL2.js.map +0 -1
  372. package/dist/chunks/config-CY0_6cMi.js.map +0 -1
  373. package/dist/chunks/config-Cw6TDzWv2.js.map +0 -1
  374. package/dist/chunks/config-D8tSR2uS.js.map +0 -1
  375. package/dist/chunks/config-DpqOnJiz2.js.map +0 -1
  376. package/dist/chunks/config-DxCTVBfx.js.map +0 -1
  377. package/dist/chunks/config-LGrZIdaZ.js +0 -21
  378. package/dist/chunks/config-LGrZIdaZ.js.map +0 -1
  379. package/dist/chunks/config-Rw9OyW70.js.map +0 -1
  380. package/dist/chunks/config-T3qdQBgU.js.map +0 -1
  381. package/dist/chunks/config-eXB87DOi2.js.map +0 -1
  382. package/dist/chunks/en-B3xMeswz.js.map +0 -1
  383. package/dist/chunks/es-CA-mkU--.js.map +0 -1
  384. package/dist/chunks/grKbdStyles-DAoxiytu.js +0 -31
  385. package/dist/chunks/grKbdStyles-DAoxiytu.js.map +0 -1
  386. package/dist/chunks/granular-provider-CtrXGa7c.js.map +0 -1
  387. package/dist/chunks/keyboard-zYvvyHd4.js.map +0 -1
  388. package/dist/chunks/optionFilter-BVY988pC.js.map +0 -1
  389. package/dist/chunks/ru-COWxgh2A.js.map +0 -1
  390. package/dist/chunks/useOverlayLayer-BupuP_hs.js.map +0 -1
  391. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-CQ9T7Lly.js.map +0 -1
  392. package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-BCNGDS6k.js.map +0 -1
  393. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-MWDYDPNC.js.map +0 -1
  394. package/dist/components/GrDataTable/chunks/GrDataTable-DVFho9Cj.js.map +0 -1
  395. package/dist/components/GrImageViewer/chunks/GrImageViewer-BeCnE8XQ.js.map +0 -1
  396. package/dist/components/GrInput/chunks/GrInput-DD2gYdqS.js.map +0 -1
  397. package/dist/components/GrInputTag/chunks/GrInputTag-DmNunixz.js.map +0 -1
  398. package/dist/components/GrKbd/chunks/GrKbd-DRFPvW3G.js.map +0 -1
  399. package/dist/components/GrPagination/chunks/GrPagination-B6N0QZBf.js.map +0 -1
  400. package/dist/components/GrProgressBar/chunks/GrProgressBar-BiHnaitu.js.map +0 -1
  401. package/dist/components/GrRadio/chunks/GrRadio-DksDro_U.js.map +0 -1
  402. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-BOIhejE6.js.map +0 -1
  403. package/dist/components/GrRating/chunks/GrRating-JrHXz0Rv.js.map +0 -1
  404. package/dist/components/GrSegmented/chunks/GrSegmented-BQIlH5KI.js.map +0 -1
  405. package/dist/components/GrSelect/chunks/GrSelect-aqkPCqjd.js.map +0 -1
  406. package/dist/components/GrSlider/chunks/GrSlider-DkU9IQSX.js.map +0 -1
  407. package/dist/components/GrSplitter/chunks/GrSplitter-7To8jbJ8.js.map +0 -1
  408. package/dist/components/GrStatistic/chunks/GrStatistic-CttyoYPT.js.map +0 -1
  409. package/dist/components/GrTable/chunks/GrTable-BB-mI_NM.js.map +0 -1
  410. package/dist/components/GrTabs/chunks/GrTabs-B8CDq2D9.js.map +0 -1
  411. package/dist/components/GrToaster/chunks/GrToaster-BKQy3Srm.js.map +0 -1
  412. package/dist/components/GrTree/chunks/GrTree-wfdbvygw.js.map +0 -1
  413. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-lLWmAMtI.js.map +0 -1
  414. package/dist/tokens.js.map +0 -1
  415. package/dist/chunks/{config-8Pcutu1j.js → config-8Pcutu1j2.js} +0 -0
  416. package/dist/chunks/{config-BE6n20np.js → config-BE6n20np2.js} +0 -0
  417. package/dist/chunks/{config-BKvX0q10.js → config-BKvX0q102.js} +0 -0
  418. package/dist/chunks/{config-BoNR7_PD.js → config-BoNR7_PD2.js} +0 -0
  419. package/dist/chunks/{config-CN2ULSQL2.js → config-CN2ULSQL.js} +0 -0
  420. package/dist/chunks/{config-CY0_6cMi.js → config-CY0_6cMi2.js} +0 -0
  421. package/dist/chunks/{config-Rw9OyW70.js → config-Rw9OyW702.js} +0 -0
  422. package/dist/chunks/{config-T3qdQBgU.js → config-T3qdQBgU2.js} +0 -0
  423. package/dist/chunks/{config-eXB87DOi2.js → config-eXB87DOi.js} +0 -0
  424. package/dist/components/GrCollapse/chunks/{GrCollapse-B2Fc6A9r.js → GrCollapse-Kk0DE8tb.js} +1 -1
  425. /package/dist/types/src/composables/{internal/useComboboxNavigation.d.ts → useComboboxNavigation.d.ts} +0 -0
@@ -1,8 +1,10 @@
1
+ import { t as x_default } from "../../../chunks/x-2KXnqxiD.js";
1
2
  import { a as useGrComponentProp, c as useGrThemeAttrs, o as useGrComponentSize } from "../../../chunks/context-BSYR7RZs.js";
2
3
  import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
- import { t as usePortalTarget } from "../../../chunks/usePortalTarget-RyzEgNTb.js";
4
+ import { t as usePortalTarget } from "../../../chunks/usePortalTarget-Cn-Wiz5d.js";
4
5
  import { vClickOutside } from "../../../directives/clickOutside.js";
5
- import { n as isComposingEvent } from "../../../chunks/keyboard-zYvvyHd4.js";
6
+ import { r as isComposingEvent } from "../../../chunks/keyboard-DE2yhz_b.js";
7
+ import { t as loader_circle_default } from "../../../chunks/loader-circle-CfVLz7lb.js";
6
8
  import { useFloating } from "../../../composables/useFloating.js";
7
9
  import { useDismissible } from "../../../composables/useDismissible.js";
8
10
  import { useVirtualList } from "../../../composables/useVirtualList.js";
@@ -10,11 +12,14 @@ import { n as useGrFormFieldContext } from "../../../chunks/context-DdNIxa1w.js"
10
12
  import { useGrFormControl } from "../../../composables/useGrFormControl.js";
11
13
  import { t as useFocusWithin } from "../../../chunks/useFocusWithin-BwHE0rzm.js";
12
14
  import { t as useControlledOpen } from "../../../chunks/useControlledOpen-DV3SqLTP.js";
13
- import { a as resolveSelectedOptions, o as useComboboxNavigation, t as filterOptions } from "../../../chunks/optionFilter-BVY988pC.js";
15
+ import { useComboboxNavigation } from "../../../composables/useComboboxNavigation.js";
16
+ import { a as resolveSelectedOptions, t as filterOptions } from "../../../chunks/optionFilter-DYw9XPPx.js";
14
17
  import { t as useControlAddons } from "../../../chunks/useControlAddons-asUr10Pf.js";
15
18
  import { t as GrBadge_default } from "../../GrBadge/chunks/GrBadge-B1tgqfGQ.js";
16
19
  import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BOaGB7Aw.js";
17
20
  import { a as autocompleteShellClass, i as autocompletePanelClasses, o as autocompleteStateClass, r as autocompleteOptionClass } from "../../../chunks/config-BbY2SKGE.js";
21
+ import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
22
+ import { t as chevron_down_default } from "../../../chunks/chevron-down-CQS2OG_s.js";
18
23
  import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, renderList, renderSlot, shallowRef, toDisplayString, unref, useId, vShow, watch, withCtx, withDirectives, withModifiers } from "vue";
19
24
  import '../styles.css';//#region src/components/GrAutocomplete/GrAutocomplete.vue?vue&type=script&setup=true&lang.ts
20
25
  var _hoisted_1 = { class: "inline-flex max-w-full items-center gap-1 align-middle" };
@@ -67,17 +72,21 @@ var _hoisted_11 = [
67
72
  "onMousemove"
68
73
  ];
69
74
  var _hoisted_12 = { class: "flex items-center gap-2 min-w-0" };
70
- var _hoisted_13 = { class: "truncate" };
71
- var _hoisted_14 = {
75
+ var _hoisted_13 = {
76
+ class: "inline-block h-4 w-4 shrink-0",
77
+ "aria-hidden": "true"
78
+ };
79
+ var _hoisted_14 = { class: "truncate" };
80
+ var _hoisted_15 = {
72
81
  key: 0,
73
82
  "data-gr-autocomplete-loading": "",
74
83
  class: "flex items-center gap-2"
75
84
  };
76
- var _hoisted_15 = {
85
+ var _hoisted_16 = {
77
86
  key: 1,
78
87
  "data-gr-autocomplete-hint": ""
79
88
  };
80
- var _hoisted_16 = {
89
+ var _hoisted_17 = {
81
90
  key: 2,
82
91
  "data-gr-autocomplete-empty": ""
83
92
  };
@@ -676,10 +685,10 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
676
685
  tabindex: "-1",
677
686
  onClick: ($event) => removeValue(option.value),
678
687
  onKeydown: ($event) => onChipKeydown($event, chipIndex, option.value)
679
- }, [..._cache[5] || (_cache[5] = [createElementVNode("span", {
680
- class: "i-lucide-x block h-3 w-3",
688
+ }, [createVNode(unref(x_default), {
689
+ class: "block h-3 w-3",
681
690
  "aria-hidden": "true"
682
- }, null, -1)])], 40, _hoisted_3)) : createCommentVNode("", true)])]),
691
+ })], 40, _hoisted_3)) : createCommentVNode("", true)])]),
683
692
  _: 2
684
693
  }, 1032, [
685
694
  "tone",
@@ -725,10 +734,10 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
725
734
  class: "inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]",
726
735
  style: normalizeStyle(unref(suffixStyle))
727
736
  }, [renderSlot(_ctx.$slots, "suffix", {}, void 0, true)], 4)) : createCommentVNode("", true),
728
- isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_5, [..._cache[6] || (_cache[6] = [createElementVNode("span", {
729
- class: "i-lucide-loader-2 block h-4 w-4 animate-spin",
737
+ isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_5, [createVNode(unref(loader_circle_default), {
738
+ class: "block h-4 w-4 animate-spin",
730
739
  "aria-hidden": "true"
731
- }, null, -1)])])) : showClear.value ? (openBlock(), createElementBlock("button", {
740
+ })])) : showClear.value ? (openBlock(), createElementBlock("button", {
732
741
  key: 4,
733
742
  type: "button",
734
743
  "data-testid": "gr-autocomplete-clear",
@@ -736,13 +745,13 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
736
745
  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)]",
737
746
  "aria-label": resolvedClearLabel.value,
738
747
  onClick: clearSelection
739
- }, [..._cache[7] || (_cache[7] = [createElementVNode("span", {
740
- class: "i-lucide-x block h-4 w-4",
748
+ }, [createVNode(unref(x_default), {
749
+ class: "block h-4 w-4",
741
750
  "aria-hidden": "true"
742
- }, null, -1)])], 8, _hoisted_6)) : (openBlock(), createElementBlock("span", _hoisted_7, [..._cache[8] || (_cache[8] = [createElementVNode("span", {
743
- class: "i-lucide-chevron-down block h-4 w-4",
751
+ })], 8, _hoisted_6)) : (openBlock(), createElementBlock("span", _hoisted_7, [createVNode(unref(chevron_down_default), {
752
+ class: "block h-4 w-4",
744
753
  "aria-hidden": "true"
745
- }, null, -1)])]))
754
+ })]))
746
755
  ], 34),
747
756
  __props.name ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(selectedValues.value, (value) => {
748
757
  return openBlock(), createElementBlock("input", {
@@ -823,20 +832,20 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
823
832
  }), [renderSlot(_ctx.$slots, "option", {
824
833
  option,
825
834
  selected: isSelected(option.value)
826
- }, () => [createElementVNode("span", _hoisted_12, [createElementVNode("span", {
827
- class: normalizeClass(["inline-block h-4 w-4 shrink-0", isSelected(option.value) ? "i-lucide-check text-[var(--gr-primary)]" : ""]),
828
- "aria-hidden": "true"
829
- }, null, 2), createElementVNode("span", _hoisted_13, toDisplayString(option.label), 1)])], true)], 16, _hoisted_11);
835
+ }, () => [createElementVNode("span", _hoisted_12, [createElementVNode("span", _hoisted_13, [isSelected(option.value) ? (openBlock(), createBlock(unref(check_default), {
836
+ key: 0,
837
+ class: "block h-4 w-4 text-[var(--gr-primary)]"
838
+ })) : createCommentVNode("", true)]), createElementVNode("span", _hoisted_14, toDisplayString(option.label), 1)])], true)], 16, _hoisted_11);
830
839
  }), 128))], 14, _hoisted_9)) : createCommentVNode("", true), isLoading.value || belowMinQuery.value || showEmpty.value ? (openBlock(), createElementBlock("div", {
831
840
  key: 1,
832
841
  "data-gr-autocomplete-status": "",
833
842
  role: "status",
834
843
  "aria-live": "polite",
835
844
  class: normalizeClass(unref(autocompleteStateClass))
836
- }, [isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_14, [renderSlot(_ctx.$slots, "loading", {}, () => [_cache[9] || (_cache[9] = createElementVNode("span", {
837
- class: "i-lucide-loader-2 block h-4 w-4 animate-spin",
845
+ }, [isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_15, [renderSlot(_ctx.$slots, "loading", {}, () => [createVNode(unref(loader_circle_default), {
846
+ class: "block h-4 w-4 animate-spin",
838
847
  "aria-hidden": "true"
839
- }, null, -1)), createElementVNode("span", null, toDisplayString(resolvedLoadingText.value), 1)], true)])) : belowMinQuery.value ? (openBlock(), createElementBlock("span", _hoisted_15, toDisplayString(resolvedTypeMoreText.value), 1)) : (openBlock(), createElementBlock("span", _hoisted_16, [renderSlot(_ctx.$slots, "empty", {}, () => [createTextVNode(toDisplayString(resolvedNoResultsText.value), 1)], true)]))], 2)) : createCommentVNode("", true)], 2)], 16), [[vShow, unref(open)]])]),
848
+ }), createElementVNode("span", null, toDisplayString(resolvedLoadingText.value), 1)], true)])) : belowMinQuery.value ? (openBlock(), createElementBlock("span", _hoisted_16, toDisplayString(resolvedTypeMoreText.value), 1)) : (openBlock(), createElementBlock("span", _hoisted_17, [renderSlot(_ctx.$slots, "empty", {}, () => [createTextVNode(toDisplayString(resolvedNoResultsText.value), 1)], true)]))], 2)) : createCommentVNode("", true)], 2)], 16), [[vShow, unref(open)]])]),
840
849
  _: 3
841
850
  })], 8, ["to", "disabled"]))
842
851
  ], 32)), [[unref(vClickOutside), {
@@ -846,6 +855,6 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
846
855
  }]]);
847
856
  };
848
857
  }
849
- }), [["__scopeId", "data-v-2a9c29c9"]]);
858
+ }), [["__scopeId", "data-v-06fe398b"]]);
850
859
  //#endregion
851
860
  export { GrAutocomplete_default as t };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrAutocomplete-gY4Xopza.js","names":[],"sources":["../../../../src/components/GrAutocomplete/GrAutocomplete.vue","../../../../src/components/GrAutocomplete/GrAutocomplete.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"TValue extends GrAutocompleteValue = string\">\nimport { computed, nextTick, onBeforeUnmount, ref, shallowRef, useId, watch } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { useGrComponentProp, useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\n\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { isComposingEvent } from '../../internal/keyboard'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useComboboxNavigation } from '../../composables/useComboboxNavigation'\nimport { filterOptions, resolveSelectedOptions } from '../shared/optionFilter'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge/grBadgeStyles'\n\nimport {\n autocompleteOptionClass,\n autocompletePanelClasses,\n autocompleteShellClass,\n autocompleteStateClass,\n type GrAutocompleteModelValue,\n type GrAutocompleteOption,\n type GrAutocompleteValue,\n type GrAutocompleteSize,\n} from './grAutocompleteStyles'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconLoaderCircle from '~icons/lucide/loader-circle'\nimport IconX from '~icons/lucide/x'\n\nexport type {\n GrAutocompleteModelValue,\n GrAutocompleteOption,\n GrAutocompleteSize,\n GrAutocompleteValue,\n} from './grAutocompleteStyles'\n\n/**\n * Публичный GR-примитив «Autocomplete / Combobox» (WAI-ARIA editable combobox).\n *\n * В отличие от `GrSelect` (select-only combobox с кнопкой-триггером), здесь\n * combobox-ом является сам текстовый `<input>`: набранный текст — это поисковый\n * запрос, а выбор опции заполняет поле. Поддерживает локальную фильтрацию,\n * удалённую (async) загрузку через событие `search` + внешний проп `loading`,\n * произвольные значения (`allowCustomValue`) и multiple с удаляемыми chips.\n */\nexport interface GrAutocompleteProps<TValue extends GrAutocompleteValue = string> {\n /** Выбранное значение (single — строка, multiple — массив строк). */\n modelValue: GrAutocompleteModelValue<TValue>\n /**\n * Доступные опции. Для локального режима — полный список (фильтруется на клиенте).\n * Для remote-режима (`filterable=false`) — список, который родитель обновляет\n * в ответ на событие `search`.\n */\n options?: GrAutocompleteOption<TValue>[]\n multiple?: boolean\n /**\n * Вид чипов выбранных значений в режиме `multiple`. Чип — это `GrBadge`, а не\n * своя плашка: на светлой теме собственная заливка чипа (примесь `--gr-muted`\n * без рамки) давала 1.02:1 к фону поля, то есть была невидима.\n */\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n disabled?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrAutocompleteSize\n placeholder?: string\n ariaLabel?: string\n /** Кнопка очистки выбранного значения/запроса. */\n clearable?: boolean\n /** Внешне управляемое состояние загрузки (для async-сценариев). */\n loading?: boolean\n /**\n * Локальная фильтрация опций по введённому запросу. Отключите (`false`) для\n * чисто удалённого поиска — тогда `options` показываются как есть, а фильтрацию\n * выполняет сервер по событию `search`.\n */\n filterable?: boolean\n /** Кастомный матчер локальной фильтрации. По умолчанию — подстрока в `label`/`value`. */\n filter?: (option: GrAutocompleteOption<TValue>, query: string) => boolean\n /**\n * Удалённая загрузка опций под управлением компонента: дебаунс, отмена\n * устаревшего запроса и `loading` берёт на себя он. Ответ на отменённый\n * запрос игнорируется — при быстром вводе в списке всегда результат\n * последнего запроса, а не того, который вернулся позже.\n *\n * `signal` пробрасывается в `fetch`. Локальная фильтрация в этом режиме\n * выключена: список фильтрует сервер. Альтернатива — событие `search`, если\n * запрос ведёт само приложение.\n */\n fetchOptions?: (query: string, signal: AbortSignal) => Promise<GrAutocompleteOption<TValue>[]>\n /** Минимальная длина запроса до эмита `search` (для дебаунса remote-загрузки). */\n minQueryLength?: number\n /** Задержка дебаунса события `search`, мс. */\n debounce?: number\n /** Разрешить ввод/коммит значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Закрывать панель после выбора (single всегда закрывает). */\n closeOnSelect?: boolean\n /** Максимальная высота панели, px. */\n dropdownMaxHeight?: number\n /**\n * Виртуализация панели: в DOM живёт только окно вокруг вьюпорта.\n *\n * Высоту окна задаёт `dropdownMaxHeight`. Включается осознанно: на сотне\n * опций выигрыша нет, а в разметке остаётся только окно — вместе с ним\n * меняется и то, что находит `querySelector` потребителя. Профильный\n * сценарий — удалённый поиск по справочнику на тысячи позиций.\n */\n virtual?: boolean\n /** i18n-тексты состояний панели / aria. */\n loadingText?: string\n noResultsText?: string\n clearLabel?: string\n /**\n * Контролируемое состояние панели (`v-model:open`). Без пропа панель ведёт\n * себя сама (uncontrolled), с ним — слушайте `update:open` и меняйте проп.\n */\n open?: boolean\n /** Имя для нативной формы: hidden input по значению модели (не по тексту запроса). */\n name?: string\n /**\n * Ширины аддонов `prefix`/`suffix` — общий контракт контролов пакета\n * (`docs/form-controls.md`).\n */\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface GrAutocompleteEmits<TValue extends GrAutocompleteValue = string> {\n (e: 'update:modelValue', value: GrAutocompleteModelValue<TValue>): void\n /** Дебаунснутый поисковый запрос — точка входа для удалённой загрузки опций. */\n (e: 'search', query: string): void\n /** Запрос `fetchOptions` завершился ошибкой (отмена устаревшего сюда не приходит). */\n (e: 'searchError', error: unknown): void\n /** Значение зафиксировано выбором или снятием опции. */\n (e: 'change', value: GrAutocompleteModelValue<TValue>): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n /** Значение снято кнопкой очистки; только при `clearable`. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrAutocompleteProps<TValue>>(),\n {\n options: undefined,\n multiple: false,\n tagTone: 'neutral',\n tagDark: false,\n // `sm`, а не `md` как у `GrInputTag`: чип живёт внутри контрола, а не в тексте.\n tagSize: 'sm',\n tagRadius: 'round',\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n placeholder: undefined,\n ariaLabel: undefined,\n clearable: undefined,\n loading: false,\n filterable: true,\n filter: undefined,\n fetchOptions: undefined,\n minQueryLength: 0,\n debounce: 250,\n allowCustomValue: false,\n closeOnSelect: true,\n dropdownMaxHeight: 280,\n virtual: false,\n loadingText: undefined,\n noResultsText: undefined,\n clearLabel: undefined,\n open: undefined,\n name: undefined,\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\n/** Ширина аддона по умолчанию — та же лестница, что у `GrInput`. */\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrAutocompleteSize, string> = {\n xs: '2rem',\n sm: '2.25rem',\n md: '2.5rem',\n lg: '3rem',\n}\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrAutocomplete' })\nconst resolvedClearable = useGrComponentProp('GrAutocomplete', 'clearable', () => props.clearable, false)\n\n/**\n * Аддоны живут флекс-соседями поля, а не поверх него: оболочка автокомплита —\n * флекс-строка с чипами, поэтому резервировать место паддингом не нужно.\n * Контракт ширин остаётся общим (`useControlAddons`).\n */\nconst { hasPrefix, hasSuffix, prefixEl, suffixEl, prefixStyle, suffixStyle } = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => '0px',\n})\n\nconst emit = defineEmits<GrAutocompleteEmits<TValue>>()\ndefineSlots<{\n /** Аддон слева от поля ввода. */\n prefix?: () => any\n /** Аддон справа, перед спиннером и крестиком. */\n suffix?: () => any\n /** Строка списка вместо подписи опции. */\n option?: (props: { option: GrAutocompleteOption<TValue>, selected: boolean }) => any\n /** Содержимое панели, пока едут опции. */\n loading?: () => any\n /** Содержимое панели, когда подходящих опций нет. */\n empty?: () => any\n}>()\n\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.autocomplete.loading', 'Loading…'))\nconst resolvedNoResultsText = computed(() => props.noResultsText ?? t('gr.autocomplete.noResults', 'No results'))\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.common.clear', 'Clear'))\n// Число уходит под двумя общепринятыми именами: `n` читают `@feugene/fint-i18n`\n// и `vue-i18n`, `count` — `i18next`. Формы для каждого языка лежат в словаре,\n// выбирает их переводчик; встроенный fallback — одна английская строка.\nconst resolvedTypeMoreText = computed(() =>\n t('gr.autocomplete.typeMore', 'Type at least {n} characters', {\n n: props.minQueryLength,\n count: props.minQueryLength,\n }),\n)\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\n// Read-only запирает контрол так же, как disabled: значение видно, но панель не\n// открывается, опции не выбираются и чипы не удаляются. Без этого `readonly`\n// оставался бы ARIA-атрибутом без поведения.\nconst locked = computed(() => isDisabled.value || isReadonly.value)\n\n// Ответ последнего `fetchOptions`. До первого ответа показываем `options` —\n// с ними компонент рисует стартовый список, не дожидаясь сервера.\nconst remoteOptions = shallowRef<GrAutocompleteOption<TValue>[]>([])\nconst remoteAnswered = ref(false)\nconst remoteLoading = ref(false)\n\nconst optionsResolved = computed<GrAutocompleteOption<TValue>[]>(() =>\n props.fetchOptions && remoteAnswered.value ? remoteOptions.value : (props.options ?? []),\n)\n\n/**\n * Подпись состава стартового списка. Сравнивать идентичность массива нельзя:\n * инлайн-литерал `:options=\"[...]\"` пересоздаётся каждым ререндером родителя —\n * в том числе тем, который вызвал сам компонент своим `update:modelValue`, —\n * и remote-результаты исчезали бы прямо посреди выбора.\n */\nfunction optionsSignature(options: GrAutocompleteOption<TValue>[] | undefined): string {\n return (options ?? []).map(o => `${String(o.value)}\\u0000${o.label}`).join('\\u0001')\n}\n\n// Родитель сменил стартовый список — он снова источник до следующего ответа\n// сервера, а летящий запрос относится к прежнему набору данных и отменяется.\nwatch(() => optionsSignature(props.options), () => {\n if (!props.fetchOptions) return\n cancelSearch()\n remoteAnswered.value = false\n remoteOptions.value = []\n})\n\nconst isLoading = computed(() => props.loading || remoteLoading.value)\n\n/** `0` — валидное значение, поэтому «пусто» проверяется явно, а не через falsy. */\nfunction isEmptyValue(value: unknown): boolean {\n return value === undefined || value === null || value === ''\n}\n\nfunction toArray(value: GrAutocompleteModelValue<TValue>): TValue[] {\n if (Array.isArray(value)) return value\n if (isEmptyValue(value)) return []\n return [value]\n}\n\nconst modelSingle = computed<TValue | ''>(() => {\n const raw = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue\n return isEmptyValue(raw) ? '' : (raw as TValue)\n})\nconst selectedValues = computed(() => (props.multiple ? toArray(props.modelValue) : (isEmptyValue(modelSingle.value) ? [] : [modelSingle.value as TValue])))\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nfunction labelFor(value: GrAutocompleteValue): string {\n return optionsResolved.value.find(o => o.value === value)?.label ?? String(value)\n}\n\n/** Опции выбранных значений (для chips в multiple). Неизвестные значения показываем как есть. */\nconst selectedOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n resolveSelectedOptions(selectedValues.value, optionsResolved.value),\n)\n\nconst singleSelectedLabel = computed(() => (props.multiple || isEmptyValue(modelSingle.value) ? '' : labelFor(modelSingle.value)))\n\n// ————— Состояние.\nconst query = ref('')\n// `dirty` — пользователь начал вводить запрос. Нужно, чтобы в single-режиме при\n// открытии заполненного поля показать весь список (а не отфильтровать по метке\n// уже выбранной опции). Сбрасывается при программной установке `query`.\nconst dirty = ref(false)\n// Имя `open` сохранено: читатели работают с computed-Ref.\nconst { open, setOpen } = useControlledOpen(\n () => props.open,\n next => emit('update:open', next),\n)\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst listboxEl = ref<HTMLElement | null>(null)\n\n// Панель телепортирована в `body`, то есть лежит вне корня, но для пользователя\n// она часть контрола: без неё уход фокуса в панель читался бы как `blur`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n containers: () => [panelEl.value],\n})\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\ndefineExpose({\n focus,\n blur,\n open: () => openDropdown(),\n close: () => closeDropdown(),\n})\nconst clickOutsideExclude = [() => panelEl.value]\n\nconst { floatingStyle } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: true,\n zIndexVar: '--gr-z-dropdown',\n})\n\nuseDismissible(open, closeDropdown)\n\n// ————— Фильтрация.\nconst searchQuery = computed(() => {\n // При `fetchOptions` фильтрует сервер: локальный матчер отсеял бы то, что он\n // уже прислал в ответ на этот же запрос.\n if (!props.filterable || props.fetchOptions) return ''\n // single: пока пользователь не начал вводить — показываем весь список.\n if (!props.multiple && !dirty.value) return ''\n return query.value.trim()\n})\n\nconst filteredOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n filterOptions(optionsResolved.value, searchQuery.value, props.filter),\n)\n\nconst belowMinQuery = computed(() => props.minQueryLength > 0 && query.value.trim().length < props.minQueryLength)\n\n/**\n * Опции к показу и навигации. Ниже `minQueryLength` — пусто: список ещё\n * относится к прошлому запросу, показывать его под подсказкой «введите ещё N»\n * значит дезинформировать.\n */\nconst effectiveOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n belowMinQuery.value ? [] : filteredOptions.value,\n)\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n // Кастомное значение набирается текстом — оно строковое по природе;\n // при числовом `TValue` эта ветка неприменима (см. docs/components.md).\n const v = query.value.trim() as TValue\n if (!v) return false\n if (props.multiple && selectedValues.value.includes(v)) return false\n if (!props.multiple && v === modelSingle.value) return false\n // Не предлагаем «Add», если такое значение/метка уже есть среди опций.\n return !optionsResolved.value.some(o => o.value === v || o.label === v)\n})\n\nconst showEmpty = computed(() =>\n !isLoading.value && effectiveOptions.value.length === 0 && !canAddCustom.value,\n)\n\n// ————— Панель: id/aria-activedescendant.\nconst listboxId = useId()\n\n/**\n * Id опции строится от позиции, а не от значения: значение с пробелом дало бы\n * невалидный `id`, а `aria-activedescendant` — два токена вместо одной ссылки,\n * и активная опция перестала бы объявляться.\n */\nfunction optionDomId(index: number): string {\n return `${listboxId}-opt-${index}`\n}\nconst addOptionDomId = computed(() => `${listboxId}-add`)\n\n/**\n * Виртуализация панели.\n *\n * Набор — `[«Add …»?] + filteredOptions`, тот же, по которому ходит клавиатура\n * (`navigableItems` ниже). Иначе верхняя распорка вытолкнула бы строку «Add …»\n * вниз: она отрисована внутри listbox'а первой и является полноценной опцией.\n */\n\n/** Оценка высоты опции: `py-2` вокруг строки кегля `sm`. Уточняется замером. */\nconst OPTION_SIZE_ESTIMATE = 36\n\nconst addOffset = computed(() => (canAddCustom.value ? 1 : 0))\nconst virtualCount = computed(() => effectiveOptions.value.length + addOffset.value)\n\nconst virtualizer = useVirtualList({\n container: listboxEl,\n count: () => (props.virtual ? virtualCount.value : 0),\n // Фильтрация/remote-ответ пересобирают набор — замеры прошлого невалидны.\n source: () => effectiveOptions.value,\n itemSize: OPTION_SIZE_ESTIMATE,\n // Панель скрыта `v-show`, пока закрыта, поэтому `clientHeight` контейнера —\n // ноль. Окно считается от объявленной высоты до первого настоящего замера.\n viewportSize: () => props.dropdownMaxHeight,\n})\n\n/** Виден ли «Add …»: вне виртуального окна его рисовать нельзя — он элемент набора. */\nconst showAddOption = computed(() => {\n if (!canAddCustom.value) return false\n return !props.virtual || virtualizer.range.value.start === 0\n})\n\n/** Опции к отрисовке вместе с их абсолютным индексом в `filteredOptions`. */\nconst renderedOptions = computed(() => {\n const all = effectiveOptions.value\n if (!props.virtual) return all.map((option, index) => ({ option, index }))\n\n const { start, end } = virtualizer.range.value\n const from = Math.max(0, start - addOffset.value)\n const to = Math.max(0, end - addOffset.value)\n\n return all.slice(from, to).map((option, offset) => ({ option, index: from + offset }))\n})\n\n/**\n * Размер набора и позиция в нём объявляются только при виртуализации: в обычном\n * режиме диктор выводит их из DOM, а при неполном наборе получил бы «3 из 15»\n * на списке в десять тысяч.\n */\nfunction optionSetProps(virtualIndex: number): Record<string, number> | undefined {\n if (!props.virtual) return undefined\n return { 'aria-setsize': virtualCount.value, 'aria-posinset': virtualIndex + 1 }\n}\n\nconst listboxStyle = computed(() => {\n const base: Record<string, string> = { maxHeight: `${props.dropdownMaxHeight}px` }\n if (!props.virtual) return base\n\n return {\n ...base,\n ...virtualizer.spacerStyle.value,\n }\n})\n\n/**\n * Клавиатурная навигация ходит и по опциям, и по варианту «добавить своё»:\n * иначе при непустом списке Enter всегда уходил бы в активную опцию, и\n * закоммитить произвольное значение с клавиатуры было бы нечем.\n */\ntype NavigableItem =\n | { kind: 'add' }\n | { kind: 'option', option: GrAutocompleteOption<TValue>, index: number }\n\nconst navigableItems = computed<NavigableItem[]>(() => {\n const items: NavigableItem[] = canAddCustom.value ? [{ kind: 'add' }] : []\n effectiveOptions.value.forEach((option, index) => {\n if (!option.disabled) items.push({ kind: 'option', option, index })\n })\n return items\n})\n\nfunction isSelected(value: TValue): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction navigableIndexOf(value: TValue): number {\n return navigableItems.value.findIndex(item => item.kind === 'option' && item.option.value === value)\n}\n\n/** Прокрутка к активному: сперва окно виртуального списка, затем доводка. */\nasync function scrollActiveIntoView(item: NavigableItem): Promise<void> {\n // Вне окна активной опции в DOM нет: `getElementById` вернул бы `null`,\n // прокрутка не случилась бы, а `aria-activedescendant` указал бы в пустоту.\n if (props.virtual)\n virtualizer.scrollToIndex(item.kind === 'add' ? 0 : item.index + addOffset.value)\n\n await nextTick()\n const id = item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)\n document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' })\n}\n\nconst {\n activeIndex,\n activeItem,\n activeDescendantId,\n init: initActiveIndex,\n reset: resetActive,\n handleNavigationKeys,\n} = useComboboxNavigation<NavigableItem>({\n items: () => navigableItems.value,\n open: () => open.value,\n idOf: item => (item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)),\n initialIndex: () => navigableItems.value.findIndex(item => item.kind === 'option' && isSelected(item.option.value)),\n scrollTo: item => scrollActiveIntoView(item),\n})\n\nconst activeValue = computed(() => (activeItem.value?.kind === 'option' ? activeItem.value.option.value : undefined))\n\n// ————— Открытие/закрытие.\nfunction openDropdown(): void {\n if (locked.value || open.value) return\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction focusInput(): void {\n inputEl.value?.focus()\n}\n\n// ————— Дебаунснутый поиск для remote-загрузки.\nlet searchTimer: ReturnType<typeof setTimeout> | null = null\n// Счётчик запросов: ответ, стартовавший раньше, но пришедший позже, обязан\n// проиграть последнему — иначе список показывает результат старого запроса.\nlet searchSeq = 0\nlet inflight: AbortController | null = null\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError'\n}\n\nasync function runFetch(query: string): Promise<void> {\n const fetchOptions = props.fetchOptions\n if (!fetchOptions) return\n\n inflight?.abort()\n const controller = new AbortController()\n inflight = controller\n const seq = ++searchSeq\n remoteLoading.value = true\n\n try {\n const result = await fetchOptions(query, controller.signal)\n if (seq !== searchSeq) return\n remoteOptions.value = result\n remoteAnswered.value = true\n }\n catch (error) {\n if (seq !== searchSeq || isAbortError(error)) return\n remoteOptions.value = []\n remoteAnswered.value = true\n emit('searchError', error)\n }\n finally {\n if (seq === searchSeq) remoteLoading.value = false\n }\n}\n\nfunction scheduleSearch(value: string): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = setTimeout(() => {\n if (props.minQueryLength > 0 && value.trim().length < props.minQueryLength) return\n emit('search', value.trim())\n void runFetch(value.trim())\n }, props.debounce)\n}\n\n/**\n * Снять запланированный и летящий запрос. Инкремент `searchSeq` обязателен\n * вместе со снятием `remoteLoading`: после него `finally` в `runFetch` считает\n * себя устаревшим и флаг не тронет — спиннер остался бы навсегда.\n */\nfunction cancelSearch(): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = null\n inflight?.abort()\n inflight = null\n searchSeq += 1\n remoteLoading.value = false\n}\n\nonBeforeUnmount(cancelSearch)\n\n// ————— Ввод.\nfunction onInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value\n query.value = value\n dirty.value = true\n openDropdown()\n scheduleSearch(value)\n}\n\nfunction onFocus(): void {\n if (locked.value) return\n openDropdown()\n}\n\nfunction setQuery(value: string): void {\n query.value = value\n dirty.value = false\n}\n\n// ————— Выбор значений.\nfunction selectSingle(value: TValue, label: string): void {\n emit('update:modelValue', value)\n emit('change', value as GrAutocompleteModelValue<TValue>)\n setQuery(label)\n closeDropdown()\n}\n\nfunction toggleMultiple(value: TValue): void {\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) next.splice(idx, 1)\n else next.push(value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n setQuery('')\n if (props.closeOnSelect) closeDropdown()\n else void nextTick(focusInput)\n}\n\nfunction chooseOption(option: GrAutocompleteOption<TValue>): void {\n if (locked.value || option.disabled) return\n if (props.multiple) toggleMultiple(option.value)\n else selectSingle(option.value, option.label)\n}\n\nfunction commitCustom(): void {\n if (locked.value) return\n // Кастомное значение строковое по природе — см. `canAddCustom`.\n const v = query.value.trim() as TValue\n if (!v) return\n if (props.multiple) toggleMultiple(v)\n else selectSingle(v, String(v))\n}\n\nfunction removeValue(value: TValue, focusAfter: () => void = focusInput): void {\n if (locked.value) return\n const next = selectedValues.value.filter(v => v !== value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n void nextTick(focusAfter)\n}\n\nfunction clearSelection(): void {\n if (locked.value) return\n const next = (props.multiple ? [] : '') as GrAutocompleteModelValue<TValue>\n emit('update:modelValue', next)\n emit('change', next)\n emit('clear')\n setQuery('')\n void nextTick(focusInput)\n}\n\nconst showClear = computed(() =>\n resolvedClearable.value && !locked.value && (hasSelection.value || query.value.length > 0),\n)\n\n// ————— Чипы (multiple): стрелки вместо таб-стопов.\n//\n// Крестики намеренно не табируемы: у combobox фокус живёт на `<input>`, и\n// двадцать выбранных значений не должны давать двадцать остановок Tab. Поэтому\n// вход в чипы — ArrowLeft из пустого запроса, а выход — ArrowRight/Esc/печать.\nfunction chipRemoveButtons(): HTMLButtonElement[] {\n return [...(rootEl.value?.querySelectorAll<HTMLButtonElement>('[data-gr-autocomplete-chip-remove]') ?? [])]\n}\n\nfunction focusChip(index: number): void {\n const buttons = chipRemoveButtons()\n if (!buttons.length) {\n focusInput()\n return\n }\n buttons[Math.min(Math.max(index, 0), buttons.length - 1)].focus()\n}\n\nfunction onChipKeydown(event: KeyboardEvent, index: number, value: TValue): void {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n focusChip(index - 1)\n break\n case 'ArrowRight':\n event.preventDefault()\n if (index >= chipRemoveButtons().length - 1) focusInput()\n else focusChip(index + 1)\n break\n case 'Home':\n event.preventDefault()\n focusChip(0)\n break\n case 'End':\n event.preventDefault()\n focusChip(chipRemoveButtons().length - 1)\n break\n case 'Delete':\n case 'Backspace':\n event.preventDefault()\n removeValue(value, () => focusChip(index))\n break\n case 'Escape':\n event.preventDefault()\n focusInput()\n break\n default:\n // Печатный символ возвращает в поле: фокус переезжает синхронно, до\n // вставки текста, поэтому символ попадает в запрос, а не пропадает.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) focusInput()\n }\n}\n\n// ————— Клавиатура.\nfunction onKeydown(event: KeyboardEvent): void {\n // Клавиша во время IME-композиции принадлежит композиции: Enter коммитит её,\n // Esc отменяет, стрелки ходят по кандидатам.\n if (isComposingEvent(event)) return\n if (locked.value) return\n\n // Курсор в начале пустого запроса — стрелка влево уходит к чипам, а не\n // двигает каретку, которой всё равно некуда двигаться.\n if (event.key === 'ArrowLeft' && props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n closeDropdown()\n focusChip(selectedValues.value.length - 1)\n return\n }\n\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n openDropdown()\n return\n }\n\n // Только при открытой панели: в закрытом инпуте Home/End двигают каретку.\n if (open.value && handleNavigationKeys(event)) return\n\n switch (event.key) {\n case 'Enter': {\n if (!open.value) break\n event.preventDefault()\n const item = activeItem.value\n if (item?.kind === 'option') chooseOption(item.option)\n else if (item?.kind === 'add' || canAddCustom.value) commitCustom()\n break\n }\n case 'Backspace':\n // multiple: пустой запрос + Backspace удаляет последний chip.\n if (props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n removeValue(selectedValues.value[selectedValues.value.length - 1])\n }\n break\n case 'Tab':\n closeDropdown()\n break\n }\n}\n\n// ————— Синхронизация query с выбранным значением (single).\nwatch(\n [singleSelectedLabel, () => props.multiple],\n ([label, multiple]) => {\n if (multiple) return\n // Не перетираем то, что пользователь сейчас набирает.\n if (open.value || dirty.value) return\n setQuery(label)\n },\n { immediate: true },\n)\n\nwatch(open, (isOpen) => {\n if (isOpen) {\n initActiveIndex()\n return\n }\n // Закрытие: сбрасываем активную опцию и «черновик».\n resetActive()\n if (props.multiple) {\n setQuery('')\n }\n else {\n // single: возвращаем метку выбранной опции (revert ввода без выбора).\n setQuery(singleSelectedLabel.value)\n }\n})\n\nconst ariaAutocomplete = computed(() => (props.allowCustomValue ? 'both' : 'list'))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-autocomplete\n class=\"relative w-full\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <div\n data-gr-autocomplete-shell\n :class=\"autocompleteShellClass({ size: resolvedSize, disabled: isDisabled, invalid: isInvalid })\"\n @mousedown.self.prevent=\"focusInput\"\n >\n <span\n v-if=\"hasPrefix\"\n ref=\"prefixEl\"\n data-gr-autocomplete-prefix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"prefixStyle\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <!-- Chips выбранных значений (multiple). -->\n <template v-if=\"multiple\">\n <GrBadge\n v-for=\"(option, chipIndex) in selectedOptions\"\n :key=\"option.value\"\n data-gr-autocomplete-chip\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n >\n <span class=\"inline-flex max-w-full items-center gap-1 align-middle\">\n <span class=\"truncate\">{{ option.label }}</span>\n <button\n v-if=\"!locked\"\n type=\"button\"\n data-gr-autocomplete-chip-remove\n class=\"inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-[var(--gr-radius-full)] text-current focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :aria-label=\"t('gr.autocomplete.removeValue', 'Remove {label}', { label: option.label })\"\n tabindex=\"-1\"\n @click=\"removeValue(option.value)\"\n @keydown=\"onChipKeydown($event, chipIndex, option.value)\"\n >\n <IconX class=\"block h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </span>\n </GrBadge>\n </template>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n data-gr-autocomplete-input\n data-testid=\"gr-autocomplete-input\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n role=\"combobox\"\n :value=\"query\"\n :disabled=\"isDisabled\"\n :placeholder=\"hasSelection && multiple ? undefined : placeholder\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :readonly=\"isReadonly\"\n aria-haspopup=\"listbox\"\n :aria-autocomplete=\"ariaAutocomplete\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n class=\"min-w-0 flex-1 bg-transparent text-inherit placeholder:text-[var(--gr-muted-fg)] focus:outline-none disabled:cursor-not-allowed\"\n @input=\"onInput\"\n @focus=\"onFocus\"\n @keydown=\"onKeydown\"\n >\n\n <span\n v-if=\"hasSuffix\"\n ref=\"suffixEl\"\n data-gr-autocomplete-suffix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"suffixStyle\"\n >\n <slot name=\"suffix\" />\n </span>\n\n <!-- Trailing: спиннер / очистка / шеврон. -->\n <span\n v-if=\"isLoading\"\n data-gr-autocomplete-spinner\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)]\"\n >\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n </span>\n\n <button\n v-else-if=\"showClear\"\n type=\"button\"\n data-testid=\"gr-autocomplete-clear\"\n data-gr-autocomplete-clear\n class=\"shrink-0 inline-flex h-5 w-5 items-center justify-center rounded-[var(--gr-radius-control)] text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)]\"\n :aria-label=\"resolvedClearLabel\"\n @click=\"clearSelection\"\n >\n <IconX class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <span\n v-else\n data-testid=\"gr-autocomplete-chevron\"\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)] pointer-events-none\"\n >\n <IconChevronDown class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <!-- Нативная форма: сериализуется модель, а не текст запроса. -->\n <template v-if=\"name\">\n <input\n v-for=\"value in selectedValues\"\n :key=\"`hidden-${String(value)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <transition\n enter-active-class=\"transition ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-autocomplete-panel\"\n v-bind=\"themeAttrs\"\n data-gr-autocomplete-panel\n data-gr-overlay-root\n :style=\"floatingStyle\"\n >\n <div :class=\"autocompletePanelClasses\">\n <!--\n Пустой listbox не рендерим: его `p-1` давал полосу пустоты над\n строкой состояния, и «ничего не найдено» выглядело прижатым к низу.\n -->\n <div\n v-if=\"showAddOption || renderedOptions.length\"\n :id=\"listboxId\"\n ref=\"listboxEl\"\n data-gr-autocomplete-listbox\n :data-gr-virtual=\"virtual ? '' : undefined\"\n class=\"p-1 overflow-auto\"\n :class=\"virtual ? 'flex flex-col' : ''\"\n :style=\"listboxStyle\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <!--\n Прямые потомки listbox — только опции: роль объявляет остальных\n недопустимыми детьми (`aria-required-children`), поэтому строки\n состояний живут ниже, вне списка. `mousedown.prevent` держит\n фокус на `<input>` — контракт combobox с `aria-activedescendant`.\n -->\n <button\n v-if=\"showAddOption\"\n :id=\"addOptionDomId\"\n type=\"button\"\n role=\"option\"\n aria-selected=\"false\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(0)\"\n data-testid=\"gr-autocomplete-add-option\"\n data-gr-autocomplete-add-option\n :class=\"autocompleteOptionClass({ disabled: false, active: activeItem?.kind === 'add' })\"\n @mousedown.prevent\n @click=\"commitCustom\"\n @mousemove=\"activeIndex = 0\"\n >\n {{ t('gr.autocomplete.addOption', 'Add \"{value}\"', { value: query.trim() }) }}\n </button>\n\n <button\n v-for=\"{ option, index: optionIndex } in renderedOptions\"\n :id=\"optionDomId(optionIndex)\"\n :key=\"option.value\"\n :ref=\"(el) => virtual && virtualizer.measure(optionIndex + addOffset, el as Element | null)\"\n data-gr-autocomplete-option\n type=\"button\"\n role=\"option\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(optionIndex + addOffset)\"\n :disabled=\"option.disabled\"\n :aria-selected=\"isSelected(option.value) ? 'true' : 'false'\"\n :aria-disabled=\"option.disabled ? 'true' : undefined\"\n :class=\"autocompleteOptionClass({\n disabled: !!option.disabled,\n active: activeValue === option.value,\n })\"\n @mousedown.prevent\n @click=\"chooseOption(option)\"\n @mousemove=\"activeIndex = navigableIndexOf(option.value)\"\n >\n <slot name=\"option\" :option=\"option\" :selected=\"isSelected(option.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span class=\"inline-block h-4 w-4 shrink-0\" aria-hidden=\"true\">\n <IconCheck v-if=\"isSelected(option.value)\" class=\"block h-4 w-4 text-[var(--gr-primary)]\" />\n </span>\n <span class=\"truncate\">{{ option.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n\n <!--\n Загрузка, «введите ещё N» и «ничего не найдено» меняются молча:\n `aria-label` на generic-элементе AT игнорируют. Один живой регион\n на все три — и вне listbox, чтобы не ломать его состав.\n -->\n <div\n v-if=\"isLoading || belowMinQuery || showEmpty\"\n data-gr-autocomplete-status\n role=\"status\"\n aria-live=\"polite\"\n :class=\"autocompleteStateClass\"\n >\n <template v-if=\"isLoading\">\n <span data-gr-autocomplete-loading class=\"flex items-center gap-2\">\n <slot name=\"loading\">\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </span>\n </template>\n <template v-else-if=\"belowMinQuery\">\n <span data-gr-autocomplete-hint>{{ resolvedTypeMoreText }}</span>\n </template>\n <template v-else>\n <span data-gr-autocomplete-empty>\n <slot name=\"empty\">\n {{ resolvedNoResultsText }}\n </slot>\n </span>\n </template>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n\n<style scoped>\n/*\n * Распорки виртуального списка.\n *\n * Не `padding` контейнера: `max-height` меряет ту же коробку, и распорка в\n * десятки тысяч пикселей раздула бы панель. Не обёртка внутри listbox'а:\n * прямыми потомками роли обязаны быть только опции (`aria-required-children`,\n * гейт — `GrAutocomplete.a11y.test.ts`). Не отступы крайних опций: при прыжке\n * прокрутки опции заменяются целиком, распорка исчезла бы вместе с ними, и\n * браузер обрезал бы `scrollTop`.\n *\n * Псевдоэлементы не узлы DOM: их не за что размонтировать, детьми listbox'а\n * они не считаются, а высота приезжает переменными в том же патче, что и опции.\n */\n[data-gr-virtual]::before,\n[data-gr-virtual]::after {\n content: '';\n display: block;\n flex: none;\n}\n\n[data-gr-virtual]::before {\n height: var(--gr-virtual-before, 0px);\n}\n\n[data-gr-virtual]::after {\n height: var(--gr-virtual-after, 0px);\n}\n</style>\n","<script setup lang=\"ts\" generic=\"TValue extends GrAutocompleteValue = string\">\nimport { computed, nextTick, onBeforeUnmount, ref, shallowRef, useId, watch } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\n\nimport { useGrComponentProp, useGrComponentSize, useGrThemeAttrs } from '../GrConfigProvider/context'\n\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/useFloating'\nimport { useDismissible } from '../../composables/useDismissible'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { isComposingEvent } from '../../internal/keyboard'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useControlledOpen } from '../../composables/internal/useControlledOpen'\nimport { useComboboxNavigation } from '../../composables/useComboboxNavigation'\nimport { filterOptions, resolveSelectedOptions } from '../shared/optionFilter'\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge/grBadgeStyles'\n\nimport {\n autocompleteOptionClass,\n autocompletePanelClasses,\n autocompleteShellClass,\n autocompleteStateClass,\n type GrAutocompleteModelValue,\n type GrAutocompleteOption,\n type GrAutocompleteValue,\n type GrAutocompleteSize,\n} from './grAutocompleteStyles'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconChevronDown from '~icons/lucide/chevron-down'\nimport IconLoaderCircle from '~icons/lucide/loader-circle'\nimport IconX from '~icons/lucide/x'\n\nexport type {\n GrAutocompleteModelValue,\n GrAutocompleteOption,\n GrAutocompleteSize,\n GrAutocompleteValue,\n} from './grAutocompleteStyles'\n\n/**\n * Публичный GR-примитив «Autocomplete / Combobox» (WAI-ARIA editable combobox).\n *\n * В отличие от `GrSelect` (select-only combobox с кнопкой-триггером), здесь\n * combobox-ом является сам текстовый `<input>`: набранный текст — это поисковый\n * запрос, а выбор опции заполняет поле. Поддерживает локальную фильтрацию,\n * удалённую (async) загрузку через событие `search` + внешний проп `loading`,\n * произвольные значения (`allowCustomValue`) и multiple с удаляемыми chips.\n */\nexport interface GrAutocompleteProps<TValue extends GrAutocompleteValue = string> {\n /** Выбранное значение (single — строка, multiple — массив строк). */\n modelValue: GrAutocompleteModelValue<TValue>\n /**\n * Доступные опции. Для локального режима — полный список (фильтруется на клиенте).\n * Для remote-режима (`filterable=false`) — список, который родитель обновляет\n * в ответ на событие `search`.\n */\n options?: GrAutocompleteOption<TValue>[]\n multiple?: boolean\n /**\n * Вид чипов выбранных значений в режиме `multiple`. Чип — это `GrBadge`, а не\n * своя плашка: на светлой теме собственная заливка чипа (примесь `--gr-muted`\n * без рамки) давала 1.02:1 к фону поля, то есть была невидима.\n */\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n disabled?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrAutocompleteSize\n placeholder?: string\n ariaLabel?: string\n /** Кнопка очистки выбранного значения/запроса. */\n clearable?: boolean\n /** Внешне управляемое состояние загрузки (для async-сценариев). */\n loading?: boolean\n /**\n * Локальная фильтрация опций по введённому запросу. Отключите (`false`) для\n * чисто удалённого поиска — тогда `options` показываются как есть, а фильтрацию\n * выполняет сервер по событию `search`.\n */\n filterable?: boolean\n /** Кастомный матчер локальной фильтрации. По умолчанию — подстрока в `label`/`value`. */\n filter?: (option: GrAutocompleteOption<TValue>, query: string) => boolean\n /**\n * Удалённая загрузка опций под управлением компонента: дебаунс, отмена\n * устаревшего запроса и `loading` берёт на себя он. Ответ на отменённый\n * запрос игнорируется — при быстром вводе в списке всегда результат\n * последнего запроса, а не того, который вернулся позже.\n *\n * `signal` пробрасывается в `fetch`. Локальная фильтрация в этом режиме\n * выключена: список фильтрует сервер. Альтернатива — событие `search`, если\n * запрос ведёт само приложение.\n */\n fetchOptions?: (query: string, signal: AbortSignal) => Promise<GrAutocompleteOption<TValue>[]>\n /** Минимальная длина запроса до эмита `search` (для дебаунса remote-загрузки). */\n minQueryLength?: number\n /** Задержка дебаунса события `search`, мс. */\n debounce?: number\n /** Разрешить ввод/коммит значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Закрывать панель после выбора (single всегда закрывает). */\n closeOnSelect?: boolean\n /** Максимальная высота панели, px. */\n dropdownMaxHeight?: number\n /**\n * Виртуализация панели: в DOM живёт только окно вокруг вьюпорта.\n *\n * Высоту окна задаёт `dropdownMaxHeight`. Включается осознанно: на сотне\n * опций выигрыша нет, а в разметке остаётся только окно — вместе с ним\n * меняется и то, что находит `querySelector` потребителя. Профильный\n * сценарий — удалённый поиск по справочнику на тысячи позиций.\n */\n virtual?: boolean\n /** i18n-тексты состояний панели / aria. */\n loadingText?: string\n noResultsText?: string\n clearLabel?: string\n /**\n * Контролируемое состояние панели (`v-model:open`). Без пропа панель ведёт\n * себя сама (uncontrolled), с ним — слушайте `update:open` и меняйте проп.\n */\n open?: boolean\n /** Имя для нативной формы: hidden input по значению модели (не по тексту запроса). */\n name?: string\n /**\n * Ширины аддонов `prefix`/`suffix` — общий контракт контролов пакета\n * (`docs/form-controls.md`).\n */\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface GrAutocompleteEmits<TValue extends GrAutocompleteValue = string> {\n (e: 'update:modelValue', value: GrAutocompleteModelValue<TValue>): void\n /** Дебаунснутый поисковый запрос — точка входа для удалённой загрузки опций. */\n (e: 'search', query: string): void\n /** Запрос `fetchOptions` завершился ошибкой (отмена устаревшего сюда не приходит). */\n (e: 'searchError', error: unknown): void\n /** Значение зафиксировано выбором или снятием опции. */\n (e: 'change', value: GrAutocompleteModelValue<TValue>): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n /** Значение снято кнопкой очистки; только при `clearable`. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrAutocompleteProps<TValue>>(),\n {\n options: undefined,\n multiple: false,\n tagTone: 'neutral',\n tagDark: false,\n // `sm`, а не `md` как у `GrInputTag`: чип живёт внутри контрола, а не в тексте.\n tagSize: 'sm',\n tagRadius: 'round',\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n placeholder: undefined,\n ariaLabel: undefined,\n clearable: undefined,\n loading: false,\n filterable: true,\n filter: undefined,\n fetchOptions: undefined,\n minQueryLength: 0,\n debounce: 250,\n allowCustomValue: false,\n closeOnSelect: true,\n dropdownMaxHeight: 280,\n virtual: false,\n loadingText: undefined,\n noResultsText: undefined,\n clearLabel: undefined,\n open: undefined,\n name: undefined,\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\n/** Ширина аддона по умолчанию — та же лестница, что у `GrInput`. */\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrAutocompleteSize, string> = {\n xs: '2rem',\n sm: '2.25rem',\n md: '2.5rem',\n lg: '3rem',\n}\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrAutocomplete' })\nconst resolvedClearable = useGrComponentProp('GrAutocomplete', 'clearable', () => props.clearable, false)\n\n/**\n * Аддоны живут флекс-соседями поля, а не поверх него: оболочка автокомплита —\n * флекс-строка с чипами, поэтому резервировать место паддингом не нужно.\n * Контракт ширин остаётся общим (`useControlAddons`).\n */\nconst { hasPrefix, hasSuffix, prefixEl, suffixEl, prefixStyle, suffixStyle } = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => '0px',\n})\n\nconst emit = defineEmits<GrAutocompleteEmits<TValue>>()\ndefineSlots<{\n /** Аддон слева от поля ввода. */\n prefix?: () => any\n /** Аддон справа, перед спиннером и крестиком. */\n suffix?: () => any\n /** Строка списка вместо подписи опции. */\n option?: (props: { option: GrAutocompleteOption<TValue>, selected: boolean }) => any\n /** Содержимое панели, пока едут опции. */\n loading?: () => any\n /** Содержимое панели, когда подходящих опций нет. */\n empty?: () => any\n}>()\n\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.autocomplete.loading', 'Loading…'))\nconst resolvedNoResultsText = computed(() => props.noResultsText ?? t('gr.autocomplete.noResults', 'No results'))\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.common.clear', 'Clear'))\n// Число уходит под двумя общепринятыми именами: `n` читают `@feugene/fint-i18n`\n// и `vue-i18n`, `count` — `i18next`. Формы для каждого языка лежат в словаре,\n// выбирает их переводчик; встроенный fallback — одна английская строка.\nconst resolvedTypeMoreText = computed(() =>\n t('gr.autocomplete.typeMore', 'Type at least {n} characters', {\n n: props.minQueryLength,\n count: props.minQueryLength,\n }),\n)\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\n// Read-only запирает контрол так же, как disabled: значение видно, но панель не\n// открывается, опции не выбираются и чипы не удаляются. Без этого `readonly`\n// оставался бы ARIA-атрибутом без поведения.\nconst locked = computed(() => isDisabled.value || isReadonly.value)\n\n// Ответ последнего `fetchOptions`. До первого ответа показываем `options` —\n// с ними компонент рисует стартовый список, не дожидаясь сервера.\nconst remoteOptions = shallowRef<GrAutocompleteOption<TValue>[]>([])\nconst remoteAnswered = ref(false)\nconst remoteLoading = ref(false)\n\nconst optionsResolved = computed<GrAutocompleteOption<TValue>[]>(() =>\n props.fetchOptions && remoteAnswered.value ? remoteOptions.value : (props.options ?? []),\n)\n\n/**\n * Подпись состава стартового списка. Сравнивать идентичность массива нельзя:\n * инлайн-литерал `:options=\"[...]\"` пересоздаётся каждым ререндером родителя —\n * в том числе тем, который вызвал сам компонент своим `update:modelValue`, —\n * и remote-результаты исчезали бы прямо посреди выбора.\n */\nfunction optionsSignature(options: GrAutocompleteOption<TValue>[] | undefined): string {\n return (options ?? []).map(o => `${String(o.value)}\\u0000${o.label}`).join('\\u0001')\n}\n\n// Родитель сменил стартовый список — он снова источник до следующего ответа\n// сервера, а летящий запрос относится к прежнему набору данных и отменяется.\nwatch(() => optionsSignature(props.options), () => {\n if (!props.fetchOptions) return\n cancelSearch()\n remoteAnswered.value = false\n remoteOptions.value = []\n})\n\nconst isLoading = computed(() => props.loading || remoteLoading.value)\n\n/** `0` — валидное значение, поэтому «пусто» проверяется явно, а не через falsy. */\nfunction isEmptyValue(value: unknown): boolean {\n return value === undefined || value === null || value === ''\n}\n\nfunction toArray(value: GrAutocompleteModelValue<TValue>): TValue[] {\n if (Array.isArray(value)) return value\n if (isEmptyValue(value)) return []\n return [value]\n}\n\nconst modelSingle = computed<TValue | ''>(() => {\n const raw = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue\n return isEmptyValue(raw) ? '' : (raw as TValue)\n})\nconst selectedValues = computed(() => (props.multiple ? toArray(props.modelValue) : (isEmptyValue(modelSingle.value) ? [] : [modelSingle.value as TValue])))\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nfunction labelFor(value: GrAutocompleteValue): string {\n return optionsResolved.value.find(o => o.value === value)?.label ?? String(value)\n}\n\n/** Опции выбранных значений (для chips в multiple). Неизвестные значения показываем как есть. */\nconst selectedOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n resolveSelectedOptions(selectedValues.value, optionsResolved.value),\n)\n\nconst singleSelectedLabel = computed(() => (props.multiple || isEmptyValue(modelSingle.value) ? '' : labelFor(modelSingle.value)))\n\n// ————— Состояние.\nconst query = ref('')\n// `dirty` — пользователь начал вводить запрос. Нужно, чтобы в single-режиме при\n// открытии заполненного поля показать весь список (а не отфильтровать по метке\n// уже выбранной опции). Сбрасывается при программной установке `query`.\nconst dirty = ref(false)\n// Имя `open` сохранено: читатели работают с computed-Ref.\nconst { open, setOpen } = useControlledOpen(\n () => props.open,\n next => emit('update:open', next),\n)\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst listboxEl = ref<HTMLElement | null>(null)\n\n// Панель телепортирована в `body`, то есть лежит вне корня, но для пользователя\n// она часть контрола: без неё уход фокуса в панель читался бы как `blur`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n containers: () => [panelEl.value],\n})\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\ndefineExpose({\n focus,\n blur,\n open: () => openDropdown(),\n close: () => closeDropdown(),\n})\nconst clickOutsideExclude = [() => panelEl.value]\n\nconst { floatingStyle } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: true,\n zIndexVar: '--gr-z-dropdown',\n})\n\nuseDismissible(open, closeDropdown)\n\n// ————— Фильтрация.\nconst searchQuery = computed(() => {\n // При `fetchOptions` фильтрует сервер: локальный матчер отсеял бы то, что он\n // уже прислал в ответ на этот же запрос.\n if (!props.filterable || props.fetchOptions) return ''\n // single: пока пользователь не начал вводить — показываем весь список.\n if (!props.multiple && !dirty.value) return ''\n return query.value.trim()\n})\n\nconst filteredOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n filterOptions(optionsResolved.value, searchQuery.value, props.filter),\n)\n\nconst belowMinQuery = computed(() => props.minQueryLength > 0 && query.value.trim().length < props.minQueryLength)\n\n/**\n * Опции к показу и навигации. Ниже `minQueryLength` — пусто: список ещё\n * относится к прошлому запросу, показывать его под подсказкой «введите ещё N»\n * значит дезинформировать.\n */\nconst effectiveOptions = computed<GrAutocompleteOption<TValue>[]>(() =>\n belowMinQuery.value ? [] : filteredOptions.value,\n)\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n // Кастомное значение набирается текстом — оно строковое по природе;\n // при числовом `TValue` эта ветка неприменима (см. docs/components.md).\n const v = query.value.trim() as TValue\n if (!v) return false\n if (props.multiple && selectedValues.value.includes(v)) return false\n if (!props.multiple && v === modelSingle.value) return false\n // Не предлагаем «Add», если такое значение/метка уже есть среди опций.\n return !optionsResolved.value.some(o => o.value === v || o.label === v)\n})\n\nconst showEmpty = computed(() =>\n !isLoading.value && effectiveOptions.value.length === 0 && !canAddCustom.value,\n)\n\n// ————— Панель: id/aria-activedescendant.\nconst listboxId = useId()\n\n/**\n * Id опции строится от позиции, а не от значения: значение с пробелом дало бы\n * невалидный `id`, а `aria-activedescendant` — два токена вместо одной ссылки,\n * и активная опция перестала бы объявляться.\n */\nfunction optionDomId(index: number): string {\n return `${listboxId}-opt-${index}`\n}\nconst addOptionDomId = computed(() => `${listboxId}-add`)\n\n/**\n * Виртуализация панели.\n *\n * Набор — `[«Add …»?] + filteredOptions`, тот же, по которому ходит клавиатура\n * (`navigableItems` ниже). Иначе верхняя распорка вытолкнула бы строку «Add …»\n * вниз: она отрисована внутри listbox'а первой и является полноценной опцией.\n */\n\n/** Оценка высоты опции: `py-2` вокруг строки кегля `sm`. Уточняется замером. */\nconst OPTION_SIZE_ESTIMATE = 36\n\nconst addOffset = computed(() => (canAddCustom.value ? 1 : 0))\nconst virtualCount = computed(() => effectiveOptions.value.length + addOffset.value)\n\nconst virtualizer = useVirtualList({\n container: listboxEl,\n count: () => (props.virtual ? virtualCount.value : 0),\n // Фильтрация/remote-ответ пересобирают набор — замеры прошлого невалидны.\n source: () => effectiveOptions.value,\n itemSize: OPTION_SIZE_ESTIMATE,\n // Панель скрыта `v-show`, пока закрыта, поэтому `clientHeight` контейнера —\n // ноль. Окно считается от объявленной высоты до первого настоящего замера.\n viewportSize: () => props.dropdownMaxHeight,\n})\n\n/** Виден ли «Add …»: вне виртуального окна его рисовать нельзя — он элемент набора. */\nconst showAddOption = computed(() => {\n if (!canAddCustom.value) return false\n return !props.virtual || virtualizer.range.value.start === 0\n})\n\n/** Опции к отрисовке вместе с их абсолютным индексом в `filteredOptions`. */\nconst renderedOptions = computed(() => {\n const all = effectiveOptions.value\n if (!props.virtual) return all.map((option, index) => ({ option, index }))\n\n const { start, end } = virtualizer.range.value\n const from = Math.max(0, start - addOffset.value)\n const to = Math.max(0, end - addOffset.value)\n\n return all.slice(from, to).map((option, offset) => ({ option, index: from + offset }))\n})\n\n/**\n * Размер набора и позиция в нём объявляются только при виртуализации: в обычном\n * режиме диктор выводит их из DOM, а при неполном наборе получил бы «3 из 15»\n * на списке в десять тысяч.\n */\nfunction optionSetProps(virtualIndex: number): Record<string, number> | undefined {\n if (!props.virtual) return undefined\n return { 'aria-setsize': virtualCount.value, 'aria-posinset': virtualIndex + 1 }\n}\n\nconst listboxStyle = computed(() => {\n const base: Record<string, string> = { maxHeight: `${props.dropdownMaxHeight}px` }\n if (!props.virtual) return base\n\n return {\n ...base,\n ...virtualizer.spacerStyle.value,\n }\n})\n\n/**\n * Клавиатурная навигация ходит и по опциям, и по варианту «добавить своё»:\n * иначе при непустом списке Enter всегда уходил бы в активную опцию, и\n * закоммитить произвольное значение с клавиатуры было бы нечем.\n */\ntype NavigableItem =\n | { kind: 'add' }\n | { kind: 'option', option: GrAutocompleteOption<TValue>, index: number }\n\nconst navigableItems = computed<NavigableItem[]>(() => {\n const items: NavigableItem[] = canAddCustom.value ? [{ kind: 'add' }] : []\n effectiveOptions.value.forEach((option, index) => {\n if (!option.disabled) items.push({ kind: 'option', option, index })\n })\n return items\n})\n\nfunction isSelected(value: TValue): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction navigableIndexOf(value: TValue): number {\n return navigableItems.value.findIndex(item => item.kind === 'option' && item.option.value === value)\n}\n\n/** Прокрутка к активному: сперва окно виртуального списка, затем доводка. */\nasync function scrollActiveIntoView(item: NavigableItem): Promise<void> {\n // Вне окна активной опции в DOM нет: `getElementById` вернул бы `null`,\n // прокрутка не случилась бы, а `aria-activedescendant` указал бы в пустоту.\n if (props.virtual)\n virtualizer.scrollToIndex(item.kind === 'add' ? 0 : item.index + addOffset.value)\n\n await nextTick()\n const id = item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)\n document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' })\n}\n\nconst {\n activeIndex,\n activeItem,\n activeDescendantId,\n init: initActiveIndex,\n reset: resetActive,\n handleNavigationKeys,\n} = useComboboxNavigation<NavigableItem>({\n items: () => navigableItems.value,\n open: () => open.value,\n idOf: item => (item.kind === 'add' ? addOptionDomId.value : optionDomId(item.index)),\n initialIndex: () => navigableItems.value.findIndex(item => item.kind === 'option' && isSelected(item.option.value)),\n scrollTo: item => scrollActiveIntoView(item),\n})\n\nconst activeValue = computed(() => (activeItem.value?.kind === 'option' ? activeItem.value.option.value : undefined))\n\n// ————— Открытие/закрытие.\nfunction openDropdown(): void {\n if (locked.value || open.value) return\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction focusInput(): void {\n inputEl.value?.focus()\n}\n\n// ————— Дебаунснутый поиск для remote-загрузки.\nlet searchTimer: ReturnType<typeof setTimeout> | null = null\n// Счётчик запросов: ответ, стартовавший раньше, но пришедший позже, обязан\n// проиграть последнему — иначе список показывает результат старого запроса.\nlet searchSeq = 0\nlet inflight: AbortController | null = null\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError'\n}\n\nasync function runFetch(query: string): Promise<void> {\n const fetchOptions = props.fetchOptions\n if (!fetchOptions) return\n\n inflight?.abort()\n const controller = new AbortController()\n inflight = controller\n const seq = ++searchSeq\n remoteLoading.value = true\n\n try {\n const result = await fetchOptions(query, controller.signal)\n if (seq !== searchSeq) return\n remoteOptions.value = result\n remoteAnswered.value = true\n }\n catch (error) {\n if (seq !== searchSeq || isAbortError(error)) return\n remoteOptions.value = []\n remoteAnswered.value = true\n emit('searchError', error)\n }\n finally {\n if (seq === searchSeq) remoteLoading.value = false\n }\n}\n\nfunction scheduleSearch(value: string): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = setTimeout(() => {\n if (props.minQueryLength > 0 && value.trim().length < props.minQueryLength) return\n emit('search', value.trim())\n void runFetch(value.trim())\n }, props.debounce)\n}\n\n/**\n * Снять запланированный и летящий запрос. Инкремент `searchSeq` обязателен\n * вместе со снятием `remoteLoading`: после него `finally` в `runFetch` считает\n * себя устаревшим и флаг не тронет — спиннер остался бы навсегда.\n */\nfunction cancelSearch(): void {\n if (searchTimer) clearTimeout(searchTimer)\n searchTimer = null\n inflight?.abort()\n inflight = null\n searchSeq += 1\n remoteLoading.value = false\n}\n\nonBeforeUnmount(cancelSearch)\n\n// ————— Ввод.\nfunction onInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value\n query.value = value\n dirty.value = true\n openDropdown()\n scheduleSearch(value)\n}\n\nfunction onFocus(): void {\n if (locked.value) return\n openDropdown()\n}\n\nfunction setQuery(value: string): void {\n query.value = value\n dirty.value = false\n}\n\n// ————— Выбор значений.\nfunction selectSingle(value: TValue, label: string): void {\n emit('update:modelValue', value)\n emit('change', value as GrAutocompleteModelValue<TValue>)\n setQuery(label)\n closeDropdown()\n}\n\nfunction toggleMultiple(value: TValue): void {\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) next.splice(idx, 1)\n else next.push(value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n setQuery('')\n if (props.closeOnSelect) closeDropdown()\n else void nextTick(focusInput)\n}\n\nfunction chooseOption(option: GrAutocompleteOption<TValue>): void {\n if (locked.value || option.disabled) return\n if (props.multiple) toggleMultiple(option.value)\n else selectSingle(option.value, option.label)\n}\n\nfunction commitCustom(): void {\n if (locked.value) return\n // Кастомное значение строковое по природе — см. `canAddCustom`.\n const v = query.value.trim() as TValue\n if (!v) return\n if (props.multiple) toggleMultiple(v)\n else selectSingle(v, String(v))\n}\n\nfunction removeValue(value: TValue, focusAfter: () => void = focusInput): void {\n if (locked.value) return\n const next = selectedValues.value.filter(v => v !== value)\n emit('update:modelValue', next)\n emit('change', next as GrAutocompleteModelValue<TValue>)\n void nextTick(focusAfter)\n}\n\nfunction clearSelection(): void {\n if (locked.value) return\n const next = (props.multiple ? [] : '') as GrAutocompleteModelValue<TValue>\n emit('update:modelValue', next)\n emit('change', next)\n emit('clear')\n setQuery('')\n void nextTick(focusInput)\n}\n\nconst showClear = computed(() =>\n resolvedClearable.value && !locked.value && (hasSelection.value || query.value.length > 0),\n)\n\n// ————— Чипы (multiple): стрелки вместо таб-стопов.\n//\n// Крестики намеренно не табируемы: у combobox фокус живёт на `<input>`, и\n// двадцать выбранных значений не должны давать двадцать остановок Tab. Поэтому\n// вход в чипы — ArrowLeft из пустого запроса, а выход — ArrowRight/Esc/печать.\nfunction chipRemoveButtons(): HTMLButtonElement[] {\n return [...(rootEl.value?.querySelectorAll<HTMLButtonElement>('[data-gr-autocomplete-chip-remove]') ?? [])]\n}\n\nfunction focusChip(index: number): void {\n const buttons = chipRemoveButtons()\n if (!buttons.length) {\n focusInput()\n return\n }\n buttons[Math.min(Math.max(index, 0), buttons.length - 1)].focus()\n}\n\nfunction onChipKeydown(event: KeyboardEvent, index: number, value: TValue): void {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n focusChip(index - 1)\n break\n case 'ArrowRight':\n event.preventDefault()\n if (index >= chipRemoveButtons().length - 1) focusInput()\n else focusChip(index + 1)\n break\n case 'Home':\n event.preventDefault()\n focusChip(0)\n break\n case 'End':\n event.preventDefault()\n focusChip(chipRemoveButtons().length - 1)\n break\n case 'Delete':\n case 'Backspace':\n event.preventDefault()\n removeValue(value, () => focusChip(index))\n break\n case 'Escape':\n event.preventDefault()\n focusInput()\n break\n default:\n // Печатный символ возвращает в поле: фокус переезжает синхронно, до\n // вставки текста, поэтому символ попадает в запрос, а не пропадает.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) focusInput()\n }\n}\n\n// ————— Клавиатура.\nfunction onKeydown(event: KeyboardEvent): void {\n // Клавиша во время IME-композиции принадлежит композиции: Enter коммитит её,\n // Esc отменяет, стрелки ходят по кандидатам.\n if (isComposingEvent(event)) return\n if (locked.value) return\n\n // Курсор в начале пустого запроса — стрелка влево уходит к чипам, а не\n // двигает каретку, которой всё равно некуда двигаться.\n if (event.key === 'ArrowLeft' && props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n closeDropdown()\n focusChip(selectedValues.value.length - 1)\n return\n }\n\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n openDropdown()\n return\n }\n\n // Только при открытой панели: в закрытом инпуте Home/End двигают каретку.\n if (open.value && handleNavigationKeys(event)) return\n\n switch (event.key) {\n case 'Enter': {\n if (!open.value) break\n event.preventDefault()\n const item = activeItem.value\n if (item?.kind === 'option') chooseOption(item.option)\n else if (item?.kind === 'add' || canAddCustom.value) commitCustom()\n break\n }\n case 'Backspace':\n // multiple: пустой запрос + Backspace удаляет последний chip.\n if (props.multiple && query.value === '' && selectedValues.value.length) {\n event.preventDefault()\n removeValue(selectedValues.value[selectedValues.value.length - 1])\n }\n break\n case 'Tab':\n closeDropdown()\n break\n }\n}\n\n// ————— Синхронизация query с выбранным значением (single).\nwatch(\n [singleSelectedLabel, () => props.multiple],\n ([label, multiple]) => {\n if (multiple) return\n // Не перетираем то, что пользователь сейчас набирает.\n if (open.value || dirty.value) return\n setQuery(label)\n },\n { immediate: true },\n)\n\nwatch(open, (isOpen) => {\n if (isOpen) {\n initActiveIndex()\n return\n }\n // Закрытие: сбрасываем активную опцию и «черновик».\n resetActive()\n if (props.multiple) {\n setQuery('')\n }\n else {\n // single: возвращаем метку выбранной опции (revert ввода без выбора).\n setQuery(singleSelectedLabel.value)\n }\n})\n\nconst ariaAutocomplete = computed(() => (props.allowCustomValue ? 'both' : 'list'))\n\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-autocomplete\n class=\"relative w-full\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <div\n data-gr-autocomplete-shell\n :class=\"autocompleteShellClass({ size: resolvedSize, disabled: isDisabled, invalid: isInvalid })\"\n @mousedown.self.prevent=\"focusInput\"\n >\n <span\n v-if=\"hasPrefix\"\n ref=\"prefixEl\"\n data-gr-autocomplete-prefix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"prefixStyle\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <!-- Chips выбранных значений (multiple). -->\n <template v-if=\"multiple\">\n <GrBadge\n v-for=\"(option, chipIndex) in selectedOptions\"\n :key=\"option.value\"\n data-gr-autocomplete-chip\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n >\n <span class=\"inline-flex max-w-full items-center gap-1 align-middle\">\n <span class=\"truncate\">{{ option.label }}</span>\n <button\n v-if=\"!locked\"\n type=\"button\"\n data-gr-autocomplete-chip-remove\n class=\"inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-[var(--gr-radius-full)] text-current focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n :aria-label=\"t('gr.autocomplete.removeValue', 'Remove {label}', { label: option.label })\"\n tabindex=\"-1\"\n @click=\"removeValue(option.value)\"\n @keydown=\"onChipKeydown($event, chipIndex, option.value)\"\n >\n <IconX class=\"block h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </span>\n </GrBadge>\n </template>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n data-gr-autocomplete-input\n data-testid=\"gr-autocomplete-input\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n role=\"combobox\"\n :value=\"query\"\n :disabled=\"isDisabled\"\n :placeholder=\"hasSelection && multiple ? undefined : placeholder\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :readonly=\"isReadonly\"\n aria-haspopup=\"listbox\"\n :aria-autocomplete=\"ariaAutocomplete\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n class=\"min-w-0 flex-1 bg-transparent text-inherit placeholder:text-[var(--gr-muted-fg)] focus:outline-none disabled:cursor-not-allowed\"\n @input=\"onInput\"\n @focus=\"onFocus\"\n @keydown=\"onKeydown\"\n >\n\n <span\n v-if=\"hasSuffix\"\n ref=\"suffixEl\"\n data-gr-autocomplete-suffix\n class=\"inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]\"\n :style=\"suffixStyle\"\n >\n <slot name=\"suffix\" />\n </span>\n\n <!-- Trailing: спиннер / очистка / шеврон. -->\n <span\n v-if=\"isLoading\"\n data-gr-autocomplete-spinner\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)]\"\n >\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n </span>\n\n <button\n v-else-if=\"showClear\"\n type=\"button\"\n data-testid=\"gr-autocomplete-clear\"\n data-gr-autocomplete-clear\n class=\"shrink-0 inline-flex h-5 w-5 items-center justify-center rounded-[var(--gr-radius-control)] text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)]\"\n :aria-label=\"resolvedClearLabel\"\n @click=\"clearSelection\"\n >\n <IconX class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <span\n v-else\n data-testid=\"gr-autocomplete-chevron\"\n class=\"shrink-0 flex items-center text-[var(--gr-muted-fg)] pointer-events-none\"\n >\n <IconChevronDown class=\"block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <!-- Нативная форма: сериализуется модель, а не текст запроса. -->\n <template v-if=\"name\">\n <input\n v-for=\"value in selectedValues\"\n :key=\"`hidden-${String(value)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\n\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <transition\n enter-active-class=\"transition ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-autocomplete-panel\"\n v-bind=\"themeAttrs\"\n data-gr-autocomplete-panel\n data-gr-overlay-root\n :style=\"floatingStyle\"\n >\n <div :class=\"autocompletePanelClasses\">\n <!--\n Пустой listbox не рендерим: его `p-1` давал полосу пустоты над\n строкой состояния, и «ничего не найдено» выглядело прижатым к низу.\n -->\n <div\n v-if=\"showAddOption || renderedOptions.length\"\n :id=\"listboxId\"\n ref=\"listboxEl\"\n data-gr-autocomplete-listbox\n :data-gr-virtual=\"virtual ? '' : undefined\"\n class=\"p-1 overflow-auto\"\n :class=\"virtual ? 'flex flex-col' : ''\"\n :style=\"listboxStyle\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <!--\n Прямые потомки listbox — только опции: роль объявляет остальных\n недопустимыми детьми (`aria-required-children`), поэтому строки\n состояний живут ниже, вне списка. `mousedown.prevent` держит\n фокус на `<input>` — контракт combobox с `aria-activedescendant`.\n -->\n <button\n v-if=\"showAddOption\"\n :id=\"addOptionDomId\"\n type=\"button\"\n role=\"option\"\n aria-selected=\"false\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(0)\"\n data-testid=\"gr-autocomplete-add-option\"\n data-gr-autocomplete-add-option\n :class=\"autocompleteOptionClass({ disabled: false, active: activeItem?.kind === 'add' })\"\n @mousedown.prevent\n @click=\"commitCustom\"\n @mousemove=\"activeIndex = 0\"\n >\n {{ t('gr.autocomplete.addOption', 'Add \"{value}\"', { value: query.trim() }) }}\n </button>\n\n <button\n v-for=\"{ option, index: optionIndex } in renderedOptions\"\n :id=\"optionDomId(optionIndex)\"\n :key=\"option.value\"\n :ref=\"(el) => virtual && virtualizer.measure(optionIndex + addOffset, el as Element | null)\"\n data-gr-autocomplete-option\n type=\"button\"\n role=\"option\"\n tabindex=\"-1\"\n v-bind=\"optionSetProps(optionIndex + addOffset)\"\n :disabled=\"option.disabled\"\n :aria-selected=\"isSelected(option.value) ? 'true' : 'false'\"\n :aria-disabled=\"option.disabled ? 'true' : undefined\"\n :class=\"autocompleteOptionClass({\n disabled: !!option.disabled,\n active: activeValue === option.value,\n })\"\n @mousedown.prevent\n @click=\"chooseOption(option)\"\n @mousemove=\"activeIndex = navigableIndexOf(option.value)\"\n >\n <slot name=\"option\" :option=\"option\" :selected=\"isSelected(option.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span class=\"inline-block h-4 w-4 shrink-0\" aria-hidden=\"true\">\n <IconCheck v-if=\"isSelected(option.value)\" class=\"block h-4 w-4 text-[var(--gr-primary)]\" />\n </span>\n <span class=\"truncate\">{{ option.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n\n <!--\n Загрузка, «введите ещё N» и «ничего не найдено» меняются молча:\n `aria-label` на generic-элементе AT игнорируют. Один живой регион\n на все три — и вне listbox, чтобы не ломать его состав.\n -->\n <div\n v-if=\"isLoading || belowMinQuery || showEmpty\"\n data-gr-autocomplete-status\n role=\"status\"\n aria-live=\"polite\"\n :class=\"autocompleteStateClass\"\n >\n <template v-if=\"isLoading\">\n <span data-gr-autocomplete-loading class=\"flex items-center gap-2\">\n <slot name=\"loading\">\n <IconLoaderCircle class=\"block h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </span>\n </template>\n <template v-else-if=\"belowMinQuery\">\n <span data-gr-autocomplete-hint>{{ resolvedTypeMoreText }}</span>\n </template>\n <template v-else>\n <span data-gr-autocomplete-empty>\n <slot name=\"empty\">\n {{ resolvedNoResultsText }}\n </slot>\n </span>\n </template>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n\n<style scoped>\n/*\n * Распорки виртуального списка.\n *\n * Не `padding` контейнера: `max-height` меряет ту же коробку, и распорка в\n * десятки тысяч пикселей раздула бы панель. Не обёртка внутри listbox'а:\n * прямыми потомками роли обязаны быть только опции (`aria-required-children`,\n * гейт — `GrAutocomplete.a11y.test.ts`). Не отступы крайних опций: при прыжке\n * прокрутки опции заменяются целиком, распорка исчезла бы вместе с ними, и\n * браузер обрезал бы `scrollTop`.\n *\n * Псевдоэлементы не узлы DOM: их не за что размонтировать, детьми listbox'а\n * они не считаются, а высота приезжает переменными в том же патче, что и опции.\n */\n[data-gr-virtual]::before,\n[data-gr-virtual]::after {\n content: '';\n display: block;\n flex: none;\n}\n\n[data-gr-virtual]::before {\n height: var(--gr-virtual-before, 0px);\n}\n\n[data-gr-virtual]::after {\n height: var(--gr-virtual-after, 0px);\n}\n</style>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { n as grAutocompleteSafelist, t as grAutocompleteConfig } from "../../chunks/config-BbY2SKGE.js";
2
- import { t as GrAutocomplete_default } from "./chunks/GrAutocomplete-CQ9T7Lly.js";
2
+ import { t as GrAutocomplete_default } from "./chunks/GrAutocomplete-gY4Xopza.js";
3
3
  export { GrAutocomplete_default as GrAutocomplete, GrAutocomplete_default as default, grAutocompleteConfig, grAutocompleteSafelist };
@@ -1 +1 @@
1
- [data-gr-virtual][data-v-2a9c29c9]:before,[data-gr-virtual][data-v-2a9c29c9]:after{content:"";flex:none;display:block}[data-gr-virtual][data-v-2a9c29c9]:before{height:var(--gr-virtual-before,0px)}[data-gr-virtual][data-v-2a9c29c9]:after{height:var(--gr-virtual-after,0px)}
1
+ [data-gr-virtual][data-v-06fe398b]:before,[data-gr-virtual][data-v-06fe398b]:after{content:"";flex:none;display:block}[data-gr-virtual][data-v-06fe398b]:before{height:var(--gr-virtual-before,0px)}[data-gr-virtual][data-v-06fe398b]:after{height:var(--gr-virtual-after,0px)}
@@ -1,8 +1,9 @@
1
1
  import { a as useGrComponentProp, o as useGrComponentSize } from "../../../chunks/context-BSYR7RZs.js";
2
2
  import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
3
  import { t as GrLink_default } from "../../GrLink/chunks/GrLink-D-cgUMXW.js";
4
+ import { n as iconTag, t as iconClass } from "../../../chunks/icon-Dag2tIMs.js";
4
5
  import { a as breadcrumbsItemIconClass, c as breadcrumbsListNowrapClass, d as breadcrumbsSeparatorClass, f as breadcrumbsSizeClassBySize, i as breadcrumbsEllipsisClass, l as breadcrumbsListWrapClass, m as resolveBreadcrumbsLayout, o as breadcrumbsLabelClass, p as resolveBreadcrumbsFit, r as breadcrumbsCurrentClass, s as breadcrumbsListClass, u as breadcrumbsRootClass } from "../../../chunks/config-DKdlqXiY.js";
5
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, nextTick, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, toDisplayString, unref, watch, withCtx } from "vue";
6
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, nextTick, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, watch, withCtx } from "vue";
6
7
  //#region src/components/GrBreadcrumbs/GrBreadcrumbs.vue?vue&type=script&setup=true&lang.ts
7
8
  var _hoisted_1 = ["aria-label"];
8
9
  var _hoisted_2 = {
@@ -227,11 +228,11 @@ var GrBreadcrumbs_default = /* @__PURE__ */ defineComponent({
227
228
  item: entry.item,
228
229
  index: entry.index,
229
230
  isCurrent: isCurrent(entry.index)
230
- }, () => [entry.item.icon ? (openBlock(), createElementBlock("span", {
231
+ }, () => [entry.item.icon ? (openBlock(), createBlock(resolveDynamicComponent(unref(iconTag)(entry.item.icon)), {
231
232
  key: 0,
232
- class: normalizeClass([unref(breadcrumbsItemIconClass), entry.item.icon]),
233
+ class: normalizeClass([unref(breadcrumbsItemIconClass), unref(iconClass)(entry.item.icon)]),
233
234
  "aria-hidden": "true"
234
- }, null, 2)) : createCommentVNode("", true), createElementVNode("span", { class: normalizeClass(unref(breadcrumbsLabelClass)) }, toDisplayString(entry.item.label), 3)])]),
235
+ }, null, 8, ["class"])) : createCommentVNode("", true), createElementVNode("span", { class: normalizeClass(unref(breadcrumbsLabelClass)) }, toDisplayString(entry.item.label), 3)])]),
235
236
  _: 2
236
237
  }, 1032, [
237
238
  "as",
@@ -253,11 +254,11 @@ var GrBreadcrumbs_default = /* @__PURE__ */ defineComponent({
253
254
  item: entry.item,
254
255
  index: entry.index,
255
256
  isCurrent: isCurrent(entry.index)
256
- }, () => [entry.item.icon ? (openBlock(), createElementBlock("span", {
257
+ }, () => [entry.item.icon ? (openBlock(), createBlock(resolveDynamicComponent(unref(iconTag)(entry.item.icon)), {
257
258
  key: 0,
258
- class: normalizeClass([unref(breadcrumbsItemIconClass), entry.item.icon]),
259
+ class: normalizeClass([unref(breadcrumbsItemIconClass), unref(iconClass)(entry.item.icon)]),
259
260
  "aria-hidden": "true"
260
- }, null, 2)) : createCommentVNode("", true), createElementVNode("span", { class: normalizeClass(unref(breadcrumbsLabelClass)) }, toDisplayString(entry.item.label), 3)])], 10, _hoisted_5))]))], 64);
261
+ }, null, 8, ["class"])) : createCommentVNode("", true), createElementVNode("span", { class: normalizeClass(unref(breadcrumbsLabelClass)) }, toDisplayString(entry.item.label), 3)])], 10, _hoisted_5))]))], 64);
261
262
  }), 128))], 2)], 10, _hoisted_1);
262
263
  };
263
264
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrBreadcrumbs-DS_-PBVt.js","names":[],"sources":["../../../../src/components/GrBreadcrumbs/GrBreadcrumbs.vue","../../../../src/components/GrBreadcrumbs/GrBreadcrumbs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrLink from '../GrLink/GrLink.vue'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { iconClass, iconTag } from '../shared/icon'\n\nimport {\n breadcrumbsCurrentClass,\n breadcrumbsEllipsisClass,\n breadcrumbsItemIconClass,\n breadcrumbsLabelClass,\n breadcrumbsListClass,\n breadcrumbsListNowrapClass,\n breadcrumbsListWrapClass,\n breadcrumbsRootClass,\n breadcrumbsSeparatorClass,\n breadcrumbsSizeClassBySize,\n type GrBreadcrumbItem,\n type GrBreadcrumbsLinkComponent,\n type GrBreadcrumbsSize,\n resolveBreadcrumbsFit,\n resolveBreadcrumbsLayout,\n} from './grBreadcrumbsStyles'\n\nexport type { GrBreadcrumbItem, GrBreadcrumbsSize } from './grBreadcrumbsStyles'\n\n/**\n * Путь до текущей страницы (WAI-ARIA breadcrumb): `<nav>` с именем, внутри\n * упорядоченный список ссылок, последний пункт — текущая страница.\n *\n * Ссылки рисует `GrLink`, поэтому роутер подключается тем же способом, что и\n * везде в пакете: `as` + `to`.\n */\nexport interface GrBreadcrumbsProps {\n /** Путь от корня к текущей странице. Последний пункт — текущая страница. */\n items: GrBreadcrumbItem[]\n /** Компонент ссылки для всех пунктов: `RouterLink`, `NuxtLink`, `Link` Inertia. */\n as?: GrBreadcrumbsLinkComponent\n /** Разделитель между пунктами. Декоративен: диктору не читается. */\n separator?: string\n size?: GrBreadcrumbsSize\n /**\n * С какого числа пунктов схлопывать середину в «…». `0`/не задано — показывать\n * путь целиком.\n */\n maxItems?: number\n /** Сколько пунктов оставить в начале при схлопывании. */\n itemsBeforeCollapse?: number\n /** Сколько пунктов оставить в конце при схлопывании. */\n itemsAfterCollapse?: number\n /** Имя лендмарка. Не задано — берётся из локали. */\n ariaLabel?: string\n /** Последний пункт остаётся ссылкой (текущая страница кликабельна). */\n linkCurrent?: boolean\n /** i18n-метка кнопки раскрытия схлопнутого пути. */\n expandLabel?: string\n /**\n * Схлопывать по доступной ширине, а не по числу пунктов. Путь становится\n * однострочным, середина уходит под «…» ровно настолько, насколько не влезает.\n *\n * Без пропа поведение прежнее: список переносится на вторую строку, а порог\n * задаёт `maxItems`. Вместе пропы совместимы — `maxItems` остаётся жёстким\n * потолком, ширина ужимает дальше.\n */\n autoCollapse?: boolean\n}\n\nconst props = withDefaults(defineProps<GrBreadcrumbsProps>(), {\n as: undefined,\n separator: undefined,\n size: undefined,\n maxItems: undefined,\n itemsBeforeCollapse: 1,\n itemsAfterCollapse: 1,\n ariaLabel: undefined,\n linkCurrent: false,\n expandLabel: undefined,\n autoCollapse: false,\n})\n\ndefineSlots<{\n /** Содержимое пункта целиком (иконка и подпись). */\n item?: (props: { item: GrBreadcrumbItem, index: number, isCurrent: boolean }) => unknown\n /** Разделитель между пунктами. */\n separator?: () => unknown\n /** Кнопка раскрытия схлопнутой середины. */\n ellipsis?: (props: { hiddenCount: number, expand: () => void }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrBreadcrumbs' })\nconst resolvedSeparator = useGrComponentProp('GrBreadcrumbs', 'separator', () => props.separator, '/')\n\nconst resolvedAriaLabel = computed(() => props.ariaLabel ?? t('gr.breadcrumbs.label', 'Breadcrumb'))\nconst resolvedExpandLabel = computed(() => props.expandLabel ?? t('gr.breadcrumbs.expand', 'Show hidden path'))\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst listEl = ref<HTMLElement | null>(null)\nconst expanded = ref(false)\n\n/**\n * Схлопывание по ширине: сколько пунктов хвоста влезает.\n *\n * `undefined` — ещё не мерили, показываем путь целиком. Первый кадр в режиме\n * `autoCollapse` намеренно полный: ширины снимаются с него, а посчитать их до\n * рендера неоткуда.\n */\nconst fitAfter = ref<number | undefined>(undefined)\n\n/** Ширины не зависят от контейнера, поэтому живут до смены самого пути. */\nlet measured: { items: number[], separator: number } | null = null\n/**\n * Ширина кнопки «…» узнаётся только когда она отрисована, то есть уже после\n * первого схлопывания. До этого считаем её нулевой: поправка может лишь\n * ужать хвост ещё на пункт, а не вернуть спрятанное, поэтому качелей не будет.\n */\nlet ellipsisWidth = 0\n/** Ширина, на которой считали в прошлый раз: по её смене раскрытый путь сворачивается. */\nlet lastAvailable = 0\n\n// Новый путь — новая страница: раскрытая середина прошлого пути к ней отношения\n// не имеет, а измеренные ширины относятся к прошлым подписям.\nwatch(() => props.items, () => {\n expanded.value = false\n measured = null\n ellipsisWidth = 0\n fitAfter.value = undefined\n void nextTick(measure)\n})\n\nconst lastIndex = computed(() => props.items.length - 1)\n\n/**\n * Схлопывание по ширине выражается через тот же `maxItems`: раскладку считает\n * одна функция, а режимы отличаются только тем, откуда взялось число.\n */\nconst autoMaxItems = computed(() => {\n if (!props.autoCollapse || fitAfter.value === undefined) return undefined\n if (fitAfter.value >= props.items.length) return undefined\n\n return props.items.length - 1\n})\n\nconst effectiveMaxItems = computed(() => {\n const limits = [props.maxItems, autoMaxItems.value].filter((value): value is number => typeof value === 'number' && value > 0)\n\n return limits.length > 0 ? Math.min(...limits) : undefined\n})\n\nconst effectiveAfter = computed(() => (\n props.autoCollapse && fitAfter.value !== undefined ? fitAfter.value : props.itemsAfterCollapse\n))\n\nconst entries = computed(() => resolveBreadcrumbsLayout({\n items: props.items,\n maxItems: effectiveMaxItems.value,\n itemsBeforeCollapse: props.itemsBeforeCollapse,\n itemsAfterCollapse: effectiveAfter.value,\n expanded: expanded.value,\n}))\n\nfunction isCurrent(index: number): boolean {\n return index === lastIndex.value\n}\n\n/** Ссылкой пункт становится, только если ведёт куда-то и не выключен. */\nfunction isLink(item: GrBreadcrumbItem, index: number): boolean {\n if (item.disabled) return false\n if (isCurrent(index) && !props.linkCurrent) return false\n return item.href !== undefined || item.to !== undefined || props.as !== undefined\n}\n\nfunction widthOf(el: Element | null | undefined): number {\n // `scrollWidth`, а не прямоугольник: у подписи стоит `truncate`, и в узком\n // контейнере прямоугольник вернул бы уже ужатую ширину, а не собственную.\n return el ? (el as HTMLElement).scrollWidth : 0\n}\n\n/**\n * Снять ширины с полного пути и пересчитать, сколько пунктов хвоста влезает.\n *\n * Замер делается один раз на путь: подписи от ширины контейнера не зависят,\n * поэтому `resize` пересчитывает только арифметику.\n */\nfunction measure(): void {\n if (!props.autoCollapse) return\n\n const list = listEl.value\n if (!list) return\n\n const ellipsis = list.querySelector('[data-gr-breadcrumbs-ellipsis-item]')\n if (ellipsis) ellipsisWidth = widthOf(ellipsis)\n\n if (!measured) {\n // Ширины снимаем только с полного пути: спрятанных пунктов в DOM нет.\n if (fitAfter.value !== undefined) return\n\n const wraps = [...list.querySelectorAll('[data-gr-breadcrumbs-item-wrap]')]\n if (wraps.length !== props.items.length) return\n\n measured = {\n items: wraps.map(widthOf),\n separator: widthOf(list.querySelector('[data-gr-breadcrumbs-separator]')),\n }\n }\n\n const available = list.clientWidth\n // jsdom и скрытый контейнер отдают 0 — решать по такой ширине нечего.\n if (available <= 0) return\n\n lastAvailable = available\n\n fitAfter.value = resolveBreadcrumbsFit({\n itemWidths: measured.items,\n separatorWidth: measured.separator,\n ellipsisWidth,\n available,\n itemsBeforeCollapse: props.itemsBeforeCollapse,\n gapWidth: listGap(list),\n })\n}\n\n/** Зазор списка в пикселях: он задан классом, а не пропом, поэтому спрашиваем стиль. */\nfunction listGap(list: HTMLElement): number {\n if (typeof getComputedStyle !== 'function') return 0\n\n return Number.parseFloat(getComputedStyle(list).columnGap) || 0\n}\n\n/**\n * Ширина кнопки «…» известна только после того, как она отрисована, то есть\n * после первого схлопывания. Поэтому решение пересчитывается ещё раз: поправка\n * либо ничего не меняет, либо ужимает хвост на пункт. Цикла нет — второй проход\n * оставляет `fitAfter` прежним, и наблюдатель больше не срабатывает.\n */\nwatch(fitAfter, () => void nextTick(measure))\n\nlet observer: ResizeObserver | null = null\n\nwatch(() => props.autoCollapse, (enabled) => {\n if (enabled) {\n void nextTick(measure)\n return\n }\n\n measured = null\n ellipsisWidth = 0\n fitAfter.value = undefined\n})\n\nonMounted(() => {\n void nextTick(measure)\n\n // На сервере и в jsdom `ResizeObserver` отсутствует — измерять там нечего.\n if (typeof ResizeObserver === 'undefined') return\n\n observer = new ResizeObserver(() => {\n // Смена ширины — новая раскладка, и раскрытая руками середина относилась к\n // прежней. Свернуть её иначе нечем: в раскрытом пути кнопки «…» нет, то есть\n // поворот телефона оставлял бы путь развёрнутым навсегда. Высота меняется от\n // самого раскрытия (путь переносится на вторую строку) — на неё не реагируем.\n if (expanded.value && listEl.value && listEl.value.clientWidth !== lastAvailable)\n expanded.value = false\n\n measure()\n })\n if (listEl.value) observer.observe(listEl.value)\n})\n\nonBeforeUnmount(() => observer?.disconnect())\n\nasync function expand(): Promise<void> {\n expanded.value = true\n\n // Кнопка исчезает вместе со схлопыванием, и фокус ушёл бы на `<body>`.\n // Переводим его на первый раскрытый пункт — там, где кнопка и стояла. Пункты\n // без ссылки держат `tabindex=\"-1\"` именно ради этого случая.\n await nextTick()\n const before = Math.max(0, Math.min(props.itemsBeforeCollapse, props.items.length))\n const revealed = rootEl.value?.querySelectorAll<HTMLElement>('[data-gr-breadcrumbs-item]')\n revealed?.[before]?.focus()\n}\n</script>\n\n<template>\n <nav\n ref=\"rootEl\"\n data-gr-breadcrumbs\n :class=\"[breadcrumbsRootClass, breadcrumbsSizeClassBySize[resolvedSize]]\"\n :aria-label=\"resolvedAriaLabel\"\n >\n <ol\n ref=\"listEl\"\n data-gr-breadcrumbs-list\n :class=\"[breadcrumbsListClass, autoCollapse && !expanded ? breadcrumbsListNowrapClass : breadcrumbsListWrapClass]\"\n >\n <template v-for=\"(entry, position) in entries\" :key=\"entry.kind === 'item' ? `item-${entry.index}` : 'ellipsis'\">\n <!--\n Разделитель — отдельный элемент списка и помечен `aria-hidden`:\n структуру пути диктору сообщает сам список, а «/» он бы читал вслух.\n -->\n <li\n v-if=\"position > 0\"\n data-gr-breadcrumbs-separator\n :class=\"breadcrumbsSeparatorClass\"\n aria-hidden=\"true\"\n >\n <slot name=\"separator\">\n {{ resolvedSeparator }}\n </slot>\n </li>\n\n <li v-if=\"entry.kind === 'ellipsis'\" data-gr-breadcrumbs-ellipsis-item class=\"min-w-0\">\n <slot name=\"ellipsis\" :hidden-count=\"entry.hiddenCount\" :expand=\"expand\">\n <button\n type=\"button\"\n data-gr-breadcrumbs-ellipsis\n data-testid=\"gr-breadcrumbs-ellipsis\"\n :class=\"breadcrumbsEllipsisClass\"\n :aria-label=\"resolvedExpandLabel\"\n @click=\"expand\"\n >\n …\n </button>\n </slot>\n </li>\n\n <li v-else data-gr-breadcrumbs-item-wrap class=\"min-w-0\">\n <GrLink\n v-if=\"isLink(entry.item, entry.index)\"\n :key=\"`link-${entry.index}`\"\n data-gr-breadcrumbs-item\n :as=\"as\"\n :href=\"entry.item.href\"\n :to=\"entry.item.to\"\n :size=\"resolvedSize\"\n :variant=\"isCurrent(entry.index) ? 'default' : 'muted'\"\n :aria-label=\"entry.item.ariaLabel\"\n :aria-current=\"isCurrent(entry.index) ? 'page' : undefined\"\n >\n <slot name=\"item\" :item=\"entry.item\" :index=\"entry.index\" :is-current=\"isCurrent(entry.index)\">\n <component\n :is=\"iconTag(entry.item.icon)\"\n v-if=\"entry.item.icon\"\n :class=\"[breadcrumbsItemIconClass, iconClass(entry.item.icon)]\"\n aria-hidden=\"true\"\n />\n <span :class=\"breadcrumbsLabelClass\">{{ entry.item.label }}</span>\n </slot>\n </GrLink>\n\n <!--\n Текущая страница и выключенный пункт — не ссылки. `aria-current`\n остаётся на текущем: именно он отвечает на вопрос «где я».\n -->\n <span\n v-else\n data-gr-breadcrumbs-item\n :class=\"breadcrumbsCurrentClass\"\n :aria-label=\"entry.item.ariaLabel\"\n :aria-current=\"isCurrent(entry.index) ? 'page' : undefined\"\n :aria-disabled=\"entry.item.disabled ? 'true' : undefined\"\n tabindex=\"-1\"\n >\n <slot name=\"item\" :item=\"entry.item\" :index=\"entry.index\" :is-current=\"isCurrent(entry.index)\">\n <component\n :is=\"iconTag(entry.item.icon)\"\n v-if=\"entry.item.icon\"\n :class=\"[breadcrumbsItemIconClass, iconClass(entry.item.icon)]\"\n aria-hidden=\"true\"\n />\n <span :class=\"breadcrumbsLabelClass\">{{ entry.item.label }}</span>\n </slot>\n </span>\n </li>\n </template>\n </ol>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrLink from '../GrLink/GrLink.vue'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { iconClass, iconTag } from '../shared/icon'\n\nimport {\n breadcrumbsCurrentClass,\n breadcrumbsEllipsisClass,\n breadcrumbsItemIconClass,\n breadcrumbsLabelClass,\n breadcrumbsListClass,\n breadcrumbsListNowrapClass,\n breadcrumbsListWrapClass,\n breadcrumbsRootClass,\n breadcrumbsSeparatorClass,\n breadcrumbsSizeClassBySize,\n type GrBreadcrumbItem,\n type GrBreadcrumbsLinkComponent,\n type GrBreadcrumbsSize,\n resolveBreadcrumbsFit,\n resolveBreadcrumbsLayout,\n} from './grBreadcrumbsStyles'\n\nexport type { GrBreadcrumbItem, GrBreadcrumbsSize } from './grBreadcrumbsStyles'\n\n/**\n * Путь до текущей страницы (WAI-ARIA breadcrumb): `<nav>` с именем, внутри\n * упорядоченный список ссылок, последний пункт — текущая страница.\n *\n * Ссылки рисует `GrLink`, поэтому роутер подключается тем же способом, что и\n * везде в пакете: `as` + `to`.\n */\nexport interface GrBreadcrumbsProps {\n /** Путь от корня к текущей странице. Последний пункт — текущая страница. */\n items: GrBreadcrumbItem[]\n /** Компонент ссылки для всех пунктов: `RouterLink`, `NuxtLink`, `Link` Inertia. */\n as?: GrBreadcrumbsLinkComponent\n /** Разделитель между пунктами. Декоративен: диктору не читается. */\n separator?: string\n size?: GrBreadcrumbsSize\n /**\n * С какого числа пунктов схлопывать середину в «…». `0`/не задано — показывать\n * путь целиком.\n */\n maxItems?: number\n /** Сколько пунктов оставить в начале при схлопывании. */\n itemsBeforeCollapse?: number\n /** Сколько пунктов оставить в конце при схлопывании. */\n itemsAfterCollapse?: number\n /** Имя лендмарка. Не задано — берётся из локали. */\n ariaLabel?: string\n /** Последний пункт остаётся ссылкой (текущая страница кликабельна). */\n linkCurrent?: boolean\n /** i18n-метка кнопки раскрытия схлопнутого пути. */\n expandLabel?: string\n /**\n * Схлопывать по доступной ширине, а не по числу пунктов. Путь становится\n * однострочным, середина уходит под «…» ровно настолько, насколько не влезает.\n *\n * Без пропа поведение прежнее: список переносится на вторую строку, а порог\n * задаёт `maxItems`. Вместе пропы совместимы — `maxItems` остаётся жёстким\n * потолком, ширина ужимает дальше.\n */\n autoCollapse?: boolean\n}\n\nconst props = withDefaults(defineProps<GrBreadcrumbsProps>(), {\n as: undefined,\n separator: undefined,\n size: undefined,\n maxItems: undefined,\n itemsBeforeCollapse: 1,\n itemsAfterCollapse: 1,\n ariaLabel: undefined,\n linkCurrent: false,\n expandLabel: undefined,\n autoCollapse: false,\n})\n\ndefineSlots<{\n /** Содержимое пункта целиком (иконка и подпись). */\n item?: (props: { item: GrBreadcrumbItem, index: number, isCurrent: boolean }) => unknown\n /** Разделитель между пунктами. */\n separator?: () => unknown\n /** Кнопка раскрытия схлопнутой середины. */\n ellipsis?: (props: { hiddenCount: number, expand: () => void }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrBreadcrumbs' })\nconst resolvedSeparator = useGrComponentProp('GrBreadcrumbs', 'separator', () => props.separator, '/')\n\nconst resolvedAriaLabel = computed(() => props.ariaLabel ?? t('gr.breadcrumbs.label', 'Breadcrumb'))\nconst resolvedExpandLabel = computed(() => props.expandLabel ?? t('gr.breadcrumbs.expand', 'Show hidden path'))\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst listEl = ref<HTMLElement | null>(null)\nconst expanded = ref(false)\n\n/**\n * Схлопывание по ширине: сколько пунктов хвоста влезает.\n *\n * `undefined` — ещё не мерили, показываем путь целиком. Первый кадр в режиме\n * `autoCollapse` намеренно полный: ширины снимаются с него, а посчитать их до\n * рендера неоткуда.\n */\nconst fitAfter = ref<number | undefined>(undefined)\n\n/** Ширины не зависят от контейнера, поэтому живут до смены самого пути. */\nlet measured: { items: number[], separator: number } | null = null\n/**\n * Ширина кнопки «…» узнаётся только когда она отрисована, то есть уже после\n * первого схлопывания. До этого считаем её нулевой: поправка может лишь\n * ужать хвост ещё на пункт, а не вернуть спрятанное, поэтому качелей не будет.\n */\nlet ellipsisWidth = 0\n/** Ширина, на которой считали в прошлый раз: по её смене раскрытый путь сворачивается. */\nlet lastAvailable = 0\n\n// Новый путь — новая страница: раскрытая середина прошлого пути к ней отношения\n// не имеет, а измеренные ширины относятся к прошлым подписям.\nwatch(() => props.items, () => {\n expanded.value = false\n measured = null\n ellipsisWidth = 0\n fitAfter.value = undefined\n void nextTick(measure)\n})\n\nconst lastIndex = computed(() => props.items.length - 1)\n\n/**\n * Схлопывание по ширине выражается через тот же `maxItems`: раскладку считает\n * одна функция, а режимы отличаются только тем, откуда взялось число.\n */\nconst autoMaxItems = computed(() => {\n if (!props.autoCollapse || fitAfter.value === undefined) return undefined\n if (fitAfter.value >= props.items.length) return undefined\n\n return props.items.length - 1\n})\n\nconst effectiveMaxItems = computed(() => {\n const limits = [props.maxItems, autoMaxItems.value].filter((value): value is number => typeof value === 'number' && value > 0)\n\n return limits.length > 0 ? Math.min(...limits) : undefined\n})\n\nconst effectiveAfter = computed(() => (\n props.autoCollapse && fitAfter.value !== undefined ? fitAfter.value : props.itemsAfterCollapse\n))\n\nconst entries = computed(() => resolveBreadcrumbsLayout({\n items: props.items,\n maxItems: effectiveMaxItems.value,\n itemsBeforeCollapse: props.itemsBeforeCollapse,\n itemsAfterCollapse: effectiveAfter.value,\n expanded: expanded.value,\n}))\n\nfunction isCurrent(index: number): boolean {\n return index === lastIndex.value\n}\n\n/** Ссылкой пункт становится, только если ведёт куда-то и не выключен. */\nfunction isLink(item: GrBreadcrumbItem, index: number): boolean {\n if (item.disabled) return false\n if (isCurrent(index) && !props.linkCurrent) return false\n return item.href !== undefined || item.to !== undefined || props.as !== undefined\n}\n\nfunction widthOf(el: Element | null | undefined): number {\n // `scrollWidth`, а не прямоугольник: у подписи стоит `truncate`, и в узком\n // контейнере прямоугольник вернул бы уже ужатую ширину, а не собственную.\n return el ? (el as HTMLElement).scrollWidth : 0\n}\n\n/**\n * Снять ширины с полного пути и пересчитать, сколько пунктов хвоста влезает.\n *\n * Замер делается один раз на путь: подписи от ширины контейнера не зависят,\n * поэтому `resize` пересчитывает только арифметику.\n */\nfunction measure(): void {\n if (!props.autoCollapse) return\n\n const list = listEl.value\n if (!list) return\n\n const ellipsis = list.querySelector('[data-gr-breadcrumbs-ellipsis-item]')\n if (ellipsis) ellipsisWidth = widthOf(ellipsis)\n\n if (!measured) {\n // Ширины снимаем только с полного пути: спрятанных пунктов в DOM нет.\n if (fitAfter.value !== undefined) return\n\n const wraps = [...list.querySelectorAll('[data-gr-breadcrumbs-item-wrap]')]\n if (wraps.length !== props.items.length) return\n\n measured = {\n items: wraps.map(widthOf),\n separator: widthOf(list.querySelector('[data-gr-breadcrumbs-separator]')),\n }\n }\n\n const available = list.clientWidth\n // jsdom и скрытый контейнер отдают 0 — решать по такой ширине нечего.\n if (available <= 0) return\n\n lastAvailable = available\n\n fitAfter.value = resolveBreadcrumbsFit({\n itemWidths: measured.items,\n separatorWidth: measured.separator,\n ellipsisWidth,\n available,\n itemsBeforeCollapse: props.itemsBeforeCollapse,\n gapWidth: listGap(list),\n })\n}\n\n/** Зазор списка в пикселях: он задан классом, а не пропом, поэтому спрашиваем стиль. */\nfunction listGap(list: HTMLElement): number {\n if (typeof getComputedStyle !== 'function') return 0\n\n return Number.parseFloat(getComputedStyle(list).columnGap) || 0\n}\n\n/**\n * Ширина кнопки «…» известна только после того, как она отрисована, то есть\n * после первого схлопывания. Поэтому решение пересчитывается ещё раз: поправка\n * либо ничего не меняет, либо ужимает хвост на пункт. Цикла нет — второй проход\n * оставляет `fitAfter` прежним, и наблюдатель больше не срабатывает.\n */\nwatch(fitAfter, () => void nextTick(measure))\n\nlet observer: ResizeObserver | null = null\n\nwatch(() => props.autoCollapse, (enabled) => {\n if (enabled) {\n void nextTick(measure)\n return\n }\n\n measured = null\n ellipsisWidth = 0\n fitAfter.value = undefined\n})\n\nonMounted(() => {\n void nextTick(measure)\n\n // На сервере и в jsdom `ResizeObserver` отсутствует — измерять там нечего.\n if (typeof ResizeObserver === 'undefined') return\n\n observer = new ResizeObserver(() => {\n // Смена ширины — новая раскладка, и раскрытая руками середина относилась к\n // прежней. Свернуть её иначе нечем: в раскрытом пути кнопки «…» нет, то есть\n // поворот телефона оставлял бы путь развёрнутым навсегда. Высота меняется от\n // самого раскрытия (путь переносится на вторую строку) — на неё не реагируем.\n if (expanded.value && listEl.value && listEl.value.clientWidth !== lastAvailable)\n expanded.value = false\n\n measure()\n })\n if (listEl.value) observer.observe(listEl.value)\n})\n\nonBeforeUnmount(() => observer?.disconnect())\n\nasync function expand(): Promise<void> {\n expanded.value = true\n\n // Кнопка исчезает вместе со схлопыванием, и фокус ушёл бы на `<body>`.\n // Переводим его на первый раскрытый пункт — там, где кнопка и стояла. Пункты\n // без ссылки держат `tabindex=\"-1\"` именно ради этого случая.\n await nextTick()\n const before = Math.max(0, Math.min(props.itemsBeforeCollapse, props.items.length))\n const revealed = rootEl.value?.querySelectorAll<HTMLElement>('[data-gr-breadcrumbs-item]')\n revealed?.[before]?.focus()\n}\n</script>\n\n<template>\n <nav\n ref=\"rootEl\"\n data-gr-breadcrumbs\n :class=\"[breadcrumbsRootClass, breadcrumbsSizeClassBySize[resolvedSize]]\"\n :aria-label=\"resolvedAriaLabel\"\n >\n <ol\n ref=\"listEl\"\n data-gr-breadcrumbs-list\n :class=\"[breadcrumbsListClass, autoCollapse && !expanded ? breadcrumbsListNowrapClass : breadcrumbsListWrapClass]\"\n >\n <template v-for=\"(entry, position) in entries\" :key=\"entry.kind === 'item' ? `item-${entry.index}` : 'ellipsis'\">\n <!--\n Разделитель — отдельный элемент списка и помечен `aria-hidden`:\n структуру пути диктору сообщает сам список, а «/» он бы читал вслух.\n -->\n <li\n v-if=\"position > 0\"\n data-gr-breadcrumbs-separator\n :class=\"breadcrumbsSeparatorClass\"\n aria-hidden=\"true\"\n >\n <slot name=\"separator\">\n {{ resolvedSeparator }}\n </slot>\n </li>\n\n <li v-if=\"entry.kind === 'ellipsis'\" data-gr-breadcrumbs-ellipsis-item class=\"min-w-0\">\n <slot name=\"ellipsis\" :hidden-count=\"entry.hiddenCount\" :expand=\"expand\">\n <button\n type=\"button\"\n data-gr-breadcrumbs-ellipsis\n data-testid=\"gr-breadcrumbs-ellipsis\"\n :class=\"breadcrumbsEllipsisClass\"\n :aria-label=\"resolvedExpandLabel\"\n @click=\"expand\"\n >\n …\n </button>\n </slot>\n </li>\n\n <li v-else data-gr-breadcrumbs-item-wrap class=\"min-w-0\">\n <GrLink\n v-if=\"isLink(entry.item, entry.index)\"\n :key=\"`link-${entry.index}`\"\n data-gr-breadcrumbs-item\n :as=\"as\"\n :href=\"entry.item.href\"\n :to=\"entry.item.to\"\n :size=\"resolvedSize\"\n :variant=\"isCurrent(entry.index) ? 'default' : 'muted'\"\n :aria-label=\"entry.item.ariaLabel\"\n :aria-current=\"isCurrent(entry.index) ? 'page' : undefined\"\n >\n <slot name=\"item\" :item=\"entry.item\" :index=\"entry.index\" :is-current=\"isCurrent(entry.index)\">\n <component\n :is=\"iconTag(entry.item.icon)\"\n v-if=\"entry.item.icon\"\n :class=\"[breadcrumbsItemIconClass, iconClass(entry.item.icon)]\"\n aria-hidden=\"true\"\n />\n <span :class=\"breadcrumbsLabelClass\">{{ entry.item.label }}</span>\n </slot>\n </GrLink>\n\n <!--\n Текущая страница и выключенный пункт — не ссылки. `aria-current`\n остаётся на текущем: именно он отвечает на вопрос «где я».\n -->\n <span\n v-else\n data-gr-breadcrumbs-item\n :class=\"breadcrumbsCurrentClass\"\n :aria-label=\"entry.item.ariaLabel\"\n :aria-current=\"isCurrent(entry.index) ? 'page' : undefined\"\n :aria-disabled=\"entry.item.disabled ? 'true' : undefined\"\n tabindex=\"-1\"\n >\n <slot name=\"item\" :item=\"entry.item\" :index=\"entry.index\" :is-current=\"isCurrent(entry.index)\">\n <component\n :is=\"iconTag(entry.item.icon)\"\n v-if=\"entry.item.icon\"\n :class=\"[breadcrumbsItemIconClass, iconClass(entry.item.icon)]\"\n aria-hidden=\"true\"\n />\n <span :class=\"breadcrumbsLabelClass\">{{ entry.item.label }}</span>\n </slot>\n </span>\n </li>\n </template>\n </ol>\n </nav>\n</template>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { m as resolveBreadcrumbsLayout, n as grBreadcrumbsSafelist, p as resolveBreadcrumbsFit, t as grBreadcrumbsConfig } from "../../chunks/config-DKdlqXiY.js";
2
- import { t as GrBreadcrumbs_default } from "./chunks/GrBreadcrumbs-BCNGDS6k.js";
2
+ import { t as GrBreadcrumbs_default } from "./chunks/GrBreadcrumbs-DS_-PBVt.js";
3
3
  export { GrBreadcrumbs_default as GrBreadcrumbs, GrBreadcrumbs_default as default, grBreadcrumbsConfig, grBreadcrumbsSafelist, resolveBreadcrumbsFit, resolveBreadcrumbsLayout };