@feugene/granularity 0.36.0 → 0.37.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 (363) hide show
  1. package/dist/chunks/config-2ZHRsdOc.js.map +1 -1
  2. package/dist/chunks/config-2vrOFn2f.js.map +1 -1
  3. package/dist/chunks/config-3bQeQvqd.js.map +1 -1
  4. package/dist/chunks/config-87fszqnD.js.map +1 -1
  5. package/dist/chunks/config-8Pcutu1j.js.map +1 -1
  6. package/dist/chunks/config-A-bBiotF.js.map +1 -1
  7. package/dist/chunks/config-B0uvcTPH.js.map +1 -1
  8. package/dist/chunks/config-B3J0alyY.js.map +1 -1
  9. package/dist/chunks/config-B5SBUa6u.js.map +1 -1
  10. package/dist/chunks/config-B7W9VlZ7.js.map +1 -1
  11. package/dist/chunks/config-BDSaD-5p.js.map +1 -1
  12. package/dist/chunks/config-BEO7brpO.js.map +1 -1
  13. package/dist/chunks/config-BNu8lFQB.js.map +1 -1
  14. package/dist/chunks/config-BRESvs9g.js.map +1 -1
  15. package/dist/chunks/config-BTmfQFwi.js.map +1 -1
  16. package/dist/chunks/{config-CuQmUkMw2.js → config-BUNxHnEz.js} +1 -1
  17. package/dist/chunks/config-BUNxHnEz.js.map +1 -0
  18. package/dist/chunks/config-BaWANzzB.js.map +1 -1
  19. package/dist/chunks/config-BcXX7S7B.js.map +1 -1
  20. package/dist/chunks/config-Bd7QfiGj.js.map +1 -1
  21. package/dist/chunks/config-BhfNgyWL.js.map +1 -1
  22. package/dist/chunks/config-Bjn30smH.js.map +1 -1
  23. package/dist/chunks/config-BlJa1Mj9.js.map +1 -1
  24. package/dist/chunks/config-BoUI0t-t.js.map +1 -1
  25. package/dist/chunks/config-BqT6PMzO.js.map +1 -1
  26. package/dist/chunks/config-BscVxZR3.js.map +1 -1
  27. package/dist/chunks/config-BuGlNPE9.js.map +1 -1
  28. package/dist/chunks/config-C0Ea9ePP.js.map +1 -1
  29. package/dist/chunks/config-C0Eu8jpG.js.map +1 -1
  30. package/dist/chunks/config-CDat1wXf.js.map +1 -1
  31. package/dist/chunks/config-CIkdsWUk.js.map +1 -1
  32. package/dist/chunks/config-CJzmDHfI.js.map +1 -1
  33. package/dist/chunks/config-CLjjjPZj.js.map +1 -1
  34. package/dist/chunks/config-CMNfZO7S.js.map +1 -1
  35. package/dist/chunks/config-CQLNjc9X.js.map +1 -0
  36. package/dist/chunks/config-CT4lP6S4.js.map +1 -1
  37. package/dist/chunks/config-CWtfxkM9.js.map +1 -1
  38. package/dist/chunks/config-CYthTVK2.js.map +1 -1
  39. package/dist/chunks/config-CZPDPJlg.js.map +1 -1
  40. package/dist/chunks/config-CbEbLyDE.js.map +1 -1
  41. package/dist/chunks/config-ChPg-BQv.js.map +1 -1
  42. package/dist/chunks/config-CjKpUcmy.js.map +1 -1
  43. package/dist/chunks/config-CjWOTgLL.js.map +1 -1
  44. package/dist/chunks/config-CjXjhphs.js.map +1 -1
  45. package/dist/chunks/config-CnYdE3HZ.js.map +1 -0
  46. package/dist/chunks/config-CsCUuukB.js.map +1 -1
  47. package/dist/chunks/config-CvbVM4ep.js.map +1 -1
  48. package/dist/chunks/config-D0NzGYaE.js.map +1 -1
  49. package/dist/chunks/config-D0W8mGFD.js.map +1 -1
  50. package/dist/chunks/config-D82r782v.js.map +1 -1
  51. package/dist/chunks/config-DBi0GVce.js.map +1 -1
  52. package/dist/chunks/config-DBmdgoVf.js.map +1 -0
  53. package/dist/chunks/config-DCq4JySZ.js.map +1 -1
  54. package/dist/chunks/config-DGC00bTp.js.map +1 -1
  55. package/dist/chunks/config-DIDz_E5m.js.map +1 -1
  56. package/dist/chunks/config-DM0lsduD.js.map +1 -1
  57. package/dist/chunks/config-DNDgp9bB.js.map +1 -1
  58. package/dist/chunks/config-DOYz_SVK.js.map +1 -1
  59. package/dist/chunks/config-DRbojEzO.js.map +1 -1
  60. package/dist/chunks/config-DUNH02Fg.js.map +1 -1
  61. package/dist/chunks/config-DUbbGcfw.js.map +1 -1
  62. package/dist/chunks/config-DVCNvhIG.js.map +1 -0
  63. package/dist/chunks/config-DWL81-aM.js.map +1 -1
  64. package/dist/chunks/config-DZCUPHPU.js.map +1 -1
  65. package/dist/chunks/config-DeyT9dJl.js.map +1 -1
  66. package/dist/chunks/config-DolULhjJ.js.map +1 -1
  67. package/dist/chunks/config-DpAYidyY.js.map +1 -0
  68. package/dist/chunks/config-Droqn55j.js.map +1 -1
  69. package/dist/chunks/config-DwABJHEo.js.map +1 -1
  70. package/dist/chunks/config-E1cTyRVV.js.map +1 -1
  71. package/dist/chunks/config-MmPym-Z7.js.map +1 -0
  72. package/dist/chunks/config-QaIc_gRa.js.map +1 -1
  73. package/dist/chunks/config-Yy67uWHY.js.map +1 -1
  74. package/dist/chunks/config-Z5LeUXys.js.map +1 -1
  75. package/dist/chunks/config-_jbgsFpw.js.map +1 -1
  76. package/dist/chunks/config-eXB87DOi.js.map +1 -1
  77. package/dist/chunks/config-rLkc9Zn2.js.map +1 -1
  78. package/dist/chunks/{context-CTbuwcYs.js → context-CPMRfwVr.js} +1 -1
  79. package/dist/chunks/{context-CTbuwcYs.js.map → context-CPMRfwVr.js.map} +1 -1
  80. package/dist/chunks/{grRadioStyles-DGL2QLy6.js → grRadioStyles-BwRyCjQQ.js} +4 -2
  81. package/dist/chunks/{grRadioStyles-DGL2QLy6.js.map → grRadioStyles-BwRyCjQQ.js.map} +1 -1
  82. package/dist/chunks/{granular-provider-Bp_BCBnr.js → granular-provider-DP2gk0oa.js} +9 -8
  83. package/dist/chunks/{granular-provider-Bp_BCBnr.js.map → granular-provider-DP2gk0oa.js.map} +1 -1
  84. package/dist/chunks/{overlayStack-0CZS99Da.js → overlayStack-B_UFaFzk.js} +65 -24
  85. package/dist/chunks/overlayStack-B_UFaFzk.js.map +1 -0
  86. package/dist/chunks/{renamedProp-BwcN-SJg.js → renamedProp-Dy96tT-F.js} +1 -1
  87. package/dist/chunks/{renamedProp-BwcN-SJg.js.map → renamedProp-Dy96tT-F.js.map} +1 -1
  88. package/dist/chunks/{useModalOverlay-BnAGZpcI.js → useModalOverlay-BwX8WEcI.js} +3 -3
  89. package/dist/chunks/{useModalOverlay-BnAGZpcI.js.map → useModalOverlay-BwX8WEcI.js.map} +1 -1
  90. package/dist/chunks/{usePortalTarget-C0p-mhaM.js → usePortalTarget-BhrRgdqW.js} +1 -1
  91. package/dist/chunks/{usePortalTarget-C0p-mhaM.js.map → usePortalTarget-BhrRgdqW.js.map} +1 -1
  92. package/dist/chunks/{validation-5HhpanPP.js → validation-BDjqsuGB.js} +1 -1
  93. package/dist/chunks/{validation-5HhpanPP.js.map → validation-BDjqsuGB.js.map} +1 -1
  94. package/dist/components/GrAlert/chunks/{GrAlert-FAVf-ksH.js → GrAlert-D72y1IEh.js} +1 -1
  95. package/dist/components/GrAlert/chunks/{GrAlert-FAVf-ksH.js.map → GrAlert-D72y1IEh.js.map} +1 -1
  96. package/dist/components/GrAlert/index.js +1 -1
  97. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-BQsSE7sj.js → GrAutocomplete-BpmFmpKs.js} +4 -6
  98. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-BQsSE7sj.js.map → GrAutocomplete-BpmFmpKs.js.map} +1 -1
  99. package/dist/components/GrAutocomplete/index.js +1 -1
  100. package/dist/components/GrAvatar/chunks/{GrAvatar-CNmLmgcr.js → GrAvatar-dWEXiCJG.js} +1 -1
  101. package/dist/components/GrAvatar/chunks/{GrAvatar-CNmLmgcr.js.map → GrAvatar-dWEXiCJG.js.map} +1 -1
  102. package/dist/components/GrAvatar/index.js +1 -1
  103. package/dist/components/GrBadge/chunks/{GrBadge-DpUDXKbq.js → GrBadge-DBeQXEir.js} +2 -2
  104. package/dist/components/GrBadge/chunks/{GrBadge-DpUDXKbq.js.map → GrBadge-DBeQXEir.js.map} +1 -1
  105. package/dist/components/GrBadge/index.js +1 -1
  106. package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-B-A3PRkE.js → GrBadgeWrap-wdfxaXiu.js} +3 -1
  107. package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-B-A3PRkE.js.map → GrBadgeWrap-wdfxaXiu.js.map} +1 -1
  108. package/dist/components/GrBadgeWrap/index.js +1 -1
  109. package/dist/components/GrBottomNav/chunks/{GrBottomNav-wtFatLjB.js → GrBottomNav-nE08pVUJ.js} +1 -1
  110. package/dist/components/GrBottomNav/chunks/{GrBottomNav-wtFatLjB.js.map → GrBottomNav-nE08pVUJ.js.map} +1 -1
  111. package/dist/components/GrBottomNav/index.js +1 -1
  112. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-DRDNan_n.js → GrBreadcrumbs-DOpwY2EF.js} +3 -3
  113. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-DRDNan_n.js.map → GrBreadcrumbs-DOpwY2EF.js.map} +1 -1
  114. package/dist/components/GrBreadcrumbs/index.js +1 -1
  115. package/dist/components/GrButton/chunks/{GrButton-7GP0zObj.js → GrButton-CS4TqMtG.js} +1 -1
  116. package/dist/components/GrButton/chunks/{GrButton-7GP0zObj.js.map → GrButton-CS4TqMtG.js.map} +1 -1
  117. package/dist/components/GrButton/index.js +1 -1
  118. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-CKyiAV8K.js.map +1 -1
  119. package/dist/components/GrButtonGroup/index.js +1 -1
  120. package/dist/components/GrCard/chunks/{GrCard-BttXz1pQ.js → GrCard-Ck5--lMF.js} +2 -2
  121. package/dist/components/GrCard/chunks/{GrCard-BttXz1pQ.js.map → GrCard-Ck5--lMF.js.map} +1 -1
  122. package/dist/components/GrCard/index.js +1 -1
  123. package/dist/components/GrCheckbox/chunks/{GrCheckbox-CEcHIsXM.js → GrCheckbox-Dp201frb.js} +1 -1
  124. package/dist/components/GrCheckbox/chunks/{GrCheckbox-CEcHIsXM.js.map → GrCheckbox-Dp201frb.js.map} +1 -1
  125. package/dist/components/GrCheckbox/index.js +1 -1
  126. package/dist/components/GrCheckboxGroup/chunks/{GrCheckboxGroup-BOwrVcFr.js → GrCheckboxGroup-IpqG5qwm.js} +2 -2
  127. package/dist/components/GrCheckboxGroup/chunks/{GrCheckboxGroup-BOwrVcFr.js.map → GrCheckboxGroup-IpqG5qwm.js.map} +1 -1
  128. package/dist/components/GrCheckboxGroup/index.js +1 -1
  129. package/dist/components/GrChip/chunks/{GrChip-BTivtwff.js → GrChip-CKqyzYcv.js} +10 -7
  130. package/dist/components/GrChip/chunks/{GrChip-BTivtwff.js.map → GrChip-CKqyzYcv.js.map} +1 -1
  131. package/dist/components/GrChip/index.js +1 -1
  132. package/dist/components/GrChipGroup/chunks/{GrChipGroup-C78Vcwos.js → GrChipGroup-dDGwnC04.js} +1 -1
  133. package/dist/components/GrChipGroup/chunks/{GrChipGroup-C78Vcwos.js.map → GrChipGroup-dDGwnC04.js.map} +1 -1
  134. package/dist/components/GrChipGroup/index.js +1 -1
  135. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-Dp0eEBTZ.js → GrCodeBlock-BUOwKwQE.js} +2 -2
  136. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-Dp0eEBTZ.js.map → GrCodeBlock-BUOwKwQE.js.map} +1 -1
  137. package/dist/components/GrCodeBlock/index.js +1 -1
  138. package/dist/components/GrCollapse/chunks/{GrCollapse-BlW6ybdL.js → GrCollapse-DWH3QAnU.js} +6 -5
  139. package/dist/components/GrCollapse/chunks/{GrCollapse-BlW6ybdL.js.map → GrCollapse-DWH3QAnU.js.map} +1 -1
  140. package/dist/components/GrCollapse/index.js +1 -1
  141. package/dist/components/GrColorPicker/chunks/{GrColorPicker-B-L4CWMt.js → GrColorPicker-BNtpj_P6.js} +4 -4
  142. package/dist/components/GrColorPicker/chunks/{GrColorPicker-B-L4CWMt.js.map → GrColorPicker-BNtpj_P6.js.map} +1 -1
  143. package/dist/components/GrColorPicker/index.js +1 -1
  144. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-8HkYmMVP.js → GrCommandPalette-BVZEd4yy.js} +7 -7
  145. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-8HkYmMVP.js.map → GrCommandPalette-BVZEd4yy.js.map} +1 -1
  146. package/dist/components/GrCommandPalette/index.js +1 -1
  147. package/dist/components/GrConfigProvider/chunks/{GrConfigProvider-BF7R3hgg.js → GrConfigProvider-t9x2UToH.js} +13 -12
  148. package/dist/components/GrConfigProvider/chunks/{GrConfigProvider-BF7R3hgg.js.map → GrConfigProvider-t9x2UToH.js.map} +1 -1
  149. package/dist/components/GrConfigProvider/index.js +2 -2
  150. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CZEO2JR4.js → GrConfirmDialog-K5ht2cYq.js} +4 -4
  151. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CZEO2JR4.js.map → GrConfirmDialog-K5ht2cYq.js.map} +1 -1
  152. package/dist/components/GrConfirmDialog/index.js +1 -1
  153. package/dist/components/GrContextMenu/chunks/{GrContextMenu-DlFWm0eW.js → GrContextMenu-Dd2tgMoL.js} +1 -1
  154. package/dist/components/GrContextMenu/chunks/{GrContextMenu-DlFWm0eW.js.map → GrContextMenu-Dd2tgMoL.js.map} +1 -1
  155. package/dist/components/GrContextMenu/index.js +1 -1
  156. package/dist/components/GrDataTable/chunks/{GrDataTable-4JAgOQ-7.js → GrDataTable-BYYG_XZA.js} +5 -5
  157. package/dist/components/GrDataTable/chunks/{GrDataTable-4JAgOQ-7.js.map → GrDataTable-BYYG_XZA.js.map} +1 -1
  158. package/dist/components/GrDataTable/index.js +1 -1
  159. package/dist/components/GrDelta/chunks/{GrDelta-BZwkgQW-.js → GrDelta-BpfqebEV.js} +0 -0
  160. package/dist/components/GrDelta/chunks/{GrDelta-BZwkgQW-.js.map → GrDelta-BpfqebEV.js.map} +1 -1
  161. package/dist/components/GrDelta/index.js +1 -1
  162. package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-BMeCgFV3.js → GrDescriptionList-D8ANFpn_.js} +2 -2
  163. package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-BMeCgFV3.js.map → GrDescriptionList-D8ANFpn_.js.map} +1 -1
  164. package/dist/components/GrDescriptionList/index.js +2 -2
  165. package/dist/components/GrDialog/chunks/{GrDialog-CKNbnxwO.js → GrDialog-CeJubnKJ.js} +3 -3
  166. package/dist/components/GrDialog/chunks/{GrDialog-CKNbnxwO.js.map → GrDialog-CeJubnKJ.js.map} +1 -1
  167. package/dist/components/GrDialog/index.js +1 -1
  168. package/dist/components/GrDialogService/chunks/{GrDialogService-Ut8iiBQh.js → GrDialogService-BArvw1DV.js} +9 -8
  169. package/dist/components/GrDialogService/chunks/{GrDialogService-Ut8iiBQh.js.map → GrDialogService-BArvw1DV.js.map} +1 -1
  170. package/dist/components/GrDialogService/index.js +1 -1
  171. package/dist/components/GrDivider/chunks/{GrDivider-BXavGcMX.js → GrDivider-DxA-zV25.js} +1 -1
  172. package/dist/components/GrDivider/chunks/{GrDivider-BXavGcMX.js.map → GrDivider-DxA-zV25.js.map} +1 -1
  173. package/dist/components/GrDivider/index.js +1 -1
  174. package/dist/components/GrDrawer/chunks/{GrDrawer-xfoy-MCj.js → GrDrawer-BBHNgeH0.js} +5 -5
  175. package/dist/components/GrDrawer/chunks/{GrDrawer-xfoy-MCj.js.map → GrDrawer-BBHNgeH0.js.map} +1 -1
  176. package/dist/components/GrDrawer/index.js +1 -1
  177. package/dist/components/GrDropdown/chunks/{GrDropdown-7r772-Cq.js → GrDropdown-CUlviqMi.js} +6 -8
  178. package/dist/components/GrDropdown/chunks/{GrDropdown-7r772-Cq.js.map → GrDropdown-CUlviqMi.js.map} +1 -1
  179. package/dist/components/GrDropdown/index.js +1 -1
  180. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-DNurbsWc.js → GrDropdownMenu-COOR1LCs.js} +1 -1
  181. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-DNurbsWc.js.map → GrDropdownMenu-COOR1LCs.js.map} +1 -1
  182. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-B33ZzwfB.js.map +1 -1
  183. package/dist/components/GrDropdownMenu/index.js +1 -1
  184. package/dist/components/GrEmptyState/chunks/{GrEmptyState-RqsWvgI9.js → GrEmptyState-BbvwE-E0.js} +2 -2
  185. package/dist/components/GrEmptyState/chunks/{GrEmptyState-RqsWvgI9.js.map → GrEmptyState-BbvwE-E0.js.map} +1 -1
  186. package/dist/components/GrEmptyState/index.js +1 -1
  187. package/dist/components/GrFilePreview/chunks/{GrFilePreview-C9Z7hMK8.js → GrFilePreview-PabkLgYX.js} +1 -1
  188. package/dist/components/GrFilePreview/chunks/{GrFilePreview-C9Z7hMK8.js.map → GrFilePreview-PabkLgYX.js.map} +1 -1
  189. package/dist/components/GrFilePreview/index.js +1 -1
  190. package/dist/components/GrFileUpload/chunks/{GrFileUpload-C0ROpmOY.js → GrFileUpload-BwMR71zN.js} +4 -4
  191. package/dist/components/GrFileUpload/chunks/{GrFileUpload-C0ROpmOY.js.map → GrFileUpload-BwMR71zN.js.map} +1 -1
  192. package/dist/components/GrFileUpload/index.js +1 -1
  193. package/dist/components/GrForm/chunks/{GrForm-CrnfCsgj.js → GrForm-xTtavR-K.js} +1 -1
  194. package/dist/components/GrForm/chunks/{GrForm-CrnfCsgj.js.map → GrForm-xTtavR-K.js.map} +1 -1
  195. package/dist/components/GrForm/index.js +2 -2
  196. package/dist/components/GrFormField/chunks/{GrFormField-DSylU7IU.js → GrFormField-DO61vxpS.js} +2 -2
  197. package/dist/components/GrFormField/chunks/{GrFormField-DSylU7IU.js.map → GrFormField-DO61vxpS.js.map} +1 -1
  198. package/dist/components/GrFormField/index.js +1 -1
  199. package/dist/components/GrFormFile/chunks/{GrFormFile-B1jGZyIj.js → GrFormFile-Dlxx-9zi.js} +3 -3
  200. package/dist/components/GrFormFile/chunks/{GrFormFile-B1jGZyIj.js.map → GrFormFile-Dlxx-9zi.js.map} +1 -1
  201. package/dist/components/GrFormFile/index.js +1 -1
  202. package/dist/components/GrFormSection/chunks/{GrFormSection-ejieOg9H.js → GrFormSection-DV1S6R_N.js} +1 -1
  203. package/dist/components/GrFormSection/chunks/{GrFormSection-ejieOg9H.js.map → GrFormSection-DV1S6R_N.js.map} +1 -1
  204. package/dist/components/GrFormSection/index.js +1 -1
  205. package/dist/components/GrIcon/chunks/{GrIcon-Dmf0c9ba.js → GrIcon-DTZIgmje.js} +1 -1
  206. package/dist/components/GrIcon/chunks/{GrIcon-Dmf0c9ba.js.map → GrIcon-DTZIgmje.js.map} +1 -1
  207. package/dist/components/GrIcon/index.js +1 -1
  208. package/dist/components/GrImageViewer/chunks/{GrImageViewer--BU8yrfx.js → GrImageViewer-BjYw-uTF.js} +2 -4
  209. package/dist/components/GrImageViewer/chunks/{GrImageViewer--BU8yrfx.js.map → GrImageViewer-BjYw-uTF.js.map} +1 -1
  210. package/dist/components/GrImageViewer/index.js +1 -1
  211. package/dist/components/GrInput/chunks/{GrInput-CJ7eu3fR.js → GrInput-TFeBm3gQ.js} +1 -1
  212. package/dist/components/GrInput/chunks/{GrInput-CJ7eu3fR.js.map → GrInput-TFeBm3gQ.js.map} +1 -1
  213. package/dist/components/GrInput/index.js +1 -1
  214. package/dist/components/GrInputTag/chunks/{GrInputTag-ByD9NugQ.js → GrInputTag-DhfUNOA5.js} +3 -3
  215. package/dist/components/GrInputTag/chunks/{GrInputTag-ByD9NugQ.js.map → GrInputTag-DhfUNOA5.js.map} +1 -1
  216. package/dist/components/GrInputTag/index.js +1 -1
  217. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-BXG7DdTe.js → GrJsonViewer-CdtmUpZ3.js} +6 -6
  218. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-BXG7DdTe.js.map → GrJsonViewer-CdtmUpZ3.js.map} +1 -1
  219. package/dist/components/GrJsonViewer/index.js +2 -2
  220. package/dist/components/GrKbd/chunks/{GrKbd-D2ga1H9i.js → GrKbd-D_a1mPLe.js} +1 -1
  221. package/dist/components/GrKbd/chunks/{GrKbd-D2ga1H9i.js.map → GrKbd-D_a1mPLe.js.map} +1 -1
  222. package/dist/components/GrKbd/index.js +1 -1
  223. package/dist/components/GrLink/chunks/{GrLink-DYXw3W9w.js → GrLink-DwWErumU.js} +2 -2
  224. package/dist/components/GrLink/chunks/{GrLink-DYXw3W9w.js.map → GrLink-DwWErumU.js.map} +1 -1
  225. package/dist/components/GrLink/index.js +2 -2
  226. package/dist/components/GrList/chunks/{GrList-B_CRJB4c.js → GrList-Unya-LXb.js} +3 -3
  227. package/dist/components/GrList/chunks/{GrList-B_CRJB4c.js.map → GrList-Unya-LXb.js.map} +1 -1
  228. package/dist/components/GrList/index.js +1 -1
  229. package/dist/components/GrLoading/chunks/{GrLoading-CMD3Ri9P.js → GrLoading-BDrlrup_.js} +1 -1
  230. package/dist/components/GrLoading/chunks/{GrLoading-CMD3Ri9P.js.map → GrLoading-BDrlrup_.js.map} +1 -1
  231. package/dist/components/GrLoading/index.js +1 -1
  232. package/dist/components/GrModal/chunks/{GrModal-DixnhJcu.js → GrModal-xIWaJozo.js} +4 -4
  233. package/dist/components/GrModal/chunks/{GrModal-DixnhJcu.js.map → GrModal-xIWaJozo.js.map} +1 -1
  234. package/dist/components/GrModal/index.js +1 -1
  235. package/dist/components/GrNavbar/chunks/{GrNavbar-Dr8e5kW-.js → GrNavbar-BRGuksiy.js} +3 -3
  236. package/dist/components/GrNavbar/chunks/{GrNavbar-Dr8e5kW-.js.map → GrNavbar-BRGuksiy.js.map} +1 -1
  237. package/dist/components/GrNavbar/index.js +1 -1
  238. package/dist/components/GrNumberInput/chunks/{GrNumberInput-DQ59YICu.js → GrNumberInput-BqPydXpK.js} +7 -9
  239. package/dist/components/GrNumberInput/chunks/{GrNumberInput-DQ59YICu.js.map → GrNumberInput-BqPydXpK.js.map} +1 -1
  240. package/dist/components/GrNumberInput/index.js +2 -2
  241. package/dist/components/GrPagination/chunks/{GrPagination-68iyAwQz.js → GrPagination-DhbNZHEF.js} +4 -4
  242. package/dist/components/GrPagination/chunks/{GrPagination-68iyAwQz.js.map → GrPagination-DhbNZHEF.js.map} +1 -1
  243. package/dist/components/GrPagination/index.js +1 -1
  244. package/dist/components/GrPopover/chunks/{GrPopover-DEFxDffj.js → GrPopover-BakLcWkX.js} +3 -3
  245. package/dist/components/GrPopover/chunks/{GrPopover-DEFxDffj.js.map → GrPopover-BakLcWkX.js.map} +1 -1
  246. package/dist/components/GrPopover/index.js +2 -2
  247. package/dist/components/GrProgressBar/chunks/{GrProgressBar-Cd7AaJtR.js → GrProgressBar-C3NRBqIP.js} +2 -2
  248. package/dist/components/GrProgressBar/chunks/{GrProgressBar-Cd7AaJtR.js.map → GrProgressBar-C3NRBqIP.js.map} +1 -1
  249. package/dist/components/GrProgressBar/index.js +1 -1
  250. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-D3yWXYDa.js → GrProgressCircle-CdvbINQc.js} +2 -2
  251. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-D3yWXYDa.js.map → GrProgressCircle-CdvbINQc.js.map} +1 -1
  252. package/dist/components/GrProgressCircle/index.js +1 -1
  253. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ckeombia.js → GrPromptDialog-C9Y1ifE_.js} +8 -8
  254. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ckeombia.js.map → GrPromptDialog-C9Y1ifE_.js.map} +1 -1
  255. package/dist/components/GrPromptDialog/index.js +1 -1
  256. package/dist/components/GrRadio/chunks/{GrRadio-Aq3v8XgG.js → GrRadio-B1776V9D.js} +2 -2
  257. package/dist/components/GrRadio/chunks/{GrRadio-Aq3v8XgG.js.map → GrRadio-B1776V9D.js.map} +1 -1
  258. package/dist/components/GrRadio/index.js +2 -2
  259. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-y9LVdTWh.js → GrRadioGroup-UgitrBFl.js} +2 -2
  260. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-y9LVdTWh.js.map → GrRadioGroup-UgitrBFl.js.map} +1 -1
  261. package/dist/components/GrRadioGroup/index.js +1 -1
  262. package/dist/components/GrRating/chunks/{GrRating-IEiTOdnF.js → GrRating-DTriyyqG.js} +1 -1
  263. package/dist/components/GrRating/chunks/{GrRating-IEiTOdnF.js.map → GrRating-DTriyyqG.js.map} +1 -1
  264. package/dist/components/GrRating/index.js +1 -1
  265. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DMeOWlzj.js → GrResponseErrorBanner-NEEJZ8zi.js} +2 -2
  266. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DMeOWlzj.js.map → GrResponseErrorBanner-NEEJZ8zi.js.map} +1 -1
  267. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CBVRpgIG.js → GrResponseErrorBanner-ZvBvEQDZ.js} +1 -1
  268. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CBVRpgIG.js.map → GrResponseErrorBanner-ZvBvEQDZ.js.map} +1 -1
  269. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  270. package/dist/components/GrSegmented/chunks/{GrSegmented-CB-VR-yL.js → GrSegmented-NKTi9bMG.js} +1 -1
  271. package/dist/components/GrSegmented/chunks/{GrSegmented-CB-VR-yL.js.map → GrSegmented-NKTi9bMG.js.map} +1 -1
  272. package/dist/components/GrSegmented/index.js +1 -1
  273. package/dist/components/GrSelect/chunks/{GrSelect-Bm3Jugx8.js → GrSelect-Do1iAyp1.js} +6 -6
  274. package/dist/components/GrSelect/chunks/{GrSelect-Bm3Jugx8.js.map → GrSelect-Do1iAyp1.js.map} +1 -1
  275. package/dist/components/GrSelect/index.js +1 -1
  276. package/dist/components/GrSidebar/chunks/{GrSidebar-alkwBUzL.js → GrSidebar-DoKLlHSw.js} +2 -2
  277. package/dist/components/GrSidebar/chunks/{GrSidebar-alkwBUzL.js.map → GrSidebar-DoKLlHSw.js.map} +1 -1
  278. package/dist/components/GrSidebar/index.js +1 -1
  279. package/dist/components/GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js.map +1 -1
  280. package/dist/components/GrSlider/chunks/{GrSlider-DM6W6U9c.js → GrSlider-Db-jACXz.js} +6 -4
  281. package/dist/components/GrSlider/chunks/{GrSlider-DM6W6U9c.js.map → GrSlider-Db-jACXz.js.map} +1 -1
  282. package/dist/components/GrSlider/index.js +1 -1
  283. package/dist/components/GrSortableList/chunks/{GrSortableList-HyKX--rO.js → GrSortableList-CQCaWivn.js} +1 -1
  284. package/dist/components/GrSortableList/chunks/{GrSortableList-HyKX--rO.js.map → GrSortableList-CQCaWivn.js.map} +1 -1
  285. package/dist/components/GrSortableList/index.js +1 -1
  286. package/dist/components/GrSplitter/chunks/{GrSplitter-MltjaADn.js → GrSplitter-C-XQzq7q.js} +1 -1
  287. package/dist/components/GrSplitter/chunks/{GrSplitter-MltjaADn.js.map → GrSplitter-C-XQzq7q.js.map} +1 -1
  288. package/dist/components/GrSplitter/index.js +1 -1
  289. package/dist/components/GrStatistic/chunks/{GrStatistic-BoLkXw7F.js → GrStatistic-C1_nY4Gc.js} +2 -2
  290. package/dist/components/GrStatistic/chunks/{GrStatistic-BoLkXw7F.js.map → GrStatistic-C1_nY4Gc.js.map} +1 -1
  291. package/dist/components/GrStatistic/index.js +1 -1
  292. package/dist/components/GrSteps/chunks/{GrSteps-CpGHXLOc.js → GrSteps-DXj7m-WO.js} +2 -2
  293. package/dist/components/GrSteps/chunks/{GrSteps-CpGHXLOc.js.map → GrSteps-DXj7m-WO.js.map} +1 -1
  294. package/dist/components/GrSteps/index.js +1 -1
  295. package/dist/components/GrSwitch/chunks/{GrSwitch-7Oe5QfQZ.js → GrSwitch-CriHWT2A.js} +1 -1
  296. package/dist/components/GrSwitch/chunks/{GrSwitch-7Oe5QfQZ.js.map → GrSwitch-CriHWT2A.js.map} +1 -1
  297. package/dist/components/GrSwitch/index.js +1 -1
  298. package/dist/components/GrTabPanels/chunks/{GrTabPanels-BZQS8-JM.js → GrTabPanels-5zsbqS1v.js} +1 -1
  299. package/dist/components/GrTabPanels/chunks/{GrTabPanels-BZQS8-JM.js.map → GrTabPanels-5zsbqS1v.js.map} +1 -1
  300. package/dist/components/GrTabPanels/index.js +1 -1
  301. package/dist/components/GrTable/chunks/{GrTable-jhUTczjv.js → GrTable-iwzHvlMC.js} +1 -1
  302. package/dist/components/GrTable/chunks/{GrTable-jhUTczjv.js.map → GrTable-iwzHvlMC.js.map} +1 -1
  303. package/dist/components/GrTable/index.js +1 -1
  304. package/dist/components/GrTabs/chunks/{GrTabs-CT2nk_HV.js → GrTabs-DHfv67cp.js} +1 -1
  305. package/dist/components/GrTabs/chunks/{GrTabs-CT2nk_HV.js.map → GrTabs-DHfv67cp.js.map} +1 -1
  306. package/dist/components/GrTabs/index.js +1 -1
  307. package/dist/components/GrTextarea/chunks/{GrTextarea-DinXG12-.js → GrTextarea-BII3w61K.js} +1 -1
  308. package/dist/components/GrTextarea/chunks/{GrTextarea-DinXG12-.js.map → GrTextarea-BII3w61K.js.map} +1 -1
  309. package/dist/components/GrTextarea/index.js +1 -1
  310. package/dist/components/GrTimeline/chunks/{GrTimeline-BiHpArsg.js → GrTimeline-Bs60huw0.js} +2 -2
  311. package/dist/components/GrTimeline/chunks/{GrTimeline-BiHpArsg.js.map → GrTimeline-Bs60huw0.js.map} +1 -1
  312. package/dist/components/GrTimeline/index.js +1 -1
  313. package/dist/components/GrToaster/chunks/{GrToaster-COXIBKzq.js → GrToaster-D-sV-9of.js} +4 -4
  314. package/dist/components/GrToaster/chunks/{GrToaster-COXIBKzq.js.map → GrToaster-D-sV-9of.js.map} +1 -1
  315. package/dist/components/GrToaster/index.js +1 -1
  316. package/dist/components/GrTooltip/chunks/{GrTooltip-uYETdbCD.js → GrTooltip-BUiTbROv.js} +3 -3
  317. package/dist/components/GrTooltip/chunks/{GrTooltip-uYETdbCD.js.map → GrTooltip-BUiTbROv.js.map} +1 -1
  318. package/dist/components/GrTooltip/index.js +1 -1
  319. package/dist/components/GrTree/chunks/{GrTree-Ci6CZfKR.js → GrTree-CkELQwR8.js} +1 -2
  320. package/dist/components/GrTree/chunks/{GrTree-Ci6CZfKR.js.map → GrTree-CkELQwR8.js.map} +1 -1
  321. package/dist/components/GrTree/index.js +1 -1
  322. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-MGsyvkJV.js → GrTreeSelect-DurwZ7Cz.js} +4 -4
  323. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-MGsyvkJV.js.map → GrTreeSelect-DurwZ7Cz.js.map} +1 -1
  324. package/dist/components/GrTreeSelect/index.js +1 -1
  325. package/dist/components/GrValue/chunks/GrValue-ezkQM05Q.js.map +1 -1
  326. package/dist/composables/useFloating.js +1 -1
  327. package/dist/composables/useGrComponentConfig.js +1 -1
  328. package/dist/composables/useOverlayLayer.js +1 -1
  329. package/dist/composables/usePortalTarget.js +1 -1
  330. package/dist/composables/useTheme.js +1 -1
  331. package/dist/composables/useTheme.js.map +1 -1
  332. package/dist/composables/useToast.js +1 -1
  333. package/dist/composables/useToast.js.map +1 -1
  334. package/dist/composables/useVirtualList.js +3 -5
  335. package/dist/composables/useVirtualList.js.map +1 -1
  336. package/dist/directives/loading.js +1 -1
  337. package/dist/granular-provider-node.js +1 -1
  338. package/dist/granular-provider.js +1 -1
  339. package/dist/index.js +86 -86
  340. package/dist/testing.js +1 -1
  341. package/dist/testing.js.map +1 -1
  342. package/dist/types/components/GrDialog/GrDialog.vue.d.ts +1 -1
  343. package/dist/types/components/GrDrawer/GrDrawer.vue.d.ts +1 -1
  344. package/dist/types/components/GrModal/GrModal.vue.d.ts +1 -1
  345. package/dist/types/internal/devHook.d.ts +53 -0
  346. package/dist/vue/index.js +2 -2
  347. package/dist/vue/index.js.map +1 -1
  348. package/dist/web-types.json +1 -1
  349. package/package.json +16 -16
  350. package/dist/chunks/config-CQLNjc9X2.js.map +0 -1
  351. package/dist/chunks/config-CnYdE3HZ2.js.map +0 -1
  352. package/dist/chunks/config-CuQmUkMw2.js.map +0 -1
  353. package/dist/chunks/config-DBmdgoVf2.js.map +0 -1
  354. package/dist/chunks/config-DVCNvhIG2.js.map +0 -1
  355. package/dist/chunks/config-DpAYidyY2.js.map +0 -1
  356. package/dist/chunks/config-MmPym-Z72.js.map +0 -1
  357. package/dist/chunks/overlayStack-0CZS99Da.js.map +0 -1
  358. /package/dist/chunks/{config-CQLNjc9X2.js → config-CQLNjc9X.js} +0 -0
  359. /package/dist/chunks/{config-CnYdE3HZ2.js → config-CnYdE3HZ.js} +0 -0
  360. /package/dist/chunks/{config-DBmdgoVf2.js → config-DBmdgoVf.js} +0 -0
  361. /package/dist/chunks/{config-DVCNvhIG2.js → config-DVCNvhIG.js} +0 -0
  362. /package/dist/chunks/{config-DpAYidyY2.js → config-DpAYidyY.js} +0 -0
  363. /package/dist/chunks/{config-MmPym-Z72.js → config-MmPym-Z7.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"GrJsonViewer-BXG7DdTe.js","names":[],"sources":["../../../../src/components/GrJsonViewer/jsonToNodes.ts","../../../../src/components/GrJsonViewer/GrJsonViewer.vue","../../../../src/components/GrJsonViewer/GrJsonViewer.vue"],"sourcesContent":["/**\n * Разбор произвольного значения в узлы дерева.\n *\n * Компонент показывает данные из БД и от чужих сервисов, поэтому обход обязан\n * пережить всё: цикл, `BigInt`, функцию в поле, массив на десять тысяч записей,\n * строку в миллион символов. Уронить страницу он права не имеет.\n */\n\n/** Что за значение в узле. Решает и цвет, и то, раскрывается ли узел. */\nexport type GrJsonNodeKind\n = | 'object'\n | 'array'\n | 'string'\n | 'number'\n | 'boolean'\n | 'null'\n /** Значения, которых в JSON не бывает: функция, символ, `undefined`. */\n | 'unsupported'\n /** Хвост обрезанного массива — «ещё N элементов». Своего значения не имеет. */\n | 'truncation'\n\nexport type GrJsonNode = {\n /** Читаемый адрес узла (`$.items[3].name`). Он же ключ для `GrTree`. */\n path: string\n /** Имя ключа или индекс. Показывается слева от двоеточия. */\n label: string\n kind: GrJsonNodeKind\n /** Что видно в строке. У ветки — счётчик, у листа — значение, возможно обрезанное. */\n preview: string\n /** Полное значение. Копирование берёт его, а не `preview`. */\n value: unknown\n /** Показ обрезан: копирование всё равно отдаёт целое. */\n truncated: boolean\n isLeaf: boolean\n children?: GrJsonNode[]\n}\n\nexport type GrJsonToNodesOptions = {\n /** Подпись корня. */\n rootLabel?: string\n /** Длина строки, после которой значение обрезается в показе. */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n}\n\n/** Маркер ветки, которая уже встречалась выше по этой же цепочке предков. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\nconst DEFAULT_ROOT_LABEL = '$'\nconst DEFAULT_MAX_STRING_LENGTH = 200\nconst DEFAULT_MAX_ARRAY_ITEMS = 100\n\n/** Ключ, который можно дописать через точку, не сломав адрес. */\nconst PLAIN_KEY = /^[A-Za-z_$][\\w$]*$/\n\nfunction appendKey(parentPath: string, key: string): string {\n return PLAIN_KEY.test(key) ? `${parentPath}.${key}` : `${parentPath}[${JSON.stringify(key)}]`\n}\n\n/**\n * Счётчик у ветки вместо её содержимого: свёрнутый узел обязан сказать, стоит\n * ли его раскрывать. Слово не склоняется — компонент не знает языка потребителя,\n * а число само по себе читается в любом.\n */\nfunction branchPreview(kind: 'object' | 'array', count: number): string {\n return kind === 'array' ? `[${count}]` : `{${count}}`\n}\n\nfunction scalarPreview(value: unknown, maxStringLength: number): { preview: string, truncated: boolean } {\n if (typeof value === 'string') {\n if (value.length <= maxStringLength)\n return { preview: JSON.stringify(value), truncated: false }\n\n // Кавычка открывается и не закрывается намеренно: закрытая читалась бы как\n // конец строки, а строка продолжается — её просто не показывают целиком.\n return { preview: `${JSON.stringify(value.slice(0, maxStringLength))}…`, truncated: true }\n }\n\n // `BigInt` представления в JSON не имеет, и `JSON.stringify` на нём бросает.\n if (typeof value === 'bigint')\n return { preview: `${value}n`, truncated: false }\n\n if (typeof value === 'function' || typeof value === 'symbol' || value === undefined)\n return { preview: String(value), truncated: false }\n\n return { preview: JSON.stringify(value) ?? String(value), truncated: false }\n}\n\nfunction kindOf(value: unknown): GrJsonNodeKind {\n if (value === null)\n return 'null'\n if (Array.isArray(value))\n return 'array'\n\n switch (typeof value) {\n case 'string': return 'string'\n case 'number': return 'number'\n case 'bigint': return 'number'\n case 'boolean': return 'boolean'\n case 'object': return 'object'\n // `undefined`, функция и символ представления в JSON не имеют — узел\n // остаётся, чтобы ключ не исчез из показа молча.\n case 'undefined':\n case 'function':\n case 'symbol': return 'unsupported'\n }\n\n // Недостижимо: ветки выше покрывают `typeof` целиком. Оставлено ради вывода\n // типов — без него TS не считает функцию всегда возвращающей.\n return 'unsupported'\n}\n\ntype BuildContext = {\n maxStringLength: number\n maxArrayItems: number\n /**\n * Цепочка предков текущего узла, а не все встреченные объекты.\n *\n * Разница принципиальна и отличает обход дерева от `replacer` у\n * `JSON.stringify`: тому стек недоступен, поэтому он метит `[Circular]` любую\n * повторную ссылку — в том числе объект, честно положенный в данные дважды.\n * Здесь стек есть, и маркер достаётся только настоящему циклу.\n */\n ancestors: Set<object>\n}\n\nfunction buildNode(label: string, path: string, value: unknown, context: BuildContext): GrJsonNode {\n const kind = kindOf(value)\n\n if (kind !== 'object' && kind !== 'array') {\n const { preview, truncated } = scalarPreview(value, context.maxStringLength)\n\n return { path, label, kind, preview, value, truncated, isLeaf: true }\n }\n\n const container = value as object\n\n if (context.ancestors.has(container)) {\n return { path, label, kind, preview: CIRCULAR_MARKER, value, truncated: false, isLeaf: true }\n }\n\n const entries = Array.isArray(value)\n ? value.map((item, index) => [String(index), item] as const)\n : Object.entries(container)\n\n // Пустой объект и пустой массив — листья: раскрывать нечего, а ветка без\n // детей рисует стрелку, которая ничего не делает.\n if (entries.length === 0) {\n return {\n path,\n label,\n kind,\n preview: Array.isArray(value) ? '[]' : '{}',\n value,\n truncated: false,\n isLeaf: true,\n }\n }\n\n context.ancestors.add(container)\n\n const limit = Math.max(0, context.maxArrayItems)\n const isOverLimit = Array.isArray(value) && entries.length > limit\n const visible = isOverLimit ? entries.slice(0, limit) : entries\n\n const children: GrJsonNode[] = visible.map(([key, item]) => buildNode(\n key,\n Array.isArray(value) ? `${path}[${key}]` : appendKey(path, key),\n item,\n context,\n ))\n\n if (isOverLimit) {\n const rest = entries.length - limit\n\n children.push({\n path: `${path}[…]`,\n label: '…',\n kind: 'truncation',\n preview: String(rest),\n value: (value as unknown[]).slice(limit),\n truncated: true,\n isLeaf: true,\n })\n }\n\n context.ancestors.delete(container)\n\n return {\n path,\n label,\n kind,\n preview: branchPreview(kind, entries.length),\n value,\n truncated: isOverLimit,\n isLeaf: false,\n children,\n }\n}\n\n/**\n * Значение → узлы дерева. Корень всегда один: даже скаляр на входе — это узел,\n * иначе дерево пришлось бы то показывать, то нет.\n */\nexport function jsonToNodes(value: unknown, options: GrJsonToNodesOptions = {}): GrJsonNode[] {\n const context: BuildContext = {\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n maxArrayItems: options.maxArrayItems ?? DEFAULT_MAX_ARRAY_ITEMS,\n ancestors: new Set(),\n }\n\n return [buildNode(options.rootLabel ?? DEFAULT_ROOT_LABEL, DEFAULT_ROOT_LABEL, value, context)]\n}\n\n/** Пути всех узлов до заданной глубины — ими задаётся начальная свёртка. */\nexport function pathsToDepth(nodes: GrJsonNode[], depth: number, level = 0): string[] {\n if (level >= depth)\n return []\n\n return nodes.flatMap(node => (\n node.children ? [node.path, ...pathsToDepth(node.children, depth, level + 1)] : []\n ))\n}\n\n/** Пути всех веток — для «раскрыть всё». */\nexport function branchPaths(nodes: GrJsonNode[]): string[] {\n return nodes.flatMap(node => (node.children ? [node.path, ...branchPaths(node.children)] : []))\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree from '../GrTree/GrTree.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { branchPaths, jsonToNodes, pathsToDepth, type GrJsonNode } from './jsonToNodes'\nimport {\n jsonValueClass,\n jsonViewerContentClass,\n jsonViewerCopyClass,\n jsonViewerGroupClass,\n jsonViewerKeyClass,\n jsonViewerMutedClass,\n jsonViewerPunctuationClass,\n jsonViewerRootClass,\n jsonViewerRowClass,\n jsonViewerSearchClass,\n jsonViewerToolbarClass,\n jsonViewerValueClass,\n} from './grJsonViewerStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrJsonNode, GrJsonNodeKind } from './jsonToNodes'\n\n/**\n * JSON со свёрткой по узлам, поиском по ключу и значению и копированием узла.\n *\n * Дерево, клавиатуру и виртуализацию несёт `GrTree`; здесь — разбор `unknown` в\n * узлы и разметка строки. Плоский текст целиком, который копируют в тикет, —\n * `GrCodeBlock`.\n */\nexport interface GrJsonViewerProps {\n /** Показываемое значение. `unknown`, потому что приходит из БД или чужого сервиса. */\n value: unknown\n /** Подпись корня. По умолчанию `$` — так адресуют путь JSONPath и dev-tools. */\n rootLabel?: string\n /** Сколько уровней раскрыто изначально. */\n defaultExpandDepth?: number\n /**\n * Длина строкового значения, после которой показ обрезается.\n *\n * Не косметика: запрос к модели с картинкой в base64 — это один лист на\n * сотни тысяч символов, и ни свёртка, ни виртуализация по строкам его не\n * берут. Копирование при этом отдаёт значение целиком.\n */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n /** Поле поиска над деревом. Выключено — поиск остаётся на `filter()` из `defineExpose`. */\n searchable?: boolean\n /** Кнопка копирования узла в строке. */\n copyable?: boolean\n /** Оставлять в DOM только окно вокруг вьюпорта. Требует `maxHeight`. */\n virtual?: boolean\n /** Высота области просмотра: число — пиксели, строка — как есть. */\n maxHeight?: string | number\n /** Имя области для скринридера. Безымянное дерево объявляется просто «дерево». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrJsonViewerEmits {\n /** Узел скопирован: путь для тикета и полное значение. */\n (e: 'copy', payload: { path: string, value: unknown }): void\n}\n\nconst props = withDefaults(defineProps<GrJsonViewerProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n rootLabel: undefined,\n defaultExpandDepth: undefined,\n maxStringLength: undefined,\n maxArrayItems: undefined,\n searchable: undefined,\n copyable: undefined,\n virtual: false,\n maxHeight: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrJsonViewerEmits>()\n\ndefineSlots<{\n /** Значение листа: ссылка, дата, денежная сумма — оформление знает приложение. */\n leaf?: (props: { node: GrJsonNode }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrJsonViewer' })\nconst resolvedDepth = useGrComponentProp('GrJsonViewer', 'defaultExpandDepth', () => props.defaultExpandDepth, 1)\nconst resolvedMaxString = useGrComponentProp('GrJsonViewer', 'maxStringLength', () => props.maxStringLength, 200)\nconst resolvedMaxArray = useGrComponentProp('GrJsonViewer', 'maxArrayItems', () => props.maxArrayItems, 100)\nconst resolvedSearchable = useGrComponentProp('GrJsonViewer', 'searchable', () => props.searchable, true)\nconst resolvedCopyable = useGrComponentProp('GrJsonViewer', 'copyable', () => props.copyable, true)\n\nconst nodes = computed(() => jsonToNodes(props.value, {\n rootLabel: props.rootLabel,\n maxStringLength: resolvedMaxString.value,\n maxArrayItems: resolvedMaxArray.value,\n}))\n\n/**\n * Раскрытие ведётся подстановкой набора путей.\n *\n * Контролируемого `expandedKeys` у `GrTree` нет, но `defaultExpandedKeys` —\n * не «начальное значение»: дерево следит за пропом и заменяет набор целиком.\n * Этого хватает и глубине по умолчанию, и кнопкам «раскрыть/свернуть всё»;\n * ручное раскрытие пользователя при этом сбрасывается — от этих кнопок ровно\n * того и ждут.\n */\nconst expandedPaths = ref<string[]>([])\n\nwatch(\n [nodes, resolvedDepth],\n ([list, depth]) => { expandedPaths.value = pathsToDepth(list, depth) },\n { immediate: true },\n)\n\nfunction expandAll(): void {\n expandedPaths.value = branchPaths(nodes.value)\n}\n\nfunction collapseAll(): void {\n expandedPaths.value = []\n}\n\nconst query = ref('')\nconst treeRef = ref<{ filter: (value: string) => void } | null>(null)\n\nwatch(query, (value) => {\n treeRef.value?.filter(value)\n})\n\nfunction filter(value: string): void {\n query.value = value\n}\n\n/** Поиск идёт и по ключу, и по значению: в чужом ответе ищут то одно, то другое. */\nfunction matchKeyOrValue(value: string, data: GrJsonNode): boolean {\n if (!value)\n return true\n\n const needle = value.toLowerCase()\n\n return data.label.toLowerCase().includes(needle) || data.preview.toLowerCase().includes(needle)\n}\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n\n if (props.virtual && props.maxHeight === undefined && __GR_DEV__) {\n console.warn('[GrJsonViewer] `virtual` без `maxHeight` не включает виртуализацию: окно считается от высоты области.')\n }\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copiedPath = ref<string | null>(null)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\n/**\n * В буфер уходит **полное** значение узла, а не то, что видно: обрезка\n * принадлежит показу, и вставить её обратно нельзя.\n */\nasync function copyNode(node: GrJsonNode): Promise<void> {\n const text = typeof node.value === 'string' ? node.value : JSON.stringify(node.value, null, 2) ?? String(node.value)\n\n try {\n await navigator.clipboard.writeText(text)\n }\n catch {\n announce(t('gr.jsonViewer.copyFailed', 'Could not copy value'))\n return\n }\n\n copiedPath.value = node.path\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copiedPath.value = null\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.jsonViewer.copied', 'Value copied'))\n emit('copy', { path: node.path, value: node.value })\n}\n\ndefineExpose({ filter, expandAll, collapseAll })\n</script>\n\n<template>\n <div data-gr-json-viewer :class=\"jsonViewerRootClass\">\n <div v-if=\"resolvedSearchable\" data-gr-json-viewer-toolbar :class=\"jsonViewerToolbarClass\">\n <GrInput\n v-model=\"query\"\n :class=\"jsonViewerSearchClass\"\n :size=\"resolvedSize\"\n clearable\n :placeholder=\"t('gr.jsonViewer.search', 'Search key or value')\"\n :aria-label=\"t('gr.jsonViewer.search', 'Search key or value')\"\n />\n\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"expandAll\">\n {{ t('gr.jsonViewer.expandAll', 'Expand all') }}\n </GrButton>\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"collapseAll\">\n {{ t('gr.jsonViewer.collapseAll', 'Collapse all') }}\n </GrButton>\n </div>\n\n <GrTree\n ref=\"treeRef\"\n :data=\"nodes\"\n node-key=\"path\"\n :props=\"{ children: 'children', label: 'label', isLeaf: 'isLeaf' }\"\n :default-expanded-keys=\"expandedPaths\"\n :filter-node-method=\"matchKeyOrValue\"\n :content-class=\"jsonViewerContentClass\"\n :virtual=\"virtual\"\n :max-height=\"maxHeight\"\n :size=\"resolvedSize\"\n :aria-label=\"ariaLabel\"\n >\n <template #default=\"{ data }\">\n <span data-gr-json-viewer-row :class=\"[jsonViewerRowClass, jsonViewerGroupClass]\">\n <span data-gr-json-viewer-key :class=\"jsonViewerKeyClass\">{{ data.label }}</span>\n <span :class=\"jsonViewerPunctuationClass\">:</span>\n\n <span\n data-gr-json-viewer-value\n :class=\"[jsonViewerValueClass, jsonValueClass[(data as GrJsonNode).kind]]\"\n >\n <slot name=\"leaf\" :node=\"data as GrJsonNode\">{{ (data as GrJsonNode).preview }}</slot>\n </span>\n\n <span\n v-if=\"(data as GrJsonNode).kind === 'truncation'\"\n :class=\"jsonViewerMutedClass\"\n >{{ t('gr.jsonViewer.moreItems', 'more') }}</span>\n\n <GrButton\n v-if=\"showCopy && (data as GrJsonNode).kind !== 'truncation'\"\n data-gr-json-viewer-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"jsonViewerCopyClass\"\n :aria-label=\"t('gr.jsonViewer.copy', 'Copy value')\"\n :title=\"t('gr.jsonViewer.copy', 'Copy value')\"\n @click.stop=\"copyNode(data as GrJsonNode)\"\n >\n <component\n :is=\"copiedPath === (data as GrJsonNode).path ? IconCheck : IconCopy\"\n class=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n </GrButton>\n </span>\n </template>\n </GrTree>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree from '../GrTree/GrTree.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { branchPaths, jsonToNodes, pathsToDepth, type GrJsonNode } from './jsonToNodes'\nimport {\n jsonValueClass,\n jsonViewerContentClass,\n jsonViewerCopyClass,\n jsonViewerGroupClass,\n jsonViewerKeyClass,\n jsonViewerMutedClass,\n jsonViewerPunctuationClass,\n jsonViewerRootClass,\n jsonViewerRowClass,\n jsonViewerSearchClass,\n jsonViewerToolbarClass,\n jsonViewerValueClass,\n} from './grJsonViewerStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrJsonNode, GrJsonNodeKind } from './jsonToNodes'\n\n/**\n * JSON со свёрткой по узлам, поиском по ключу и значению и копированием узла.\n *\n * Дерево, клавиатуру и виртуализацию несёт `GrTree`; здесь — разбор `unknown` в\n * узлы и разметка строки. Плоский текст целиком, который копируют в тикет, —\n * `GrCodeBlock`.\n */\nexport interface GrJsonViewerProps {\n /** Показываемое значение. `unknown`, потому что приходит из БД или чужого сервиса. */\n value: unknown\n /** Подпись корня. По умолчанию `$` — так адресуют путь JSONPath и dev-tools. */\n rootLabel?: string\n /** Сколько уровней раскрыто изначально. */\n defaultExpandDepth?: number\n /**\n * Длина строкового значения, после которой показ обрезается.\n *\n * Не косметика: запрос к модели с картинкой в base64 — это один лист на\n * сотни тысяч символов, и ни свёртка, ни виртуализация по строкам его не\n * берут. Копирование при этом отдаёт значение целиком.\n */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n /** Поле поиска над деревом. Выключено — поиск остаётся на `filter()` из `defineExpose`. */\n searchable?: boolean\n /** Кнопка копирования узла в строке. */\n copyable?: boolean\n /** Оставлять в DOM только окно вокруг вьюпорта. Требует `maxHeight`. */\n virtual?: boolean\n /** Высота области просмотра: число — пиксели, строка — как есть. */\n maxHeight?: string | number\n /** Имя области для скринридера. Безымянное дерево объявляется просто «дерево». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrJsonViewerEmits {\n /** Узел скопирован: путь для тикета и полное значение. */\n (e: 'copy', payload: { path: string, value: unknown }): void\n}\n\nconst props = withDefaults(defineProps<GrJsonViewerProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n rootLabel: undefined,\n defaultExpandDepth: undefined,\n maxStringLength: undefined,\n maxArrayItems: undefined,\n searchable: undefined,\n copyable: undefined,\n virtual: false,\n maxHeight: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrJsonViewerEmits>()\n\ndefineSlots<{\n /** Значение листа: ссылка, дата, денежная сумма — оформление знает приложение. */\n leaf?: (props: { node: GrJsonNode }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrJsonViewer' })\nconst resolvedDepth = useGrComponentProp('GrJsonViewer', 'defaultExpandDepth', () => props.defaultExpandDepth, 1)\nconst resolvedMaxString = useGrComponentProp('GrJsonViewer', 'maxStringLength', () => props.maxStringLength, 200)\nconst resolvedMaxArray = useGrComponentProp('GrJsonViewer', 'maxArrayItems', () => props.maxArrayItems, 100)\nconst resolvedSearchable = useGrComponentProp('GrJsonViewer', 'searchable', () => props.searchable, true)\nconst resolvedCopyable = useGrComponentProp('GrJsonViewer', 'copyable', () => props.copyable, true)\n\nconst nodes = computed(() => jsonToNodes(props.value, {\n rootLabel: props.rootLabel,\n maxStringLength: resolvedMaxString.value,\n maxArrayItems: resolvedMaxArray.value,\n}))\n\n/**\n * Раскрытие ведётся подстановкой набора путей.\n *\n * Контролируемого `expandedKeys` у `GrTree` нет, но `defaultExpandedKeys` —\n * не «начальное значение»: дерево следит за пропом и заменяет набор целиком.\n * Этого хватает и глубине по умолчанию, и кнопкам «раскрыть/свернуть всё»;\n * ручное раскрытие пользователя при этом сбрасывается — от этих кнопок ровно\n * того и ждут.\n */\nconst expandedPaths = ref<string[]>([])\n\nwatch(\n [nodes, resolvedDepth],\n ([list, depth]) => { expandedPaths.value = pathsToDepth(list, depth) },\n { immediate: true },\n)\n\nfunction expandAll(): void {\n expandedPaths.value = branchPaths(nodes.value)\n}\n\nfunction collapseAll(): void {\n expandedPaths.value = []\n}\n\nconst query = ref('')\nconst treeRef = ref<{ filter: (value: string) => void } | null>(null)\n\nwatch(query, (value) => {\n treeRef.value?.filter(value)\n})\n\nfunction filter(value: string): void {\n query.value = value\n}\n\n/** Поиск идёт и по ключу, и по значению: в чужом ответе ищут то одно, то другое. */\nfunction matchKeyOrValue(value: string, data: GrJsonNode): boolean {\n if (!value)\n return true\n\n const needle = value.toLowerCase()\n\n return data.label.toLowerCase().includes(needle) || data.preview.toLowerCase().includes(needle)\n}\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n\n if (props.virtual && props.maxHeight === undefined && __GR_DEV__) {\n console.warn('[GrJsonViewer] `virtual` без `maxHeight` не включает виртуализацию: окно считается от высоты области.')\n }\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copiedPath = ref<string | null>(null)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\n/**\n * В буфер уходит **полное** значение узла, а не то, что видно: обрезка\n * принадлежит показу, и вставить её обратно нельзя.\n */\nasync function copyNode(node: GrJsonNode): Promise<void> {\n const text = typeof node.value === 'string' ? node.value : JSON.stringify(node.value, null, 2) ?? String(node.value)\n\n try {\n await navigator.clipboard.writeText(text)\n }\n catch {\n announce(t('gr.jsonViewer.copyFailed', 'Could not copy value'))\n return\n }\n\n copiedPath.value = node.path\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copiedPath.value = null\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.jsonViewer.copied', 'Value copied'))\n emit('copy', { path: node.path, value: node.value })\n}\n\ndefineExpose({ filter, expandAll, collapseAll })\n</script>\n\n<template>\n <div data-gr-json-viewer :class=\"jsonViewerRootClass\">\n <div v-if=\"resolvedSearchable\" data-gr-json-viewer-toolbar :class=\"jsonViewerToolbarClass\">\n <GrInput\n v-model=\"query\"\n :class=\"jsonViewerSearchClass\"\n :size=\"resolvedSize\"\n clearable\n :placeholder=\"t('gr.jsonViewer.search', 'Search key or value')\"\n :aria-label=\"t('gr.jsonViewer.search', 'Search key or value')\"\n />\n\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"expandAll\">\n {{ t('gr.jsonViewer.expandAll', 'Expand all') }}\n </GrButton>\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"collapseAll\">\n {{ t('gr.jsonViewer.collapseAll', 'Collapse all') }}\n </GrButton>\n </div>\n\n <GrTree\n ref=\"treeRef\"\n :data=\"nodes\"\n node-key=\"path\"\n :props=\"{ children: 'children', label: 'label', isLeaf: 'isLeaf' }\"\n :default-expanded-keys=\"expandedPaths\"\n :filter-node-method=\"matchKeyOrValue\"\n :content-class=\"jsonViewerContentClass\"\n :virtual=\"virtual\"\n :max-height=\"maxHeight\"\n :size=\"resolvedSize\"\n :aria-label=\"ariaLabel\"\n >\n <template #default=\"{ data }\">\n <span data-gr-json-viewer-row :class=\"[jsonViewerRowClass, jsonViewerGroupClass]\">\n <span data-gr-json-viewer-key :class=\"jsonViewerKeyClass\">{{ data.label }}</span>\n <span :class=\"jsonViewerPunctuationClass\">:</span>\n\n <span\n data-gr-json-viewer-value\n :class=\"[jsonViewerValueClass, jsonValueClass[(data as GrJsonNode).kind]]\"\n >\n <slot name=\"leaf\" :node=\"data as GrJsonNode\">{{ (data as GrJsonNode).preview }}</slot>\n </span>\n\n <span\n v-if=\"(data as GrJsonNode).kind === 'truncation'\"\n :class=\"jsonViewerMutedClass\"\n >{{ t('gr.jsonViewer.moreItems', 'more') }}</span>\n\n <GrButton\n v-if=\"showCopy && (data as GrJsonNode).kind !== 'truncation'\"\n data-gr-json-viewer-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"jsonViewerCopyClass\"\n :aria-label=\"t('gr.jsonViewer.copy', 'Copy value')\"\n :title=\"t('gr.jsonViewer.copy', 'Copy value')\"\n @click.stop=\"copyNode(data as GrJsonNode)\"\n >\n <component\n :is=\"copiedPath === (data as GrJsonNode).path ? IconCheck : IconCopy\"\n class=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n </GrButton>\n </span>\n </template>\n </GrTree>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrJsonViewer-CdtmUpZ3.js","names":[],"sources":["../../../../src/components/GrJsonViewer/jsonToNodes.ts","../../../../src/components/GrJsonViewer/GrJsonViewer.vue","../../../../src/components/GrJsonViewer/GrJsonViewer.vue"],"sourcesContent":["/**\n * Разбор произвольного значения в узлы дерева.\n *\n * Компонент показывает данные из БД и от чужих сервисов, поэтому обход обязан\n * пережить всё: цикл, `BigInt`, функцию в поле, массив на десять тысяч записей,\n * строку в миллион символов. Уронить страницу он права не имеет.\n */\n\n/** Что за значение в узле. Решает и цвет, и то, раскрывается ли узел. */\nexport type GrJsonNodeKind\n = | 'object'\n | 'array'\n | 'string'\n | 'number'\n | 'boolean'\n | 'null'\n /** Значения, которых в JSON не бывает: функция, символ, `undefined`. */\n | 'unsupported'\n /** Хвост обрезанного массива — «ещё N элементов». Своего значения не имеет. */\n | 'truncation'\n\nexport type GrJsonNode = {\n /** Читаемый адрес узла (`$.items[3].name`). Он же ключ для `GrTree`. */\n path: string\n /** Имя ключа или индекс. Показывается слева от двоеточия. */\n label: string\n kind: GrJsonNodeKind\n /** Что видно в строке. У ветки — счётчик, у листа — значение, возможно обрезанное. */\n preview: string\n /** Полное значение. Копирование берёт его, а не `preview`. */\n value: unknown\n /** Показ обрезан: копирование всё равно отдаёт целое. */\n truncated: boolean\n isLeaf: boolean\n children?: GrJsonNode[]\n}\n\nexport type GrJsonToNodesOptions = {\n /** Подпись корня. */\n rootLabel?: string\n /** Длина строки, после которой значение обрезается в показе. */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n}\n\n/** Маркер ветки, которая уже встречалась выше по этой же цепочке предков. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\nconst DEFAULT_ROOT_LABEL = '$'\nconst DEFAULT_MAX_STRING_LENGTH = 200\nconst DEFAULT_MAX_ARRAY_ITEMS = 100\n\n/** Ключ, который можно дописать через точку, не сломав адрес. */\nconst PLAIN_KEY = /^[A-Za-z_$][\\w$]*$/\n\nfunction appendKey(parentPath: string, key: string): string {\n return PLAIN_KEY.test(key) ? `${parentPath}.${key}` : `${parentPath}[${JSON.stringify(key)}]`\n}\n\n/**\n * Счётчик у ветки вместо её содержимого: свёрнутый узел обязан сказать, стоит\n * ли его раскрывать. Слово не склоняется — компонент не знает языка потребителя,\n * а число само по себе читается в любом.\n */\nfunction branchPreview(kind: 'object' | 'array', count: number): string {\n return kind === 'array' ? `[${count}]` : `{${count}}`\n}\n\nfunction scalarPreview(value: unknown, maxStringLength: number): { preview: string, truncated: boolean } {\n if (typeof value === 'string') {\n if (value.length <= maxStringLength)\n return { preview: JSON.stringify(value), truncated: false }\n\n // Кавычка открывается и не закрывается намеренно: закрытая читалась бы как\n // конец строки, а строка продолжается — её просто не показывают целиком.\n return { preview: `${JSON.stringify(value.slice(0, maxStringLength))}…`, truncated: true }\n }\n\n // `BigInt` представления в JSON не имеет, и `JSON.stringify` на нём бросает.\n if (typeof value === 'bigint')\n return { preview: `${value}n`, truncated: false }\n\n if (typeof value === 'function' || typeof value === 'symbol' || value === undefined)\n return { preview: String(value), truncated: false }\n\n return { preview: JSON.stringify(value) ?? String(value), truncated: false }\n}\n\nfunction kindOf(value: unknown): GrJsonNodeKind {\n if (value === null)\n return 'null'\n if (Array.isArray(value))\n return 'array'\n\n switch (typeof value) {\n case 'string': return 'string'\n case 'number': return 'number'\n case 'bigint': return 'number'\n case 'boolean': return 'boolean'\n case 'object': return 'object'\n // `undefined`, функция и символ представления в JSON не имеют — узел\n // остаётся, чтобы ключ не исчез из показа молча.\n case 'undefined':\n case 'function':\n case 'symbol': return 'unsupported'\n }\n\n // Недостижимо: ветки выше покрывают `typeof` целиком. Оставлено ради вывода\n // типов — без него TS не считает функцию всегда возвращающей.\n return 'unsupported'\n}\n\ntype BuildContext = {\n maxStringLength: number\n maxArrayItems: number\n /**\n * Цепочка предков текущего узла, а не все встреченные объекты.\n *\n * Разница принципиальна и отличает обход дерева от `replacer` у\n * `JSON.stringify`: тому стек недоступен, поэтому он метит `[Circular]` любую\n * повторную ссылку — в том числе объект, честно положенный в данные дважды.\n * Здесь стек есть, и маркер достаётся только настоящему циклу.\n */\n ancestors: Set<object>\n}\n\nfunction buildNode(label: string, path: string, value: unknown, context: BuildContext): GrJsonNode {\n const kind = kindOf(value)\n\n if (kind !== 'object' && kind !== 'array') {\n const { preview, truncated } = scalarPreview(value, context.maxStringLength)\n\n return { path, label, kind, preview, value, truncated, isLeaf: true }\n }\n\n const container = value as object\n\n if (context.ancestors.has(container)) {\n return { path, label, kind, preview: CIRCULAR_MARKER, value, truncated: false, isLeaf: true }\n }\n\n const entries = Array.isArray(value)\n ? value.map((item, index) => [String(index), item] as const)\n : Object.entries(container)\n\n // Пустой объект и пустой массив — листья: раскрывать нечего, а ветка без\n // детей рисует стрелку, которая ничего не делает.\n if (entries.length === 0) {\n return {\n path,\n label,\n kind,\n preview: Array.isArray(value) ? '[]' : '{}',\n value,\n truncated: false,\n isLeaf: true,\n }\n }\n\n context.ancestors.add(container)\n\n const limit = Math.max(0, context.maxArrayItems)\n const isOverLimit = Array.isArray(value) && entries.length > limit\n const visible = isOverLimit ? entries.slice(0, limit) : entries\n\n const children: GrJsonNode[] = visible.map(([key, item]) => buildNode(\n key,\n Array.isArray(value) ? `${path}[${key}]` : appendKey(path, key),\n item,\n context,\n ))\n\n if (isOverLimit) {\n const rest = entries.length - limit\n\n children.push({\n path: `${path}[…]`,\n label: '…',\n kind: 'truncation',\n preview: String(rest),\n value: (value as unknown[]).slice(limit),\n truncated: true,\n isLeaf: true,\n })\n }\n\n context.ancestors.delete(container)\n\n return {\n path,\n label,\n kind,\n preview: branchPreview(kind, entries.length),\n value,\n truncated: isOverLimit,\n isLeaf: false,\n children,\n }\n}\n\n/**\n * Значение → узлы дерева. Корень всегда один: даже скаляр на входе — это узел,\n * иначе дерево пришлось бы то показывать, то нет.\n */\nexport function jsonToNodes(value: unknown, options: GrJsonToNodesOptions = {}): GrJsonNode[] {\n const context: BuildContext = {\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n maxArrayItems: options.maxArrayItems ?? DEFAULT_MAX_ARRAY_ITEMS,\n ancestors: new Set(),\n }\n\n return [buildNode(options.rootLabel ?? DEFAULT_ROOT_LABEL, DEFAULT_ROOT_LABEL, value, context)]\n}\n\n/** Пути всех узлов до заданной глубины — ими задаётся начальная свёртка. */\nexport function pathsToDepth(nodes: GrJsonNode[], depth: number, level = 0): string[] {\n if (level >= depth)\n return []\n\n return nodes.flatMap(node => (\n node.children ? [node.path, ...pathsToDepth(node.children, depth, level + 1)] : []\n ))\n}\n\n/** Пути всех веток — для «раскрыть всё». */\nexport function branchPaths(nodes: GrJsonNode[]): string[] {\n return nodes.flatMap(node => (node.children ? [node.path, ...branchPaths(node.children)] : []))\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree from '../GrTree/GrTree.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { branchPaths, jsonToNodes, pathsToDepth, type GrJsonNode } from './jsonToNodes'\nimport {\n jsonValueClass,\n jsonViewerContentClass,\n jsonViewerCopyClass,\n jsonViewerGroupClass,\n jsonViewerKeyClass,\n jsonViewerMutedClass,\n jsonViewerPunctuationClass,\n jsonViewerRootClass,\n jsonViewerRowClass,\n jsonViewerSearchClass,\n jsonViewerToolbarClass,\n jsonViewerValueClass,\n} from './grJsonViewerStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrJsonNode, GrJsonNodeKind } from './jsonToNodes'\n\n/**\n * JSON со свёрткой по узлам, поиском по ключу и значению и копированием узла.\n *\n * Дерево, клавиатуру и виртуализацию несёт `GrTree`; здесь — разбор `unknown` в\n * узлы и разметка строки. Плоский текст целиком, который копируют в тикет, —\n * `GrCodeBlock`.\n */\nexport interface GrJsonViewerProps {\n /** Показываемое значение. `unknown`, потому что приходит из БД или чужого сервиса. */\n value: unknown\n /** Подпись корня. По умолчанию `$` — так адресуют путь JSONPath и dev-tools. */\n rootLabel?: string\n /** Сколько уровней раскрыто изначально. */\n defaultExpandDepth?: number\n /**\n * Длина строкового значения, после которой показ обрезается.\n *\n * Не косметика: запрос к модели с картинкой в base64 — это один лист на\n * сотни тысяч символов, и ни свёртка, ни виртуализация по строкам его не\n * берут. Копирование при этом отдаёт значение целиком.\n */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n /** Поле поиска над деревом. Выключено — поиск остаётся на `filter()` из `defineExpose`. */\n searchable?: boolean\n /** Кнопка копирования узла в строке. */\n copyable?: boolean\n /** Оставлять в DOM только окно вокруг вьюпорта. Требует `maxHeight`. */\n virtual?: boolean\n /** Высота области просмотра: число — пиксели, строка — как есть. */\n maxHeight?: string | number\n /** Имя области для скринридера. Безымянное дерево объявляется просто «дерево». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrJsonViewerEmits {\n /** Узел скопирован: путь для тикета и полное значение. */\n (e: 'copy', payload: { path: string, value: unknown }): void\n}\n\nconst props = withDefaults(defineProps<GrJsonViewerProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n rootLabel: undefined,\n defaultExpandDepth: undefined,\n maxStringLength: undefined,\n maxArrayItems: undefined,\n searchable: undefined,\n copyable: undefined,\n virtual: false,\n maxHeight: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrJsonViewerEmits>()\n\ndefineSlots<{\n /** Значение листа: ссылка, дата, денежная сумма — оформление знает приложение. */\n leaf?: (props: { node: GrJsonNode }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrJsonViewer' })\nconst resolvedDepth = useGrComponentProp('GrJsonViewer', 'defaultExpandDepth', () => props.defaultExpandDepth, 1)\nconst resolvedMaxString = useGrComponentProp('GrJsonViewer', 'maxStringLength', () => props.maxStringLength, 200)\nconst resolvedMaxArray = useGrComponentProp('GrJsonViewer', 'maxArrayItems', () => props.maxArrayItems, 100)\nconst resolvedSearchable = useGrComponentProp('GrJsonViewer', 'searchable', () => props.searchable, true)\nconst resolvedCopyable = useGrComponentProp('GrJsonViewer', 'copyable', () => props.copyable, true)\n\nconst nodes = computed(() => jsonToNodes(props.value, {\n rootLabel: props.rootLabel,\n maxStringLength: resolvedMaxString.value,\n maxArrayItems: resolvedMaxArray.value,\n}))\n\n/**\n * Раскрытие ведётся подстановкой набора путей.\n *\n * Контролируемого `expandedKeys` у `GrTree` нет, но `defaultExpandedKeys` —\n * не «начальное значение»: дерево следит за пропом и заменяет набор целиком.\n * Этого хватает и глубине по умолчанию, и кнопкам «раскрыть/свернуть всё»;\n * ручное раскрытие пользователя при этом сбрасывается — от этих кнопок ровно\n * того и ждут.\n */\nconst expandedPaths = ref<string[]>([])\n\nwatch(\n [nodes, resolvedDepth],\n ([list, depth]) => { expandedPaths.value = pathsToDepth(list, depth) },\n { immediate: true },\n)\n\nfunction expandAll(): void {\n expandedPaths.value = branchPaths(nodes.value)\n}\n\nfunction collapseAll(): void {\n expandedPaths.value = []\n}\n\nconst query = ref('')\nconst treeRef = ref<{ filter: (value: string) => void } | null>(null)\n\nwatch(query, (value) => {\n treeRef.value?.filter(value)\n})\n\nfunction filter(value: string): void {\n query.value = value\n}\n\n/** Поиск идёт и по ключу, и по значению: в чужом ответе ищут то одно, то другое. */\nfunction matchKeyOrValue(value: string, data: GrJsonNode): boolean {\n if (!value)\n return true\n\n const needle = value.toLowerCase()\n\n return data.label.toLowerCase().includes(needle) || data.preview.toLowerCase().includes(needle)\n}\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n\n if (props.virtual && props.maxHeight === undefined && __GR_DEV__) {\n console.warn('[GrJsonViewer] `virtual` без `maxHeight` не включает виртуализацию: окно считается от высоты области.')\n }\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copiedPath = ref<string | null>(null)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\n/**\n * В буфер уходит **полное** значение узла, а не то, что видно: обрезка\n * принадлежит показу, и вставить её обратно нельзя.\n */\nasync function copyNode(node: GrJsonNode): Promise<void> {\n const text = typeof node.value === 'string' ? node.value : JSON.stringify(node.value, null, 2) ?? String(node.value)\n\n try {\n await navigator.clipboard.writeText(text)\n }\n catch {\n announce(t('gr.jsonViewer.copyFailed', 'Could not copy value'))\n return\n }\n\n copiedPath.value = node.path\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copiedPath.value = null\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.jsonViewer.copied', 'Value copied'))\n emit('copy', { path: node.path, value: node.value })\n}\n\ndefineExpose({ filter, expandAll, collapseAll })\n</script>\n\n<template>\n <div data-gr-json-viewer :class=\"jsonViewerRootClass\">\n <div v-if=\"resolvedSearchable\" data-gr-json-viewer-toolbar :class=\"jsonViewerToolbarClass\">\n <GrInput\n v-model=\"query\"\n :class=\"jsonViewerSearchClass\"\n :size=\"resolvedSize\"\n clearable\n :placeholder=\"t('gr.jsonViewer.search', 'Search key or value')\"\n :aria-label=\"t('gr.jsonViewer.search', 'Search key or value')\"\n />\n\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"expandAll\">\n {{ t('gr.jsonViewer.expandAll', 'Expand all') }}\n </GrButton>\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"collapseAll\">\n {{ t('gr.jsonViewer.collapseAll', 'Collapse all') }}\n </GrButton>\n </div>\n\n <GrTree\n ref=\"treeRef\"\n :data=\"nodes\"\n node-key=\"path\"\n :props=\"{ children: 'children', label: 'label', isLeaf: 'isLeaf' }\"\n :default-expanded-keys=\"expandedPaths\"\n :filter-node-method=\"matchKeyOrValue\"\n :content-class=\"jsonViewerContentClass\"\n :virtual=\"virtual\"\n :max-height=\"maxHeight\"\n :size=\"resolvedSize\"\n :aria-label=\"ariaLabel\"\n >\n <template #default=\"{ data }\">\n <span data-gr-json-viewer-row :class=\"[jsonViewerRowClass, jsonViewerGroupClass]\">\n <span data-gr-json-viewer-key :class=\"jsonViewerKeyClass\">{{ data.label }}</span>\n <span :class=\"jsonViewerPunctuationClass\">:</span>\n\n <span\n data-gr-json-viewer-value\n :class=\"[jsonViewerValueClass, jsonValueClass[(data as GrJsonNode).kind]]\"\n >\n <slot name=\"leaf\" :node=\"data as GrJsonNode\">{{ (data as GrJsonNode).preview }}</slot>\n </span>\n\n <span\n v-if=\"(data as GrJsonNode).kind === 'truncation'\"\n :class=\"jsonViewerMutedClass\"\n >{{ t('gr.jsonViewer.moreItems', 'more') }}</span>\n\n <GrButton\n v-if=\"showCopy && (data as GrJsonNode).kind !== 'truncation'\"\n data-gr-json-viewer-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"jsonViewerCopyClass\"\n :aria-label=\"t('gr.jsonViewer.copy', 'Copy value')\"\n :title=\"t('gr.jsonViewer.copy', 'Copy value')\"\n @click.stop=\"copyNode(data as GrJsonNode)\"\n >\n <component\n :is=\"copiedPath === (data as GrJsonNode).path ? IconCheck : IconCopy\"\n class=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n </GrButton>\n </span>\n </template>\n </GrTree>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree from '../GrTree/GrTree.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { branchPaths, jsonToNodes, pathsToDepth, type GrJsonNode } from './jsonToNodes'\nimport {\n jsonValueClass,\n jsonViewerContentClass,\n jsonViewerCopyClass,\n jsonViewerGroupClass,\n jsonViewerKeyClass,\n jsonViewerMutedClass,\n jsonViewerPunctuationClass,\n jsonViewerRootClass,\n jsonViewerRowClass,\n jsonViewerSearchClass,\n jsonViewerToolbarClass,\n jsonViewerValueClass,\n} from './grJsonViewerStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrJsonNode, GrJsonNodeKind } from './jsonToNodes'\n\n/**\n * JSON со свёрткой по узлам, поиском по ключу и значению и копированием узла.\n *\n * Дерево, клавиатуру и виртуализацию несёт `GrTree`; здесь — разбор `unknown` в\n * узлы и разметка строки. Плоский текст целиком, который копируют в тикет, —\n * `GrCodeBlock`.\n */\nexport interface GrJsonViewerProps {\n /** Показываемое значение. `unknown`, потому что приходит из БД или чужого сервиса. */\n value: unknown\n /** Подпись корня. По умолчанию `$` — так адресуют путь JSONPath и dev-tools. */\n rootLabel?: string\n /** Сколько уровней раскрыто изначально. */\n defaultExpandDepth?: number\n /**\n * Длина строкового значения, после которой показ обрезается.\n *\n * Не косметика: запрос к модели с картинкой в base64 — это один лист на\n * сотни тысяч символов, и ни свёртка, ни виртуализация по строкам его не\n * берут. Копирование при этом отдаёт значение целиком.\n */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n /** Поле поиска над деревом. Выключено — поиск остаётся на `filter()` из `defineExpose`. */\n searchable?: boolean\n /** Кнопка копирования узла в строке. */\n copyable?: boolean\n /** Оставлять в DOM только окно вокруг вьюпорта. Требует `maxHeight`. */\n virtual?: boolean\n /** Высота области просмотра: число — пиксели, строка — как есть. */\n maxHeight?: string | number\n /** Имя области для скринридера. Безымянное дерево объявляется просто «дерево». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrJsonViewerEmits {\n /** Узел скопирован: путь для тикета и полное значение. */\n (e: 'copy', payload: { path: string, value: unknown }): void\n}\n\nconst props = withDefaults(defineProps<GrJsonViewerProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n rootLabel: undefined,\n defaultExpandDepth: undefined,\n maxStringLength: undefined,\n maxArrayItems: undefined,\n searchable: undefined,\n copyable: undefined,\n virtual: false,\n maxHeight: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrJsonViewerEmits>()\n\ndefineSlots<{\n /** Значение листа: ссылка, дата, денежная сумма — оформление знает приложение. */\n leaf?: (props: { node: GrJsonNode }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrJsonViewer' })\nconst resolvedDepth = useGrComponentProp('GrJsonViewer', 'defaultExpandDepth', () => props.defaultExpandDepth, 1)\nconst resolvedMaxString = useGrComponentProp('GrJsonViewer', 'maxStringLength', () => props.maxStringLength, 200)\nconst resolvedMaxArray = useGrComponentProp('GrJsonViewer', 'maxArrayItems', () => props.maxArrayItems, 100)\nconst resolvedSearchable = useGrComponentProp('GrJsonViewer', 'searchable', () => props.searchable, true)\nconst resolvedCopyable = useGrComponentProp('GrJsonViewer', 'copyable', () => props.copyable, true)\n\nconst nodes = computed(() => jsonToNodes(props.value, {\n rootLabel: props.rootLabel,\n maxStringLength: resolvedMaxString.value,\n maxArrayItems: resolvedMaxArray.value,\n}))\n\n/**\n * Раскрытие ведётся подстановкой набора путей.\n *\n * Контролируемого `expandedKeys` у `GrTree` нет, но `defaultExpandedKeys` —\n * не «начальное значение»: дерево следит за пропом и заменяет набор целиком.\n * Этого хватает и глубине по умолчанию, и кнопкам «раскрыть/свернуть всё»;\n * ручное раскрытие пользователя при этом сбрасывается — от этих кнопок ровно\n * того и ждут.\n */\nconst expandedPaths = ref<string[]>([])\n\nwatch(\n [nodes, resolvedDepth],\n ([list, depth]) => { expandedPaths.value = pathsToDepth(list, depth) },\n { immediate: true },\n)\n\nfunction expandAll(): void {\n expandedPaths.value = branchPaths(nodes.value)\n}\n\nfunction collapseAll(): void {\n expandedPaths.value = []\n}\n\nconst query = ref('')\nconst treeRef = ref<{ filter: (value: string) => void } | null>(null)\n\nwatch(query, (value) => {\n treeRef.value?.filter(value)\n})\n\nfunction filter(value: string): void {\n query.value = value\n}\n\n/** Поиск идёт и по ключу, и по значению: в чужом ответе ищут то одно, то другое. */\nfunction matchKeyOrValue(value: string, data: GrJsonNode): boolean {\n if (!value)\n return true\n\n const needle = value.toLowerCase()\n\n return data.label.toLowerCase().includes(needle) || data.preview.toLowerCase().includes(needle)\n}\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n\n if (props.virtual && props.maxHeight === undefined && __GR_DEV__) {\n console.warn('[GrJsonViewer] `virtual` без `maxHeight` не включает виртуализацию: окно считается от высоты области.')\n }\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copiedPath = ref<string | null>(null)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\n/**\n * В буфер уходит **полное** значение узла, а не то, что видно: обрезка\n * принадлежит показу, и вставить её обратно нельзя.\n */\nasync function copyNode(node: GrJsonNode): Promise<void> {\n const text = typeof node.value === 'string' ? node.value : JSON.stringify(node.value, null, 2) ?? String(node.value)\n\n try {\n await navigator.clipboard.writeText(text)\n }\n catch {\n announce(t('gr.jsonViewer.copyFailed', 'Could not copy value'))\n return\n }\n\n copiedPath.value = node.path\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copiedPath.value = null\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.jsonViewer.copied', 'Value copied'))\n emit('copy', { path: node.path, value: node.value })\n}\n\ndefineExpose({ filter, expandAll, collapseAll })\n</script>\n\n<template>\n <div data-gr-json-viewer :class=\"jsonViewerRootClass\">\n <div v-if=\"resolvedSearchable\" data-gr-json-viewer-toolbar :class=\"jsonViewerToolbarClass\">\n <GrInput\n v-model=\"query\"\n :class=\"jsonViewerSearchClass\"\n :size=\"resolvedSize\"\n clearable\n :placeholder=\"t('gr.jsonViewer.search', 'Search key or value')\"\n :aria-label=\"t('gr.jsonViewer.search', 'Search key or value')\"\n />\n\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"expandAll\">\n {{ t('gr.jsonViewer.expandAll', 'Expand all') }}\n </GrButton>\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"collapseAll\">\n {{ t('gr.jsonViewer.collapseAll', 'Collapse all') }}\n </GrButton>\n </div>\n\n <GrTree\n ref=\"treeRef\"\n :data=\"nodes\"\n node-key=\"path\"\n :props=\"{ children: 'children', label: 'label', isLeaf: 'isLeaf' }\"\n :default-expanded-keys=\"expandedPaths\"\n :filter-node-method=\"matchKeyOrValue\"\n :content-class=\"jsonViewerContentClass\"\n :virtual=\"virtual\"\n :max-height=\"maxHeight\"\n :size=\"resolvedSize\"\n :aria-label=\"ariaLabel\"\n >\n <template #default=\"{ data }\">\n <span data-gr-json-viewer-row :class=\"[jsonViewerRowClass, jsonViewerGroupClass]\">\n <span data-gr-json-viewer-key :class=\"jsonViewerKeyClass\">{{ data.label }}</span>\n <span :class=\"jsonViewerPunctuationClass\">:</span>\n\n <span\n data-gr-json-viewer-value\n :class=\"[jsonViewerValueClass, jsonValueClass[(data as GrJsonNode).kind]]\"\n >\n <slot name=\"leaf\" :node=\"data as GrJsonNode\">{{ (data as GrJsonNode).preview }}</slot>\n </span>\n\n <span\n v-if=\"(data as GrJsonNode).kind === 'truncation'\"\n :class=\"jsonViewerMutedClass\"\n >{{ t('gr.jsonViewer.moreItems', 'more') }}</span>\n\n <GrButton\n v-if=\"showCopy && (data as GrJsonNode).kind !== 'truncation'\"\n data-gr-json-viewer-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"jsonViewerCopyClass\"\n :aria-label=\"t('gr.jsonViewer.copy', 'Copy value')\"\n :title=\"t('gr.jsonViewer.copy', 'Copy value')\"\n @click.stop=\"copyNode(data as GrJsonNode)\"\n >\n <component\n :is=\"copiedPath === (data as GrJsonNode).path ? IconCheck : IconCopy\"\n class=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n </GrButton>\n </span>\n </template>\n </GrTree>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;AA+CA,IAAa,kBAAkB;AAE/B,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAClC,IAAM,0BAA0B;;AAGhC,IAAM,YAAY;AAElB,SAAS,UAAU,YAAoB,KAAqB;CAC1D,OAAO,UAAU,KAAK,GAAG,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,KAAK,UAAU,GAAG,EAAE;AAC7F;;;;;;AAOA,SAAS,cAAc,MAA0B,OAAuB;CACtE,OAAO,SAAS,UAAU,IAAI,MAAM,KAAK,IAAI,MAAM;AACrD;AAEA,SAAS,cAAc,OAAgB,iBAAkE;CACvG,IAAI,OAAO,UAAU,UAAU;EAC7B,IAAI,MAAM,UAAU,iBAClB,OAAO;GAAE,SAAS,KAAK,UAAU,KAAK;GAAG,WAAW;EAAM;EAI5D,OAAO;GAAE,SAAS,GAAG,KAAK,UAAU,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE;GAAI,WAAW;EAAK;CAC3F;CAGA,IAAI,OAAO,UAAU,UACnB,OAAO;EAAE,SAAS,GAAG,MAAM;EAAI,WAAW;CAAM;CAElD,IAAI,OAAO,UAAU,cAAc,OAAO,UAAU,YAAY,UAAU,KAAA,GACxE,OAAO;EAAE,SAAS,OAAO,KAAK;EAAG,WAAW;CAAM;CAEpD,OAAO;EAAE,SAAS,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK;EAAG,WAAW;CAAM;AAC7E;AAEA,SAAS,OAAO,OAAgC;CAC9C,IAAI,UAAU,MACZ,OAAO;CACT,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO;CAET,QAAQ,OAAO,OAAf;EACE,KAAK,UAAU,OAAO;EACtB,KAAK,UAAU,OAAO;EACtB,KAAK,UAAU,OAAO;EACtB,KAAK,WAAW,OAAO;EACvB,KAAK,UAAU,OAAO;EAGtB,KAAK;EACL,KAAK;EACL,KAAK,UAAU,OAAO;CACxB;CAIA,OAAO;AACT;AAgBA,SAAS,UAAU,OAAe,MAAc,OAAgB,SAAmC;CACjG,MAAM,OAAO,OAAO,KAAK;CAEzB,IAAI,SAAS,YAAY,SAAS,SAAS;EACzC,MAAM,EAAE,SAAS,cAAc,cAAc,OAAO,QAAQ,eAAe;EAE3E,OAAO;GAAE;GAAM;GAAO;GAAM;GAAS;GAAO;GAAW,QAAQ;EAAK;CACtE;CAEA,MAAM,YAAY;CAElB,IAAI,QAAQ,UAAU,IAAI,SAAS,GACjC,OAAO;EAAE;EAAM;EAAO;EAAM,SAAS;EAAiB;EAAO,WAAW;EAAO,QAAQ;CAAK;CAG9F,MAAM,UAAU,MAAM,QAAQ,KAAK,IAC/B,MAAM,KAAK,MAAM,UAAU,CAAC,OAAO,KAAK,GAAG,IAAI,CAAU,IACzD,OAAO,QAAQ,SAAS;CAI5B,IAAI,QAAQ,WAAW,GACrB,OAAO;EACL;EACA;EACA;EACA,SAAS,MAAM,QAAQ,KAAK,IAAI,OAAO;EACvC;EACA,WAAW;EACX,QAAQ;CACV;CAGF,QAAQ,UAAU,IAAI,SAAS;CAE/B,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,aAAa;CAC/C,MAAM,cAAc,MAAM,QAAQ,KAAK,KAAK,QAAQ,SAAS;CAG7D,MAAM,YAFU,cAAc,QAAQ,MAAM,GAAG,KAAK,IAAI,QAAA,CAEjB,KAAK,CAAC,KAAK,UAAU,UAC1D,KACA,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,GAAG,IAAI,KAAK,UAAU,MAAM,GAAG,GAC9D,MACA,OACF,CAAC;CAED,IAAI,aAAa;EACf,MAAM,OAAO,QAAQ,SAAS;EAE9B,SAAS,KAAK;GACZ,MAAM,GAAG,KAAK;GACd,OAAO;GACP,MAAM;GACN,SAAS,OAAO,IAAI;GACpB,OAAQ,MAAoB,MAAM,KAAK;GACvC,WAAW;GACX,QAAQ;EACV,CAAC;CACH;CAEA,QAAQ,UAAU,OAAO,SAAS;CAElC,OAAO;EACL;EACA;EACA;EACA,SAAS,cAAc,MAAM,QAAQ,MAAM;EAC3C;EACA,WAAW;EACX,QAAQ;EACR;CACF;AACF;;;;;AAMA,SAAgB,YAAY,OAAgB,UAAgC,CAAC,GAAiB;CAC5F,MAAM,UAAwB;EAC5B,iBAAiB,QAAQ,mBAAmB;EAC5C,eAAe,QAAQ,iBAAiB;EACxC,2BAAW,IAAI,IAAI;CACrB;CAEA,OAAO,CAAC,UAAU,QAAQ,aAAa,oBAAoB,oBAAoB,OAAO,OAAO,CAAC;AAChG;;AAGA,SAAgB,aAAa,OAAqB,OAAe,QAAQ,GAAa;CACpF,IAAI,SAAS,OACX,OAAO,CAAC;CAEV,OAAO,MAAM,SAAQ,SACnB,KAAK,WAAW,CAAC,KAAK,MAAM,GAAG,aAAa,KAAK,UAAU,OAAO,QAAQ,CAAC,CAAC,IAAI,CAAC,CAClF;AACH;;AAGA,SAAgB,YAAY,OAA+B;CACzD,OAAO,MAAM,SAAQ,SAAS,KAAK,WAAW,CAAC,KAAK,MAAM,GAAG,YAAY,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAE;AAChG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECzJA,MAAM,QAAQ;EAed,MAAM,OAAO;EAOb,MAAM,EAAE,MAAM,2BAA2B;EACzC,MAAM,EAAE,aAAa,aAAa;EAElC,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,eAAe,CAAC;EACvF,MAAM,gBAAgB,mBAAmB,gBAAgB,4BAA4B,MAAM,oBAAoB,CAAC;EAChH,MAAM,oBAAoB,mBAAmB,gBAAgB,yBAAyB,MAAM,iBAAiB,GAAG;EAChH,MAAM,mBAAmB,mBAAmB,gBAAgB,uBAAuB,MAAM,eAAe,GAAG;EAC3G,MAAM,qBAAqB,mBAAmB,gBAAgB,oBAAoB,MAAM,YAAY,IAAI;EACxG,MAAM,mBAAmB,mBAAmB,gBAAgB,kBAAkB,MAAM,UAAU,IAAI;EAElG,MAAM,QAAQ,eAAe,YAAY,MAAM,OAAO;GACpD,WAAW,MAAM;GACjB,iBAAiB,kBAAkB;GACnC,eAAe,iBAAiB;EAClC,CAAC,CAAC;;;;;;;;;;EAWF,MAAM,gBAAgB,IAAc,CAAC,CAAC;EAEtC,MACE,CAAC,OAAO,aAAa,IACpB,CAAC,MAAM,WAAW;GAAE,cAAc,QAAQ,aAAa,MAAM,KAAK;EAAE,GACrE,EAAE,WAAW,KAAK,CACpB;EAEA,SAAS,YAAkB;GACzB,cAAc,QAAQ,YAAY,MAAM,KAAK;EAC/C;EAEA,SAAS,cAAoB;GAC3B,cAAc,QAAQ,CAAC;EACzB;EAEA,MAAM,QAAQ,IAAI,EAAE;EACpB,MAAM,UAAU,IAAgD,IAAI;EAEpE,MAAM,QAAQ,UAAU;GACtB,QAAQ,OAAO,OAAO,KAAK;EAC7B,CAAC;EAED,SAAS,OAAO,OAAqB;GACnC,MAAM,QAAQ;EAChB;;EAGA,SAAS,gBAAgB,OAAe,MAA2B;GACjE,IAAI,CAAC,OACH,OAAO;GAET,MAAM,SAAS,MAAM,YAAY;GAEjC,OAAO,KAAK,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,KAAK,KAAK,QAAQ,YAAY,CAAC,CAAC,SAAS,MAAM;EAChG;;;;;;EAOA,MAAM,UAAU,IAAI,KAAK;EAEzB,gBAAgB;GACd,QAAQ,QAAQ,OAAO,cAAc,eAAe,OAAO,UAAU,WAAW,cAAc;GAE9F,IAAI,MAAM,WAAW,MAAM,cAAc,KAAA,KAAA,QAAA,IAAA,aAAA,cACvC,QAAQ,KAAK,uGAAuG;EAExH,CAAC;EAED,MAAM,WAAW,eAAe,iBAAiB,SAAS,QAAQ,KAAK;EAEvE,MAAM,aAAa,IAAmB,IAAI;EAC1C,IAAI,cAA6B;EAEjC,SAAS,mBAAyB;GAChC,IAAI,gBAAgB,MAAM;IACxB,aAAa,WAAW;IACxB,cAAc;GAChB;EACF;EAEA,gBAAgB,gBAAgB;;;;;EAMhC,eAAe,SAAS,MAAiC;GACvD,MAAM,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,UAAU,KAAK,OAAO,MAAM,CAAC,KAAK,OAAO,KAAK,KAAK;GAEnH,IAAI;IACF,MAAM,UAAU,UAAU,UAAU,IAAI;GAC1C,QACM;IACJ,SAAS,EAAE,4BAA4B,sBAAsB,CAAC;IAC9D;GACF;GAEA,WAAW,QAAQ,KAAK;GACxB,iBAAiB;GACjB,cAAc,OAAO,iBAAiB;IACpC,WAAW,QAAQ;IACnB,cAAc;GAChB,GAAG,IAAI;GAEP,SAAS,EAAE,wBAAwB,cAAc,CAAC;GAClD,KAAK,QAAQ;IAAE,MAAM,KAAK;IAAM,OAAO,KAAK;GAAM,CAAC;EACrD;EAEA,SAAa;GAAE;GAAQ;GAAW;EAAY,CAAC;;GAI7C,OAAA,UAAA,GAAA,mBAqEM,OAAA;IArED,uBAAA;IAAqB,OAAK,eAAE,MAAA,mBAAA,CAAmB;GACvC,GAAA,CAAA,MAAA,kBAAA,KAAX,UAAA,GAAA,mBAgBM,OAAA;;IAhByB,+BAAA;IAA6B,OAAK,eAAE,MAAA,sBAAA,CAAsB;;IACvF,YAOE,iBAAA;KANS,YAAA,MAAA;KAAA,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAK,QAAA;KACb,OAAK,eAAE,MAAA,qBAAA,CAAqB;KAC5B,MAAM,MAAA,YAAA;KACP,WAAA;KACC,aAAa,MAAA,CAAA,CAAC,CAAA,wBAAA,qBAAA;KACd,cAAY,MAAA,CAAA,CAAC,CAAA,wBAAA,qBAAA;;;;;;;;IAGhB,YAEW,kBAAA;KAFD,SAAQ;KAAS,MAAM,MAAA,YAAA;KAAe,SAAO;;KACrD,SAAA,cAAgD,CAA7C,gBAAA,gBAAA,MAAA,CAAA,CAAC,CAAA,2BAAA,YAAA,CAAA,GAAA,CAAA,CAAA,CAAA;;;IAEN,YAEW,kBAAA;KAFD,SAAQ;KAAS,MAAM,MAAA,YAAA;KAAe,SAAO;;KACrD,SAAA,cAAoD,CAAjD,gBAAA,gBAAA,MAAA,CAAA,CAAC,CAAA,6BAAA,cAAA,CAAA,GAAA,CAAA,CAAA,CAAA;;;GAIR,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAAA,YAiDS,gBAAA;IAhDH,SAAA;IAAJ,KAAI;IACH,MAAM,MAAA;IACP,YAAS;IACR,OAAO;KAAA,UAAA;KAAA,OAAA;KAAA,QAAA;IAAA;IACP,yBAAuB,cAAA;IACvB,sBAAoB;IACpB,iBAAe,MAAA,sBAAA;IACf,SAAS,QAAA;IACT,cAAY,QAAA;IACZ,MAAM,MAAA,YAAA;IACN,cAAY,QAAA;;IAEF,SAAO,SAkCT,EAlCa,WAAI,CACxB,mBAiCO,QAAA;KAjCD,2BAAA;KAAyB,OAAK,eAAA,CAAG,MAAA,kBAAA,GAAoB,MAAA,oBAAA,CAAoB,CAAA;;KAC7E,mBAAiF,QAAA;MAA3E,2BAAA;MAAyB,OAAK,eAAE,MAAA,kBAAA,CAAkB;KAAK,GAAA,gBAAA,KAAK,KAAK,GAAA,CAAA;KACvE,mBAAkD,QAAA,EAA3C,OAAK,eAAE,MAAA,0BAAA,CAA0B,EAAE,GAAA,KAAC,CAAA;KAE3C,mBAKO,QAAA;MAJL,6BAAA;MACC,OAAK,eAAA,CAAG,MAAA,oBAAA,GAAsB,MAAA,cAAA,CAAc,CAAE,KAAoB,KAAI,CAAA;KAEvE,GAAA,CAAA,WAAsF,KAAA,QAAA,QAAA,EAAnE,MAAM,KAAzB,SAAsF,CAArC,gBAAA,gBAAA,KAAoB,OAAO,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA;KAIrE,KAAoB,SAAI,gBADjC,UAAA,GAAA,mBAGkD,QAAA;;MAD/C,OAAK,eAAE,MAAA,oBAAA,CAAoB;KAC1B,GAAA,gBAAA,MAAA,CAAA,CAAC,CAAA,2BAAA,MAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAGG,SAAA,SAAa,KAAoB,SAAI,gBAD7C,UAAA,GAAA,YAgBW,kBAAA;;MAdT,4BAAA;MACA,SAAQ;MACR,MAAK;MACL,QAAA;MACC,OAAK,eAAE,MAAA,mBAAA,CAAmB;MAC1B,cAAY,MAAA,CAAA,CAAC,CAAA,sBAAA,YAAA;MACb,OAAO,MAAA,CAAA,CAAC,CAAA,sBAAA,YAAA;MACR,SAAK,eAAA,WAAO,SAAS,IAAI,GAAA,CAAA,MAAA,CAAA;;MAE1B,SAAA,cAIE,EAJF,UAAA,GAAA,YAIE,wBAHK,WAAA,UAAgB,KAAoB,OAAO,MAAA,aAAA,IAAY,MAAA,YAAA,CAAQ,GAAA;OACpE,OAAM;OACN,eAAY"}
@@ -1,3 +1,3 @@
1
- import { t as GrJsonViewer_default } from "./chunks/GrJsonViewer-BXG7DdTe.js";
2
- import { n as grJsonViewerSafelist, t as grJsonViewerConfig } from "../../chunks/config-DpAYidyY2.js";
1
+ import { t as GrJsonViewer_default } from "./chunks/GrJsonViewer-CdtmUpZ3.js";
2
+ import { n as grJsonViewerSafelist, t as grJsonViewerConfig } from "../../chunks/config-DpAYidyY.js";
3
3
  export { GrJsonViewer_default as GrJsonViewer, GrJsonViewer_default as default, grJsonViewerConfig, grJsonViewerSafelist };
@@ -1,4 +1,4 @@
1
- import { a as useGrComponentSize } from "../../../chunks/context-CTbuwcYs.js";
1
+ import { a as useGrComponentSize } from "../../../chunks/context-CPMRfwVr.js";
2
2
  import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
3
  import { a as shiftSatisfied, n as isAppleDevice, r as isComposingEvent, t as eventMatchesKey } from "../../../chunks/keyboard-DE2yhz_b.js";
4
4
  import { a as grKbdKeyClass, d as sequenceSeparatorClass, i as grKbdInnerKeyClass, r as grKbdComboClass, u as separatorClass } from "../../../chunks/grKbdStyles-DENu8pUK.js";
@@ -1 +1 @@
1
- {"version":3,"file":"GrKbd-D2ga1H9i.js","names":[],"sources":["../../../../src/components/shared/hotkeyTokens.ts","../../../../src/components/shared/hotkey.ts","../../../../src/components/GrKbd/GrKbd.vue","../../../../src/components/GrKbd/GrKbd.vue"],"sourcesContent":["/**\n * Каталог клавиш, которые понимает `keys` у `GrKbd`.\n *\n * Таблица, а не цепочка `if`, по двум причинам: во-первых, список — часть\n * публичного контракта (потребителю нужно знать, что можно написать), и\n * витрина показывает именно его, а не свою копию; во-вторых, копия в демо или\n * в доке разошлась бы с форматтером молча.\n */\n\n/** Ключи читаемых имён — `gr.kbd.<name>`. */\nexport type GrKbdKeyName\n = | 'command' | 'option' | 'shift' | 'control' | 'enter' | 'escape' | 'tab'\n | 'backspace' | 'delete' | 'pageUp' | 'pageDown'\n | 'arrowUp' | 'arrowDown' | 'arrowLeft' | 'arrowRight'\n\n/** Одна клавиша в человекочитаемом виде. */\nexport interface HotkeyKeyView {\n /** Что видно на экране: символ (`⌘`) или слово (`Ctrl`). */\n label: string\n /**\n * Ключ читаемого имени (`gr.kbd.<name>`) для символьных клавиш. У `K` или\n * `Ctrl` его нет — они и так читаются вслух.\n */\n name?: GrKbdKeyName\n /** Символ (`⌘`, `↑`) или слово (`Ctrl`, `Esc`): от этого зависит склейка в одной плашке. */\n symbol?: boolean\n}\n\n/** Смысловая группа — ею витрина и дока раскладывают каталог. */\nexport type GrKbdTokenGroup = 'modifier' | 'editing' | 'navigation'\n\nexport interface GrKbdTokenSpec {\n /** Канонический токен: его пишут в `keys`. */\n token: string\n /** Что ещё принимается — синонимы и сам глиф. */\n aliases: readonly string[]\n group: GrKbdTokenGroup\n /** Вид на macOS и на остальных платформах. */\n apple: HotkeyKeyView\n other: HotkeyKeyView\n}\n\nfunction symbol(label: string, name: GrKbdKeyName): HotkeyKeyView {\n return { label, name, symbol: true }\n}\n\nfunction word(label: string, name?: GrKbdKeyName): HotkeyKeyView {\n return name ? { label, name } : { label }\n}\n\nexport const GR_KBD_TOKENS: readonly GrKbdTokenSpec[] = [\n // Модификаторы: на macOS символы, на остальных платформах слова.\n { token: 'mod', aliases: [], group: 'modifier', apple: symbol('⌘', 'command'), other: word('Ctrl') },\n { token: 'meta', aliases: ['cmd', 'command', '⌘'], group: 'modifier', apple: symbol('⌘', 'command'), other: word('Meta') },\n { token: 'ctrl', aliases: ['control', '⌃'], group: 'modifier', apple: symbol('⌃', 'control'), other: word('Ctrl') },\n { token: 'alt', aliases: ['option', '⌥'], group: 'modifier', apple: symbol('⌥', 'option'), other: word('Alt') },\n { token: 'shift', aliases: ['⇧'], group: 'modifier', apple: symbol('⇧', 'shift'), other: word('Shift') },\n\n { token: 'enter', aliases: ['return', '↵', '↩'], group: 'editing', apple: symbol('↩', 'enter'), other: word('Enter') },\n { token: 'esc', aliases: ['escape'], group: 'editing', apple: word('Esc', 'escape'), other: word('Esc', 'escape') },\n { token: 'tab', aliases: ['⇥'], group: 'editing', apple: symbol('⇥', 'tab'), other: word('Tab') },\n { token: 'space', aliases: [], group: 'editing', apple: word('Space'), other: word('Space') },\n { token: 'backspace', aliases: ['⌫'], group: 'editing', apple: symbol('⌫', 'backspace'), other: word('Backspace') },\n { token: 'delete', aliases: ['del', '⌦'], group: 'editing', apple: symbol('⌦', 'delete'), other: word('Del') },\n\n { token: 'up', aliases: ['arrowup', '↑'], group: 'navigation', apple: symbol('↑', 'arrowUp'), other: symbol('↑', 'arrowUp') },\n { token: 'down', aliases: ['arrowdown', '↓'], group: 'navigation', apple: symbol('↓', 'arrowDown'), other: symbol('↓', 'arrowDown') },\n { token: 'left', aliases: ['arrowleft', '←'], group: 'navigation', apple: symbol('←', 'arrowLeft'), other: symbol('←', 'arrowLeft') },\n { token: 'right', aliases: ['arrowright', '→'], group: 'navigation', apple: symbol('→', 'arrowRight'), other: symbol('→', 'arrowRight') },\n { token: 'home', aliases: [], group: 'navigation', apple: word('Home'), other: word('Home') },\n { token: 'end', aliases: [], group: 'navigation', apple: word('End'), other: word('End') },\n { token: 'pageup', aliases: ['pgup', '⇞'], group: 'navigation', apple: symbol('⇞', 'pageUp'), other: word('PgUp') },\n { token: 'pagedown', aliases: ['pgdn', 'pgdown', '⇟'], group: 'navigation', apple: symbol('⇟', 'pageDown'), other: word('PgDn') },\n]\n\nconst BY_ALIAS = new Map<string, GrKbdTokenSpec>()\nfor (const spec of GR_KBD_TOKENS) {\n BY_ALIAS.set(spec.token, spec)\n for (const alias of spec.aliases) BY_ALIAS.set(alias, spec)\n}\n\n/** Токен или его синоним — в описание клавиши. Регистр не важен, глиф тоже принимается. */\nexport function findKbdToken(token: string): GrKbdTokenSpec | undefined {\n const trimmed = token.trim()\n return BY_ALIAS.get(trimmed.toLowerCase()) ?? BY_ALIAS.get(trimmed)\n}\n","/**\n * Разбор, матчинг и человекочитаемое представление сочетаний клавиш.\n *\n * Общий модуль, а не часть `GrCommandPalette`: то же нужно `GrKbd`, а импорт из\n * чужой компонентной директории на сборке дал бы ребро `GrKbd →\n * GrCommandPalette` (хелпер уехал бы в чанк палитры) — `granular doctor` считает\n * такое незадекларированной зависимостью.\n *\n * Директива `v-hotkey` для палитры не годится: ей нужен смонтированный элемент,\n * а палитра в закрытом состоянии не рендерит ничего. Поэтому здесь — свой\n * матчер на `window`, с тем же синтаксисом комбинаций плюс токен `mod`.\n */\n\nimport { eventMatchesKey, isAppleDevice, isComposingEvent, shiftSatisfied } from '../../internal/keyboard'\n\nimport { findKbdToken, type HotkeyKeyView } from './hotkeyTokens'\n\nexport { isAppleDevice } from '../../internal/keyboard'\nexport { findKbdToken, GR_KBD_TOKENS } from './hotkeyTokens'\nexport type { GrKbdKeyName, GrKbdTokenGroup, GrKbdTokenSpec, HotkeyKeyView } from './hotkeyTokens'\n\nexport type ParsedCommandHotkey = {\n key: string\n ctrl: boolean\n meta: boolean\n alt: boolean\n shift: boolean\n /** `mod` — Cmd на macOS, Ctrl на остальных платформах. */\n mod: boolean\n}\n\nexport function parseCommandHotkey(combo: string): ParsedCommandHotkey | null {\n const parts = combo.split('+').map(p => p.trim()).filter(Boolean)\n const keyToken = parts.at(-1)\n if (!keyToken)\n return null\n\n const parsed: ParsedCommandHotkey = {\n key: keyToken.length === 1 ? keyToken.toLowerCase() : keyToken,\n ctrl: false,\n meta: false,\n alt: false,\n shift: false,\n mod: false,\n }\n\n for (const part of parts.slice(0, -1)) {\n const token = part.toLowerCase()\n if (token === 'mod')\n parsed.mod = true\n else if (token === 'ctrl' || token === 'control')\n parsed.ctrl = true\n else if (token === 'meta' || token === 'cmd' || token === 'command' || token === '⌘')\n parsed.meta = true\n else if (token === 'alt' || token === 'option')\n parsed.alt = true\n else if (token === 'shift')\n parsed.shift = true\n }\n\n return parsed\n}\n\nexport function matchesCommandHotkey(\n event: KeyboardEvent,\n hotkey: ParsedCommandHotkey,\n apple = isAppleDevice(),\n): boolean {\n // Клавиша во время IME-композиции принадлежит композиции, а не сочетанию.\n if (isComposingEvent(event))\n return false\n\n const expectMeta = hotkey.meta || (hotkey.mod && apple)\n const expectCtrl = hotkey.ctrl || (hotkey.mod && !apple)\n\n if (event.metaKey !== expectMeta)\n return false\n if (event.ctrlKey !== expectCtrl)\n return false\n if (event.altKey !== hotkey.alt)\n return false\n if (!shiftSatisfied(event, hotkey.key, hotkey.shift))\n return false\n\n // Комбинация с модификаторами матчится и по физическому коду: на нелатинской\n // раскладке `mod+K` приходит как `key: 'л'`, и без кода сочетание мертво.\n return eventMatchesKey(event, hotkey.key, {\n codeFallback: expectMeta || expectCtrl || hotkey.alt,\n })\n}\n\n/** Человекочитаемые клавиши для подсказки в поле ввода (`⌘` / `Ctrl`). */\nexport function formatCommandHotkey(hotkey: ParsedCommandHotkey, apple = isAppleDevice()): string[] {\n const keys: string[] = []\n if (hotkey.meta || (hotkey.mod && apple))\n keys.push('⌘')\n if (hotkey.ctrl || (hotkey.mod && !apple))\n keys.push('Ctrl')\n if (hotkey.alt)\n keys.push(apple ? '⌥' : 'Alt')\n if (hotkey.shift)\n keys.push(apple ? '⇧' : 'Shift')\n keys.push(hotkey.key.length === 1 ? hotkey.key.toUpperCase() : hotkey.key)\n return keys\n}\n\n/**\n * Токен → то, что видит пользователь. Каталог общий с витриной и докой\n * (`hotkeyTokens.ts`): цепочка `if` расходилась бы со списком, который читает\n * потребитель.\n */\nexport function formatHotkeyToken(token: string, apple: boolean): HotkeyKeyView {\n const spec = findKbdToken(token)\n if (spec)\n return apple ? spec.apple : spec.other\n\n // Не из каталога — печатная клавиша: одиночный символ заглавным, потому что\n // `k` и `K` на клавише выглядят одинаково.\n const t = token.trim()\n return { label: t.length === 1 ? t.toUpperCase() : t }\n}\n\nexport function formatHotkeyTokens(tokens: readonly string[], apple: boolean): HotkeyKeyView[] {\n return tokens.map(token => formatHotkeyToken(token, apple))\n}\n\n/** Комбинация строкой (`mod+shift+K`) — в набор токенов в порядке показа. */\nexport function splitHotkeyCombo(combo: string): string[] {\n return combo.split('+').map(part => part.trim()).filter(Boolean)\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport type { GrComponentSize } from '../shared/sizes'\nimport { formatHotkeyTokens, isAppleDevice, splitHotkeyCombo } from '../shared/hotkey'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grKbdComboClass,\n grKbdInnerKeyClass,\n grKbdKeyClass,\n separatorClass,\n sequenceSeparatorClass,\n type GrKbdVariant,\n} from './grKbdStyles'\n\nexport interface GrKbdProps {\n size?: GrKbdSize\n /**\n * `merged` (по умолчанию) — сочетание одной плашкой, как его пишут сами\n * системы: `⌘K` на macOS, `Ctrl+K` на прочих. `split` — по плашке на клавишу.\n * `sequence` — аккорд «G затем I»: клавиши нажимают одну за другой.\n */\n variant?: GrKbdVariant\n /**\n * Сочетание: строкой (`\"mod+shift+K\"`) или набором токенов\n * (`['mod', 'K']`). Токен `mod` — Cmd на macOS, Ctrl на остальных.\n */\n keys?: string | string[]\n /**\n * Разделитель между клавишами. Не задан — авто: в общей плашке символы\n * склеиваются (`⌘K`), а слова разделяются плюсом (`Ctrl+K`); у `split` это\n * плюс, у `sequence` — слово из локали. Пустая строка — только зазор.\n */\n separator?: string\n platform?: GrKbdPlatform\n}\n\n/**\n * GrKbd — GR-примитив для отображения клавиш и сочетаний (`<kbd>`).\n *\n * Одна клавиша — слотом: `<GrKbd>Esc</GrKbd>`.\n * Сочетание — пропом: `<GrKbd keys=\"mod+K\" />` (на macOS `⌘ K`, иначе `Ctrl K`).\n */\nexport type GrKbdSize = GrComponentSize\n\nexport type { GrKbdVariant } from './grKbdStyles'\n\n/** Откуда брать платформу для токена `mod`. */\nexport type GrKbdPlatform = 'auto' | 'apple' | 'other'\n\nconst props = withDefaults(\n defineProps<GrKbdProps>(),\n {\n size: undefined,\n variant: 'merged',\n keys: undefined,\n separator: undefined,\n platform: 'auto',\n },\n)\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrKbd' })\nconst { t } = useGranularityTranslations()\n\n/**\n * Платформа уточняется только после монтирования: `navigator` в теле `setup`\n * либо роняет серверный рендер, либо расходится с ним и ломает гидрацию.\n * До этого момента показываем не-Apple вариант — он же приходит с сервера.\n */\nconst detectedApple = ref(false)\n\nonMounted(() => {\n detectedApple.value = isAppleDevice()\n})\n\nconst isApple = computed(() => {\n if (props.platform === 'apple')\n return true\n if (props.platform === 'other')\n return false\n return detectedApple.value\n})\n\nconst tokens = computed(() => {\n if (props.keys === undefined)\n return []\n return Array.isArray(props.keys) ? props.keys : splitHotkeyCombo(props.keys)\n})\n\n// Сочетание рисуется вложенными `<kbd>` — приём из спецификации HTML, а не\n// обёртка-div: сочетание остаётся клавиатурным вводом целиком.\nconst keyViews = computed(() => formatHotkeyTokens(tokens.value, isApple.value))\nconst hasCombo = computed(() => keyViews.value.length > 0)\n\nconst keyClass = computed(() => grKbdKeyClass(resolvedSize.value))\nconst innerKeyClass = computed(() => grKbdInnerKeyClass(resolvedSize.value, props.variant))\nconst comboClass = computed(() => grKbdComboClass(resolvedSize.value, props.variant))\n\n/**\n * Разделитель перед клавишей `index`.\n *\n * Авто-правило одно: в общей плашке символы склеиваются, слова — нет. Внутри\n * одной платформы набор однороден (macOS даёт `⌘⌥⇧`, прочие — `Ctrl`, `Alt`),\n * поэтому «слева символ» и означает «пишем как система».\n */\nfunction separatorAt(index: number): string {\n if (index === 0)\n return ''\n if (props.separator !== undefined)\n return props.separator\n if (props.variant === 'sequence')\n return t('gr.kbd.then', 'then')\n if (props.variant === 'split')\n return '+'\n\n return keyViews.value[index - 1]?.symbol ? '' : '+'\n}\n\nconst NAME_FALLBACKS: Record<string, string> = {\n command: 'Command',\n option: 'Option',\n shift: 'Shift',\n control: 'Control',\n enter: 'Enter',\n escape: 'Escape',\n tab: 'Tab',\n backspace: 'Backspace',\n delete: 'Delete',\n pageUp: 'Page Up',\n pageDown: 'Page Down',\n arrowUp: 'Arrow Up',\n arrowDown: 'Arrow Down',\n arrowLeft: 'Arrow Left',\n arrowRight: 'Arrow Right',\n}\n\n/** Читаемое имя символьной клавиши: `⌘` без него диктор произносит как знак. */\nfunction readableName(name: string): string {\n return t(`gr.kbd.${name}`, NAME_FALLBACKS[name] ?? name)\n}\n\ndefineSlots<{\n /** Клавиша или сочетание вместо пропа `keys`. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <kbd\n v-if=\"hasCombo\"\n data-gr-kbd\n data-gr-kbd-combo\n :data-variant=\"variant\"\n :class=\"comboClass\"\n ><template\n v-for=\"(key, index) in keyViews\"\n :key=\"`${key.label}-${index}`\"\n ><span\n v-if=\"separatorAt(index)\"\n :class=\"variant === 'sequence' ? sequenceSeparatorClass : separatorClass\"\n aria-hidden=\"true\"\n >{{ separatorAt(index) }}</span><kbd\n data-gr-kbd-key\n :class=\"innerKeyClass\"\n ><span :aria-hidden=\"key.name ? 'true' : undefined\">{{ key.label }}</span><span\n v-if=\"key.name\"\n class=\"sr-only\"\n >{{ readableName(key.name) }}</span></kbd></template></kbd>\n\n <kbd\n v-else\n data-gr-kbd\n data-gr-kbd-key\n :class=\"keyClass\"\n ><slot /></kbd>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport type { GrComponentSize } from '../shared/sizes'\nimport { formatHotkeyTokens, isAppleDevice, splitHotkeyCombo } from '../shared/hotkey'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grKbdComboClass,\n grKbdInnerKeyClass,\n grKbdKeyClass,\n separatorClass,\n sequenceSeparatorClass,\n type GrKbdVariant,\n} from './grKbdStyles'\n\nexport interface GrKbdProps {\n size?: GrKbdSize\n /**\n * `merged` (по умолчанию) — сочетание одной плашкой, как его пишут сами\n * системы: `⌘K` на macOS, `Ctrl+K` на прочих. `split` — по плашке на клавишу.\n * `sequence` — аккорд «G затем I»: клавиши нажимают одну за другой.\n */\n variant?: GrKbdVariant\n /**\n * Сочетание: строкой (`\"mod+shift+K\"`) или набором токенов\n * (`['mod', 'K']`). Токен `mod` — Cmd на macOS, Ctrl на остальных.\n */\n keys?: string | string[]\n /**\n * Разделитель между клавишами. Не задан — авто: в общей плашке символы\n * склеиваются (`⌘K`), а слова разделяются плюсом (`Ctrl+K`); у `split` это\n * плюс, у `sequence` — слово из локали. Пустая строка — только зазор.\n */\n separator?: string\n platform?: GrKbdPlatform\n}\n\n/**\n * GrKbd — GR-примитив для отображения клавиш и сочетаний (`<kbd>`).\n *\n * Одна клавиша — слотом: `<GrKbd>Esc</GrKbd>`.\n * Сочетание — пропом: `<GrKbd keys=\"mod+K\" />` (на macOS `⌘ K`, иначе `Ctrl K`).\n */\nexport type GrKbdSize = GrComponentSize\n\nexport type { GrKbdVariant } from './grKbdStyles'\n\n/** Откуда брать платформу для токена `mod`. */\nexport type GrKbdPlatform = 'auto' | 'apple' | 'other'\n\nconst props = withDefaults(\n defineProps<GrKbdProps>(),\n {\n size: undefined,\n variant: 'merged',\n keys: undefined,\n separator: undefined,\n platform: 'auto',\n },\n)\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrKbd' })\nconst { t } = useGranularityTranslations()\n\n/**\n * Платформа уточняется только после монтирования: `navigator` в теле `setup`\n * либо роняет серверный рендер, либо расходится с ним и ломает гидрацию.\n * До этого момента показываем не-Apple вариант — он же приходит с сервера.\n */\nconst detectedApple = ref(false)\n\nonMounted(() => {\n detectedApple.value = isAppleDevice()\n})\n\nconst isApple = computed(() => {\n if (props.platform === 'apple')\n return true\n if (props.platform === 'other')\n return false\n return detectedApple.value\n})\n\nconst tokens = computed(() => {\n if (props.keys === undefined)\n return []\n return Array.isArray(props.keys) ? props.keys : splitHotkeyCombo(props.keys)\n})\n\n// Сочетание рисуется вложенными `<kbd>` — приём из спецификации HTML, а не\n// обёртка-div: сочетание остаётся клавиатурным вводом целиком.\nconst keyViews = computed(() => formatHotkeyTokens(tokens.value, isApple.value))\nconst hasCombo = computed(() => keyViews.value.length > 0)\n\nconst keyClass = computed(() => grKbdKeyClass(resolvedSize.value))\nconst innerKeyClass = computed(() => grKbdInnerKeyClass(resolvedSize.value, props.variant))\nconst comboClass = computed(() => grKbdComboClass(resolvedSize.value, props.variant))\n\n/**\n * Разделитель перед клавишей `index`.\n *\n * Авто-правило одно: в общей плашке символы склеиваются, слова — нет. Внутри\n * одной платформы набор однороден (macOS даёт `⌘⌥⇧`, прочие — `Ctrl`, `Alt`),\n * поэтому «слева символ» и означает «пишем как система».\n */\nfunction separatorAt(index: number): string {\n if (index === 0)\n return ''\n if (props.separator !== undefined)\n return props.separator\n if (props.variant === 'sequence')\n return t('gr.kbd.then', 'then')\n if (props.variant === 'split')\n return '+'\n\n return keyViews.value[index - 1]?.symbol ? '' : '+'\n}\n\nconst NAME_FALLBACKS: Record<string, string> = {\n command: 'Command',\n option: 'Option',\n shift: 'Shift',\n control: 'Control',\n enter: 'Enter',\n escape: 'Escape',\n tab: 'Tab',\n backspace: 'Backspace',\n delete: 'Delete',\n pageUp: 'Page Up',\n pageDown: 'Page Down',\n arrowUp: 'Arrow Up',\n arrowDown: 'Arrow Down',\n arrowLeft: 'Arrow Left',\n arrowRight: 'Arrow Right',\n}\n\n/** Читаемое имя символьной клавиши: `⌘` без него диктор произносит как знак. */\nfunction readableName(name: string): string {\n return t(`gr.kbd.${name}`, NAME_FALLBACKS[name] ?? name)\n}\n\ndefineSlots<{\n /** Клавиша или сочетание вместо пропа `keys`. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <kbd\n v-if=\"hasCombo\"\n data-gr-kbd\n data-gr-kbd-combo\n :data-variant=\"variant\"\n :class=\"comboClass\"\n ><template\n v-for=\"(key, index) in keyViews\"\n :key=\"`${key.label}-${index}`\"\n ><span\n v-if=\"separatorAt(index)\"\n :class=\"variant === 'sequence' ? sequenceSeparatorClass : separatorClass\"\n aria-hidden=\"true\"\n >{{ separatorAt(index) }}</span><kbd\n data-gr-kbd-key\n :class=\"innerKeyClass\"\n ><span :aria-hidden=\"key.name ? 'true' : undefined\">{{ key.label }}</span><span\n v-if=\"key.name\"\n class=\"sr-only\"\n >{{ readableName(key.name) }}</span></kbd></template></kbd>\n\n <kbd\n v-else\n data-gr-kbd\n data-gr-kbd-key\n :class=\"keyClass\"\n ><slot /></kbd>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrKbd-D_a1mPLe.js","names":[],"sources":["../../../../src/components/shared/hotkeyTokens.ts","../../../../src/components/shared/hotkey.ts","../../../../src/components/GrKbd/GrKbd.vue","../../../../src/components/GrKbd/GrKbd.vue"],"sourcesContent":["/**\n * Каталог клавиш, которые понимает `keys` у `GrKbd`.\n *\n * Таблица, а не цепочка `if`, по двум причинам: во-первых, список — часть\n * публичного контракта (потребителю нужно знать, что можно написать), и\n * витрина показывает именно его, а не свою копию; во-вторых, копия в демо или\n * в доке разошлась бы с форматтером молча.\n */\n\n/** Ключи читаемых имён — `gr.kbd.<name>`. */\nexport type GrKbdKeyName\n = | 'command' | 'option' | 'shift' | 'control' | 'enter' | 'escape' | 'tab'\n | 'backspace' | 'delete' | 'pageUp' | 'pageDown'\n | 'arrowUp' | 'arrowDown' | 'arrowLeft' | 'arrowRight'\n\n/** Одна клавиша в человекочитаемом виде. */\nexport interface HotkeyKeyView {\n /** Что видно на экране: символ (`⌘`) или слово (`Ctrl`). */\n label: string\n /**\n * Ключ читаемого имени (`gr.kbd.<name>`) для символьных клавиш. У `K` или\n * `Ctrl` его нет — они и так читаются вслух.\n */\n name?: GrKbdKeyName\n /** Символ (`⌘`, `↑`) или слово (`Ctrl`, `Esc`): от этого зависит склейка в одной плашке. */\n symbol?: boolean\n}\n\n/** Смысловая группа — ею витрина и дока раскладывают каталог. */\nexport type GrKbdTokenGroup = 'modifier' | 'editing' | 'navigation'\n\nexport interface GrKbdTokenSpec {\n /** Канонический токен: его пишут в `keys`. */\n token: string\n /** Что ещё принимается — синонимы и сам глиф. */\n aliases: readonly string[]\n group: GrKbdTokenGroup\n /** Вид на macOS и на остальных платформах. */\n apple: HotkeyKeyView\n other: HotkeyKeyView\n}\n\nfunction symbol(label: string, name: GrKbdKeyName): HotkeyKeyView {\n return { label, name, symbol: true }\n}\n\nfunction word(label: string, name?: GrKbdKeyName): HotkeyKeyView {\n return name ? { label, name } : { label }\n}\n\nexport const GR_KBD_TOKENS: readonly GrKbdTokenSpec[] = [\n // Модификаторы: на macOS символы, на остальных платформах слова.\n { token: 'mod', aliases: [], group: 'modifier', apple: symbol('⌘', 'command'), other: word('Ctrl') },\n { token: 'meta', aliases: ['cmd', 'command', '⌘'], group: 'modifier', apple: symbol('⌘', 'command'), other: word('Meta') },\n { token: 'ctrl', aliases: ['control', '⌃'], group: 'modifier', apple: symbol('⌃', 'control'), other: word('Ctrl') },\n { token: 'alt', aliases: ['option', '⌥'], group: 'modifier', apple: symbol('⌥', 'option'), other: word('Alt') },\n { token: 'shift', aliases: ['⇧'], group: 'modifier', apple: symbol('⇧', 'shift'), other: word('Shift') },\n\n { token: 'enter', aliases: ['return', '↵', '↩'], group: 'editing', apple: symbol('↩', 'enter'), other: word('Enter') },\n { token: 'esc', aliases: ['escape'], group: 'editing', apple: word('Esc', 'escape'), other: word('Esc', 'escape') },\n { token: 'tab', aliases: ['⇥'], group: 'editing', apple: symbol('⇥', 'tab'), other: word('Tab') },\n { token: 'space', aliases: [], group: 'editing', apple: word('Space'), other: word('Space') },\n { token: 'backspace', aliases: ['⌫'], group: 'editing', apple: symbol('⌫', 'backspace'), other: word('Backspace') },\n { token: 'delete', aliases: ['del', '⌦'], group: 'editing', apple: symbol('⌦', 'delete'), other: word('Del') },\n\n { token: 'up', aliases: ['arrowup', '↑'], group: 'navigation', apple: symbol('↑', 'arrowUp'), other: symbol('↑', 'arrowUp') },\n { token: 'down', aliases: ['arrowdown', '↓'], group: 'navigation', apple: symbol('↓', 'arrowDown'), other: symbol('↓', 'arrowDown') },\n { token: 'left', aliases: ['arrowleft', '←'], group: 'navigation', apple: symbol('←', 'arrowLeft'), other: symbol('←', 'arrowLeft') },\n { token: 'right', aliases: ['arrowright', '→'], group: 'navigation', apple: symbol('→', 'arrowRight'), other: symbol('→', 'arrowRight') },\n { token: 'home', aliases: [], group: 'navigation', apple: word('Home'), other: word('Home') },\n { token: 'end', aliases: [], group: 'navigation', apple: word('End'), other: word('End') },\n { token: 'pageup', aliases: ['pgup', '⇞'], group: 'navigation', apple: symbol('⇞', 'pageUp'), other: word('PgUp') },\n { token: 'pagedown', aliases: ['pgdn', 'pgdown', '⇟'], group: 'navigation', apple: symbol('⇟', 'pageDown'), other: word('PgDn') },\n]\n\nconst BY_ALIAS = new Map<string, GrKbdTokenSpec>()\nfor (const spec of GR_KBD_TOKENS) {\n BY_ALIAS.set(spec.token, spec)\n for (const alias of spec.aliases) BY_ALIAS.set(alias, spec)\n}\n\n/** Токен или его синоним — в описание клавиши. Регистр не важен, глиф тоже принимается. */\nexport function findKbdToken(token: string): GrKbdTokenSpec | undefined {\n const trimmed = token.trim()\n return BY_ALIAS.get(trimmed.toLowerCase()) ?? BY_ALIAS.get(trimmed)\n}\n","/**\n * Разбор, матчинг и человекочитаемое представление сочетаний клавиш.\n *\n * Общий модуль, а не часть `GrCommandPalette`: то же нужно `GrKbd`, а импорт из\n * чужой компонентной директории на сборке дал бы ребро `GrKbd →\n * GrCommandPalette` (хелпер уехал бы в чанк палитры) — `granular doctor` считает\n * такое незадекларированной зависимостью.\n *\n * Директива `v-hotkey` для палитры не годится: ей нужен смонтированный элемент,\n * а палитра в закрытом состоянии не рендерит ничего. Поэтому здесь — свой\n * матчер на `window`, с тем же синтаксисом комбинаций плюс токен `mod`.\n */\n\nimport { eventMatchesKey, isAppleDevice, isComposingEvent, shiftSatisfied } from '../../internal/keyboard'\n\nimport { findKbdToken, type HotkeyKeyView } from './hotkeyTokens'\n\nexport { isAppleDevice } from '../../internal/keyboard'\nexport { findKbdToken, GR_KBD_TOKENS } from './hotkeyTokens'\nexport type { GrKbdKeyName, GrKbdTokenGroup, GrKbdTokenSpec, HotkeyKeyView } from './hotkeyTokens'\n\nexport type ParsedCommandHotkey = {\n key: string\n ctrl: boolean\n meta: boolean\n alt: boolean\n shift: boolean\n /** `mod` — Cmd на macOS, Ctrl на остальных платформах. */\n mod: boolean\n}\n\nexport function parseCommandHotkey(combo: string): ParsedCommandHotkey | null {\n const parts = combo.split('+').map(p => p.trim()).filter(Boolean)\n const keyToken = parts.at(-1)\n if (!keyToken)\n return null\n\n const parsed: ParsedCommandHotkey = {\n key: keyToken.length === 1 ? keyToken.toLowerCase() : keyToken,\n ctrl: false,\n meta: false,\n alt: false,\n shift: false,\n mod: false,\n }\n\n for (const part of parts.slice(0, -1)) {\n const token = part.toLowerCase()\n if (token === 'mod')\n parsed.mod = true\n else if (token === 'ctrl' || token === 'control')\n parsed.ctrl = true\n else if (token === 'meta' || token === 'cmd' || token === 'command' || token === '⌘')\n parsed.meta = true\n else if (token === 'alt' || token === 'option')\n parsed.alt = true\n else if (token === 'shift')\n parsed.shift = true\n }\n\n return parsed\n}\n\nexport function matchesCommandHotkey(\n event: KeyboardEvent,\n hotkey: ParsedCommandHotkey,\n apple = isAppleDevice(),\n): boolean {\n // Клавиша во время IME-композиции принадлежит композиции, а не сочетанию.\n if (isComposingEvent(event))\n return false\n\n const expectMeta = hotkey.meta || (hotkey.mod && apple)\n const expectCtrl = hotkey.ctrl || (hotkey.mod && !apple)\n\n if (event.metaKey !== expectMeta)\n return false\n if (event.ctrlKey !== expectCtrl)\n return false\n if (event.altKey !== hotkey.alt)\n return false\n if (!shiftSatisfied(event, hotkey.key, hotkey.shift))\n return false\n\n // Комбинация с модификаторами матчится и по физическому коду: на нелатинской\n // раскладке `mod+K` приходит как `key: 'л'`, и без кода сочетание мертво.\n return eventMatchesKey(event, hotkey.key, {\n codeFallback: expectMeta || expectCtrl || hotkey.alt,\n })\n}\n\n/** Человекочитаемые клавиши для подсказки в поле ввода (`⌘` / `Ctrl`). */\nexport function formatCommandHotkey(hotkey: ParsedCommandHotkey, apple = isAppleDevice()): string[] {\n const keys: string[] = []\n if (hotkey.meta || (hotkey.mod && apple))\n keys.push('⌘')\n if (hotkey.ctrl || (hotkey.mod && !apple))\n keys.push('Ctrl')\n if (hotkey.alt)\n keys.push(apple ? '⌥' : 'Alt')\n if (hotkey.shift)\n keys.push(apple ? '⇧' : 'Shift')\n keys.push(hotkey.key.length === 1 ? hotkey.key.toUpperCase() : hotkey.key)\n return keys\n}\n\n/**\n * Токен → то, что видит пользователь. Каталог общий с витриной и докой\n * (`hotkeyTokens.ts`): цепочка `if` расходилась бы со списком, который читает\n * потребитель.\n */\nexport function formatHotkeyToken(token: string, apple: boolean): HotkeyKeyView {\n const spec = findKbdToken(token)\n if (spec)\n return apple ? spec.apple : spec.other\n\n // Не из каталога — печатная клавиша: одиночный символ заглавным, потому что\n // `k` и `K` на клавише выглядят одинаково.\n const t = token.trim()\n return { label: t.length === 1 ? t.toUpperCase() : t }\n}\n\nexport function formatHotkeyTokens(tokens: readonly string[], apple: boolean): HotkeyKeyView[] {\n return tokens.map(token => formatHotkeyToken(token, apple))\n}\n\n/** Комбинация строкой (`mod+shift+K`) — в набор токенов в порядке показа. */\nexport function splitHotkeyCombo(combo: string): string[] {\n return combo.split('+').map(part => part.trim()).filter(Boolean)\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport type { GrComponentSize } from '../shared/sizes'\nimport { formatHotkeyTokens, isAppleDevice, splitHotkeyCombo } from '../shared/hotkey'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grKbdComboClass,\n grKbdInnerKeyClass,\n grKbdKeyClass,\n separatorClass,\n sequenceSeparatorClass,\n type GrKbdVariant,\n} from './grKbdStyles'\n\nexport interface GrKbdProps {\n size?: GrKbdSize\n /**\n * `merged` (по умолчанию) — сочетание одной плашкой, как его пишут сами\n * системы: `⌘K` на macOS, `Ctrl+K` на прочих. `split` — по плашке на клавишу.\n * `sequence` — аккорд «G затем I»: клавиши нажимают одну за другой.\n */\n variant?: GrKbdVariant\n /**\n * Сочетание: строкой (`\"mod+shift+K\"`) или набором токенов\n * (`['mod', 'K']`). Токен `mod` — Cmd на macOS, Ctrl на остальных.\n */\n keys?: string | string[]\n /**\n * Разделитель между клавишами. Не задан — авто: в общей плашке символы\n * склеиваются (`⌘K`), а слова разделяются плюсом (`Ctrl+K`); у `split` это\n * плюс, у `sequence` — слово из локали. Пустая строка — только зазор.\n */\n separator?: string\n platform?: GrKbdPlatform\n}\n\n/**\n * GrKbd — GR-примитив для отображения клавиш и сочетаний (`<kbd>`).\n *\n * Одна клавиша — слотом: `<GrKbd>Esc</GrKbd>`.\n * Сочетание — пропом: `<GrKbd keys=\"mod+K\" />` (на macOS `⌘ K`, иначе `Ctrl K`).\n */\nexport type GrKbdSize = GrComponentSize\n\nexport type { GrKbdVariant } from './grKbdStyles'\n\n/** Откуда брать платформу для токена `mod`. */\nexport type GrKbdPlatform = 'auto' | 'apple' | 'other'\n\nconst props = withDefaults(\n defineProps<GrKbdProps>(),\n {\n size: undefined,\n variant: 'merged',\n keys: undefined,\n separator: undefined,\n platform: 'auto',\n },\n)\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrKbd' })\nconst { t } = useGranularityTranslations()\n\n/**\n * Платформа уточняется только после монтирования: `navigator` в теле `setup`\n * либо роняет серверный рендер, либо расходится с ним и ломает гидрацию.\n * До этого момента показываем не-Apple вариант — он же приходит с сервера.\n */\nconst detectedApple = ref(false)\n\nonMounted(() => {\n detectedApple.value = isAppleDevice()\n})\n\nconst isApple = computed(() => {\n if (props.platform === 'apple')\n return true\n if (props.platform === 'other')\n return false\n return detectedApple.value\n})\n\nconst tokens = computed(() => {\n if (props.keys === undefined)\n return []\n return Array.isArray(props.keys) ? props.keys : splitHotkeyCombo(props.keys)\n})\n\n// Сочетание рисуется вложенными `<kbd>` — приём из спецификации HTML, а не\n// обёртка-div: сочетание остаётся клавиатурным вводом целиком.\nconst keyViews = computed(() => formatHotkeyTokens(tokens.value, isApple.value))\nconst hasCombo = computed(() => keyViews.value.length > 0)\n\nconst keyClass = computed(() => grKbdKeyClass(resolvedSize.value))\nconst innerKeyClass = computed(() => grKbdInnerKeyClass(resolvedSize.value, props.variant))\nconst comboClass = computed(() => grKbdComboClass(resolvedSize.value, props.variant))\n\n/**\n * Разделитель перед клавишей `index`.\n *\n * Авто-правило одно: в общей плашке символы склеиваются, слова — нет. Внутри\n * одной платформы набор однороден (macOS даёт `⌘⌥⇧`, прочие — `Ctrl`, `Alt`),\n * поэтому «слева символ» и означает «пишем как система».\n */\nfunction separatorAt(index: number): string {\n if (index === 0)\n return ''\n if (props.separator !== undefined)\n return props.separator\n if (props.variant === 'sequence')\n return t('gr.kbd.then', 'then')\n if (props.variant === 'split')\n return '+'\n\n return keyViews.value[index - 1]?.symbol ? '' : '+'\n}\n\nconst NAME_FALLBACKS: Record<string, string> = {\n command: 'Command',\n option: 'Option',\n shift: 'Shift',\n control: 'Control',\n enter: 'Enter',\n escape: 'Escape',\n tab: 'Tab',\n backspace: 'Backspace',\n delete: 'Delete',\n pageUp: 'Page Up',\n pageDown: 'Page Down',\n arrowUp: 'Arrow Up',\n arrowDown: 'Arrow Down',\n arrowLeft: 'Arrow Left',\n arrowRight: 'Arrow Right',\n}\n\n/** Читаемое имя символьной клавиши: `⌘` без него диктор произносит как знак. */\nfunction readableName(name: string): string {\n return t(`gr.kbd.${name}`, NAME_FALLBACKS[name] ?? name)\n}\n\ndefineSlots<{\n /** Клавиша или сочетание вместо пропа `keys`. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <kbd\n v-if=\"hasCombo\"\n data-gr-kbd\n data-gr-kbd-combo\n :data-variant=\"variant\"\n :class=\"comboClass\"\n ><template\n v-for=\"(key, index) in keyViews\"\n :key=\"`${key.label}-${index}`\"\n ><span\n v-if=\"separatorAt(index)\"\n :class=\"variant === 'sequence' ? sequenceSeparatorClass : separatorClass\"\n aria-hidden=\"true\"\n >{{ separatorAt(index) }}</span><kbd\n data-gr-kbd-key\n :class=\"innerKeyClass\"\n ><span :aria-hidden=\"key.name ? 'true' : undefined\">{{ key.label }}</span><span\n v-if=\"key.name\"\n class=\"sr-only\"\n >{{ readableName(key.name) }}</span></kbd></template></kbd>\n\n <kbd\n v-else\n data-gr-kbd\n data-gr-kbd-key\n :class=\"keyClass\"\n ><slot /></kbd>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport type { GrComponentSize } from '../shared/sizes'\nimport { formatHotkeyTokens, isAppleDevice, splitHotkeyCombo } from '../shared/hotkey'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grKbdComboClass,\n grKbdInnerKeyClass,\n grKbdKeyClass,\n separatorClass,\n sequenceSeparatorClass,\n type GrKbdVariant,\n} from './grKbdStyles'\n\nexport interface GrKbdProps {\n size?: GrKbdSize\n /**\n * `merged` (по умолчанию) — сочетание одной плашкой, как его пишут сами\n * системы: `⌘K` на macOS, `Ctrl+K` на прочих. `split` — по плашке на клавишу.\n * `sequence` — аккорд «G затем I»: клавиши нажимают одну за другой.\n */\n variant?: GrKbdVariant\n /**\n * Сочетание: строкой (`\"mod+shift+K\"`) или набором токенов\n * (`['mod', 'K']`). Токен `mod` — Cmd на macOS, Ctrl на остальных.\n */\n keys?: string | string[]\n /**\n * Разделитель между клавишами. Не задан — авто: в общей плашке символы\n * склеиваются (`⌘K`), а слова разделяются плюсом (`Ctrl+K`); у `split` это\n * плюс, у `sequence` — слово из локали. Пустая строка — только зазор.\n */\n separator?: string\n platform?: GrKbdPlatform\n}\n\n/**\n * GrKbd — GR-примитив для отображения клавиш и сочетаний (`<kbd>`).\n *\n * Одна клавиша — слотом: `<GrKbd>Esc</GrKbd>`.\n * Сочетание — пропом: `<GrKbd keys=\"mod+K\" />` (на macOS `⌘ K`, иначе `Ctrl K`).\n */\nexport type GrKbdSize = GrComponentSize\n\nexport type { GrKbdVariant } from './grKbdStyles'\n\n/** Откуда брать платформу для токена `mod`. */\nexport type GrKbdPlatform = 'auto' | 'apple' | 'other'\n\nconst props = withDefaults(\n defineProps<GrKbdProps>(),\n {\n size: undefined,\n variant: 'merged',\n keys: undefined,\n separator: undefined,\n platform: 'auto',\n },\n)\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrKbd' })\nconst { t } = useGranularityTranslations()\n\n/**\n * Платформа уточняется только после монтирования: `navigator` в теле `setup`\n * либо роняет серверный рендер, либо расходится с ним и ломает гидрацию.\n * До этого момента показываем не-Apple вариант — он же приходит с сервера.\n */\nconst detectedApple = ref(false)\n\nonMounted(() => {\n detectedApple.value = isAppleDevice()\n})\n\nconst isApple = computed(() => {\n if (props.platform === 'apple')\n return true\n if (props.platform === 'other')\n return false\n return detectedApple.value\n})\n\nconst tokens = computed(() => {\n if (props.keys === undefined)\n return []\n return Array.isArray(props.keys) ? props.keys : splitHotkeyCombo(props.keys)\n})\n\n// Сочетание рисуется вложенными `<kbd>` — приём из спецификации HTML, а не\n// обёртка-div: сочетание остаётся клавиатурным вводом целиком.\nconst keyViews = computed(() => formatHotkeyTokens(tokens.value, isApple.value))\nconst hasCombo = computed(() => keyViews.value.length > 0)\n\nconst keyClass = computed(() => grKbdKeyClass(resolvedSize.value))\nconst innerKeyClass = computed(() => grKbdInnerKeyClass(resolvedSize.value, props.variant))\nconst comboClass = computed(() => grKbdComboClass(resolvedSize.value, props.variant))\n\n/**\n * Разделитель перед клавишей `index`.\n *\n * Авто-правило одно: в общей плашке символы склеиваются, слова — нет. Внутри\n * одной платформы набор однороден (macOS даёт `⌘⌥⇧`, прочие — `Ctrl`, `Alt`),\n * поэтому «слева символ» и означает «пишем как система».\n */\nfunction separatorAt(index: number): string {\n if (index === 0)\n return ''\n if (props.separator !== undefined)\n return props.separator\n if (props.variant === 'sequence')\n return t('gr.kbd.then', 'then')\n if (props.variant === 'split')\n return '+'\n\n return keyViews.value[index - 1]?.symbol ? '' : '+'\n}\n\nconst NAME_FALLBACKS: Record<string, string> = {\n command: 'Command',\n option: 'Option',\n shift: 'Shift',\n control: 'Control',\n enter: 'Enter',\n escape: 'Escape',\n tab: 'Tab',\n backspace: 'Backspace',\n delete: 'Delete',\n pageUp: 'Page Up',\n pageDown: 'Page Down',\n arrowUp: 'Arrow Up',\n arrowDown: 'Arrow Down',\n arrowLeft: 'Arrow Left',\n arrowRight: 'Arrow Right',\n}\n\n/** Читаемое имя символьной клавиши: `⌘` без него диктор произносит как знак. */\nfunction readableName(name: string): string {\n return t(`gr.kbd.${name}`, NAME_FALLBACKS[name] ?? name)\n}\n\ndefineSlots<{\n /** Клавиша или сочетание вместо пропа `keys`. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <kbd\n v-if=\"hasCombo\"\n data-gr-kbd\n data-gr-kbd-combo\n :data-variant=\"variant\"\n :class=\"comboClass\"\n ><template\n v-for=\"(key, index) in keyViews\"\n :key=\"`${key.label}-${index}`\"\n ><span\n v-if=\"separatorAt(index)\"\n :class=\"variant === 'sequence' ? sequenceSeparatorClass : separatorClass\"\n aria-hidden=\"true\"\n >{{ separatorAt(index) }}</span><kbd\n data-gr-kbd-key\n :class=\"innerKeyClass\"\n ><span :aria-hidden=\"key.name ? 'true' : undefined\">{{ key.label }}</span><span\n v-if=\"key.name\"\n class=\"sr-only\"\n >{{ readableName(key.name) }}</span></kbd></template></kbd>\n\n <kbd\n v-else\n data-gr-kbd\n data-gr-kbd-key\n :class=\"keyClass\"\n ><slot /></kbd>\n</template>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { a as GR_KBD_TOKENS, o as findKbdToken, t as GrKbd_default } from "./chunks/GrKbd-D2ga1H9i.js";
1
+ import { a as GR_KBD_TOKENS, o as findKbdToken, t as GrKbd_default } from "./chunks/GrKbd-D_a1mPLe.js";
2
2
  import { n as grKbdSafelist, t as grKbdConfig } from "../../chunks/config-BRESvs9g.js";
3
3
  export { GR_KBD_TOKENS, GrKbd_default as GrKbd, GrKbd_default as default, findKbdToken, grKbdConfig, grKbdSafelist };
@@ -1,6 +1,6 @@
1
- import { a as useGrComponentSize } from "../../../chunks/context-CTbuwcYs.js";
1
+ import { a as useGrComponentSize } from "../../../chunks/context-CPMRfwVr.js";
2
2
  import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
- import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-Dmf0c9ba.js";
3
+ import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-DTZIgmje.js";
4
4
  import { a as grLinkClass, i as focusRingClass, o as grLinkColorStyle, t as baseRootClass } from "../../../chunks/grLinkStyles-FME_qMpi.js";
5
5
  import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, markRaw, mergeProps, openBlock, renderSlot, resolveDynamicComponent, toDisplayString, unref, useAttrs, withCtx } from "vue";
6
6
  //#region ~icons/lucide/external-link
@@ -1 +1 @@
1
- {"version":3,"file":"GrLink-DYXw3W9w.js","names":[],"sources":["../../../../src/components/GrLink/GrLink.vue","../../../../src/components/GrLink/GrLink.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoEA,MAAM,QAAQ;EAgBd,MAAM,QAAQ,SAAS;EAIvB,MAAM,gBAAgB,eAAe,CAAC,MAAM,aAAa,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,KAAK;EAEpF,MAAM,WAAW,eAAmC;GAClD,IAAI,CAAC,cAAc,OACjB,OAAO;GACT,IAAI,MAAM,IAIR,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE;GAEnE,OAAO;EACT,CAAC;EAED,MAAM,iBAAiB,eAAe;GACpC,IAAI,MAAM,QACR,OAAO,MAAM;GAEf,IAAI,MAAM,UACR,OAAO;EAGX,CAAC;EAED,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,KACR,OAAO,MAAM;GAIf,IAAI,eAAe,UAAU,UAC3B,OAAO;EAGX,CAAC;EAED,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAEjF,MAAM,EAAE,MAAM,2BAA2B;;;;;;EAOzC,MAAM,gBAAgB,eAAe,cAAc,SAAS,eAAe,UAAU,QAAQ;EAC7F,MAAM,aAAa,eAAe,MAAM,eAAe,EAAE,yBAAyB,oBAAoB,CAAC;EAEvG,MAAM,mBAAmB,eAAe,MAAM,gBAAgB,cAAc,KAAK;;;;;EAMjF,MAAM,oBAAoB,eAAe;GACvC,IAAI,CAAC,MAAM,WACT,OAAO,KAAA;GAET,OAAO,cAAc,QAAQ,GAAG,MAAM,UAAU,IAAI,WAAW,UAAU,MAAM;EACjF,CAAC;EAED,MAAM,WAAW,eAAgB,aAAa,UAAU,OAAO,OAAO,IAAK;EAE3E,MAAM,YAAY,eAAe;GAC/B,MAAM,eAAe,YAAY;IAC/B,MAAM,aAAa;IACnB,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GAID,OAAO,MAAM,WACT,GAAG,cAAc,GAAG,iBACpB,GAAG,cAAc,GAAG,eAAe,GAAG;EAC5C,CAAC;EAGD,MAAM,aAAa,eAAe,iBAAiB;GACjD,MAAM,MAAM;GACZ,SAAS,MAAM;GACf,UAAU,MAAM;EAClB,CAAC,CAAC;;uBASA,YAmBY,wBAlBL,SAAA,KAAQ,GADf,WAEU,MAiBE,KAAA,GAjBG;IACZ,MAAM,cAAA,QAAgB,QAAA,OAAO,KAAA;IAC7B,QAAQ,cAAA,QAAgB,eAAA,QAAiB,KAAA;IACzC,KAAK,cAAA,QAAgB,YAAA,QAAc,KAAA;IACnC,cAAY,kBAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAO,UAAA;IACP,OAAO,WAAA;;2BAEA;KAAR,WAAQ,KAAA,QAAA,SAAA;KAEM,iBAAA,SAAA,UAAA,GAAd,YAES,gBAAA;;MAFwB,MAAM,SAAA;;6BACrB,CAAhB,YAAgB,MAAA,qBAAA,CAAA,CAAA,CAAA;;;KAIN,cAAA,SAAa,CAAK,QAAA,aAAA,UAAA,GAA9B,mBAAkF,QAAlF,YAAyD,MAAC,gBAAG,WAAA,KAAU,IAAG,KAAC,CAAA,KAAA,mBAAA,IAAA,IAAA"}
1
+ {"version":3,"file":"GrLink-DwWErumU.js","names":[],"sources":["../../../../src/components/GrLink/GrLink.vue","../../../../src/components/GrLink/GrLink.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoEA,MAAM,QAAQ;EAgBd,MAAM,QAAQ,SAAS;EAIvB,MAAM,gBAAgB,eAAe,CAAC,MAAM,aAAa,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,KAAK;EAEpF,MAAM,WAAW,eAAmC;GAClD,IAAI,CAAC,cAAc,OACjB,OAAO;GACT,IAAI,MAAM,IAIR,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE;GAEnE,OAAO;EACT,CAAC;EAED,MAAM,iBAAiB,eAAe;GACpC,IAAI,MAAM,QACR,OAAO,MAAM;GAEf,IAAI,MAAM,UACR,OAAO;EAGX,CAAC;EAED,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,KACR,OAAO,MAAM;GAIf,IAAI,eAAe,UAAU,UAC3B,OAAO;EAGX,CAAC;EAED,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAEjF,MAAM,EAAE,MAAM,2BAA2B;;;;;;EAOzC,MAAM,gBAAgB,eAAe,cAAc,SAAS,eAAe,UAAU,QAAQ;EAC7F,MAAM,aAAa,eAAe,MAAM,eAAe,EAAE,yBAAyB,oBAAoB,CAAC;EAEvG,MAAM,mBAAmB,eAAe,MAAM,gBAAgB,cAAc,KAAK;;;;;EAMjF,MAAM,oBAAoB,eAAe;GACvC,IAAI,CAAC,MAAM,WACT,OAAO,KAAA;GAET,OAAO,cAAc,QAAQ,GAAG,MAAM,UAAU,IAAI,WAAW,UAAU,MAAM;EACjF,CAAC;EAED,MAAM,WAAW,eAAgB,aAAa,UAAU,OAAO,OAAO,IAAK;EAE3E,MAAM,YAAY,eAAe;GAC/B,MAAM,eAAe,YAAY;IAC/B,MAAM,aAAa;IACnB,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GAID,OAAO,MAAM,WACT,GAAG,cAAc,GAAG,iBACpB,GAAG,cAAc,GAAG,eAAe,GAAG;EAC5C,CAAC;EAGD,MAAM,aAAa,eAAe,iBAAiB;GACjD,MAAM,MAAM;GACZ,SAAS,MAAM;GACf,UAAU,MAAM;EAClB,CAAC,CAAC;;GASA,OAAA,UAAA,GAAA,YAmBY,wBAlBL,SAAA,KAAQ,GADf,WAEU,MAiBE,KAAA,GAjBG;IACZ,MAAM,cAAA,QAAgB,QAAA,OAAO,KAAA;IAC7B,QAAQ,cAAA,QAAgB,eAAA,QAAiB,KAAA;IACzC,KAAK,cAAA,QAAgB,YAAA,QAAc,KAAA;IACnC,cAAY,kBAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAO,UAAA;IACP,OAAO,WAAA;;IAER,SAAA,cAAQ;KAAR,WAAQ,KAAA,QAAA,SAAA;KAEM,iBAAA,SAAd,UAAA,GAAA,YAES,gBAAA;;MAFwB,MAAM,SAAA;;MACrC,SAAA,cAAgB,CAAhB,YAAgB,MAAA,qBAAA,CAAA,CAAA,CAAA;;;KAIN,cAAA,SAAa,CAAK,QAAA,aAA9B,UAAA,GAAA,mBAAkF,QAAlF,YAAyD,MAAC,gBAAG,WAAA,KAAU,IAAG,KAAC,CAAA,KAAA,mBAAA,IAAA,IAAA"}
@@ -1,3 +1,3 @@
1
- import { t as GrLink_default } from "./chunks/GrLink-DYXw3W9w.js";
2
- import { n as grLinkSafelist, t as grLinkConfig } from "../../chunks/config-CQLNjc9X2.js";
1
+ import { t as GrLink_default } from "./chunks/GrLink-DwWErumU.js";
2
+ import { n as grLinkSafelist, t as grLinkConfig } from "../../chunks/config-CQLNjc9X.js";
3
3
  export { GrLink_default as GrLink, GrLink_default as default, grLinkConfig, grLinkSafelist };
@@ -2,7 +2,7 @@ import { n as useGranularityTranslations } from "../../../chunks/granularityI18n
2
2
  import { useVirtualList } from "../../../composables/useVirtualList.js";
3
3
  import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BOaGB7Aw.js";
4
4
  import { t as GrSkeleton_default } from "../../GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js";
5
- import { t as GrCard_default } from "../../GrCard/chunks/GrCard-BttXz1pQ.js";
5
+ import { t as GrCard_default } from "../../GrCard/chunks/GrCard-Ck5--lMF.js";
6
6
  import { n as hasMeaningfulSlotContent } from "../../../chunks/slotNodes-DxNXpC7v.js";
7
7
  import { a as grListItemPaddingClass, c as itemHoverClass, d as itemTitleClass, f as listSurfaceClass, i as emptyClass, l as itemInteractiveClass, o as itemDescriptionClass, p as loadingRowClass, r as dividedClass, s as itemDisabledClass, u as itemLayoutClass } from "../../../chunks/config-D0W8mGFD.js";
8
8
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, markRaw, normalizeClass, normalizeStyle, onMounted, onUpdated, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useSlots, watch, withCtx } from "vue";
@@ -141,7 +141,7 @@ var GrList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
141
141
  virtualizer.scrollToIndex(index, align);
142
142
  }
143
143
  __expose({ scrollToIndex });
144
- if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
144
+ if (process.env.NODE_ENV !== "production") {
145
145
  watch(() => [
146
146
  props.virtual,
147
147
  dataMode.value,
@@ -263,7 +263,7 @@ var GrListItem_default = /* @__PURE__ */ defineComponent({
263
263
  function isFocusableTag(tag) {
264
264
  return tag === "button" || tag === "a" && !!props.href;
265
265
  }
266
- if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") watch(() => [isInteractive.value, props.as], ([interactive, as]) => {
266
+ if (process.env.NODE_ENV !== "production") watch(() => [isInteractive.value, props.as], ([interactive, as]) => {
267
267
  if (!interactive || typeof as !== "string" || isFocusableTag(as)) return;
268
268
  console.warn(`[granularity] GrListItem: as="${as}" не попадает в таб-порядок — строка кликается мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (\`button\`, \`a\` со ссылкой), или компонент роутера.`);
269
269
  }, { immediate: true });