@feugene/granularity 0.31.0 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/README.md +0 -1
  2. package/dist/chunks/adapter-CAOxmn2G.js.map +1 -1
  3. package/dist/chunks/{config-BHcS5Cwm.js → config-87fszqnD.js} +1 -1
  4. package/dist/chunks/{config-BHcS5Cwm.js.map → config-87fszqnD.js.map} +1 -1
  5. package/dist/chunks/config-A-bBiotF.js.map +1 -1
  6. package/dist/chunks/config-B3J0alyY.js.map +1 -1
  7. package/dist/chunks/config-BhfNgyWL.js.map +1 -1
  8. package/dist/chunks/config-Bjn30smH.js.map +1 -1
  9. package/dist/chunks/config-BqT6PMzO.js.map +1 -1
  10. package/dist/chunks/config-C0Ea9ePP.js.map +1 -1
  11. package/dist/chunks/config-C0Eu8jpG.js.map +1 -1
  12. package/dist/chunks/config-CDat1wXf.js.map +1 -1
  13. package/dist/chunks/config-CQLNjc9X2.js.map +1 -1
  14. package/dist/chunks/config-CbEbLyDE.js.map +1 -1
  15. package/dist/chunks/{config-jyr6AIZt2.js → config-CuQmUkMw2.js} +2 -2
  16. package/dist/chunks/{config-jyr6AIZt2.js.map → config-CuQmUkMw2.js.map} +1 -1
  17. package/dist/chunks/config-DeyT9dJl.js.map +1 -1
  18. package/dist/chunks/config-DnF472Ox.js.map +1 -1
  19. package/dist/chunks/config-DwABJHEo.js.map +1 -1
  20. package/dist/chunks/{config-tiUtEn4Q.js → config-E1cTyRVV.js} +1 -1
  21. package/dist/chunks/{config-tiUtEn4Q.js.map → config-E1cTyRVV.js.map} +1 -1
  22. package/dist/chunks/config-MmPym-Z72.js.map +1 -1
  23. package/dist/chunks/config-T3-DMXYm.js.map +1 -1
  24. package/dist/chunks/config-Yy67uWHY.js.map +1 -1
  25. package/dist/chunks/config-_jbgsFpw.js.map +1 -1
  26. package/dist/chunks/config-g8-JGZ_Y.js.map +1 -1
  27. package/dist/chunks/config-mlheIMtV.js.map +1 -1
  28. package/dist/chunks/config-rLkc9Zn2.js.map +1 -1
  29. package/dist/chunks/config-uzUuosv7.js.map +1 -1
  30. package/dist/chunks/context-CTbuwcYs.js.map +1 -1
  31. package/dist/chunks/deltaTone-B5OoS1vo.js.map +1 -1
  32. package/dist/chunks/es-Do2CNrC2.js.map +1 -1
  33. package/dist/chunks/fileValidation-D5TFBhbv.js.map +1 -1
  34. package/dist/chunks/focusables-BkzK42SQ.js.map +1 -1
  35. package/dist/chunks/{generated-DJ2dJ__C.js → generated-CSyPsWtM.js} +1 -1
  36. package/dist/chunks/{generated-DJ2dJ__C.js.map → generated-CSyPsWtM.js.map} +1 -1
  37. package/dist/chunks/grBadgeStyles-D8Mz73B5.js.map +1 -1
  38. package/dist/chunks/{grButtonStyles-B_-YRE__.js → grButtonStyles-B3wRWW_i.js} +4 -4
  39. package/dist/chunks/grButtonStyles-B3wRWW_i.js.map +1 -0
  40. package/dist/chunks/grLinkStyles-FME_qMpi.js.map +1 -1
  41. package/dist/chunks/grLoadingStyles-CnQaf5ry.js.map +1 -1
  42. package/dist/chunks/{grRadioStyles-D4gnU65n.js → grRadioStyles-DGL2QLy6.js} +1 -1
  43. package/dist/chunks/{grRadioStyles-D4gnU65n.js.map → grRadioStyles-DGL2QLy6.js.map} +1 -1
  44. package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +1 -1
  45. package/dist/chunks/{grSliderStyles-B5uNxNJq.js → grSliderStyles-kaz6VXZ3.js} +9 -1
  46. package/dist/chunks/grSliderStyles-kaz6VXZ3.js.map +1 -0
  47. package/dist/chunks/{granular-provider-yJNX1g1G.js → granular-provider-Da2CJO6j.js} +3 -3
  48. package/dist/chunks/{granular-provider-yJNX1g1G.js.map → granular-provider-Da2CJO6j.js.map} +1 -1
  49. package/dist/chunks/granularityI18n-BC5zsj_d.js.map +1 -1
  50. package/dist/chunks/icon-Dag2tIMs.js.map +1 -1
  51. package/dist/chunks/inert-DIHCCn_1.js.map +1 -1
  52. package/dist/chunks/keyboard-DE2yhz_b.js.map +1 -1
  53. package/dist/chunks/numericStep-Bq2lj4ew.js.map +1 -1
  54. package/dist/chunks/optionFilter-DYw9XPPx.js.map +1 -1
  55. package/dist/chunks/overlayStack-C8cfsqXa.js.map +1 -1
  56. package/dist/chunks/portalRoot-CVhCgdG6.js.map +1 -1
  57. package/dist/chunks/renamedProp-BwcN-SJg.js.map +1 -1
  58. package/dist/chunks/{safelist-BOq5Li7p.js → safelist-DxkMj5IW.js} +1 -1
  59. package/dist/chunks/{safelist-BOq5Li7p.js.map → safelist-DxkMj5IW.js.map} +1 -1
  60. package/dist/chunks/slotNodes-DxNXpC7v.js.map +1 -1
  61. package/dist/chunks/useControlAddons-asUr10Pf.js.map +1 -1
  62. package/dist/chunks/useControlledOpen-DV3SqLTP.js.map +1 -1
  63. package/dist/chunks/useDragSort-0l47h4yb.js.map +1 -1
  64. package/dist/chunks/useFilePreviews-DnwgdyBs.js.map +1 -1
  65. package/dist/chunks/useFocusWithin-BwHE0rzm.js.map +1 -1
  66. package/dist/chunks/useModalOverlay-Cv5hF63W.js.map +1 -1
  67. package/dist/chunks/usePortalTarget-C0p-mhaM.js.map +1 -1
  68. package/dist/chunks/validation-5HhpanPP.js.map +1 -1
  69. package/dist/components/GrAlert/chunks/GrAlert-FAVf-ksH.js.map +1 -1
  70. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-X6RlmiJQ.js → GrAutocomplete-rYEY15UI.js} +1 -1
  71. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-rYEY15UI.js.map +1 -0
  72. package/dist/components/GrAutocomplete/index.js +1 -1
  73. package/dist/components/GrAutocomplete/styles.css +1 -1
  74. package/dist/components/GrAvatar/chunks/GrAvatar-CNmLmgcr.js.map +1 -1
  75. package/dist/components/GrBadge/chunks/{GrBadge-BsoocpF-.js → GrBadge-D2MfThPz.js} +1 -1
  76. package/dist/components/GrBadge/chunks/GrBadge-D2MfThPz.js.map +1 -0
  77. package/dist/components/GrBadge/index.js +1 -1
  78. package/dist/components/GrBadge/styles.css +1 -1
  79. package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-B-A3PRkE.js.map +1 -1
  80. package/dist/components/GrBottomNav/chunks/GrBottomNav-wtFatLjB.js.map +1 -1
  81. package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-DRDNan_n.js.map +1 -1
  82. package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js → GrButton-7GP0zObj.js} +1 -1
  83. package/dist/components/GrButton/chunks/GrButton-7GP0zObj.js.map +1 -0
  84. package/dist/components/GrButton/index.js +3 -3
  85. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-CKyiAV8K.js.map +1 -1
  86. package/dist/components/GrCard/chunks/GrCard-BttXz1pQ.js.map +1 -1
  87. package/dist/components/GrCheckbox/chunks/GrCheckbox-CEcHIsXM.js.map +1 -1
  88. package/dist/components/GrCheckboxGroup/chunks/GrCheckboxGroup-BOwrVcFr.js.map +1 -1
  89. package/dist/components/GrChip/chunks/GrChip-C0vU69tf.js.map +1 -1
  90. package/dist/components/GrChipGroup/chunks/GrChipGroup-C78Vcwos.js.map +1 -1
  91. package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-Dkvkr91t.js → GrCodeBlock-Dp0eEBTZ.js} +1 -1
  92. package/dist/components/GrCodeBlock/chunks/GrCodeBlock-Dp0eEBTZ.js.map +1 -0
  93. package/dist/components/GrCodeBlock/index.js +1 -1
  94. package/dist/components/GrCollapse/chunks/GrCollapse-BlW6ybdL.js.map +1 -1
  95. package/dist/components/GrColorPicker/chunks/{GrColorPicker-C7ifyQ0R.js → GrColorPicker-B20gQ6AR.js} +1 -1
  96. package/dist/components/GrColorPicker/chunks/GrColorPicker-B20gQ6AR.js.map +1 -0
  97. package/dist/components/GrColorPicker/index.js +1 -1
  98. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-C5abL2Y7.js → GrCommandPalette-Cd0Yo3Dw.js} +1 -1
  99. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-Cd0Yo3Dw.js.map +1 -0
  100. package/dist/components/GrCommandPalette/index.js +1 -1
  101. package/dist/components/GrCommandPalette/styles.css +1 -1
  102. package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BF7R3hgg.js.map +1 -1
  103. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-B9mIU78E.js → GrConfirmDialog-DOsbRhoZ.js} +3 -3
  104. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-DOsbRhoZ.js.map +1 -0
  105. package/dist/components/GrConfirmDialog/index.js +1 -1
  106. package/dist/components/GrContextMenu/chunks/GrContextMenu-_FKwQvuQ.js.map +1 -1
  107. package/dist/components/GrDataTable/chunks/{GrDataTable-C8a9_nCg.js → GrDataTable-CAGgRk27.js} +5 -3
  108. package/dist/components/GrDataTable/chunks/GrDataTable-CAGgRk27.js.map +1 -0
  109. package/dist/components/GrDataTable/index.js +1 -1
  110. package/dist/components/GrDelta/chunks/GrDelta-BZwkgQW-.js.map +1 -1
  111. package/dist/components/GrDescriptionList/chunks/GrDescriptionList-BMeCgFV3.js.map +1 -1
  112. package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js → GrDialog-S1Vl-OhX.js} +1 -1
  113. package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js.map → GrDialog-S1Vl-OhX.js.map} +1 -1
  114. package/dist/components/GrDialog/index.js +1 -1
  115. package/dist/components/GrDialogService/chunks/{GrDialogService-6s2cQ-_6.js → GrDialogService-Cuhc4YLp.js} +4 -4
  116. package/dist/components/GrDialogService/chunks/GrDialogService-Cuhc4YLp.js.map +1 -0
  117. package/dist/components/GrDialogService/index.js +1 -1
  118. package/dist/components/GrDivider/chunks/GrDivider-BXavGcMX.js.map +1 -1
  119. package/dist/components/GrDrawer/chunks/{GrDrawer-B7PiAcbU.js → GrDrawer-C0YqVE9e.js} +1 -1
  120. package/dist/components/GrDrawer/chunks/GrDrawer-C0YqVE9e.js.map +1 -0
  121. package/dist/components/GrDrawer/index.js +1 -1
  122. package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js.map +1 -1
  123. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-GOzuAXsi.js.map +1 -1
  124. package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-B33ZzwfB.js.map +1 -1
  125. package/dist/components/GrFilePreview/chunks/GrFilePreview-C9Z7hMK8.js.map +1 -1
  126. package/dist/components/GrFileUpload/chunks/GrFileUpload-NsHkRRIa.js.map +1 -1
  127. package/dist/components/GrForm/chunks/GrForm-CrnfCsgj.js.map +1 -1
  128. package/dist/components/GrFormField/chunks/GrFormField-DSylU7IU.js.map +1 -1
  129. package/dist/components/GrFormFile/chunks/{GrFormFile-CYtXU9HV.js → GrFormFile-B1jGZyIj.js} +2 -2
  130. package/dist/components/GrFormFile/chunks/GrFormFile-B1jGZyIj.js.map +1 -0
  131. package/dist/components/GrFormFile/index.js +1 -1
  132. package/dist/components/GrFormFile/styles.css +1 -1
  133. package/dist/components/GrIcon/chunks/GrIcon-Dmf0c9ba.js.map +1 -1
  134. package/dist/components/GrImageViewer/chunks/GrImageViewer-CyVN6Zpc.js.map +1 -1
  135. package/dist/components/GrInput/chunks/GrInput-BLywRZUN.js.map +1 -1
  136. package/dist/components/GrInputTag/chunks/GrInputTag-Cj4A9Xgs.js.map +1 -1
  137. package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-Cy2bknHO.js → GrJsonViewer-BCZ7lE2k.js} +2 -2
  138. package/dist/components/GrJsonViewer/chunks/GrJsonViewer-BCZ7lE2k.js.map +1 -0
  139. package/dist/components/GrJsonViewer/index.js +1 -1
  140. package/dist/components/GrKbd/chunks/GrKbd-D2ga1H9i.js.map +1 -1
  141. package/dist/components/GrLink/chunks/GrLink-DYXw3W9w.js.map +1 -1
  142. package/dist/components/GrList/chunks/{GrList-DmT4qzLO.js → GrList-B_CRJB4c.js} +1 -1
  143. package/dist/components/GrList/chunks/GrList-B_CRJB4c.js.map +1 -0
  144. package/dist/components/GrList/index.js +1 -1
  145. package/dist/components/GrList/styles.css +1 -1
  146. package/dist/components/GrModal/chunks/GrModal-CCoWZuIl.js.map +1 -1
  147. package/dist/components/GrNavbar/chunks/{GrNavbar-C5EjMKaC.js → GrNavbar-_rtHKwTf.js} +1 -1
  148. package/dist/components/GrNavbar/chunks/{GrNavbar-C5EjMKaC.js.map → GrNavbar-_rtHKwTf.js.map} +1 -1
  149. package/dist/components/GrNavbar/index.js +1 -1
  150. package/dist/components/GrNumberInput/chunks/GrNumberInput-DQ59YICu.js.map +1 -1
  151. package/dist/components/GrPagination/chunks/{GrPagination-Cw3R21dp.js → GrPagination-B_kAhoAV.js} +2 -2
  152. package/dist/components/GrPagination/chunks/{GrPagination-Cw3R21dp.js.map → GrPagination-B_kAhoAV.js.map} +1 -1
  153. package/dist/components/GrPagination/index.js +1 -1
  154. package/dist/components/GrPopover/chunks/GrPopover-CofG-Tfe.js.map +1 -1
  155. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-DLcl1xiq.js → GrPromptDialog-BuJQMZLM.js} +4 -4
  156. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-BuJQMZLM.js.map +1 -0
  157. package/dist/components/GrPromptDialog/index.js +1 -1
  158. package/dist/components/GrRadio/chunks/{GrRadio-C_rESc5g.js → GrRadio-Aq3v8XgG.js} +1 -1
  159. package/dist/components/GrRadio/chunks/GrRadio-Aq3v8XgG.js.map +1 -0
  160. package/dist/components/GrRadio/index.js +2 -2
  161. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CdQO4G-U.js → GrRadioGroup-y9LVdTWh.js} +1 -1
  162. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-y9LVdTWh.js.map +1 -0
  163. package/dist/components/GrRadioGroup/index.js +1 -1
  164. package/dist/components/GrRating/chunks/GrRating-IEiTOdnF.js.map +1 -1
  165. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-C_skObtv.js → GrResponseErrorBanner-CBVRpgIG.js} +1 -1
  166. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-C_skObtv.js.map → GrResponseErrorBanner-CBVRpgIG.js.map} +1 -1
  167. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DudGc9LO.js → GrResponseErrorBanner-DMeOWlzj.js} +1 -1
  168. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DMeOWlzj.js.map +1 -0
  169. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  170. package/dist/components/GrSegmented/chunks/GrSegmented-CB-VR-yL.js.map +1 -1
  171. package/dist/components/GrSelect/chunks/{GrSelect-BiUrSSxc.js → GrSelect-gCeRFgQo.js} +1 -1
  172. package/dist/components/GrSelect/chunks/GrSelect-gCeRFgQo.js.map +1 -0
  173. package/dist/components/GrSelect/index.js +1 -1
  174. package/dist/components/GrSelect/styles.css +1 -1
  175. package/dist/components/GrSidebar/chunks/{GrSidebar-B8EQIlfW.js → GrSidebar-alkwBUzL.js} +1 -1
  176. package/dist/components/GrSidebar/chunks/GrSidebar-alkwBUzL.js.map +1 -0
  177. package/dist/components/GrSidebar/index.js +1 -1
  178. package/dist/components/GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js.map +1 -1
  179. package/dist/components/GrSlider/chunks/{GrSlider-DJfeEl4h.js → GrSlider-DTTrZTHK.js} +3 -3
  180. package/dist/components/GrSlider/chunks/GrSlider-DTTrZTHK.js.map +1 -0
  181. package/dist/components/GrSlider/index.js +2 -2
  182. package/dist/components/GrSortableList/chunks/GrSortableList-HyKX--rO.js.map +1 -1
  183. package/dist/components/GrSplitter/chunks/GrSplitter-MltjaADn.js.map +1 -1
  184. package/dist/components/GrStatistic/chunks/GrStatistic-qHVasK37.js.map +1 -1
  185. package/dist/components/GrSteps/chunks/GrSteps-C2jxpZoN.js.map +1 -1
  186. package/dist/components/GrSwitch/chunks/{GrSwitch-g_OOSkh7.js → GrSwitch-7Oe5QfQZ.js} +1 -1
  187. package/dist/components/GrSwitch/chunks/GrSwitch-7Oe5QfQZ.js.map +1 -0
  188. package/dist/components/GrSwitch/index.js +1 -1
  189. package/dist/components/GrTabPanels/chunks/GrTabPanels-BZQS8-JM.js.map +1 -1
  190. package/dist/components/GrTable/chunks/GrTable-Zcdz2St_.js.map +1 -1
  191. package/dist/components/GrTextarea/chunks/{GrTextarea-KREK75Bu.js → GrTextarea-DinXG12-.js} +10 -10
  192. package/dist/components/GrTextarea/chunks/GrTextarea-DinXG12-.js.map +1 -0
  193. package/dist/components/GrTextarea/index.js +1 -1
  194. package/dist/components/GrTimeline/chunks/GrTimeline-BiHpArsg.js.map +1 -1
  195. package/dist/components/GrToaster/chunks/{GrToaster-Dp7-4B8k.js → GrToaster-COXIBKzq.js} +2 -2
  196. package/dist/components/GrToaster/chunks/GrToaster-COXIBKzq.js.map +1 -0
  197. package/dist/components/GrToaster/index.js +1 -1
  198. package/dist/components/GrToaster/styles.css +1 -1
  199. package/dist/components/GrTooltip/chunks/GrTooltip-uYETdbCD.js.map +1 -1
  200. package/dist/components/GrTree/chunks/{GrTree-DC89I4AP.js → GrTree-Ci6CZfKR.js} +5 -5
  201. package/dist/components/GrTree/chunks/GrTree-Ci6CZfKR.js.map +1 -0
  202. package/dist/components/GrTree/index.js +1 -1
  203. package/dist/components/GrTree/styles.css +1 -1
  204. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-iJnMl2t9.js → GrTreeSelect-BHwgGJcT.js} +1 -1
  205. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-BHwgGJcT.js.map +1 -0
  206. package/dist/components/GrTreeSelect/index.js +1 -1
  207. package/dist/composables/useAnnouncer.js.map +1 -1
  208. package/dist/composables/useComboboxNavigation.js.map +1 -1
  209. package/dist/composables/useDragGesture.js.map +1 -1
  210. package/dist/composables/useFocusTrap.js.map +1 -1
  211. package/dist/composables/useOverlayLayer.js.map +1 -1
  212. package/dist/composables/useRovingFocus.js.map +1 -1
  213. package/dist/composables/useTheme.js.map +1 -1
  214. package/dist/composables/useToast.js.map +1 -1
  215. package/dist/composables/useVirtualList.js.map +1 -1
  216. package/dist/directives/autofocus.js.map +1 -1
  217. package/dist/directives/autosize.js.map +1 -1
  218. package/dist/directives/clickOutside.js.map +1 -1
  219. package/dist/directives/dropzone.js.map +1 -1
  220. package/dist/directives/hotkey.js.map +1 -1
  221. package/dist/directives/loading.js.map +1 -1
  222. package/dist/granular-provider-node.js +1 -1
  223. package/dist/granular-provider.js +1 -1
  224. package/dist/index.js +33 -33
  225. package/dist/testing.js +2 -2
  226. package/dist/testing.js.map +1 -1
  227. package/dist/theme-apply.js.map +1 -1
  228. package/dist/theme.js +1 -1
  229. package/dist/theme.js.map +1 -1
  230. package/dist/tokens.js +1 -1
  231. package/dist/types/components/GrCodeBlock/tokenizeJson.d.ts +1 -3
  232. package/dist/types/components/GrImageViewer/GrImageViewer.vue.d.ts +1 -1
  233. package/dist/types/components/GrInput/GrInput.vue.d.ts +24 -24
  234. package/dist/types/components/GrSlider/grSliderStyles.d.ts +9 -1
  235. package/dist/types/components/GrTabPanels/GrTabPanel.vue.d.ts +3 -3
  236. package/dist/vue/index.js.map +1 -1
  237. package/dist/web-types.json +1 -1
  238. package/package.json +3 -3
  239. package/dist/chunks/grButtonStyles-B_-YRE__.js.map +0 -1
  240. package/dist/chunks/grSliderStyles-B5uNxNJq.js.map +0 -1
  241. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-X6RlmiJQ.js.map +0 -1
  242. package/dist/components/GrBadge/chunks/GrBadge-BsoocpF-.js.map +0 -1
  243. package/dist/components/GrButton/chunks/GrButton-Bw2AH4uX.js.map +0 -1
  244. package/dist/components/GrCodeBlock/chunks/GrCodeBlock-Dkvkr91t.js.map +0 -1
  245. package/dist/components/GrColorPicker/chunks/GrColorPicker-C7ifyQ0R.js.map +0 -1
  246. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-C5abL2Y7.js.map +0 -1
  247. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-B9mIU78E.js.map +0 -1
  248. package/dist/components/GrDataTable/chunks/GrDataTable-C8a9_nCg.js.map +0 -1
  249. package/dist/components/GrDialogService/chunks/GrDialogService-6s2cQ-_6.js.map +0 -1
  250. package/dist/components/GrDrawer/chunks/GrDrawer-B7PiAcbU.js.map +0 -1
  251. package/dist/components/GrFormFile/chunks/GrFormFile-CYtXU9HV.js.map +0 -1
  252. package/dist/components/GrJsonViewer/chunks/GrJsonViewer-Cy2bknHO.js.map +0 -1
  253. package/dist/components/GrList/chunks/GrList-DmT4qzLO.js.map +0 -1
  254. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-DLcl1xiq.js.map +0 -1
  255. package/dist/components/GrRadio/chunks/GrRadio-C_rESc5g.js.map +0 -1
  256. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-CdQO4G-U.js.map +0 -1
  257. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DudGc9LO.js.map +0 -1
  258. package/dist/components/GrSelect/chunks/GrSelect-BiUrSSxc.js.map +0 -1
  259. package/dist/components/GrSidebar/chunks/GrSidebar-B8EQIlfW.js.map +0 -1
  260. package/dist/components/GrSlider/chunks/GrSlider-DJfeEl4h.js.map +0 -1
  261. package/dist/components/GrSwitch/chunks/GrSwitch-g_OOSkh7.js.map +0 -1
  262. package/dist/components/GrTextarea/chunks/GrTextarea-KREK75Bu.js.map +0 -1
  263. package/dist/components/GrToaster/chunks/GrToaster-Dp7-4B8k.js.map +0 -1
  264. package/dist/components/GrTree/chunks/GrTree-DC89I4AP.js.map +0 -1
  265. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-iJnMl2t9.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrSlider-DJfeEl4h.js","names":[],"sources":["../../../../src/components/GrSlider/GrSlider.vue","../../../../src/components/GrSlider/GrSlider.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { bigStep } from '../shared/numericStep'\nimport { useDragGesture } from '../../composables/useDragGesture'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n sliderFillClass,\n sliderMarkLabelClassFor,\n sliderMarkTickClass,\n sliderRailClass,\n sliderRootClass,\n sliderThumbClass,\n sliderTooltipClass,\n sliderTrackHeightBySize,\n sliderTrackVerticalLengthClass,\n sliderTrackWidthBySize,\n sliderFillOrientationClass,\n type GrSliderMarks,\n type GrSliderModelValue,\n type GrSliderOrientation,\n type GrSliderSize,\n} from './grSliderStyles'\n\nexport type {\n GrSliderMarks,\n GrSliderModelValue,\n GrSliderOrientation,\n GrSliderSize,\n} from './grSliderStyles'\n\n/**\n * Публичный GR-примитив «Slider» (WAI-ARIA slider pattern).\n *\n * Поддерживает одиночное значение и диапазон (`range` — два бегунка), шаг, метки\n * делений, всплывающее значение (tooltip), полную клавиатурную навигацию\n * (стрелки / PageUp/Down / Home / End) и `role=\"slider\"` с\n * `aria-valuemin`/`max`/`now` на каждом бегунке.\n */\nexport interface GrSliderProps {\n /** `number` — одиночное значение; `[lo, hi]` — диапазон (при `range=true`). */\n modelValue: GrSliderModelValue\n min?: number\n max?: number\n step?: number\n /** Диапазон с двумя бегунками; модель — кортеж `[lo, hi]`. */\n range?: boolean\n disabled?: boolean\n /** Только для чтения: значение видно, но не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrSliderSize\n /** Метки делений: `{ [value]: label }` или массив значений. */\n marks?: GrSliderMarks\n /** Всплывающее значение над бегунком: `true`/`'hover'` — при hover/drag/focus, `'always'` — всегда. */\n showTooltip?: boolean | 'always' | 'hover'\n /** Форматирование значения в tooltip. Оно же уходит в `aria-valuetext`. */\n formatTooltip?: (value: number) => string\n /** Ориентация дорожки. В вертикальной минимум внизу. */\n orientation?: GrSliderOrientation\n /**\n * Эмитить `update:modelValue` только по завершении жеста: во время\n * перетаскивания значение ведёт сам слайдер. Клавиатура коммитит сразу —\n * нажатие клавиши дискретно, придерживать его нечего.\n */\n lazy?: boolean\n ariaLabel?: string\n /** Имя для нативной формы: hidden input на значение, при `range` — два с одним именем. */\n name?: string\n}\n\nexport interface GrSliderEmits {\n (e: 'update:modelValue', value: GrSliderModelValue): void\n /** Значение зафиксировано (отпущен бегунок / клавиша). */\n (e: 'change', value: GrSliderModelValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrSliderProps>(),\n {\n min: 0,\n max: 100,\n step: 1,\n range: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n marks: undefined,\n showTooltip: false,\n formatTooltip: undefined,\n orientation: 'horizontal',\n lazy: false,\n ariaLabel: undefined,\n name: undefined,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst isVertical = computed(() => props.orientation === 'vertical')\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrSlider',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrSliderEmits>()\n\n// Контекст `GrFormField`: id/aria-describedby/invalid/required как fallback.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\nconst trackEl = ref<HTMLElement | null>(null)\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\nconst thumbEls = ref<HTMLElement[]>([])\n\nfunction focus(): void {\n thumbEls.value[0]?.focus()\n}\n\nfunction blur(): void {\n thumbEls.value[0]?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n// Нормализованные границы (гард от max<=min).\nconst span = computed(() => (props.max > props.min ? props.max - props.min : 1))\n\nconst stepDecimals = computed(() => {\n const s = String(props.step)\n const dot = s.indexOf('.')\n return dot >= 0 ? s.length - dot - 1 : 0\n})\n\nfunction clamp(value: number): number {\n return Math.min(props.max, Math.max(props.min, value))\n}\n\nfunction snap(value: number): number {\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\n return Number(clamp(stepped).toFixed(stepDecimals.value))\n}\n\nfunction percent(value: number): number {\n return ((clamp(value) - props.min) / span.value) * 100\n}\n\n// Черновик жеста: при `lazy` значение до отпускания живёт здесь, а наружу не\n// уходит — иначе смысл режима теряется.\nconst draftValues = ref<number[] | null>(null)\n\n// ————— Значения бегунков как массив (single = один бегунок).\nconst modelValues = computed<number[]>(() => {\n if (props.range) {\n const v = Array.isArray(props.modelValue) ? props.modelValue : [props.min, props.min]\n return [snap(v[0] ?? props.min), snap(v[1] ?? props.min)]\n }\n const v = Array.isArray(props.modelValue) ? (props.modelValue[0] ?? props.min) : props.modelValue\n return [snap(v)]\n})\n\nconst values = computed<number[]>(() => draftValues.value ?? modelValues.value)\n\n// `[touch-action:none]` обязателен: без него вертикальный свайп по бегунку\n// уходит в скролл страницы, браузер шлёт `pointercancel`, и слайдер пальцем\n// неуправляем. Утилиты `touch-none` нет ни в `presetMini`, ни в extra-rules —\n// поэтому arbitrary-значение.\nconst trackClass = computed(() => (isVertical.value\n ? `[touch-action:none] ${sliderTrackWidthBySize[resolvedSize.value]} ${sliderTrackVerticalLengthClass}`\n : `[touch-action:none] w-full ${sliderTrackHeightBySize[resolvedSize.value]}`))\n\n/** Смещение вдоль дорожки: по горизонтали слева, по вертикали снизу. */\nfunction offsetStyle(value: number): Record<string, string> {\n return isVertical.value ? { bottom: `${percent(value)}%` } : { left: `${percent(value)}%` }\n}\n\nconst fillStyle = computed(() => {\n const start = props.range\n ? Math.min(percent(values.value[0]), percent(values.value[1]))\n : 0\n const end = props.range\n ? Math.max(percent(values.value[0]), percent(values.value[1]))\n : percent(values.value[0])\n\n return isVertical.value\n ? { bottom: `${start}%`, top: `${100 - end}%` }\n : { left: `${start}%`, right: `${100 - end}%` }\n})\n\n// ————— Метки делений.\ntype SliderMark = { value: number, label: string }\nconst normalizedMarks = computed<SliderMark[]>(() => {\n if (!props.marks) return []\n if (Array.isArray(props.marks)) return props.marks.map(v => ({ value: v, label: String(v) }))\n return Object.entries(props.marks).map(([value, label]) => ({ value: Number(value), label }))\n})\n\nfunction tooltipText(value: number): string {\n return props.formatTooltip ? props.formatTooltip(value) : String(value)\n}\n\n// ————— Обновление значения конкретного бегунка.\nfunction emitValue(next: number[], commit: boolean): void {\n const payload: GrSliderModelValue = props.range ? [next[0], next[1]] : next[0]\n emit('update:modelValue', payload)\n if (commit) emit('change', payload)\n}\n\nfunction setThumb(index: number, value: number, commit: boolean): void {\n const next = values.value.slice()\n next[index] = snap(value)\n\n // range: не даём бегункам «перепрыгнуть» друг друга.\n if (props.range) {\n if (index === 0) next[0] = Math.min(next[0], next[1])\n else next[1] = Math.max(next[1], next[0])\n }\n\n // `lazy` придерживает только непрерывный жест: `commit` приходит от клавиатуры\n // и от отпускания, и там значение уходит наружу как обычно.\n if (props.lazy && !commit) {\n draftValues.value = next\n return\n }\n\n draftValues.value = null\n emitValue(next, commit)\n}\n\n// ————— Pointer-драг.\nconst activeThumb = ref<number | null>(null)\nconst hoveredThumb = ref<number | null>(null)\n\nfunction valueFromPointer(event: { clientX: number, clientY: number }): number {\n const rect = trackEl.value?.getBoundingClientRect()\n if (!rect) return props.min\n\n // В вертикали минимум внизу, поэтому доля отсчитывается от нижнего края.\n const ratio = isVertical.value\n ? (rect.height === 0 ? 0 : 1 - (event.clientY - rect.top) / rect.height)\n : (rect.width === 0 ? 0 : (event.clientX - rect.left) / rect.width)\n\n return props.min + Math.min(1, Math.max(0, ratio)) * span.value\n}\n\n/**\n * Какой бегунок повезёт клик. При равенстве расстояний берём верхний: иначе\n * схлопнувшийся в точку диапазон не развести мышью — нижний бегунок упирается\n * в верхний и оба стоят на месте.\n */\nfunction nearestThumb(value: number): number {\n if (!props.range) return 0\n\n const [lo, hi] = values.value\n if (value < lo) return 0\n if (value > hi) return 1\n\n return Math.abs(value - lo) < Math.abs(value - hi) ? 0 : 1\n}\n\nfunction endDrag(commit: boolean): void {\n if (activeThumb.value !== null && commit) {\n // Финальный commit значения: у `lazy` он же и единственный `update:modelValue`.\n const next = values.value.slice()\n draftValues.value = null\n emitValue(next, true)\n }\n // Оборванный жест (`pointercancel`: браузер забрал указатель) значения не\n // коммитит — черновик просто отбрасывается, бегунок возвращается к модели.\n draftValues.value = null\n activeThumb.value = null\n}\n\nconst drag = useDragGesture({\n disabled: () => isDisabled.value || isReadonly.value,\n onStart: (event) => {\n const value = valueFromPointer(event)\n const index = nearestThumb(value)\n activeThumb.value = index\n // Фокусируем активный бегунок сразу — чтобы клавиатура работала после клика по\n // дорожке (div не получает фокус по клику автоматически, только через `.focus()`).\n thumbEls.value[index]?.focus()\n setThumb(index, value, false)\n },\n onMove: (event) => {\n if (activeThumb.value === null) return\n event.preventDefault()\n setThumb(activeThumb.value, valueFromPointer(event), false)\n },\n onEnd: () => endDrag(true),\n onCancel: () => endDrag(false),\n})\n\n// ————— Клавиатура.\nfunction keyboardBigStep(): number {\n // Границы передаются от нормализованного размаха, а не от сырых пропов:\n // `span` уже подставил единицу вместо вырожденного `max <= min`.\n return bigStep(props.step, props.min, props.min + span.value)\n}\n\nfunction onThumbKeydown(event: KeyboardEvent, index: number): void {\n if (isDisabled.value || isReadonly.value) return\n const current = values.value[index]\n let next: number | null = null\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = current + props.step\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n next = current - props.step\n break\n case 'PageUp':\n next = current + keyboardBigStep()\n break\n case 'PageDown':\n next = current - keyboardBigStep()\n break\n case 'Home':\n next = props.min\n break\n case 'End':\n next = props.max\n break\n default:\n return\n }\n\n event.preventDefault()\n setThumb(index, next, true)\n}\n\nconst showTooltipFor = (index: number): boolean => {\n if (props.showTooltip === 'always') return true\n if (!props.showTooltip) return false\n return activeThumb.value === index || hoveredThumb.value === index\n}\n\nfunction thumbAriaLabel(index: number): string | undefined {\n if (!props.ariaLabel) return undefined\n if (!props.range) return props.ariaLabel\n\n const bound = index === 0 ? t('gr.slider.min', 'min') : t('gr.slider.max', 'max')\n return `${props.ariaLabel} (${bound})`\n}\n\n// `aria-valuetext` нужен, только когда число само по себе не читается: «1200»\n// против «$1 200». Без своего формата атрибут не добавляем.\nfunction thumbValueText(value: number): string | undefined {\n return props.formatTooltip ? props.formatTooltip(value) : undefined\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n data-gr-slider\n :data-orientation=\"orientation\"\n :class=\"sliderRootClass({ size: resolvedSize, disabled: isDisabled, hasMarks: normalizedMarks.length > 0, orientation })\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Нативная форма: роль-виджет не labelable и в submit не попадает.\n Сериализуется модель (snapped), а не черновик жеста. -->\n <template v-if=\"name\">\n <input\n v-for=\"(value, index) in modelValues\"\n :key=\"index\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\n <div\n ref=\"trackEl\"\n data-gr-slider-track\n class=\"relative\"\n :class=\"trackClass\"\n @pointerdown=\"drag.start\"\n >\n <div :class=\"sliderRailClass\" />\n <div\n data-gr-slider-fill\n :class=\"[sliderFillClass(isDisabled), sliderFillOrientationClass[orientation]]\"\n :style=\"fillStyle\"\n />\n\n <!-- Метки делений. -->\n <template v-for=\"mark in normalizedMarks\" :key=\"mark.value\">\n <span\n data-gr-slider-mark\n :class=\"sliderMarkTickClass(orientation)\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n />\n <!-- Подпись метки дублирует значение, которое диктор читает с бегунка:\n как отдельный текст внутри слайдера она только мешает. -->\n <span\n data-gr-slider-mark-label\n :class=\"sliderMarkLabelClassFor(percent(mark.value), { orientation, disabled: isDisabled })\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n >\n {{ mark.label }}\n </span>\n </template>\n\n <!-- Бегунки. -->\n <div\n v-for=\"(value, index) in values\"\n :id=\"index === 0 ? resolvedId : undefined\"\n :key=\"index\"\n :ref=\"(el) => { if (el) thumbEls[index] = el as HTMLElement }\"\n data-gr-slider-thumb\n :data-testid=\"`gr-slider-thumb-${index}`\"\n :class=\"sliderThumbClass({ size: resolvedSize, disabled: isDisabled, orientation })\"\n :style=\"offsetStyle(value)\"\n role=\"slider\"\n :tabindex=\"isDisabled ? -1 : 0\"\n :aria-valuemin=\"range && index === 1 ? values[0] : min\"\n :aria-valuemax=\"range && index === 0 ? values[1] : max\"\n :aria-valuenow=\"value\"\n :aria-valuetext=\"thumbValueText(value)\"\n :aria-orientation=\"orientation\"\n :aria-label=\"thumbAriaLabel(index)\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"index === 0 ? describedBy : undefined\"\n :aria-required=\"isRequired && index === 0 ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n @keydown=\"onThumbKeydown($event, index)\"\n @mouseenter=\"hoveredThumb = index\"\n @mouseleave=\"hoveredThumb = null\"\n @focus=\"hoveredThumb = index\"\n @blur=\"hoveredThumb = null\"\n >\n <span\n v-if=\"showTooltipFor(index)\"\n data-gr-slider-tooltip\n :class=\"sliderTooltipClass(orientation)\"\n >\n {{ tooltipText(value) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { bigStep } from '../shared/numericStep'\nimport { useDragGesture } from '../../composables/useDragGesture'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n sliderFillClass,\n sliderMarkLabelClassFor,\n sliderMarkTickClass,\n sliderRailClass,\n sliderRootClass,\n sliderThumbClass,\n sliderTooltipClass,\n sliderTrackHeightBySize,\n sliderTrackVerticalLengthClass,\n sliderTrackWidthBySize,\n sliderFillOrientationClass,\n type GrSliderMarks,\n type GrSliderModelValue,\n type GrSliderOrientation,\n type GrSliderSize,\n} from './grSliderStyles'\n\nexport type {\n GrSliderMarks,\n GrSliderModelValue,\n GrSliderOrientation,\n GrSliderSize,\n} from './grSliderStyles'\n\n/**\n * Публичный GR-примитив «Slider» (WAI-ARIA slider pattern).\n *\n * Поддерживает одиночное значение и диапазон (`range` — два бегунка), шаг, метки\n * делений, всплывающее значение (tooltip), полную клавиатурную навигацию\n * (стрелки / PageUp/Down / Home / End) и `role=\"slider\"` с\n * `aria-valuemin`/`max`/`now` на каждом бегунке.\n */\nexport interface GrSliderProps {\n /** `number` — одиночное значение; `[lo, hi]` — диапазон (при `range=true`). */\n modelValue: GrSliderModelValue\n min?: number\n max?: number\n step?: number\n /** Диапазон с двумя бегунками; модель — кортеж `[lo, hi]`. */\n range?: boolean\n disabled?: boolean\n /** Только для чтения: значение видно, но не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrSliderSize\n /** Метки делений: `{ [value]: label }` или массив значений. */\n marks?: GrSliderMarks\n /** Всплывающее значение над бегунком: `true`/`'hover'` — при hover/drag/focus, `'always'` — всегда. */\n showTooltip?: boolean | 'always' | 'hover'\n /** Форматирование значения в tooltip. Оно же уходит в `aria-valuetext`. */\n formatTooltip?: (value: number) => string\n /** Ориентация дорожки. В вертикальной минимум внизу. */\n orientation?: GrSliderOrientation\n /**\n * Эмитить `update:modelValue` только по завершении жеста: во время\n * перетаскивания значение ведёт сам слайдер. Клавиатура коммитит сразу —\n * нажатие клавиши дискретно, придерживать его нечего.\n */\n lazy?: boolean\n ariaLabel?: string\n /** Имя для нативной формы: hidden input на значение, при `range` — два с одним именем. */\n name?: string\n}\n\nexport interface GrSliderEmits {\n (e: 'update:modelValue', value: GrSliderModelValue): void\n /** Значение зафиксировано (отпущен бегунок / клавиша). */\n (e: 'change', value: GrSliderModelValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrSliderProps>(),\n {\n min: 0,\n max: 100,\n step: 1,\n range: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n marks: undefined,\n showTooltip: false,\n formatTooltip: undefined,\n orientation: 'horizontal',\n lazy: false,\n ariaLabel: undefined,\n name: undefined,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst isVertical = computed(() => props.orientation === 'vertical')\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrSlider',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrSliderEmits>()\n\n// Контекст `GrFormField`: id/aria-describedby/invalid/required как fallback.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\nconst trackEl = ref<HTMLElement | null>(null)\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\nconst thumbEls = ref<HTMLElement[]>([])\n\nfunction focus(): void {\n thumbEls.value[0]?.focus()\n}\n\nfunction blur(): void {\n thumbEls.value[0]?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n// Нормализованные границы (гард от max<=min).\nconst span = computed(() => (props.max > props.min ? props.max - props.min : 1))\n\nconst stepDecimals = computed(() => {\n const s = String(props.step)\n const dot = s.indexOf('.')\n return dot >= 0 ? s.length - dot - 1 : 0\n})\n\nfunction clamp(value: number): number {\n return Math.min(props.max, Math.max(props.min, value))\n}\n\nfunction snap(value: number): number {\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\n return Number(clamp(stepped).toFixed(stepDecimals.value))\n}\n\nfunction percent(value: number): number {\n return ((clamp(value) - props.min) / span.value) * 100\n}\n\n// Черновик жеста: при `lazy` значение до отпускания живёт здесь, а наружу не\n// уходит — иначе смысл режима теряется.\nconst draftValues = ref<number[] | null>(null)\n\n// ————— Значения бегунков как массив (single = один бегунок).\nconst modelValues = computed<number[]>(() => {\n if (props.range) {\n const v = Array.isArray(props.modelValue) ? props.modelValue : [props.min, props.min]\n return [snap(v[0] ?? props.min), snap(v[1] ?? props.min)]\n }\n const v = Array.isArray(props.modelValue) ? (props.modelValue[0] ?? props.min) : props.modelValue\n return [snap(v)]\n})\n\nconst values = computed<number[]>(() => draftValues.value ?? modelValues.value)\n\n// `[touch-action:none]` обязателен: без него вертикальный свайп по бегунку\n// уходит в скролл страницы, браузер шлёт `pointercancel`, и слайдер пальцем\n// неуправляем. Утилиты `touch-none` нет ни в `presetMini`, ни в extra-rules —\n// поэтому arbitrary-значение.\nconst trackClass = computed(() => (isVertical.value\n ? `[touch-action:none] ${sliderTrackWidthBySize[resolvedSize.value]} ${sliderTrackVerticalLengthClass}`\n : `[touch-action:none] w-full ${sliderTrackHeightBySize[resolvedSize.value]}`))\n\n/** Смещение вдоль дорожки: по горизонтали слева, по вертикали снизу. */\nfunction offsetStyle(value: number): Record<string, string> {\n return isVertical.value ? { bottom: `${percent(value)}%` } : { left: `${percent(value)}%` }\n}\n\nconst fillStyle = computed(() => {\n const start = props.range\n ? Math.min(percent(values.value[0]), percent(values.value[1]))\n : 0\n const end = props.range\n ? Math.max(percent(values.value[0]), percent(values.value[1]))\n : percent(values.value[0])\n\n return isVertical.value\n ? { bottom: `${start}%`, top: `${100 - end}%` }\n : { left: `${start}%`, right: `${100 - end}%` }\n})\n\n// ————— Метки делений.\ntype SliderMark = { value: number, label: string }\nconst normalizedMarks = computed<SliderMark[]>(() => {\n if (!props.marks) return []\n if (Array.isArray(props.marks)) return props.marks.map(v => ({ value: v, label: String(v) }))\n return Object.entries(props.marks).map(([value, label]) => ({ value: Number(value), label }))\n})\n\nfunction tooltipText(value: number): string {\n return props.formatTooltip ? props.formatTooltip(value) : String(value)\n}\n\n// ————— Обновление значения конкретного бегунка.\nfunction emitValue(next: number[], commit: boolean): void {\n const payload: GrSliderModelValue = props.range ? [next[0], next[1]] : next[0]\n emit('update:modelValue', payload)\n if (commit) emit('change', payload)\n}\n\nfunction setThumb(index: number, value: number, commit: boolean): void {\n const next = values.value.slice()\n next[index] = snap(value)\n\n // range: не даём бегункам «перепрыгнуть» друг друга.\n if (props.range) {\n if (index === 0) next[0] = Math.min(next[0], next[1])\n else next[1] = Math.max(next[1], next[0])\n }\n\n // `lazy` придерживает только непрерывный жест: `commit` приходит от клавиатуры\n // и от отпускания, и там значение уходит наружу как обычно.\n if (props.lazy && !commit) {\n draftValues.value = next\n return\n }\n\n draftValues.value = null\n emitValue(next, commit)\n}\n\n// ————— Pointer-драг.\nconst activeThumb = ref<number | null>(null)\nconst hoveredThumb = ref<number | null>(null)\n\nfunction valueFromPointer(event: { clientX: number, clientY: number }): number {\n const rect = trackEl.value?.getBoundingClientRect()\n if (!rect) return props.min\n\n // В вертикали минимум внизу, поэтому доля отсчитывается от нижнего края.\n const ratio = isVertical.value\n ? (rect.height === 0 ? 0 : 1 - (event.clientY - rect.top) / rect.height)\n : (rect.width === 0 ? 0 : (event.clientX - rect.left) / rect.width)\n\n return props.min + Math.min(1, Math.max(0, ratio)) * span.value\n}\n\n/**\n * Какой бегунок повезёт клик. При равенстве расстояний берём верхний: иначе\n * схлопнувшийся в точку диапазон не развести мышью — нижний бегунок упирается\n * в верхний и оба стоят на месте.\n */\nfunction nearestThumb(value: number): number {\n if (!props.range) return 0\n\n const [lo, hi] = values.value\n if (value < lo) return 0\n if (value > hi) return 1\n\n return Math.abs(value - lo) < Math.abs(value - hi) ? 0 : 1\n}\n\nfunction endDrag(commit: boolean): void {\n if (activeThumb.value !== null && commit) {\n // Финальный commit значения: у `lazy` он же и единственный `update:modelValue`.\n const next = values.value.slice()\n draftValues.value = null\n emitValue(next, true)\n }\n // Оборванный жест (`pointercancel`: браузер забрал указатель) значения не\n // коммитит — черновик просто отбрасывается, бегунок возвращается к модели.\n draftValues.value = null\n activeThumb.value = null\n}\n\nconst drag = useDragGesture({\n disabled: () => isDisabled.value || isReadonly.value,\n onStart: (event) => {\n const value = valueFromPointer(event)\n const index = nearestThumb(value)\n activeThumb.value = index\n // Фокусируем активный бегунок сразу — чтобы клавиатура работала после клика по\n // дорожке (div не получает фокус по клику автоматически, только через `.focus()`).\n thumbEls.value[index]?.focus()\n setThumb(index, value, false)\n },\n onMove: (event) => {\n if (activeThumb.value === null) return\n event.preventDefault()\n setThumb(activeThumb.value, valueFromPointer(event), false)\n },\n onEnd: () => endDrag(true),\n onCancel: () => endDrag(false),\n})\n\n// ————— Клавиатура.\nfunction keyboardBigStep(): number {\n // Границы передаются от нормализованного размаха, а не от сырых пропов:\n // `span` уже подставил единицу вместо вырожденного `max <= min`.\n return bigStep(props.step, props.min, props.min + span.value)\n}\n\nfunction onThumbKeydown(event: KeyboardEvent, index: number): void {\n if (isDisabled.value || isReadonly.value) return\n const current = values.value[index]\n let next: number | null = null\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = current + props.step\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n next = current - props.step\n break\n case 'PageUp':\n next = current + keyboardBigStep()\n break\n case 'PageDown':\n next = current - keyboardBigStep()\n break\n case 'Home':\n next = props.min\n break\n case 'End':\n next = props.max\n break\n default:\n return\n }\n\n event.preventDefault()\n setThumb(index, next, true)\n}\n\nconst showTooltipFor = (index: number): boolean => {\n if (props.showTooltip === 'always') return true\n if (!props.showTooltip) return false\n return activeThumb.value === index || hoveredThumb.value === index\n}\n\nfunction thumbAriaLabel(index: number): string | undefined {\n if (!props.ariaLabel) return undefined\n if (!props.range) return props.ariaLabel\n\n const bound = index === 0 ? t('gr.slider.min', 'min') : t('gr.slider.max', 'max')\n return `${props.ariaLabel} (${bound})`\n}\n\n// `aria-valuetext` нужен, только когда число само по себе не читается: «1200»\n// против «$1 200». Без своего формата атрибут не добавляем.\nfunction thumbValueText(value: number): string | undefined {\n return props.formatTooltip ? props.formatTooltip(value) : undefined\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n data-gr-slider\n :data-orientation=\"orientation\"\n :class=\"sliderRootClass({ size: resolvedSize, disabled: isDisabled, hasMarks: normalizedMarks.length > 0, orientation })\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Нативная форма: роль-виджет не labelable и в submit не попадает.\n Сериализуется модель (snapped), а не черновик жеста. -->\n <template v-if=\"name\">\n <input\n v-for=\"(value, index) in modelValues\"\n :key=\"index\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\n <div\n ref=\"trackEl\"\n data-gr-slider-track\n class=\"relative\"\n :class=\"trackClass\"\n @pointerdown=\"drag.start\"\n >\n <div :class=\"sliderRailClass\" />\n <div\n data-gr-slider-fill\n :class=\"[sliderFillClass(isDisabled), sliderFillOrientationClass[orientation]]\"\n :style=\"fillStyle\"\n />\n\n <!-- Метки делений. -->\n <template v-for=\"mark in normalizedMarks\" :key=\"mark.value\">\n <span\n data-gr-slider-mark\n :class=\"sliderMarkTickClass(orientation)\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n />\n <!-- Подпись метки дублирует значение, которое диктор читает с бегунка:\n как отдельный текст внутри слайдера она только мешает. -->\n <span\n data-gr-slider-mark-label\n :class=\"sliderMarkLabelClassFor(percent(mark.value), { orientation, disabled: isDisabled })\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n >\n {{ mark.label }}\n </span>\n </template>\n\n <!-- Бегунки. -->\n <div\n v-for=\"(value, index) in values\"\n :id=\"index === 0 ? resolvedId : undefined\"\n :key=\"index\"\n :ref=\"(el) => { if (el) thumbEls[index] = el as HTMLElement }\"\n data-gr-slider-thumb\n :data-testid=\"`gr-slider-thumb-${index}`\"\n :class=\"sliderThumbClass({ size: resolvedSize, disabled: isDisabled, orientation })\"\n :style=\"offsetStyle(value)\"\n role=\"slider\"\n :tabindex=\"isDisabled ? -1 : 0\"\n :aria-valuemin=\"range && index === 1 ? values[0] : min\"\n :aria-valuemax=\"range && index === 0 ? values[1] : max\"\n :aria-valuenow=\"value\"\n :aria-valuetext=\"thumbValueText(value)\"\n :aria-orientation=\"orientation\"\n :aria-label=\"thumbAriaLabel(index)\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"index === 0 ? describedBy : undefined\"\n :aria-required=\"isRequired && index === 0 ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n @keydown=\"onThumbKeydown($event, index)\"\n @mouseenter=\"hoveredThumb = index\"\n @mouseleave=\"hoveredThumb = null\"\n @focus=\"hoveredThumb = index\"\n @blur=\"hoveredThumb = null\"\n >\n <span\n v-if=\"showTooltipFor(index)\"\n data-gr-slider-tooltip\n :class=\"sliderTooltipClass(orientation)\"\n >\n {{ tooltipText(value) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrSwitch-g_OOSkh7.js","names":["$attrs","$slots"],"sources":["../../../../src/components/GrSwitch/GrSwitch.vue","../../../../src/components/GrSwitch/GrSwitch.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grSwitchLabelClass,\n grSwitchRootClass,\n grSwitchSpinnerClass,\n grSwitchThumbClass,\n grSwitchTrackClass,\n type GrSwitchLabelPosition,\n type GrSwitchSize,\n} from './grSwitchStyles'\n\nexport type { GrSwitchLabelPosition, GrSwitchSize } from './grSwitchStyles'\n\n/**\n * Пропсы публичного GR-примитива «Switch».\n */\nexport interface GrSwitchProps {\n /**\n * Значение переключателя. Необязательное: без `v-model` контрол рисуется\n * выключенным — так же, как `GrCheckbox`. Обязательный проп заставлял бы\n * заводить `ref` даже там, где переключатель presentational.\n */\n modelValue?: boolean\n disabled?: boolean\n /** Только для чтения: состояние видно, но не переключается. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n ariaLabel?: string\n size?: GrSwitchSize\n /** Сторона подписи относительно дорожки. */\n labelPosition?: GrSwitchLabelPosition\n /** Идёт сохранение: бегунок показывает спиннер, переключение заблокировано. */\n loading?: boolean\n /** i18n: что именно грузится. `aria-busy` сам по себе часть AT не объявляет. */\n loadingText?: string\n /** Имя поля для нативной отправки формы. Без него скрытое поле не рендерится. */\n name?: string\n /** Значение, уходящее в форму во включённом состоянии. */\n value?: string\n /** `id` формы, если переключатель лежит вне неё. */\n form?: string\n /** Кастомный цвет фона в активном состоянии. Если не задан — `var(--gr-primary)`. */\n activeBackgroundColor?: string\n /** Кастомный цвет фона в неактивном состоянии. Если не задан — `var(--gr-muted)`. */\n inactiveBackgroundColor?: string\n}\n\nexport interface GrSwitchEmits {\n (e: 'update:modelValue', value: boolean): void\n (e: 'change', value: boolean): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst getCustomColor = (value?: string) => value?.trim() || undefined\n\n// Скрытое поле — сосед кнопки, а не её потомок: интерактивный контент внутри\n// `<button>` невалиден. Из-за этого корень компонента — фрагмент, и атрибуты\n// потребителя надо донести до кнопки руками.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(\n defineProps<GrSwitchProps>(),\n {\n modelValue: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n ariaLabel: undefined,\n size: undefined,\n labelPosition: 'end',\n loading: false,\n loadingText: undefined,\n name: undefined,\n value: 'on',\n form: undefined,\n activeBackgroundColor: undefined,\n inactiveBackgroundColor: undefined,\n },\n)\n\n// Контекст `GrFormField`: id для `<label for>`, описание ошибкой, невалидность.\n// `<button>` — labelable-элемент, поэтому клик по подписи фокусирует переключатель.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst { t } = useGranularityTranslations()\n\nconst rootEl = ref<HTMLButtonElement | null>(null)\n\nfunction focus(): void {\n rootEl.value?.focus()\n}\n\nfunction blur(): void {\n rootEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrSwitch',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrSwitchEmits>()\ndefineSlots<{\n /** Подпись переключателя. */\n default?: () => any\n}>()\n\n\nconst rootClass = computed(() => grSwitchRootClass(props.labelPosition))\n\nconst trackClass = computed(() => grSwitchTrackClass(resolvedSize.value))\n\nconst trackStyle = computed(() => {\n const isChecked = props.modelValue\n const defaultBackgroundColor = isChecked ? 'var(--gr-primary)' : 'var(--gr-muted)'\n const customBackgroundColor = getCustomColor(\n isChecked ? props.activeBackgroundColor : props.inactiveBackgroundColor,\n )\n // Недоступность гасится токеном и перебивает кастомный цвет: иначе выключенный\n // переключатель остался бы таким же ярким, как рабочий.\n const backgroundColor = isDisabled.value\n ? 'var(--gr-disabled-bg)'\n : customBackgroundColor ?? defaultBackgroundColor\n\n return {\n '--gr-switch-track-bg': backgroundColor,\n '--gr-switch-track-brd': isDisabled.value\n ? 'var(--gr-disabled-brd)'\n : customBackgroundColor\n ? backgroundColor\n : isChecked\n ? 'var(--gr-primary)'\n : 'var(--gr-brd)',\n backgroundColor: 'var(--gr-switch-track-bg)',\n }\n})\n\nconst thumbClass = computed(() => grSwitchThumbClass({size: resolvedSize.value, checked: props.modelValue}))\n\nconst spinnerClass = computed(() => grSwitchSpinnerClass(resolvedSize.value))\n\nconst labelClass = computed(() => grSwitchLabelClass(resolvedSize.value, isDisabled.value))\n\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.switch.loading', 'Saving…'))\n\n// Выключенный переключатель не отправляется вовсе — так устроен чекбокс в HTML,\n// и сервер отличает «выкл» по отсутствию ключа.\nconst submitsValue = computed(\n () => props.modelValue && !isDisabled.value && Boolean(props.name),\n)\n\nfunction toggle(): void {\n if (isDisabled.value || isReadonly.value || props.loading) {\n return\n }\n\n const next = !props.modelValue\n emit('update:modelValue', next)\n emit('change', next)\n}\n</script>\n\n<template>\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"value\"\n :form=\"form\"\n >\n\n <button\n :id=\"fieldId\"\n ref=\"rootEl\"\n v-bind=\"$attrs\"\n type=\"button\"\n role=\"switch\"\n data-gr-switch\n :aria-checked=\"modelValue ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-busy=\"loading ? 'true' : undefined\"\n :disabled=\"isDisabled\"\n :class=\"rootClass\"\n @click=\"toggle\"\n @focus=\"emit('focus', $event)\"\n @blur=\"emit('blur', $event)\"\n >\n <span\n data-testid=\"gr-switch-track\"\n data-gr-switch-track\n :class=\"trackClass\"\n :style=\"trackStyle\"\n >\n <span\n data-testid=\"gr-switch-thumb\"\n data-gr-switch-thumb\n :class=\"thumbClass\"\n aria-hidden=\"true\"\n >\n <IconLoader v-if=\"loading\" data-gr-switch-spinner :class=\"spinnerClass\" />\n </span>\n </span>\n <span\n v-if=\"$slots.default\"\n data-gr-switch-label\n :class=\"labelClass\"\n >\n <slot />\n </span>\n <span v-if=\"loading\" data-gr-switch-loading-text class=\"sr-only\">{{ resolvedLoadingText }}</span>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grSwitchLabelClass,\n grSwitchRootClass,\n grSwitchSpinnerClass,\n grSwitchThumbClass,\n grSwitchTrackClass,\n type GrSwitchLabelPosition,\n type GrSwitchSize,\n} from './grSwitchStyles'\n\nexport type { GrSwitchLabelPosition, GrSwitchSize } from './grSwitchStyles'\n\n/**\n * Пропсы публичного GR-примитива «Switch».\n */\nexport interface GrSwitchProps {\n /**\n * Значение переключателя. Необязательное: без `v-model` контрол рисуется\n * выключенным — так же, как `GrCheckbox`. Обязательный проп заставлял бы\n * заводить `ref` даже там, где переключатель presentational.\n */\n modelValue?: boolean\n disabled?: boolean\n /** Только для чтения: состояние видно, но не переключается. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n ariaLabel?: string\n size?: GrSwitchSize\n /** Сторона подписи относительно дорожки. */\n labelPosition?: GrSwitchLabelPosition\n /** Идёт сохранение: бегунок показывает спиннер, переключение заблокировано. */\n loading?: boolean\n /** i18n: что именно грузится. `aria-busy` сам по себе часть AT не объявляет. */\n loadingText?: string\n /** Имя поля для нативной отправки формы. Без него скрытое поле не рендерится. */\n name?: string\n /** Значение, уходящее в форму во включённом состоянии. */\n value?: string\n /** `id` формы, если переключатель лежит вне неё. */\n form?: string\n /** Кастомный цвет фона в активном состоянии. Если не задан — `var(--gr-primary)`. */\n activeBackgroundColor?: string\n /** Кастомный цвет фона в неактивном состоянии. Если не задан — `var(--gr-muted)`. */\n inactiveBackgroundColor?: string\n}\n\nexport interface GrSwitchEmits {\n (e: 'update:modelValue', value: boolean): void\n (e: 'change', value: boolean): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst getCustomColor = (value?: string) => value?.trim() || undefined\n\n// Скрытое поле — сосед кнопки, а не её потомок: интерактивный контент внутри\n// `<button>` невалиден. Из-за этого корень компонента — фрагмент, и атрибуты\n// потребителя надо донести до кнопки руками.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(\n defineProps<GrSwitchProps>(),\n {\n modelValue: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n ariaLabel: undefined,\n size: undefined,\n labelPosition: 'end',\n loading: false,\n loadingText: undefined,\n name: undefined,\n value: 'on',\n form: undefined,\n activeBackgroundColor: undefined,\n inactiveBackgroundColor: undefined,\n },\n)\n\n// Контекст `GrFormField`: id для `<label for>`, описание ошибкой, невалидность.\n// `<button>` — labelable-элемент, поэтому клик по подписи фокусирует переключатель.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst { t } = useGranularityTranslations()\n\nconst rootEl = ref<HTMLButtonElement | null>(null)\n\nfunction focus(): void {\n rootEl.value?.focus()\n}\n\nfunction blur(): void {\n rootEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrSwitch',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrSwitchEmits>()\ndefineSlots<{\n /** Подпись переключателя. */\n default?: () => any\n}>()\n\n\nconst rootClass = computed(() => grSwitchRootClass(props.labelPosition))\n\nconst trackClass = computed(() => grSwitchTrackClass(resolvedSize.value))\n\nconst trackStyle = computed(() => {\n const isChecked = props.modelValue\n const defaultBackgroundColor = isChecked ? 'var(--gr-primary)' : 'var(--gr-muted)'\n const customBackgroundColor = getCustomColor(\n isChecked ? props.activeBackgroundColor : props.inactiveBackgroundColor,\n )\n // Недоступность гасится токеном и перебивает кастомный цвет: иначе выключенный\n // переключатель остался бы таким же ярким, как рабочий.\n const backgroundColor = isDisabled.value\n ? 'var(--gr-disabled-bg)'\n : customBackgroundColor ?? defaultBackgroundColor\n\n return {\n '--gr-switch-track-bg': backgroundColor,\n '--gr-switch-track-brd': isDisabled.value\n ? 'var(--gr-disabled-brd)'\n : customBackgroundColor\n ? backgroundColor\n : isChecked\n ? 'var(--gr-primary)'\n : 'var(--gr-brd)',\n backgroundColor: 'var(--gr-switch-track-bg)',\n }\n})\n\nconst thumbClass = computed(() => grSwitchThumbClass({size: resolvedSize.value, checked: props.modelValue}))\n\nconst spinnerClass = computed(() => grSwitchSpinnerClass(resolvedSize.value))\n\nconst labelClass = computed(() => grSwitchLabelClass(resolvedSize.value, isDisabled.value))\n\nconst resolvedLoadingText = computed(() => props.loadingText ?? t('gr.switch.loading', 'Saving…'))\n\n// Выключенный переключатель не отправляется вовсе — так устроен чекбокс в HTML,\n// и сервер отличает «выкл» по отсутствию ключа.\nconst submitsValue = computed(\n () => props.modelValue && !isDisabled.value && Boolean(props.name),\n)\n\nfunction toggle(): void {\n if (isDisabled.value || isReadonly.value || props.loading) {\n return\n }\n\n const next = !props.modelValue\n emit('update:modelValue', next)\n emit('change', next)\n}\n</script>\n\n<template>\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"value\"\n :form=\"form\"\n >\n\n <button\n :id=\"fieldId\"\n ref=\"rootEl\"\n v-bind=\"$attrs\"\n type=\"button\"\n role=\"switch\"\n data-gr-switch\n :aria-checked=\"modelValue ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-busy=\"loading ? 'true' : undefined\"\n :disabled=\"isDisabled\"\n :class=\"rootClass\"\n @click=\"toggle\"\n @focus=\"emit('focus', $event)\"\n @blur=\"emit('blur', $event)\"\n >\n <span\n data-testid=\"gr-switch-track\"\n data-gr-switch-track\n :class=\"trackClass\"\n :style=\"trackStyle\"\n >\n <span\n data-testid=\"gr-switch-thumb\"\n data-gr-switch-thumb\n :class=\"thumbClass\"\n aria-hidden=\"true\"\n >\n <IconLoader v-if=\"loading\" data-gr-switch-spinner :class=\"spinnerClass\" />\n </span>\n </span>\n <span\n v-if=\"$slots.default\"\n data-gr-switch-label\n :class=\"labelClass\"\n >\n <slot />\n </span>\n <span v-if=\"loading\" data-gr-switch-loading-text class=\"sr-only\">{{ resolvedLoadingText }}</span>\n </button>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrTextarea-KREK75Bu.js","names":["$attrs"],"sources":["../../../../src/components/GrTextarea/GrTextarea.vue","../../../../src/components/GrTextarea/GrTextarea.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrTextarea — многострочное поле ввода GR-примитива.\n *\n * Состояния:\n * - `state`: визуальный оттенок рамки (`default | success | warning | danger`).\n * - `invalid`: форсирует `danger`-состояние и проставляет `aria-invalid=\"true\"`.\n */\nimport { computed, ref, useId } from 'vue'\nimport {\n countClass,\n countRowClass,\n lineCountClass,\n disabledSurfaceClass,\n enabledSurfaceClass,\n grTextareaClass,\n resizeClass,\n sizes,\n type GrTextareaResize,\n type GrTextareaSize,\n type GrTextareaState,\n} from './grTextareaStyles'\nimport IconX from '~icons/lucide/x'\nimport { vAutosize } from '../../directives'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nexport interface GrTextareaProps {\n modelValue: string\n placeholder?: string\n autocomplete?: string\n disabled?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n invalid?: boolean\n /** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */\n required?: boolean\n /** Доступное имя вне `GrFormField`. */\n ariaLabel?: string\n state?: GrTextareaState\n name?: string\n id?: string\n rows?: number\n size?: GrTextareaSize\n /** Ограничение длины + основа для счётчика символов. */\n maxlength?: number\n /** Показывать счётчик символов (`len` или `len/maxlength`). */\n showCount?: boolean\n /**\n * Показывать счётчик строк. Считаются **логические** строки (переводы строки), а\n * не визуальные переносы: при `autosize` число не меняется от ширины поля.\n */\n showLineCount?: boolean\n /**\n * Ориентир по числу строк для счётчика (`3 / 10`). Ввод не ограничивает: обрезать\n * набранный текст за пользователя компонент не вправе.\n */\n maxLines?: number\n /** Подгонять высоту под содержимое (директива `v-autosize`). */\n autosize?: boolean\n /** Кнопка очистки значения. Настраивается через `GrConfigProvider`. */\n clearable?: boolean\n /** A11y-подпись кнопки очистки. */\n clearLabel?: string\n /** Ручное изменение размера пользователем. */\n resize?: GrTextareaResize\n}\n\nexport interface GrTextareaEmits {\n (e: 'update:modelValue', value: string): void\n /** Значение зафиксировано нативным `change` — по `blur`. */\n (e: 'change', value: string): void\n /** Значение стёрто кнопкой очистки. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrTextareaProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n name: undefined,\n id: undefined,\n rows: 4,\n size: undefined,\n maxlength: undefined,\n showCount: false,\n autosize: false,\n clearable: undefined,\n clearLabel: undefined,\n resize: 'vertical',\n})\n\ndefineOptions({\n // Иначе атрибуты потребителя (`data-*`, `aria-*`, `name`) садятся на корень,\n // а корень со `showCount` — обёртка счётчика, а не само поле.\n inheritAttrs: false,\n})\n\nconst emit = defineEmits<GrTextareaEmits>()\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => props.id ?? field?.id.value)\n\n// Счётчик обязан быть частью описания поля: иначе «12 / 60» видно глазами, но\n// не слышно — при том что ограничение длины и есть его смысл.\nconst countId = useId()\nconst lineCountId = useId()\nconst describedBy = computed(() =>\n [\n field?.describedById.value,\n props.showCount ? countId : undefined,\n props.showLineCount ? lineCountId : undefined,\n ]\n .filter(Boolean)\n .join(' ') || undefined,\n)\n\n/** Обёртка нужна любому из счётчиков — и кнопке очистки. */\nconst hasCounters = computed(() => props.showCount || props.showLineCount)\n\nconst lineCount = computed(() => props.modelValue.split('\\n').length)\n\nconst countText = computed(() =>\n props.maxlength !== undefined ? `${props.modelValue.length} / ${props.maxlength}` : String(props.modelValue.length),\n)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst textareaEl = ref<HTMLTextAreaElement | null>(null)\n\nfunction focus(): void {\n textareaEl.value?.focus()\n}\n\nfunction blur(): void {\n textareaEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTextarea' })\nconst resolvedClearable = useGrComponentProp('GrTextarea', 'clearable', () => props.clearable, false)\n\nconst { t } = useGranularityTranslations()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\n\n/** С `maxLines` — «3 / 10», без него — локализованная подпись с формой числа. */\nconst lineCountText = computed(() => (\n props.maxLines !== undefined\n ? `${lineCount.value} / ${props.maxLines}`\n : t('gr.textarea.lines', '{n} lines', { n: lineCount.value })\n))\n\nconst showClear = computed(() =>\n resolvedClearable.value && !isDisabled.value && !isReadonly.value && props.modelValue !== '',\n)\n\nfunction clear(): void {\n emit('update:modelValue', '')\n emit('change', '')\n emit('clear')\n focus()\n}\n\nconst baseClass = 'w-full rounded-[var(--gr-radius-control)] border text-[var(--gr-fg)] placeholder:text-[var(--gr-muted-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed'\n\nconst className = computed(() => [\n sizes[resolvedSize.value],\n resizeClass[props.resize],\n isDisabled.value ? disabledSurfaceClass : enabledSurfaceClass,\n grTextareaClass({\n state: props.state,\n invalid: isInvalid.value,\n }),\n].join(' '))\n\n// Обе ветки шаблона (со счётчиком и без) рендерят одно и то же поле, поэтому\n// атрибуты живут одним объектом: двадцать строк копипасты расходятся молча.\nconst textareaAttrs = computed(() => ({\n id: resolvedId.value,\n 'data-gr-textarea': '',\n name: props.name,\n rows: props.rows,\n maxlength: props.maxlength,\n autocomplete: props.autocomplete,\n placeholder: props.placeholder,\n disabled: isDisabled.value,\n value: props.modelValue,\n 'aria-invalid': isInvalid.value ? ('true' as const) : undefined,\n 'aria-describedby': describedBy.value,\n 'aria-required': isRequired.value ? ('true' as const) : undefined,\n 'aria-readonly': isReadonly.value ? ('true' as const) : undefined,\n 'aria-label': props.ariaLabel,\n readonly: isReadonly.value,\n class: [baseClass, className.value],\n}))\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLTextAreaElement).value)\n}\n\n// Объявленный emit уходит из `$attrs`, поэтому нативные события переизлучаем\n// руками — иначе `@change`/`@focus`/`@blur` у потребителя перестали бы работать.\nfunction onChange(e: Event): void {\n emit('change', (e.target as HTMLTextAreaElement).value)\n}\n\nfunction onFocus(e: FocusEvent): void {\n emit('focus', e)\n}\n\nfunction onBlur(e: FocusEvent): void {\n emit('blur', e)\n}\n</script>\n\n<template>\n <div v-if=\"hasCounters\" data-gr-textarea-wrap class=\"relative w-full\">\n <textarea\n ref=\"textareaEl\"\n v-autosize=\"autosize\"\n v-bind=\"{ ...textareaAttrs, ...$attrs }\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n />\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-textarea-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <div :class=\"countRowClass\">\n <div\n v-if=\"showLineCount\"\n :id=\"lineCountId\"\n data-gr-textarea-line-count\n :class=\"lineCountClass\"\n >\n {{ lineCountText }}\n </div>\n\n <!-- Символьный счётчик всегда прижат вправо: без счётчика строк он остаётся\n единственным в ряду, и `justify-between` его туда и отправляет. -->\n <div\n v-if=\"showCount\"\n :id=\"countId\"\n data-gr-textarea-count\n class=\"ml-auto\"\n :class=\"countClass\"\n >\n {{ countText }}\n </div>\n </div>\n </div>\n\n <!-- Обёртка появляется только под кнопку очистки: без неё поле остаётся\n корневым элементом — на этом стоит контракт fallthrough-атрибутов. -->\n <div v-else-if=\"resolvedClearable\" class=\"relative w-full\">\n <textarea\n ref=\"textareaEl\"\n v-autosize=\"autosize\"\n v-bind=\"{ ...textareaAttrs, ...$attrs }\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n />\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-textarea-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <textarea\n v-else\n ref=\"textareaEl\"\n v-autosize=\"autosize\"\n v-bind=\"{ ...textareaAttrs, ...$attrs }\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrTextarea — многострочное поле ввода GR-примитива.\n *\n * Состояния:\n * - `state`: визуальный оттенок рамки (`default | success | warning | danger`).\n * - `invalid`: форсирует `danger`-состояние и проставляет `aria-invalid=\"true\"`.\n */\nimport { computed, ref, useId } from 'vue'\nimport {\n countClass,\n countRowClass,\n lineCountClass,\n disabledSurfaceClass,\n enabledSurfaceClass,\n grTextareaClass,\n resizeClass,\n sizes,\n type GrTextareaResize,\n type GrTextareaSize,\n type GrTextareaState,\n} from './grTextareaStyles'\nimport IconX from '~icons/lucide/x'\nimport { vAutosize } from '../../directives'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nexport interface GrTextareaProps {\n modelValue: string\n placeholder?: string\n autocomplete?: string\n disabled?: boolean\n /** Только для чтения: значение видно и уходит в форму, но не редактируется. */\n readonly?: boolean\n invalid?: boolean\n /** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */\n required?: boolean\n /** Доступное имя вне `GrFormField`. */\n ariaLabel?: string\n state?: GrTextareaState\n name?: string\n id?: string\n rows?: number\n size?: GrTextareaSize\n /** Ограничение длины + основа для счётчика символов. */\n maxlength?: number\n /** Показывать счётчик символов (`len` или `len/maxlength`). */\n showCount?: boolean\n /**\n * Показывать счётчик строк. Считаются **логические** строки (переводы строки), а\n * не визуальные переносы: при `autosize` число не меняется от ширины поля.\n */\n showLineCount?: boolean\n /**\n * Ориентир по числу строк для счётчика (`3 / 10`). Ввод не ограничивает: обрезать\n * набранный текст за пользователя компонент не вправе.\n */\n maxLines?: number\n /** Подгонять высоту под содержимое (директива `v-autosize`). */\n autosize?: boolean\n /** Кнопка очистки значения. Настраивается через `GrConfigProvider`. */\n clearable?: boolean\n /** A11y-подпись кнопки очистки. */\n clearLabel?: string\n /** Ручное изменение размера пользователем. */\n resize?: GrTextareaResize\n}\n\nexport interface GrTextareaEmits {\n (e: 'update:modelValue', value: string): void\n /** Значение зафиксировано нативным `change` — по `blur`. */\n (e: 'change', value: string): void\n /** Значение стёрто кнопкой очистки. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrTextareaProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n name: undefined,\n id: undefined,\n rows: 4,\n size: undefined,\n maxlength: undefined,\n showCount: false,\n autosize: false,\n clearable: undefined,\n clearLabel: undefined,\n resize: 'vertical',\n})\n\ndefineOptions({\n // Иначе атрибуты потребителя (`data-*`, `aria-*`, `name`) садятся на корень,\n // а корень со `showCount` — обёртка счётчика, а не само поле.\n inheritAttrs: false,\n})\n\nconst emit = defineEmits<GrTextareaEmits>()\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => props.id ?? field?.id.value)\n\n// Счётчик обязан быть частью описания поля: иначе «12 / 60» видно глазами, но\n// не слышно — при том что ограничение длины и есть его смысл.\nconst countId = useId()\nconst lineCountId = useId()\nconst describedBy = computed(() =>\n [\n field?.describedById.value,\n props.showCount ? countId : undefined,\n props.showLineCount ? lineCountId : undefined,\n ]\n .filter(Boolean)\n .join(' ') || undefined,\n)\n\n/** Обёртка нужна любому из счётчиков — и кнопке очистки. */\nconst hasCounters = computed(() => props.showCount || props.showLineCount)\n\nconst lineCount = computed(() => props.modelValue.split('\\n').length)\n\nconst countText = computed(() =>\n props.maxlength !== undefined ? `${props.modelValue.length} / ${props.maxlength}` : String(props.modelValue.length),\n)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst textareaEl = ref<HTMLTextAreaElement | null>(null)\n\nfunction focus(): void {\n textareaEl.value?.focus()\n}\n\nfunction blur(): void {\n textareaEl.value?.blur()\n}\n\ndefineExpose({ focus, blur })\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrTextarea' })\nconst resolvedClearable = useGrComponentProp('GrTextarea', 'clearable', () => props.clearable, false)\n\nconst { t } = useGranularityTranslations()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\n\n/** С `maxLines` — «3 / 10», без него — локализованная подпись с формой числа. */\nconst lineCountText = computed(() => (\n props.maxLines !== undefined\n ? `${lineCount.value} / ${props.maxLines}`\n : t('gr.textarea.lines', '{n} lines', { n: lineCount.value })\n))\n\nconst showClear = computed(() =>\n resolvedClearable.value && !isDisabled.value && !isReadonly.value && props.modelValue !== '',\n)\n\nfunction clear(): void {\n emit('update:modelValue', '')\n emit('change', '')\n emit('clear')\n focus()\n}\n\nconst baseClass = 'w-full rounded-[var(--gr-radius-control)] border text-[var(--gr-fg)] placeholder:text-[var(--gr-muted-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed'\n\nconst className = computed(() => [\n sizes[resolvedSize.value],\n resizeClass[props.resize],\n isDisabled.value ? disabledSurfaceClass : enabledSurfaceClass,\n grTextareaClass({\n state: props.state,\n invalid: isInvalid.value,\n }),\n].join(' '))\n\n// Обе ветки шаблона (со счётчиком и без) рендерят одно и то же поле, поэтому\n// атрибуты живут одним объектом: двадцать строк копипасты расходятся молча.\nconst textareaAttrs = computed(() => ({\n id: resolvedId.value,\n 'data-gr-textarea': '',\n name: props.name,\n rows: props.rows,\n maxlength: props.maxlength,\n autocomplete: props.autocomplete,\n placeholder: props.placeholder,\n disabled: isDisabled.value,\n value: props.modelValue,\n 'aria-invalid': isInvalid.value ? ('true' as const) : undefined,\n 'aria-describedby': describedBy.value,\n 'aria-required': isRequired.value ? ('true' as const) : undefined,\n 'aria-readonly': isReadonly.value ? ('true' as const) : undefined,\n 'aria-label': props.ariaLabel,\n readonly: isReadonly.value,\n class: [baseClass, className.value],\n}))\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLTextAreaElement).value)\n}\n\n// Объявленный emit уходит из `$attrs`, поэтому нативные события переизлучаем\n// руками — иначе `@change`/`@focus`/`@blur` у потребителя перестали бы работать.\nfunction onChange(e: Event): void {\n emit('change', (e.target as HTMLTextAreaElement).value)\n}\n\nfunction onFocus(e: FocusEvent): void {\n emit('focus', e)\n}\n\nfunction onBlur(e: FocusEvent): void {\n emit('blur', e)\n}\n</script>\n\n<template>\n <div v-if=\"hasCounters\" data-gr-textarea-wrap class=\"relative w-full\">\n <textarea\n ref=\"textareaEl\"\n v-autosize=\"autosize\"\n v-bind=\"{ ...textareaAttrs, ...$attrs }\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n />\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-textarea-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <div :class=\"countRowClass\">\n <div\n v-if=\"showLineCount\"\n :id=\"lineCountId\"\n data-gr-textarea-line-count\n :class=\"lineCountClass\"\n >\n {{ lineCountText }}\n </div>\n\n <!-- Символьный счётчик всегда прижат вправо: без счётчика строк он остаётся\n единственным в ряду, и `justify-between` его туда и отправляет. -->\n <div\n v-if=\"showCount\"\n :id=\"countId\"\n data-gr-textarea-count\n class=\"ml-auto\"\n :class=\"countClass\"\n >\n {{ countText }}\n </div>\n </div>\n </div>\n\n <!-- Обёртка появляется только под кнопку очистки: без неё поле остаётся\n корневым элементом — на этом стоит контракт fallthrough-атрибутов. -->\n <div v-else-if=\"resolvedClearable\" class=\"relative w-full\">\n <textarea\n ref=\"textareaEl\"\n v-autosize=\"autosize\"\n v-bind=\"{ ...textareaAttrs, ...$attrs }\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n />\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-textarea-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <textarea\n v-else\n ref=\"textareaEl\"\n v-autosize=\"autosize\"\n v-bind=\"{ ...textareaAttrs, ...$attrs }\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n />\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrToaster-Dp7-4B8k.js","names":[],"sources":["../../../../src/components/GrToaster/GrToaster.vue","../../../../src/components/GrToaster/GrToaster.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrToaster — GR-примитив области уведомлений (toast).\n *\n * Модель состояния берётся из `useToast`, поэтому в приложении предполагается\n * **один активный `GrToaster`** на корень (или app-scoped через плагин).\n *\n * A11y:\n * - контейнер — `role=\"region\"` с `aria-label` (i18n-проп `regionLabel`);\n * - объявляет каждый тост сам собой: `role=\"status\"`, а критичные\n * (`warning`/`danger`) — ассертивный `role=\"alert\"`. Обёртки-live-region над\n * ними нет намеренно: вложение регионов с разной ассертивностью\n * спецификацией не определено, браузеры и SR расходятся вплоть до потери\n * объявления;\n * - стек достижим с клавиатуры по `focusHotkey` (по умолчанию `F6`) — тосты\n * телепортированы в конец `body`, и без хоткея кнопка «Отменить» лежала бы\n * за пределами разумного числа нажатий `Tab`.\n *\n * UX:\n * - `maxVisible` ограничивает стек; лишние ждут в очереди (их таймеры на паузе);\n * - автозакрытие **останавливается под курсором/фокусом** (WCAG 2.2.1) и\n * визуализируется прогресс-баром до закрытия;\n * - `placement` настраивает угол экрана; слой — `--gr-z-toast`.\n */\nimport { computed, nextTick, ref, useSlots, watchEffect } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\nimport type { Component } from 'vue'\n\nimport { useDragGesture } from '../../composables/useDragGesture'\nimport { useToast } from '../../composables/useToast'\nimport { vHotkey } from '../../directives'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrToastTone, Toast, ToastAction } from '../../composables/useToast'\nimport GrButton from '../GrButton'\nimport GrIcon from '../GrIcon'\nimport {\n PLACEMENT_CLASS,\n SWIPE_DIRECTION,\n SWIPE_FLY_OUT_PX,\n SWIPE_RESISTANCE,\n SWIPE_THRESHOLD_MIN_PX,\n SWIPE_THRESHOLD_RATIO,\n swipeOpacity,\n type GrToasterPlacement,\n} from './grToasterStyles'\n\nimport IconCheck from '~icons/lucide/check-circle'\nimport IconWarning from '~icons/lucide/alert-triangle'\nimport IconError from '~icons/lucide/x-circle'\nimport IconInfo from '~icons/lucide/info'\nimport IconClose from '~icons/lucide/x'\n\nexport type { GrToasterPlacement }\n\ninterface ToneMeta {\n icon: Component\n color: string\n role: 'status' | 'alert'\n}\n\n// Единый маппинг по tone'у: иконка, цвет, a11y-роль. На уровне модуля —\n// чтобы не пересоздавать объект на каждый рендер.\nconst TONE_META: Record<GrToastTone, ToneMeta> = {\n primary: { icon: IconInfo, color: 'var(--gr-primary)', role: 'status' },\n neutral: { icon: IconInfo, color: 'var(--gr-muted-fg)', role: 'status' },\n info: { icon: IconInfo, color: 'var(--gr-info)', role: 'status' },\n success: { icon: IconCheck, color: 'var(--gr-success)', role: 'status' },\n warning: { icon: IconWarning, color: 'var(--gr-warning)', role: 'alert' },\n danger: { icon: IconError, color: 'var(--gr-danger)', role: 'alert' },\n slate: { icon: IconInfo, color: 'var(--gr-slate)', role: 'status' },\n azure: { icon: IconInfo, color: 'var(--gr-azure)', role: 'status' },\n}\n\nexport interface GrToasterProps {\n /** Угол экрана для стека уведомлений. */\n placement?: GrToasterPlacement\n /** Максимум одновременно видимых тостов; остальные ждут в очереди. По умолчанию `4`. */\n maxVisible?: number\n /** A11y-лейбл кнопки закрытия (i18n). */\n dismissLabel?: string\n /** A11y-лейбл контейнера-региона (i18n). */\n regionLabel?: string\n /**\n * Ширина стека. Число — пиксели, строка — любая CSS-длина. Уезжает в\n * `--gr-toaster-width`, поэтому то же самое можно задать и темой.\n */\n width?: string | number\n /**\n * Клавиша перевода фокуса в стек уведомлений; `false` отключает хоткей.\n * `F6` — рекомендация APG для перехода между «регионами» страницы.\n */\n focusHotkey?: string | false\n /**\n * Смахивание тоста к своему краю экрана. Клавиатурный эквивалент — `Delete`\n * и `Backspace` на сфокусированном тосте — остаётся и при выключенном жесте.\n */\n swipeDismiss?: boolean\n}\n\nimport { useGrThemeAttrs } from '../GrConfigProvider/context'\n\nconst props = withDefaults(defineProps<GrToasterProps>(), {\n placement: 'top-right',\n maxVisible: 4,\n dismissLabel: undefined,\n regionLabel: undefined,\n width: undefined,\n focusHotkey: 'F6',\n swipeDismiss: true,\n})\n\n/**\n * Слот `actions` полностью заменяет дефолтные action-кнопки тоста. Получает сам\n * `toast` и `dismiss` — функцию, закрывающую именно этот тост.\n */\ndefineSlots<{\n actions?: (props: { toast: Toast, dismiss: () => void }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst slots = useSlots()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? t('gr.toaster.dismiss', 'Dismiss'))\nconst resolvedRegionLabel = computed(() => props.regionLabel ?? t('gr.toaster.region', 'Notifications'))\n\nconst { list, dismiss, pause, resume } = useToast()\n\n// Единый список кнопок тоста: `action` (шорткат) + массив `actions`.\nfunction toastActions(toast: Toast): ToastAction[] {\n return [...(toast.action ? [toast.action] : []), ...(toast.actions ?? [])]\n}\n\n// Показываем блок действий, если есть кнопки или задан слот `actions`.\nfunction hasActions(toast: Toast): boolean {\n return Boolean(slots.actions) || toastActions(toast).length > 0\n}\n\n// Клик по action-кнопке тоста: вызываем обработчик и (по умолчанию) закрываем тост.\nfunction onAction(toast: Toast, action: ToastAction): void {\n action.onClick()\n if (action.dismissOnClick !== false)\n dismiss(toast.id)\n}\n\n// SSR-guard: на сервере `document.body` недоступен — отключаем `teleport`.\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n\n// Видимые тосты (не больше `maxVisible`); остальные ждут в очереди. Список\n// хранится новейшими вперёд, поэтому «видимые дожинают, новые ждут» — это\n// хвост: старейшие N. Взяв голову, стек показывал бы новейшие, и каждый пуш\n// вытеснял бы с экрана тост, который пользователь ещё читает.\nconst visibleToasts = computed(() => list.value.slice(-Math.max(1, props.maxVisible)))\nconst visibleIds = computed(() => new Set(visibleToasts.value.map(toast => toast.id)))\n\n// Пауза под курсором/фокусом (WCAG 2.2.1). Флага два и они независимы: один\n// `paused` терялся на «фокус внутри + мышь прошла насквозь» — `mouseleave`\n// возобновлял отсчёт под клавиатурным фокусом.\nconst hovered = ref(false)\nconst focusWithin = ref(false)\n\n// Контейнер стека: по нему ищутся тосты — и для фокуса, и для жеста.\nconst containerEl = ref<HTMLElement | null>(null)\n\n// ————— Смахивание.\n\nconst swipingId = ref<string | null>(null)\nconst swipeOffset = ref(0)\nlet swipeStartX = 0\nlet swipeThreshold = SWIPE_THRESHOLD_MIN_PX\n\n// Тост, который тянут, тоже держит таймеры: иначе уведомление сгорало бы прямо\n// под пальцем, посреди жеста.\nconst paused = computed(() => hovered.value || focusWithin.value || swipingId.value !== null)\n\nfunction toastElement(id: string): HTMLElement | null {\n return containerEl.value?.querySelector<HTMLElement>(`[data-gr-toast][data-toast-id=\"${id}\"]`) ?? null\n}\n\n/** Жест отпущен и тост закрывается: переход снова разрешён (см. `releaseSwiped`). */\nconst swipeReleased = ref(false)\n\nfunction resetSwipe(): void {\n swipingId.value = null\n swipeOffset.value = 0\n swipeReleased.value = false\n}\n\n/**\n * Отпущенный тост уезжает за свой край и только потом закрывается.\n *\n * Два кадра здесь обязательны, и оба по одной причине: уходящий элемент Vue\n * больше не перерисовывает — он уезжает с тем стилем, что был на последнем\n * рендере. Значит и разрешение перехода, и конечную точку нужно успеть отдать\n * до закрытия: первый кадр снимает инлайновый `transition: none` (во время\n * жеста он нужен, иначе переходы группы борются с пальцем), второй задаёт точку\n * отлёта — с этого момента анимация уже идёт, и `leave` подхватывает её на ходу.\n *\n * Закрыв тост сразу, получили бы не отлёт, а мгновенное исчезновение: элемент\n * ушёл бы в `leave` с запрещённым переходом.\n */\nasync function releaseSwiped(id: string): Promise<void> {\n swipeReleased.value = true\n await nextTick()\n\n swipeOffset.value = SWIPE_DIRECTION[props.placement] * SWIPE_FLY_OUT_PX\n await nextTick()\n\n dismiss(id)\n resetSwipe()\n}\n\n/**\n * Движение к своему краю идёт один к одному, в обратную сторону — с\n * сопротивлением: тост едет за пальцем, но закрыть его «наоборот» нельзя.\n */\nfunction resistOffset(delta: number): number {\n const toEdge = SWIPE_DIRECTION[props.placement]\n\n return Math.sign(delta) === toEdge ? delta : delta / SWIPE_RESISTANCE\n}\n\nconst swipe = useDragGesture({\n disabled: () => !props.swipeDismiss,\n onStart: (event) => {\n const target = event.target as HTMLElement | null\n // Нажатие на кнопке — это нажатие на кнопке: закрытие и действия тоста не\n // должны превращаться в протяжку.\n if (target?.closest('button, a[href]')) return false\n\n const host = target?.closest<HTMLElement>('[data-gr-toast]')\n const id = host?.dataset.toastId\n if (!host || !id) return false\n\n swipingId.value = id\n swipeOffset.value = 0\n swipeStartX = event.clientX\n swipeThreshold = Math.max(SWIPE_THRESHOLD_MIN_PX, host.getBoundingClientRect().width * SWIPE_THRESHOLD_RATIO)\n },\n onMove: (event) => {\n // Против выделения текста тоста во время протяжки.\n event.preventDefault()\n swipeOffset.value = resistOffset(event.clientX - swipeStartX)\n },\n onEnd: () => {\n const id = swipingId.value\n const passed = id !== null\n && Math.sign(swipeOffset.value) === SWIPE_DIRECTION[props.placement]\n && Math.abs(swipeOffset.value) >= swipeThreshold\n\n if (passed) void releaseSwiped(id)\n else resetSwipe()\n },\n // Обрыв возвращает тост на место, а не дожимает смахивание: указатель забрал\n // браузер, а не пользователь довёл жест.\n onCancel: resetSwipe,\n})\n\nfunction swipeStyle(toast: Toast): Record<string, string> | undefined {\n if (swipingId.value !== toast.id) return undefined\n\n return {\n transform: `translateX(${swipeOffset.value}px)`,\n opacity: String(swipeOpacity(swipeOffset.value, swipeThreshold)),\n // Переходы группы во время жеста борются с пальцем: тост едет с задержкой\n // и «догоняет» курсор после отпускания. После отпускания запрет снимается —\n // уходящему тосту переход нужен, иначе он не уедет вовсе.\n ...(swipeReleased.value ? {} : { transition: 'none' }),\n }\n}\n\n/**\n * Клавиатурный эквивалент смахивания. `Escape` намеренно не берём: тостер не\n * модальный слой, и перехват закрывал бы уведомление вместо диалога под ним.\n */\nfunction onToastKeydown(event: KeyboardEvent, toast: Toast): void {\n if (event.key !== 'Delete' && event.key !== 'Backspace') return\n\n event.preventDefault()\n\n const index = visibleToasts.value.findIndex(item => item.id === toast.id)\n const next = visibleToasts.value[index + 1] ?? visibleToasts.value[index - 1]\n\n dismiss(toast.id)\n\n // Фокус не должен упасть на `body`: дальше по стеку есть что читать.\n if (next) void nextTick(() => toastElement(next.id)?.focus())\n}\n\n// Единый источник правды по таймерам: тост тикает, только если он видим И стек не на\n// паузе. Очередь и hover — на паузе. Реагирует на изменения списка/hover.\n//\n// `timeoutMs` читается ради зависимости, а не ради значения: `toast.promise` и\n// `update` перезапускают таймер уже существующего тоста, не трогая ни список, ни\n// паузу. Без этой зависимости эффект не просыпался бы, и заново заведённый\n// таймер тикал бы под курсором или у тоста, который ещё ждёт в очереди, — тот\n// сгорал бы, ни разу не показавшись.\nwatchEffect(() => {\n for (const toast of list.value) {\n const shouldTick = toast.timeoutMs > 0 && visibleIds.value.has(toast.id) && !paused.value\n\n if (shouldTick)\n resume(toast.id)\n else\n pause(toast.id)\n }\n})\n\nfunction metaFor(tone: GrToastTone): ToneMeta {\n return TONE_META[tone] ?? TONE_META.info\n}\n\nconst containerClass = computed(() => PLACEMENT_CLASS[props.placement])\n\nconst containerStyle = computed(() => {\n if (props.width === undefined) return undefined\n\n return { '--gr-toaster-width': typeof props.width === 'number' ? `${props.width}px` : props.width }\n})\n\n\n/**\n * Переводит фокус на верхний тост. Тост фокусируем только программно\n * (`tabindex=\"-1\"`): собственной остановкой `Tab` стек быть не должен, иначе\n * всплывающее уведомление перехватывало бы обход страницы.\n */\nfunction focus(): boolean {\n const target = containerEl.value?.querySelector<HTMLElement>('[data-gr-toast]')\n if (!target) return false\n\n target.focus()\n return true\n}\n\nconst hotkeyBinding = computed(() => ({\n enabled: props.focusHotkey !== false && visibleToasts.value.length > 0,\n handlers: { [props.focusHotkey || 'F6']: { handler: onFocusHotkey, allowInEditable: true } },\n}))\n\nfunction onFocusHotkey(event: KeyboardEvent): void {\n if (focus()) event.preventDefault()\n}\n\ndefineExpose({ focus })\n</script>\n\n<template>\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <div\n ref=\"containerEl\"\n v-hotkey=\"hotkeyBinding\"\n v-bind=\"themeAttrs\"\n data-gr-toaster\n data-gr-overlay-root\n role=\"region\"\n :aria-label=\"resolvedRegionLabel\"\n class=\"fixed z-[var(--gr-z-toast)] w-[var(--gr-toaster-width,360px)] max-w-[calc(100vw-2rem)]\"\n :class=\"containerClass\"\n :style=\"containerStyle\"\n @mouseenter=\"hovered = true\"\n @mouseleave=\"hovered = false\"\n @focusin=\"focusWithin = true\"\n @focusout=\"focusWithin = false\"\n >\n <TransitionGroup\n tag=\"div\"\n class=\"grid gap-3\"\n enter-active-class=\"transition duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n enter-from-class=\"opacity-0 translate-y-2\"\n enter-to-class=\"opacity-100 translate-y-0\"\n leave-active-class=\"transition duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-in)] absolute\"\n leave-from-class=\"opacity-100 translate-y-0\"\n leave-to-class=\"opacity-0 translate-y-2\"\n move-class=\"transition-transform duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n >\n <div\n v-for=\"toast in visibleToasts\"\n :key=\"toast.id\"\n data-gr-toast\n :data-tone=\"toast.tone\"\n :data-toast-id=\"toast.id\"\n :role=\"metaFor(toast.tone).role\"\n aria-atomic=\"true\"\n tabindex=\"-1\"\n class=\"relative overflow-hidden rounded-[var(--gr-radius-lg)] border border-[var(--gr-brd)] bg-[var(--gr-card)] px-4 py-3 shadow-[var(--gr-shadow-2)] focus-visible:outline-none focus-visible:shadow-[var(--gr-shadow-2),0_0_0_2px_var(--gr-ring)]\"\n :class=\"swipeDismiss ? '[touch-action:pan-y]' : ''\"\n :style=\"swipeStyle(toast)\"\n @pointerdown=\"swipe.start\"\n @keydown=\"onToastKeydown($event, toast)\"\n >\n <div class=\"flex items-start gap-3\">\n <GrIcon size=\"md\" class=\"mt-0.5\" :style=\"{ color: metaFor(toast.tone).color }\">\n <component :is=\"metaFor(toast.tone).icon\" />\n </GrIcon>\n <div class=\"min-w-0 flex-1\">\n <div class=\"text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] font-700\">\n {{ toast.title }}\n </div>\n <div v-if=\"toast.message\" class=\"mt-0.5 text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] text-[var(--gr-muted-fg)]\">\n {{ toast.message }}\n </div>\n\n <!-- Action-кнопки тоста: например «Отменить»/«Повторить». Можно\n задать массивом (`actions`) или переопределить слотом. -->\n <div v-if=\"hasActions(toast)\" class=\"mt-2 flex flex-wrap gap-2\">\n <slot name=\"actions\" :toast=\"toast\" :dismiss=\"() => dismiss(toast.id)\">\n <GrButton\n v-for=\"(action, index) in toastActions(toast)\"\n :key=\"index\"\n :variant=\"action.variant ?? 'outline'\"\n :size=\"action.size ?? 'sm'\"\n data-gr-toast-action\n @click=\"onAction(toast, action)\"\n >\n {{ action.label }}\n </GrButton>\n </slot>\n </div>\n </div>\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedDismissLabel\"\n @click=\"dismiss(toast.id)\"\n >\n <GrIcon size=\"sm\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <!-- Прогресс до автозакрытия; замирает вместе с таймером на паузе. -->\n <div\n v-if=\"toast.timeoutMs > 0\"\n data-gr-toast-progress\n aria-hidden=\"true\"\n class=\"gr-toast-progress absolute bottom-0 left-0 h-[2px] w-full origin-left\"\n :style=\"{\n backgroundColor: metaFor(toast.tone).color,\n animationDuration: `${toast.timeoutMs}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }\"\n />\n </div>\n </TransitionGroup>\n </div>\n </teleport>\n</template>\n\n<style scoped>\n.gr-toast-progress {\n animation-name: gr-toast-progress;\n animation-timing-function: linear;\n animation-fill-mode: forwards;\n}\n\n@keyframes gr-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n\n/*\n * Единственная анимация пакета, которую глобальный кламп чинит неверно.\n *\n * У полосы `animation-fill-mode: forwards`, поэтому сжатая до `0.01ms` она не\n * возвращается в исходное состояние, а фиксируется на `scaleX(0)`: таймер\n * выглядит истёкшим, пока тост ещё висит на экране. Полоса — единственный\n * носитель этого смысла, и статичной её оставить нельзя, поэтому под reduce она\n * убирается совсем. Автозакрытие ведёт JS-таймер, оно не страдает, а сам элемент\n * и так `aria-hidden`.\n */\n@media (prefers-reduced-motion: reduce) {\n .gr-toast-progress {\n display: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * GrToaster — GR-примитив области уведомлений (toast).\n *\n * Модель состояния берётся из `useToast`, поэтому в приложении предполагается\n * **один активный `GrToaster`** на корень (или app-scoped через плагин).\n *\n * A11y:\n * - контейнер — `role=\"region\"` с `aria-label` (i18n-проп `regionLabel`);\n * - объявляет каждый тост сам собой: `role=\"status\"`, а критичные\n * (`warning`/`danger`) — ассертивный `role=\"alert\"`. Обёртки-live-region над\n * ними нет намеренно: вложение регионов с разной ассертивностью\n * спецификацией не определено, браузеры и SR расходятся вплоть до потери\n * объявления;\n * - стек достижим с клавиатуры по `focusHotkey` (по умолчанию `F6`) — тосты\n * телепортированы в конец `body`, и без хоткея кнопка «Отменить» лежала бы\n * за пределами разумного числа нажатий `Tab`.\n *\n * UX:\n * - `maxVisible` ограничивает стек; лишние ждут в очереди (их таймеры на паузе);\n * - автозакрытие **останавливается под курсором/фокусом** (WCAG 2.2.1) и\n * визуализируется прогресс-баром до закрытия;\n * - `placement` настраивает угол экрана; слой — `--gr-z-toast`.\n */\nimport { computed, nextTick, ref, useSlots, watchEffect } from 'vue'\n\nimport { usePortalTarget } from '../../composables/usePortalTarget'\nimport type { Component } from 'vue'\n\nimport { useDragGesture } from '../../composables/useDragGesture'\nimport { useToast } from '../../composables/useToast'\nimport { vHotkey } from '../../directives'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrToastTone, Toast, ToastAction } from '../../composables/useToast'\nimport GrButton from '../GrButton'\nimport GrIcon from '../GrIcon'\nimport {\n PLACEMENT_CLASS,\n SWIPE_DIRECTION,\n SWIPE_FLY_OUT_PX,\n SWIPE_RESISTANCE,\n SWIPE_THRESHOLD_MIN_PX,\n SWIPE_THRESHOLD_RATIO,\n swipeOpacity,\n type GrToasterPlacement,\n} from './grToasterStyles'\n\nimport IconCheck from '~icons/lucide/check-circle'\nimport IconWarning from '~icons/lucide/alert-triangle'\nimport IconError from '~icons/lucide/x-circle'\nimport IconInfo from '~icons/lucide/info'\nimport IconClose from '~icons/lucide/x'\n\nexport type { GrToasterPlacement }\n\ninterface ToneMeta {\n icon: Component\n color: string\n role: 'status' | 'alert'\n}\n\n// Единый маппинг по tone'у: иконка, цвет, a11y-роль. На уровне модуля —\n// чтобы не пересоздавать объект на каждый рендер.\nconst TONE_META: Record<GrToastTone, ToneMeta> = {\n primary: { icon: IconInfo, color: 'var(--gr-primary)', role: 'status' },\n neutral: { icon: IconInfo, color: 'var(--gr-muted-fg)', role: 'status' },\n info: { icon: IconInfo, color: 'var(--gr-info)', role: 'status' },\n success: { icon: IconCheck, color: 'var(--gr-success)', role: 'status' },\n warning: { icon: IconWarning, color: 'var(--gr-warning)', role: 'alert' },\n danger: { icon: IconError, color: 'var(--gr-danger)', role: 'alert' },\n slate: { icon: IconInfo, color: 'var(--gr-slate)', role: 'status' },\n azure: { icon: IconInfo, color: 'var(--gr-azure)', role: 'status' },\n}\n\nexport interface GrToasterProps {\n /** Угол экрана для стека уведомлений. */\n placement?: GrToasterPlacement\n /** Максимум одновременно видимых тостов; остальные ждут в очереди. По умолчанию `4`. */\n maxVisible?: number\n /** A11y-лейбл кнопки закрытия (i18n). */\n dismissLabel?: string\n /** A11y-лейбл контейнера-региона (i18n). */\n regionLabel?: string\n /**\n * Ширина стека. Число — пиксели, строка — любая CSS-длина. Уезжает в\n * `--gr-toaster-width`, поэтому то же самое можно задать и темой.\n */\n width?: string | number\n /**\n * Клавиша перевода фокуса в стек уведомлений; `false` отключает хоткей.\n * `F6` — рекомендация APG для перехода между «регионами» страницы.\n */\n focusHotkey?: string | false\n /**\n * Смахивание тоста к своему краю экрана. Клавиатурный эквивалент — `Delete`\n * и `Backspace` на сфокусированном тосте — остаётся и при выключенном жесте.\n */\n swipeDismiss?: boolean\n}\n\nimport { useGrThemeAttrs } from '../GrConfigProvider/context'\n\nconst props = withDefaults(defineProps<GrToasterProps>(), {\n placement: 'top-right',\n maxVisible: 4,\n dismissLabel: undefined,\n regionLabel: undefined,\n width: undefined,\n focusHotkey: 'F6',\n swipeDismiss: true,\n})\n\n/**\n * Слот `actions` полностью заменяет дефолтные action-кнопки тоста. Получает сам\n * `toast` и `dismiss` — функцию, закрывающую именно этот тост.\n */\ndefineSlots<{\n actions?: (props: { toast: Toast, dismiss: () => void }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst slots = useSlots()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? t('gr.toaster.dismiss', 'Dismiss'))\nconst resolvedRegionLabel = computed(() => props.regionLabel ?? t('gr.toaster.region', 'Notifications'))\n\nconst { list, dismiss, pause, resume } = useToast()\n\n// Единый список кнопок тоста: `action` (шорткат) + массив `actions`.\nfunction toastActions(toast: Toast): ToastAction[] {\n return [...(toast.action ? [toast.action] : []), ...(toast.actions ?? [])]\n}\n\n// Показываем блок действий, если есть кнопки или задан слот `actions`.\nfunction hasActions(toast: Toast): boolean {\n return Boolean(slots.actions) || toastActions(toast).length > 0\n}\n\n// Клик по action-кнопке тоста: вызываем обработчик и (по умолчанию) закрываем тост.\nfunction onAction(toast: Toast, action: ToastAction): void {\n action.onClick()\n if (action.dismissOnClick !== false)\n dismiss(toast.id)\n}\n\n// SSR-guard: на сервере `document.body` недоступен — отключаем `teleport`.\n// Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n// рендер не совпадает с серверным и ломается гидрация (см. композабл).\nconst { target: portalTarget, enabled: teleportEnabled } = usePortalTarget()\n\n// Тема поддерева на телепортированную панель: в DOM она уезжает в `body`, то\n// есть вне обёртки провайдера, и `data-theme` с неё не наследуется. В дереве\n// компонентов панель остаётся внутри — `inject` доходит, и тему она ставит себе\n// сама.\nconst themeAttrs = useGrThemeAttrs()\n\n// Видимые тосты (не больше `maxVisible`); остальные ждут в очереди. Список\n// хранится новейшими вперёд, поэтому «видимые дожинают, новые ждут» — это\n// хвост: старейшие N. Взяв голову, стек показывал бы новейшие, и каждый пуш\n// вытеснял бы с экрана тост, который пользователь ещё читает.\nconst visibleToasts = computed(() => list.value.slice(-Math.max(1, props.maxVisible)))\nconst visibleIds = computed(() => new Set(visibleToasts.value.map(toast => toast.id)))\n\n// Пауза под курсором/фокусом (WCAG 2.2.1). Флага два и они независимы: один\n// `paused` терялся на «фокус внутри + мышь прошла насквозь» — `mouseleave`\n// возобновлял отсчёт под клавиатурным фокусом.\nconst hovered = ref(false)\nconst focusWithin = ref(false)\n\n// Контейнер стека: по нему ищутся тосты — и для фокуса, и для жеста.\nconst containerEl = ref<HTMLElement | null>(null)\n\n// ————— Смахивание.\n\nconst swipingId = ref<string | null>(null)\nconst swipeOffset = ref(0)\nlet swipeStartX = 0\nlet swipeThreshold = SWIPE_THRESHOLD_MIN_PX\n\n// Тост, который тянут, тоже держит таймеры: иначе уведомление сгорало бы прямо\n// под пальцем, посреди жеста.\nconst paused = computed(() => hovered.value || focusWithin.value || swipingId.value !== null)\n\nfunction toastElement(id: string): HTMLElement | null {\n return containerEl.value?.querySelector<HTMLElement>(`[data-gr-toast][data-toast-id=\"${id}\"]`) ?? null\n}\n\n/** Жест отпущен и тост закрывается: переход снова разрешён (см. `releaseSwiped`). */\nconst swipeReleased = ref(false)\n\nfunction resetSwipe(): void {\n swipingId.value = null\n swipeOffset.value = 0\n swipeReleased.value = false\n}\n\n/**\n * Отпущенный тост уезжает за свой край и только потом закрывается.\n *\n * Два кадра здесь обязательны, и оба по одной причине: уходящий элемент Vue\n * больше не перерисовывает — он уезжает с тем стилем, что был на последнем\n * рендере. Значит и разрешение перехода, и конечную точку нужно успеть отдать\n * до закрытия: первый кадр снимает инлайновый `transition: none` (во время\n * жеста он нужен, иначе переходы группы борются с пальцем), второй задаёт точку\n * отлёта — с этого момента анимация уже идёт, и `leave` подхватывает её на ходу.\n *\n * Закрыв тост сразу, получили бы не отлёт, а мгновенное исчезновение: элемент\n * ушёл бы в `leave` с запрещённым переходом.\n */\nasync function releaseSwiped(id: string): Promise<void> {\n swipeReleased.value = true\n await nextTick()\n\n swipeOffset.value = SWIPE_DIRECTION[props.placement] * SWIPE_FLY_OUT_PX\n await nextTick()\n\n dismiss(id)\n resetSwipe()\n}\n\n/**\n * Движение к своему краю идёт один к одному, в обратную сторону — с\n * сопротивлением: тост едет за пальцем, но закрыть его «наоборот» нельзя.\n */\nfunction resistOffset(delta: number): number {\n const toEdge = SWIPE_DIRECTION[props.placement]\n\n return Math.sign(delta) === toEdge ? delta : delta / SWIPE_RESISTANCE\n}\n\nconst swipe = useDragGesture({\n disabled: () => !props.swipeDismiss,\n onStart: (event) => {\n const target = event.target as HTMLElement | null\n // Нажатие на кнопке — это нажатие на кнопке: закрытие и действия тоста не\n // должны превращаться в протяжку.\n if (target?.closest('button, a[href]')) return false\n\n const host = target?.closest<HTMLElement>('[data-gr-toast]')\n const id = host?.dataset.toastId\n if (!host || !id) return false\n\n swipingId.value = id\n swipeOffset.value = 0\n swipeStartX = event.clientX\n swipeThreshold = Math.max(SWIPE_THRESHOLD_MIN_PX, host.getBoundingClientRect().width * SWIPE_THRESHOLD_RATIO)\n },\n onMove: (event) => {\n // Против выделения текста тоста во время протяжки.\n event.preventDefault()\n swipeOffset.value = resistOffset(event.clientX - swipeStartX)\n },\n onEnd: () => {\n const id = swipingId.value\n const passed = id !== null\n && Math.sign(swipeOffset.value) === SWIPE_DIRECTION[props.placement]\n && Math.abs(swipeOffset.value) >= swipeThreshold\n\n if (passed) void releaseSwiped(id)\n else resetSwipe()\n },\n // Обрыв возвращает тост на место, а не дожимает смахивание: указатель забрал\n // браузер, а не пользователь довёл жест.\n onCancel: resetSwipe,\n})\n\nfunction swipeStyle(toast: Toast): Record<string, string> | undefined {\n if (swipingId.value !== toast.id) return undefined\n\n return {\n transform: `translateX(${swipeOffset.value}px)`,\n opacity: String(swipeOpacity(swipeOffset.value, swipeThreshold)),\n // Переходы группы во время жеста борются с пальцем: тост едет с задержкой\n // и «догоняет» курсор после отпускания. После отпускания запрет снимается —\n // уходящему тосту переход нужен, иначе он не уедет вовсе.\n ...(swipeReleased.value ? {} : { transition: 'none' }),\n }\n}\n\n/**\n * Клавиатурный эквивалент смахивания. `Escape` намеренно не берём: тостер не\n * модальный слой, и перехват закрывал бы уведомление вместо диалога под ним.\n */\nfunction onToastKeydown(event: KeyboardEvent, toast: Toast): void {\n if (event.key !== 'Delete' && event.key !== 'Backspace') return\n\n event.preventDefault()\n\n const index = visibleToasts.value.findIndex(item => item.id === toast.id)\n const next = visibleToasts.value[index + 1] ?? visibleToasts.value[index - 1]\n\n dismiss(toast.id)\n\n // Фокус не должен упасть на `body`: дальше по стеку есть что читать.\n if (next) void nextTick(() => toastElement(next.id)?.focus())\n}\n\n// Единый источник правды по таймерам: тост тикает, только если он видим И стек не на\n// паузе. Очередь и hover — на паузе. Реагирует на изменения списка/hover.\n//\n// `timeoutMs` читается ради зависимости, а не ради значения: `toast.promise` и\n// `update` перезапускают таймер уже существующего тоста, не трогая ни список, ни\n// паузу. Без этой зависимости эффект не просыпался бы, и заново заведённый\n// таймер тикал бы под курсором или у тоста, который ещё ждёт в очереди, — тот\n// сгорал бы, ни разу не показавшись.\nwatchEffect(() => {\n for (const toast of list.value) {\n const shouldTick = toast.timeoutMs > 0 && visibleIds.value.has(toast.id) && !paused.value\n\n if (shouldTick)\n resume(toast.id)\n else\n pause(toast.id)\n }\n})\n\nfunction metaFor(tone: GrToastTone): ToneMeta {\n return TONE_META[tone] ?? TONE_META.info\n}\n\nconst containerClass = computed(() => PLACEMENT_CLASS[props.placement])\n\nconst containerStyle = computed(() => {\n if (props.width === undefined) return undefined\n\n return { '--gr-toaster-width': typeof props.width === 'number' ? `${props.width}px` : props.width }\n})\n\n\n/**\n * Переводит фокус на верхний тост. Тост фокусируем только программно\n * (`tabindex=\"-1\"`): собственной остановкой `Tab` стек быть не должен, иначе\n * всплывающее уведомление перехватывало бы обход страницы.\n */\nfunction focus(): boolean {\n const target = containerEl.value?.querySelector<HTMLElement>('[data-gr-toast]')\n if (!target) return false\n\n target.focus()\n return true\n}\n\nconst hotkeyBinding = computed(() => ({\n enabled: props.focusHotkey !== false && visibleToasts.value.length > 0,\n handlers: { [props.focusHotkey || 'F6']: { handler: onFocusHotkey, allowInEditable: true } },\n}))\n\nfunction onFocusHotkey(event: KeyboardEvent): void {\n if (focus()) event.preventDefault()\n}\n\ndefineExpose({ focus })\n</script>\n\n<template>\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <div\n ref=\"containerEl\"\n v-hotkey=\"hotkeyBinding\"\n v-bind=\"themeAttrs\"\n data-gr-toaster\n data-gr-overlay-root\n role=\"region\"\n :aria-label=\"resolvedRegionLabel\"\n class=\"fixed z-[var(--gr-z-toast)] w-[var(--gr-toaster-width,360px)] max-w-[calc(100vw-2rem)]\"\n :class=\"containerClass\"\n :style=\"containerStyle\"\n @mouseenter=\"hovered = true\"\n @mouseleave=\"hovered = false\"\n @focusin=\"focusWithin = true\"\n @focusout=\"focusWithin = false\"\n >\n <TransitionGroup\n tag=\"div\"\n class=\"grid gap-3\"\n enter-active-class=\"transition duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n enter-from-class=\"opacity-0 translate-y-2\"\n enter-to-class=\"opacity-100 translate-y-0\"\n leave-active-class=\"transition duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-in)] absolute\"\n leave-from-class=\"opacity-100 translate-y-0\"\n leave-to-class=\"opacity-0 translate-y-2\"\n move-class=\"transition-transform duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n >\n <div\n v-for=\"toast in visibleToasts\"\n :key=\"toast.id\"\n data-gr-toast\n :data-tone=\"toast.tone\"\n :data-toast-id=\"toast.id\"\n :role=\"metaFor(toast.tone).role\"\n aria-atomic=\"true\"\n tabindex=\"-1\"\n class=\"relative overflow-hidden rounded-[var(--gr-radius-lg)] border border-[var(--gr-brd)] bg-[var(--gr-card)] px-4 py-3 shadow-[var(--gr-shadow-2)] focus-visible:outline-none focus-visible:shadow-[var(--gr-shadow-2),0_0_0_2px_var(--gr-ring)]\"\n :class=\"swipeDismiss ? '[touch-action:pan-y]' : ''\"\n :style=\"swipeStyle(toast)\"\n @pointerdown=\"swipe.start\"\n @keydown=\"onToastKeydown($event, toast)\"\n >\n <div class=\"flex items-start gap-3\">\n <GrIcon size=\"md\" class=\"mt-0.5\" :style=\"{ color: metaFor(toast.tone).color }\">\n <component :is=\"metaFor(toast.tone).icon\" />\n </GrIcon>\n <div class=\"min-w-0 flex-1\">\n <div class=\"text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] font-700\">\n {{ toast.title }}\n </div>\n <div v-if=\"toast.message\" class=\"mt-0.5 text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] text-[var(--gr-muted-fg)]\">\n {{ toast.message }}\n </div>\n\n <!-- Action-кнопки тоста: например «Отменить»/«Повторить». Можно\n задать массивом (`actions`) или переопределить слотом. -->\n <div v-if=\"hasActions(toast)\" class=\"mt-2 flex flex-wrap gap-2\">\n <slot name=\"actions\" :toast=\"toast\" :dismiss=\"() => dismiss(toast.id)\">\n <GrButton\n v-for=\"(action, index) in toastActions(toast)\"\n :key=\"index\"\n :variant=\"action.variant ?? 'outline'\"\n :size=\"action.size ?? 'sm'\"\n data-gr-toast-action\n @click=\"onAction(toast, action)\"\n >\n {{ action.label }}\n </GrButton>\n </slot>\n </div>\n </div>\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedDismissLabel\"\n @click=\"dismiss(toast.id)\"\n >\n <GrIcon size=\"sm\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <!-- Прогресс до автозакрытия; замирает вместе с таймером на паузе. -->\n <div\n v-if=\"toast.timeoutMs > 0\"\n data-gr-toast-progress\n aria-hidden=\"true\"\n class=\"gr-toast-progress absolute bottom-0 left-0 h-[2px] w-full origin-left\"\n :style=\"{\n backgroundColor: metaFor(toast.tone).color,\n animationDuration: `${toast.timeoutMs}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }\"\n />\n </div>\n </TransitionGroup>\n </div>\n </teleport>\n</template>\n\n<style scoped>\n.gr-toast-progress {\n animation-name: gr-toast-progress;\n animation-timing-function: linear;\n animation-fill-mode: forwards;\n}\n\n@keyframes gr-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n\n/*\n * Единственная анимация пакета, которую глобальный кламп чинит неверно.\n *\n * У полосы `animation-fill-mode: forwards`, поэтому сжатая до `0.01ms` она не\n * возвращается в исходное состояние, а фиксируется на `scaleX(0)`: таймер\n * выглядит истёкшим, пока тост ещё висит на экране. Полоса — единственный\n * носитель этого смысла, и статичной её оставить нельзя, поэтому под reduce она\n * убирается совсем. Автозакрытие ведёт JS-таймер, оно не страдает, а сам элемент\n * и так `aria-hidden`.\n */\n@media (prefers-reduced-motion: reduce) {\n .gr-toast-progress {\n display: none;\n }\n}\n</style>\n"],"mappings":"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"}