@feugene/granularity 0.24.1 → 0.25.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 (290) hide show
  1. package/dist/chunks/config-BViooYNv.js.map +1 -0
  2. package/dist/chunks/config-BfsBepkH.js.map +1 -0
  3. package/dist/chunks/config-BuGlNPE92.js.map +1 -0
  4. package/dist/chunks/config-CPSxM7XE.js +8 -0
  5. package/dist/chunks/config-CPSxM7XE.js.map +1 -0
  6. package/dist/chunks/config-D7zgE_Gr.js +107 -0
  7. package/dist/chunks/config-D7zgE_Gr.js.map +1 -0
  8. package/dist/chunks/config-DAX8Deh1.js.map +1 -0
  9. package/dist/chunks/config-DGCMHPCR.js +19 -0
  10. package/dist/chunks/config-DGCMHPCR.js.map +1 -0
  11. package/dist/chunks/config-DGWFal5h.js +128 -0
  12. package/dist/chunks/config-DGWFal5h.js.map +1 -0
  13. package/dist/chunks/config-DKISz9gR.js +39 -0
  14. package/dist/chunks/config-DKISz9gR.js.map +1 -0
  15. package/dist/chunks/{config-BSyiFlbi2.js → config-DtgYuGUn2.js} +2 -1
  16. package/dist/chunks/config-DtgYuGUn2.js.map +1 -0
  17. package/dist/chunks/config-Iu684VNI.js +21 -0
  18. package/dist/chunks/config-Iu684VNI.js.map +1 -0
  19. package/dist/chunks/config-T3qdQBgU.js.map +1 -0
  20. package/dist/chunks/config-rkcKArZQ.js.map +1 -0
  21. package/dist/chunks/config-vSji8w90.js.map +1 -0
  22. package/dist/chunks/config-zjeAJ4ho2.js.map +1 -0
  23. package/dist/chunks/context-BfIQv7m12.js.map +1 -0
  24. package/dist/chunks/context-CTbuwcYs.js.map +1 -1
  25. package/dist/chunks/{en-DCCFP4qu.js → en-NpP9e7lC.js} +16 -2
  26. package/dist/chunks/en-NpP9e7lC.js.map +1 -0
  27. package/dist/chunks/{es-CT34HBVM.js → es-DaeA2Ql-.js} +16 -2
  28. package/dist/chunks/es-DaeA2Ql-.js.map +1 -0
  29. package/dist/chunks/{es-DdJsxlUr.js → es-Do2CNrC2.js} +3 -3
  30. package/dist/chunks/{es-DdJsxlUr.js.map → es-Do2CNrC2.js.map} +1 -1
  31. package/dist/chunks/{generated-1WEi10KK.js → generated-CJe6hdiE.js} +49 -0
  32. package/dist/chunks/{generated-1WEi10KK.js.map → generated-CJe6hdiE.js.map} +1 -1
  33. package/dist/chunks/{grBadgeStyles-C2wLlkjF.js → grBadgeStyles-C1S56Alo.js} +6 -1
  34. package/dist/chunks/{grBadgeStyles-C2wLlkjF.js.map → grBadgeStyles-C1S56Alo.js.map} +1 -1
  35. package/dist/chunks/grChipGroupContext-DZXkACap.js +4 -0
  36. package/dist/chunks/grChipGroupContext-DZXkACap.js.map +1 -0
  37. package/dist/chunks/{config-C5mO5DYZ.js → grDropdownMenuStyles-D1LOkdVj.js} +1 -38
  38. package/dist/chunks/grDropdownMenuStyles-D1LOkdVj.js.map +1 -0
  39. package/dist/chunks/{grPopoverStyles-Rz_Z5nLW.js → grPopoverStyles-Cke_BZsM.js} +16 -3
  40. package/dist/chunks/grPopoverStyles-Cke_BZsM.js.map +1 -0
  41. package/dist/chunks/{granular-provider-Dr2PKgl1.js → granular-provider-BaHkLjFX.js} +19 -11
  42. package/dist/chunks/granular-provider-BaHkLjFX.js.map +1 -0
  43. package/dist/chunks/{ru-CLiAMwrx.js → ru-Bg9-DPmg.js} +16 -2
  44. package/dist/chunks/ru-Bg9-DPmg.js.map +1 -0
  45. package/dist/chunks/{config-qVg3Zz_A.js → safelist-DOfCQR5O.js} +2 -19
  46. package/dist/chunks/safelist-DOfCQR5O.js.map +1 -0
  47. package/dist/chunks/useMenuItemsFocus-OyOnIAlr.js +117 -0
  48. package/dist/chunks/useMenuItemsFocus-OyOnIAlr.js.map +1 -0
  49. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-DsevPtiS.js → GrAutocomplete-D9c9BNFg.js} +1 -1
  50. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-DsevPtiS.js.map → GrAutocomplete-D9c9BNFg.js.map} +1 -1
  51. package/dist/components/GrAutocomplete/index.js +1 -1
  52. package/dist/components/GrBadge/chunks/{GrBadge-u2ZdS7CN.js → GrBadge-_9rCHuE1.js} +1 -1
  53. package/dist/components/GrBadge/chunks/{GrBadge-u2ZdS7CN.js.map → GrBadge-_9rCHuE1.js.map} +1 -1
  54. package/dist/components/GrBadge/index.js +3 -2
  55. package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js → GrButton-DCz-Wn-V.js} +1 -1
  56. package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js.map → GrButton-DCz-Wn-V.js.map} +1 -1
  57. package/dist/components/GrButton/index.js +1 -1
  58. package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CKyiAV8K.js → GrButtonGroup-BQo5VQ_c.js} +1 -1
  59. package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CKyiAV8K.js.map → GrButtonGroup-BQo5VQ_c.js.map} +1 -1
  60. package/dist/components/GrButtonGroup/index.js +2 -2
  61. package/dist/components/GrCard/chunks/{GrCard-gI2wA8ZZ.js → GrCard-aQIjk6I9.js} +1 -1
  62. package/dist/components/GrCard/chunks/GrCard-aQIjk6I9.js.map +1 -0
  63. package/dist/components/GrCard/index.js +1 -1
  64. package/dist/components/GrCheckboxGroup/index.js +1 -1
  65. package/dist/components/GrChip/chunks/GrChip-BnL2FedO.js +222 -0
  66. package/dist/components/GrChip/chunks/GrChip-BnL2FedO.js.map +1 -0
  67. package/dist/components/GrChip/index.js +3 -0
  68. package/dist/components/GrChipGroup/chunks/GrChipGroup-DefmSdY9.js +209 -0
  69. package/dist/components/GrChipGroup/chunks/GrChipGroup-DefmSdY9.js.map +1 -0
  70. package/dist/components/GrChipGroup/index.js +3 -0
  71. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-BmXCLgLS.js → GrCodeBlock-CyptiEo4.js} +1 -1
  72. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-BmXCLgLS.js.map → GrCodeBlock-CyptiEo4.js.map} +1 -1
  73. package/dist/components/GrCodeBlock/index.js +1 -1
  74. package/dist/components/GrCollapse/chunks/{GrCollapse-DIHEiTju.js → GrCollapse-DUROgAgc.js} +1 -1
  75. package/dist/components/GrCollapse/chunks/{GrCollapse-DIHEiTju.js.map → GrCollapse-DUROgAgc.js.map} +1 -1
  76. package/dist/components/GrCollapse/index.js +1 -1
  77. package/dist/components/GrColorPicker/chunks/{GrColorPicker-qQdoEPHF.js → GrColorPicker-CDaJRVIM.js} +1 -1
  78. package/dist/components/GrColorPicker/chunks/{GrColorPicker-qQdoEPHF.js.map → GrColorPicker-CDaJRVIM.js.map} +1 -1
  79. package/dist/components/GrColorPicker/index.js +1 -1
  80. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BFcMf4Pn.js → GrCommandPalette-BPsVN_XU.js} +3 -3
  81. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BPsVN_XU.js.map +1 -0
  82. package/dist/components/GrCommandPalette/index.js +1 -1
  83. package/dist/components/GrCommandPalette/styles.css +1 -1
  84. package/dist/components/GrConfigProvider/chunks/{GrConfigProvider-UP-EaoAf.js → GrConfigProvider-BF7R3hgg.js} +1 -1
  85. package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BF7R3hgg.js.map +1 -0
  86. package/dist/components/GrConfigProvider/index.js +1 -1
  87. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-rRtMScJX.js → GrConfirmDialog-CwXNLyzk.js} +3 -3
  88. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-rRtMScJX.js.map → GrConfirmDialog-CwXNLyzk.js.map} +1 -1
  89. package/dist/components/GrConfirmDialog/index.js +1 -1
  90. package/dist/components/GrContextMenu/chunks/GrContextMenu-BbAJholy.js +331 -0
  91. package/dist/components/GrContextMenu/chunks/GrContextMenu-BbAJholy.js.map +1 -0
  92. package/dist/components/GrContextMenu/index.js +3 -0
  93. package/dist/components/GrDataTable/chunks/{GrDataTable-B5MmZFUg.js → GrDataTable-NRbrXpdx.js} +2 -2
  94. package/dist/components/GrDataTable/chunks/GrDataTable-NRbrXpdx.js.map +1 -0
  95. package/dist/components/GrDataTable/index.js +1 -1
  96. package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-C6Pxjjbh.js → GrDescriptionList-1mICfrqX.js} +1 -1
  97. package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-C6Pxjjbh.js.map → GrDescriptionList-1mICfrqX.js.map} +1 -1
  98. package/dist/components/GrDescriptionList/index.js +2 -2
  99. package/dist/components/GrDialog/chunks/{GrDialog-D0U5iRqV.js → GrDialog-5Y5K87ss.js} +2 -2
  100. package/dist/components/GrDialog/chunks/{GrDialog-D0U5iRqV.js.map → GrDialog-5Y5K87ss.js.map} +1 -1
  101. package/dist/components/GrDialog/index.js +1 -1
  102. package/dist/components/GrDialogService/chunks/{GrDialogService-CrwA4aJq.js → GrDialogService-CHhzGRWy.js} +6 -6
  103. package/dist/components/GrDialogService/chunks/GrDialogService-CHhzGRWy.js.map +1 -0
  104. package/dist/components/GrDialogService/index.js +1 -1
  105. package/dist/components/GrDrawer/chunks/{GrDrawer-C3zAg2tG.js → GrDrawer-CXy3-SVd.js} +2 -2
  106. package/dist/components/GrDrawer/chunks/GrDrawer-CXy3-SVd.js.map +1 -0
  107. package/dist/components/GrDrawer/index.js +1 -1
  108. package/dist/components/GrDropdown/chunks/{GrDropdown-BSfFOcDx.js → GrDropdown-BfTmdPyh.js} +10 -79
  109. package/dist/components/GrDropdown/chunks/GrDropdown-BfTmdPyh.js.map +1 -0
  110. package/dist/components/GrDropdown/index.js +2 -2
  111. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-l5HEqzjr.js +130 -0
  112. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-l5HEqzjr.js.map +1 -0
  113. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-DyS6dWDp.js → GrDropdownMenuList-BGA_xNVo.js} +57 -159
  114. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-BGA_xNVo.js.map +1 -0
  115. package/dist/components/GrDropdownMenu/index.js +3 -2
  116. package/dist/components/GrEmptyState/chunks/{GrEmptyState-BGUX8O0U.js → GrEmptyState-CS9do8eM.js} +1 -1
  117. package/dist/components/GrEmptyState/chunks/{GrEmptyState-BGUX8O0U.js.map → GrEmptyState-CS9do8eM.js.map} +1 -1
  118. package/dist/components/GrEmptyState/index.js +2 -2
  119. package/dist/components/GrFileUpload/chunks/{GrFileUpload-D3-vC-ap.js → GrFileUpload-wNXzHb5N.js} +1 -1
  120. package/dist/components/GrFileUpload/chunks/GrFileUpload-wNXzHb5N.js.map +1 -0
  121. package/dist/components/GrFileUpload/index.js +1 -1
  122. package/dist/components/GrFormField/chunks/{GrFormField-kqfVY30N.js → GrFormField-DSylU7IU.js} +1 -1
  123. package/dist/components/GrFormField/chunks/GrFormField-DSylU7IU.js.map +1 -0
  124. package/dist/components/GrFormField/index.js +1 -1
  125. package/dist/components/GrFormFile/chunks/{GrFormFile-D722xHn8.js → GrFormFile-DhVkB2gm.js} +1 -1
  126. package/dist/components/GrFormFile/chunks/{GrFormFile-D722xHn8.js.map → GrFormFile-DhVkB2gm.js.map} +1 -1
  127. package/dist/components/GrFormFile/index.js +1 -1
  128. package/dist/components/GrInputTag/chunks/{GrInputTag-C7QVPijj.js → GrInputTag-UI8kIB_F.js} +2 -2
  129. package/dist/components/GrInputTag/chunks/{GrInputTag-C7QVPijj.js.map → GrInputTag-UI8kIB_F.js.map} +1 -1
  130. package/dist/components/GrInputTag/index.js +1 -1
  131. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-DFUppRNb.js → GrJsonViewer-jk3ylOTc.js} +2 -2
  132. package/dist/components/GrJsonViewer/chunks/GrJsonViewer-jk3ylOTc.js.map +1 -0
  133. package/dist/components/GrJsonViewer/index.js +1 -1
  134. package/dist/components/GrList/chunks/{GrList-DAtzVIHh.js → GrList-B5bclmHV.js} +30 -3
  135. package/dist/components/GrList/chunks/GrList-B5bclmHV.js.map +1 -0
  136. package/dist/components/GrList/index.js +1 -1
  137. package/dist/components/GrList/styles.css +1 -1
  138. package/dist/components/GrModal/chunks/{GrModal-CPz7SWD_.js → GrModal-CCoWZuIl.js} +2 -2
  139. package/dist/components/GrModal/chunks/GrModal-CCoWZuIl.js.map +1 -0
  140. package/dist/components/GrModal/index.js +1 -1
  141. package/dist/components/GrNavbar/chunks/{GrNavbar-DWaaoV7n.js → GrNavbar-DPj30sHX.js} +1 -1
  142. package/dist/components/GrNavbar/chunks/{GrNavbar-DWaaoV7n.js.map → GrNavbar-DPj30sHX.js.map} +1 -1
  143. package/dist/components/GrNavbar/index.js +1 -1
  144. package/dist/components/GrNumberInput/chunks/{GrNumberInput-BotnzW77.js → GrNumberInput-V2mZvDDC.js} +1 -1
  145. package/dist/components/GrNumberInput/chunks/{GrNumberInput-BotnzW77.js.map → GrNumberInput-V2mZvDDC.js.map} +1 -1
  146. package/dist/components/GrNumberInput/index.js +2 -2
  147. package/dist/components/GrPagination/chunks/{GrPagination-GtGtSMJG.js → GrPagination-DG7kp6VL.js} +2 -2
  148. package/dist/components/GrPagination/chunks/{GrPagination-GtGtSMJG.js.map → GrPagination-DG7kp6VL.js.map} +1 -1
  149. package/dist/components/GrPagination/index.js +1 -1
  150. package/dist/components/GrPopover/chunks/{GrPopover-b70bIlQ9.js → GrPopover-DDChM1jD.js} +28 -10
  151. package/dist/components/GrPopover/chunks/GrPopover-DDChM1jD.js.map +1 -0
  152. package/dist/components/GrPopover/index.js +2 -2
  153. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CEI8bf-t.js → GrPromptDialog-DjF-5oD9.js} +4 -4
  154. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CEI8bf-t.js.map → GrPromptDialog-DjF-5oD9.js.map} +1 -1
  155. package/dist/components/GrPromptDialog/index.js +1 -1
  156. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BVmjKXcv.js → GrRadioGroup-7w4bXIg_.js} +1 -1
  157. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BVmjKXcv.js.map → GrRadioGroup-7w4bXIg_.js.map} +1 -1
  158. package/dist/components/GrRadioGroup/index.js +1 -1
  159. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CZONq8uT.js → GrResponseErrorBanner-BhP-xw2P.js} +1 -1
  160. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CZONq8uT.js.map → GrResponseErrorBanner-BhP-xw2P.js.map} +1 -1
  161. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CgfAwlg3.js → GrResponseErrorBanner-DA4q90Zt.js} +1 -1
  162. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CgfAwlg3.js.map → GrResponseErrorBanner-DA4q90Zt.js.map} +1 -1
  163. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  164. package/dist/components/GrSelect/chunks/{GrSelect-CaGuyvtb.js → GrSelect-BA1wfX3c.js} +19 -3
  165. package/dist/components/GrSelect/chunks/GrSelect-BA1wfX3c.js.map +1 -0
  166. package/dist/components/GrSelect/index.js +1 -1
  167. package/dist/components/GrSelect/styles.css +1 -1
  168. package/dist/components/GrSidebar/chunks/{GrSidebar-D7WxRtIu.js → GrSidebar-DY-LgIbS.js} +2 -2
  169. package/dist/components/GrSidebar/chunks/{GrSidebar-D7WxRtIu.js.map → GrSidebar-DY-LgIbS.js.map} +1 -1
  170. package/dist/components/GrSidebar/index.js +2 -2
  171. package/dist/components/GrSlider/index.js +1 -1
  172. package/dist/components/GrSortableList/chunks/{GrSortableList-BmcUVAMb.js → GrSortableList-BMXyxmRc.js} +3 -3
  173. package/dist/components/GrSortableList/chunks/{GrSortableList-BmcUVAMb.js.map → GrSortableList-BMXyxmRc.js.map} +1 -1
  174. package/dist/components/GrSortableList/index.js +2 -2
  175. package/dist/components/GrSteps/chunks/GrSteps-Cce4rfr3.js +282 -0
  176. package/dist/components/GrSteps/chunks/GrSteps-Cce4rfr3.js.map +1 -0
  177. package/dist/components/GrSteps/index.js +3 -0
  178. package/dist/components/GrTabPanels/chunks/{GrTabPanels-AJt1w1XC.js → GrTabPanels-BZQS8-JM.js} +12 -1
  179. package/dist/components/GrTabPanels/chunks/{GrTabPanels-AJt1w1XC.js.map → GrTabPanels-BZQS8-JM.js.map} +1 -1
  180. package/dist/components/GrTabPanels/index.js +1 -1
  181. package/dist/components/GrTimeline/chunks/{GrTimeline-Ch9sHzwL.js → GrTimeline-DIDdkTn7.js} +12 -1
  182. package/dist/components/GrTimeline/chunks/GrTimeline-DIDdkTn7.js.map +1 -0
  183. package/dist/components/GrTimeline/index.js +1 -1
  184. package/dist/components/GrToaster/chunks/{GrToaster-DJUzx3sR.js → GrToaster-CC49q3a4.js} +1 -1
  185. package/dist/components/GrToaster/chunks/{GrToaster-DJUzx3sR.js.map → GrToaster-CC49q3a4.js.map} +1 -1
  186. package/dist/components/GrToaster/index.js +1 -1
  187. package/dist/components/GrTree/chunks/{GrTree-BvOeQ3xJ.js.map → GrTree-J5q8aCya.js.map} +1 -1
  188. package/dist/components/GrTree/index.js +1 -1
  189. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-_LjTRR7X.js → GrTreeSelect-DEfxWAc3.js} +1 -1
  190. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-_LjTRR7X.js.map → GrTreeSelect-DEfxWAc3.js.map} +1 -1
  191. package/dist/components/GrTreeSelect/index.js +1 -1
  192. package/dist/composables/useFloating.js +47 -7
  193. package/dist/composables/useFloating.js.map +1 -1
  194. package/dist/composables/useTheme.js.map +1 -1
  195. package/dist/composables/useToast.js.map +1 -1
  196. package/dist/granular-provider-node.js +1 -1
  197. package/dist/granular-provider.js +1 -1
  198. package/dist/i18n/all.js +1 -1
  199. package/dist/i18n/index.js +1 -1
  200. package/dist/index.js +66 -56
  201. package/dist/theme.js +1 -1
  202. package/dist/tokens.js +1 -1
  203. package/dist/types/components/GrBadge/grBadgeStyles.d.ts +7 -0
  204. package/dist/types/components/GrCard/GrCard.vue.d.ts +1 -1
  205. package/dist/types/components/GrChip/GrChip.vue.d.ts +118 -0
  206. package/dist/types/components/GrChip/config.d.ts +1 -0
  207. package/dist/types/components/GrChip/defaults.d.ts +16 -0
  208. package/dist/types/components/GrChip/grChipGroupContext.d.ts +52 -0
  209. package/dist/types/components/GrChip/grChipStyles.d.ts +64 -0
  210. package/dist/types/components/GrChip/index.d.ts +11 -0
  211. package/dist/types/components/GrChip/safelist.d.ts +7 -0
  212. package/dist/types/components/GrChipGroup/GrChipGroup.vue.d.ts +128 -0
  213. package/dist/types/components/GrChipGroup/config.d.ts +1 -0
  214. package/dist/types/components/GrChipGroup/defaults.d.ts +13 -0
  215. package/dist/types/components/GrChipGroup/grChipGroupStyles.d.ts +7 -0
  216. package/dist/types/components/GrChipGroup/index.d.ts +9 -0
  217. package/dist/types/components/GrChipGroup/safelist.d.ts +1 -0
  218. package/dist/types/components/GrContextMenu/GrContextMenu.vue.d.ts +157 -0
  219. package/dist/types/components/GrContextMenu/config.d.ts +1 -0
  220. package/dist/types/components/GrContextMenu/contextMenuAnchor.d.ts +29 -0
  221. package/dist/types/components/GrContextMenu/index.d.ts +7 -0
  222. package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +1 -1
  223. package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +3 -3
  224. package/dist/types/components/GrDropdownMenu/GrDropdownMenuEntries.vue.d.ts +22 -0
  225. package/dist/types/components/GrFilePreview/GrFilePreview.vue.d.ts +1 -1
  226. package/dist/types/components/GrList/GrListItem.vue.d.ts +1 -1
  227. package/dist/types/components/GrPopover/GrPopover.vue.d.ts +21 -2
  228. package/dist/types/components/GrPopover/grPopoverStyles.d.ts +11 -1
  229. package/dist/types/components/GrPopover/index.d.ts +1 -1
  230. package/dist/types/components/GrStatistic/GrStatistic.vue.d.ts +1 -1
  231. package/dist/types/components/GrSteps/GrSteps.vue.d.ts +118 -0
  232. package/dist/types/components/GrSteps/config.d.ts +1 -0
  233. package/dist/types/components/GrSteps/defaults.d.ts +18 -0
  234. package/dist/types/components/GrSteps/grStepsStyles.d.ts +63 -0
  235. package/dist/types/components/GrSteps/index.d.ts +11 -0
  236. package/dist/types/components/GrSteps/safelist.d.ts +1 -0
  237. package/dist/types/components/GrSteps/stepsModel.d.ts +57 -0
  238. package/dist/types/composables/internal/useMenuItemsFocus.d.ts +24 -0
  239. package/dist/types/composables/internal/useTypeahead.d.ts +30 -0
  240. package/dist/types/composables/useFloating.d.ts +33 -2
  241. package/dist/types/granular-provider/shared.d.ts +4 -0
  242. package/dist/types/i18n/messages/en.d.ts +26 -0
  243. package/dist/types/i18n/messages/es.d.ts +26 -0
  244. package/dist/types/i18n/messages/ru.d.ts +26 -0
  245. package/dist/types/index.d.ts +4 -0
  246. package/dist/web-types.json +790 -241
  247. package/package.json +18 -2
  248. package/dist/chunks/config-BSyiFlbi2.js.map +0 -1
  249. package/dist/chunks/config-BViooYNv2.js.map +0 -1
  250. package/dist/chunks/config-BfsBepkH2.js.map +0 -1
  251. package/dist/chunks/config-BuGlNPE9.js.map +0 -1
  252. package/dist/chunks/config-C5mO5DYZ.js.map +0 -1
  253. package/dist/chunks/config-DAX8Deh12.js.map +0 -1
  254. package/dist/chunks/config-T3qdQBgU2.js.map +0 -1
  255. package/dist/chunks/config-qVg3Zz_A.js.map +0 -1
  256. package/dist/chunks/config-rkcKArZQ2.js.map +0 -1
  257. package/dist/chunks/config-vSji8w902.js.map +0 -1
  258. package/dist/chunks/config-zjeAJ4ho.js.map +0 -1
  259. package/dist/chunks/context-BfIQv7m1.js.map +0 -1
  260. package/dist/chunks/en-DCCFP4qu.js.map +0 -1
  261. package/dist/chunks/es-CT34HBVM.js.map +0 -1
  262. package/dist/chunks/grPopoverStyles-Rz_Z5nLW.js.map +0 -1
  263. package/dist/chunks/granular-provider-Dr2PKgl1.js.map +0 -1
  264. package/dist/chunks/ru-CLiAMwrx.js.map +0 -1
  265. package/dist/components/GrCard/chunks/GrCard-gI2wA8ZZ.js.map +0 -1
  266. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BFcMf4Pn.js.map +0 -1
  267. package/dist/components/GrConfigProvider/chunks/GrConfigProvider-UP-EaoAf.js.map +0 -1
  268. package/dist/components/GrDataTable/chunks/GrDataTable-B5MmZFUg.js.map +0 -1
  269. package/dist/components/GrDialogService/chunks/GrDialogService-CrwA4aJq.js.map +0 -1
  270. package/dist/components/GrDrawer/chunks/GrDrawer-C3zAg2tG.js.map +0 -1
  271. package/dist/components/GrDropdown/chunks/GrDropdown-BSfFOcDx.js.map +0 -1
  272. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-DyS6dWDp.js.map +0 -1
  273. package/dist/components/GrFileUpload/chunks/GrFileUpload-D3-vC-ap.js.map +0 -1
  274. package/dist/components/GrFormField/chunks/GrFormField-kqfVY30N.js.map +0 -1
  275. package/dist/components/GrJsonViewer/chunks/GrJsonViewer-DFUppRNb.js.map +0 -1
  276. package/dist/components/GrList/chunks/GrList-DAtzVIHh.js.map +0 -1
  277. package/dist/components/GrModal/chunks/GrModal-CPz7SWD_.js.map +0 -1
  278. package/dist/components/GrPopover/chunks/GrPopover-b70bIlQ9.js.map +0 -1
  279. package/dist/components/GrSelect/chunks/GrSelect-CaGuyvtb.js.map +0 -1
  280. package/dist/components/GrTimeline/chunks/GrTimeline-Ch9sHzwL.js.map +0 -1
  281. package/dist/chunks/{config-BViooYNv2.js → config-BViooYNv.js} +0 -0
  282. package/dist/chunks/{config-BfsBepkH2.js → config-BfsBepkH.js} +0 -0
  283. package/dist/chunks/{config-BuGlNPE9.js → config-BuGlNPE92.js} +0 -0
  284. package/dist/chunks/{config-DAX8Deh12.js → config-DAX8Deh1.js} +0 -0
  285. package/dist/chunks/{config-T3qdQBgU2.js → config-T3qdQBgU.js} +0 -0
  286. package/dist/chunks/{config-rkcKArZQ2.js → config-rkcKArZQ.js} +0 -0
  287. package/dist/chunks/{config-vSji8w902.js → config-vSji8w90.js} +0 -0
  288. package/dist/chunks/{config-zjeAJ4ho.js → config-zjeAJ4ho2.js} +0 -0
  289. package/dist/chunks/{context-BfIQv7m1.js → context-BfIQv7m12.js} +0 -0
  290. package/dist/components/GrTree/chunks/{GrTree-BvOeQ3xJ.js → GrTree-J5q8aCya.js} +1 -1
