@feugene/granularity 0.9.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. package/README.md +44 -45
  2. package/dist/GrButtonGroup.css +1 -1
  3. package/dist/GrFormFile.css +1 -1
  4. package/dist/GrIcon.css +1 -1
  5. package/dist/GrLoading.css +1 -1
  6. package/dist/GrSkeleton.css +1 -1
  7. package/dist/GrToaster.css +1 -0
  8. package/dist/GrTree.css +1 -1
  9. package/dist/chunks/{adapter-BqqjEZGU.js.map → adapter-CAOxmn2G.js.map} +1 -1
  10. package/dist/chunks/arrow-up-C_G4jg8M.js.map +1 -0
  11. package/dist/chunks/{classTokens-BPeBXNzu.js.map → classTokens-BzazaERD.js.map} +1 -1
  12. package/dist/chunks/{config-B_nSBc7W.js → config-1uSYl98Y.js} +5 -5
  13. package/dist/chunks/{config-B_nSBc7W.js.map → config-1uSYl98Y.js.map} +1 -1
  14. package/dist/chunks/{config-DaHI5bD_2.js.map → config-8Pcutu1j2.js.map} +1 -1
  15. package/dist/chunks/{config-DoBk_CzG.js.map → config-B0qIpo8m.js.map} +1 -1
  16. package/dist/chunks/{config-BY81UKv0.js → config-B7IIWGt_2.js} +3 -3
  17. package/dist/chunks/config-B7IIWGt_2.js.map +1 -0
  18. package/dist/chunks/{config-DbghHyY_.js.map → config-BEegFP74.js.map} +1 -1
  19. package/dist/chunks/{config-72sW9Bag2.js → config-BIRO0vDJ2.js} +6 -6
  20. package/dist/chunks/{config-72sW9Bag2.js.map → config-BIRO0vDJ2.js.map} +1 -1
  21. package/dist/chunks/{config-Dzd6BzIN2.js.map → config-BLHqZhy-2.js.map} +1 -1
  22. package/dist/chunks/{config-BV4vBzdm.js.map → config-BNu8lFQB.js.map} +1 -1
  23. package/dist/chunks/config-BOlr7qw7.js +106 -0
  24. package/dist/chunks/config-BOlr7qw7.js.map +1 -0
  25. package/dist/chunks/{config-B1kFmhpM.js.map → config-BUHuGBik.js.map} +1 -1
  26. package/dist/chunks/{config-DF_RlCIO.js.map → config-BWGGp7uV.js.map} +1 -1
  27. package/dist/chunks/config-BX1JTrkV2.js +14 -0
  28. package/dist/chunks/config-BX1JTrkV2.js.map +1 -0
  29. package/dist/chunks/{config-BduXEXjt.js.map → config-BXtAmU3s.js.map} +1 -1
  30. package/dist/chunks/{config-O1_FTqD3.js.map → config-Bah4oHwL.js.map} +1 -1
  31. package/dist/chunks/{config-Br_qI3Za.js → config-C49QUoJL.js} +3 -3
  32. package/dist/chunks/config-C49QUoJL.js.map +1 -0
  33. package/dist/chunks/{config-3I3vqDW4.js → config-C4snOIXg.js} +1 -1
  34. package/dist/chunks/{config-3I3vqDW4.js.map → config-C4snOIXg.js.map} +1 -1
  35. package/dist/chunks/{config-CdJpnjDj.js → config-C7NpDrfO.js} +5 -5
  36. package/dist/chunks/{config-CdJpnjDj.js.map → config-C7NpDrfO.js.map} +1 -1
  37. package/dist/chunks/{config-DSBlHQVa.js.map → config-C86UzUYf.js.map} +1 -1
  38. package/dist/chunks/{config-BgBWt9-A.js.map → config-C9AcVDkv.js.map} +1 -1
  39. package/dist/chunks/{config-BeQuykDk2.js.map → config-CGHLoBlH2.js.map} +1 -1
  40. package/dist/chunks/{config-ChQxl75m.js.map → config-CThzliFi.js.map} +1 -1
  41. package/dist/chunks/{config-DmjwIqx1.js.map → config-Cb2PAlSD.js.map} +1 -1
  42. package/dist/chunks/{config-BnWSHTEU.js → config-ClhKPJvg.js} +5 -5
  43. package/dist/chunks/{config-BnWSHTEU.js.map → config-ClhKPJvg.js.map} +1 -1
  44. package/dist/chunks/{config-d386uO3O.js → config-Cpi9Mh2J.js} +3 -3
  45. package/dist/chunks/{config-d386uO3O.js.map → config-Cpi9Mh2J.js.map} +1 -1
  46. package/dist/chunks/{config-Cp_XHsze.js → config-CrG1XiE7.js} +4 -4
  47. package/dist/chunks/{config-Cp_XHsze.js.map → config-CrG1XiE7.js.map} +1 -1
  48. package/dist/chunks/{config-C5OwJM3-.js → config-CziwTDDL.js} +3 -3
  49. package/dist/chunks/{config-C5OwJM3-.js.map → config-CziwTDDL.js.map} +1 -1
  50. package/dist/chunks/{config-BbKroFtR.js.map → config-D7GZO-Dx.js.map} +1 -1
  51. package/dist/chunks/{config-CUwF4E4R.js.map → config-DBmdgoVf.js.map} +1 -1
  52. package/dist/chunks/{config-C9MblwGq.js.map → config-DICOd-2X.js.map} +1 -1
  53. package/dist/chunks/{config-C6zT56Jc2.js → config-DUcu1D68.js} +3 -3
  54. package/dist/chunks/config-DUcu1D68.js.map +1 -0
  55. package/dist/chunks/{config-D4s_QWVP.js → config-D_R56_gT.js} +5 -5
  56. package/dist/chunks/{config-D4s_QWVP.js.map → config-D_R56_gT.js.map} +1 -1
  57. package/dist/chunks/{config-_3De4HFf.js → config-DezDih8_.js} +4 -4
  58. package/dist/chunks/config-DezDih8_.js.map +1 -0
  59. package/dist/chunks/{config-CFf7xaG1.js → config-DnvPqu4g.js} +3 -3
  60. package/dist/chunks/{config-CFf7xaG1.js.map → config-DnvPqu4g.js.map} +1 -1
  61. package/dist/chunks/{config-D6Oe3blW2.js.map → config-DpqOnJiz2.js.map} +1 -1
  62. package/dist/chunks/{config-V3LF-b_F.js.map → config-Dxuwwepq.js.map} +1 -1
  63. package/dist/chunks/{config-CvGdaXvH.js.map → config-HZto2PVy.js.map} +1 -1
  64. package/dist/chunks/{config-BOdu-O_B.js.map → config-JOtB4mzs.js.map} +1 -1
  65. package/dist/chunks/{config-iuMz79Mg.js.map → config-JpHA4Xx0.js.map} +1 -1
  66. package/dist/chunks/{config-CetsgpaZ2.js → config-LGrZIdaZ2.js} +1 -1
  67. package/dist/chunks/{config-CetsgpaZ2.js.map → config-LGrZIdaZ2.js.map} +1 -1
  68. package/dist/chunks/{config-Dcb7xK-C.js.map → config-PFg39mYo.js.map} +1 -1
  69. package/dist/chunks/{config-CU_CBd0k.js → config-QYGvEBu5.js} +2 -2
  70. package/dist/chunks/{config-CU_CBd0k.js.map → config-QYGvEBu5.js.map} +1 -1
  71. package/dist/chunks/{config-BU0olrvr.js.map → config-QaIc_gRa.js.map} +1 -1
  72. package/dist/chunks/{config-vcDVCYXo.js.map → config-U4m9PSql.js.map} +1 -1
  73. package/dist/chunks/{config-Ci9nXGUV.js.map → config-ZiXWIRBX.js.map} +1 -1
  74. package/dist/chunks/{config-B2YTEe_h.js → config-eYLZYOUS2.js} +63 -63
  75. package/dist/chunks/config-eYLZYOUS2.js.map +1 -0
  76. package/dist/chunks/{config-Bcf-8XlK2.js.map → config-ipvU0lzP2.js.map} +1 -1
  77. package/dist/chunks/{config-B61cX08u.js → config-jBp9wVv-.js} +4 -4
  78. package/dist/chunks/{config-B61cX08u.js.map → config-jBp9wVv-.js.map} +1 -1
  79. package/dist/chunks/{config-D5ek7VSi.js.map → config-xR4XVmcy.js.map} +1 -1
  80. package/dist/chunks/{dialogShared-BtXUk6Zc.js.map → dialogShared-CxW62PQE.js.map} +1 -1
  81. package/dist/chunks/en-BdtiS2d3.js +138 -0
  82. package/dist/chunks/en-BdtiS2d3.js.map +1 -0
  83. package/dist/chunks/es-BGRMZbe9.js +138 -0
  84. package/dist/chunks/es-BGRMZbe9.js.map +1 -0
  85. package/dist/chunks/{es-Bx3Kuy6z.js → es-DBhctWg-.js} +3 -3
  86. package/dist/chunks/{es-Bx3Kuy6z.js.map → es-DBhctWg-.js.map} +1 -1
  87. package/dist/chunks/{fileValidation-c_iBg74s.js → fileValidation-C3WFaWU2.js} +2 -1
  88. package/dist/chunks/{fileValidation-c_iBg74s.js.map → fileValidation-C3WFaWU2.js.map} +1 -1
  89. package/dist/chunks/{grBadgeStyles-Cq0hH2Ls.js → grBadgeStyles-DrycBuKT.js} +13 -13
  90. package/dist/chunks/grBadgeStyles-DrycBuKT.js.map +1 -0
  91. package/dist/chunks/grButtonStyles-C7Yga_Q1.js +191 -0
  92. package/dist/chunks/grButtonStyles-C7Yga_Q1.js.map +1 -0
  93. package/dist/chunks/grDropdownStyles-B_-DKytw.js +35 -0
  94. package/dist/chunks/grDropdownStyles-B_-DKytw.js.map +1 -0
  95. package/dist/chunks/{grLoadingStyles-DmI-SJ3i.js.map → grLoadingStyles-R5sreH__.js.map} +1 -1
  96. package/dist/chunks/{grModalStyles-DS1Vl6EW.js → grModalStyles-DWd3i05Q.js} +7 -7
  97. package/dist/chunks/grModalStyles-DWd3i05Q.js.map +1 -0
  98. package/dist/chunks/{grSelectStyles-pUt19VGy.js → grSelectStyles-CLK5hRd3.js} +3 -3
  99. package/dist/chunks/grSelectStyles-CLK5hRd3.js.map +1 -0
  100. package/dist/chunks/{grStyle-CUaTmpyW.js → grStyle-Blc9otAx.js} +6 -6
  101. package/dist/chunks/grStyle-Blc9otAx.js.map +1 -0
  102. package/dist/chunks/{granularityI18n-LcJ88Vce.js → granularityI18n-DoV7Ca2m.js} +18 -3
  103. package/dist/chunks/granularityI18n-DoV7Ca2m.js.map +1 -0
  104. package/dist/chunks/info-Be2OccTu.js.map +1 -0
  105. package/dist/chunks/loader-circle-CfVLz7lb.js.map +1 -0
  106. package/dist/chunks/ru-BFCnOiML.js +138 -0
  107. package/dist/chunks/ru-BFCnOiML.js.map +1 -0
  108. package/dist/chunks/{safelist-_UVzJrgE2.js → safelist-CkF4v7DC.js} +3 -3
  109. package/dist/chunks/safelist-CkF4v7DC.js.map +1 -0
  110. package/dist/chunks/{safelist-DccvJqCE.js → safelist-Do2RtzLB.js} +3 -3
  111. package/dist/chunks/{safelist-DccvJqCE.js.map → safelist-Do2RtzLB.js.map} +1 -1
  112. package/dist/chunks/shared-ChvcQgDl.js +168 -0
  113. package/dist/chunks/shared-ChvcQgDl.js.map +1 -0
  114. package/dist/chunks/{transition-mJ3PZCHH.js → transition-TLFw4KBn.js} +26 -12
  115. package/dist/chunks/{transition-mJ3PZCHH.js.map → transition-TLFw4KBn.js.map} +1 -1
  116. package/dist/chunks/useAddonMeasurement-DM0akF4C.js +65 -0
  117. package/dist/chunks/useAddonMeasurement-DM0akF4C.js.map +1 -0
  118. package/dist/chunks/useEscapeToClose-C2t_S1gh.js +27 -0
  119. package/dist/chunks/useEscapeToClose-C2t_S1gh.js.map +1 -0
  120. package/dist/chunks/useFloating-DtM6TyE4.js +1249 -0
  121. package/dist/chunks/useFloating-DtM6TyE4.js.map +1 -0
  122. package/dist/chunks/x-2KXnqxiD.js.map +1 -0
  123. package/dist/chunks/x-circle-BCBURwyX.js.map +1 -0
  124. package/dist/components/GrAlert/chunks/{GrAlert-HyCjYJeL.js → GrAlert-DPJSU_fG.js} +40 -37
  125. package/dist/components/GrAlert/chunks/GrAlert-DPJSU_fG.js.map +1 -0
  126. package/dist/components/GrAlert/index.js +2 -2
  127. package/dist/components/GrAvatar/chunks/{GrAvatar-BjnPKNAL.js → GrAvatar-96CpTCq5.js} +2 -2
  128. package/dist/components/GrAvatar/chunks/{GrAvatar-BjnPKNAL.js.map → GrAvatar-96CpTCq5.js.map} +1 -1
  129. package/dist/components/GrAvatar/index.js +2 -2
  130. package/dist/components/GrBadge/chunks/{GrBadge-Cs_cVfuO.js → GrBadge-B77XlaqE.js} +3 -3
  131. package/dist/components/GrBadge/chunks/{GrBadge-Cs_cVfuO.js.map → GrBadge-B77XlaqE.js.map} +1 -1
  132. package/dist/components/GrBadge/index.js +2 -2
  133. package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-BEnE9DyJ.js → GrBadgeWrap-CcktMOdg.js} +2 -2
  134. package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-BEnE9DyJ.js.map → GrBadgeWrap-CcktMOdg.js.map} +1 -1
  135. package/dist/components/GrBadgeWrap/index.js +2 -2
  136. package/dist/components/GrBottomNav/chunks/{GrBottomNav-XPHwd7UQ.js.map → GrBottomNav-usuMgG8_.js.map} +1 -1
  137. package/dist/components/GrBottomNav/index.js +2 -2
  138. package/dist/components/GrButton/chunks/GrButton-BlffdeJr.js +124 -0
  139. package/dist/components/GrButton/chunks/GrButton-BlffdeJr.js.map +1 -0
  140. package/dist/components/GrButton/index.js +4 -4
  141. package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CPOH6V-P.js → GrButtonGroup-Bs9dEKfA.js} +1 -1
  142. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-Bs9dEKfA.js.map +1 -0
  143. package/dist/components/GrButtonGroup/index.js +2 -2
  144. package/dist/components/GrCard/chunks/{GrCard-6yk1RRAQ.js → GrCard-DUNMkuHW.js} +3 -3
  145. package/dist/components/GrCard/chunks/GrCard-DUNMkuHW.js.map +1 -0
  146. package/dist/components/GrCard/index.js +2 -2
  147. package/dist/components/GrCheckbox/chunks/{GrCheckbox-Bt3d7ckE.js → GrCheckbox-DJhzZZdO.js} +66 -18
  148. package/dist/components/GrCheckbox/chunks/GrCheckbox-DJhzZZdO.js.map +1 -0
  149. package/dist/components/GrCheckbox/index.js +2 -2
  150. package/dist/components/GrCollapse/chunks/{GrCollapse-CU94BTAR.js → GrCollapse-CPPLh_xU.js} +12 -12
  151. package/dist/components/GrCollapse/chunks/{GrCollapse-CU94BTAR.js.map → GrCollapse-CPPLh_xU.js.map} +1 -1
  152. package/dist/components/GrCollapse/index.js +2 -2
  153. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-Br6WJkLE.js → GrConfirmDialog-BWYKCsHd.js} +20 -14
  154. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-BWYKCsHd.js.map +1 -0
  155. package/dist/components/GrConfirmDialog/index.js +2 -2
  156. package/dist/components/GrDataTable/chunks/{GrDataTable-ClcYiyc8.js → GrDataTable-ptbARmJ3.js} +7 -7
  157. package/dist/components/GrDataTable/chunks/{GrDataTable-ClcYiyc8.js.map → GrDataTable-ptbARmJ3.js.map} +1 -1
  158. package/dist/components/GrDataTable/index.js +2 -2
  159. package/dist/components/GrDialog/chunks/{GrDialog-C7XLxp8e.js → GrDialog-5DAS-1Lb.js} +14 -12
  160. package/dist/components/GrDialog/chunks/GrDialog-5DAS-1Lb.js.map +1 -0
  161. package/dist/components/GrDialog/index.js +3 -3
  162. package/dist/components/GrDialogService/chunks/GrDialogService-DY_fmaMu.js +425 -0
  163. package/dist/components/GrDialogService/chunks/GrDialogService-DY_fmaMu.js.map +1 -0
  164. package/dist/components/GrDialogService/index.js +3 -0
  165. package/dist/components/GrDrawer/chunks/{GrDrawer-BW3wtI7h.js → GrDrawer-D1kzsjXN.js} +19 -15
  166. package/dist/components/GrDrawer/chunks/GrDrawer-D1kzsjXN.js.map +1 -0
  167. package/dist/components/GrDrawer/index.js +2 -2
  168. package/dist/components/GrDropdown/chunks/GrDropdown-BXb5Cxxf.js +95 -0
  169. package/dist/components/GrDropdown/chunks/GrDropdown-BXb5Cxxf.js.map +1 -0
  170. package/dist/components/GrDropdown/index.js +2 -2
  171. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-CLPwbiyG.js → GrDropdownMenu-DFEJLPcV.js} +10 -10
  172. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-CLPwbiyG.js.map → GrDropdownMenu-DFEJLPcV.js.map} +1 -1
  173. package/dist/components/GrDropdownMenu/index.js +2 -2
  174. package/dist/components/GrEmptyState/chunks/{GrEmptyState-Dj2V5GaX.js → GrEmptyState-CA3BiTV7.js} +3 -3
  175. package/dist/components/GrEmptyState/chunks/{GrEmptyState-Dj2V5GaX.js.map → GrEmptyState-CA3BiTV7.js.map} +1 -1
  176. package/dist/components/GrEmptyState/index.js +2 -2
  177. package/dist/components/GrFileUpload/chunks/{GrFileUpload-BEKNB1RM.js → GrFileUpload-DvWPpiJS.js} +26 -23
  178. package/dist/components/GrFileUpload/chunks/GrFileUpload-DvWPpiJS.js.map +1 -0
  179. package/dist/components/GrFileUpload/index.js +3 -3
  180. package/dist/components/GrFormField/chunks/{GrFormField-U7c_aDPJ.js → GrFormField-BujNQrw8.js} +1 -1
  181. package/dist/components/GrFormField/chunks/{GrFormField-U7c_aDPJ.js.map → GrFormField-BujNQrw8.js.map} +1 -1
  182. package/dist/components/GrFormField/index.js +2 -2
  183. package/dist/components/GrFormFile/chunks/{GrFormFile-CIleXnfr.js → GrFormFile-CXcqqdvV.js} +38 -31
  184. package/dist/components/GrFormFile/chunks/GrFormFile-CXcqqdvV.js.map +1 -0
  185. package/dist/components/GrFormFile/index.js +2 -2
  186. package/dist/components/GrFormSection/chunks/{GrFormSection-DpGhY7MX.js → GrFormSection-Ce21jcgo.js} +1 -1
  187. package/dist/components/GrFormSection/chunks/{GrFormSection-DpGhY7MX.js.map → GrFormSection-Ce21jcgo.js.map} +1 -1
  188. package/dist/components/GrFormSection/index.js +2 -2
  189. package/dist/components/GrIcon/chunks/{GrIcon-BV7RBU_0.js → GrIcon-D9AWQLk2.js} +3 -3
  190. package/dist/components/GrIcon/chunks/{GrIcon-BV7RBU_0.js.map → GrIcon-D9AWQLk2.js.map} +1 -1
  191. package/dist/components/GrIcon/index.js +2 -2
  192. package/dist/components/GrImageViewer/chunks/{GrImageViewer-CsvXQHM9.js → GrImageViewer-OCUIrNh2.js} +44 -33
  193. package/dist/components/GrImageViewer/chunks/GrImageViewer-OCUIrNh2.js.map +1 -0
  194. package/dist/components/GrImageViewer/index.js +2 -2
  195. package/dist/components/GrInput/chunks/{GrInput-gwFQ1sk4.js → GrInput-C8pOJgCi.js} +43 -66
  196. package/dist/components/GrInput/chunks/GrInput-C8pOJgCi.js.map +1 -0
  197. package/dist/components/GrInput/index.js +2 -2
  198. package/dist/components/GrInputTag/chunks/{GrInputTag-Dcj3F6rQ.js → GrInputTag-zMgFqp2g.js} +27 -24
  199. package/dist/components/GrInputTag/chunks/GrInputTag-zMgFqp2g.js.map +1 -0
  200. package/dist/components/GrInputTag/index.js +2 -2
  201. package/dist/components/GrLink/chunks/{GrLink-BMwn2iJ9.js → GrLink-CYUe-Q8k.js} +14 -7
  202. package/dist/components/GrLink/chunks/GrLink-CYUe-Q8k.js.map +1 -0
  203. package/dist/components/GrLink/index.js +2 -2
  204. package/dist/components/GrList/chunks/{GrList-D8nM1FuV.js → GrList-Dx772Ns4.js} +1 -1
  205. package/dist/components/GrList/chunks/{GrList-D8nM1FuV.js.map → GrList-Dx772Ns4.js.map} +1 -1
  206. package/dist/components/GrList/index.js +2 -2
  207. package/dist/components/GrLoading/chunks/{GrLoading-mfyfghxs.js → GrLoading--X_7rdbb.js} +7 -7
  208. package/dist/components/GrLoading/chunks/{GrLoading-mfyfghxs.js.map → GrLoading--X_7rdbb.js.map} +1 -1
  209. package/dist/components/GrLoading/index.js +2 -2
  210. package/dist/components/GrModal/chunks/{GrModal-lh0lOmoi.js → GrModal-BkjW9jZS.js} +7 -7
  211. package/dist/components/GrModal/chunks/{GrModal-lh0lOmoi.js.map → GrModal-BkjW9jZS.js.map} +1 -1
  212. package/dist/components/GrModal/index.js +3 -3
  213. package/dist/components/GrNavbar/chunks/{GrNavbar-BH7XiF9q.js → GrNavbar-DW3MiIIf.js} +6 -6
  214. package/dist/components/GrNavbar/chunks/{GrNavbar-BH7XiF9q.js.map → GrNavbar-DW3MiIIf.js.map} +1 -1
  215. package/dist/components/GrNavbar/index.js +2 -2
  216. package/dist/components/GrNumberInput/chunks/{GrNumberInput-YIWhV85S.js → GrNumberInput-CldPIWQx.js} +51 -61
  217. package/dist/components/GrNumberInput/chunks/GrNumberInput-CldPIWQx.js.map +1 -0
  218. package/dist/components/GrNumberInput/index.js +2 -2
  219. package/dist/components/GrPagination/chunks/GrPagination-Bu9wv6it.js +165 -0
  220. package/dist/components/GrPagination/chunks/GrPagination-Bu9wv6it.js.map +1 -0
  221. package/dist/components/GrPagination/index.js +2 -2
  222. package/dist/components/GrProgressBar/chunks/{GrProgressBar-C2HdYjp4.js → GrProgressBar-CC3z-Zqp.js} +3 -3
  223. package/dist/components/GrProgressBar/chunks/{GrProgressBar-C2HdYjp4.js.map → GrProgressBar-CC3z-Zqp.js.map} +1 -1
  224. package/dist/components/GrProgressBar/index.js +2 -2
  225. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-BV4EW3XO.js → GrPromptDialog-TmWXbpEP.js} +31 -23
  226. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-TmWXbpEP.js.map +1 -0
  227. package/dist/components/GrPromptDialog/index.js +2 -2
  228. package/dist/components/GrRadio/chunks/{GrRadio-IFU9Hiu2.js → GrRadio-CPTixXaY.js} +6 -6
  229. package/dist/components/GrRadio/chunks/{GrRadio-IFU9Hiu2.js.map → GrRadio-CPTixXaY.js.map} +1 -1
  230. package/dist/components/GrRadio/index.js +2 -2
  231. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-C8IcJuuF.js → GrRadioGroup-vKZCbkCp.js} +3 -3
  232. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-C8IcJuuF.js.map → GrRadioGroup-vKZCbkCp.js.map} +1 -1
  233. package/dist/components/GrRadioGroup/index.js +2 -2
  234. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-BMvXREcv.js → GrResponseErrorBanner-BWJNe2gr.js} +21 -18
  235. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-BWJNe2gr.js.map +1 -0
  236. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CzdTh4gd.js → GrResponseErrorBanner-Cdu9ekK9.js} +5 -5
  237. package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CzdTh4gd.js.map → GrResponseErrorBanner-Cdu9ekK9.js.map} +1 -1
  238. package/dist/components/GrResponseErrorBanner/index.js +2 -2
  239. package/dist/components/GrSegmented/chunks/{GrSegmented-BRmwBsEG.js → GrSegmented-C1y0UXpQ.js} +5 -5
  240. package/dist/components/GrSegmented/chunks/{GrSegmented-BRmwBsEG.js.map → GrSegmented-C1y0UXpQ.js.map} +1 -1
  241. package/dist/components/GrSegmented/index.js +2 -2
  242. package/dist/components/GrSelect/chunks/{GrSelect-CnHdbPlf.js → GrSelect-RShK21lK.js} +106 -112
  243. package/dist/components/GrSelect/chunks/GrSelect-RShK21lK.js.map +1 -0
  244. package/dist/components/GrSelect/index.js +2 -2
  245. package/dist/components/GrSidebar/chunks/GrSidebar-DkWozzwA.js +238 -0
  246. package/dist/components/GrSidebar/chunks/GrSidebar-DkWozzwA.js.map +1 -0
  247. package/dist/components/GrSidebar/index.js +3 -3
  248. package/dist/components/GrSkeleton/chunks/{GrSkeleton-URyMkBqI.js → GrSkeleton-DsN-_CDZ.js} +1 -1
  249. package/dist/components/GrSkeleton/chunks/{GrSkeleton-URyMkBqI.js.map → GrSkeleton-DsN-_CDZ.js.map} +1 -1
  250. package/dist/components/GrSkeleton/index.js +2 -2
  251. package/dist/components/GrSwitch/chunks/{GrSwitch-29h0QhMO.js → GrSwitch-DAioyccZ.js} +10 -10
  252. package/dist/components/GrSwitch/chunks/{GrSwitch-29h0QhMO.js.map → GrSwitch-DAioyccZ.js.map} +1 -1
  253. package/dist/components/GrSwitch/index.js +2 -2
  254. package/dist/components/GrTable/chunks/{GrTable-dTWUxzRm.js → GrTable-Db3hRnKA.js} +3 -3
  255. package/dist/components/GrTable/chunks/{GrTable-dTWUxzRm.js.map → GrTable-Db3hRnKA.js.map} +1 -1
  256. package/dist/components/GrTable/index.js +2 -2
  257. package/dist/components/GrTabs/chunks/{GrTabs-D3ntDdtg.js → GrTabs-Z-FDJUr_.js} +3 -3
  258. package/dist/components/GrTabs/chunks/{GrTabs-D3ntDdtg.js.map → GrTabs-Z-FDJUr_.js.map} +1 -1
  259. package/dist/components/GrTabs/index.js +2 -2
  260. package/dist/components/GrTextarea/chunks/{GrTextarea-Cv6r2GuC.js → GrTextarea-D56YxhKq.js} +3 -3
  261. package/dist/components/GrTextarea/chunks/{GrTextarea-Cv6r2GuC.js.map → GrTextarea-D56YxhKq.js.map} +1 -1
  262. package/dist/components/GrTextarea/index.js +2 -2
  263. package/dist/components/GrToaster/chunks/GrToaster-eI8YvnIo.js +189 -0
  264. package/dist/components/GrToaster/chunks/GrToaster-eI8YvnIo.js.map +1 -0
  265. package/dist/components/GrToaster/index.js +2 -2
  266. package/dist/components/GrTooltip/chunks/GrTooltip-BeyZ8ssR.js +109 -0
  267. package/dist/components/GrTooltip/chunks/GrTooltip-BeyZ8ssR.js.map +1 -0
  268. package/dist/components/GrTooltip/index.js +2 -2
  269. package/dist/components/GrTree/chunks/{GrTree-B2SMU5Py.js → GrTree-D3ODe2dB.js} +40 -35
  270. package/dist/components/GrTree/chunks/GrTree-D3ODe2dB.js.map +1 -0
  271. package/dist/components/GrTree/index.js +2 -2
  272. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-C1abr5uH.js → GrTreeSelect-B2xBKs5Q.js} +21 -49
  273. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-B2xBKs5Q.js.map +1 -0
  274. package/dist/components/GrTreeSelect/index.js +2 -2
  275. package/dist/composables/useTheme.js +48 -8
  276. package/dist/composables/useTheme.js.map +1 -1
  277. package/dist/composables/useToast.js +88 -26
  278. package/dist/composables/useToast.js.map +1 -1
  279. package/dist/directives/autofocus.js.map +1 -1
  280. package/dist/directives/autosize.js.map +1 -1
  281. package/dist/directives/clickOutside.js.map +1 -1
  282. package/dist/directives/dropzone.js +6 -5
  283. package/dist/directives/dropzone.js.map +1 -1
  284. package/dist/directives/hotkey.js +24 -7
  285. package/dist/directives/hotkey.js.map +1 -1
  286. package/dist/directives/loading.js +2 -2
  287. package/dist/directives/loading.js.map +1 -1
  288. package/dist/fileValidation/index.js +1 -1
  289. package/dist/granular-provider-node.js +7 -7
  290. package/dist/granular-provider-node.js.map +1 -1
  291. package/dist/granular-provider.js +1 -1
  292. package/dist/granular-provider.js.map +1 -1
  293. package/dist/i18n/all.js +1 -1
  294. package/dist/i18n/all.js.map +1 -1
  295. package/dist/i18n/index.js +2 -2
  296. package/dist/index.js +109 -534
  297. package/dist/index.js.map +1 -1
  298. package/dist/styles/base.css +32 -0
  299. package/dist/styles/index.css +15 -0
  300. package/dist/styles/preflight.css +29 -0
  301. package/dist/styles/themes/dark.css +107 -0
  302. package/dist/styles/themes/light.css +98 -0
  303. package/dist/styles/tokens.css +135 -0
  304. package/dist/types/src/components/GrButton/GrButton.vue.d.ts +25 -2
  305. package/dist/types/src/components/GrCheckbox/GrCheckbox.vue.d.ts +7 -2
  306. package/dist/types/src/components/GrConfirmDialog/GrConfirmDialog.vue.d.ts +8 -8
  307. package/dist/types/src/components/GrDataTable/GrDataTable.vue.d.ts +1 -1
  308. package/dist/types/src/components/GrDialog/GrDialog.vue.d.ts +2 -2
  309. package/dist/types/src/components/GrDialog/GrDialogHeader.vue.d.ts +2 -2
  310. package/dist/types/src/components/GrDrawer/GrDrawer.vue.d.ts +2 -2
  311. package/dist/types/src/components/GrDropdown/grDropdownStyles.d.ts +3 -2
  312. package/dist/types/src/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +2 -2
  313. package/dist/types/src/components/GrDropdownMenu/GrDropdownMenuGroup.vue.d.ts +1 -1
  314. package/dist/types/src/components/GrDropdownMenu/GrDropdownMenuList.vue.d.ts +2 -2
  315. package/dist/types/src/components/GrFileUpload/GrFileUpload.vue.d.ts +5 -5
  316. package/dist/types/src/components/GrFormFile/GrFormFile.vue.d.ts +11 -11
  317. package/dist/types/src/components/GrImageViewer/GrImageViewer.vue.d.ts +18 -18
  318. package/dist/types/src/components/GrInput/GrInput.vue.d.ts +14 -0
  319. package/dist/types/src/components/GrInput/grInputStyles.d.ts +3 -3
  320. package/dist/types/src/components/GrInputTag/GrInputTag.vue.d.ts +2 -2
  321. package/dist/types/src/components/GrLink/GrLink.vue.d.ts +11 -5
  322. package/dist/types/src/components/GrLink/grLinkStyles.d.ts +31 -3
  323. package/dist/types/src/components/GrLink/index.d.ts +1 -1
  324. package/dist/types/src/components/GrLoading/GrLoading.vue.d.ts +1 -1
  325. package/dist/types/src/components/GrModal/grModalStyles.d.ts +2 -2
  326. package/dist/types/src/components/GrNumberInput/GrNumberInput.vue.d.ts +17 -4
  327. package/dist/types/src/components/GrPagination/GrPagination.vue.d.ts +13 -1
  328. package/dist/types/src/components/GrPromptDialog/GrPromptDialog.vue.d.ts +12 -12
  329. package/dist/types/src/components/GrSegmented/GrSegmented.vue.d.ts +2 -2
  330. package/dist/types/src/components/GrSegmented/grSegmentedStyles.d.ts +5 -5
  331. package/dist/types/src/components/GrSelect/GrSelect.vue.d.ts +7 -7
  332. package/dist/types/src/components/GrSelect/grSelectStyles.d.ts +2 -1
  333. package/dist/types/src/components/GrSidebar/GrSidebar.vue.d.ts +33 -10
  334. package/dist/types/src/components/GrSidebar/GrSidebarItem.vue.d.ts +64 -0
  335. package/dist/types/src/components/GrSidebar/index.d.ts +1 -0
  336. package/dist/types/src/components/GrSidebar/sidebarContext.d.ts +9 -0
  337. package/dist/types/src/components/GrSwitch/GrSwitch.vue.d.ts +1 -1
  338. package/dist/types/src/components/GrSwitch/grSwitchStyles.d.ts +2 -2
  339. package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +9 -4
  340. package/dist/types/src/components/GrTree/GrTree.vue.d.ts +1 -1
  341. package/dist/types/src/components/GrTreeSelect/GrTreeSelect.vue.d.ts +2 -2
  342. package/dist/types/src/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
  343. package/dist/types/src/components/GrTreeSelect/grTreeSelectTypes.d.ts +1 -1
  344. package/dist/types/src/composables/internal/useAddonMeasurement.d.ts +18 -0
  345. package/dist/types/src/composables/internal/useEscapeToClose.d.ts +11 -0
  346. package/dist/types/src/composables/internal/useFloating.d.ts +53 -0
  347. package/dist/types/src/composables/useTheme.d.ts +5 -2
  348. package/dist/types/src/composables/useToast.d.ts +33 -1
  349. package/dist/types/src/directives/hotkey.d.ts +14 -0
  350. package/dist/types/src/granular-provider/shared.d.ts +1 -3
  351. package/dist/types/src/i18n/messages/en.d.ts +132 -0
  352. package/dist/types/src/i18n/messages/es.d.ts +132 -0
  353. package/dist/types/src/i18n/messages/ru.d.ts +132 -0
  354. package/dist/types/src/index.d.ts +2 -2
  355. package/dist/vue/index.js.map +1 -1
  356. package/dist/web-types.json +4673 -0
  357. package/package.json +33 -19
  358. package/dist/chunks/arrow-up-DZdIrpaF.js.map +0 -1
  359. package/dist/chunks/config-B2YTEe_h.js.map +0 -1
  360. package/dist/chunks/config-BY81UKv0.js.map +0 -1
  361. package/dist/chunks/config-Br_qI3Za.js.map +0 -1
  362. package/dist/chunks/config-C6zT56Jc2.js.map +0 -1
  363. package/dist/chunks/config-CpMYmvVT.js +0 -59
  364. package/dist/chunks/config-CpMYmvVT.js.map +0 -1
  365. package/dist/chunks/config-_3De4HFf.js.map +0 -1
  366. package/dist/chunks/en-foQA8imU.js +0 -65
  367. package/dist/chunks/en-foQA8imU.js.map +0 -1
  368. package/dist/chunks/es-DZFSojYL.js +0 -65
  369. package/dist/chunks/es-DZFSojYL.js.map +0 -1
  370. package/dist/chunks/grBadgeStyles-Cq0hH2Ls.js.map +0 -1
  371. package/dist/chunks/grButtonStyles-DKuXUemw.js +0 -191
  372. package/dist/chunks/grButtonStyles-DKuXUemw.js.map +0 -1
  373. package/dist/chunks/grDropdownStyles-B41mVR1H.js +0 -32
  374. package/dist/chunks/grDropdownStyles-B41mVR1H.js.map +0 -1
  375. package/dist/chunks/grModalStyles-DS1Vl6EW.js.map +0 -1
  376. package/dist/chunks/grSelectStyles-pUt19VGy.js.map +0 -1
  377. package/dist/chunks/grStyle-CUaTmpyW.js.map +0 -1
  378. package/dist/chunks/granularityI18n-LcJ88Vce.js.map +0 -1
  379. package/dist/chunks/info-C0bHmdZq.js.map +0 -1
  380. package/dist/chunks/loader-circle-BUntgosj.js.map +0 -1
  381. package/dist/chunks/ru-LhkPpPIl.js +0 -65
  382. package/dist/chunks/ru-LhkPpPIl.js.map +0 -1
  383. package/dist/chunks/safelist-_UVzJrgE2.js.map +0 -1
  384. package/dist/chunks/shared-BiqXd5MW.js +0 -169
  385. package/dist/chunks/shared-BiqXd5MW.js.map +0 -1
  386. package/dist/chunks/x-Pa6C2hwi.js.map +0 -1
  387. package/dist/chunks/x-circle-DQSlBLmX.js.map +0 -1
  388. package/dist/components/GrAlert/chunks/GrAlert-HyCjYJeL.js.map +0 -1
  389. package/dist/components/GrButton/chunks/GrButton-CPDKonoB.js +0 -83
  390. package/dist/components/GrButton/chunks/GrButton-CPDKonoB.js.map +0 -1
  391. package/dist/components/GrButtonGroup/chunks/GrButtonGroup-CPOH6V-P.js.map +0 -1
  392. package/dist/components/GrCard/chunks/GrCard-6yk1RRAQ.js.map +0 -1
  393. package/dist/components/GrCheckbox/chunks/GrCheckbox-Bt3d7ckE.js.map +0 -1
  394. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-Br6WJkLE.js.map +0 -1
  395. package/dist/components/GrDialog/chunks/GrDialog-C7XLxp8e.js.map +0 -1
  396. package/dist/components/GrDrawer/chunks/GrDrawer-BW3wtI7h.js.map +0 -1
  397. package/dist/components/GrDropdown/chunks/GrDropdown-DOvHKnih.js +0 -151
  398. package/dist/components/GrDropdown/chunks/GrDropdown-DOvHKnih.js.map +0 -1
  399. package/dist/components/GrFileUpload/chunks/GrFileUpload-BEKNB1RM.js.map +0 -1
  400. package/dist/components/GrFormFile/chunks/GrFormFile-CIleXnfr.js.map +0 -1
  401. package/dist/components/GrImageViewer/chunks/GrImageViewer-CsvXQHM9.js.map +0 -1
  402. package/dist/components/GrInput/chunks/GrInput-gwFQ1sk4.js.map +0 -1
  403. package/dist/components/GrInputTag/chunks/GrInputTag-Dcj3F6rQ.js.map +0 -1
  404. package/dist/components/GrLink/chunks/GrLink-BMwn2iJ9.js.map +0 -1
  405. package/dist/components/GrNumberInput/chunks/GrNumberInput-YIWhV85S.js.map +0 -1
  406. package/dist/components/GrPagination/chunks/GrPagination-BNDEqM8_.js +0 -114
  407. package/dist/components/GrPagination/chunks/GrPagination-BNDEqM8_.js.map +0 -1
  408. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-BV4EW3XO.js.map +0 -1
  409. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-BMvXREcv.js.map +0 -1
  410. package/dist/components/GrSelect/chunks/GrSelect-CnHdbPlf.js.map +0 -1
  411. package/dist/components/GrSidebar/chunks/GrSidebar-BpBtAGBs.js +0 -44
  412. package/dist/components/GrSidebar/chunks/GrSidebar-BpBtAGBs.js.map +0 -1
  413. package/dist/components/GrToaster/chunks/GrToaster-DluJmR5D.js +0 -171
  414. package/dist/components/GrToaster/chunks/GrToaster-DluJmR5D.js.map +0 -1
  415. package/dist/components/GrTooltip/chunks/GrTooltip-B7I1AVIC.js +0 -55
  416. package/dist/components/GrTooltip/chunks/GrTooltip-B7I1AVIC.js.map +0 -1
  417. package/dist/components/GrTree/chunks/GrTree-B2SMU5Py.js.map +0 -1
  418. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-C1abr5uH.js.map +0 -1
  419. /package/dist/chunks/{_plugin-vue_export-helper-DQR7Sy7Q.js → _plugin-vue_export-helper-BOaGB7Aw.js} +0 -0
  420. /package/dist/chunks/{adapter-BqqjEZGU.js → adapter-CAOxmn2G.js} +0 -0
  421. /package/dist/chunks/{arrow-up-DZdIrpaF.js → arrow-up-C_G4jg8M.js} +0 -0
  422. /package/dist/chunks/{classTokens-BPeBXNzu.js → classTokens-BzazaERD.js} +0 -0
  423. /package/dist/chunks/{config-DaHI5bD_2.js → config-8Pcutu1j2.js} +0 -0
  424. /package/dist/chunks/{config-DoBk_CzG.js → config-B0qIpo8m.js} +0 -0
  425. /package/dist/chunks/{config-DbghHyY_.js → config-BEegFP74.js} +0 -0
  426. /package/dist/chunks/{config-Dzd6BzIN2.js → config-BLHqZhy-2.js} +0 -0
  427. /package/dist/chunks/{config-BV4vBzdm.js → config-BNu8lFQB.js} +0 -0
  428. /package/dist/chunks/{config-B1kFmhpM.js → config-BUHuGBik.js} +0 -0
  429. /package/dist/chunks/{config-DF_RlCIO.js → config-BWGGp7uV.js} +0 -0
  430. /package/dist/chunks/{config-BduXEXjt.js → config-BXtAmU3s.js} +0 -0
  431. /package/dist/chunks/{config-O1_FTqD3.js → config-Bah4oHwL.js} +0 -0
  432. /package/dist/chunks/{config-DSBlHQVa.js → config-C86UzUYf.js} +0 -0
  433. /package/dist/chunks/{config-BgBWt9-A.js → config-C9AcVDkv.js} +0 -0
  434. /package/dist/chunks/{config-BeQuykDk2.js → config-CGHLoBlH2.js} +0 -0
  435. /package/dist/chunks/{config-ChQxl75m.js → config-CThzliFi.js} +0 -0
  436. /package/dist/chunks/{config-DmjwIqx1.js → config-Cb2PAlSD.js} +0 -0
  437. /package/dist/chunks/{config-BbKroFtR.js → config-D7GZO-Dx.js} +0 -0
  438. /package/dist/chunks/{config-CUwF4E4R.js → config-DBmdgoVf.js} +0 -0
  439. /package/dist/chunks/{config-C9MblwGq.js → config-DICOd-2X.js} +0 -0
  440. /package/dist/chunks/{config-D6Oe3blW2.js → config-DpqOnJiz2.js} +0 -0
  441. /package/dist/chunks/{config-V3LF-b_F.js → config-Dxuwwepq.js} +0 -0
  442. /package/dist/chunks/{config-CvGdaXvH.js → config-HZto2PVy.js} +0 -0
  443. /package/dist/chunks/{config-BOdu-O_B.js → config-JOtB4mzs.js} +0 -0
  444. /package/dist/chunks/{config-iuMz79Mg.js → config-JpHA4Xx0.js} +0 -0
  445. /package/dist/chunks/{config-Dcb7xK-C.js → config-PFg39mYo.js} +0 -0
  446. /package/dist/chunks/{config-BU0olrvr.js → config-QaIc_gRa.js} +0 -0
  447. /package/dist/chunks/{config-vcDVCYXo.js → config-U4m9PSql.js} +0 -0
  448. /package/dist/chunks/{config-Ci9nXGUV.js → config-ZiXWIRBX.js} +0 -0
  449. /package/dist/chunks/{config-Bcf-8XlK2.js → config-ipvU0lzP2.js} +0 -0
  450. /package/dist/chunks/{config-D5ek7VSi.js → config-xR4XVmcy.js} +0 -0
  451. /package/dist/chunks/{dialogShared-BtXUk6Zc.js → dialogShared-CxW62PQE.js} +0 -0
  452. /package/dist/chunks/{grLoadingStyles-DmI-SJ3i.js → grLoadingStyles-R5sreH__.js} +0 -0
  453. /package/dist/chunks/{info-C0bHmdZq.js → info-Be2OccTu.js} +0 -0
  454. /package/dist/chunks/{loader-circle-BUntgosj.js → loader-circle-CfVLz7lb.js} +0 -0
  455. /package/dist/chunks/{x-Pa6C2hwi.js → x-2KXnqxiD.js} +0 -0
  456. /package/dist/chunks/{x-circle-DQSlBLmX.js → x-circle-BCBURwyX.js} +0 -0
  457. /package/dist/components/GrBottomNav/chunks/{GrBottomNav-XPHwd7UQ.js → GrBottomNav-usuMgG8_.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrImageViewer-CsvXQHM9.js","names":["$slots"],"sources":["../../../../src/components/GrImageViewer/GrImageViewer.vue","../../../../src/components/GrImageViewer/GrImageViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'\nimport { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\n/**\n * Usage:\n *\n * <GrImageViewer\n * v-model=\"open\"\n * :url-list=\"images\"\n * :initial-index=\"0\"\n * show-progress\n * hide-on-click-modal\n * />\n */\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n urlList: string[]\n initialIndex?: number\n zoomRate?: number\n minScale?: number\n maxScale?: number\n hideOnClickModal?: boolean\n closeOnPressEscape?: boolean\n showProgress?: boolean\n showZoomValue?: boolean\n /** Включает масштабирование колесом мыши / жестом на трекпаде. По умолчанию включено. */\n wheelZoom?: boolean\n /** Включает перетаскивание (pan) картинки мышью. При наведении курсор «рука». По умолчанию выключено. */\n draggable?: boolean\n zIndex?: number\n /** i18n: aria-label кнопки закрытия. */\n closeLabel?: string\n /** i18n: aria-label кнопки «предыдущее изображение». */\n prevLabel?: string\n /** i18n: aria-label кнопки «следующее изображение». */\n nextLabel?: string\n /** i18n: aria-label кнопки «увеличить». */\n zoomInLabel?: string\n /** i18n: aria-label кнопки «уменьшить». */\n zoomOutLabel?: string\n /** i18n: aria-label кнопки «сбросить масштаб». */\n resetZoomLabel?: string\n /** i18n: aria-label кнопки «повернуть влево». */\n rotateLeftLabel?: string\n /** i18n: aria-label кнопки «повернуть вправо». */\n rotateRightLabel?: string\n /** i18n: текст в пустом состоянии (нет изображений). */\n emptyText?: string\n }>(),\n {\n initialIndex: 0,\n zoomRate: 1.2,\n minScale: 0.5,\n maxScale: 5,\n hideOnClickModal: false,\n closeOnPressEscape: true,\n showProgress: false,\n showZoomValue: true,\n wheelZoom: true,\n draggable: false,\n zIndex: undefined,\n closeLabel: 'Close image viewer',\n prevLabel: 'Previous image',\n nextLabel: 'Next image',\n zoomInLabel: 'Zoom in',\n zoomOutLabel: 'Zoom out',\n resetZoomLabel: 'Reset zoom',\n rotateLeftLabel: 'Rotate left',\n rotateRightLabel: 'Rotate right',\n emptyText: 'No image',\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'close'): void\n (e: 'switch', newIndex: number): void\n (e: 'rotate', deg: number): void\n}>()\n\ntype GrImageViewerToolbarActions = {\n close: () => void\n prev: () => void\n next: () => void\n zoomIn: () => void\n zoomOut: () => void\n reset: () => void\n rotateLeft: () => void\n rotateRight: () => void\n}\n\ntype GrImageViewerSlotProps = {\n index: number\n displayIndex: number\n total: number\n scale: number\n rotation: number\n /** Натуральный (исходный) размер картинки, px. 0 пока изображение не загружено. */\n naturalWidth: number\n naturalHeight: number\n /** Фактический размер изображения на экране c учётом scale, px (footprint вдоль осей картинки). */\n renderedWidth: number\n renderedHeight: number\n /** Реальный масштаб относительно натурального размера (доля): renderedWidth / naturalWidth. */\n realScale: number\n /** Реальный масштаб в процентах, отформатированный (например `67`). */\n realScalePercent: string\n actions: GrImageViewerToolbarActions\n}\n\nconst DEFAULT_Z_INDEX = 2000\nconst TRAILING_ZERO_DECIMAL_RE = /\\.0$/\n// Чувствительность масштабирования колесом/трекпадом: чем больше — тем резче реакция.\nconst WHEEL_ZOOM_SENSITIVITY = 0.0015\n// Сколько ms простоя после последнего wheel-события считаем «зум завершён»\n// (после этого возвращаем плавный CSS-переход).\nconst WHEEL_IDLE_MS = 120\n\nconst open = computed(() => props.modelValue)\nconst total = computed(() => props.urlList.length)\nconst hasImages = computed(() => total.value > 0)\n\nconst currentIndex = ref(0)\nconst scale = ref(1)\nconst rotation = ref(0)\nconst offsetX = ref(0)\nconst offsetY = ref(0)\n\nconst imageEl = ref<HTMLImageElement | null>(null)\n\n// Флаг активного зума колесом/трекпадом. Пока true — CSS-переход отключён,\n// чтобы непрерывное масштабирование не «наслаивалось» и не дёргалось.\nconst isWheelZooming = ref(false)\n\n// Накопленный deltaY между кадрами и id запланированного rAF.\nlet pendingWheelDelta = 0\nlet wheelFrameId: number | null = null\nlet wheelIdleTimer: ReturnType<typeof setTimeout> | null = null\n\n// Флаг активного перетаскивания (pan) картинки мышью.\nconst isDragging = ref(false)\n\n// Координаты указателя и offset на момент начала перетаскивания.\nlet dragStartX = 0\nlet dragStartY = 0\nlet dragOffsetStartX = 0\nlet dragOffsetStartY = 0\n\n// Натуральный (исходный) размер картинки, читается из `<img>` при загрузке.\nconst naturalWidth = ref(0)\nconst naturalHeight = ref(0)\n\n// Layout-размер вписанного (`object-contain`) изображения без учёта CSS transform/scale.\nconst fittedWidth = ref(0)\nconst fittedHeight = ref(0)\n\nlet resizeObserver: ResizeObserver | null = null\n\nconst currentUrl = computed(() => {\n if (!hasImages.value) {\n return undefined\n }\n\n return props.urlList[currentIndex.value]\n})\n\nconst displayIndex = computed(() => {\n if (!hasImages.value) {\n return 0\n }\n\n return currentIndex.value + 1\n})\n\nconst viewerStyle = computed(() => ({\n zIndex: String(Number.isFinite(props.zIndex) ? props.zIndex : DEFAULT_Z_INDEX),\n}))\n\nconst imageStyle = computed(() => ({\n transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value}) rotate(${rotation.value}deg)`,\n}))\n\n// Плавный CSS-переход transform только для дискретных зумов (кнопки/клавиши).\n// При непрерывном зуме колесом и при перетаскивании переход отключаем —\n// иначе картинка «дёргается»/наслаивается и тянется за курсором с задержкой.\nconst imageTransitionClass = computed(() =>\n isWheelZooming.value || isDragging.value ? '' : 'transition-transform duration-150 ease-out',\n)\n\n// Курсор «рука» при наведении (grab) и при зажатии (grabbing), только если включён drag.\nconst imageCursorClass = computed(() => {\n if (!props.draggable) {\n return ''\n }\n\n return isDragging.value ? 'cursor-grabbing' : 'cursor-grab'\n})\n\nfunction formatPercent(value: number): string {\n if (Number.isInteger(value)) {\n return String(value)\n }\n\n return value.toFixed(1).replace(TRAILING_ZERO_DECIMAL_RE, '')\n}\n\nconst zoomValueText = computed(() => formatPercent(scale.value * 100))\n\n// Фактический размер изображения на экране вдоль его осей (footprint), c учётом scale.\nconst renderedWidth = computed(() => Math.round(fittedWidth.value * scale.value))\nconst renderedHeight = computed(() => Math.round(fittedHeight.value * scale.value))\n\n// Реальный масштаб относительно натурального размера (доля). Не зависит от rotation,\n// так как scale равномерный по осям картинки.\nconst realScale = computed(() => {\n if (!naturalWidth.value) {\n return 0\n }\n\n return (fittedWidth.value * scale.value) / naturalWidth.value\n})\n\nconst realScalePercent = computed(() => formatPercent(realScale.value * 100))\n\nconst toolbarSlotProps = computed<GrImageViewerSlotProps>(() => ({\n index: currentIndex.value,\n displayIndex: displayIndex.value,\n total: total.value,\n scale: scale.value,\n rotation: rotation.value,\n naturalWidth: naturalWidth.value,\n naturalHeight: naturalHeight.value,\n renderedWidth: renderedWidth.value,\n renderedHeight: renderedHeight.value,\n realScale: realScale.value,\n realScalePercent: realScalePercent.value,\n actions: toolbarActions,\n}))\n\nfunction normalizeIndex(value: number): number {\n if (!hasImages.value) {\n return 0\n }\n\n const normalized = Number.isFinite(value) ? Math.trunc(value) : 0\n\n return ((normalized % total.value) + total.value) % total.value\n}\n\nfunction resetTransform(): void {\n scale.value = 1\n rotation.value = 0\n offsetX.value = 0\n offsetY.value = 0\n isDragging.value = false\n}\n\nfunction resetImageMetrics(): void {\n naturalWidth.value = 0\n naturalHeight.value = 0\n fittedWidth.value = 0\n fittedHeight.value = 0\n}\n\nfunction measureFitted(): void {\n const el = imageEl.value\n\n if (!el) {\n return\n }\n\n // offsetWidth/Height — layout-размер вписанного изображения, без учёта CSS transform.\n fittedWidth.value = el.offsetWidth\n fittedHeight.value = el.offsetHeight\n}\n\nfunction onImageLoad(event: Event): void {\n const el = event.target as HTMLImageElement\n\n naturalWidth.value = el.naturalWidth\n naturalHeight.value = el.naturalHeight\n measureFitted()\n}\n\nfunction startObservingImage(): void {\n const el = imageEl.value\n\n if (!el || typeof ResizeObserver === 'undefined') {\n return\n }\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => measureFitted())\n }\n\n resizeObserver.observe(el)\n\n // Если картинка уже в кэше и `load` не сработал — считаем метрики сразу.\n if (el.complete && el.naturalWidth) {\n naturalWidth.value = el.naturalWidth\n naturalHeight.value = el.naturalHeight\n }\n\n measureFitted()\n}\n\nfunction stopObservingImage(): void {\n resizeObserver?.disconnect()\n}\n\nfunction setIndex(nextIndex: number, options?: { emitSwitch?: boolean }): void {\n const normalizedIndex = normalizeIndex(nextIndex)\n\n if (normalizedIndex === currentIndex.value) {\n return\n }\n\n currentIndex.value = normalizedIndex\n resetTransform()\n resetImageMetrics()\n endWheelZoom()\n\n if (options?.emitSwitch !== false) {\n emit('switch', normalizedIndex)\n }\n}\n\nfunction syncIndexFromInitial(): void {\n if (!hasImages.value) {\n currentIndex.value = 0\n return\n }\n\n currentIndex.value = normalizeIndex(props.initialIndex)\n resetTransform()\n resetImageMetrics()\n endWheelZoom()\n}\n\nfunction preloadAt(index: number): void {\n const url = props.urlList[index]\n\n if (!url) {\n return\n }\n\n const image = new Image()\n image.decoding = 'async'\n image.src = url\n}\n\nfunction closeViewer(): void {\n emit('update:modelValue', false)\n emit('close')\n}\n\nfunction prev(): void {\n setIndex(currentIndex.value - 1)\n}\n\nfunction next(): void {\n setIndex(currentIndex.value + 1)\n}\n\nfunction setScale(value: number): void {\n const clamped = Math.min(props.maxScale, Math.max(props.minScale, value))\n\n scale.value = Number(clamped.toFixed(4))\n}\n\nfunction zoomIn(): void {\n setScale(scale.value * props.zoomRate)\n}\n\nfunction zoomOut(): void {\n setScale(scale.value / props.zoomRate)\n}\n\nfunction cancelWheelFrame(): void {\n if (wheelFrameId !== null) {\n cancelAnimationFrame(wheelFrameId)\n wheelFrameId = null\n }\n}\n\nfunction endWheelZoom(): void {\n if (wheelIdleTimer !== null) {\n clearTimeout(wheelIdleTimer)\n wheelIdleTimer = null\n }\n\n isWheelZooming.value = false\n pendingWheelDelta = 0\n cancelWheelFrame()\n}\n\n// Применяем накопленный за кадр deltaY одним обновлением scale — так рендер\n// происходит не чаще одного раза в кадр (без «дёрганья» от частых обновлений).\nfunction flushWheelZoom(): void {\n wheelFrameId = null\n\n const delta = pendingWheelDelta\n pendingWheelDelta = 0\n\n if (!delta) {\n return\n }\n\n // Экспоненциальный шаг — плавно и одинаково ощущается и для колеса, и для трекпада.\n // delta < 0 (от себя / scroll up) — приближение, delta > 0 — отдаление.\n const factor = Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY)\n\n setScale(scale.value * factor)\n}\n\nfunction onWheel(event: WheelEvent): void {\n if (!props.wheelZoom || !hasImages.value) {\n return\n }\n\n // Не даём странице/оверлею проскроллиться — колесо отдаём под зум.\n event.preventDefault()\n\n // Во время непрерывного зума отключаем CSS-переход: плавность даёт само\n // покадровое обновление, а transition на каждом микрошаге вызывает лаг/наслоение.\n isWheelZooming.value = true\n\n pendingWheelDelta += event.deltaY\n\n if (wheelFrameId === null) {\n wheelFrameId = requestAnimationFrame(flushWheelZoom)\n }\n\n if (wheelIdleTimer !== null) {\n clearTimeout(wheelIdleTimer)\n }\n\n // По завершении жеста возвращаем плавный переход для последующих дискретных зумов.\n wheelIdleTimer = setTimeout(() => {\n wheelIdleTimer = null\n isWheelZooming.value = false\n }, WHEEL_IDLE_MS)\n}\n\nfunction onPointerDown(event: PointerEvent): void {\n // Тянем только основной кнопкой и только если drag включён и есть картинка.\n if (!props.draggable || !hasImages.value || event.button !== 0) {\n return\n }\n\n event.preventDefault()\n\n isDragging.value = true\n dragStartX = event.clientX\n dragStartY = event.clientY\n dragOffsetStartX = offsetX.value\n dragOffsetStartY = offsetY.value\n\n // Захватываем указатель, чтобы движение/отпускание ловились даже за пределами картинки.\n ;(event.currentTarget as Element).setPointerCapture?.(event.pointerId)\n}\n\nfunction onPointerMove(event: PointerEvent): void {\n if (!isDragging.value) {\n return\n }\n\n offsetX.value = dragOffsetStartX + (event.clientX - dragStartX)\n offsetY.value = dragOffsetStartY + (event.clientY - dragStartY)\n}\n\nfunction onPointerUp(event: PointerEvent): void {\n if (!isDragging.value) {\n return\n }\n\n isDragging.value = false\n ;(event.currentTarget as Element).releasePointerCapture?.(event.pointerId)\n}\n\nfunction rotateLeft(): void {\n rotation.value -= 90\n emit('rotate', rotation.value)\n}\n\nfunction rotateRight(): void {\n rotation.value += 90\n emit('rotate', rotation.value)\n}\n\nconst toolbarActions: GrImageViewerToolbarActions = {\n close: closeViewer,\n prev,\n next,\n zoomIn,\n zoomOut,\n reset: resetTransform,\n rotateLeft,\n rotateRight,\n}\n\nfunction onBackdropClick(): void {\n if (!props.hideOnClickModal) {\n return\n }\n\n closeViewer()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'Escape':\n if (!props.closeOnPressEscape) {\n return\n }\n\n event.preventDefault()\n closeViewer()\n return\n\n case 'ArrowLeft':\n event.preventDefault()\n prev()\n return\n\n case 'ArrowRight':\n event.preventDefault()\n next()\n return\n\n case '+':\n case '=':\n case 'Add':\n event.preventDefault()\n zoomIn()\n return\n\n case '-':\n case '_':\n case 'Subtract':\n event.preventDefault()\n zoomOut()\n return\n\n case '0':\n event.preventDefault()\n resetTransform()\n break\n\n default:\n }\n}\n\nwatch(\n () => props.modelValue,\n async (isOpen) => {\n if (!isOpen) {\n stopObservingImage()\n endWheelZoom()\n isDragging.value = false\n return\n }\n\n syncIndexFromInitial()\n await nextTick()\n startObservingImage()\n },\n { immediate: true },\n)\n\nwatch(\n () => props.initialIndex,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n)\n\nwatch(\n () => props.urlList,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n { deep: true },\n)\n\nwatch(\n currentIndex,\n (index) => {\n if (!hasImages.value || total.value < 2) {\n return\n }\n\n preloadAt(normalizeIndex(index - 1))\n preloadAt(normalizeIndex(index + 1))\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n stopObservingImage()\n endWheelZoom()\n})\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"open\" as=\"template\">\n <Dialog\n as=\"div\"\n class=\"fixed inset-0\"\n :style=\"viewerStyle\"\n role=\"dialog\"\n aria-modal=\"true\"\n :static=\"true\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0 overflow-hidden\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-150 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-120 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-image-viewer-overlay\n class=\"absolute inset-0 bg-black/60 backdrop-blur-sm\"\n aria-hidden=\"true\"\n @click=\"onBackdropClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-180 ease-out\"\n enter-from=\"opacity-0 scale-98\"\n enter-to=\"opacity-100 scale-100\"\n leave=\"duration-130 ease-in\"\n leave-from=\"opacity-100 scale-100\"\n leave-to=\"opacity-0 scale-98\"\n >\n <DialogPanel\n data-ds-image-viewer-panel\n class=\"relative z-10 h-full w-full outline-none\"\n tabindex=\"-1\"\n >\n <div class=\"pointer-events-none absolute inset-x-0 top-0 z-30 px-3 py-3 sm:px-6\">\n <div class=\"pointer-events-auto flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-2\">\n <div\n v-if=\"showProgress\"\n data-ds-image-viewer-progress\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-600 text-white/95 sm:text-sm\"\n >\n {{ displayIndex }} / {{ total }}\n </div>\n\n <div\n v-if=\"showZoomValue\"\n data-ds-image-viewer-zoom-value\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-700 text-white/95 sm:text-sm\"\n >\n {{ zoomValueText }}%\n </div>\n </div>\n\n <button\n type=\"button\"\n data-ds-image-viewer-close\n :aria-label=\"closeLabel\"\n class=\"h-11 w-11 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"closeViewer\"\n >\n <span aria-hidden=\"true\" class=\"text-lg leading-none\">✕</span>\n </button>\n </div>\n </div>\n\n <div\n class=\"relative z-10 h-full w-full flex items-center justify-center px-16 py-16 sm:px-24 sm:py-20\"\n @wheel=\"onWheel\"\n >\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-prev\n :aria-label=\"prevLabel\"\n class=\"absolute left-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:left-6\"\n @click=\"prev\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">‹</span>\n </button>\n\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-next\n :aria-label=\"nextLabel\"\n class=\"absolute right-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:right-6\"\n @click=\"next\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">›</span>\n </button>\n\n <div class=\"h-full w-full flex items-center justify-center\">\n <img\n v-if=\"currentUrl\"\n ref=\"imageEl\"\n data-ds-image-viewer-image\n :src=\"currentUrl\"\n alt=\"\"\n draggable=\"false\"\n class=\"max-h-full max-w-full select-none object-contain will-change-transform\"\n :class=\"[imageTransitionClass, imageCursorClass]\"\n :style=\"imageStyle\"\n @load=\"onImageLoad\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointercancel=\"onPointerUp\"\n >\n\n <div\n v-else\n class=\"rounded-[var(--ds-radius-xl)] border border-white/20 bg-black/25 px-4 py-3 text-sm text-white/80\"\n >\n {{ emptyText }}\n </div>\n </div>\n </div>\n\n <div class=\"pointer-events-none absolute inset-x-0 bottom-0 z-30 px-3 pb-3 sm:px-6 sm:pb-6\">\n <div class=\"pointer-events-auto mx-auto w-full max-w-max\">\n <slot\n name=\"toolbar\"\n v-bind=\"toolbarSlotProps\"\n >\n <div class=\"rounded-full border border-white/20 bg-black/35 p-1 backdrop-blur-sm flex items-center gap-1\">\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-out\n :aria-label=\"zoomOutLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomOut\"\n >\n −\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-reset\n :aria-label=\"resetZoomLabel\"\n class=\"h-11 min-w-11 px-3 flex items-center justify-center rounded-full text-xs font-700 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"resetTransform\"\n >\n 100%\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-in\n :aria-label=\"zoomInLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomIn\"\n >\n +\n </button>\n\n <template v-if=\"$slots['toolbar-actions']\">\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <slot\n name=\"toolbar-actions\"\n v-bind=\"toolbarSlotProps\"\n />\n\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n </template>\n\n <div v-else class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-left\n :aria-label=\"rotateLeftLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateLeft\"\n >\n ↺\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-right\n :aria-label=\"rotateRightLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateRight\"\n >\n ↻\n </button>\n </div>\n </slot>\n </div>\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'\nimport { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\n/**\n * Usage:\n *\n * <GrImageViewer\n * v-model=\"open\"\n * :url-list=\"images\"\n * :initial-index=\"0\"\n * show-progress\n * hide-on-click-modal\n * />\n */\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n urlList: string[]\n initialIndex?: number\n zoomRate?: number\n minScale?: number\n maxScale?: number\n hideOnClickModal?: boolean\n closeOnPressEscape?: boolean\n showProgress?: boolean\n showZoomValue?: boolean\n /** Включает масштабирование колесом мыши / жестом на трекпаде. По умолчанию включено. */\n wheelZoom?: boolean\n /** Включает перетаскивание (pan) картинки мышью. При наведении курсор «рука». По умолчанию выключено. */\n draggable?: boolean\n zIndex?: number\n /** i18n: aria-label кнопки закрытия. */\n closeLabel?: string\n /** i18n: aria-label кнопки «предыдущее изображение». */\n prevLabel?: string\n /** i18n: aria-label кнопки «следующее изображение». */\n nextLabel?: string\n /** i18n: aria-label кнопки «увеличить». */\n zoomInLabel?: string\n /** i18n: aria-label кнопки «уменьшить». */\n zoomOutLabel?: string\n /** i18n: aria-label кнопки «сбросить масштаб». */\n resetZoomLabel?: string\n /** i18n: aria-label кнопки «повернуть влево». */\n rotateLeftLabel?: string\n /** i18n: aria-label кнопки «повернуть вправо». */\n rotateRightLabel?: string\n /** i18n: текст в пустом состоянии (нет изображений). */\n emptyText?: string\n }>(),\n {\n initialIndex: 0,\n zoomRate: 1.2,\n minScale: 0.5,\n maxScale: 5,\n hideOnClickModal: false,\n closeOnPressEscape: true,\n showProgress: false,\n showZoomValue: true,\n wheelZoom: true,\n draggable: false,\n zIndex: undefined,\n closeLabel: 'Close image viewer',\n prevLabel: 'Previous image',\n nextLabel: 'Next image',\n zoomInLabel: 'Zoom in',\n zoomOutLabel: 'Zoom out',\n resetZoomLabel: 'Reset zoom',\n rotateLeftLabel: 'Rotate left',\n rotateRightLabel: 'Rotate right',\n emptyText: 'No image',\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'close'): void\n (e: 'switch', newIndex: number): void\n (e: 'rotate', deg: number): void\n}>()\n\ntype GrImageViewerToolbarActions = {\n close: () => void\n prev: () => void\n next: () => void\n zoomIn: () => void\n zoomOut: () => void\n reset: () => void\n rotateLeft: () => void\n rotateRight: () => void\n}\n\ntype GrImageViewerSlotProps = {\n index: number\n displayIndex: number\n total: number\n scale: number\n rotation: number\n /** Натуральный (исходный) размер картинки, px. 0 пока изображение не загружено. */\n naturalWidth: number\n naturalHeight: number\n /** Фактический размер изображения на экране c учётом scale, px (footprint вдоль осей картинки). */\n renderedWidth: number\n renderedHeight: number\n /** Реальный масштаб относительно натурального размера (доля): renderedWidth / naturalWidth. */\n realScale: number\n /** Реальный масштаб в процентах, отформатированный (например `67`). */\n realScalePercent: string\n actions: GrImageViewerToolbarActions\n}\n\nconst DEFAULT_Z_INDEX = 2000\nconst TRAILING_ZERO_DECIMAL_RE = /\\.0$/\n// Чувствительность масштабирования колесом/трекпадом: чем больше — тем резче реакция.\nconst WHEEL_ZOOM_SENSITIVITY = 0.0015\n// Сколько ms простоя после последнего wheel-события считаем «зум завершён»\n// (после этого возвращаем плавный CSS-переход).\nconst WHEEL_IDLE_MS = 120\n\nconst open = computed(() => props.modelValue)\nconst total = computed(() => props.urlList.length)\nconst hasImages = computed(() => total.value > 0)\n\nconst currentIndex = ref(0)\nconst scale = ref(1)\nconst rotation = ref(0)\nconst offsetX = ref(0)\nconst offsetY = ref(0)\n\nconst imageEl = ref<HTMLImageElement | null>(null)\n\n// Флаг активного зума колесом/трекпадом. Пока true — CSS-переход отключён,\n// чтобы непрерывное масштабирование не «наслаивалось» и не дёргалось.\nconst isWheelZooming = ref(false)\n\n// Накопленный deltaY между кадрами и id запланированного rAF.\nlet pendingWheelDelta = 0\nlet wheelFrameId: number | null = null\nlet wheelIdleTimer: ReturnType<typeof setTimeout> | null = null\n\n// Флаг активного перетаскивания (pan) картинки мышью.\nconst isDragging = ref(false)\n\n// Координаты указателя и offset на момент начала перетаскивания.\nlet dragStartX = 0\nlet dragStartY = 0\nlet dragOffsetStartX = 0\nlet dragOffsetStartY = 0\n\n// Натуральный (исходный) размер картинки, читается из `<img>` при загрузке.\nconst naturalWidth = ref(0)\nconst naturalHeight = ref(0)\n\n// Layout-размер вписанного (`object-contain`) изображения без учёта CSS transform/scale.\nconst fittedWidth = ref(0)\nconst fittedHeight = ref(0)\n\nlet resizeObserver: ResizeObserver | null = null\n\nconst currentUrl = computed(() => {\n if (!hasImages.value) {\n return undefined\n }\n\n return props.urlList[currentIndex.value]\n})\n\nconst displayIndex = computed(() => {\n if (!hasImages.value) {\n return 0\n }\n\n return currentIndex.value + 1\n})\n\nconst viewerStyle = computed(() => ({\n zIndex: String(Number.isFinite(props.zIndex) ? props.zIndex : DEFAULT_Z_INDEX),\n}))\n\nconst imageStyle = computed(() => ({\n transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value}) rotate(${rotation.value}deg)`,\n}))\n\n// Плавный CSS-переход transform только для дискретных зумов (кнопки/клавиши).\n// При непрерывном зуме колесом и при перетаскивании переход отключаем —\n// иначе картинка «дёргается»/наслаивается и тянется за курсором с задержкой.\nconst imageTransitionClass = computed(() =>\n isWheelZooming.value || isDragging.value ? '' : 'transition-transform duration-150 ease-out',\n)\n\n// Курсор «рука» при наведении (grab) и при зажатии (grabbing), только если включён drag.\nconst imageCursorClass = computed(() => {\n if (!props.draggable) {\n return ''\n }\n\n return isDragging.value ? 'cursor-grabbing' : 'cursor-grab'\n})\n\nfunction formatPercent(value: number): string {\n if (Number.isInteger(value)) {\n return String(value)\n }\n\n return value.toFixed(1).replace(TRAILING_ZERO_DECIMAL_RE, '')\n}\n\nconst zoomValueText = computed(() => formatPercent(scale.value * 100))\n\n// Фактический размер изображения на экране вдоль его осей (footprint), c учётом scale.\nconst renderedWidth = computed(() => Math.round(fittedWidth.value * scale.value))\nconst renderedHeight = computed(() => Math.round(fittedHeight.value * scale.value))\n\n// Реальный масштаб относительно натурального размера (доля). Не зависит от rotation,\n// так как scale равномерный по осям картинки.\nconst realScale = computed(() => {\n if (!naturalWidth.value) {\n return 0\n }\n\n return (fittedWidth.value * scale.value) / naturalWidth.value\n})\n\nconst realScalePercent = computed(() => formatPercent(realScale.value * 100))\n\nconst toolbarSlotProps = computed<GrImageViewerSlotProps>(() => ({\n index: currentIndex.value,\n displayIndex: displayIndex.value,\n total: total.value,\n scale: scale.value,\n rotation: rotation.value,\n naturalWidth: naturalWidth.value,\n naturalHeight: naturalHeight.value,\n renderedWidth: renderedWidth.value,\n renderedHeight: renderedHeight.value,\n realScale: realScale.value,\n realScalePercent: realScalePercent.value,\n actions: toolbarActions,\n}))\n\nfunction normalizeIndex(value: number): number {\n if (!hasImages.value) {\n return 0\n }\n\n const normalized = Number.isFinite(value) ? Math.trunc(value) : 0\n\n return ((normalized % total.value) + total.value) % total.value\n}\n\nfunction resetTransform(): void {\n scale.value = 1\n rotation.value = 0\n offsetX.value = 0\n offsetY.value = 0\n isDragging.value = false\n}\n\nfunction resetImageMetrics(): void {\n naturalWidth.value = 0\n naturalHeight.value = 0\n fittedWidth.value = 0\n fittedHeight.value = 0\n}\n\nfunction measureFitted(): void {\n const el = imageEl.value\n\n if (!el) {\n return\n }\n\n // offsetWidth/Height — layout-размер вписанного изображения, без учёта CSS transform.\n fittedWidth.value = el.offsetWidth\n fittedHeight.value = el.offsetHeight\n}\n\nfunction onImageLoad(event: Event): void {\n const el = event.target as HTMLImageElement\n\n naturalWidth.value = el.naturalWidth\n naturalHeight.value = el.naturalHeight\n measureFitted()\n}\n\nfunction startObservingImage(): void {\n const el = imageEl.value\n\n if (!el || typeof ResizeObserver === 'undefined') {\n return\n }\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => measureFitted())\n }\n\n resizeObserver.observe(el)\n\n // Если картинка уже в кэше и `load` не сработал — считаем метрики сразу.\n if (el.complete && el.naturalWidth) {\n naturalWidth.value = el.naturalWidth\n naturalHeight.value = el.naturalHeight\n }\n\n measureFitted()\n}\n\nfunction stopObservingImage(): void {\n resizeObserver?.disconnect()\n}\n\nfunction setIndex(nextIndex: number, options?: { emitSwitch?: boolean }): void {\n const normalizedIndex = normalizeIndex(nextIndex)\n\n if (normalizedIndex === currentIndex.value) {\n return\n }\n\n currentIndex.value = normalizedIndex\n resetTransform()\n resetImageMetrics()\n endWheelZoom()\n\n if (options?.emitSwitch !== false) {\n emit('switch', normalizedIndex)\n }\n}\n\nfunction syncIndexFromInitial(): void {\n if (!hasImages.value) {\n currentIndex.value = 0\n return\n }\n\n currentIndex.value = normalizeIndex(props.initialIndex)\n resetTransform()\n resetImageMetrics()\n endWheelZoom()\n}\n\nfunction preloadAt(index: number): void {\n const url = props.urlList[index]\n\n if (!url) {\n return\n }\n\n const image = new Image()\n image.decoding = 'async'\n image.src = url\n}\n\nfunction closeViewer(): void {\n emit('update:modelValue', false)\n emit('close')\n}\n\nfunction prev(): void {\n setIndex(currentIndex.value - 1)\n}\n\nfunction next(): void {\n setIndex(currentIndex.value + 1)\n}\n\nfunction setScale(value: number): void {\n const clamped = Math.min(props.maxScale, Math.max(props.minScale, value))\n\n scale.value = Number(clamped.toFixed(4))\n}\n\nfunction zoomIn(): void {\n setScale(scale.value * props.zoomRate)\n}\n\nfunction zoomOut(): void {\n setScale(scale.value / props.zoomRate)\n}\n\nfunction cancelWheelFrame(): void {\n if (wheelFrameId !== null) {\n cancelAnimationFrame(wheelFrameId)\n wheelFrameId = null\n }\n}\n\nfunction endWheelZoom(): void {\n if (wheelIdleTimer !== null) {\n clearTimeout(wheelIdleTimer)\n wheelIdleTimer = null\n }\n\n isWheelZooming.value = false\n pendingWheelDelta = 0\n cancelWheelFrame()\n}\n\n// Применяем накопленный за кадр deltaY одним обновлением scale — так рендер\n// происходит не чаще одного раза в кадр (без «дёрганья» от частых обновлений).\nfunction flushWheelZoom(): void {\n wheelFrameId = null\n\n const delta = pendingWheelDelta\n pendingWheelDelta = 0\n\n if (!delta) {\n return\n }\n\n // Экспоненциальный шаг — плавно и одинаково ощущается и для колеса, и для трекпада.\n // delta < 0 (от себя / scroll up) — приближение, delta > 0 — отдаление.\n const factor = Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY)\n\n setScale(scale.value * factor)\n}\n\nfunction onWheel(event: WheelEvent): void {\n if (!props.wheelZoom || !hasImages.value) {\n return\n }\n\n // Не даём странице/оверлею проскроллиться — колесо отдаём под зум.\n event.preventDefault()\n\n // Во время непрерывного зума отключаем CSS-переход: плавность даёт само\n // покадровое обновление, а transition на каждом микрошаге вызывает лаг/наслоение.\n isWheelZooming.value = true\n\n pendingWheelDelta += event.deltaY\n\n if (wheelFrameId === null) {\n wheelFrameId = requestAnimationFrame(flushWheelZoom)\n }\n\n if (wheelIdleTimer !== null) {\n clearTimeout(wheelIdleTimer)\n }\n\n // По завершении жеста возвращаем плавный переход для последующих дискретных зумов.\n wheelIdleTimer = setTimeout(() => {\n wheelIdleTimer = null\n isWheelZooming.value = false\n }, WHEEL_IDLE_MS)\n}\n\nfunction onPointerDown(event: PointerEvent): void {\n // Тянем только основной кнопкой и только если drag включён и есть картинка.\n if (!props.draggable || !hasImages.value || event.button !== 0) {\n return\n }\n\n event.preventDefault()\n\n isDragging.value = true\n dragStartX = event.clientX\n dragStartY = event.clientY\n dragOffsetStartX = offsetX.value\n dragOffsetStartY = offsetY.value\n\n // Захватываем указатель, чтобы движение/отпускание ловились даже за пределами картинки.\n ;(event.currentTarget as Element).setPointerCapture?.(event.pointerId)\n}\n\nfunction onPointerMove(event: PointerEvent): void {\n if (!isDragging.value) {\n return\n }\n\n offsetX.value = dragOffsetStartX + (event.clientX - dragStartX)\n offsetY.value = dragOffsetStartY + (event.clientY - dragStartY)\n}\n\nfunction onPointerUp(event: PointerEvent): void {\n if (!isDragging.value) {\n return\n }\n\n isDragging.value = false\n ;(event.currentTarget as Element).releasePointerCapture?.(event.pointerId)\n}\n\nfunction rotateLeft(): void {\n rotation.value -= 90\n emit('rotate', rotation.value)\n}\n\nfunction rotateRight(): void {\n rotation.value += 90\n emit('rotate', rotation.value)\n}\n\nconst toolbarActions: GrImageViewerToolbarActions = {\n close: closeViewer,\n prev,\n next,\n zoomIn,\n zoomOut,\n reset: resetTransform,\n rotateLeft,\n rotateRight,\n}\n\nfunction onBackdropClick(): void {\n if (!props.hideOnClickModal) {\n return\n }\n\n closeViewer()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'Escape':\n if (!props.closeOnPressEscape) {\n return\n }\n\n event.preventDefault()\n closeViewer()\n return\n\n case 'ArrowLeft':\n event.preventDefault()\n prev()\n return\n\n case 'ArrowRight':\n event.preventDefault()\n next()\n return\n\n case '+':\n case '=':\n case 'Add':\n event.preventDefault()\n zoomIn()\n return\n\n case '-':\n case '_':\n case 'Subtract':\n event.preventDefault()\n zoomOut()\n return\n\n case '0':\n event.preventDefault()\n resetTransform()\n break\n\n default:\n }\n}\n\nwatch(\n () => props.modelValue,\n async (isOpen) => {\n if (!isOpen) {\n stopObservingImage()\n endWheelZoom()\n isDragging.value = false\n return\n }\n\n syncIndexFromInitial()\n await nextTick()\n startObservingImage()\n },\n { immediate: true },\n)\n\nwatch(\n () => props.initialIndex,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n)\n\nwatch(\n () => props.urlList,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n { deep: true },\n)\n\nwatch(\n currentIndex,\n (index) => {\n if (!hasImages.value || total.value < 2) {\n return\n }\n\n preloadAt(normalizeIndex(index - 1))\n preloadAt(normalizeIndex(index + 1))\n },\n { immediate: true },\n)\n\nonBeforeUnmount(() => {\n stopObservingImage()\n endWheelZoom()\n})\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"open\" as=\"template\">\n <Dialog\n as=\"div\"\n class=\"fixed inset-0\"\n :style=\"viewerStyle\"\n role=\"dialog\"\n aria-modal=\"true\"\n :static=\"true\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0 overflow-hidden\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-150 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-120 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-image-viewer-overlay\n class=\"absolute inset-0 bg-black/60 backdrop-blur-sm\"\n aria-hidden=\"true\"\n @click=\"onBackdropClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-180 ease-out\"\n enter-from=\"opacity-0 scale-98\"\n enter-to=\"opacity-100 scale-100\"\n leave=\"duration-130 ease-in\"\n leave-from=\"opacity-100 scale-100\"\n leave-to=\"opacity-0 scale-98\"\n >\n <DialogPanel\n data-ds-image-viewer-panel\n class=\"relative z-10 h-full w-full outline-none\"\n tabindex=\"-1\"\n >\n <div class=\"pointer-events-none absolute inset-x-0 top-0 z-30 px-3 py-3 sm:px-6\">\n <div class=\"pointer-events-auto flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-2\">\n <div\n v-if=\"showProgress\"\n data-ds-image-viewer-progress\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-600 text-white/95 sm:text-sm\"\n >\n {{ displayIndex }} / {{ total }}\n </div>\n\n <div\n v-if=\"showZoomValue\"\n data-ds-image-viewer-zoom-value\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-700 text-white/95 sm:text-sm\"\n >\n {{ zoomValueText }}%\n </div>\n </div>\n\n <button\n type=\"button\"\n data-ds-image-viewer-close\n :aria-label=\"closeLabel\"\n class=\"h-11 w-11 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"closeViewer\"\n >\n <span aria-hidden=\"true\" class=\"text-lg leading-none\">✕</span>\n </button>\n </div>\n </div>\n\n <div\n class=\"relative z-10 h-full w-full flex items-center justify-center px-16 py-16 sm:px-24 sm:py-20\"\n @wheel=\"onWheel\"\n >\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-prev\n :aria-label=\"prevLabel\"\n class=\"absolute left-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:left-6\"\n @click=\"prev\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">‹</span>\n </button>\n\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-next\n :aria-label=\"nextLabel\"\n class=\"absolute right-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:right-6\"\n @click=\"next\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">›</span>\n </button>\n\n <div class=\"h-full w-full flex items-center justify-center\">\n <img\n v-if=\"currentUrl\"\n ref=\"imageEl\"\n data-ds-image-viewer-image\n :src=\"currentUrl\"\n alt=\"\"\n draggable=\"false\"\n class=\"max-h-full max-w-full select-none object-contain will-change-transform\"\n :class=\"[imageTransitionClass, imageCursorClass]\"\n :style=\"imageStyle\"\n @load=\"onImageLoad\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointercancel=\"onPointerUp\"\n >\n\n <div\n v-else\n class=\"rounded-[var(--ds-radius-xl)] border border-white/20 bg-black/25 px-4 py-3 text-sm text-white/80\"\n >\n {{ emptyText }}\n </div>\n </div>\n </div>\n\n <div class=\"pointer-events-none absolute inset-x-0 bottom-0 z-30 px-3 pb-3 sm:px-6 sm:pb-6\">\n <div class=\"pointer-events-auto mx-auto w-full max-w-max\">\n <slot\n name=\"toolbar\"\n v-bind=\"toolbarSlotProps\"\n >\n <div class=\"rounded-full border border-white/20 bg-black/35 p-1 backdrop-blur-sm flex items-center gap-1\">\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-out\n :aria-label=\"zoomOutLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomOut\"\n >\n −\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-reset\n :aria-label=\"resetZoomLabel\"\n class=\"h-11 min-w-11 px-3 flex items-center justify-center rounded-full text-xs font-700 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"resetTransform\"\n >\n 100%\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-in\n :aria-label=\"zoomInLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomIn\"\n >\n +\n </button>\n\n <template v-if=\"$slots['toolbar-actions']\">\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <slot\n name=\"toolbar-actions\"\n v-bind=\"toolbarSlotProps\"\n />\n\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n </template>\n\n <div v-else class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-left\n :aria-label=\"rotateLeftLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateLeft\"\n >\n ↺\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-right\n :aria-label=\"rotateRightLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateRight\"\n >\n ↻\n </button>\n </div>\n </slot>\n </div>\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrInput-gwFQ1sk4.js","names":["$slots","$attrs"],"sources":["../../../../src/components/GrInput/GrInput.vue","../../../../src/components/GrInput/GrInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {computed, nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useSlots} from 'vue'\nimport type {InputHTMLAttributes} from 'vue'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nexport type GrInputSize = 'xs' | 'sm' | 'md' | 'lg'\nexport type GrInputTextAlign = 'left' | 'center' | 'right'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string\n type?: 'text' | 'email' | 'password' | 'number' | 'search'\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n invalid?: boolean\n state?: 'default' | 'success' | 'warning' | 'danger'\n name?: string\n id?: string\n size?: GrInputSize\n\n textAlign?: GrInputTextAlign\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n }>(),\n {\n type: 'text',\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: undefined,\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n textAlign: 'left',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst defaultAddonMinWidth = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '2rem', // w-8\n sm: '2.25rem', // w-9\n md: '2.5rem', // w-10\n lg: '3rem', // w-12\n }\n\n return map[props.size]\n})\n\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst prefixEl = ref<HTMLElement | null>(null)\nconst suffixEl = ref<HTMLElement | null>(null)\n\nconst measuredPrefixWidth = ref<string | undefined>(undefined)\nconst measuredSuffixWidth = ref<string | undefined>(undefined)\n\nlet ro: ResizeObserver | null = null\nlet scheduled = false\n\nfunction readWidthPx(el: HTMLElement | null): string | undefined {\n if (!el) return undefined\n\n const width = Math.ceil(el.getBoundingClientRect().width || 0)\n // jsdom often reports 0; don't overwrite fallback minWidth in that case.\n if (width <= 0) return undefined\n\n return `${width}px`\n}\n\nfunction measure(): void {\n measuredPrefixWidth.value = hasPrefix.value ? readWidthPx(prefixEl.value) : undefined\n measuredSuffixWidth.value = hasSuffix.value ? readWidthPx(suffixEl.value) : undefined\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') return\n if (!ro) ro = new ResizeObserver(() => measure())\n\n ro.disconnect()\n if (prefixEl.value) ro.observe(prefixEl.value)\n if (suffixEl.value) ro.observe(suffixEl.value)\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) return\n scheduled = true\n\n void nextTick(() => {\n scheduled = false\n measure()\n refreshObserver()\n })\n}\n\nonMounted(() => scheduleMeasure())\nonUpdated(() => scheduleMeasure())\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction addLen(a: string, b: string): string {\n if (a === '0px') return b\n if (b === '0px') return a\n\n const apx = a.endsWith('px') ? Number(a.slice(0, -2)) : null\n const bpx = b.endsWith('px') ? Number(b.slice(0, -2)) : null\n\n if (apx !== null && Number.isFinite(apx) && bpx !== null && Number.isFinite(bpx)) {\n return `${apx + bpx}px`\n }\n\n return `calc(${a} + ${b})`\n}\n\nconst basePaddingXLen = computed(() => {\n // Must mirror `sizeClass` horizontal padding (px-*) because inline paddings override class paddings.\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '10px', // px-2.5\n sm: '12px', // px-3\n md: '12px', // px-3\n lg: '16px', // px-4\n }\n\n return map[props.size]\n})\n\nconst inputStyle = computed(() => {\n const leftReserved = hasPrefix.value ? measuredPrefixWidth.value ?? prefixMinWidth.value : '0px'\n const rightReserved = hasSuffix.value ? measuredSuffixWidth.value ?? suffixMinWidth.value : '0px'\n\n // Keep the same visual text padding as without addons (px-*), but add it on top of reserved space.\n const left = hasPrefix.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const right = hasSuffix.value ? addLen(rightReserved, basePaddingXLen.value) : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst prefixStyle = computed(() => {\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n return {\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst sizeClass = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: 'h-7 px-2.5 text-[12px]',\n sm: 'h-8 px-3 text-[13px]',\n md: 'h-10 px-3 text-[14px]',\n lg: 'h-11 px-4 text-[16px]',\n }\n\n return map[props.size]\n})\n\nconst textAlignClass = computed(() => {\n const map: Record<NonNullable<typeof props.textAlign>, string> = {\n left: 'text-left',\n center: 'text-center',\n right: 'text-right',\n }\n\n return map[props.textAlign]\n})\n\nconst className = computed(() => {\n const state = props.state\n\n const borderByState: Record<typeof state, string> = {\n default: 'border-[var(--brd)]',\n success: 'border-[var(--ds-success)] focus-visible:ring-[var(--ds-success)]',\n warning: 'border-[var(--ds-warning)] focus-visible:ring-[var(--ds-warning)]',\n danger: 'border-[var(--ds-danger)] focus-visible:ring-[var(--ds-danger)]',\n }\n\n return [\n sizeClass.value,\n textAlignClass.value,\n props.invalid ? borderByState.danger : borderByState[state],\n ].join(' ')\n})\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLInputElement).value)\n}\n</script>\n\n<template>\n <div class=\"relative w-full\">\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"ds-input-prefix\"\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"props.id\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"props.name\"\n :type=\"props.type\"\n :inputmode=\"props.inputmode\"\n :autocomplete=\"props.autocomplete\"\n :placeholder=\"props.placeholder\"\n :disabled=\"props.disabled\"\n :value=\"props.modelValue\"\n :aria-invalid=\"props.invalid ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"className\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"ds-input-suffix\"\n class=\"absolute inset-y-0 right-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {computed, nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useSlots} from 'vue'\nimport type {InputHTMLAttributes} from 'vue'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nexport type GrInputSize = 'xs' | 'sm' | 'md' | 'lg'\nexport type GrInputTextAlign = 'left' | 'center' | 'right'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string\n type?: 'text' | 'email' | 'password' | 'number' | 'search'\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n invalid?: boolean\n state?: 'default' | 'success' | 'warning' | 'danger'\n name?: string\n id?: string\n size?: GrInputSize\n\n textAlign?: GrInputTextAlign\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n }>(),\n {\n type: 'text',\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: undefined,\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n textAlign: 'left',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst defaultAddonMinWidth = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '2rem', // w-8\n sm: '2.25rem', // w-9\n md: '2.5rem', // w-10\n lg: '3rem', // w-12\n }\n\n return map[props.size]\n})\n\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst prefixEl = ref<HTMLElement | null>(null)\nconst suffixEl = ref<HTMLElement | null>(null)\n\nconst measuredPrefixWidth = ref<string | undefined>(undefined)\nconst measuredSuffixWidth = ref<string | undefined>(undefined)\n\nlet ro: ResizeObserver | null = null\nlet scheduled = false\n\nfunction readWidthPx(el: HTMLElement | null): string | undefined {\n if (!el) return undefined\n\n const width = Math.ceil(el.getBoundingClientRect().width || 0)\n // jsdom often reports 0; don't overwrite fallback minWidth in that case.\n if (width <= 0) return undefined\n\n return `${width}px`\n}\n\nfunction measure(): void {\n measuredPrefixWidth.value = hasPrefix.value ? readWidthPx(prefixEl.value) : undefined\n measuredSuffixWidth.value = hasSuffix.value ? readWidthPx(suffixEl.value) : undefined\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') return\n if (!ro) ro = new ResizeObserver(() => measure())\n\n ro.disconnect()\n if (prefixEl.value) ro.observe(prefixEl.value)\n if (suffixEl.value) ro.observe(suffixEl.value)\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) return\n scheduled = true\n\n void nextTick(() => {\n scheduled = false\n measure()\n refreshObserver()\n })\n}\n\nonMounted(() => scheduleMeasure())\nonUpdated(() => scheduleMeasure())\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction addLen(a: string, b: string): string {\n if (a === '0px') return b\n if (b === '0px') return a\n\n const apx = a.endsWith('px') ? Number(a.slice(0, -2)) : null\n const bpx = b.endsWith('px') ? Number(b.slice(0, -2)) : null\n\n if (apx !== null && Number.isFinite(apx) && bpx !== null && Number.isFinite(bpx)) {\n return `${apx + bpx}px`\n }\n\n return `calc(${a} + ${b})`\n}\n\nconst basePaddingXLen = computed(() => {\n // Must mirror `sizeClass` horizontal padding (px-*) because inline paddings override class paddings.\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '10px', // px-2.5\n sm: '12px', // px-3\n md: '12px', // px-3\n lg: '16px', // px-4\n }\n\n return map[props.size]\n})\n\nconst inputStyle = computed(() => {\n const leftReserved = hasPrefix.value ? measuredPrefixWidth.value ?? prefixMinWidth.value : '0px'\n const rightReserved = hasSuffix.value ? measuredSuffixWidth.value ?? suffixMinWidth.value : '0px'\n\n // Keep the same visual text padding as without addons (px-*), but add it on top of reserved space.\n const left = hasPrefix.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const right = hasSuffix.value ? addLen(rightReserved, basePaddingXLen.value) : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst prefixStyle = computed(() => {\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n return {\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst sizeClass = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: 'h-7 px-2.5 text-[12px]',\n sm: 'h-8 px-3 text-[13px]',\n md: 'h-10 px-3 text-[14px]',\n lg: 'h-11 px-4 text-[16px]',\n }\n\n return map[props.size]\n})\n\nconst textAlignClass = computed(() => {\n const map: Record<NonNullable<typeof props.textAlign>, string> = {\n left: 'text-left',\n center: 'text-center',\n right: 'text-right',\n }\n\n return map[props.textAlign]\n})\n\nconst className = computed(() => {\n const state = props.state\n\n const borderByState: Record<typeof state, string> = {\n default: 'border-[var(--brd)]',\n success: 'border-[var(--ds-success)] focus-visible:ring-[var(--ds-success)]',\n warning: 'border-[var(--ds-warning)] focus-visible:ring-[var(--ds-warning)]',\n danger: 'border-[var(--ds-danger)] focus-visible:ring-[var(--ds-danger)]',\n }\n\n return [\n sizeClass.value,\n textAlignClass.value,\n props.invalid ? borderByState.danger : borderByState[state],\n ].join(' ')\n})\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLInputElement).value)\n}\n</script>\n\n<template>\n <div class=\"relative w-full\">\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"ds-input-prefix\"\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"props.id\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"props.name\"\n :type=\"props.type\"\n :inputmode=\"props.inputmode\"\n :autocomplete=\"props.autocomplete\"\n :placeholder=\"props.placeholder\"\n :disabled=\"props.disabled\"\n :value=\"props.modelValue\"\n :aria-invalid=\"props.invalid ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"className\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"ds-input-suffix\"\n class=\"absolute inset-y-0 right-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrInputTag-Dcj3F6rQ.js","names":[],"sources":["../../../../src/components/GrInputTag/GrInputTag.vue","../../../../src/components/GrInputTag/GrInputTag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconClose from '~icons/lucide/x'\n\nimport {\n grInputTagInputClass,\n grInputTagWrapperClass,\n type GrInputTagSize,\n type GrInputTagState,\n} from './grInputTagStyles'\n\nexport type { GrInputTagSize, GrInputTagState } from './grInputTagStyles'\n\nconst REGEX_SPECIAL_CHAR_RE = /[.*+?^${}()|[\\]\\\\]/g\n\n/**\n * Props for {@link GrInputTag}.\n * Tag-input primitive: renders a set of badges with an inline `<input>` for entering new tags.\n * Splits input by configurable `separators`, supports paste, Enter and Backspace editing.\n */\nexport interface GrInputTagProps {\n modelValue: string[]\n placeholder?: string\n disabled?: boolean\n readonly?: boolean\n invalid?: boolean\n state?: GrInputTagState\n size?: GrInputTagSize\n separators?: string[]\n allowDuplicates?: boolean\n trim?: boolean\n max?: number\n addOnBlur?: boolean\n clearInputOnAdd?: boolean\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n tagClosable?: boolean\n /** i18n-friendly aria-label for the per-tag remove button. */\n removeTagLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrInputTagProps>(),\n {\n placeholder: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n state: 'default',\n size: 'md',\n separators: () => [','],\n allowDuplicates: false,\n trim: true,\n max: undefined,\n addOnBlur: false,\n clearInputOnAdd: true,\n tagTone: 'neutral',\n tagDark: false,\n tagSize: 'md',\n tagRadius: 'round',\n tagClosable: true,\n removeTagLabel: 'Remove tag',\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string[]): void\n (e: 'add', value: string): void\n (e: 'remove', value: string, index: number): void\n}>()\n\nconst inputValue = ref('')\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({ focus })\n\nconst isMaxed = computed(() => props.max !== undefined && props.modelValue.length >= props.max)\nconst canEdit = computed(() => !props.disabled && !props.readonly)\nconst showRemove = computed(() => props.tagClosable && canEdit.value)\n\nfunction escapeRegexChar(ch: string): string {\n return ch.replace(REGEX_SPECIAL_CHAR_RE, '\\\\$&')\n}\n\nconst splitRegex = computed(() => {\n const seps = (props.separators ?? []).filter(Boolean).map(s => s[0]!)\n const uniq = [...new Set([...seps, '\\n', '\\r', '\\t'])]\n const body = uniq.map(escapeRegexChar).join('')\n return new RegExp(`[${body}]+`, 'g')\n})\n\nfunction normalizeTag(raw: string): string {\n return props.trim ? raw.trim() : raw\n}\n\nfunction splitToTags(raw: string): string[] {\n return raw\n .split(splitRegex.value)\n .map(v => normalizeTag(v))\n .filter(Boolean)\n}\n\nfunction addMany(rawTags: string[]): boolean {\n if (!canEdit.value || isMaxed.value)\n return false\n\n const next = props.modelValue.slice()\n let changed = false\n\n for (const raw of rawTags) {\n const tag = normalizeTag(raw)\n if (!tag)\n continue\n\n if (!props.allowDuplicates && next.includes(tag))\n continue\n\n if (props.max !== undefined && next.length >= props.max)\n break\n\n next.push(tag)\n changed = true\n emit('add', tag)\n }\n\n if (!changed)\n return false\n\n emit('update:modelValue', next)\n return true\n}\n\nfunction commitInput(): void {\n if (!canEdit.value || isMaxed.value || !inputValue.value)\n return\n\n const tags = splitToTags(inputValue.value)\n if (!tags.length)\n return\n\n const changed = addMany(tags)\n if (changed && props.clearInputOnAdd)\n inputValue.value = ''\n}\n\nfunction removeAt(index: number): void {\n if (!canEdit.value)\n return\n\n const next = props.modelValue.slice()\n const removed = next[index]\n if (removed === undefined)\n return\n\n next.splice(index, 1)\n emit('update:modelValue', next)\n emit('remove', removed, index)\n}\n\nfunction onInput(e: Event): void {\n inputValue.value = (e.target as HTMLInputElement).value\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (!canEdit.value)\n return\n\n if (e.key === 'Enter') {\n e.preventDefault()\n commitInput()\n return\n }\n\n if ((props.separators ?? []).includes(e.key)) {\n e.preventDefault()\n commitInput()\n return\n }\n\n if (e.key === 'Backspace' && inputValue.value.length === 0 && props.modelValue.length > 0) {\n e.preventDefault()\n removeAt(props.modelValue.length - 1)\n }\n}\n\nfunction onBlur(): void {\n if (props.addOnBlur)\n commitInput()\n}\n\nfunction onPaste(e: ClipboardEvent): void {\n if (!canEdit.value || isMaxed.value)\n return\n\n const pasted = e.clipboardData?.getData('text')\n if (!pasted)\n return\n\n const tags = splitToTags(pasted)\n if (tags.length <= 1)\n return\n\n e.preventDefault()\n addMany(tags)\n}\n\nconst wrapperClassName = computed(() => {\n return grInputTagWrapperClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n disabled: props.disabled,\n })\n})\n\nconst inputClassName = computed(() => {\n return grInputTagInputClass(props.size)\n})\n\nconst placeholderText = computed(() => props.modelValue.length > 0 ? undefined : props.placeholder)\n</script>\n\n<template>\n <div\n data-ds-input-tag\n data-testid=\"ds-input-tag\"\n class=\"w-full flex flex-wrap items-center rounded-md border bg-[var(--bg)] text-[var(--fg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"wrapperClassName\"\n @click=\"focus\"\n >\n <GrBadge\n v-for=\"(tag, i) in modelValue\"\n :key=\"`${tag}-${i}`\"\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n class=\"gap-1\"\n data-ds-input-tag-item\n data-testid=\"ds-input-tag-item\"\n :data-index=\"i\"\n >\n <slot name=\"tag\" :tag=\"tag\" :index=\"i\" :remove=\"() => removeAt(i)\">\n <span class=\"truncate max-w-[18rem]\">{{ tag }}</span>\n </slot>\n\n <button\n v-if=\"showRemove\"\n type=\"button\"\n class=\"-mr-0.5 inline-flex items-center justify-center rounded-[6px] p-0.5 opacity-70 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :aria-label=\"removeTagLabel\"\n data-ds-input-tag-remove\n data-testid=\"ds-input-tag-remove\"\n :data-index=\"i\"\n @mousedown.prevent.stop\n @click.stop=\"removeAt(i)\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </button>\n </GrBadge>\n\n <input\n ref=\"inputEl\"\n data-ds-input-tag-input\n data-testid=\"ds-input-tag-input\"\n :value=\"inputValue\"\n :disabled=\"disabled || isMaxed\"\n :readonly=\"readonly\"\n :placeholder=\"placeholderText\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"flex-1 min-w-[120px] bg-transparent border-none outline-none placeholder:text-[var(--muted-fg)]\"\n :class=\"inputClassName\"\n @input=\"onInput\"\n @keydown=\"onKeydown\"\n @blur=\"onBlur\"\n @paste=\"onPaste\"\n >\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconClose from '~icons/lucide/x'\n\nimport {\n grInputTagInputClass,\n grInputTagWrapperClass,\n type GrInputTagSize,\n type GrInputTagState,\n} from './grInputTagStyles'\n\nexport type { GrInputTagSize, GrInputTagState } from './grInputTagStyles'\n\nconst REGEX_SPECIAL_CHAR_RE = /[.*+?^${}()|[\\]\\\\]/g\n\n/**\n * Props for {@link GrInputTag}.\n * Tag-input primitive: renders a set of badges with an inline `<input>` for entering new tags.\n * Splits input by configurable `separators`, supports paste, Enter and Backspace editing.\n */\nexport interface GrInputTagProps {\n modelValue: string[]\n placeholder?: string\n disabled?: boolean\n readonly?: boolean\n invalid?: boolean\n state?: GrInputTagState\n size?: GrInputTagSize\n separators?: string[]\n allowDuplicates?: boolean\n trim?: boolean\n max?: number\n addOnBlur?: boolean\n clearInputOnAdd?: boolean\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n tagClosable?: boolean\n /** i18n-friendly aria-label for the per-tag remove button. */\n removeTagLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrInputTagProps>(),\n {\n placeholder: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n state: 'default',\n size: 'md',\n separators: () => [','],\n allowDuplicates: false,\n trim: true,\n max: undefined,\n addOnBlur: false,\n clearInputOnAdd: true,\n tagTone: 'neutral',\n tagDark: false,\n tagSize: 'md',\n tagRadius: 'round',\n tagClosable: true,\n removeTagLabel: 'Remove tag',\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string[]): void\n (e: 'add', value: string): void\n (e: 'remove', value: string, index: number): void\n}>()\n\nconst inputValue = ref('')\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({ focus })\n\nconst isMaxed = computed(() => props.max !== undefined && props.modelValue.length >= props.max)\nconst canEdit = computed(() => !props.disabled && !props.readonly)\nconst showRemove = computed(() => props.tagClosable && canEdit.value)\n\nfunction escapeRegexChar(ch: string): string {\n return ch.replace(REGEX_SPECIAL_CHAR_RE, '\\\\$&')\n}\n\nconst splitRegex = computed(() => {\n const seps = (props.separators ?? []).filter(Boolean).map(s => s[0]!)\n const uniq = [...new Set([...seps, '\\n', '\\r', '\\t'])]\n const body = uniq.map(escapeRegexChar).join('')\n return new RegExp(`[${body}]+`, 'g')\n})\n\nfunction normalizeTag(raw: string): string {\n return props.trim ? raw.trim() : raw\n}\n\nfunction splitToTags(raw: string): string[] {\n return raw\n .split(splitRegex.value)\n .map(v => normalizeTag(v))\n .filter(Boolean)\n}\n\nfunction addMany(rawTags: string[]): boolean {\n if (!canEdit.value || isMaxed.value)\n return false\n\n const next = props.modelValue.slice()\n let changed = false\n\n for (const raw of rawTags) {\n const tag = normalizeTag(raw)\n if (!tag)\n continue\n\n if (!props.allowDuplicates && next.includes(tag))\n continue\n\n if (props.max !== undefined && next.length >= props.max)\n break\n\n next.push(tag)\n changed = true\n emit('add', tag)\n }\n\n if (!changed)\n return false\n\n emit('update:modelValue', next)\n return true\n}\n\nfunction commitInput(): void {\n if (!canEdit.value || isMaxed.value || !inputValue.value)\n return\n\n const tags = splitToTags(inputValue.value)\n if (!tags.length)\n return\n\n const changed = addMany(tags)\n if (changed && props.clearInputOnAdd)\n inputValue.value = ''\n}\n\nfunction removeAt(index: number): void {\n if (!canEdit.value)\n return\n\n const next = props.modelValue.slice()\n const removed = next[index]\n if (removed === undefined)\n return\n\n next.splice(index, 1)\n emit('update:modelValue', next)\n emit('remove', removed, index)\n}\n\nfunction onInput(e: Event): void {\n inputValue.value = (e.target as HTMLInputElement).value\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (!canEdit.value)\n return\n\n if (e.key === 'Enter') {\n e.preventDefault()\n commitInput()\n return\n }\n\n if ((props.separators ?? []).includes(e.key)) {\n e.preventDefault()\n commitInput()\n return\n }\n\n if (e.key === 'Backspace' && inputValue.value.length === 0 && props.modelValue.length > 0) {\n e.preventDefault()\n removeAt(props.modelValue.length - 1)\n }\n}\n\nfunction onBlur(): void {\n if (props.addOnBlur)\n commitInput()\n}\n\nfunction onPaste(e: ClipboardEvent): void {\n if (!canEdit.value || isMaxed.value)\n return\n\n const pasted = e.clipboardData?.getData('text')\n if (!pasted)\n return\n\n const tags = splitToTags(pasted)\n if (tags.length <= 1)\n return\n\n e.preventDefault()\n addMany(tags)\n}\n\nconst wrapperClassName = computed(() => {\n return grInputTagWrapperClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n disabled: props.disabled,\n })\n})\n\nconst inputClassName = computed(() => {\n return grInputTagInputClass(props.size)\n})\n\nconst placeholderText = computed(() => props.modelValue.length > 0 ? undefined : props.placeholder)\n</script>\n\n<template>\n <div\n data-ds-input-tag\n data-testid=\"ds-input-tag\"\n class=\"w-full flex flex-wrap items-center rounded-md border bg-[var(--bg)] text-[var(--fg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"wrapperClassName\"\n @click=\"focus\"\n >\n <GrBadge\n v-for=\"(tag, i) in modelValue\"\n :key=\"`${tag}-${i}`\"\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n class=\"gap-1\"\n data-ds-input-tag-item\n data-testid=\"ds-input-tag-item\"\n :data-index=\"i\"\n >\n <slot name=\"tag\" :tag=\"tag\" :index=\"i\" :remove=\"() => removeAt(i)\">\n <span class=\"truncate max-w-[18rem]\">{{ tag }}</span>\n </slot>\n\n <button\n v-if=\"showRemove\"\n type=\"button\"\n class=\"-mr-0.5 inline-flex items-center justify-center rounded-[6px] p-0.5 opacity-70 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :aria-label=\"removeTagLabel\"\n data-ds-input-tag-remove\n data-testid=\"ds-input-tag-remove\"\n :data-index=\"i\"\n @mousedown.prevent.stop\n @click.stop=\"removeAt(i)\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </button>\n </GrBadge>\n\n <input\n ref=\"inputEl\"\n data-ds-input-tag-input\n data-testid=\"ds-input-tag-input\"\n :value=\"inputValue\"\n :disabled=\"disabled || isMaxed\"\n :readonly=\"readonly\"\n :placeholder=\"placeholderText\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"flex-1 min-w-[120px] bg-transparent border-none outline-none placeholder:text-[var(--muted-fg)]\"\n :class=\"inputClassName\"\n @input=\"onInput\"\n @keydown=\"onKeydown\"\n @blur=\"onBlur\"\n @paste=\"onPaste\"\n >\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrLink-BMwn2iJ9.js","names":[],"sources":["../../../../src/components/GrLink/GrLink.vue","../../../../src/components/GrLink/GrLink.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrLink — DS-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение DS остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n} from './grLinkStyles'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<{\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n}>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n variant: 'primary',\n underline: 'auto',\n size: 'md',\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — DS не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value) return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: props.size,\n underline: props.underline,\n variant: props.variant,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n >\n <slot />\n </component>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrLink — DS-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение DS остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n} from './grLinkStyles'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<{\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n}>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n variant: 'primary',\n underline: 'auto',\n size: 'md',\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — DS не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value) return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: props.size,\n underline: props.underline,\n variant: props.variant,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n >\n <slot />\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCA,MAAM,QAAQ;EA4Bd,MAAM,QAAQ,UAAS;EAIvB,MAAM,gBAAgB,eAAe,CAAC,MAAM,aAAa,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,MAAK;EAEpF,MAAM,WAAW,eAAmC;AAClD,OAAI,CAAC,cAAc,MAAO,QAAO;AACjC,OAAI,MAAM,GAIR,QAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,GAAE;AAEnE,UAAO;IACR;EAED,MAAM,iBAAiB,eAAe;AACpC,OAAI,MAAM,OACR,QAAO,MAAM;AAEf,OAAI,MAAM,SACR,QAAO;IAGV;EAED,MAAM,cAAc,eAAe;AACjC,OAAI,MAAM,IACR,QAAO,MAAM;AAIf,OAAI,eAAe,UAAU,SAC3B,QAAO;IAGV;EAED,MAAM,YAAY,eAAe;GAC/B,MAAM,eAAe,YAAY;IAC/B,MAAM,MAAM;IACZ,WAAW,MAAM;IACjB,SAAS,MAAM;IACf,UAAU,MAAM;IACjB,CAAA;AAID,UAAO,MAAM,WACT,GAAG,cAAc,GAAG,iBACpB,GAAG,cAAc,GAAG,eAAe,GAAG;IAC3C;;uBAIC,YAWY,wBAVL,SAAA,MAAQ,EADf,WAEU,MASE,MATG,EAAA;IACZ,MAAM,cAAA,QAAgB,QAAA,OAAO,KAAA;IAC7B,QAAQ,cAAA,QAAgB,eAAA,QAAiB,KAAA;IACzC,KAAK,cAAA,QAAgB,YAAA,QAAc,KAAA;IACnC,cAAY,QAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAO,UAAA;;2BAEA,CAAR,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrNumberInput-YIWhV85S.js","names":["$slots","$attrs"],"sources":["../../../../src/components/GrNumberInput/GrNumberInput.vue","../../../../src/components/GrNumberInput/GrNumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { InputHTMLAttributes } from 'vue'\nimport { computed, nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useSlots } from 'vue'\n\nimport {\n grNumberInputInputClass,\n grNumberInputShellClass,\n type GrNumberInputControlsDirection,\n type GrNumberInputSize,\n type GrNumberInputState,\n type GrNumberInputTextAlign,\n} from './grNumberInputStyles'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst ADDON_PX_BY_SIZE: Record<GrNumberInputSize, number> = {\n xs: 32,\n sm: 36,\n md: 40,\n lg: 48,\n}\n\nconst BASE_PADDING_X_LEN_BY_SIZE: Record<GrNumberInputSize, string> = {\n xs: '10px',\n sm: '12px',\n md: '12px',\n lg: '16px',\n}\n\nfunction px(n: number): string {\n return `${n}px`\n}\n\nexport interface GrNumberInputProps {\n /** Значение (строка — контролируем форматирование внутри). */\n modelValue: string\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Быстрый флаг невалидности; эквивалент `state='danger'` + `aria-invalid`. */\n invalid?: boolean\n state?: GrNumberInputState\n name?: string\n id?: string\n size?: GrNumberInputSize\n\n textAlign?: GrNumberInputTextAlign\n\n decimalSeparator?: string\n step?: number\n min?: number\n max?: number\n precision?: number\n\n /** Показывать кнопки +/-. */\n controls?: boolean\n controlsDirection?: GrNumberInputControlsDirection\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n\n /** i18n-friendly aria-label для кнопки \"увеличить\". */\n increaseLabel?: string\n /** i18n-friendly aria-label для кнопки \"уменьшить\". */\n decreaseLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrNumberInputProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: 'decimal',\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n textAlign: 'left',\n\n decimalSeparator: '.',\n step: 1,\n min: undefined,\n max: undefined,\n precision: undefined,\n\n controls: false,\n controlsDirection: 'vertical',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n\n increaseLabel: 'Increase',\n decreaseLabel: 'Decrease',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n (e: 'change', value: string): void\n}>()\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst hasHorizontalControls = computed(() => props.controls && props.controlsDirection === 'horizontal')\nconst hasVerticalControls = computed(() => props.controls && props.controlsDirection === 'vertical')\n\nconst addonPx = computed(() => ADDON_PX_BY_SIZE[props.size])\nconst addonLen = computed(() => px(addonPx.value))\nconst basePaddingXLen = computed(() => BASE_PADDING_X_LEN_BY_SIZE[props.size])\n\nconst defaultAddonMinWidth = computed(() => addonLen.value)\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst prefixEl = ref<HTMLElement | null>(null)\nconst suffixEl = ref<HTMLElement | null>(null)\n\nconst measuredPrefixWidth = ref<string | undefined>(undefined)\nconst measuredSuffixWidth = ref<string | undefined>(undefined)\n\nlet ro: ResizeObserver | null = null\nlet scheduled = false\n\nfunction readWidthPx(el: HTMLElement | null): string | undefined {\n if (!el) return undefined\n\n const width = Math.ceil(el.getBoundingClientRect().width || 0)\n if (width <= 0) return undefined\n\n return `${width}px`\n}\n\nfunction measure(): void {\n measuredPrefixWidth.value = hasPrefix.value ? readWidthPx(prefixEl.value) : undefined\n measuredSuffixWidth.value = hasSuffix.value ? readWidthPx(suffixEl.value) : undefined\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') return\n if (!ro) ro = new ResizeObserver(() => measure())\n\n ro.disconnect()\n if (prefixEl.value) ro.observe(prefixEl.value)\n if (suffixEl.value) ro.observe(suffixEl.value)\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) return\n scheduled = true\n\n void nextTick(() => {\n scheduled = false\n measure()\n refreshObserver()\n })\n}\n\nonMounted(() => scheduleMeasure())\nonUpdated(() => scheduleMeasure())\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction addLen(a: string, b: string): string {\n if (a === '0px') return b\n if (b === '0px') return a\n\n const apx = a.endsWith('px') ? Number(a.slice(0, -2)) : null\n const bpx = b.endsWith('px') ? Number(b.slice(0, -2)) : null\n\n if (apx !== null && Number.isFinite(apx) && bpx !== null && Number.isFinite(bpx))\n return `${apx + bpx}px`\n\n return `calc(${a} + ${b})`\n}\n\nconst prefixLen = computed(() => (hasPrefix.value ? measuredPrefixWidth.value ?? prefixMinWidth.value : '0px'))\nconst suffixLen = computed(() => (hasSuffix.value ? measuredSuffixWidth.value ?? suffixMinWidth.value : '0px'))\n\nfunction addonStyle(side: 'left' | 'right', offset: string): Record<string, string> {\n return {\n width: addonLen.value,\n [side]: offset,\n }\n}\n\nconst inputStyle = computed(() => {\n const leftControls = hasHorizontalControls.value ? addonLen.value : '0px'\n const rightControlsCount = (hasHorizontalControls.value ? 1 : 0) + (hasVerticalControls.value ? 1 : 0)\n const rightControls = rightControlsCount > 0 ? px(addonPx.value * rightControlsCount) : '0px'\n\n const leftReserved = addLen(prefixLen.value, leftControls)\n const rightReserved = addLen(suffixLen.value, rightControls)\n\n const left = hasPrefix.value || hasHorizontalControls.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const right = hasSuffix.value || rightControlsCount > 0 ? addLen(rightReserved, basePaddingXLen.value) : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n return {\n right: '0px',\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n }\n})\n\nconst prefixStyle = computed(() => {\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n }\n})\n\nconst verticalControlsStyle = computed(() => addonStyle('right', suffixLen.value))\nconst horizontalLeftControlsStyle = computed(() => addonStyle('left', prefixLen.value))\nconst horizontalRightControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\nconst shellClassName = computed(() => {\n return grNumberInputShellClass({\n disabled: props.disabled,\n state: props.invalid ? 'danger' : props.state,\n })\n})\n\nconst inputClassName = computed(() => {\n return grNumberInputInputClass({\n size: props.size,\n textAlign: props.textAlign,\n })\n})\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction sanitize(raw: string): string {\n const sep = props.decimalSeparator || '.'\n\n let out = ''\n let hasSepLocal = false\n\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i]\n if (isDigit(ch)) {\n out += ch\n continue\n }\n\n if ((ch === sep || ch === '.' || ch === ',') && !hasSepLocal) {\n out += sep\n hasSepLocal = true\n }\n }\n\n return out\n}\n\nfunction toNumber(value: string): number | null {\n if (value.trim() === '') return null\n const sep = props.decimalSeparator || '.'\n const normalized = sep === '.' ? value : value.replace(sep, '.')\n const n = Number(normalized)\n if (!Number.isFinite(n)) return null\n return n\n}\n\nfunction clamp(v: number): number {\n if (props.min !== undefined) v = Math.max(props.min, v)\n if (props.max !== undefined) v = Math.min(props.max, v)\n return v\n}\n\nfunction normalize(v: number): number {\n if (props.precision === undefined) return v\n const p = Math.max(0, props.precision)\n return Number(v.toFixed(p))\n}\n\nfunction format(n: number): string {\n const sep = props.decimalSeparator || '.'\n const p = props.precision\n\n let s = p === undefined ? String(n) : n.toFixed(Math.max(0, p))\n if (sep !== '.') s = s.replace('.', sep)\n return s\n}\n\nfunction onInput(e: Event): void {\n const el = e.target as HTMLInputElement\n const next = sanitize(el.value)\n if (el.value !== next) el.value = next\n emit('update:modelValue', next)\n}\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLInputElement\n const next = sanitize(el.value)\n if (el.value !== next) el.value = next\n emit('change', next)\n}\n\nfunction stepBy(dir: 1 | -1): void {\n if (props.disabled) return\n\n const current = toNumber(props.modelValue) ?? 0\n const next = clamp(normalize(current + (props.step ?? 1) * dir))\n const nextStr = format(next)\n\n emit('update:modelValue', nextStr)\n emit('change', nextStr)\n focus()\n}\n</script>\n\n<template>\n <div\n data-ds-number-input\n class=\"relative w-full overflow-hidden rounded-md border bg-[var(--bg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"shellClassName\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"number-input-prefix\"\n data-ds-number-input-prefix\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"id\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"name\"\n type=\"text\"\n :inputmode=\"inputmode\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :value=\"modelValue\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"inputClassName\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"number-input-suffix\"\n data-ds-number-input-suffix\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <div\n v-if=\"hasVerticalControls\"\n data-testid=\"number-input-controls-vertical\"\n data-ds-number-input-controls=\"vertical\"\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)]\"\n :style=\"verticalControlsStyle\"\n >\n <div class=\"flex flex-col justify-center gap-1\">\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"increaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 14l5-5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"decreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 10l5 5 5-5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-left\"\n data-ds-number-input-controls=\"horizontal-left\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-r border-[var(--brd)]\"\n :style=\"horizontalLeftControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"decreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M14 7l-5 5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-right\"\n data-ds-number-input-controls=\"horizontal-right\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-l border-[var(--brd)]\"\n :style=\"horizontalRightControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"increaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10 7l5 5-5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { InputHTMLAttributes } from 'vue'\nimport { computed, nextTick, onBeforeUnmount, onMounted, onUpdated, ref, useSlots } from 'vue'\n\nimport {\n grNumberInputInputClass,\n grNumberInputShellClass,\n type GrNumberInputControlsDirection,\n type GrNumberInputSize,\n type GrNumberInputState,\n type GrNumberInputTextAlign,\n} from './grNumberInputStyles'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst ADDON_PX_BY_SIZE: Record<GrNumberInputSize, number> = {\n xs: 32,\n sm: 36,\n md: 40,\n lg: 48,\n}\n\nconst BASE_PADDING_X_LEN_BY_SIZE: Record<GrNumberInputSize, string> = {\n xs: '10px',\n sm: '12px',\n md: '12px',\n lg: '16px',\n}\n\nfunction px(n: number): string {\n return `${n}px`\n}\n\nexport interface GrNumberInputProps {\n /** Значение (строка — контролируем форматирование внутри). */\n modelValue: string\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Быстрый флаг невалидности; эквивалент `state='danger'` + `aria-invalid`. */\n invalid?: boolean\n state?: GrNumberInputState\n name?: string\n id?: string\n size?: GrNumberInputSize\n\n textAlign?: GrNumberInputTextAlign\n\n decimalSeparator?: string\n step?: number\n min?: number\n max?: number\n precision?: number\n\n /** Показывать кнопки +/-. */\n controls?: boolean\n controlsDirection?: GrNumberInputControlsDirection\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n\n /** i18n-friendly aria-label для кнопки \"увеличить\". */\n increaseLabel?: string\n /** i18n-friendly aria-label для кнопки \"уменьшить\". */\n decreaseLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrNumberInputProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: 'decimal',\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n textAlign: 'left',\n\n decimalSeparator: '.',\n step: 1,\n min: undefined,\n max: undefined,\n precision: undefined,\n\n controls: false,\n controlsDirection: 'vertical',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n\n increaseLabel: 'Increase',\n decreaseLabel: 'Decrease',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n (e: 'change', value: string): void\n}>()\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst hasHorizontalControls = computed(() => props.controls && props.controlsDirection === 'horizontal')\nconst hasVerticalControls = computed(() => props.controls && props.controlsDirection === 'vertical')\n\nconst addonPx = computed(() => ADDON_PX_BY_SIZE[props.size])\nconst addonLen = computed(() => px(addonPx.value))\nconst basePaddingXLen = computed(() => BASE_PADDING_X_LEN_BY_SIZE[props.size])\n\nconst defaultAddonMinWidth = computed(() => addonLen.value)\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst prefixEl = ref<HTMLElement | null>(null)\nconst suffixEl = ref<HTMLElement | null>(null)\n\nconst measuredPrefixWidth = ref<string | undefined>(undefined)\nconst measuredSuffixWidth = ref<string | undefined>(undefined)\n\nlet ro: ResizeObserver | null = null\nlet scheduled = false\n\nfunction readWidthPx(el: HTMLElement | null): string | undefined {\n if (!el) return undefined\n\n const width = Math.ceil(el.getBoundingClientRect().width || 0)\n if (width <= 0) return undefined\n\n return `${width}px`\n}\n\nfunction measure(): void {\n measuredPrefixWidth.value = hasPrefix.value ? readWidthPx(prefixEl.value) : undefined\n measuredSuffixWidth.value = hasSuffix.value ? readWidthPx(suffixEl.value) : undefined\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') return\n if (!ro) ro = new ResizeObserver(() => measure())\n\n ro.disconnect()\n if (prefixEl.value) ro.observe(prefixEl.value)\n if (suffixEl.value) ro.observe(suffixEl.value)\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) return\n scheduled = true\n\n void nextTick(() => {\n scheduled = false\n measure()\n refreshObserver()\n })\n}\n\nonMounted(() => scheduleMeasure())\nonUpdated(() => scheduleMeasure())\nonBeforeUnmount(() => ro?.disconnect())\n\nfunction addLen(a: string, b: string): string {\n if (a === '0px') return b\n if (b === '0px') return a\n\n const apx = a.endsWith('px') ? Number(a.slice(0, -2)) : null\n const bpx = b.endsWith('px') ? Number(b.slice(0, -2)) : null\n\n if (apx !== null && Number.isFinite(apx) && bpx !== null && Number.isFinite(bpx))\n return `${apx + bpx}px`\n\n return `calc(${a} + ${b})`\n}\n\nconst prefixLen = computed(() => (hasPrefix.value ? measuredPrefixWidth.value ?? prefixMinWidth.value : '0px'))\nconst suffixLen = computed(() => (hasSuffix.value ? measuredSuffixWidth.value ?? suffixMinWidth.value : '0px'))\n\nfunction addonStyle(side: 'left' | 'right', offset: string): Record<string, string> {\n return {\n width: addonLen.value,\n [side]: offset,\n }\n}\n\nconst inputStyle = computed(() => {\n const leftControls = hasHorizontalControls.value ? addonLen.value : '0px'\n const rightControlsCount = (hasHorizontalControls.value ? 1 : 0) + (hasVerticalControls.value ? 1 : 0)\n const rightControls = rightControlsCount > 0 ? px(addonPx.value * rightControlsCount) : '0px'\n\n const leftReserved = addLen(prefixLen.value, leftControls)\n const rightReserved = addLen(suffixLen.value, rightControls)\n\n const left = hasPrefix.value || hasHorizontalControls.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const right = hasSuffix.value || rightControlsCount > 0 ? addLen(rightReserved, basePaddingXLen.value) : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n return {\n right: '0px',\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n }\n})\n\nconst prefixStyle = computed(() => {\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n }\n})\n\nconst verticalControlsStyle = computed(() => addonStyle('right', suffixLen.value))\nconst horizontalLeftControlsStyle = computed(() => addonStyle('left', prefixLen.value))\nconst horizontalRightControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\nconst shellClassName = computed(() => {\n return grNumberInputShellClass({\n disabled: props.disabled,\n state: props.invalid ? 'danger' : props.state,\n })\n})\n\nconst inputClassName = computed(() => {\n return grNumberInputInputClass({\n size: props.size,\n textAlign: props.textAlign,\n })\n})\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction sanitize(raw: string): string {\n const sep = props.decimalSeparator || '.'\n\n let out = ''\n let hasSepLocal = false\n\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i]\n if (isDigit(ch)) {\n out += ch\n continue\n }\n\n if ((ch === sep || ch === '.' || ch === ',') && !hasSepLocal) {\n out += sep\n hasSepLocal = true\n }\n }\n\n return out\n}\n\nfunction toNumber(value: string): number | null {\n if (value.trim() === '') return null\n const sep = props.decimalSeparator || '.'\n const normalized = sep === '.' ? value : value.replace(sep, '.')\n const n = Number(normalized)\n if (!Number.isFinite(n)) return null\n return n\n}\n\nfunction clamp(v: number): number {\n if (props.min !== undefined) v = Math.max(props.min, v)\n if (props.max !== undefined) v = Math.min(props.max, v)\n return v\n}\n\nfunction normalize(v: number): number {\n if (props.precision === undefined) return v\n const p = Math.max(0, props.precision)\n return Number(v.toFixed(p))\n}\n\nfunction format(n: number): string {\n const sep = props.decimalSeparator || '.'\n const p = props.precision\n\n let s = p === undefined ? String(n) : n.toFixed(Math.max(0, p))\n if (sep !== '.') s = s.replace('.', sep)\n return s\n}\n\nfunction onInput(e: Event): void {\n const el = e.target as HTMLInputElement\n const next = sanitize(el.value)\n if (el.value !== next) el.value = next\n emit('update:modelValue', next)\n}\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLInputElement\n const next = sanitize(el.value)\n if (el.value !== next) el.value = next\n emit('change', next)\n}\n\nfunction stepBy(dir: 1 | -1): void {\n if (props.disabled) return\n\n const current = toNumber(props.modelValue) ?? 0\n const next = clamp(normalize(current + (props.step ?? 1) * dir))\n const nextStr = format(next)\n\n emit('update:modelValue', nextStr)\n emit('change', nextStr)\n focus()\n}\n</script>\n\n<template>\n <div\n data-ds-number-input\n class=\"relative w-full overflow-hidden rounded-md border bg-[var(--bg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"shellClassName\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"number-input-prefix\"\n data-ds-number-input-prefix\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"id\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"name\"\n type=\"text\"\n :inputmode=\"inputmode\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :value=\"modelValue\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"inputClassName\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"number-input-suffix\"\n data-ds-number-input-suffix\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <div\n v-if=\"hasVerticalControls\"\n data-testid=\"number-input-controls-vertical\"\n data-ds-number-input-controls=\"vertical\"\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)]\"\n :style=\"verticalControlsStyle\"\n >\n <div class=\"flex flex-col justify-center gap-1\">\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"increaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 14l5-5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"decreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 10l5 5 5-5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-left\"\n data-ds-number-input-controls=\"horizontal-left\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-r border-[var(--brd)]\"\n :style=\"horizontalLeftControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"decreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M14 7l-5 5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-right\"\n data-ds-number-input-controls=\"horizontal-right\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-l border-[var(--brd)]\"\n :style=\"horizontalRightControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"increaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10 7l5 5-5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,114 +0,0 @@
