@feugene/granularity 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/dist/chunks/{adapter-CRyH4YC8.js.map → adapter-BqqjEZGU.js.map} +1 -1
  2. package/dist/chunks/arrow-up-DZdIrpaF.js.map +1 -0
  3. package/dist/chunks/config-72sW9Bag2.js.map +1 -0
  4. package/dist/chunks/{config-B8rC_K97.js.map → config-B1kFmhpM.js.map} +1 -1
  5. package/dist/chunks/{config-BhvNm4Pp.js.map → config-B2YTEe_h.js.map} +1 -1
  6. package/dist/chunks/{config-C0rHS1-l.js.map → config-BOdu-O_B.js.map} +1 -1
  7. package/dist/chunks/{config-D27BL0H9.js.map → config-BU0olrvr.js.map} +1 -1
  8. package/dist/chunks/{config-XoANT5mU.js → config-BY81UKv0.js} +1 -1
  9. package/dist/chunks/{config-XoANT5mU.js.map → config-BY81UKv0.js.map} +1 -1
  10. package/dist/chunks/config-B_nSBc7W.js.map +1 -0
  11. package/dist/chunks/{config-DbqFk-IW.js.map → config-BbKroFtR.js.map} +1 -1
  12. package/dist/chunks/config-Bcf-8XlK2.js.map +1 -0
  13. package/dist/chunks/config-BduXEXjt.js.map +1 -0
  14. package/dist/chunks/config-BeQuykDk2.js.map +1 -0
  15. package/dist/chunks/{config-Dusr9jcE.js.map → config-BnWSHTEU.js.map} +1 -1
  16. package/dist/chunks/{config-C4AlNm912.js → config-Br_qI3Za.js} +1 -1
  17. package/dist/chunks/config-Br_qI3Za.js.map +1 -0
  18. package/dist/chunks/{config-C5TN9J2Z.js → config-C5OwJM3-.js} +1 -1
  19. package/dist/chunks/{config-C5TN9J2Z.js.map → config-C5OwJM3-.js.map} +1 -1
  20. package/dist/chunks/config-C6zT56Jc2.js.map +1 -0
  21. package/dist/chunks/{config-ZJVOVgqR.js → config-CFf7xaG1.js} +1 -1
  22. package/dist/chunks/{config-ZJVOVgqR.js.map → config-CFf7xaG1.js.map} +1 -1
  23. package/dist/chunks/{config-wo-ncgTH.js.map → config-CdJpnjDj.js.map} +1 -1
  24. package/dist/chunks/config-CetsgpaZ2.js.map +1 -0
  25. package/dist/chunks/{config-D1DI3roo.js.map → config-Ci9nXGUV.js.map} +1 -1
  26. package/dist/chunks/{config-CjEplP6D.js.map → config-CpMYmvVT.js.map} +1 -1
  27. package/dist/chunks/{config-DZAQpfz0.js.map → config-Cp_XHsze.js.map} +1 -1
  28. package/dist/chunks/{config-D98m8iyz.js.map → config-CvGdaXvH.js.map} +1 -1
  29. package/dist/chunks/{config-CtomKf-k.js.map → config-D4s_QWVP.js.map} +1 -1
  30. package/dist/chunks/{config-D32znLtV2.js.map → config-D6Oe3blW2.js.map} +1 -1
  31. package/dist/chunks/{config-CVnluhKO.js.map → config-DF_RlCIO.js.map} +1 -1
  32. package/dist/chunks/{config-C4qAA0Sb.js.map → config-DSBlHQVa.js.map} +1 -1
  33. package/dist/chunks/{config-Mp9DJ9Mq2.js.map → config-DaHI5bD_2.js.map} +1 -1
  34. package/dist/chunks/{config-3SftO0uG.js.map → config-Dcb7xK-C.js.map} +1 -1
  35. package/dist/chunks/config-DmjwIqx1.js.map +1 -0
  36. package/dist/chunks/{config-D6Af6WZw.js.map → config-DoBk_CzG.js.map} +1 -1
  37. package/dist/chunks/config-Dzd6BzIN2.js.map +1 -0
  38. package/dist/chunks/{config-Db4BEG8Y.js.map → config-O1_FTqD3.js.map} +1 -1
  39. package/dist/chunks/config-_3De4HFf.js.map +1 -0
  40. package/dist/chunks/{config-B44QVIge.js → config-d386uO3O.js} +1 -1
  41. package/dist/chunks/{config-B44QVIge.js.map → config-d386uO3O.js.map} +1 -1
  42. package/dist/chunks/{config-DudL02ag.js.map → config-iuMz79Mg.js.map} +1 -1
  43. package/dist/chunks/config-vcDVCYXo.js.map +1 -0
  44. package/dist/chunks/{en-QNZhjmSJ.js.map → en-foQA8imU.js.map} +1 -1
  45. package/dist/chunks/{es-CKLPuE0h.js → es-Bx3Kuy6z.js} +3 -3
  46. package/dist/chunks/{es-CKLPuE0h.js.map → es-Bx3Kuy6z.js.map} +1 -1
  47. package/dist/chunks/{es-C1c5XsIK.js.map → es-DZFSojYL.js.map} +1 -1
  48. package/dist/chunks/{fileValidation-CwJA8Vmn.js.map → fileValidation-c_iBg74s.js.map} +1 -1
  49. package/dist/chunks/{grDropdownStyles-DMp7RrI9.js.map → grDropdownStyles-B41mVR1H.js.map} +1 -1
  50. package/dist/chunks/{grLoadingStyles-CrxZHFL0.js.map → grLoadingStyles-DmI-SJ3i.js.map} +1 -1
  51. package/dist/chunks/{grModalStyles-Dv2NXBKi.js.map → grModalStyles-DS1Vl6EW.js.map} +1 -1
  52. package/dist/chunks/{grSelectStyles-DTAYNzxn.js.map → grSelectStyles-pUt19VGy.js.map} +1 -1
  53. package/dist/chunks/{grStyle-5EMYYctb.js.map → grStyle-CUaTmpyW.js.map} +1 -1
  54. package/dist/chunks/{granularityI18n-CXa_uN7f.js → granularityI18n-LcJ88Vce.js} +1 -1
  55. package/dist/chunks/{granularityI18n-CXa_uN7f.js.map → granularityI18n-LcJ88Vce.js.map} +1 -1
  56. package/dist/chunks/{ru-DcF7ciz0.js.map → ru-LhkPpPIl.js.map} +1 -1
  57. package/dist/chunks/safelist-_UVzJrgE2.js.map +1 -0
  58. package/dist/chunks/{shared-D7q-upeJ.js → shared-BiqXd5MW.js} +37 -37
  59. package/dist/chunks/{shared-D7q-upeJ.js.map → shared-BiqXd5MW.js.map} +1 -1
  60. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DE-d77Us.js → GrConfirmDialog-DuUQA7nv.js} +31 -8
  61. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-DuUQA7nv.js.map +1 -0
  62. package/dist/components/GrConfirmDialog/index.js +2 -2
  63. package/dist/components/GrDataTable/chunks/{GrDataTable-QDOJWmdY.js → GrDataTable-ClcYiyc8.js} +2 -2
  64. package/dist/components/GrDataTable/chunks/{GrDataTable-QDOJWmdY.js.map → GrDataTable-ClcYiyc8.js.map} +1 -1
  65. package/dist/components/GrDataTable/index.js +2 -2
  66. package/dist/components/GrDialog/chunks/{GrDialog-Ba3Ojcsq.js → GrDialog-SYcF-4op.js} +1 -1
  67. package/dist/components/GrDialog/chunks/{GrDialog-Ba3Ojcsq.js.map → GrDialog-SYcF-4op.js.map} +1 -1
  68. package/dist/components/GrDialog/index.js +2 -2
  69. package/dist/components/GrDrawer/chunks/{GrDrawer-h33X403E.js → GrDrawer-BW3wtI7h.js} +1 -1
  70. package/dist/components/GrDrawer/chunks/{GrDrawer-h33X403E.js.map → GrDrawer-BW3wtI7h.js.map} +1 -1
  71. package/dist/components/GrDrawer/index.js +2 -2
  72. package/dist/components/GrDropdown/chunks/{GrDropdown-BkACCXdp.js → GrDropdown-DOvHKnih.js} +1 -1
  73. package/dist/components/GrDropdown/chunks/{GrDropdown-BkACCXdp.js.map → GrDropdown-DOvHKnih.js.map} +1 -1
  74. package/dist/components/GrDropdown/index.js +2 -2
  75. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BEenXrJw.js → GrDropdownMenu-CLPwbiyG.js} +1 -1
  76. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BEenXrJw.js.map → GrDropdownMenu-CLPwbiyG.js.map} +1 -1
  77. package/dist/components/GrDropdownMenu/index.js +2 -2
  78. package/dist/components/GrEmptyState/chunks/{GrEmptyState-BLbMJFW-.js.map → GrEmptyState-Dj2V5GaX.js.map} +1 -1
  79. package/dist/components/GrEmptyState/index.js +2 -2
  80. package/dist/components/GrFileUpload/chunks/{GrFileUpload-DYRERhI2.js → GrFileUpload-BEKNB1RM.js} +5 -5
  81. package/dist/components/GrFileUpload/chunks/GrFileUpload-BEKNB1RM.js.map +1 -0
  82. package/dist/components/GrFileUpload/index.js +3 -3
  83. package/dist/components/GrFormField/chunks/{GrFormField-C1oWTT_0.js.map → GrFormField-U7c_aDPJ.js.map} +1 -1
  84. package/dist/components/GrFormField/index.js +2 -2
  85. package/dist/components/GrFormFile/chunks/{GrFormFile-WqF-dPTB.js → GrFormFile-CIleXnfr.js} +1 -1
  86. package/dist/components/GrFormFile/chunks/{GrFormFile-WqF-dPTB.js.map → GrFormFile-CIleXnfr.js.map} +1 -1
  87. package/dist/components/GrFormFile/index.js +2 -2
  88. package/dist/components/GrFormSection/chunks/{GrFormSection-CWBk2EUm.js.map → GrFormSection-DpGhY7MX.js.map} +1 -1
  89. package/dist/components/GrFormSection/index.js +2 -2
  90. package/dist/components/GrImageViewer/chunks/{GrImageViewer-Cpj5zyZQ.js.map → GrImageViewer-Cn1X-sWS.js.map} +1 -1
  91. package/dist/components/GrImageViewer/index.js +2 -2
  92. package/dist/components/GrInput/chunks/{GrInput-CfO2RsM-.js.map → GrInput-gwFQ1sk4.js.map} +1 -1
  93. package/dist/components/GrInput/index.js +2 -2
  94. package/dist/components/GrInputTag/chunks/{GrInputTag-CeEy0IwD.js → GrInputTag-Dcj3F6rQ.js} +1 -1
  95. package/dist/components/GrInputTag/chunks/{GrInputTag-CeEy0IwD.js.map → GrInputTag-Dcj3F6rQ.js.map} +1 -1
  96. package/dist/components/GrInputTag/index.js +2 -2
  97. package/dist/components/GrLink/chunks/{GrLink-BLPigqij.js → GrLink-BMwn2iJ9.js} +1 -1
  98. package/dist/components/GrLink/chunks/{GrLink-BLPigqij.js.map → GrLink-BMwn2iJ9.js.map} +1 -1
  99. package/dist/components/GrLink/index.js +2 -2
  100. package/dist/components/GrList/chunks/{GrList-C2GpK56-.js.map → GrList-D8nM1FuV.js.map} +1 -1
  101. package/dist/components/GrList/index.js +2 -2
  102. package/dist/components/GrLoading/chunks/{GrLoading-CxNgHgNw.js → GrLoading-mfyfghxs.js} +1 -1
  103. package/dist/components/GrLoading/chunks/{GrLoading-CxNgHgNw.js.map → GrLoading-mfyfghxs.js.map} +1 -1
  104. package/dist/components/GrLoading/index.js +2 -2
  105. package/dist/components/GrModal/chunks/{GrModal-gvfvlp7k.js → GrModal-2ALC3kwy.js} +73 -7
  106. package/dist/components/GrModal/chunks/GrModal-2ALC3kwy.js.map +1 -0
  107. package/dist/components/GrModal/index.js +3 -3
  108. package/dist/components/GrNavbar/chunks/{GrNavbar-CxHwBvSG.js → GrNavbar-BH7XiF9q.js} +1 -1
  109. package/dist/components/GrNavbar/chunks/{GrNavbar-CxHwBvSG.js.map → GrNavbar-BH7XiF9q.js.map} +1 -1
  110. package/dist/components/GrNavbar/index.js +2 -2
  111. package/dist/components/GrNumberInput/chunks/{GrNumberInput-ECtFCIKV.js → GrNumberInput-YIWhV85S.js} +1 -1
  112. package/dist/components/GrNumberInput/chunks/{GrNumberInput-ECtFCIKV.js.map → GrNumberInput-YIWhV85S.js.map} +1 -1
  113. package/dist/components/GrNumberInput/index.js +2 -2
  114. package/dist/components/GrPagination/chunks/{GrPagination-dMXZz120.js → GrPagination-cD6rFO9W.js} +2 -2
  115. package/dist/components/GrPagination/chunks/{GrPagination-dMXZz120.js.map → GrPagination-cD6rFO9W.js.map} +1 -1
  116. package/dist/components/GrPagination/index.js +2 -2
  117. package/dist/components/GrProgressBar/chunks/{GrProgressBar-hQQK8cYh.js → GrProgressBar-C2HdYjp4.js} +1 -1
  118. package/dist/components/GrProgressBar/chunks/{GrProgressBar-hQQK8cYh.js.map → GrProgressBar-C2HdYjp4.js.map} +1 -1
  119. package/dist/components/GrProgressBar/index.js +2 -2
  120. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CfKf4tPx.js → GrPromptDialog-23x2nZvv.js} +54 -28
  121. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-23x2nZvv.js.map +1 -0
  122. package/dist/components/GrPromptDialog/index.js +2 -2
  123. package/dist/components/GrRadio/chunks/{GrRadio-CQ3w3Bj6.js → GrRadio-IFU9Hiu2.js} +1 -1
  124. package/dist/components/GrRadio/chunks/{GrRadio-CQ3w3Bj6.js.map → GrRadio-IFU9Hiu2.js.map} +1 -1
  125. package/dist/components/GrRadio/index.js +2 -2
  126. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-DAaVzlPx.js → GrRadioGroup-C8IcJuuF.js} +1 -1
  127. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-DAaVzlPx.js.map → GrRadioGroup-C8IcJuuF.js.map} +1 -1
  128. package/dist/components/GrRadioGroup/index.js +2 -2
  129. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-BMvXREcv.js +221 -0
  130. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-BMvXREcv.js.map +1 -0
  131. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-CzdTh4gd.js +782 -0
  132. package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-CzdTh4gd.js.map +1 -0
  133. package/dist/components/GrResponseErrorBanner/index.js +3 -0
  134. package/dist/components/GrSegmented/chunks/{GrSegmented-BPl1koHc.js → GrSegmented-BRmwBsEG.js} +1 -1
  135. package/dist/components/GrSegmented/chunks/{GrSegmented-BPl1koHc.js.map → GrSegmented-BRmwBsEG.js.map} +1 -1
  136. package/dist/components/GrSegmented/index.js +2 -2
  137. package/dist/components/GrSelect/chunks/{GrSelect-C3NAfTqL.js → GrSelect-W9lbAjRZ.js} +2 -2
  138. package/dist/components/GrSelect/chunks/{GrSelect-C3NAfTqL.js.map → GrSelect-W9lbAjRZ.js.map} +1 -1
  139. package/dist/components/GrSelect/index.js +2 -2
  140. package/dist/components/GrSidebar/chunks/{GrSidebar-DVucgcfV.js.map → GrSidebar-BpBtAGBs.js.map} +1 -1
  141. package/dist/components/GrSidebar/index.js +2 -2
  142. package/dist/components/GrSkeleton/chunks/{GrSkeleton-CDTvCSoH.js.map → GrSkeleton-URyMkBqI.js.map} +1 -1
  143. package/dist/components/GrSkeleton/index.js +2 -2
  144. package/dist/components/GrSwitch/chunks/{GrSwitch-Bnamgqb2.js → GrSwitch-29h0QhMO.js} +1 -1
  145. package/dist/components/GrSwitch/chunks/{GrSwitch-Bnamgqb2.js.map → GrSwitch-29h0QhMO.js.map} +1 -1
  146. package/dist/components/GrSwitch/index.js +2 -2
  147. package/dist/components/GrTable/chunks/{GrTable-CyQ9uw8A.js.map → GrTable-dTWUxzRm.js.map} +1 -1
  148. package/dist/components/GrTable/index.js +2 -2
  149. package/dist/components/GrTabs/chunks/{GrTabs-CiGmcmvm.js.map → GrTabs-D3ntDdtg.js.map} +1 -1
  150. package/dist/components/GrTabs/index.js +2 -2
  151. package/dist/components/GrTextarea/chunks/{GrTextarea-VBSc6A7D.js → GrTextarea-Cv6r2GuC.js} +1 -1
  152. package/dist/components/GrTextarea/chunks/{GrTextarea-VBSc6A7D.js.map → GrTextarea-Cv6r2GuC.js.map} +1 -1
  153. package/dist/components/GrTextarea/index.js +2 -2
  154. package/dist/components/GrToaster/chunks/{GrToaster-BGNNmnr9.js → GrToaster-DluJmR5D.js} +1 -1
  155. package/dist/components/GrToaster/chunks/{GrToaster-BGNNmnr9.js.map → GrToaster-DluJmR5D.js.map} +1 -1
  156. package/dist/components/GrToaster/index.js +2 -2
  157. package/dist/components/GrTooltip/chunks/{GrTooltip-CrV1gI_c.js.map → GrTooltip-B7I1AVIC.js.map} +1 -1
  158. package/dist/components/GrTooltip/index.js +2 -2
  159. package/dist/components/GrTree/chunks/{GrTree-mYa3COxG.js.map → GrTree-B2SMU5Py.js.map} +1 -1
  160. package/dist/components/GrTree/index.js +2 -2
  161. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-BEdtrjwX.js → GrTreeSelect-C1abr5uH.js} +4 -4
  162. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-BEdtrjwX.js.map → GrTreeSelect-C1abr5uH.js.map} +1 -1
  163. package/dist/components/GrTreeSelect/index.js +2 -2
  164. package/dist/directives/dropzone.js +1 -1
  165. package/dist/directives/loading.js +1 -1
  166. package/dist/fileValidation/index.js +1 -1
  167. package/dist/granular-provider-node.js +2 -2
  168. package/dist/granular-provider.js +1 -1
  169. package/dist/i18n/all.js +1 -1
  170. package/dist/i18n/index.js +2 -2
  171. package/dist/index.js +474 -1015
  172. package/dist/index.js.map +1 -1
  173. package/dist/types/src/components/GrConfirmDialog/GrConfirmDialog.vue.d.ts +36 -3
  174. package/dist/types/src/components/GrDialogService/GrDialogServiceHost.vue.d.ts +3 -0
  175. package/dist/types/src/components/GrDialogService/config.d.ts +1 -0
  176. package/dist/types/src/components/GrDialogService/index.d.ts +4 -0
  177. package/dist/types/src/components/GrDialogService/store.d.ts +22 -0
  178. package/dist/types/src/components/GrDialogService/types.d.ts +141 -0
  179. package/dist/types/src/components/GrDialogService/useDialogService.d.ts +13 -0
  180. package/dist/types/src/components/GrFileUpload/GrFileUpload.vue.d.ts +3 -3
  181. package/dist/types/src/components/GrFormFile/GrFormFile.vue.d.ts +2 -2
  182. package/dist/types/src/components/GrInput/GrInput.vue.d.ts +2 -2
  183. package/dist/types/src/components/GrInputTag/GrInputTag.vue.d.ts +1 -1
  184. package/dist/types/src/components/GrModal/grModalEscStack.d.ts +30 -0
  185. package/dist/types/src/components/GrNumberInput/GrNumberInput.vue.d.ts +4 -4
  186. package/dist/types/src/components/GrPromptDialog/GrPromptDialog.vue.d.ts +42 -3
  187. package/dist/types/src/components/GrResponseErrorBanner/GrUploadErrorBanner.vue.d.ts +1 -1
  188. package/dist/types/src/components/GrResponseErrorBanner/index.d.ts +2 -2
  189. package/dist/types/src/components/GrResponseErrorBanner/parsers/createResponseErrorClassifier.d.ts +27 -0
  190. package/dist/types/src/components/GrResponseErrorBanner/parsers/index.d.ts +2 -2
  191. package/dist/types/src/components/GrSelect/GrSelect.vue.d.ts +1 -1
  192. package/dist/types/src/components/GrTextarea/GrTextarea.vue.d.ts +2 -2
  193. package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +1 -1
  194. package/dist/types/src/index.d.ts +1 -0
  195. package/package.json +5 -1
  196. package/dist/chunks/arrow-up-DNXrMoAI.js.map +0 -1
  197. package/dist/chunks/config-C0b_3LJV.js.map +0 -1
  198. package/dist/chunks/config-C4AlNm912.js.map +0 -1
  199. package/dist/chunks/config-CxCAfXZ1.js.map +0 -1
  200. package/dist/chunks/config-DDEWvph-2.js.map +0 -1
  201. package/dist/chunks/config-DDLoUczC.js.map +0 -1
  202. package/dist/chunks/config-DK2NpdyX2.js.map +0 -1
  203. package/dist/chunks/config-DQOSlWvp.js.map +0 -1
  204. package/dist/chunks/config-DnsISjD8.js.map +0 -1
  205. package/dist/chunks/config-DxqMXzNV2.js.map +0 -1
  206. package/dist/chunks/config-DxqpV-KM.js.map +0 -1
  207. package/dist/chunks/config-iDTkWUdh2.js.map +0 -1
  208. package/dist/chunks/config-rxFL-6yF2.js.map +0 -1
  209. package/dist/chunks/safelist-BF822K15.js.map +0 -1
  210. package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-DE-d77Us.js.map +0 -1
  211. package/dist/components/GrFileUpload/chunks/GrFileUpload-DYRERhI2.js.map +0 -1
  212. package/dist/components/GrModal/chunks/GrModal-gvfvlp7k.js.map +0 -1
  213. package/dist/components/GrPromptDialog/chunks/GrPromptDialog-CfKf4tPx.js.map +0 -1
  214. /package/dist/chunks/{adapter-CRyH4YC8.js → adapter-BqqjEZGU.js} +0 -0
  215. /package/dist/chunks/{arrow-up-DNXrMoAI.js → arrow-up-DZdIrpaF.js} +0 -0
  216. /package/dist/chunks/{config-C0b_3LJV.js → config-72sW9Bag2.js} +0 -0
  217. /package/dist/chunks/{config-B8rC_K97.js → config-B1kFmhpM.js} +0 -0
  218. /package/dist/chunks/{config-BhvNm4Pp.js → config-B2YTEe_h.js} +0 -0
  219. /package/dist/chunks/{config-C0rHS1-l.js → config-BOdu-O_B.js} +0 -0
  220. /package/dist/chunks/{config-D27BL0H9.js → config-BU0olrvr.js} +0 -0
  221. /package/dist/chunks/{config-DxqMXzNV2.js → config-B_nSBc7W.js} +0 -0
  222. /package/dist/chunks/{config-DbqFk-IW.js → config-BbKroFtR.js} +0 -0
  223. /package/dist/chunks/{config-DQOSlWvp.js → config-Bcf-8XlK2.js} +0 -0
  224. /package/dist/chunks/{config-iDTkWUdh2.js → config-BduXEXjt.js} +0 -0
  225. /package/dist/chunks/{config-DnsISjD8.js → config-BeQuykDk2.js} +0 -0
  226. /package/dist/chunks/{config-Dusr9jcE.js → config-BnWSHTEU.js} +0 -0
  227. /package/dist/chunks/{config-DxqpV-KM.js → config-C6zT56Jc2.js} +0 -0
  228. /package/dist/chunks/{config-wo-ncgTH.js → config-CdJpnjDj.js} +0 -0
  229. /package/dist/chunks/{config-CxCAfXZ1.js → config-CetsgpaZ2.js} +0 -0
  230. /package/dist/chunks/{config-D1DI3roo.js → config-Ci9nXGUV.js} +0 -0
  231. /package/dist/chunks/{config-CjEplP6D.js → config-CpMYmvVT.js} +0 -0
  232. /package/dist/chunks/{config-DZAQpfz0.js → config-Cp_XHsze.js} +0 -0
  233. /package/dist/chunks/{config-D98m8iyz.js → config-CvGdaXvH.js} +0 -0
  234. /package/dist/chunks/{config-CtomKf-k.js → config-D4s_QWVP.js} +0 -0
  235. /package/dist/chunks/{config-D32znLtV2.js → config-D6Oe3blW2.js} +0 -0
  236. /package/dist/chunks/{config-CVnluhKO.js → config-DF_RlCIO.js} +0 -0
  237. /package/dist/chunks/{config-C4qAA0Sb.js → config-DSBlHQVa.js} +0 -0
  238. /package/dist/chunks/{config-Mp9DJ9Mq2.js → config-DaHI5bD_2.js} +0 -0
  239. /package/dist/chunks/{config-3SftO0uG.js → config-Dcb7xK-C.js} +0 -0
  240. /package/dist/chunks/{config-DDEWvph-2.js → config-DmjwIqx1.js} +0 -0
  241. /package/dist/chunks/{config-D6Af6WZw.js → config-DoBk_CzG.js} +0 -0
  242. /package/dist/chunks/{config-DDLoUczC.js → config-Dzd6BzIN2.js} +0 -0
  243. /package/dist/chunks/{config-Db4BEG8Y.js → config-O1_FTqD3.js} +0 -0
  244. /package/dist/chunks/{config-rxFL-6yF2.js → config-_3De4HFf.js} +0 -0
  245. /package/dist/chunks/{config-DudL02ag.js → config-iuMz79Mg.js} +0 -0
  246. /package/dist/chunks/{config-DK2NpdyX2.js → config-vcDVCYXo.js} +0 -0
  247. /package/dist/chunks/{en-QNZhjmSJ.js → en-foQA8imU.js} +0 -0
  248. /package/dist/chunks/{es-C1c5XsIK.js → es-DZFSojYL.js} +0 -0
  249. /package/dist/chunks/{fileValidation-CwJA8Vmn.js → fileValidation-c_iBg74s.js} +0 -0
  250. /package/dist/chunks/{grDropdownStyles-DMp7RrI9.js → grDropdownStyles-B41mVR1H.js} +0 -0
  251. /package/dist/chunks/{grLoadingStyles-CrxZHFL0.js → grLoadingStyles-DmI-SJ3i.js} +0 -0
  252. /package/dist/chunks/{grModalStyles-Dv2NXBKi.js → grModalStyles-DS1Vl6EW.js} +0 -0
  253. /package/dist/chunks/{grSelectStyles-DTAYNzxn.js → grSelectStyles-pUt19VGy.js} +0 -0
  254. /package/dist/chunks/{grStyle-5EMYYctb.js → grStyle-CUaTmpyW.js} +0 -0
  255. /package/dist/chunks/{ru-DcF7ciz0.js → ru-LhkPpPIl.js} +0 -0
  256. /package/dist/chunks/{safelist-BF822K15.js → safelist-_UVzJrgE2.js} +0 -0
  257. /package/dist/components/GrEmptyState/chunks/{GrEmptyState-BLbMJFW-.js → GrEmptyState-Dj2V5GaX.js} +0 -0
  258. /package/dist/components/GrFormField/chunks/{GrFormField-C1oWTT_0.js → GrFormField-U7c_aDPJ.js} +0 -0
  259. /package/dist/components/GrFormSection/chunks/{GrFormSection-CWBk2EUm.js → GrFormSection-DpGhY7MX.js} +0 -0
  260. /package/dist/components/GrImageViewer/chunks/{GrImageViewer-Cpj5zyZQ.js → GrImageViewer-Cn1X-sWS.js} +0 -0
  261. /package/dist/components/GrInput/chunks/{GrInput-CfO2RsM-.js → GrInput-gwFQ1sk4.js} +0 -0
  262. /package/dist/components/GrList/chunks/{GrList-C2GpK56-.js → GrList-D8nM1FuV.js} +0 -0
  263. /package/dist/components/GrSidebar/chunks/{GrSidebar-DVucgcfV.js → GrSidebar-BpBtAGBs.js} +0 -0
  264. /package/dist/components/GrSkeleton/chunks/{GrSkeleton-CDTvCSoH.js → GrSkeleton-URyMkBqI.js} +0 -0
  265. /package/dist/components/GrTable/chunks/{GrTable-CyQ9uw8A.js → GrTable-dTWUxzRm.js} +0 -0
  266. /package/dist/components/GrTabs/chunks/{GrTabs-CiGmcmvm.js → GrTabs-D3ntDdtg.js} +0 -0
  267. /package/dist/components/GrTooltip/chunks/{GrTooltip-CrV1gI_c.js → GrTooltip-B7I1AVIC.js} +0 -0
  268. /package/dist/components/GrTree/chunks/{GrTree-mYa3COxG.js → GrTree-B2SMU5Py.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GrResponseErrorBanner-CzdTh4gd.js","names":[],"sources":["../../../../src/components/GrResponseErrorBanner/responseError.defaults.ts","../../../../src/components/GrResponseErrorBanner/parsers/abortErrorParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/fileValidationParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/httpStatusParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/jsonApiErrorParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/laravelValidationParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/networkErrorParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/normalizeError.ts","../../../../src/components/GrResponseErrorBanner/parsers/plainMessageParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/problemDetailsParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/createResponseErrorClassifier.ts","../../../../src/components/GrResponseErrorBanner/GrResponseErrorBanner.vue","../../../../src/components/GrResponseErrorBanner/GrResponseErrorBanner.vue"],"sourcesContent":["import type { ResponseErrorKind, ResponseErrorTexts, ResponseErrorTone } from './responseError.types'\n\n/**\n * Default English texts. Any field can be overridden via the `texts` prop\n * on `GrResponseErrorBanner` or the `texts` option of `useResponseError`.\n */\nexport const DEFAULT_RESPONSE_ERROR_TEXTS: ResponseErrorTexts = {\n networkTitle: 'No connection to the server',\n networkMessage: 'Check your internet connection and try again.',\n abortedTitle: 'Request cancelled',\n abortedMessage: 'The request was cancelled.',\n validationTitle: 'Check the data',\n validationMessage: 'The server returned a validation error. Fix the fields and try again.',\n clientTitle: 'Request error',\n clientMessage: 'The server rejected the request.',\n serverTitle: 'Server error',\n serverMessage: 'A server error occurred. Please try again.',\n unknownTitle: 'Something went wrong',\n unknownMessage: 'The operation could not be completed. Please try again.',\n retryLabel: 'Retry',\n dismissLabel: 'Dismiss',\n}\n\n/**\n * Маппинг `kind` → `tone` по умолчанию. Применяется внутри баннера, если\n * пользователь не передал `tone` или `toneByKind`. Цель — визуально отделить\n * «исправь ввод» (warning) от «упало по сети/серверу» (danger).\n */\nexport const DEFAULT_TONE_BY_KIND: Record<ResponseErrorKind, ResponseErrorTone> = {\n network: 'danger',\n aborted: 'info',\n validation: 'warning',\n client: 'warning',\n server: 'danger',\n unknown: 'danger',\n}\n\n/**\n * `kind`'ы, которые по умолчанию не показываются (баннер скрыт).\n * Семантика: отмена пользователем — не «ошибка», UI обычно тихо проглатывает.\n * Пользователь может переопределить через `autoHideKinds: []`, чтобы показать info-плашку.\n */\nexport const DEFAULT_AUTO_HIDE_KINDS: ResponseErrorKind[] = []\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Распознаёт отмену запроса клиентом.\n *\n * Триггеры:\n * - `ctx.isAbort === true` (выставляется `normalizeError` для `AbortError`,\n * axios `isCancel`/`ERR_CANCELED`, xhr `status === 0 && readyState === 4`).\n *\n * Возвращает `kind: 'aborted'` и `stop: true` — другие парсеры пропускаются,\n * чтобы случайный `status === 0` не интерпретировался как сетевая ошибка.\n *\n * По умолчанию `aborted` показывается info-плашкой; чтобы скрыть совсем —\n * добавьте `'aborted'` в `autoHideKinds`.\n */\nexport const abortErrorParser: ResponseErrorParser = (ctx) => {\n if (!ctx.isAbort) return null\n return {\n kind: 'aborted',\n message: ctx.texts.abortedMessage,\n stop: true,\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Распознаёт `FileValidationError` от `GrFileUpload` (или совместимый по форме\n * объект). Не зависит от жёсткого импорта класса — определяет по `name`.\n *\n * Активируется, если `ctx.raw.name === 'FileValidationError'` и есть массив\n * `issues`/`errors` с полями `{ file?, code?, message }`.\n *\n * Возвращает `kind: 'validation'` и `stop: true` — это локальная клиентская\n * валидация, незачем дальше идти по HTTP-парсерам.\n */\nexport const fileValidationParser: ResponseErrorParser = (ctx) => {\n const raw = ctx.raw as\n | undefined\n | {\n name?: string\n message?: string\n issues?: Array<{ file?: { name?: string }, message?: unknown, code?: string }>\n errors?: Array<{ file?: { name?: string }, message?: unknown, code?: string }>\n }\n if (!raw || typeof raw !== 'object') return null\n if (raw.name !== 'FileValidationError') return null\n\n const list = raw.issues ?? raw.errors ?? []\n const details: string[] = []\n for (const it of list) {\n if (!it || typeof it !== 'object') continue\n const msg = typeof it.message === 'string' ? it.message : ''\n if (!msg) continue\n const fileName = it.file?.name\n details.push(fileName ? `${fileName}: ${msg}` : msg)\n }\n\n return {\n kind: 'validation',\n message: raw.message || ctx.texts.validationMessage,\n details: details.length ? details : undefined,\n stop: true,\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Базовая классификация по HTTP-статусу.\n *\n * Правила:\n * - `400` / `422` ⇒ `kind: 'validation'`,\n * - остальные `4xx` ⇒ `kind: 'client'`,\n * - `>= 500` ⇒ `kind: 'server'`.\n *\n * Не использует `stop` — даёт более специализированным парсерам\n * (Laravel/JSON:API/RFC 7807) уточнить сообщение и `fieldErrors`.\n *\n * `message` берёт из `ctx.texts` как осмысленный фолбэк.\n */\nexport const httpStatusParser: ResponseErrorParser = (ctx) => {\n const status = ctx.status\n if (typeof status !== 'number') return null\n\n if (status >= 500) {\n return { kind: 'server', status, message: ctx.texts.serverMessage }\n }\n if (status === 400 || status === 422) {\n return { kind: 'validation', status, message: ctx.texts.validationMessage }\n }\n if (status >= 400) {\n return { kind: 'client', status, message: ctx.texts.clientMessage }\n }\n return null\n}\n","import type { ResponseErrorFieldError, ResponseErrorParser } from '../responseError.types'\n\n/**\n * Парсер JSON:API-стиля:\n *\n * ```json\n * { \"errors\": [\n * { \"status\": \"422\", \"title\": \"Invalid Attribute\",\n * \"detail\": \"Email is required.\",\n * \"source\": { \"pointer\": \"/data/attributes/email\" } }\n * ] }\n * ```\n *\n * Активируется, когда `ctx.body.errors` — массив объектов с `title`/`detail`.\n * Поле извлекается из последнего сегмента `source.pointer`. Все `detail`\n * собираются в `details`, первый `title` — в `message`.\n */\nexport const jsonApiErrorParser: ResponseErrorParser = (ctx) => {\n const body = ctx.body\n if (!body || typeof body !== 'object') return null\n\n const errors = (body as { errors?: unknown }).errors\n if (!Array.isArray(errors) || errors.length === 0) return null\n\n // Эвристика: первый элемент должен иметь title/detail/source — иначе это не JSON:API\n const first = errors[0]\n if (!first || typeof first !== 'object') return null\n const has = (key: string) => key in (first as object)\n if (!has('title') && !has('detail') && !has('source')) return null\n\n const fieldErrorsMap = new Map<string, string[]>()\n const details: string[] = []\n let message: string | undefined\n\n for (const e of errors) {\n if (!e || typeof e !== 'object') continue\n const eo = e as { title?: unknown, detail?: unknown, source?: { pointer?: unknown } }\n const text = (typeof eo.detail === 'string' && eo.detail) || (typeof eo.title === 'string' && eo.title) || ''\n if (!text) continue\n if (!message && typeof eo.title === 'string') message = eo.title\n\n const pointer = typeof eo.source?.pointer === 'string' ? eo.source.pointer : ''\n const field = pointer ? pointer.split('/').filter(Boolean).pop() : undefined\n\n if (field) {\n const list = fieldErrorsMap.get(field) ?? []\n list.push(text)\n fieldErrorsMap.set(field, list)\n }\n details.push(text)\n }\n\n if (!details.length) return null\n\n const fieldErrors: ResponseErrorFieldError[] = Array.from(fieldErrorsMap, ([field, messages]) => ({ field, messages }))\n\n const kind = fieldErrors.length ? 'validation' : undefined\n\n return {\n kind,\n message: message || (kind === 'validation' ? ctx.texts.validationMessage : ctx.texts.clientMessage),\n details,\n fieldErrors: fieldErrors.length ? fieldErrors : undefined,\n }\n}\n","import type { ResponseErrorFieldError, ResponseErrorParser } from '../responseError.types'\n\n/**\n * Парсит Laravel-формат ответа об ошибке валидации:\n *\n * ```json\n * { \"message\": \"The given data was invalid.\",\n * \"errors\": { \"file\": [\"The file is required.\"], \"amount\": [\"...\"] } }\n * ```\n *\n * Активируется при наличии `ctx.body.errors` как plain-объекта (значения —\n * массив строк или строка). Если есть `fieldErrors`, повышает `kind` до\n * `validation` независимо от статуса (полезно, когда сервер прислал 200/4xx\n * без 422). `message` берётся из `body.message` / `body.error`.\n */\nexport const laravelValidationParser: ResponseErrorParser = (ctx) => {\n const body = ctx.body\n if (!body || typeof body !== 'object' || Array.isArray(body)) return null\n\n const data = body as Record<string, unknown>\n const errors = data.errors\n if (!errors || typeof errors !== 'object' || Array.isArray(errors)) return null\n\n const fieldErrors: ResponseErrorFieldError[] = []\n for (const [field, raw] of Object.entries(errors as Record<string, unknown>)) {\n const messages = Array.isArray(raw)\n ? raw.filter((m): m is string => typeof m === 'string')\n : typeof raw === 'string' ? [raw] : []\n if (messages.length) fieldErrors.push({ field, messages })\n }\n\n if (!fieldErrors.length) return null\n\n const message = (typeof data.message === 'string' && data.message)\n || (typeof data.error === 'string' && data.error)\n || ctx.texts.validationMessage\n\n const details = fieldErrors.flatMap(fe => fe.messages)\n\n return {\n kind: 'validation',\n message,\n details,\n fieldErrors,\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Распознаёт транспортные ошибки **без ответа сервера**: отсутствие интернета,\n * DNS-фейл, CORS-preflight, fetch `TypeError('Failed to fetch')`,\n * axios `ERR_NETWORK`/`ECONNABORTED`.\n *\n * Триггеры:\n * - `ctx.isNetwork === true` (выставлено `normalizeError`),\n * - либо нет ни `status`, ни `body`, ни признака отмены, но есть исходная\n * ошибка с `code === 'ERR_NETWORK'` / `message ~ /network|fetch/i/`.\n *\n * Возвращает `kind: 'network'` и `stop: true`.\n */\nexport const networkErrorParser: ResponseErrorParser = (ctx) => {\n if (ctx.isAbort) return null\n\n if (ctx.isNetwork) {\n return {\n kind: 'network',\n message: ctx.texts.networkMessage,\n stop: true,\n }\n }\n\n // Эвристика для случаев, когда normalizeError не пометил, но статус-кода нет.\n if (typeof ctx.status !== 'number' && ctx.body == null && ctx.raw && typeof ctx.raw === 'object') {\n const r = ctx.raw as { code?: string, message?: string, name?: string }\n if (r.code === 'ERR_NETWORK' || (typeof r.message === 'string' && /network|failed to fetch/i.test(r.message))) {\n return {\n kind: 'network',\n message: ctx.texts.networkMessage,\n stop: true,\n }\n }\n }\n\n return null\n}\n","import type { NormalizedRawError } from '../responseError.types'\n\n/**\n * Универсально приводит «сырой» вход к {@link NormalizedRawError}.\n *\n * Поддерживаемые источники:\n * - **axios** — `AxiosError` с `response.status`/`response.data`/`response.headers`,\n * либо `request` без `response` + `code === 'ERR_NETWORK'`, либо `code === 'ERR_CANCELED'`.\n * - **fetch `Response`** — объект `Response` с `ok=false`. Тело читается через\n * `clone().json()` (с фолбэком на `.text()`); поэтому функция асинхронная.\n * - **fetch `TypeError`** — `TypeError('Failed to fetch')` ⇒ `isNetwork=true`.\n * - **`{ response: Response, body: unknown }`** — если потребитель уже прочитал тело сам.\n * - **`XMLHttpRequest`** / `{ status, responseText, response }` — пара полей.\n * - **`AbortError` / `DOMException`** ⇒ `isAbort=true`.\n * - **`Error`/строка/произвольный объект** — кладётся в `body`/`raw` как есть.\n *\n * Цель — отделить «откуда пришла ошибка» от «как её классифицировать»: парсеры\n * работают только с нормализованным `ResponseErrorContext`, не зная о транспорте.\n */\nexport async function normalizeError(raw: unknown): Promise<NormalizedRawError> {\n // 1) Уже нормализованный вход (потребитель сам подготовил)\n if (isPlainNormalized(raw)) {\n return { ...(raw), raw }\n }\n\n // 2) `{ response: Response, body }` — уже прочитанное тело\n if (isResponseEnvelope(raw)) {\n const res = raw.response\n return {\n raw,\n status: res.status,\n body: raw.body,\n headers: readHeaders(res),\n }\n }\n\n // 3) Голый fetch Response\n if (isFetchResponse(raw)) {\n const body = await readFetchBody(raw)\n return {\n raw,\n status: raw.status,\n body,\n headers: readHeaders(raw),\n }\n }\n\n // 4) XMLHttpRequest\n if (isXhr(raw)) {\n const body = parseMaybeJson(raw.responseText) ?? raw.response\n return {\n raw,\n status: typeof raw.status === 'number' ? raw.status : undefined,\n body,\n isAbort: raw.status === 0 && raw.readyState === 4,\n }\n }\n\n // 5) axios error (есть `isAxiosError` или `response`/`request` + `code`)\n if (isAxiosLike(raw)) {\n const e = raw\n const status = e.response?.status\n const code = e.code\n const isAbort = code === 'ERR_CANCELED' || code === 'CANCELED' || (e as { __CANCEL__?: boolean }).__CANCEL__ === true\n const isNetwork = !e.response && (code === 'ERR_NETWORK' || code === 'ECONNABORTED')\n\n return {\n raw,\n status,\n body: e.response?.data,\n headers: normalizeHeaderRecord(e.response?.headers),\n isAbort,\n isNetwork,\n }\n }\n\n // 6) AbortError / DOMException\n if (isAbortError(raw)) {\n return { raw, isAbort: true }\n }\n\n // 7) Сетевой TypeError от fetch\n if (raw instanceof TypeError && /fetch|network/i.test(raw.message || '')) {\n return { raw, isNetwork: true }\n }\n\n // 8) Произвольный Error / строка / объект\n if (raw instanceof Error) {\n return { raw, body: { message: raw.message } }\n }\n\n if (typeof raw === 'string') {\n return { raw, body: { message: raw } }\n }\n\n if (raw && typeof raw === 'object') {\n return { raw, body: raw }\n }\n\n return { raw }\n}\n\n// ----------------- helpers -----------------\n\ntype AxiosLikeError = Error & {\n isAxiosError?: boolean\n code?: string\n response?: { status?: number, data?: unknown, headers?: unknown }\n request?: unknown\n}\n\nfunction isPlainNormalized(v: unknown): v is NormalizedRawError {\n if (!v || typeof v !== 'object') return false\n const o = v as Record<string, unknown>\n return 'raw' in o && (\n 'status' in o || 'body' in o || 'isAbort' in o || 'isNetwork' in o || 'headers' in o\n )\n}\n\nfunction isResponseEnvelope(v: unknown): v is { response: Response, body: unknown } {\n return !!v && typeof v === 'object'\n && 'response' in (v)\n && isFetchResponse((v).response)\n}\n\nfunction isFetchResponse(v: unknown): v is Response {\n return typeof Response !== 'undefined' && v instanceof Response\n}\n\nfunction isXhr(v: unknown): v is XMLHttpRequest {\n return typeof XMLHttpRequest !== 'undefined' && v instanceof XMLHttpRequest\n}\n\nfunction isAxiosLike(v: unknown): v is AxiosLikeError {\n if (!v || typeof v !== 'object') return false\n const o = v as Record<string, unknown>\n return o.isAxiosError === true\n || ('response' in o && typeof o.response === 'object' && o.response !== null)\n || ('code' in o && typeof o.code === 'string')\n}\n\nfunction isAbortError(v: unknown): boolean {\n if (!v || typeof v !== 'object') return false\n const o = v as { name?: string, code?: string }\n return o.name === 'AbortError' || o.name === 'CanceledError' || o.code === 'ERR_CANCELED'\n}\n\nasync function readFetchBody(res: Response): Promise<unknown> {\n if (res.bodyUsed) return undefined\n try {\n const clone = res.clone()\n const text = await clone.text()\n return parseMaybeJson(text) ?? text\n }\n catch {\n return undefined\n }\n}\n\nfunction readHeaders(res: Response): Record<string, string> {\n const out: Record<string, string> = {}\n res.headers.forEach((value, key) => {\n out[key.toLowerCase()] = value\n })\n return out\n}\n\nfunction normalizeHeaderRecord(h: unknown): Record<string, string> | undefined {\n if (!h || typeof h !== 'object') return undefined\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(h as Record<string, unknown>)) {\n if (typeof v === 'string') out[k.toLowerCase()] = v\n }\n return out\n}\n\nfunction parseMaybeJson(text: string | undefined): unknown {\n if (typeof text !== 'string' || !text) return undefined\n try {\n return JSON.parse(text)\n }\n catch {\n return undefined\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Финальный фолбэк-парсер. Достаёт хоть какое-нибудь человекочитаемое\n * сообщение, чтобы баннер не был пустым.\n *\n * Источники (по приоритету):\n * 1. `ctx.body` — если строка ⇒ message,\n * 2. `ctx.body[messageKey]` (default `'message'`),\n * 3. `ctx.body.error` (строка),\n * 4. `ctx.raw.message` (для голых `Error`).\n *\n * Не выставляет `kind` — оставляет на усмотрение классификатора.\n * Использует ключ `messageKey` из `ctx.meta._messageKey`, если задан.\n */\nexport const plainMessageParser: ResponseErrorParser = (ctx) => {\n const messageKey = (ctx.meta._messageKey as string | undefined) || 'message'\n\n if (typeof ctx.body === 'string' && ctx.body.trim()) {\n return { message: ctx.body }\n }\n\n if (ctx.body && typeof ctx.body === 'object') {\n const data = ctx.body as Record<string, unknown>\n const fromKey = data[messageKey]\n if (typeof fromKey === 'string' && fromKey) return { message: fromKey }\n if (typeof data.error === 'string' && data.error) return { message: data.error }\n }\n\n if (ctx.raw instanceof Error && ctx.raw.message) {\n return { message: ctx.raw.message }\n }\n\n return null\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Парсер RFC 7807 «Problem Details for HTTP APIs»:\n *\n * ```json\n * { \"type\": \"https://example.com/probs/out-of-credit\",\n * \"title\": \"You do not have enough credit.\",\n * \"status\": 403,\n * \"detail\": \"Your current balance is 30.\",\n * \"errors\": { \"balance\": [\"...\"] } }\n * ```\n *\n * Активируется, если есть `body.type` + `body.title`, либо\n * Content-Type заголовка `application/problem+json`. Берёт `title` как\n * `message`, `detail` — в `details[0]`, status — в `status`.\n */\nexport const problemDetailsParser: ResponseErrorParser = (ctx) => {\n const body = ctx.body\n if (!body || typeof body !== 'object' || Array.isArray(body)) return null\n\n const data = body as { type?: unknown, title?: unknown, detail?: unknown, status?: unknown }\n const isProblemContentType = ctx.headers?.['content-type']?.includes('application/problem+json')\n const hasShape = typeof data.title === 'string' && (typeof data.type === 'string' || typeof data.status === 'number')\n\n if (!isProblemContentType && !hasShape) return null\n\n const message = typeof data.title === 'string' ? data.title : ctx.texts.unknownMessage\n const details: string[] = []\n if (typeof data.detail === 'string' && data.detail) details.push(data.detail)\n\n const status = typeof data.status === 'number' ? data.status : ctx.status\n\n return {\n message,\n details: details.length ? details : undefined,\n status,\n }\n}\n","import { DEFAULT_RESPONSE_ERROR_TEXTS } from '../responseError.defaults'\nimport type {\n ResponseErrorContext,\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorParser,\n ResponseErrorTexts,\n} from '../responseError.types'\n\nimport { abortErrorParser } from './abortErrorParser'\nimport { fileValidationParser } from './fileValidationParser'\nimport { httpStatusParser } from './httpStatusParser'\nimport { jsonApiErrorParser } from './jsonApiErrorParser'\nimport { laravelValidationParser } from './laravelValidationParser'\nimport { networkErrorParser } from './networkErrorParser'\nimport { normalizeError } from './normalizeError'\nimport { plainMessageParser } from './plainMessageParser'\nimport { problemDetailsParser } from './problemDetailsParser'\n\n/**\n * Дефолтная цепочка парсеров. Порядок важен:\n * `abort → network → fileValidation → laravel → problemDetails → jsonApi → httpStatus → plainMessage`.\n *\n * - Ранние специализированные парсеры могут поставить `stop` (abort/file).\n * - HTTP-парсер даёт fallback-kind/message, чтобы остальное могло уточнить.\n * - `plainMessage` всегда последний — добывает хоть какое-то сообщение.\n */\nexport const defaultResponseErrorParsers: ResponseErrorParser[] = [\n abortErrorParser,\n networkErrorParser,\n fileValidationParser,\n laravelValidationParser,\n problemDetailsParser,\n jsonApiErrorParser,\n httpStatusParser,\n plainMessageParser,\n]\n\n/**\n * Универсальное «ядро» парсеров — транспортно-независимый набор, безопасный\n * для любого бэкенда (Laravel / Symfony / Go / …). Не делает формат-специфичных\n * допущений: разбирает только отмену, сетевые ошибки, HTTP-статус и «плоское»\n * сообщение из тела. Используется как дефолт в `useDialogService`.\n *\n * Порядок: `abort → network → httpStatus → plainMessage`.\n */\nexport const coreResponseErrorParsers: ResponseErrorParser[] = [\n abortErrorParser,\n networkErrorParser,\n httpStatusParser,\n plainMessageParser,\n]\n\n/**\n * Именованные пресеты-строительные блоки для удобной композиции цепочки\n * парсеров без ручного импорта каждого из них.\n *\n * - `core` — универсальное ядро (см. `coreResponseErrorParsers`).\n * - `all` — полная дефолтная цепочка (`defaultResponseErrorParsers`).\n * - остальные — серверо-специфичные парсеры, подключаемые осознанно.\n */\nexport type ResponseErrorParserPresets = {\n core: ResponseErrorParser[]\n all: ResponseErrorParser[]\n laravel: ResponseErrorParser\n problemDetails: ResponseErrorParser\n jsonApi: ResponseErrorParser\n fileValidation: ResponseErrorParser\n}\n\n/** Объект пресетов, передаётся builder-функции `errorParsers`. */\nexport const responseErrorParserPresets: ResponseErrorParserPresets = {\n core: coreResponseErrorParsers,\n all: defaultResponseErrorParsers,\n laravel: laravelValidationParser,\n problemDetails: problemDetailsParser,\n jsonApi: jsonApiErrorParser,\n fileValidation: fileValidationParser,\n}\n\n/** Добавляет пользовательские парсеры в начало дефолтной цепочки. */\nexport function extendDefaultParsers(extra: ResponseErrorParser[]): ResponseErrorParser[] {\n return [...extra, ...defaultResponseErrorParsers]\n}\n\nexport type CreateResponseErrorClassifierOptions = {\n parsers?: ResponseErrorParser[]\n texts?: Partial<ResponseErrorTexts>\n /** Имя поля основного сообщения в body (для `plainMessageParser`). */\n messageKey?: string\n}\n\n/**\n * Фабрика классификатора. Возвращает функцию `(raw, meta?) => Promise<ResponseErrorInfo>`,\n * которая:\n * 1. нормализует вход через `normalizeError`,\n * 2. строит `ResponseErrorContext`,\n * 3. прогоняет цепочку парсеров (с поддержкой `stop`),\n * 4. собирает финальный `ResponseErrorInfo` с фолбэками из `texts`.\n */\nexport function createResponseErrorClassifier(options: CreateResponseErrorClassifierOptions = {}) {\n const parsers = options.parsers ?? defaultResponseErrorParsers\n const texts: ResponseErrorTexts = { ...DEFAULT_RESPONSE_ERROR_TEXTS, ...(options.texts ?? {}) }\n const messageKey = options.messageKey ?? 'message'\n\n return async function classify(raw: unknown, meta: Record<string, unknown> = {}): Promise<ResponseErrorInfo> {\n const normalized = await normalizeError(raw)\n\n const ctx: ResponseErrorContext = {\n ...normalized,\n meta: { ...meta, _messageKey: messageKey },\n texts,\n }\n\n let kind: ResponseErrorKind | undefined\n let status: number | undefined = ctx.status\n let message: string | undefined\n let details: string[] | undefined\n let fieldErrors: ResponseErrorInfo['fieldErrors']\n\n for (const parser of parsers) {\n const result = parser(ctx)\n if (!result) continue\n if (result.kind) kind = result.kind\n if (typeof result.status === 'number') status = result.status\n if (result.message) message = result.message\n if (result.details?.length) details = result.details\n if (result.fieldErrors?.length) fieldErrors = result.fieldErrors\n if (result.stop) break\n }\n\n if (!kind) kind = 'unknown'\n\n if (!message) message = resolveDefaultMessage(kind, texts)\n\n return {\n kind,\n status,\n message,\n details,\n fieldErrors,\n raw,\n meta,\n }\n }\n}\n\nfunction resolveDefaultMessage(kind: ResponseErrorKind, texts: ResponseErrorTexts): string {\n switch (kind) {\n case 'network': return texts.networkMessage\n case 'aborted': return texts.abortedMessage\n case 'validation': return texts.validationMessage\n case 'client': return texts.clientMessage\n case 'server': return texts.serverMessage\n case 'unknown':\n default: return texts.unknownMessage\n }\n}\n\nexport function resolveResponseErrorTitle(kind: ResponseErrorKind, texts: ResponseErrorTexts): string {\n switch (kind) {\n case 'network': return texts.networkTitle\n case 'aborted': return texts.abortedTitle\n case 'validation': return texts.validationTitle\n case 'client': return texts.clientTitle\n case 'server': return texts.serverTitle\n case 'unknown':\n default: return texts.unknownTitle\n }\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrAlert from '../GrAlert/GrAlert.vue'\nimport GrButton from '../GrButton/GrButton.vue'\n\nimport { resolveResponseErrorTitle } from './parsers/createResponseErrorClassifier'\nimport {\n DEFAULT_AUTO_HIDE_KINDS,\n DEFAULT_RESPONSE_ERROR_TEXTS,\n DEFAULT_TONE_BY_KIND,\n} from './responseError.defaults'\nimport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nexport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nconst props = withDefaults(defineProps<{\n /** Готовая структура ошибки. Если `null` — баннер не рендерится. */\n error: ResponseErrorInfo | null\n /** Частичный override текстов. Мерджится с `DEFAULT_RESPONSE_ERROR_TEXTS`. */\n texts?: Partial<ResponseErrorTexts>\n /** Жёсткий оверрайд тона. Бьёт `toneByKind`. */\n tone?: ResponseErrorTone\n /** Маппинг `kind` → `tone`. Мерджится с `DEFAULT_TONE_BY_KIND`. */\n toneByKind?: Partial<Record<ResponseErrorKind, ResponseErrorTone>>\n /** Показывать ли блок `details`/`fieldErrors` под основным сообщением. */\n showDetails?: boolean\n /** Префиксить ли `field:` перед сообщениями по полям. */\n showFieldLabels?: boolean\n /** Человекочитаемые подписи полей (`{ file: 'Файл' }`). */\n fieldLabels?: Record<string, string>\n /** Удалять дубликаты сообщений между `message` и `details`. */\n dedupeDetails?: boolean\n /** Показывать ли кнопку «Повторить». */\n canRetry?: boolean\n /** Показывать ли кнопку «Скрыть». */\n canDismiss?: boolean\n /** Какие `kind` не рендерить вообще (баннер скрывается). */\n autoHideKinds?: ResponseErrorKind[]\n /** Показывать ли HTTP-status бейдж. */\n showStatus?: boolean\n /** Префикс `data-testid`. */\n testIdPrefix?: string\n}>(), {\n texts: () => ({}),\n tone: undefined,\n toneByKind: () => ({}),\n showDetails: true,\n showFieldLabels: true,\n fieldLabels: () => ({}),\n dedupeDetails: true,\n canRetry: false,\n canDismiss: true,\n autoHideKinds: () => DEFAULT_AUTO_HIDE_KINDS,\n showStatus: true,\n testIdPrefix: 'response-error',\n})\n\nconst emit = defineEmits<{\n (e: 'retry', error: ResponseErrorInfo): void\n (e: 'dismiss'): void\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst i18nTexts = computed<ResponseErrorTexts>(() => {\n const out = {} as ResponseErrorTexts\n for (const key of Object.keys(DEFAULT_RESPONSE_ERROR_TEXTS) as (keyof ResponseErrorTexts)[]) {\n out[key] = t(`gr.responseError.${key}`, DEFAULT_RESPONSE_ERROR_TEXTS[key])\n }\n return out\n})\n\nconst mergedTexts = computed<ResponseErrorTexts>(() => ({\n ...DEFAULT_RESPONSE_ERROR_TEXTS,\n ...i18nTexts.value,\n ...props.texts,\n}))\n\nconst mergedToneByKind = computed<Record<ResponseErrorKind, ResponseErrorTone>>(() => ({\n ...DEFAULT_TONE_BY_KIND,\n ...props.toneByKind,\n}))\n\nconst isHidden = computed(() => {\n if (!props.error) return true\n return props.autoHideKinds.includes(props.error.kind)\n})\n\nconst tone = computed<ResponseErrorTone>(() => {\n if (props.tone) return props.tone\n if (!props.error) return 'danger'\n return mergedToneByKind.value[props.error.kind] ?? 'danger'\n})\n\nconst title = computed(() => {\n if (!props.error) return ''\n return resolveResponseErrorTitle(props.error.kind, mergedTexts.value)\n})\n\n/**\n * Маппинг `kind` → ключ поля сообщения в `ResponseErrorTexts`.\n * Используется, чтобы подменить «дефолтное английское» fallback-сообщение\n * классификатора на i18n-перевод (или текущий `mergedTexts.<kind>Message`).\n */\nconst KIND_MESSAGE_KEY: Record<ResponseErrorKind, keyof ResponseErrorTexts> = {\n network: 'networkMessage',\n aborted: 'abortedMessage',\n validation: 'validationMessage',\n client: 'clientMessage',\n server: 'serverMessage',\n unknown: 'unknownMessage',\n}\n\nconst DEFAULT_FALLBACK_MESSAGES = new Set<string>([\n DEFAULT_RESPONSE_ERROR_TEXTS.networkMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.abortedMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.validationMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.clientMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.serverMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.unknownMessage,\n])\n\nconst message = computed(() => {\n const raw = props.error?.message ?? ''\n if (!props.error) return raw\n // Если в `ResponseErrorInfo.message` сидит англ. дефолт-фолбэк классификатора,\n // подменяем на i18n-перевод соответствующего kind.\n if (!raw || DEFAULT_FALLBACK_MESSAGES.has(raw)) {\n return mergedTexts.value[KIND_MESSAGE_KEY[props.error.kind]]\n }\n return raw\n})\n\n/**\n * Сводный список «деталей» под основным `message`.\n * Предпочитаем `fieldErrors` (поле → сообщения), затем — плоский `details`.\n * Дедуплицируем и убираем повтор с `message`.\n */\nconst detailEntries = computed<{ field?: string, text: string }[]>(() => {\n if (!props.error || !props.showDetails) return []\n\n const seen = new Set<string>()\n if (props.dedupeDetails) {\n const main = message.value.trim()\n if (main) seen.add(main)\n }\n\n const result: { field?: string, text: string }[] = []\n const fieldErrors = props.error.fieldErrors\n if (fieldErrors?.length) {\n for (const fe of fieldErrors) {\n for (const m of fe.messages) {\n const text = m.trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ field: fe.field, text })\n }\n }\n if (result.length) return result\n }\n\n const details = props.error.details\n if (details?.length) {\n for (const d of details) {\n const text = (d ?? '').trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ text })\n }\n }\n return result\n})\n\nfunction fieldLabel(field?: string): string | undefined {\n if (!field) return undefined\n return props.fieldLabels[field] ?? field\n}\n\nfunction onRetry(): void {\n if (props.error) emit('retry', props.error)\n}\n\nfunction onDismiss(): void {\n emit('dismiss')\n}\n</script>\n\n<template>\n <GrAlert\n v-if=\"!isHidden && props.error\"\n :tone=\"tone\"\n :title=\"title\"\n :closable=\"props.canDismiss\"\n :data-testid=\"`${props.testIdPrefix}-banner`\"\n :data-kind=\"props.error.kind\"\n @close=\"onDismiss\"\n >\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n v-if=\"props.showStatus && typeof props.error.status === 'number'\"\n class=\"inline-flex items-center rounded-[var(--ds-radius-sm)] bg-[color-mix(in_srgb,currentColor_12%,transparent)] px-1.5 py-0.5 text-[11px] font-700 leading-none\"\n :data-testid=\"`${props.testIdPrefix}-status`\"\n >\n HTTP {{ props.error.status }}\n </span>\n <span\n class=\"text-[13px]\"\n :data-testid=\"`${props.testIdPrefix}-message`\"\n >\n {{ message }}\n </span>\n </div>\n\n <ul\n v-if=\"detailEntries.length\"\n class=\"m-0 list-disc pl-5 text-[12px]\"\n :data-testid=\"`${props.testIdPrefix}-details`\"\n >\n <li\n v-for=\"(entry, idx) in detailEntries\"\n :key=\"`${entry.field ?? ''}:${idx}`\"\n >\n <template v-if=\"props.showFieldLabels && entry.field\">\n <strong>{{ fieldLabel(entry.field) }}:</strong>&nbsp;\n </template>{{ entry.text }}\n </li>\n </ul>\n\n <div\n v-if=\"props.canRetry\"\n class=\"mt-1 flex flex-wrap items-center gap-2\"\n >\n <GrButton\n size=\"sm\"\n variant=\"outline\"\n :data-testid=\"`${props.testIdPrefix}-retry`\"\n @click=\"onRetry\"\n >\n {{ mergedTexts.retryLabel }}\n </GrButton>\n </div>\n </div>\n </GrAlert>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrAlert from '../GrAlert/GrAlert.vue'\nimport GrButton from '../GrButton/GrButton.vue'\n\nimport { resolveResponseErrorTitle } from './parsers/createResponseErrorClassifier'\nimport {\n DEFAULT_AUTO_HIDE_KINDS,\n DEFAULT_RESPONSE_ERROR_TEXTS,\n DEFAULT_TONE_BY_KIND,\n} from './responseError.defaults'\nimport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nexport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nconst props = withDefaults(defineProps<{\n /** Готовая структура ошибки. Если `null` — баннер не рендерится. */\n error: ResponseErrorInfo | null\n /** Частичный override текстов. Мерджится с `DEFAULT_RESPONSE_ERROR_TEXTS`. */\n texts?: Partial<ResponseErrorTexts>\n /** Жёсткий оверрайд тона. Бьёт `toneByKind`. */\n tone?: ResponseErrorTone\n /** Маппинг `kind` → `tone`. Мерджится с `DEFAULT_TONE_BY_KIND`. */\n toneByKind?: Partial<Record<ResponseErrorKind, ResponseErrorTone>>\n /** Показывать ли блок `details`/`fieldErrors` под основным сообщением. */\n showDetails?: boolean\n /** Префиксить ли `field:` перед сообщениями по полям. */\n showFieldLabels?: boolean\n /** Человекочитаемые подписи полей (`{ file: 'Файл' }`). */\n fieldLabels?: Record<string, string>\n /** Удалять дубликаты сообщений между `message` и `details`. */\n dedupeDetails?: boolean\n /** Показывать ли кнопку «Повторить». */\n canRetry?: boolean\n /** Показывать ли кнопку «Скрыть». */\n canDismiss?: boolean\n /** Какие `kind` не рендерить вообще (баннер скрывается). */\n autoHideKinds?: ResponseErrorKind[]\n /** Показывать ли HTTP-status бейдж. */\n showStatus?: boolean\n /** Префикс `data-testid`. */\n testIdPrefix?: string\n}>(), {\n texts: () => ({}),\n tone: undefined,\n toneByKind: () => ({}),\n showDetails: true,\n showFieldLabels: true,\n fieldLabels: () => ({}),\n dedupeDetails: true,\n canRetry: false,\n canDismiss: true,\n autoHideKinds: () => DEFAULT_AUTO_HIDE_KINDS,\n showStatus: true,\n testIdPrefix: 'response-error',\n})\n\nconst emit = defineEmits<{\n (e: 'retry', error: ResponseErrorInfo): void\n (e: 'dismiss'): void\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst i18nTexts = computed<ResponseErrorTexts>(() => {\n const out = {} as ResponseErrorTexts\n for (const key of Object.keys(DEFAULT_RESPONSE_ERROR_TEXTS) as (keyof ResponseErrorTexts)[]) {\n out[key] = t(`gr.responseError.${key}`, DEFAULT_RESPONSE_ERROR_TEXTS[key])\n }\n return out\n})\n\nconst mergedTexts = computed<ResponseErrorTexts>(() => ({\n ...DEFAULT_RESPONSE_ERROR_TEXTS,\n ...i18nTexts.value,\n ...props.texts,\n}))\n\nconst mergedToneByKind = computed<Record<ResponseErrorKind, ResponseErrorTone>>(() => ({\n ...DEFAULT_TONE_BY_KIND,\n ...props.toneByKind,\n}))\n\nconst isHidden = computed(() => {\n if (!props.error) return true\n return props.autoHideKinds.includes(props.error.kind)\n})\n\nconst tone = computed<ResponseErrorTone>(() => {\n if (props.tone) return props.tone\n if (!props.error) return 'danger'\n return mergedToneByKind.value[props.error.kind] ?? 'danger'\n})\n\nconst title = computed(() => {\n if (!props.error) return ''\n return resolveResponseErrorTitle(props.error.kind, mergedTexts.value)\n})\n\n/**\n * Маппинг `kind` → ключ поля сообщения в `ResponseErrorTexts`.\n * Используется, чтобы подменить «дефолтное английское» fallback-сообщение\n * классификатора на i18n-перевод (или текущий `mergedTexts.<kind>Message`).\n */\nconst KIND_MESSAGE_KEY: Record<ResponseErrorKind, keyof ResponseErrorTexts> = {\n network: 'networkMessage',\n aborted: 'abortedMessage',\n validation: 'validationMessage',\n client: 'clientMessage',\n server: 'serverMessage',\n unknown: 'unknownMessage',\n}\n\nconst DEFAULT_FALLBACK_MESSAGES = new Set<string>([\n DEFAULT_RESPONSE_ERROR_TEXTS.networkMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.abortedMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.validationMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.clientMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.serverMessage,\n DEFAULT_RESPONSE_ERROR_TEXTS.unknownMessage,\n])\n\nconst message = computed(() => {\n const raw = props.error?.message ?? ''\n if (!props.error) return raw\n // Если в `ResponseErrorInfo.message` сидит англ. дефолт-фолбэк классификатора,\n // подменяем на i18n-перевод соответствующего kind.\n if (!raw || DEFAULT_FALLBACK_MESSAGES.has(raw)) {\n return mergedTexts.value[KIND_MESSAGE_KEY[props.error.kind]]\n }\n return raw\n})\n\n/**\n * Сводный список «деталей» под основным `message`.\n * Предпочитаем `fieldErrors` (поле → сообщения), затем — плоский `details`.\n * Дедуплицируем и убираем повтор с `message`.\n */\nconst detailEntries = computed<{ field?: string, text: string }[]>(() => {\n if (!props.error || !props.showDetails) return []\n\n const seen = new Set<string>()\n if (props.dedupeDetails) {\n const main = message.value.trim()\n if (main) seen.add(main)\n }\n\n const result: { field?: string, text: string }[] = []\n const fieldErrors = props.error.fieldErrors\n if (fieldErrors?.length) {\n for (const fe of fieldErrors) {\n for (const m of fe.messages) {\n const text = m.trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ field: fe.field, text })\n }\n }\n if (result.length) return result\n }\n\n const details = props.error.details\n if (details?.length) {\n for (const d of details) {\n const text = (d ?? '').trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ text })\n }\n }\n return result\n})\n\nfunction fieldLabel(field?: string): string | undefined {\n if (!field) return undefined\n return props.fieldLabels[field] ?? field\n}\n\nfunction onRetry(): void {\n if (props.error) emit('retry', props.error)\n}\n\nfunction onDismiss(): void {\n emit('dismiss')\n}\n</script>\n\n<template>\n <GrAlert\n v-if=\"!isHidden && props.error\"\n :tone=\"tone\"\n :title=\"title\"\n :closable=\"props.canDismiss\"\n :data-testid=\"`${props.testIdPrefix}-banner`\"\n :data-kind=\"props.error.kind\"\n @close=\"onDismiss\"\n >\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n v-if=\"props.showStatus && typeof props.error.status === 'number'\"\n class=\"inline-flex items-center rounded-[var(--ds-radius-sm)] bg-[color-mix(in_srgb,currentColor_12%,transparent)] px-1.5 py-0.5 text-[11px] font-700 leading-none\"\n :data-testid=\"`${props.testIdPrefix}-status`\"\n >\n HTTP {{ props.error.status }}\n </span>\n <span\n class=\"text-[13px]\"\n :data-testid=\"`${props.testIdPrefix}-message`\"\n >\n {{ message }}\n </span>\n </div>\n\n <ul\n v-if=\"detailEntries.length\"\n class=\"m-0 list-disc pl-5 text-[12px]\"\n :data-testid=\"`${props.testIdPrefix}-details`\"\n >\n <li\n v-for=\"(entry, idx) in detailEntries\"\n :key=\"`${entry.field ?? ''}:${idx}`\"\n >\n <template v-if=\"props.showFieldLabels && entry.field\">\n <strong>{{ fieldLabel(entry.field) }}:</strong>&nbsp;\n </template>{{ entry.text }}\n </li>\n </ul>\n\n <div\n v-if=\"props.canRetry\"\n class=\"mt-1 flex flex-wrap items-center gap-2\"\n >\n <GrButton\n size=\"sm\"\n variant=\"outline\"\n :data-testid=\"`${props.testIdPrefix}-retry`\"\n @click=\"onRetry\"\n >\n {{ mergedTexts.retryLabel }}\n </GrButton>\n </div>\n </div>\n </GrAlert>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { _ as DEFAULT_AUTO_HIDE_KINDS, a as extendDefaultParsers, c as problemDetailsParser, d as networkErrorParser, f as laravelValidationParser, g as abortErrorParser, h as fileValidationParser, i as defaultResponseErrorParsers, l as plainMessageParser, m as httpStatusParser, n as coreResponseErrorParsers, o as resolveResponseErrorTitle, p as jsonApiErrorParser, r as createResponseErrorClassifier, s as responseErrorParserPresets, t as GrResponseErrorBanner_default, u as normalizeError, v as DEFAULT_RESPONSE_ERROR_TEXTS, y as DEFAULT_TONE_BY_KIND } from "./chunks/GrResponseErrorBanner-CzdTh4gd.js";
2
+ import { n as GrFormErrorBanner_default, r as GrUploadErrorBanner_default, t as useResponseError } from "./chunks/GrResponseErrorBanner-BMvXREcv.js";
3
+ export { DEFAULT_AUTO_HIDE_KINDS, DEFAULT_RESPONSE_ERROR_TEXTS, DEFAULT_TONE_BY_KIND, GrFormErrorBanner_default as GrFormErrorBanner, GrResponseErrorBanner_default as GrResponseErrorBanner, GrResponseErrorBanner_default as default, GrUploadErrorBanner_default as GrUploadErrorBanner, abortErrorParser, coreResponseErrorParsers, createResponseErrorClassifier, defaultResponseErrorParsers, extendDefaultParsers, fileValidationParser, httpStatusParser, jsonApiErrorParser, laravelValidationParser, networkErrorParser, normalizeError, plainMessageParser, problemDetailsParser, resolveResponseErrorTitle, responseErrorParserPresets, useResponseError };
@@ -1,4 +1,4 @@
1
- import { a as grSegmentedItemIconClass, c as grSegmentedRootStyle, i as grSegmentedItemClass, o as grSegmentedItemLabelClass, r as grSegmentedIndicatorClass, s as grSegmentedRootClass } from "../../../chunks/config-BhvNm4Pp.js";
1
+ import { a as grSegmentedItemIconClass, c as grSegmentedRootStyle, i as grSegmentedItemClass, o as grSegmentedItemLabelClass, r as grSegmentedIndicatorClass, s as grSegmentedRootClass } from "../../../chunks/config-B2YTEe_h.js";
2
2
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, getCurrentInstance, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, watch } from "vue";
3
3
  //#region src/components/GrSegmented/GrSegmented.vue?vue&type=script&setup=true&lang.ts
4
4
  var _hoisted_1 = [
@@ -1 +1 @@
1
- {"version":3,"file":"GrSegmented-BPl1koHc.js","names":[],"sources":["../../../../src/components/GrSegmented/GrSegmented.vue","../../../../src/components/GrSegmented/GrSegmented.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>"],"mappings":"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"}
1
+ {"version":3,"file":"GrSegmented-BRmwBsEG.js","names":[],"sources":["../../../../src/components/GrSegmented/GrSegmented.vue","../../../../src/components/GrSegmented/GrSegmented.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { n as grSegmentedSafelist, t as grSegmentedConfig } from "../../chunks/config-BhvNm4Pp.js";
2
- import { t as GrSegmented_default } from "./chunks/GrSegmented-BPl1koHc.js";
1
+ import { n as grSegmentedSafelist, t as grSegmentedConfig } from "../../chunks/config-B2YTEe_h.js";
2
+ import { t as GrSegmented_default } from "./chunks/GrSegmented-BRmwBsEG.js";
3
3
  export { GrSegmented_default as GrSegmented, GrSegmented_default as default, grSegmentedConfig, grSegmentedSafelist };
@@ -1,6 +1,6 @@
1
+ import { t as GrInput_default } from "../../GrInput/chunks/GrInput-gwFQ1sk4.js";
1
2
  import { vClickOutside } from "../../../directives/clickOutside.js";
2
- import { t as GrInput_default } from "../../GrInput/chunks/GrInput-CfO2RsM-.js";
3
- import { a as grSelectPanelClasses, i as grSelectNativeClass, n as grSelectLinkNativeLabelClass, o as grSelectTriggerClass, r as grSelectLinkNativeOverlayClass, s as linkBaseClass, t as defaultBaseClass } from "../../../chunks/grSelectStyles-DTAYNzxn.js";
3
+ import { a as grSelectPanelClasses, i as grSelectNativeClass, n as grSelectLinkNativeLabelClass, o as grSelectTriggerClass, r as grSelectLinkNativeOverlayClass, s as linkBaseClass, t as defaultBaseClass } from "../../../chunks/grSelectStyles-pUt19VGy.js";
4
4
  import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onUnmounted, openBlock, ref, renderList, renderSlot, toDisplayString, unref, vShow, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
5
5
  //#region src/components/GrSelect/GrSelect.vue?vue&type=script&setup=true&lang.ts
6
6
  var _hoisted_1 = [
@@ -1 +1 @@
1
- {"version":3,"file":"GrSelect-C3NAfTqL.js","names":[],"sources":["../../../../src/components/GrSelect/GrSelect.vue","../../../../src/components/GrSelect/GrSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного DS-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n options?: GrSelectOption[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: 'Add value…',\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: 'Clear',\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\nconst optionsResolved = computed(() => props.options ?? [])\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(optionsResolved.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return optionsResolved.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => optionsResolved.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return optionsResolved.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined') return\n\n const root = rootEl.value\n if (!root) return\n\n const rect = root.getBoundingClientRect()\n\n // Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n // и без отдельной обработки панель тоже становилась шириной выбранной опции, из-за чего\n // её размер «прыгал» при выборе разных опций. Решение — отдать вычисление ширины браузеру:\n // `width: max-content` (по самому длинному ребёнку — `w-full`-кнопкам с `whitespace-nowrap`),\n // `min-width` равной ширине триггера (чтобы панель не была уже самого триггера).\n // Это чисто CSS-подход — без JS-замеров и ResizeObserver, максимально перформантный.\n if (props.view === 'link') {\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: 'max-content',\n minWidth: `${rect.width}px`,\n zIndex: '2147483647',\n }\n return\n }\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') closeDropdown()\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen) {\n customValue.value = ''\n return\n }\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n if (props.allowCustomValue) {\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nconst visibleOptions = computed(() => {\n const base = optionsResolved.value\n\n const withCustom = (() => {\n if (!props.allowCustomValue) return base\n if (props.multiple) return base\n if (modelSingle.value === '') return base\n if (hasModelInOptions.value) return base\n return [{ value: modelSingle.value, label: modelSingle.value }, ...base]\n })()\n\n if (!props.allowCustomValue) return withCustom\n\n const q = customValue.value.trim().toLowerCase()\n if (!q) return withCustom\n\n return withCustom.filter((o) => {\n return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)\n })\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !optionsResolved.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !optionsResolved.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\nconst showNativeChevron = computed(() => {\n return props.optionsView === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !props.options.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && props.optionsView === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: props.optionsView,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"optionsView === 'native'\"\n data-ds-select\n :class=\"rootClass\"\n >\n <select\n data-ds-select-native\n :value=\"multiple ? modelMultiple : modelSingle\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" />\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\">\n {{ modelSingle }}\n </option>\n <option v-for=\"opt in options\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-ds-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"ds-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-select\n :class=\"rootClass\"\n >\n <button\n data-testid=\"ds-select-trigger\"\n data-ds-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"ds-select-chevron\"\n class=\"shrink-0 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"ds-select-clear\"\n data-ds-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"clearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-select-panel\"\n data-ds-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"ds-select-custom-input\"\n type=\"text\"\n :placeholder=\"customValuePlaceholder\"\n size=\"sm\"\n @keydown.enter.prevent=\"addCustom\"\n />\n </div>\n\n <div\n data-ds-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"ds-select-add-option\"\n data-ds-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n Add “{{ customValue.trim() }}”\n </button>\n\n <button\n v-for=\"opt in visibleOptions\"\n :key=\"opt.value\"\n data-ds-select-option\n type=\"button\"\n role=\"option\"\n :aria-selected=\"isSelected(opt.value) ? 'true' : 'false'\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"toggleValue(opt.value)\"\n >\n <slot name=\"option\" :option=\"opt\" :selected=\"isSelected(opt.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(opt.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного DS-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n options?: GrSelectOption[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: 'Add value…',\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: 'Clear',\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\nconst optionsResolved = computed(() => props.options ?? [])\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(optionsResolved.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return optionsResolved.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => optionsResolved.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return optionsResolved.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined') return\n\n const root = rootEl.value\n if (!root) return\n\n const rect = root.getBoundingClientRect()\n\n // Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n // и без отдельной обработки панель тоже становилась шириной выбранной опции, из-за чего\n // её размер «прыгал» при выборе разных опций. Решение — отдать вычисление ширины браузеру:\n // `width: max-content` (по самому длинному ребёнку — `w-full`-кнопкам с `whitespace-nowrap`),\n // `min-width` равной ширине триггера (чтобы панель не была уже самого триггера).\n // Это чисто CSS-подход — без JS-замеров и ResizeObserver, максимально перформантный.\n if (props.view === 'link') {\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: 'max-content',\n minWidth: `${rect.width}px`,\n zIndex: '2147483647',\n }\n return\n }\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') closeDropdown()\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen) {\n customValue.value = ''\n return\n }\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n if (props.allowCustomValue) {\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nconst visibleOptions = computed(() => {\n const base = optionsResolved.value\n\n const withCustom = (() => {\n if (!props.allowCustomValue) return base\n if (props.multiple) return base\n if (modelSingle.value === '') return base\n if (hasModelInOptions.value) return base\n return [{ value: modelSingle.value, label: modelSingle.value }, ...base]\n })()\n\n if (!props.allowCustomValue) return withCustom\n\n const q = customValue.value.trim().toLowerCase()\n if (!q) return withCustom\n\n return withCustom.filter((o) => {\n return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)\n })\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !optionsResolved.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !optionsResolved.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\nconst showNativeChevron = computed(() => {\n return props.optionsView === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !props.options.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && props.optionsView === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: props.optionsView,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"optionsView === 'native'\"\n data-ds-select\n :class=\"rootClass\"\n >\n <select\n data-ds-select-native\n :value=\"multiple ? modelMultiple : modelSingle\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" />\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\">\n {{ modelSingle }}\n </option>\n <option v-for=\"opt in options\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-ds-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"ds-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-select\n :class=\"rootClass\"\n >\n <button\n data-testid=\"ds-select-trigger\"\n data-ds-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"ds-select-chevron\"\n class=\"shrink-0 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"ds-select-clear\"\n data-ds-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"clearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-select-panel\"\n data-ds-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"ds-select-custom-input\"\n type=\"text\"\n :placeholder=\"customValuePlaceholder\"\n size=\"sm\"\n @keydown.enter.prevent=\"addCustom\"\n />\n </div>\n\n <div\n data-ds-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"ds-select-add-option\"\n data-ds-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n Add “{{ customValue.trim() }}”\n </button>\n\n <button\n v-for=\"opt in visibleOptions\"\n :key=\"opt.value\"\n data-ds-select-option\n type=\"button\"\n role=\"option\"\n :aria-selected=\"isSelected(opt.value) ? 'true' : 'false'\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"toggleValue(opt.value)\"\n >\n <slot name=\"option\" :option=\"opt\" :selected=\"isSelected(opt.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(opt.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrSelect-W9lbAjRZ.js","names":[],"sources":["../../../../src/components/GrSelect/GrSelect.vue","../../../../src/components/GrSelect/GrSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного DS-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n options?: GrSelectOption[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: 'Add value…',\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: 'Clear',\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\nconst optionsResolved = computed(() => props.options ?? [])\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(optionsResolved.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return optionsResolved.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => optionsResolved.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return optionsResolved.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined') return\n\n const root = rootEl.value\n if (!root) return\n\n const rect = root.getBoundingClientRect()\n\n // Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n // и без отдельной обработки панель тоже становилась шириной выбранной опции, из-за чего\n // её размер «прыгал» при выборе разных опций. Решение — отдать вычисление ширины браузеру:\n // `width: max-content` (по самому длинному ребёнку — `w-full`-кнопкам с `whitespace-nowrap`),\n // `min-width` равной ширине триггера (чтобы панель не была уже самого триггера).\n // Это чисто CSS-подход — без JS-замеров и ResizeObserver, максимально перформантный.\n if (props.view === 'link') {\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: 'max-content',\n minWidth: `${rect.width}px`,\n zIndex: '2147483647',\n }\n return\n }\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') closeDropdown()\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen) {\n customValue.value = ''\n return\n }\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n if (props.allowCustomValue) {\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nconst visibleOptions = computed(() => {\n const base = optionsResolved.value\n\n const withCustom = (() => {\n if (!props.allowCustomValue) return base\n if (props.multiple) return base\n if (modelSingle.value === '') return base\n if (hasModelInOptions.value) return base\n return [{ value: modelSingle.value, label: modelSingle.value }, ...base]\n })()\n\n if (!props.allowCustomValue) return withCustom\n\n const q = customValue.value.trim().toLowerCase()\n if (!q) return withCustom\n\n return withCustom.filter((o) => {\n return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)\n })\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !optionsResolved.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !optionsResolved.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\nconst showNativeChevron = computed(() => {\n return props.optionsView === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !props.options.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && props.optionsView === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: props.optionsView,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"optionsView === 'native'\"\n data-ds-select\n :class=\"rootClass\"\n >\n <select\n data-ds-select-native\n :value=\"multiple ? modelMultiple : modelSingle\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" />\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\">\n {{ modelSingle }}\n </option>\n <option v-for=\"opt in options\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-ds-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"ds-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-select\n :class=\"rootClass\"\n >\n <button\n data-testid=\"ds-select-trigger\"\n data-ds-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"ds-select-chevron\"\n class=\"shrink-0 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"ds-select-clear\"\n data-ds-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"clearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-select-panel\"\n data-ds-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"ds-select-custom-input\"\n type=\"text\"\n :placeholder=\"customValuePlaceholder\"\n size=\"sm\"\n @keydown.enter.prevent=\"addCustom\"\n />\n </div>\n\n <div\n data-ds-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"ds-select-add-option\"\n data-ds-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n Add “{{ customValue.trim() }}”\n </button>\n\n <button\n v-for=\"opt in visibleOptions\"\n :key=\"opt.value\"\n data-ds-select-option\n type=\"button\"\n role=\"option\"\n :aria-selected=\"isSelected(opt.value) ? 'true' : 'false'\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"toggleValue(opt.value)\"\n >\n <slot name=\"option\" :option=\"opt\" :selected=\"isSelected(opt.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(opt.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного DS-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n options?: GrSelectOption[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: 'Add value…',\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: 'Clear',\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\nconst optionsResolved = computed(() => props.options ?? [])\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(optionsResolved.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return optionsResolved.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => optionsResolved.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return optionsResolved.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined') return\n\n const root = rootEl.value\n if (!root) return\n\n const rect = root.getBoundingClientRect()\n\n // Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n // и без отдельной обработки панель тоже становилась шириной выбранной опции, из-за чего\n // её размер «прыгал» при выборе разных опций. Решение — отдать вычисление ширины браузеру:\n // `width: max-content` (по самому длинному ребёнку — `w-full`-кнопкам с `whitespace-nowrap`),\n // `min-width` равной ширине триггера (чтобы панель не была уже самого триггера).\n // Это чисто CSS-подход — без JS-замеров и ResizeObserver, максимально перформантный.\n if (props.view === 'link') {\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: 'max-content',\n minWidth: `${rect.width}px`,\n zIndex: '2147483647',\n }\n return\n }\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') closeDropdown()\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen) {\n customValue.value = ''\n return\n }\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n if (props.allowCustomValue) {\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nconst visibleOptions = computed(() => {\n const base = optionsResolved.value\n\n const withCustom = (() => {\n if (!props.allowCustomValue) return base\n if (props.multiple) return base\n if (modelSingle.value === '') return base\n if (hasModelInOptions.value) return base\n return [{ value: modelSingle.value, label: modelSingle.value }, ...base]\n })()\n\n if (!props.allowCustomValue) return withCustom\n\n const q = customValue.value.trim().toLowerCase()\n if (!q) return withCustom\n\n return withCustom.filter((o) => {\n return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)\n })\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !optionsResolved.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !optionsResolved.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\nconst showNativeChevron = computed(() => {\n return props.optionsView === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !props.options.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && props.optionsView === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: props.optionsView,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"optionsView === 'native'\"\n data-ds-select\n :class=\"rootClass\"\n >\n <select\n data-ds-select-native\n :value=\"multiple ? modelMultiple : modelSingle\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" />\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\">\n {{ modelSingle }}\n </option>\n <option v-for=\"opt in options\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-ds-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"ds-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-select\n :class=\"rootClass\"\n >\n <button\n data-testid=\"ds-select-trigger\"\n data-ds-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"ds-select-chevron\"\n class=\"shrink-0 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"ds-select-clear\"\n data-ds-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"clearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-select-panel\"\n data-ds-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"ds-select-custom-input\"\n type=\"text\"\n :placeholder=\"customValuePlaceholder\"\n size=\"sm\"\n @keydown.enter.prevent=\"addCustom\"\n />\n </div>\n\n <div\n data-ds-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"ds-select-add-option\"\n data-ds-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n Add “{{ customValue.trim() }}”\n </button>\n\n <button\n v-for=\"opt in visibleOptions\"\n :key=\"opt.value\"\n data-ds-select-option\n type=\"button\"\n role=\"option\"\n :aria-selected=\"isSelected(opt.value) ? 'true' : 'false'\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"toggleValue(opt.value)\"\n >\n <slot name=\"option\" :option=\"opt\" :selected=\"isSelected(opt.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(opt.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}