@@ -0,0 +1,209 @@
1
+ import { a as useGrComponentSize } from "../../../chunks/context-CTbuwcYs.js";
2
+ import { n as useGrFormFieldContext } from "../../../chunks/context-DdNIxa1w.js";
3
+ import { useGrFormControl } from "../../../composables/useGrFormControl.js";
4
+ import { t as useFocusWithin } from "../../../chunks/useFocusWithin-BwHE0rzm.js";
5
+ import { t as GR_CHIP_GROUP_CONTEXT } from "../../../chunks/grChipGroupContext-DZXkACap.js";
6
+ import { useRovingFocus } from "../../../composables/useRovingFocus.js";
7
+ import { r as chipGroupRootClass } from "../../../chunks/config-Iu684VNI.js";
8
+ import { Fragment, computed, createElementBlock, defineComponent, nextTick, normalizeClass, openBlock, provide, ref, renderList, renderSlot, unref } from "vue";
9
+ //#region src/components/GrChipGroup/GrChipGroup.vue?vue&type=script&setup=true&lang.ts
10
+ var _hoisted_1 = [
11
+ "id",
12
+ "role",
13
+ "aria-multiselectable",
14
+ "aria-label",
15
+ "aria-labelledby",
16
+ "aria-describedby",
17
+ "aria-invalid",
18
+ "aria-required",
19
+ "aria-readonly",
20
+ "aria-disabled"
21
+ ];
22
+ var _hoisted_2 = ["name", "value"];
23
+ //#endregion
24
+ //#region src/components/GrChipGroup/GrChipGroup.vue
25
+ var GrChipGroup_default = /* @__PURE__ */ defineComponent({
26
+ __name: "GrChipGroup",
27
+ props: {
28
+ modelValue: {
29
+ type: [
30
+ String,
31
+ Number,
32
+ Boolean,
33
+ Array,
34
+ null
35
+ ],
36
+ default: void 0
37
+ },
38
+ selection: { default: "multiple" },
39
+ name: { default: void 0 },
40
+ disabled: {
41
+ type: Boolean,
42
+ default: false
43
+ },
44
+ readonly: {
45
+ type: Boolean,
46
+ default: false
47
+ },
48
+ invalid: {
49
+ type: Boolean,
50
+ default: false
51
+ },
52
+ required: {
53
+ type: Boolean,
54
+ default: false
55
+ },
56
+ closable: {
57
+ type: Boolean,
58
+ default: false
59
+ },
60
+ size: { default: void 0 },
61
+ tone: { default: void 0 },
62
+ radius: { default: void 0 },
63
+ dark: {
64
+ type: Boolean,
65
+ default: void 0
66
+ },
67
+ ariaLabel: { default: void 0 }
68
+ },
69
+ emits: [
70
+ "update:modelValue",
71
+ "change",
72
+ "remove",
73
+ "focus",
74
+ "blur"
75
+ ],
76
+ setup(__props, { expose: __expose, emit: __emit }) {
77
+ const props = __props;
78
+ const emit = __emit;
79
+ const resolvedSize = useGrComponentSize(() => props.size, { component: "GrChipGroup" });
80
+ const field = useGrFormFieldContext();
81
+ const fieldId = computed(() => field?.id.value);
82
+ const describedBy = computed(() => field?.describedById.value);
83
+ const labelledBy = computed(() => props.ariaLabel ? void 0 : field?.labelId.value);
84
+ const { disabled: isDisabled, invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props);
85
+ const isMultiple = computed(() => props.selection === "multiple");
86
+ const selectedValues = computed(() => {
87
+ if (props.modelValue === void 0 || props.modelValue === null) return [];
88
+ return Array.isArray(props.modelValue) ? [...props.modelValue] : [props.modelValue];
89
+ });
90
+ function isSelected(value) {
91
+ return selectedValues.value.includes(value);
92
+ }
93
+ function commit(next) {
94
+ emit("update:modelValue", next);
95
+ emit("change", next);
96
+ }
97
+ /**
98
+ * В одиночном режиме повторный выбор снимает отметку.
99
+ *
100
+ * Так ведут себя фильтры: набор без выбранного значения осмыслен («любой»), и
101
+ * отменить выбор иначе было бы нечем — в отличие от `radiogroup` формы, где
102
+ * пустое значение обычно запрещено.
103
+ */
104
+ function toggle(value) {
105
+ if (isDisabled.value || isReadonly.value) return;
106
+ if (!isMultiple.value) {
107
+ commit(isSelected(value) ? null : value);
108
+ return;
109
+ }
110
+ const next = selectedValues.value.filter((item) => item !== value);
111
+ if (next.length === selectedValues.value.length) next.push(value);
112
+ commit(next);
113
+ }
114
+ function requestRemove(value) {
115
+ if (isDisabled.value || isReadonly.value) return;
116
+ emit("remove", value);
117
+ }
118
+ const entries = ref([]);
119
+ function register(entry) {
120
+ entries.value.push(entry);
121
+ return () => {
122
+ const index = entries.value.indexOf(entry);
123
+ if (index >= 0) entries.value.splice(index, 1);
124
+ };
125
+ }
126
+ function entryOf(value) {
127
+ return entries.value.find((entry) => entry.value() === value);
128
+ }
129
+ /**
130
+ * Кольцо roving-фокуса. Обе оси — чипы переносятся на новую строку, и «вниз»
131
+ * означает следующий чип так же, как «вправо».
132
+ *
133
+ * Стрелка двигает только фокус, даже в одиночном режиме: у чипов есть второе
134
+ * действие (снятие по `Delete`), и переносить выбор вместе с фокусом значило бы
135
+ * менять модель при попытке дойти до нужного чипа. Тем же рассуждением живёт
136
+ * `GrTabs` с `activationMode="manual"`.
137
+ */
138
+ const roving = useRovingFocus({
139
+ items: () => entries.value.map((entry) => entry.value()),
140
+ elementFor: (value) => entryOf(value)?.el() ?? null,
141
+ isDisabled: (value) => entryOf(value)?.disabled() ?? true,
142
+ orientation: () => "both",
143
+ skipDisabled: () => true,
144
+ initialKey: () => {
145
+ return selectedValues.value.find((value) => entryOf(value) && !entryOf(value).disabled());
146
+ },
147
+ beforeFocus: () => nextTick()
148
+ });
149
+ const rootEl = ref(null);
150
+ const { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {
151
+ enter: (event) => emit("focus", event),
152
+ leave: (event) => emit("blur", event)
153
+ });
154
+ function rovingElement() {
155
+ const value = roving.rovingKey.value;
156
+ return value === void 0 ? null : entryOf(value)?.el() ?? null;
157
+ }
158
+ provide(GR_CHIP_GROUP_CONTEXT, {
159
+ selection: computed(() => props.selection),
160
+ isSelected,
161
+ toggle,
162
+ requestRemove,
163
+ disabled: isDisabled,
164
+ readonly: isReadonly,
165
+ invalid: isInvalid,
166
+ closable: computed(() => props.closable),
167
+ size: resolvedSize,
168
+ tone: computed(() => props.tone),
169
+ radius: computed(() => props.radius),
170
+ dark: computed(() => props.dark),
171
+ register,
172
+ rovingValue: roving.rovingKey,
173
+ handleNavigationKeys: roving.handleNavigationKeys
174
+ });
175
+ __expose({
176
+ focus: () => rovingElement()?.focus(),
177
+ blur: () => rovingElement()?.blur()
178
+ });
179
+ return (_ctx, _cache) => {
180
+ return openBlock(), createElementBlock("div", {
181
+ id: fieldId.value,
182
+ ref_key: "rootEl",
183
+ ref: rootEl,
184
+ "data-gr-chip-group": "",
185
+ class: normalizeClass(unref(chipGroupRootClass)),
186
+ role: isMultiple.value ? "listbox" : "radiogroup",
187
+ "aria-multiselectable": isMultiple.value ? "true" : void 0,
188
+ "aria-label": __props.ariaLabel,
189
+ "aria-labelledby": labelledBy.value,
190
+ "aria-describedby": describedBy.value,
191
+ "aria-invalid": unref(isInvalid) ? "true" : void 0,
192
+ "aria-required": unref(isRequired) ? "true" : void 0,
193
+ "aria-readonly": unref(isReadonly) ? "true" : void 0,
194
+ "aria-disabled": unref(isDisabled) ? "true" : void 0,
195
+ onFocusin: _cache[0] || (_cache[0] = (...args) => unref(onFocusIn) && unref(onFocusIn)(...args)),
196
+ onFocusout: _cache[1] || (_cache[1] = (...args) => unref(onFocusOut) && unref(onFocusOut)(...args))
197
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.name ? selectedValues.value : [], (value) => {
198
+ return openBlock(), createElementBlock("input", {
199
+ key: `hidden-${String(value)}`,
200
+ type: "hidden",
201
+ name: __props.name,
202
+ value: String(value)
203
+ }, null, 8, _hoisted_2);
204
+ }), 128)), renderSlot(_ctx.$slots, "default")], 42, _hoisted_1);
205
+ };
206
+ }
207
+ });
208
+ //#endregion
209
+ export { GrChipGroup_default as t };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrChipGroup-DefmSdY9.js","names":[],"sources":["../../../../src/components/GrChipGroup/GrChipGroup.vue","../../../../src/components/GrChipGroup/GrChipGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, provide, ref } from 'vue'\n\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\n\nimport type { GrChipEntry, GrChipSelection, GrChipValue } from '../GrChip/grChipGroupContext'\nimport { GR_CHIP_GROUP_CONTEXT } from '../GrChip/grChipGroupContext'\nimport type { GrChipRadius, GrChipSize, GrChipTone } from '../GrChip/grChipStyles'\n\nimport { chipGroupRootClass } from './grChipGroupStyles'\n\n/**\n * GrChipGroup — набор выбираемых чипов: фильтры, теги записи, быстрый выбор.\n *\n * Составной виджет: одна остановка `Tab`, внутрь попадают стрелками\n * (roving tabindex). Роль зависит от множественности выбора — `radiogroup`\n * в одиночном, `listbox` с `aria-multiselectable` во множественном; чипы\n * получают парную роль через контекст и сами её не выбирают.\n *\n * Состав группа не рисует: чипы приходят слотом, потому что у каждого своя\n * подпись и иконка. Отсюда же и `remove` — группа его ретранслирует, а снимает\n * чип потребитель, у которого лежит массив.\n */\nexport interface GrChipGroupProps {\n /** Одиночный выбор — значение, множественный — массив. */\n modelValue?: GrChipValue | GrChipValue[] | null\n selection?: GrChipSelection\n /** Имя для нативной формы. Множественный выбор отдаёт по полю на значение. */\n name?: string\n disabled?: boolean\n /** Выбор видно, но он не меняется. */\n readonly?: boolean\n invalid?: boolean\n required?: boolean\n /** Крестик у всех чипов набора. Точечно перебивается пропом самого чипа. */\n closable?: boolean\n size?: GrChipSize\n tone?: GrChipTone\n radius?: GrChipRadius\n dark?: boolean\n ariaLabel?: string\n}\n\nexport interface GrChipGroupEmits {\n (e: 'update:modelValue', value: GrChipValue | GrChipValue[] | null): void\n (e: 'change', value: GrChipValue | GrChipValue[] | null): void\n (e: 'remove', value: GrChipValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrChipGroupProps>(), {\n modelValue: undefined,\n selection: 'multiple',\n name: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n closable: false,\n // Оформление разрешается в самих чипах: группа отдаёт им своё значение как\n // «локальное», и там же оно спорит с `GrConfigProvider`.\n size: undefined,\n tone: undefined,\n radius: undefined,\n dark: undefined,\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrChipGroupEmits>()\n\ndefineSlots<{\n /** Чипы набора. */\n default?: () => unknown\n}>()\n\nconst resolvedSize = useGrComponentSize<GrChipSize>(() => props.size, { component: 'GrChipGroup' })\n\n// Группа — не labelable-элемент, поэтому имя приходит через `aria-labelledby`\n// на подпись поля, а не через `<label for>`.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst isMultiple = computed(() => props.selection === 'multiple')\n\nconst selectedValues = computed<GrChipValue[]>(() => {\n if (props.modelValue === undefined || props.modelValue === null) return []\n return Array.isArray(props.modelValue) ? [...props.modelValue] : [props.modelValue]\n})\n\nfunction isSelected(value: GrChipValue): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction commit(next: GrChipValue | GrChipValue[] | null): void {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\n/**\n * В одиночном режиме повторный выбор снимает отметку.\n *\n * Так ведут себя фильтры: набор без выбранного значения осмыслен («любой»), и\n * отменить выбор иначе было бы нечем — в отличие от `radiogroup` формы, где\n * пустое значение обычно запрещено.\n */\nfunction toggle(value: GrChipValue): void {\n if (isDisabled.value || isReadonly.value) return\n\n if (!isMultiple.value) {\n commit(isSelected(value) ? null : value)\n return\n }\n\n const next = selectedValues.value.filter(item => item !== value)\n if (next.length === selectedValues.value.length) next.push(value)\n commit(next)\n}\n\nfunction requestRemove(value: GrChipValue): void {\n if (isDisabled.value || isReadonly.value) return\n emit('remove', value)\n}\n\nconst entries = ref<GrChipEntry[]>([])\n\nfunction register(entry: GrChipEntry): () => void {\n entries.value.push(entry)\n return () => {\n const index = entries.value.indexOf(entry)\n if (index >= 0) entries.value.splice(index, 1)\n }\n}\n\nfunction entryOf(value: GrChipValue): GrChipEntry | undefined {\n return entries.value.find(entry => entry.value() === value)\n}\n\n/**\n * Кольцо roving-фокуса. Обе оси — чипы переносятся на новую строку, и «вниз»\n * означает следующий чип так же, как «вправо».\n *\n * Стрелка двигает только фокус, даже в одиночном режиме: у чипов есть второе\n * действие (снятие по `Delete`), и переносить выбор вместе с фокусом значило бы\n * менять модель при попытке дойти до нужного чипа. Тем же рассуждением живёт\n * `GrTabs` с `activationMode=\"manual\"`.\n */\nconst roving = useRovingFocus<GrChipValue>({\n items: () => entries.value.map(entry => entry.value()),\n elementFor: value => entryOf(value)?.el() ?? null,\n isDisabled: value => entryOf(value)?.disabled() ?? true,\n orientation: () => 'both',\n skipDisabled: () => true,\n initialKey: () => {\n const first = selectedValues.value.find(value => entryOf(value) && !entryOf(value)!.disabled())\n return first\n },\n // Снятие чипа перерисовывает набор: без ожидания фокус уехал бы на узел,\n // которого уже нет.\n beforeFocus: () => nextTick(),\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// Фокус ходит между чипами: без границы каждая стрелка давала бы потребителю\n// пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nfunction rovingElement(): HTMLElement | null {\n const value = roving.rovingKey.value\n return value === undefined ? null : entryOf(value)?.el() ?? null\n}\n\nprovide(GR_CHIP_GROUP_CONTEXT, {\n selection: computed(() => props.selection),\n isSelected,\n toggle,\n requestRemove,\n disabled: isDisabled,\n readonly: isReadonly,\n invalid: isInvalid,\n closable: computed(() => props.closable),\n size: resolvedSize,\n tone: computed(() => props.tone),\n radius: computed(() => props.radius),\n dark: computed(() => props.dark),\n register,\n rovingValue: roving.rovingKey,\n handleNavigationKeys: roving.handleNavigationKeys,\n})\n\ndefineExpose({\n focus: () => rovingElement()?.focus(),\n blur: () => rovingElement()?.blur(),\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootEl\"\n data-gr-chip-group\n :class=\"chipGroupRootClass\"\n :role=\"isMultiple ? 'listbox' : 'radiogroup'\"\n :aria-multiselectable=\"isMultiple ? 'true' : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!--\n Значение уходит в форму скрытыми полями рядом с чипами, а не внутри них:\n внутрь роли-виджета нельзя вкладывать интерактивное, и скрытый `<input>`\n исключением не является.\n -->\n <input\n v-for=\"value in (name ? selectedValues : [])\"\n :key=\"`hidden-${String(value)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, provide, ref } from 'vue'\n\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\n\nimport type { GrChipEntry, GrChipSelection, GrChipValue } from '../GrChip/grChipGroupContext'\nimport { GR_CHIP_GROUP_CONTEXT } from '../GrChip/grChipGroupContext'\nimport type { GrChipRadius, GrChipSize, GrChipTone } from '../GrChip/grChipStyles'\n\nimport { chipGroupRootClass } from './grChipGroupStyles'\n\n/**\n * GrChipGroup — набор выбираемых чипов: фильтры, теги записи, быстрый выбор.\n *\n * Составной виджет: одна остановка `Tab`, внутрь попадают стрелками\n * (roving tabindex). Роль зависит от множественности выбора — `radiogroup`\n * в одиночном, `listbox` с `aria-multiselectable` во множественном; чипы\n * получают парную роль через контекст и сами её не выбирают.\n *\n * Состав группа не рисует: чипы приходят слотом, потому что у каждого своя\n * подпись и иконка. Отсюда же и `remove` — группа его ретранслирует, а снимает\n * чип потребитель, у которого лежит массив.\n */\nexport interface GrChipGroupProps {\n /** Одиночный выбор — значение, множественный — массив. */\n modelValue?: GrChipValue | GrChipValue[] | null\n selection?: GrChipSelection\n /** Имя для нативной формы. Множественный выбор отдаёт по полю на значение. */\n name?: string\n disabled?: boolean\n /** Выбор видно, но он не меняется. */\n readonly?: boolean\n invalid?: boolean\n required?: boolean\n /** Крестик у всех чипов набора. Точечно перебивается пропом самого чипа. */\n closable?: boolean\n size?: GrChipSize\n tone?: GrChipTone\n radius?: GrChipRadius\n dark?: boolean\n ariaLabel?: string\n}\n\nexport interface GrChipGroupEmits {\n (e: 'update:modelValue', value: GrChipValue | GrChipValue[] | null): void\n (e: 'change', value: GrChipValue | GrChipValue[] | null): void\n (e: 'remove', value: GrChipValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrChipGroupProps>(), {\n modelValue: undefined,\n selection: 'multiple',\n name: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n closable: false,\n // Оформление разрешается в самих чипах: группа отдаёт им своё значение как\n // «локальное», и там же оно спорит с `GrConfigProvider`.\n size: undefined,\n tone: undefined,\n radius: undefined,\n dark: undefined,\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrChipGroupEmits>()\n\ndefineSlots<{\n /** Чипы набора. */\n default?: () => unknown\n}>()\n\nconst resolvedSize = useGrComponentSize<GrChipSize>(() => props.size, { component: 'GrChipGroup' })\n\n// Группа — не labelable-элемент, поэтому имя приходит через `aria-labelledby`\n// на подпись поля, а не через `<label for>`.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst isMultiple = computed(() => props.selection === 'multiple')\n\nconst selectedValues = computed<GrChipValue[]>(() => {\n if (props.modelValue === undefined || props.modelValue === null) return []\n return Array.isArray(props.modelValue) ? [...props.modelValue] : [props.modelValue]\n})\n\nfunction isSelected(value: GrChipValue): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction commit(next: GrChipValue | GrChipValue[] | null): void {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\n/**\n * В одиночном режиме повторный выбор снимает отметку.\n *\n * Так ведут себя фильтры: набор без выбранного значения осмыслен («любой»), и\n * отменить выбор иначе было бы нечем — в отличие от `radiogroup` формы, где\n * пустое значение обычно запрещено.\n */\nfunction toggle(value: GrChipValue): void {\n if (isDisabled.value || isReadonly.value) return\n\n if (!isMultiple.value) {\n commit(isSelected(value) ? null : value)\n return\n }\n\n const next = selectedValues.value.filter(item => item !== value)\n if (next.length === selectedValues.value.length) next.push(value)\n commit(next)\n}\n\nfunction requestRemove(value: GrChipValue): void {\n if (isDisabled.value || isReadonly.value) return\n emit('remove', value)\n}\n\nconst entries = ref<GrChipEntry[]>([])\n\nfunction register(entry: GrChipEntry): () => void {\n entries.value.push(entry)\n return () => {\n const index = entries.value.indexOf(entry)\n if (index >= 0) entries.value.splice(index, 1)\n }\n}\n\nfunction entryOf(value: GrChipValue): GrChipEntry | undefined {\n return entries.value.find(entry => entry.value() === value)\n}\n\n/**\n * Кольцо roving-фокуса. Обе оси — чипы переносятся на новую строку, и «вниз»\n * означает следующий чип так же, как «вправо».\n *\n * Стрелка двигает только фокус, даже в одиночном режиме: у чипов есть второе\n * действие (снятие по `Delete`), и переносить выбор вместе с фокусом значило бы\n * менять модель при попытке дойти до нужного чипа. Тем же рассуждением живёт\n * `GrTabs` с `activationMode=\"manual\"`.\n */\nconst roving = useRovingFocus<GrChipValue>({\n items: () => entries.value.map(entry => entry.value()),\n elementFor: value => entryOf(value)?.el() ?? null,\n isDisabled: value => entryOf(value)?.disabled() ?? true,\n orientation: () => 'both',\n skipDisabled: () => true,\n initialKey: () => {\n const first = selectedValues.value.find(value => entryOf(value) && !entryOf(value)!.disabled())\n return first\n },\n // Снятие чипа перерисовывает набор: без ожидания фокус уехал бы на узел,\n // которого уже нет.\n beforeFocus: () => nextTick(),\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// Фокус ходит между чипами: без границы каждая стрелка давала бы потребителю\n// пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nfunction rovingElement(): HTMLElement | null {\n const value = roving.rovingKey.value\n return value === undefined ? null : entryOf(value)?.el() ?? null\n}\n\nprovide(GR_CHIP_GROUP_CONTEXT, {\n selection: computed(() => props.selection),\n isSelected,\n toggle,\n requestRemove,\n disabled: isDisabled,\n readonly: isReadonly,\n invalid: isInvalid,\n closable: computed(() => props.closable),\n size: resolvedSize,\n tone: computed(() => props.tone),\n radius: computed(() => props.radius),\n dark: computed(() => props.dark),\n register,\n rovingValue: roving.rovingKey,\n handleNavigationKeys: roving.handleNavigationKeys,\n})\n\ndefineExpose({\n focus: () => rovingElement()?.focus(),\n blur: () => rovingElement()?.blur(),\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootEl\"\n data-gr-chip-group\n :class=\"chipGroupRootClass\"\n :role=\"isMultiple ? 'listbox' : 'radiogroup'\"\n :aria-multiselectable=\"isMultiple ? 'true' : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!--\n Значение уходит в форму скрытыми полями рядом с чипами, а не внутри них:\n внутрь роли-виджета нельзя вкладывать интерактивное, и скрытый `<input>`\n исключением не является.\n -->\n <input\n v-for=\"value in (name ? selectedValues : [])\"\n :key=\"`hidden-${String(value)}`\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n\n <slot />\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { n as grChipGroupSafelist, t as grChipGroupConfig } from "../../chunks/config-Iu684VNI.js";
2
+ import { t as GrChipGroup_default } from "./chunks/GrChipGroup-DefmSdY9.js";
3
+ export { GrChipGroup_default as GrChipGroup, GrChipGroup_default as default, grChipGroupConfig, grChipGroupSafelist };
@@ -1,7 +1,7 @@
1
1
  import { a as useGrComponentSize, i as useGrComponentProp } from "../../../chunks/context-CTbuwcYs.js";
2
2
  import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
3
  import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
4
- import { t as GrButton_default } from "../../GrButton/chunks/GrButton-Bw2AH4uX.js";
4
+ import { t as GrButton_default } from "../../GrButton/chunks/GrButton-DCz-Wn-V.js";
5
5
  import { useAnnouncer } from "../../../composables/useAnnouncer.js";
6
6
  import { t as copy_default } from "../../../chunks/copy-DChChcq-.js";
7
7
  import { a as codeBlockHookClass, c as codeBlockPaddings, d as codeBlockTextSizes, f as codeBlockWrapClass, i as codeBlockGutterClass, l as codeBlockRootClass, o as codeBlockNowrapClass, p as codeTokenClass, r as codeBlockCopyClass, s as codeBlockNumberedClass, u as codeBlockScrollClass } from "../../../chunks/config-B_qi4Fma.js";
@@ -1 +1 @@
1
- {"version":3,"file":"GrCodeBlock-BmXCLgLS.js","names":[],"sources":["../../../../src/components/GrCodeBlock/serializeCode.ts","../../../../src/components/GrCodeBlock/tokenizeJson.ts","../../../../src/components/GrCodeBlock/GrCodeBlock.vue","../../../../src/components/GrCodeBlock/GrCodeBlock.vue"],"sourcesContent":["/**\n * Приведение произвольного значения к тексту для показа.\n *\n * Компонент показывает данные из БД, и они бывают любыми: `unknown`-поле ответа\n * модели, `meta` запуска, тело ошибки. Поэтому сериализация обязана пережить всё\n * — уронить страницу она права не имеет.\n */\n\n/** Маркер повторной ссылки. Показывается вместо ветки, которая уже встречалась выше. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\n/** Что печатается, когда сериализовать не удалось вовсе. */\nexport const UNSERIALIZABLE_MARKER = '[Unserializable]'\n\n/**\n * Замена значений, на которых штатный `JSON.stringify` спотыкается.\n *\n * Маркером помечается **повторная ссылка**, а не только настоящий цикл: объект,\n * положенный в дерево дважды, тоже её получит. Отличить одно от другого можно\n * лишь стеком предков, а `replacer` его не отдаёт — узнать, что обход вышел из\n * ветки, изнутри нечем. Размен осознанный: показать `[Circular]` там, где данные\n * просто переиспользуют объект, — неточность; зациклиться на настоящем цикле —\n * зависшая вкладка.\n */\nfunction createSafeReplacer(): (key: string, value: unknown) => unknown {\n const seen = new WeakSet<object>()\n\n return function safeReplacer(_key: string, value: unknown): unknown {\n // `BigInt` не имеет представления в JSON, и `stringify` на нём бросает.\n if (typeof value === 'bigint') return `${value}n`\n\n if (typeof value !== 'object' || value === null) return value\n\n if (seen.has(value)) return CIRCULAR_MARKER\n\n seen.add(value)\n return value\n }\n}\n\n/**\n * Значение → текст.\n *\n * Строка проходит как есть: это уже готовый текст, и оборачивать его в кавычки\n * значило бы показать не то, что пришло. `undefined` даёт пустую строку —\n * показывать нечего. Всё остальное сериализуется с отступом.\n */\nexport function serializeCode(value: unknown, indent = 2): string {\n if (typeof value === 'string') return value\n if (value === undefined) return ''\n\n try {\n const serialized = JSON.stringify(value, createSafeReplacer(), indent)\n // `stringify` отдаёт `undefined` на функции и символе — печатать «undefined»\n // строкой было бы враньём про содержимое.\n return serialized ?? ''\n }\n catch {\n return UNSERIALIZABLE_MARKER\n }\n}\n","/**\n * Токенизация JSON для подсветки.\n *\n * Своя, а не библиотечная: тянуть highlight.js ради четырёх ролей несоразмерно\n * — компонент показывает JSON и обычный текст, а не произвольные языки.\n *\n * Разбор посимвольный и **ничего не теряет**: пробелы, переносы и запятые тоже\n * становятся токенами. Отсюда инвариант, который держит тест: склейка `text`\n * всех токенов равна исходной строке. Любая потеря символа — сдвиг разметки.\n */\n\nexport type GrCodeTokenKind =\n /** Имя поля объекта: строка, за которой идёт двоеточие. */\n | 'key'\n | 'string'\n | 'number'\n /** `true`, `false`, `null`. */\n | 'literal'\n /** Скобки, запятые, двоеточия. */\n | 'punctuation'\n /** Пробелы, переносы и всё, что не опознано. */\n | 'plain'\n\nexport interface GrCodeToken {\n text: string\n kind: GrCodeTokenKind\n}\n\nconst PUNCTUATION = new Set(['{', '}', '[', ']', ',', ':'])\nconst LITERALS = ['true', 'false', 'null'] as const\n\n/** Строка от открывающей кавычки до закрывающей, с учётом экранирования. */\nfunction readString(source: string, start: number): number {\n let index = start + 1\n\n while (index < source.length) {\n const char = source[index]\n\n // Экранированная кавычка не закрывает строку: `\"a\\\":b\"` — одно значение,\n // а не строка плюс мусор.\n if (char === '\\\\') {\n index += 2\n continue\n }\n\n index += 1\n if (char === '\"') break\n }\n\n return index\n}\n\n/** Число целиком: знак, дробная часть, экспонента. */\nfunction readNumber(source: string, start: number): number {\n let index = start\n\n if (source[index] === '-') index += 1\n while (index < source.length && /[\\d.eE+-]/.test(source[index])) index += 1\n\n return index\n}\n\n/** Первый непробельный символ после позиции — по нему строка отличается от ключа. */\nfunction nextMeaningful(source: string, from: number): string | undefined {\n for (let index = from; index < source.length; index += 1) {\n if (!/\\s/.test(source[index])) return source[index]\n }\n\n return undefined\n}\n\nexport function tokenizeJson(source: string): GrCodeToken[] {\n const tokens: GrCodeToken[] = []\n let index = 0\n let plainStart = 0\n\n const flushPlain = (until: number): void => {\n if (until > plainStart) tokens.push({ text: source.slice(plainStart, until), kind: 'plain' })\n }\n\n while (index < source.length) {\n const char = source[index]\n\n if (char === '\"') {\n flushPlain(index)\n const end = readString(source, index)\n // Ключ от строки отличает только идущее следом двоеточие.\n const kind: GrCodeTokenKind = nextMeaningful(source, end) === ':' ? 'key' : 'string'\n tokens.push({ text: source.slice(index, end), kind })\n index = end\n plainStart = index\n continue\n }\n\n if (PUNCTUATION.has(char)) {\n flushPlain(index)\n tokens.push({ text: char, kind: 'punctuation' })\n index += 1\n plainStart = index\n continue\n }\n\n if (char === '-' || (char >= '0' && char <= '9')) {\n flushPlain(index)\n const end = readNumber(source, index)\n tokens.push({ text: source.slice(index, end), kind: 'number' })\n index = end\n plainStart = index\n continue\n }\n\n const literal = LITERALS.find(candidate => source.startsWith(candidate, index))\n if (literal) {\n flushPlain(index)\n tokens.push({ text: literal, kind: 'literal' })\n index += literal.length\n plainStart = index\n continue\n }\n\n index += 1\n }\n\n flushPlain(source.length)\n\n return tokens\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.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 { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\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\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\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\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.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 { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\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\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\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\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrCodeBlock-CyptiEo4.js","names":[],"sources":["../../../../src/components/GrCodeBlock/serializeCode.ts","../../../../src/components/GrCodeBlock/tokenizeJson.ts","../../../../src/components/GrCodeBlock/GrCodeBlock.vue","../../../../src/components/GrCodeBlock/GrCodeBlock.vue"],"sourcesContent":["/**\n * Приведение произвольного значения к тексту для показа.\n *\n * Компонент показывает данные из БД, и они бывают любыми: `unknown`-поле ответа\n * модели, `meta` запуска, тело ошибки. Поэтому сериализация обязана пережить всё\n * — уронить страницу она права не имеет.\n */\n\n/** Маркер повторной ссылки. Показывается вместо ветки, которая уже встречалась выше. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\n/** Что печатается, когда сериализовать не удалось вовсе. */\nexport const UNSERIALIZABLE_MARKER = '[Unserializable]'\n\n/**\n * Замена значений, на которых штатный `JSON.stringify` спотыкается.\n *\n * Маркером помечается **повторная ссылка**, а не только настоящий цикл: объект,\n * положенный в дерево дважды, тоже её получит. Отличить одно от другого можно\n * лишь стеком предков, а `replacer` его не отдаёт — узнать, что обход вышел из\n * ветки, изнутри нечем. Размен осознанный: показать `[Circular]` там, где данные\n * просто переиспользуют объект, — неточность; зациклиться на настоящем цикле —\n * зависшая вкладка.\n */\nfunction createSafeReplacer(): (key: string, value: unknown) => unknown {\n const seen = new WeakSet<object>()\n\n return function safeReplacer(_key: string, value: unknown): unknown {\n // `BigInt` не имеет представления в JSON, и `stringify` на нём бросает.\n if (typeof value === 'bigint') return `${value}n`\n\n if (typeof value !== 'object' || value === null) return value\n\n if (seen.has(value)) return CIRCULAR_MARKER\n\n seen.add(value)\n return value\n }\n}\n\n/**\n * Значение → текст.\n *\n * Строка проходит как есть: это уже готовый текст, и оборачивать его в кавычки\n * значило бы показать не то, что пришло. `undefined` даёт пустую строку —\n * показывать нечего. Всё остальное сериализуется с отступом.\n */\nexport function serializeCode(value: unknown, indent = 2): string {\n if (typeof value === 'string') return value\n if (value === undefined) return ''\n\n try {\n const serialized = JSON.stringify(value, createSafeReplacer(), indent)\n // `stringify` отдаёт `undefined` на функции и символе — печатать «undefined»\n // строкой было бы враньём про содержимое.\n return serialized ?? ''\n }\n catch {\n return UNSERIALIZABLE_MARKER\n }\n}\n","/**\n * Токенизация JSON для подсветки.\n *\n * Своя, а не библиотечная: тянуть highlight.js ради четырёх ролей несоразмерно\n * — компонент показывает JSON и обычный текст, а не произвольные языки.\n *\n * Разбор посимвольный и **ничего не теряет**: пробелы, переносы и запятые тоже\n * становятся токенами. Отсюда инвариант, который держит тест: склейка `text`\n * всех токенов равна исходной строке. Любая потеря символа — сдвиг разметки.\n */\n\nexport type GrCodeTokenKind =\n /** Имя поля объекта: строка, за которой идёт двоеточие. */\n | 'key'\n | 'string'\n | 'number'\n /** `true`, `false`, `null`. */\n | 'literal'\n /** Скобки, запятые, двоеточия. */\n | 'punctuation'\n /** Пробелы, переносы и всё, что не опознано. */\n | 'plain'\n\nexport interface GrCodeToken {\n text: string\n kind: GrCodeTokenKind\n}\n\nconst PUNCTUATION = new Set(['{', '}', '[', ']', ',', ':'])\nconst LITERALS = ['true', 'false', 'null'] as const\n\n/** Строка от открывающей кавычки до закрывающей, с учётом экранирования. */\nfunction readString(source: string, start: number): number {\n let index = start + 1\n\n while (index < source.length) {\n const char = source[index]\n\n // Экранированная кавычка не закрывает строку: `\"a\\\":b\"` — одно значение,\n // а не строка плюс мусор.\n if (char === '\\\\') {\n index += 2\n continue\n }\n\n index += 1\n if (char === '\"') break\n }\n\n return index\n}\n\n/** Число целиком: знак, дробная часть, экспонента. */\nfunction readNumber(source: string, start: number): number {\n let index = start\n\n if (source[index] === '-') index += 1\n while (index < source.length && /[\\d.eE+-]/.test(source[index])) index += 1\n\n return index\n}\n\n/** Первый непробельный символ после позиции — по нему строка отличается от ключа. */\nfunction nextMeaningful(source: string, from: number): string | undefined {\n for (let index = from; index < source.length; index += 1) {\n if (!/\\s/.test(source[index])) return source[index]\n }\n\n return undefined\n}\n\nexport function tokenizeJson(source: string): GrCodeToken[] {\n const tokens: GrCodeToken[] = []\n let index = 0\n let plainStart = 0\n\n const flushPlain = (until: number): void => {\n if (until > plainStart) tokens.push({ text: source.slice(plainStart, until), kind: 'plain' })\n }\n\n while (index < source.length) {\n const char = source[index]\n\n if (char === '\"') {\n flushPlain(index)\n const end = readString(source, index)\n // Ключ от строки отличает только идущее следом двоеточие.\n const kind: GrCodeTokenKind = nextMeaningful(source, end) === ':' ? 'key' : 'string'\n tokens.push({ text: source.slice(index, end), kind })\n index = end\n plainStart = index\n continue\n }\n\n if (PUNCTUATION.has(char)) {\n flushPlain(index)\n tokens.push({ text: char, kind: 'punctuation' })\n index += 1\n plainStart = index\n continue\n }\n\n if (char === '-' || (char >= '0' && char <= '9')) {\n flushPlain(index)\n const end = readNumber(source, index)\n tokens.push({ text: source.slice(index, end), kind: 'number' })\n index = end\n plainStart = index\n continue\n }\n\n const literal = LITERALS.find(candidate => source.startsWith(candidate, index))\n if (literal) {\n flushPlain(index)\n tokens.push({ text: literal, kind: 'literal' })\n index += literal.length\n plainStart = index\n continue\n }\n\n index += 1\n }\n\n flushPlain(source.length)\n\n return tokens\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.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 { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\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\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\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\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.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 { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\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\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\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\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { t as GrCodeBlock_default } from "./chunks/GrCodeBlock-BmXCLgLS.js";
1
+ import { t as GrCodeBlock_default } from "./chunks/GrCodeBlock-CyptiEo4.js";
2
2
  import { n as grCodeBlockSafelist, t as grCodeBlockConfig } from "../../chunks/config-B_qi4Fma.js";
3
3
  export { GrCodeBlock_default as GrCodeBlock, GrCodeBlock_default as default, grCodeBlockConfig, grCodeBlockSafelist };
@@ -2,7 +2,7 @@ import { a as useGrComponentSize, i as useGrComponentProp } from "../../../chunk
2
2
  import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
3
3
  import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-bQwtZtYT.js";
4
4
  import { t as chevron_down_default } from "../../../chunks/chevron-down-CQS2OG_s.js";
5
- import { t as GrCard_default } from "../../GrCard/chunks/GrCard-gI2wA8ZZ.js";
5
+ import { t as GrCard_default } from "../../GrCard/chunks/GrCard-aQIjk6I9.js";
6
6
  import { n as hasMeaningfulSlotContent } from "../../../chunks/slotNodes-DxNXpC7v.js";
7
7
  import { a as grCollapseBodyClass, c as grCollapseHeaderClass, i as collapseExtraPaddings, l as grCollapseRowClass, o as grCollapseChevronClass, r as collapseChevronSizes, s as grCollapseEmptyClass, u as grCollapseTitleClass } from "../../../chunks/config-DU2DNSRx.js";
8
8
  import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, normalizeClass, openBlock, provide, ref, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useSlots, withCtx } from "vue";