1
- import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CPDKonoB.js";
2
- import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-LcJ88Vce.js";
3
- import { t as GrSelect_default } from "../../GrSelect/chunks/GrSelect-CnHdbPlf.js";
4
- import { Fragment, computed, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, renderList, toDisplayString, unref, withCtx } from "vue";
5
- //#region src/components/GrPagination/GrPagination.vue?vue&type=script&setup=true&lang.ts
6
- var _hoisted_1 = ["aria-label"];
7
- var _hoisted_2 = { class: "min-w-[100px]" };
8
- var _hoisted_3 = { class: "flex items-center gap-1" };
9
- var _hoisted_4 = [
10
- "aria-current",
11
- "aria-label",
12
- "onClick"
13
- ];
14
- //#endregion
15
- //#region src/components/GrPagination/GrPagination.vue
16
- var GrPagination_default = /* @__PURE__ */ defineComponent({
17
- __name: "GrPagination",
18
- props: {
19
- page: {},
20
- pageSize: {},
21
- total: {},
22
- pageSizes: { default: () => [
23
- 10,
24
- 20,
25
- 50
26
- ] }
27
- },
28
- emits: ["update:page", "update:pageSize"],
29
- setup(__props, { emit: __emit }) {
30
- const { t } = useGranularityTranslations();
31
- const props = __props;
32
- const emit = __emit;
33
- const pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)));
34
- const pageSizeModel = computed({
35
- get: () => String(props.pageSize),
36
- set: (value) => emit("update:pageSize", Number(value))
37
- });
38
- const pageSizeOptions = computed(() => props.pageSizes.map((pageSize) => ({
39
- value: String(pageSize),
40
- label: String(pageSize)
41
- })));
42
- const pages = computed(() => {
43
- const max = pageCount.value;
44
- const current = props.page;
45
- const windowSize = 5;
46
- const half = Math.floor(windowSize / 2);
47
- let start = Math.max(1, current - half);
48
- const end = Math.min(max, start + windowSize - 1);
49
- start = Math.max(1, end - windowSize + 1);
50
- const list = [];
51
- for (let page = start; page <= end; page += 1) list.push(page);
52
- return list;
53
- });
54
- function goTo(page) {
55
- emit("update:page", page);
56
- }
57
- function prev() {
58
- goTo(Math.max(1, props.page - 1));
59
- }
60
- function next() {
61
- goTo(Math.min(pageCount.value, props.page + 1));
62
- }
63
- return (_ctx, _cache) => {
64
- return openBlock(), createElementBlock("div", {
65
- class: "flex flex-wrap items-center justify-end gap-3",
66
- "data-ds-pagination": "",
67
- role: "navigation",
68
- "aria-label": unref(t)("gr.pagination.pageSize", "Pagination")
69
- }, [
70
- createElementVNode("div", _hoisted_2, [createVNode(GrSelect_default, {
71
- modelValue: pageSizeModel.value,
72
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => pageSizeModel.value = $event),
73
- options: pageSizeOptions.value,
74
- "aria-label": unref(t)("gr.pagination.pageSize", "Page size")
75
- }, null, 8, [
76
- "modelValue",
77
- "options",
78
- "aria-label"
79
- ])]),
80
- createVNode(GrButton_default, {
81
- variant: "ghost",
82
- size: "sm",
83
- disabled: __props.page <= 1,
84
- onClick: prev
85
- }, {
86
- default: withCtx(() => [createTextVNode(toDisplayString(unref(t)("gr.pagination.prev", "Prev")), 1)]),
87
- _: 1
88
- }, 8, ["disabled"]),
89
- createElementVNode("div", _hoisted_3, [(openBlock(true), createElementBlock(Fragment, null, renderList(pages.value, (pageNumber) => {
90
- return openBlock(), createElementBlock("button", {
91
- key: pageNumber,
92
- type: "button",
93
- "data-ds-pagination-page": "",
94
- "aria-current": pageNumber === __props.page ? "page" : void 0,
95
- "aria-label": `Page ${pageNumber}`,
96
- class: normalizeClass(["h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]", pageNumber === __props.page ? "bg-[var(--primary)] text-[var(--primary-fg)]" : "text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]"]),
97
- onClick: ($event) => goTo(pageNumber)
98
- }, toDisplayString(pageNumber), 11, _hoisted_4);
99
- }), 128))]),
100
- createVNode(GrButton_default, {
101
- variant: "ghost",
102
- size: "sm",
103
- disabled: __props.page >= pageCount.value,
104
- onClick: next
105
- }, {
106
- default: withCtx(() => [createTextVNode(toDisplayString(unref(t)("gr.pagination.next", "Next")), 1)]),
107
- _: 1
108
- }, 8, ["disabled"])
109
- ], 8, _hoisted_1);
110
- };
111
- }
112
- });
113
- //#endregion
114
- export { GrPagination_default as t };
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrPagination-BNDEqM8_.js","names":[],"sources":["../../../../src/components/GrPagination/GrPagination.vue","../../../../src/components/GrPagination/GrPagination.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Окно отображаемых номеров — 5.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n})\n\nconst emit = defineEmits<{\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}>()\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\nconst pages = computed(() => {\n const max = pageCount.value\n const current = props.page\n const windowSize = 5\n const half = Math.floor(windowSize / 2)\n\n let start = Math.max(1, current - half)\n const end = Math.min(max, start + windowSize - 1)\n start = Math.max(1, end - windowSize + 1)\n\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n\n return list\n})\n\nfunction goTo(page: number): void {\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, props.page - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, props.page + 1))\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end gap-3\"\n data-ds-pagination\n role=\"navigation\"\n :aria-label=\"t('gr.pagination.pageSize', 'Pagination')\"\n >\n <div class=\"min-w-[100px]\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <div class=\"flex items-center gap-1\">\n <button\n v-for=\"pageNumber in pages\"\n :key=\"pageNumber\"\n type=\"button\"\n data-ds-pagination-page\n :aria-current=\"pageNumber === page ? 'page' : undefined\"\n :aria-label=\"`Page ${pageNumber}`\"\n class=\"h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :class=\"pageNumber === page ? 'bg-[var(--primary)] text-[var(--primary-fg)]' : 'text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]'\"\n @click=\"goTo(pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Окно отображаемых номеров — 5.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n})\n\nconst emit = defineEmits<{\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}>()\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\nconst pages = computed(() => {\n const max = pageCount.value\n const current = props.page\n const windowSize = 5\n const half = Math.floor(windowSize / 2)\n\n let start = Math.max(1, current - half)\n const end = Math.min(max, start + windowSize - 1)\n start = Math.max(1, end - windowSize + 1)\n\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n\n return list\n})\n\nfunction goTo(page: number): void {\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, props.page - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, props.page + 1))\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end gap-3\"\n data-ds-pagination\n role=\"navigation\"\n :aria-label=\"t('gr.pagination.pageSize', 'Pagination')\"\n >\n <div class=\"min-w-[100px]\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <div class=\"flex items-center gap-1\">\n <button\n v-for=\"pageNumber in pages\"\n :key=\"pageNumber\"\n type=\"button\"\n data-ds-pagination-page\n :aria-current=\"pageNumber === page ? 'page' : undefined\"\n :aria-label=\"`Page ${pageNumber}`\"\n class=\"h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :class=\"pageNumber === page ? 'bg-[var(--primary)] text-[var(--primary-fg)]' : 'text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]'\"\n @click=\"goTo(pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBA,MAAM,EAAE,MAAM,4BAA2B;EAEzC,MAAM,QAAQ;EAId,MAAM,OAAO;EAKb,MAAM,YAAY,eAAe,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,QAAQ,MAAM,SAAS,CAAC,CAAA;EAErF,MAAM,gBAAgB,SAAS;GAC7B,WAAW,OAAO,MAAM,SAAS;GACjC,MAAM,UAAkB,KAAK,mBAAmB,OAAO,MAAM,CAAC;GAC/D,CAAA;EAED,MAAM,kBAAkB,eACtB,MAAM,UAAU,KAAI,cAAa;GAC/B,OAAO,OAAO,SAAS;GACvB,OAAO,OAAO,SAAS;GACxB,EAAE,CACL;EAEA,MAAM,QAAQ,eAAe;GAC3B,MAAM,MAAM,UAAU;GACtB,MAAM,UAAU,MAAM;GACtB,MAAM,aAAa;GACnB,MAAM,OAAO,KAAK,MAAM,aAAa,EAAC;GAEtC,IAAI,QAAQ,KAAK,IAAI,GAAG,UAAU,KAAI;GACtC,MAAM,MAAM,KAAK,IAAI,KAAK,QAAQ,aAAa,EAAC;AAChD,WAAQ,KAAK,IAAI,GAAG,MAAM,aAAa,EAAC;GAExC,MAAM,OAAiB,EAAC;AACxB,QAAK,IAAI,OAAO,OAAO,QAAQ,KAAK,QAAQ,EAC1C,MAAK,KAAK,KAAI;AAEhB,UAAO;IACR;EAED,SAAS,KAAK,MAAoB;AAChC,QAAK,eAAe,KAAI;;EAG1B,SAAS,OAAa;AACpB,QAAK,KAAK,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;;EAGlC,SAAS,OAAa;AACpB,QAAK,KAAK,IAAI,UAAU,OAAO,MAAM,OAAO,EAAE,CAAA;;;uBAK9C,mBAqCM,OAAA;IApCJ,OAAM;IACN,sBAAA;IACA,MAAK;IACJ,cAAY,MAAA,EAAC,CAAA,0BAAA,aAAA;;IAEd,mBAMM,OANN,YAMM,CALJ,YAIE,kBAAA;iBAHS,cAAA;gFAAa,QAAA;KACrB,SAAS,gBAAA;KACT,cAAY,MAAA,EAAC,CAAA,0BAAA,YAAA;;;;;;IAIlB,YAEW,kBAAA;KAFD,SAAQ;KAAQ,MAAK;KAAM,UAAU,QAAA,QAAI;KAAQ,SAAO;;4BAC3B,CAAA,gBAAA,gBAAlC,MAAA,EAAC,CAAA,sBAAA,OAAA,CAAA,EAAA,EAAA,CAAA,CAAA;;;IAGN,mBAcM,OAdN,YAcM,EAAA,UAAA,KAAA,EAbJ,mBAYS,UAAA,MAAA,WAXc,MAAA,QAAd,eAAU;yBADnB,mBAYS,UAAA;MAVN,KAAK;MACN,MAAK;MACL,2BAAA;MACC,gBAAc,eAAe,QAAA,OAAI,SAAY,KAAA;MAC7C,cAAU,QAAU;MACrB,OAAK,eAAA,CAAC,wJACE,eAAe,QAAA,OAAI,iDAAA,wEAAA,CAAA;MAC1B,UAAK,WAAE,KAAK,WAAU;wBAEpB,WAAU,EAAA,IAAA,WAAA;;IAIjB,YAEW,kBAAA;KAFD,SAAQ;KAAQ,MAAK;KAAM,UAAU,QAAA,QAAQ,UAAA;KAAY,SAAO;;4BACnC,CAAA,gBAAA,gBAAlC,MAAA,EAAC,CAAA,sBAAA,OAAA,CAAA,EAAA,EAAA,CAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GrPromptDialog-BV4EW3XO.js","names":[],"sources":["../../../../src/components/GrPromptDialog/GrPromptDialog.vue","../../../../src/components/GrPromptDialog/GrPromptDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrPromptDialog — DS-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n */\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой `required`. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n}\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: 'Prompt',\n description: undefined,\n label: 'Value',\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n buttonSize: undefined,\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n requiredErrorText: 'Enter a value.',\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}>()\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (v) touched.value = false\n },\n)\n\nconst canConfirm = computed(() => {\n if (!props.required) return true\n return valueModel.value.trim().length > 0\n})\n\nconst validationError = computed(() => {\n if (!props.required) return undefined\n if (!touched.value) return undefined\n return valueModel.value.trim().length > 0\n ? undefined\n : props.requiredErrorText\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n if (!canConfirm.value) {\n touched.value = true\n inputRef.value?.focus()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"title\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"closeLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <GrFormField :label=\"label\" :error=\"fieldErrorMessage\" for-id=\"ds-prompt-input\">\n <GrInput\n id=\"ds-prompt-input\"\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"ds-prompt-input\"\n :placeholder=\"placeholder\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"touched = true\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"ds-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ cancelText }}\n </GrButton>\n <GrButton\n data-testid=\"ds-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading\"\n :disabled=\"confirmDisabled || (required && touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ confirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrPromptDialog — DS-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n */\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой `required`. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n}\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: 'Prompt',\n description: undefined,\n label: 'Value',\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n buttonSize: undefined,\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n requiredErrorText: 'Enter a value.',\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}>()\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (v) touched.value = false\n },\n)\n\nconst canConfirm = computed(() => {\n if (!props.required) return true\n return valueModel.value.trim().length > 0\n})\n\nconst validationError = computed(() => {\n if (!props.required) return undefined\n if (!touched.value) return undefined\n return valueModel.value.trim().length > 0\n ? undefined\n : props.requiredErrorText\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n if (!canConfirm.value) {\n touched.value = true\n inputRef.value?.focus()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"title\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"closeLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <GrFormField :label=\"label\" :error=\"fieldErrorMessage\" for-id=\"ds-prompt-input\">\n <GrInput\n id=\"ds-prompt-input\"\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"ds-prompt-input\"\n :placeholder=\"placeholder\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"touched = true\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"ds-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ cancelText }}\n </GrButton>\n <GrButton\n data-testid=\"ds-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading\"\n :disabled=\"confirmDisabled || (required && touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ confirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n"],"mappings":"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"}