@feugene/granularity 0.16.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/dist/chunks/chevron-left-D8AUBAM7.js +23 -0
  2. package/dist/chunks/chevron-left-D8AUBAM7.js.map +1 -0
  3. package/dist/chunks/{chevron-right-BfqMVpbB.js → chevron-right-C6Z01h0X.js} +1 -22
  4. package/dist/chunks/chevron-right-C6Z01h0X.js.map +1 -0
  5. package/dist/chunks/config-8Pcutu1j2.js.map +1 -0
  6. package/dist/chunks/{config-K0C2tcLB.js → config-9PYRZlKE.js} +1 -1
  7. package/dist/chunks/{config-K0C2tcLB.js.map → config-9PYRZlKE.js.map} +1 -1
  8. package/dist/chunks/{config-Zc_bUvZY2.js → config-BDfTXHo12.js} +1 -1
  9. package/dist/chunks/{config-Zc_bUvZY2.js.map → config-BDfTXHo12.js.map} +1 -1
  10. package/dist/chunks/config-BE6n20np2.js.map +1 -0
  11. package/dist/chunks/config-BJ7Bv_G2.js +148 -0
  12. package/dist/chunks/config-BJ7Bv_G2.js.map +1 -0
  13. package/dist/chunks/config-BKvX0q102.js.map +1 -0
  14. package/dist/chunks/{config-CpwwkFBO.js → config-Baj7Wvx-.js} +1 -1
  15. package/dist/chunks/{config-CpwwkFBO.js.map → config-Baj7Wvx-.js.map} +1 -1
  16. package/dist/chunks/{config-D8tSR2uS.js → config-Be9k9bOf.js} +2 -8
  17. package/dist/chunks/config-Be9k9bOf.js.map +1 -0
  18. package/dist/chunks/config-BoNR7_PD2.js.map +1 -0
  19. package/dist/chunks/{config-DxCTVBfx.js → config-BsPDQ0dS.js} +2 -2
  20. package/dist/chunks/config-BsPDQ0dS.js.map +1 -0
  21. package/dist/chunks/{config-DpqOnJiz2.js → config-CDat1wXf2.js} +1 -1
  22. package/dist/chunks/config-CDat1wXf2.js.map +1 -0
  23. package/dist/chunks/config-CN2ULSQL.js.map +1 -0
  24. package/dist/chunks/{config-Cw6TDzWv2.js → config-COpuWGr_.js} +1 -1
  25. package/dist/chunks/config-COpuWGr_.js.map +1 -0
  26. package/dist/chunks/config-CY0_6cMi2.js.map +1 -0
  27. package/dist/chunks/{config-B-g6lT19.js → config-DA6SnEoy.js} +5 -2
  28. package/dist/chunks/config-DA6SnEoy.js.map +1 -0
  29. package/dist/chunks/config-DAX8Deh1.js +68 -0
  30. package/dist/chunks/config-DAX8Deh1.js.map +1 -0
  31. package/dist/chunks/config-DKdlqXiY.js.map +1 -1
  32. package/dist/chunks/{config-CvC0sX8p.js → config-MRvhUl-O.js} +1 -1
  33. package/dist/chunks/{config-CvC0sX8p.js.map → config-MRvhUl-O.js.map} +1 -1
  34. package/dist/chunks/config-Rw9OyW702.js.map +1 -0
  35. package/dist/chunks/config-ScuvHw6k2.js +42 -0
  36. package/dist/chunks/config-ScuvHw6k2.js.map +1 -0
  37. package/dist/chunks/config-T3qdQBgU2.js.map +1 -0
  38. package/dist/chunks/config-eXB87DOi.js.map +1 -0
  39. package/dist/chunks/context-BSYR7RZs.js.map +1 -1
  40. package/dist/chunks/{en-B3xMeswz.js → en-COdzxgAX.js} +30 -5
  41. package/dist/chunks/en-COdzxgAX.js.map +1 -0
  42. package/dist/chunks/{es-CA-mkU--.js → es-ClqJ-4hr.js} +30 -5
  43. package/dist/chunks/es-ClqJ-4hr.js.map +1 -0
  44. package/dist/chunks/{es-Bv8vvqdT.js → es-DGMHwknJ.js} +3 -3
  45. package/dist/chunks/{es-Bv8vvqdT.js.map → es-DGMHwknJ.js.map} +1 -1
  46. package/dist/chunks/generated-BRkbhyxk.js +3270 -0
  47. package/dist/chunks/generated-BRkbhyxk.js.map +1 -0
  48. package/dist/chunks/grKbdStyles-CeZhGQHr.js +43 -0
  49. package/dist/chunks/grKbdStyles-CeZhGQHr.js.map +1 -0
  50. package/dist/chunks/{grTextareaStyles-CWI2SqC8.js → grTextareaStyles-iOcHuVMM.js} +2 -2
  51. package/dist/chunks/{grTextareaStyles-CWI2SqC8.js.map → grTextareaStyles-iOcHuVMM.js.map} +1 -1
  52. package/dist/chunks/{granular-provider-CtrXGa7c.js → granular-provider-C6uAujdJ.js} +23 -21
  53. package/dist/chunks/granular-provider-C6uAujdJ.js.map +1 -0
  54. package/dist/chunks/grip-vertical-lRha2S7q.js +16 -0
  55. package/dist/chunks/grip-vertical-lRha2S7q.js.map +1 -0
  56. package/dist/chunks/icon-Dag2tIMs.js +21 -0
  57. package/dist/chunks/icon-Dag2tIMs.js.map +1 -0
  58. package/dist/chunks/{keyboard-zYvvyHd4.js → keyboard-DE2yhz_b.js} +10 -1
  59. package/dist/chunks/keyboard-DE2yhz_b.js.map +1 -0
  60. package/dist/chunks/{optionFilter-BVY988pC.js → optionFilter-DYw9XPPx.js} +1 -71
  61. package/dist/chunks/optionFilter-DYw9XPPx.js.map +1 -0
  62. package/dist/chunks/{useOverlayLayer-BupuP_hs.js → overlayStack-C8cfsqXa.js} +11 -61
  63. package/dist/chunks/overlayStack-C8cfsqXa.js.map +1 -0
  64. package/dist/chunks/{portalRoot-CWD4zHBm.js → portalRoot-CVhCgdG6.js} +10 -1
  65. package/dist/chunks/{portalRoot-CWD4zHBm.js.map → portalRoot-CVhCgdG6.js.map} +1 -1
  66. package/dist/chunks/{ru-COWxgh2A.js → ru-9z8ix45c.js} +30 -5
  67. package/dist/chunks/ru-9z8ix45c.js.map +1 -0
  68. package/dist/chunks/useDragSort-0l47h4yb.js +265 -0
  69. package/dist/chunks/useDragSort-0l47h4yb.js.map +1 -0
  70. package/dist/chunks/{useModalOverlay-DCM4d3yA.js → useModalOverlay-DrQVYwwA.js} +2 -2
  71. package/dist/chunks/{useModalOverlay-DCM4d3yA.js.map → useModalOverlay-DrQVYwwA.js.map} +1 -1
  72. package/dist/chunks/{usePortalTarget-RyzEgNTb.js → usePortalTarget-Cn-Wiz5d.js} +1 -1
  73. package/dist/chunks/{usePortalTarget-RyzEgNTb.js.map → usePortalTarget-Cn-Wiz5d.js.map} +1 -1
  74. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CQ9T7Lly.js → GrAutocomplete-gY4Xopza.js} +35 -26
  75. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-gY4Xopza.js.map +1 -0
  76. package/dist/components/GrAutocomplete/index.js +1 -1
  77. package/dist/components/GrAutocomplete/styles.css +1 -1
  78. package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-BCNGDS6k.js → GrBreadcrumbs-DS_-PBVt.js} +8 -7
  79. package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-DS_-PBVt.js.map +1 -0
  80. package/dist/components/GrBreadcrumbs/index.js +1 -1
  81. package/dist/components/GrCollapse/chunks/{GrCollapse-B2Fc6A9r.js.map → GrCollapse-Kk0DE8tb.js.map} +1 -1
  82. package/dist/components/GrCollapse/index.js +1 -1
  83. package/dist/components/GrColorPicker/chunks/{GrColorPicker-bnYBRGk8.js → GrColorPicker-CkBTzkqn.js} +4 -4
  84. package/dist/components/GrColorPicker/chunks/{GrColorPicker-bnYBRGk8.js.map → GrColorPicker-CkBTzkqn.js.map} +1 -1
  85. package/dist/components/GrColorPicker/index.js +2 -2
  86. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-MWDYDPNC.js → GrCommandPalette-BugTlGdj.js} +41 -16
  87. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BugTlGdj.js.map +1 -0
  88. package/dist/components/GrCommandPalette/index.js +2 -2
  89. package/dist/components/GrCommandPalette/styles.css +1 -1
  90. package/dist/components/GrConfigProvider/index.js +2 -2
  91. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DaQmlp1_.js → GrConfirmDialog-Ds5T0GiR.js} +1 -1
  92. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DaQmlp1_.js.map → GrConfirmDialog-Ds5T0GiR.js.map} +1 -1
  93. package/dist/components/GrConfirmDialog/index.js +1 -1
  94. package/dist/components/GrDataTable/chunks/{GrDataTable-DVFho9Cj.js → GrDataTable-CYe2A2WA.js} +498 -48
  95. package/dist/components/GrDataTable/chunks/GrDataTable-CYe2A2WA.js.map +1 -0
  96. package/dist/components/GrDataTable/index.js +2 -2
  97. package/dist/components/GrDialog/chunks/{GrDialog-AE1gIZuz.js → GrDialog-C3tYaA8v.js} +1 -1
  98. package/dist/components/GrDialog/chunks/{GrDialog-AE1gIZuz.js.map → GrDialog-C3tYaA8v.js.map} +1 -1
  99. package/dist/components/GrDialog/index.js +1 -1
  100. package/dist/components/GrDialogService/chunks/{GrDialogService-B4DYWDkA.js → GrDialogService-CTBiiJDI.js} +3 -3
  101. package/dist/components/GrDialogService/chunks/{GrDialogService-B4DYWDkA.js.map → GrDialogService-CTBiiJDI.js.map} +1 -1
  102. package/dist/components/GrDialogService/index.js +1 -1
  103. package/dist/components/GrDivider/chunks/{GrDivider-B2Xwc_QO.js → GrDivider-xxq1_fIl.js} +1 -1
  104. package/dist/components/GrDivider/chunks/{GrDivider-B2Xwc_QO.js.map → GrDivider-xxq1_fIl.js.map} +1 -1
  105. package/dist/components/GrDivider/index.js +2 -2
  106. package/dist/components/GrDrawer/chunks/{GrDrawer-DQwi6aRw.js → GrDrawer-BSSuiNHU.js} +1 -1
  107. package/dist/components/GrDrawer/chunks/{GrDrawer-DQwi6aRw.js.map → GrDrawer-BSSuiNHU.js.map} +1 -1
  108. package/dist/components/GrDrawer/index.js +1 -1
  109. package/dist/components/GrDropdown/chunks/{GrDropdown-BLng6V1m.js → GrDropdown-Bh6UK2M5.js} +3 -3
  110. package/dist/components/GrDropdown/chunks/{GrDropdown-BLng6V1m.js.map → GrDropdown-Bh6UK2M5.js.map} +1 -1
  111. package/dist/components/GrDropdown/index.js +1 -1
  112. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BImk2Vs7.js → GrDropdownMenu-LyfEFUbg.js} +2 -2
  113. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BImk2Vs7.js.map → GrDropdownMenu-LyfEFUbg.js.map} +1 -1
  114. package/dist/components/GrDropdownMenu/index.js +2 -2
  115. package/dist/components/GrFileUpload/chunks/{GrFileUpload-D0B-BEsd.js → GrFileUpload-DuDzC43S.js} +2 -2
  116. package/dist/components/GrFileUpload/chunks/{GrFileUpload-D0B-BEsd.js.map → GrFileUpload-DuDzC43S.js.map} +1 -1
  117. package/dist/components/GrFileUpload/index.js +1 -1
  118. package/dist/components/GrImageViewer/chunks/{GrImageViewer-BeCnE8XQ.js → GrImageViewer-DfD4euH5.js} +48 -23
  119. package/dist/components/GrImageViewer/chunks/GrImageViewer-DfD4euH5.js.map +1 -0
  120. package/dist/components/GrImageViewer/index.js +1 -1
  121. package/dist/components/GrInput/chunks/{GrInput-DD2gYdqS.js → GrInput-2wd7Crs0.js} +1 -1
  122. package/dist/components/GrInput/chunks/GrInput-2wd7Crs0.js.map +1 -0
  123. package/dist/components/GrInput/index.js +1 -1
  124. package/dist/components/GrInputTag/chunks/{GrInputTag-DmNunixz.js → GrInputTag-CyV2eY7Z.js} +20 -33
  125. package/dist/components/GrInputTag/chunks/GrInputTag-CyV2eY7Z.js.map +1 -0
  126. package/dist/components/GrInputTag/index.js +1 -1
  127. package/dist/components/GrKbd/chunks/{GrKbd-DRFPvW3G.js → GrKbd-DU2lBkRV.js} +218 -52
  128. package/dist/components/GrKbd/chunks/GrKbd-DU2lBkRV.js.map +1 -0
  129. package/dist/components/GrKbd/index.js +3 -3
  130. package/dist/components/GrModal/chunks/{GrModal-B8I78T0N.js → GrModal-CbMMr6W1.js} +1 -1
  131. package/dist/components/GrModal/chunks/{GrModal-B8I78T0N.js.map → GrModal-CbMMr6W1.js.map} +1 -1
  132. package/dist/components/GrModal/index.js +1 -1
  133. package/dist/components/GrNumberInput/chunks/{GrNumberInput-CZOyPz4J.js → GrNumberInput-B5CHZDBj.js} +2 -1
  134. package/dist/components/GrNumberInput/chunks/{GrNumberInput-CZOyPz4J.js.map → GrNumberInput-B5CHZDBj.js.map} +1 -1
  135. package/dist/components/GrNumberInput/index.js +1 -1
  136. package/dist/components/GrPagination/chunks/{GrPagination-B6N0QZBf.js → GrPagination-BM4FrD6Q.js} +4 -4
  137. package/dist/components/GrPagination/chunks/GrPagination-BM4FrD6Q.js.map +1 -0
  138. package/dist/components/GrPagination/index.js +1 -1
  139. package/dist/components/GrPopover/chunks/{GrPopover-B1eRCOoO.js → GrPopover-DMH4Jjst.js} +1 -1
  140. package/dist/components/GrPopover/chunks/{GrPopover-B1eRCOoO.js.map → GrPopover-DMH4Jjst.js.map} +1 -1
  141. package/dist/components/GrPopover/index.js +1 -1
  142. package/dist/components/GrProgressBar/chunks/{GrProgressBar-BiHnaitu.js → GrProgressBar-xwbNM9QB.js} +1 -1
  143. package/dist/components/GrProgressBar/chunks/GrProgressBar-xwbNM9QB.js.map +1 -0
  144. package/dist/components/GrProgressBar/index.js +1 -1
  145. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-CTogjcHz.js → GrProgressCircle-B8olXRo5.js} +1 -1
  146. package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-CTogjcHz.js.map → GrProgressCircle-B8olXRo5.js.map} +1 -1
  147. package/dist/components/GrProgressCircle/index.js +2 -2
  148. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ct174NVD.js → GrPromptDialog-Pwr9NPxK.js} +3 -3
  149. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ct174NVD.js.map → GrPromptDialog-Pwr9NPxK.js.map} +1 -1
  150. package/dist/components/GrPromptDialog/index.js +1 -1
  151. package/dist/components/GrRadio/chunks/{GrRadio-DksDro_U.js → GrRadio-DWiRaMzt.js} +8 -32
  152. package/dist/components/GrRadio/chunks/GrRadio-DWiRaMzt.js.map +1 -0
  153. package/dist/components/GrRadio/index.js +1 -1
  154. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BOIhejE6.js → GrRadioGroup-DIuBw0wk.js} +34 -27
  155. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-DIuBw0wk.js.map +1 -0
  156. package/dist/components/GrRadioGroup/index.js +1 -1
  157. package/dist/components/GrRating/chunks/{GrRating-JrHXz0Rv.js → GrRating-DVFH3lYu.js} +10 -9
  158. package/dist/components/GrRating/chunks/GrRating-DVFH3lYu.js.map +1 -0
  159. package/dist/components/GrRating/index.js +2 -2
  160. package/dist/components/GrSegmented/chunks/{GrSegmented-BQIlH5KI.js → GrSegmented-CQw3ijqa.js} +47 -65
  161. package/dist/components/GrSegmented/chunks/GrSegmented-CQw3ijqa.js.map +1 -0
  162. package/dist/components/GrSegmented/index.js +1 -1
  163. package/dist/components/GrSelect/chunks/{GrSelect-aqkPCqjd.js → GrSelect-B-Voni7D.js} +69 -55
  164. package/dist/components/GrSelect/chunks/GrSelect-B-Voni7D.js.map +1 -0
  165. package/dist/components/GrSelect/index.js +1 -1
  166. package/dist/components/GrSelect/styles.css +1 -1
  167. package/dist/components/GrSidebar/chunks/{GrSidebar-DRGnnViw.js → GrSidebar-i7AxjhY_.js} +3 -2
  168. package/dist/components/GrSidebar/chunks/{GrSidebar-DRGnnViw.js.map → GrSidebar-i7AxjhY_.js.map} +1 -1
  169. package/dist/components/GrSidebar/index.js +2 -2
  170. package/dist/components/GrSkeleton/index.js +1 -1
  171. package/dist/components/GrSlider/chunks/{GrSlider-DkU9IQSX.js → GrSlider-CyhVsNKn.js} +21 -34
  172. package/dist/components/GrSlider/chunks/GrSlider-CyhVsNKn.js.map +1 -0
  173. package/dist/components/GrSlider/index.js +2 -2
  174. package/dist/components/GrSortableList/chunks/GrSortableList-XB7mXM1W.js +277 -0
  175. package/dist/components/GrSortableList/chunks/GrSortableList-XB7mXM1W.js.map +1 -0
  176. package/dist/components/GrSortableList/index.js +3 -0
  177. package/dist/components/GrSplitter/chunks/{GrSplitter-7To8jbJ8.js → GrSplitter-BzxVQwbY.js} +17 -37
  178. package/dist/components/GrSplitter/chunks/GrSplitter-BzxVQwbY.js.map +1 -0
  179. package/dist/components/GrSplitter/index.js +2 -2
  180. package/dist/components/GrStatistic/chunks/{GrStatistic-CttyoYPT.js → GrStatistic-DOsM6s5V.js} +54 -7
  181. package/dist/components/GrStatistic/chunks/GrStatistic-DOsM6s5V.js.map +1 -0
  182. package/dist/components/GrStatistic/index.js +2 -2
  183. package/dist/components/GrSwitch/chunks/{GrSwitch-B9TYbiih.js → GrSwitch-DpyvVrZG.js} +1 -1
  184. package/dist/components/GrSwitch/chunks/{GrSwitch-B9TYbiih.js.map → GrSwitch-DpyvVrZG.js.map} +1 -1
  185. package/dist/components/GrSwitch/index.js +2 -2
  186. package/dist/components/GrTabPanels/index.js +1 -1
  187. package/dist/components/GrTable/chunks/{GrTable-BB-mI_NM.js → GrTable-BfqgwETQ.js} +7 -1
  188. package/dist/components/GrTable/chunks/GrTable-BfqgwETQ.js.map +1 -0
  189. package/dist/components/GrTable/index.js +1 -1
  190. package/dist/components/GrTabs/chunks/{GrTabs-B8CDq2D9.js → GrTabs-f3-oQenu.js} +55 -65
  191. package/dist/components/GrTabs/chunks/GrTabs-f3-oQenu.js.map +1 -0
  192. package/dist/components/GrTabs/index.js +1 -1
  193. package/dist/components/GrTextarea/chunks/{GrTextarea-DUh5Tlo4.js → GrTextarea-DSvd1nSy.js} +1 -1
  194. package/dist/components/GrTextarea/chunks/{GrTextarea-DUh5Tlo4.js.map → GrTextarea-DSvd1nSy.js.map} +1 -1
  195. package/dist/components/GrTextarea/index.js +2 -2
  196. package/dist/components/GrTimeline/chunks/{GrTimeline-CE9b3s18.js → GrTimeline-B-h2Yanw.js} +1 -1
  197. package/dist/components/GrTimeline/chunks/{GrTimeline-CE9b3s18.js.map → GrTimeline-B-h2Yanw.js.map} +1 -1
  198. package/dist/components/GrTimeline/index.js +2 -2
  199. package/dist/components/GrToaster/chunks/{GrToaster-BKQy3Srm.js → GrToaster-Dqja0Lf9.js} +114 -13
  200. package/dist/components/GrToaster/chunks/GrToaster-Dqja0Lf9.js.map +1 -0
  201. package/dist/components/GrToaster/index.js +2 -2
  202. package/dist/components/GrToaster/styles.css +1 -1
  203. package/dist/components/GrTooltip/chunks/{GrTooltip-DEbejfhA.js → GrTooltip-Cn08heWz.js} +2 -2
  204. package/dist/components/GrTooltip/chunks/{GrTooltip-DEbejfhA.js.map → GrTooltip-Cn08heWz.js.map} +1 -1
  205. package/dist/components/GrTooltip/index.js +2 -2
  206. package/dist/components/GrTree/chunks/{GrTree-wfdbvygw.js → GrTree-BR9a2pay.js} +148 -111
  207. package/dist/components/GrTree/chunks/GrTree-BR9a2pay.js.map +1 -0
  208. package/dist/components/GrTree/index.js +2 -2
  209. package/dist/components/GrTree/styles.css +1 -1
  210. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-lLWmAMtI.js → GrTreeSelect--muUeB0v.js} +28 -14
  211. package/dist/components/GrTreeSelect/chunks/GrTreeSelect--muUeB0v.js.map +1 -0
  212. package/dist/components/GrTreeSelect/index.js +1 -1
  213. package/dist/composables/useAnnouncer.js +1 -1
  214. package/dist/composables/useComboboxNavigation.js +71 -0
  215. package/dist/composables/useComboboxNavigation.js.map +1 -0
  216. package/dist/composables/useDismissible.js +1 -1
  217. package/dist/composables/useDragGesture.js +55 -0
  218. package/dist/composables/useDragGesture.js.map +1 -0
  219. package/dist/composables/useDragSort.js +2 -0
  220. package/dist/composables/useFloating.js +22 -1
  221. package/dist/composables/useFloating.js.map +1 -1
  222. package/dist/composables/useGrComponentConfig.js +2 -0
  223. package/dist/composables/useGranularityTranslations.js +2 -0
  224. package/dist/composables/useOverlayLayer.js +64 -1
  225. package/dist/composables/useOverlayLayer.js.map +1 -0
  226. package/dist/composables/usePortalTarget.js +1 -1
  227. package/dist/composables/useRovingFocus.js +150 -0
  228. package/dist/composables/useRovingFocus.js.map +1 -0
  229. package/dist/directives/hotkey.js +13 -7
  230. package/dist/directives/hotkey.js.map +1 -1
  231. package/dist/directives/loading.js +1 -1
  232. package/dist/granular-provider-node.js +1 -1
  233. package/dist/granular-provider.js +1 -1
  234. package/dist/i18n/all.js +1 -1
  235. package/dist/i18n/index.js +1 -1
  236. package/dist/index.js +77 -68
  237. package/dist/styles/themes/dark.css +6 -1
  238. package/dist/testing.js +199 -0
  239. package/dist/testing.js.map +1 -0
  240. package/dist/theme-apply.js +33 -0
  241. package/dist/theme-apply.js.map +1 -0
  242. package/dist/theme.js +501 -0
  243. package/dist/theme.js.map +1 -0
  244. package/dist/tokens.js +1 -3131
  245. package/dist/types/src/components/GrAlert/defaults.d.ts +1 -1
  246. package/dist/types/src/components/GrAutocomplete/defaults.d.ts +1 -1
  247. package/dist/types/src/components/GrAvatar/defaults.d.ts +1 -1
  248. package/dist/types/src/components/GrBadge/defaults.d.ts +1 -1
  249. package/dist/types/src/components/GrBottomNav/defaults.d.ts +1 -1
  250. package/dist/types/src/components/GrBreadcrumbs/defaults.d.ts +1 -1
  251. package/dist/types/src/components/GrBreadcrumbs/grBreadcrumbsStyles.d.ts +5 -2
  252. package/dist/types/src/components/GrButton/defaults.d.ts +1 -1
  253. package/dist/types/src/components/GrCard/defaults.d.ts +1 -1
  254. package/dist/types/src/components/GrCheckbox/defaults.d.ts +1 -1
  255. package/dist/types/src/components/GrCheckboxGroup/defaults.d.ts +1 -1
  256. package/dist/types/src/components/GrCollapse/defaults.d.ts +1 -1
  257. package/dist/types/src/components/GrColorPicker/defaults.d.ts +1 -1
  258. package/dist/types/src/components/GrColorPicker/grColorPickerStyles.d.ts +2 -2
  259. package/dist/types/src/components/GrCommandPalette/GrCommandPalette.vue.d.ts +6 -6
  260. package/dist/types/src/components/GrCommandPalette/defaults.d.ts +1 -1
  261. package/dist/types/src/components/GrCommandPalette/filtering.d.ts +6 -2
  262. package/dist/types/src/components/GrConfigProvider/context.d.ts +6 -22
  263. package/dist/types/src/components/GrConfigProvider/index.d.ts +1 -1
  264. package/dist/types/src/components/GrConfirmDialog/defaults.d.ts +1 -1
  265. package/dist/types/src/components/GrDataTable/GrDataTable.vue.d.ts +53 -0
  266. package/dist/types/src/components/GrDataTable/defaults.d.ts +1 -1
  267. package/dist/types/src/components/GrDataTable/grDataTableStyles.d.ts +38 -0
  268. package/dist/types/src/components/GrDialog/defaults.d.ts +1 -1
  269. package/dist/types/src/components/GrDivider/defaults.d.ts +1 -1
  270. package/dist/types/src/components/GrDrawer/defaults.d.ts +1 -1
  271. package/dist/types/src/components/GrEmptyState/defaults.d.ts +1 -1
  272. package/dist/types/src/components/GrFileUpload/defaults.d.ts +1 -1
  273. package/dist/types/src/components/GrFormField/defaults.d.ts +1 -1
  274. package/dist/types/src/components/GrFormFile/defaults.d.ts +1 -1
  275. package/dist/types/src/components/GrFormSection/defaults.d.ts +1 -1
  276. package/dist/types/src/components/GrIcon/defaults.d.ts +1 -1
  277. package/dist/types/src/components/GrImageViewer/composables/usePointerGestures.d.ts +1 -0
  278. package/dist/types/src/components/GrImageViewer/composables/useZoomPan.d.ts +1 -2
  279. package/dist/types/src/components/GrInput/defaults.d.ts +1 -1
  280. package/dist/types/src/components/GrInputTag/defaults.d.ts +1 -1
  281. package/dist/types/src/components/GrKbd/GrKbd.vue.d.ts +18 -3
  282. package/dist/types/src/components/GrKbd/defaults.d.ts +1 -1
  283. package/dist/types/src/components/GrKbd/grKbdStyles.d.ts +20 -3
  284. package/dist/types/src/components/GrKbd/index.d.ts +6 -0
  285. package/dist/types/src/components/GrLink/defaults.d.ts +1 -1
  286. package/dist/types/src/components/GrModal/defaults.d.ts +1 -1
  287. package/dist/types/src/components/GrNumberInput/defaults.d.ts +1 -1
  288. package/dist/types/src/components/GrPagination/defaults.d.ts +1 -1
  289. package/dist/types/src/components/GrPopover/GrPopover.vue.d.ts +1 -1
  290. package/dist/types/src/components/GrPopover/defaults.d.ts +1 -1
  291. package/dist/types/src/components/GrProgressBar/defaults.d.ts +1 -1
  292. package/dist/types/src/components/GrProgressCircle/defaults.d.ts +1 -1
  293. package/dist/types/src/components/GrProgressCircle/grProgressCircleStyles.d.ts +1 -1
  294. package/dist/types/src/components/GrPromptDialog/defaults.d.ts +1 -1
  295. package/dist/types/src/components/GrRadio/defaults.d.ts +1 -1
  296. package/dist/types/src/components/GrRadio/grRadioGroupContext.d.ts +12 -4
  297. package/dist/types/src/components/GrRadioGroup/defaults.d.ts +1 -1
  298. package/dist/types/src/components/GrRating/GrRating.vue.d.ts +9 -4
  299. package/dist/types/src/components/GrRating/defaults.d.ts +1 -1
  300. package/dist/types/src/components/GrSegmented/defaults.d.ts +1 -1
  301. package/dist/types/src/components/GrSelect/defaults.d.ts +1 -1
  302. package/dist/types/src/components/GrSlider/defaults.d.ts +1 -1
  303. package/dist/types/src/components/GrSortableList/GrSortableList.vue.d.ts +68 -0
  304. package/dist/types/src/components/GrSortableList/config.d.ts +1 -0
  305. package/dist/types/src/components/GrSortableList/grSortableListStyles.d.ts +37 -0
  306. package/dist/types/src/components/GrSortableList/index.d.ts +9 -0
  307. package/dist/types/src/components/GrSortableList/safelist.d.ts +1 -0
  308. package/dist/types/src/components/GrSplitter/defaults.d.ts +1 -1
  309. package/dist/types/src/components/GrStatistic/GrStatistic.vue.d.ts +12 -9
  310. package/dist/types/src/components/GrStatistic/defaults.d.ts +1 -1
  311. package/dist/types/src/components/GrStatistic/grStatisticStyles.d.ts +1 -2
  312. package/dist/types/src/components/GrSwitch/defaults.d.ts +1 -1
  313. package/dist/types/src/components/GrTable/GrTable.vue.d.ts +13 -0
  314. package/dist/types/src/components/GrTable/defaults.d.ts +1 -1
  315. package/dist/types/src/components/GrTabs/GrTabs.vue.d.ts +6 -2
  316. package/dist/types/src/components/GrTabs/defaults.d.ts +1 -1
  317. package/dist/types/src/components/GrTextarea/defaults.d.ts +1 -1
  318. package/dist/types/src/components/GrTextarea/grTextareaStyles.d.ts +2 -2
  319. package/dist/types/src/components/GrTimeline/defaults.d.ts +1 -1
  320. package/dist/types/src/components/GrTimeline/grTimelineStyles.d.ts +1 -1
  321. package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +8 -0
  322. package/dist/types/src/components/GrToaster/grToasterStyles.d.ts +27 -0
  323. package/dist/types/src/components/GrTooltip/defaults.d.ts +1 -1
  324. package/dist/types/src/components/GrTree/defaults.d.ts +1 -1
  325. package/dist/types/src/components/GrTree/grTreeInteractionContext.d.ts +0 -1
  326. package/dist/types/src/components/GrTree/grTreeProps.d.ts +11 -4
  327. package/dist/types/src/components/GrTree/safelist.d.ts +1 -1
  328. package/dist/types/src/components/GrTreeSelect/defaults.d.ts +1 -1
  329. package/dist/types/src/components/shared/hotkey.d.ts +7 -15
  330. package/dist/types/src/components/shared/hotkeyTokens.d.ts +37 -0
  331. package/dist/types/src/components/shared/icon.d.ts +13 -0
  332. package/dist/types/src/composables/internal/dragSortGeometry.d.ts +59 -0
  333. package/dist/types/src/composables/internal/overlayStack.d.ts +11 -0
  334. package/dist/types/src/composables/useDragGesture.d.ts +63 -0
  335. package/dist/types/src/composables/useDragSort.d.ts +80 -0
  336. package/dist/types/src/composables/useFloating.d.ts +5 -1
  337. package/dist/types/src/composables/useGrComponentConfig.d.ts +41 -0
  338. package/dist/types/src/composables/useGranularityTranslations.d.ts +13 -0
  339. package/dist/types/src/composables/useRovingFocus.d.ts +108 -0
  340. package/dist/types/src/granular-provider/shared.d.ts +1 -0
  341. package/dist/types/src/i18n/messages/en.d.ts +48 -0
  342. package/dist/types/src/i18n/messages/es.d.ts +48 -0
  343. package/dist/types/src/i18n/messages/ru.d.ts +48 -0
  344. package/dist/types/src/index.d.ts +12 -0
  345. package/dist/types/src/internal/keyboard.d.ts +6 -0
  346. package/dist/types/src/testing/announcer.d.ts +10 -0
  347. package/dist/types/src/testing/context.d.ts +36 -0
  348. package/dist/types/src/testing/env.d.ts +25 -0
  349. package/dist/types/src/testing/geometry.d.ts +23 -0
  350. package/dist/types/src/testing/index.d.ts +12 -0
  351. package/dist/types/src/testing/pointer.d.ts +31 -0
  352. package/dist/types/src/theme/apply.d.ts +27 -0
  353. package/dist/types/src/theme/color.d.ts +35 -0
  354. package/dist/types/src/theme/extendTheme.d.ts +63 -0
  355. package/dist/types/src/theme/index.d.ts +22 -0
  356. package/dist/types/src/theme/roles.d.ts +16 -0
  357. package/dist/types/src/theme/tone.d.ts +32 -0
  358. package/dist/types/src/theme/validate.d.ts +16 -0
  359. package/dist/types/src/tokens/types.d.ts +8 -0
  360. package/dist/web-types.json +214 -13
  361. package/package.json +47 -4
  362. package/tokens/themes/dark.json +2 -1
  363. package/dist/chunks/chevron-right-BfqMVpbB.js.map +0 -1
  364. package/dist/chunks/config-8Pcutu1j.js.map +0 -1
  365. package/dist/chunks/config-B-g6lT19.js.map +0 -1
  366. package/dist/chunks/config-BE6n20np.js.map +0 -1
  367. package/dist/chunks/config-BKvX0q10.js.map +0 -1
  368. package/dist/chunks/config-BNfQwyL4.js +0 -97
  369. package/dist/chunks/config-BNfQwyL4.js.map +0 -1
  370. package/dist/chunks/config-BoNR7_PD.js.map +0 -1
  371. package/dist/chunks/config-CN2ULSQL2.js.map +0 -1
  372. package/dist/chunks/config-CY0_6cMi.js.map +0 -1
  373. package/dist/chunks/config-Cw6TDzWv2.js.map +0 -1
  374. package/dist/chunks/config-D8tSR2uS.js.map +0 -1
  375. package/dist/chunks/config-DpqOnJiz2.js.map +0 -1
  376. package/dist/chunks/config-DxCTVBfx.js.map +0 -1
  377. package/dist/chunks/config-LGrZIdaZ.js +0 -21
  378. package/dist/chunks/config-LGrZIdaZ.js.map +0 -1
  379. package/dist/chunks/config-Rw9OyW70.js.map +0 -1
  380. package/dist/chunks/config-T3qdQBgU.js.map +0 -1
  381. package/dist/chunks/config-eXB87DOi2.js.map +0 -1
  382. package/dist/chunks/en-B3xMeswz.js.map +0 -1
  383. package/dist/chunks/es-CA-mkU--.js.map +0 -1
  384. package/dist/chunks/grKbdStyles-DAoxiytu.js +0 -31
  385. package/dist/chunks/grKbdStyles-DAoxiytu.js.map +0 -1
  386. package/dist/chunks/granular-provider-CtrXGa7c.js.map +0 -1
  387. package/dist/chunks/keyboard-zYvvyHd4.js.map +0 -1
  388. package/dist/chunks/optionFilter-BVY988pC.js.map +0 -1
  389. package/dist/chunks/ru-COWxgh2A.js.map +0 -1
  390. package/dist/chunks/useOverlayLayer-BupuP_hs.js.map +0 -1
  391. package/dist/components/GrAutocomplete/chunks/GrAutocomplete-CQ9T7Lly.js.map +0 -1
  392. package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-BCNGDS6k.js.map +0 -1
  393. package/dist/components/GrCommandPalette/chunks/GrCommandPalette-MWDYDPNC.js.map +0 -1
  394. package/dist/components/GrDataTable/chunks/GrDataTable-DVFho9Cj.js.map +0 -1
  395. package/dist/components/GrImageViewer/chunks/GrImageViewer-BeCnE8XQ.js.map +0 -1
  396. package/dist/components/GrInput/chunks/GrInput-DD2gYdqS.js.map +0 -1
  397. package/dist/components/GrInputTag/chunks/GrInputTag-DmNunixz.js.map +0 -1
  398. package/dist/components/GrKbd/chunks/GrKbd-DRFPvW3G.js.map +0 -1
  399. package/dist/components/GrPagination/chunks/GrPagination-B6N0QZBf.js.map +0 -1
  400. package/dist/components/GrProgressBar/chunks/GrProgressBar-BiHnaitu.js.map +0 -1
  401. package/dist/components/GrRadio/chunks/GrRadio-DksDro_U.js.map +0 -1
  402. package/dist/components/GrRadioGroup/chunks/GrRadioGroup-BOIhejE6.js.map +0 -1
  403. package/dist/components/GrRating/chunks/GrRating-JrHXz0Rv.js.map +0 -1
  404. package/dist/components/GrSegmented/chunks/GrSegmented-BQIlH5KI.js.map +0 -1
  405. package/dist/components/GrSelect/chunks/GrSelect-aqkPCqjd.js.map +0 -1
  406. package/dist/components/GrSlider/chunks/GrSlider-DkU9IQSX.js.map +0 -1
  407. package/dist/components/GrSplitter/chunks/GrSplitter-7To8jbJ8.js.map +0 -1
  408. package/dist/components/GrStatistic/chunks/GrStatistic-CttyoYPT.js.map +0 -1
  409. package/dist/components/GrTable/chunks/GrTable-BB-mI_NM.js.map +0 -1
  410. package/dist/components/GrTabs/chunks/GrTabs-B8CDq2D9.js.map +0 -1
  411. package/dist/components/GrToaster/chunks/GrToaster-BKQy3Srm.js.map +0 -1
  412. package/dist/components/GrTree/chunks/GrTree-wfdbvygw.js.map +0 -1
  413. package/dist/components/GrTreeSelect/chunks/GrTreeSelect-lLWmAMtI.js.map +0 -1
  414. package/dist/tokens.js.map +0 -1
  415. package/dist/chunks/{config-8Pcutu1j.js → config-8Pcutu1j2.js} +0 -0
  416. package/dist/chunks/{config-BE6n20np.js → config-BE6n20np2.js} +0 -0
  417. package/dist/chunks/{config-BKvX0q10.js → config-BKvX0q102.js} +0 -0
  418. package/dist/chunks/{config-BoNR7_PD.js → config-BoNR7_PD2.js} +0 -0
  419. package/dist/chunks/{config-CN2ULSQL2.js → config-CN2ULSQL.js} +0 -0
  420. package/dist/chunks/{config-CY0_6cMi.js → config-CY0_6cMi2.js} +0 -0
  421. package/dist/chunks/{config-Rw9OyW70.js → config-Rw9OyW702.js} +0 -0
  422. package/dist/chunks/{config-T3qdQBgU.js → config-T3qdQBgU2.js} +0 -0
  423. package/dist/chunks/{config-eXB87DOi2.js → config-eXB87DOi.js} +0 -0
  424. package/dist/components/GrCollapse/chunks/{GrCollapse-B2Fc6A9r.js → GrCollapse-Kk0DE8tb.js} +1 -1
  425. /package/dist/types/src/composables/{internal/useComboboxNavigation.d.ts → useComboboxNavigation.d.ts} +0 -0
@@ -26,7 +26,8 @@ var avatar = {
26
26
  var tree = {
27
27
  "drag": "Перетащить",
28
28
  "expand": "Развернуть",
29
- "collapse": "Свернуть"
29
+ "collapse": "Свернуть",
30
+ "moved": "{label} перемещён"
30
31
  };
31
32
  var select = {
32
33
  "customValuePlaceholder": "Добавить значение…",
@@ -44,7 +45,11 @@ var dataTable = {
44
45
  "selectRow": "Выбрать строку",
45
46
  "sortAsc": "Нажмите для сортировки по возрастанию",
46
47
  "sortDesc": "Нажмите для сортировки по убыванию",
47
- "sortNone": "Нажмите, чтобы снять сортировку"
48
+ "sortNone": "Нажмите, чтобы снять сортировку",
49
+ "moveColumn": "Переставить колонку {label}",
50
+ "columnMoved": "Колонка {label} перемещена на позицию {position} из {count}",
51
+ "resizeColumn": "Ширина колонки {label}",
52
+ "columnResized": "Ширина колонки {label} — {width} пикселей"
48
53
  };
49
54
  var breadcrumbs = {
50
55
  "label": "Навигационная цепочка",
@@ -251,7 +256,17 @@ var kbd = {
251
256
  "shift": "Shift",
252
257
  "control": "Control",
253
258
  "enter": "Enter",
254
- "escape": "Escape"
259
+ "escape": "Escape",
260
+ "tab": "Tab",
261
+ "backspace": "Backspace",
262
+ "delete": "Delete",
263
+ "pageUp": "Page Up",
264
+ "pageDown": "Page Down",
265
+ "arrowUp": "стрелка вверх",
266
+ "arrowDown": "стрелка вниз",
267
+ "arrowLeft": "стрелка влево",
268
+ "arrowRight": "стрелка вправо",
269
+ "then": "затем"
255
270
  };
256
271
  var link = { "opensInNewTab": "откроется в новой вкладке" };
257
272
  var colorPicker = {
@@ -278,6 +293,15 @@ var bottomNav = {
278
293
  "badge": "{count} новых"
279
294
  };
280
295
  var emptyState = { "title": "Пока пусто" };
296
+ var sortable = {
297
+ "label": "Сортируемый список",
298
+ "handle": "Переставить",
299
+ "empty": "Пока нечего сортировать",
300
+ "grabbed": "Взято. Позиция {position} из {count}",
301
+ "moved": "Позиция {position} из {count}",
302
+ "dropped": "Отпущено на позиции {position} из {count}",
303
+ "cancelled": "Перенос отменён"
304
+ };
281
305
  var ru_default = {
282
306
  common,
283
307
  loading,
@@ -325,7 +349,8 @@ var ru_default = {
325
349
  timeline,
326
350
  tabs,
327
351
  bottomNav,
328
- emptyState
352
+ emptyState,
353
+ sortable
329
354
  };
330
355
  //#endregion
331
- export { autocomplete, avatar, badgeWrap, bottomNav, breadcrumbs, button, collapse, colorPicker, colors, commandPalette, common, dataTable, ru_default as default, dialog, drawer, emptyState, fileUpload, fileValidation, form, formErrorBanner, formFile, imageViewer, input, inputTag, kbd, link, list, loading, modal, navbar, numberInput, pagination, rating, responseError, select, sidebar, slider, splitter, statistic, table, tabs, textarea, timeline, toaster, tree, treeSelect, uploadErrorBanner };
356
+ export { autocomplete, avatar, badgeWrap, bottomNav, breadcrumbs, button, collapse, colorPicker, colors, commandPalette, common, dataTable, ru_default as default, dialog, drawer, emptyState, fileUpload, fileValidation, form, formErrorBanner, formFile, imageViewer, input, inputTag, kbd, link, list, loading, modal, navbar, numberInput, pagination, rating, responseError, select, sidebar, slider, sortable, splitter, statistic, table, tabs, textarea, timeline, toaster, tree, treeSelect, uploadErrorBanner };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ru-9z8ix45c.js","names":[],"sources":["../../src/i18n/locales/ru.json"],"sourcesContent":["{\n \"common\": {\n \"confirm\": \"Подтвердить\",\n \"cancel\": \"Отмена\",\n \"add\": \"Добавить\",\n \"clear\": \"Очистить\",\n \"close\": \"Закрыть\"\n },\n \"loading\": {\n \"defaultText\": \"Загрузка...\"\n },\n \"treeSelect\": {\n \"filterPlaceholder\": \"Поиск…\",\n \"empty\": \"Нет данных\",\n \"loading\": \"Загрузка…\"\n },\n \"badgeWrap\": {\n \"count\": \"{count} новых\"\n },\n \"avatar\": {\n \"group\": \"Участники\",\n \"groupOverflow\": \"и ещё {count}\",\n \"status\": {\n \"online\": \"в сети\",\n \"offline\": \"не в сети\",\n \"busy\": \"занят\",\n \"away\": \"отошёл\"\n }\n },\n \"tree\": {\n \"drag\": \"Перетащить\",\n \"expand\": \"Развернуть\",\n \"collapse\": \"Свернуть\",\n \"moved\": \"{label} перемещён\"\n },\n \"select\": {\n \"customValuePlaceholder\": \"Добавить значение…\",\n \"addOption\": \"Добавить «{value}»\",\n \"clearOption\": \"Не выбрано\",\n \"searchPlaceholder\": \"Поиск…\",\n \"loading\": \"Загрузка…\",\n \"noResults\": \"Ничего не найдено\",\n \"removeTag\": \"Удалить {label}\"\n },\n \"dataTable\": {\n \"loading\": \"Загрузка…\",\n \"empty\": \"Нет данных\",\n \"selectAll\": \"Выбрать все строки\",\n \"selectRow\": \"Выбрать строку\",\n \"sortAsc\": \"Нажмите для сортировки по возрастанию\",\n \"sortDesc\": \"Нажмите для сортировки по убыванию\",\n \"sortNone\": \"Нажмите, чтобы снять сортировку\",\n \"moveColumn\": \"Переставить колонку {label}\",\n \"columnMoved\": \"Колонка {label} перемещена на позицию {position} из {count}\",\n \"resizeColumn\": \"Ширина колонки {label}\",\n \"columnResized\": \"Ширина колонки {label} — {width} пикселей\"\n },\n \"breadcrumbs\": {\n \"label\": \"Навигационная цепочка\",\n \"expand\": \"Показать скрытые уровни\"\n },\n \"autocomplete\": {\n \"loading\": \"Загрузка…\",\n \"noResults\": \"Ничего не найдено\",\n \"addOption\": \"Добавить «{value}»\",\n \"removeValue\": \"Убрать {label}\",\n \"typeMore\": {\n \"one\": \"Введите минимум {n} символ\",\n \"few\": \"Введите минимум {n} символа\",\n \"many\": \"Введите минимум {n} символов\",\n \"other\": \"Введите минимум {n} символа\"\n }\n },\n \"form\": {\n \"required\": \"Обязательное поле\",\n \"min\": \"Минимум {min}\",\n \"max\": \"Максимум {max}\",\n \"len\": \"Длина должна быть {len}\",\n \"pattern\": \"Неверный формат\",\n \"email\": \"Введите корректный e-mail\",\n \"url\": \"Введите корректный URL\",\n \"file\": \"Некорректный файл\",\n \"invalid\": \"Некорректное значение\",\n \"readonly\": \"Только для чтения\",\n \"validating\": \"Проверяем…\"\n },\n \"dialog\": {\n \"confirm\": {\n \"title\": \"Подтверждение\"\n },\n \"prompt\": {\n \"title\": \"Введите значение\",\n \"label\": \"Значение\",\n \"required\": \"Введите значение.\"\n },\n \"ok\": \"ОК\"\n },\n \"switch\": {\n \"loading\": \"Сохраняем…\"\n },\n \"slider\": {\n \"min\": \"минимум\",\n \"max\": \"максимум\"\n },\n \"sidebar\": {\n \"expand\": \"Развернуть панель\",\n \"collapse\": \"Свернуть панель\"\n },\n \"splitter\": {\n \"label\": \"Изменить размер панелей\"\n },\n \"statistic\": {\n \"trendUp\": \"Рост\",\n \"trendDown\": \"Падение\",\n \"trendFlat\": \"Без изменений\"\n },\n \"pagination\": {\n \"label\": \"Пагинация\",\n \"pageSize\": \"Размер страницы\",\n \"prev\": \"Назад\",\n \"next\": \"Вперёд\",\n \"page\": \"Страница {n}\",\n \"first\": \"Первая страница\",\n \"last\": \"Последняя страница\",\n \"jumpTo\": \"Перейти к\",\n \"total\": \"{from}–{to} из {total}\",\n \"status\": \"Страница {page} из {count}\"\n },\n \"navbar\": {\n \"openMenu\": \"Открыть меню\"\n },\n \"responseError\": {\n \"networkTitle\": \"Нет связи с сервером\",\n \"networkMessage\": \"Проверьте подключение к интернету и попробуйте ещё раз.\",\n \"abortedTitle\": \"Запрос отменён\",\n \"abortedMessage\": \"Запрос был отменён.\",\n \"validationTitle\": \"Проверьте данные\",\n \"validationMessage\": \"Сервер вернул ошибку валидации. Исправьте поля и попробуйте снова.\",\n \"clientTitle\": \"Ошибка запроса\",\n \"clientMessage\": \"Сервер отклонил запрос.\",\n \"serverTitle\": \"Ошибка сервера\",\n \"serverMessage\": \"Произошла ошибка на сервере. Попробуйте ещё раз.\",\n \"unknownTitle\": \"Что-то пошло не так\",\n \"unknownMessage\": \"Не удалось выполнить операцию. Попробуйте ещё раз.\",\n \"statusLabel\": \"HTTP {status}\",\n \"retryLabel\": \"Повторить\",\n \"dismissLabel\": \"Скрыть\"\n },\n \"colors\": {\n \"neutral\": \"Нейтральный\",\n \"success\": \"Успех\",\n \"warning\": \"Предупреждение\",\n \"danger\": \"Опасность\",\n \"info\": \"Инфо\",\n \"primary\": \"Primary\",\n \"slate\": \"Сланцевый\",\n \"azure\": \"Лазурный\"\n },\n \"drawer\": {\n \"title\": \"Панель\"\n },\n \"modal\": {\n \"title\": \"Диалог\"\n },\n \"numberInput\": {\n \"increase\": \"Увеличить\",\n \"decrease\": \"Уменьшить\"\n },\n \"toaster\": {\n \"dismiss\": \"Закрыть\",\n \"region\": \"Уведомления\"\n },\n \"inputTag\": {\n \"removeTag\": \"Удалить тег\",\n \"removeTagNamed\": \"Удалить тег {tag}\",\n \"clearAll\": \"Удалить все теги\",\n \"added\": \"Добавлен тег: {tag}\",\n \"addedMany\": \"Добавлено тегов: {count}\",\n \"removed\": \"Удалён тег: {tag}\",\n \"cleared\": \"Все теги удалены\",\n \"limitReached\": \"Достигнут предел тегов\"\n },\n \"fileUpload\": {\n \"placeholder\": \"Перетащите файлы сюда или нажмите для выбора\",\n \"progress\": \"Прогресс загрузки\",\n \"uploading\": \"Загрузка…\",\n \"success\": \"Загрузка завершена\",\n \"error\": \"Не удалось загрузить\",\n \"remove\": \"Убрать {fileName}\",\n \"retry\": \"Повторить загрузку\",\n \"retryFile\": \"Повторить {fileName}\",\n \"abortFile\": \"Отменить загрузку {fileName}\",\n \"statusPending\": \"В очереди\",\n \"statusUploading\": \"Загружается\",\n \"statusSuccess\": \"Загружен\",\n \"statusError\": \"Ошибка\"\n },\n \"formFile\": {\n \"upload\": \"Загрузить файл\",\n \"change\": \"Заменить файл\",\n \"remove\": \"Удалить\",\n \"clearAll\": \"Очистить всё\",\n \"placeholder\": \"Файлы не выбраны\",\n \"validateError\": \"Не удалось проверить перетащенные файлы\",\n \"removeFile\": \"Убрать {fileName}\"\n },\n \"imageViewer\": {\n \"close\": \"Закрыть просмотрщик\",\n \"prev\": \"Предыдущее изображение\",\n \"next\": \"Следующее изображение\",\n \"zoomIn\": \"Увеличить\",\n \"zoomOut\": \"Уменьшить\",\n \"resetZoom\": \"Сбросить масштаб\",\n \"zoomToNatural\": \"Натуральный размер (1:1)\",\n \"rotateLeft\": \"Повернуть влево\",\n \"rotateRight\": \"Повернуть вправо\",\n \"download\": \"Скачать изображение\",\n \"empty\": \"Нет изображения\",\n \"label\": \"Просмотр изображений\",\n \"position\": \"Изображение {index} из {total}\"\n },\n \"formErrorBanner\": {\n \"validationTitle\": \"Проверьте поля формы\",\n \"validationMessage\": \"Сервер отклонил данные. Исправьте выделенные поля и попробуйте снова.\",\n \"clientTitle\": \"Не удалось сохранить\",\n \"serverTitle\": \"Ошибка сохранения\",\n \"serverMessage\": \"Не удалось сохранить данные. Попробуйте позже.\"\n },\n \"uploadErrorBanner\": {\n \"networkTitle\": \"Не удалось загрузить файлы\",\n \"networkMessage\": \"Проверьте подключение и попробуйте снова.\",\n \"validationTitle\": \"Файлы не прошли проверку\",\n \"validationMessage\": \"Некоторые файлы не соответствуют требованиям.\",\n \"serverTitle\": \"Ошибка загрузки на сервере\",\n \"serverMessage\": \"Сервер не смог обработать загрузку. Попробуйте снова.\"\n },\n \"input\": {\n \"clear\": \"Очистить\",\n \"showPassword\": \"Показать пароль\",\n \"hidePassword\": \"Скрыть пароль\",\n \"limitReached\": \"Достигнут предел символов\"\n },\n \"textarea\": {\n \"lines\": {\n \"one\": \"{n} строка\",\n \"few\": \"{n} строки\",\n \"many\": \"{n} строк\",\n \"other\": \"{n} строки\"\n }\n },\n \"rating\": {\n \"label\": \"Оценка\",\n \"valueText\": \"{value} из {max}\",\n \"valueTextWithLabel\": \"{value} из {max}, {label}\"\n },\n \"commandPalette\": {\n \"label\": \"Палитра команд\",\n \"placeholder\": \"Введите команду или запрос…\",\n \"empty\": \"Ничего не найдено\",\n \"recent\": \"Недавние\",\n \"loading\": \"Загрузка…\"\n },\n \"fileValidation\": {\n \"accept\": \"Файл «{fileName}» не подходит под accept=\\\"{accept}\\\"\",\n \"extension\": \"У файла «{fileName}» недопустимое расширение «.{extension}»\",\n \"mimeType\": \"У файла «{fileName}» недопустимый тип «{mimeType}»\",\n \"mimeTypeFallback\": \"У файла «{fileName}» неопределённый тип «{mimeType}»\",\n \"maxFileSize\": \"Файл «{fileName}» слишком большой ({size} байт), максимум {maxBytes}\",\n \"maxCount\": \"Выбрано слишком много файлов ({count}), максимум — {maxCount}\",\n \"maxTotalSize\": \"Суммарный размер файлов слишком большой ({total} байт), максимум {maxBytes}\"\n },\n \"kbd\": {\n \"command\": \"Command\",\n \"option\": \"Option\",\n \"shift\": \"Shift\",\n \"control\": \"Control\",\n \"enter\": \"Enter\",\n \"escape\": \"Escape\",\n \"tab\": \"Tab\",\n \"backspace\": \"Backspace\",\n \"delete\": \"Delete\",\n \"pageUp\": \"Page Up\",\n \"pageDown\": \"Page Down\",\n \"arrowUp\": \"стрелка вверх\",\n \"arrowDown\": \"стрелка вниз\",\n \"arrowLeft\": \"стрелка влево\",\n \"arrowRight\": \"стрелка вправо\",\n \"then\": \"затем\"\n },\n \"link\": {\n \"opensInNewTab\": \"откроется в новой вкладке\"\n },\n \"colorPicker\": {\n \"panelLabel\": \"Выбор цвета\",\n \"hue\": \"Оттенок\",\n \"saturation\": \"Насыщенность\",\n \"lightness\": \"Светлота\",\n \"opacity\": \"Непрозрачность\",\n \"hexLabel\": \"Значение hex\",\n \"presetsLabel\": \"Палитра\",\n \"swatchLabel\": \"Цвет {color}\"\n },\n \"collapse\": {\n \"empty\": \"Пока пусто\"\n },\n \"list\": {\n \"empty\": \"Пока пусто\"\n },\n \"button\": {\n \"loading\": \"Загрузка…\"\n },\n \"table\": {\n \"empty\": \"Пока пусто\"\n },\n \"timeline\": {\n \"empty\": \"Пока пусто\"\n },\n \"tabs\": {\n \"empty\": \"Пока пусто\",\n \"close\": \"Закрыть вкладку\"\n },\n \"bottomNav\": {\n \"label\": \"Нижняя навигация\",\n \"badge\": \"{count} новых\"\n },\n \"emptyState\": {\n \"title\": \"Пока пусто\"\n },\n \"sortable\": {\n \"label\": \"Сортируемый список\",\n \"handle\": \"Переставить\",\n \"empty\": \"Пока нечего сортировать\",\n \"grabbed\": \"Взято. Позиция {position} из {count}\",\n \"moved\": \"Позиция {position} из {count}\",\n \"dropped\": \"Отпущено на позиции {position} из {count}\",\n \"cancelled\": \"Перенос отменён\"\n }\n}\n"],"mappings":""}
@@ -0,0 +1,265 @@
1
+ import { useDragGesture } from "../composables/useDragGesture.js";
2
+ import { computed, onScopeDispose, shallowRef } from "vue";
3
+ //#region src/composables/internal/dragSortGeometry.ts
4
+ /**
5
+ * Какой элемент под указателем и в каком месте его.
6
+ *
7
+ * Промах не отбрасывается: между строками есть зазоры, а курсор уезжает за
8
+ * край списка — и в обоих случаях перенос обязан продолжать показывать
9
+ * ближайшую цель, иначе индикатор мигает на каждом пикселе между строками.
10
+ *
11
+ * Вырожденный отрезок (нулевая высота строки) даёт `fraction: 0` — цель
12
+ * остаётся детерминированной, а не зависящей от направления подхода.
13
+ */
14
+ function hitTest(spans, position) {
15
+ if (spans.length === 0) return null;
16
+ let nearestIndex = 0;
17
+ let nearestDistance = Number.POSITIVE_INFINITY;
18
+ for (let index = 0; index < spans.length; index += 1) {
19
+ const span = spans[index];
20
+ if (position >= span.start && position <= span.end) return {
21
+ index,
22
+ fraction: fractionIn(span, position)
23
+ };
24
+ const distance = position < span.start ? span.start - position : position - span.end;
25
+ if (distance < nearestDistance) {
26
+ nearestDistance = distance;
27
+ nearestIndex = index;
28
+ }
29
+ }
30
+ const nearest = spans[nearestIndex];
31
+ return {
32
+ index: nearestIndex,
33
+ fraction: fractionIn(nearest, position)
34
+ };
35
+ }
36
+ function fractionIn(span, position) {
37
+ const size = span.end - span.start;
38
+ if (size <= 0) return 0;
39
+ return clamp((position - span.start) / size, 0, 1);
40
+ }
41
+ /**
42
+ * Куда встанет элемент, если отпустить сейчас.
43
+ *
44
+ * Верхняя половина строки — «до неё», нижняя — «после». Индекс возвращается
45
+ * **в терминах итогового массива**, то есть уже с поправкой на то, что
46
+ * перетаскиваемый элемент из него временно вынут: без поправки перенос вниз
47
+ * на одну позицию не двигал бы ничего.
48
+ */
49
+ function insertionIndex(hit, from, count) {
50
+ if (count <= 0) return 0;
51
+ const base = hit.fraction < .5 ? hit.index : hit.index + 1;
52
+ return clamp(from < base ? base - 1 : base, 0, count - 1);
53
+ }
54
+ /**
55
+ * На сколько прокрутить контейнер за кадр: отрицательное — к началу.
56
+ *
57
+ * Скорость растёт линейно по мере приближения к краю, а не включается
58
+ * ступенькой: ступенька на длинном списке ощущается как рывок и промахивается
59
+ * мимо цели.
60
+ */
61
+ function autoScrollDelta(viewport, position, options = {}) {
62
+ const zone = options.zone ?? 32;
63
+ const speed = options.speed ?? 12;
64
+ if (zone <= 0 || speed <= 0) return 0;
65
+ if (viewport.end - viewport.start <= zone * 2) return 0;
66
+ if (position < viewport.start || position > viewport.end) return 0;
67
+ const fromStart = position - viewport.start;
68
+ if (fromStart < zone) return -Math.ceil((zone - fromStart) / zone * speed);
69
+ const fromEnd = viewport.end - position;
70
+ if (fromEnd < zone) return Math.ceil((zone - fromEnd) / zone * speed);
71
+ return 0;
72
+ }
73
+ /**
74
+ * Перестановка без мутации входа: массив потребителя остаётся нетронутым,
75
+ * наружу уходит новый. `v-model` с мутацией исходного набора ломает и
76
+ * сравнение «было — стало», и историю в сторе потребителя.
77
+ */
78
+ function moveItem(items, from, to) {
79
+ const next = items.slice();
80
+ if (from === to || from < 0 || from >= items.length) return next;
81
+ const [moved] = next.splice(from, 1);
82
+ next.splice(clamp(to, 0, next.length), 0, moved);
83
+ return next;
84
+ }
85
+ function clamp(value, min, max) {
86
+ return Math.min(max, Math.max(min, value));
87
+ }
88
+ //#endregion
89
+ //#region src/composables/useDragSort.ts
90
+ function useDragSort(options) {
91
+ const source = shallowRef(null);
92
+ const target = shallowRef(null);
93
+ const mode = shallowRef(null);
94
+ const isActive = computed(() => mode.value !== null);
95
+ let pendingKey = null;
96
+ let originX = 0;
97
+ let originY = 0;
98
+ let lastPoint = null;
99
+ let scrollFrame = null;
100
+ const gesture = useDragGesture({
101
+ disabled: () => options.disabled?.() ?? false,
102
+ onStart: (event) => {
103
+ if (pendingKey === null) return false;
104
+ if (options.canDrag?.(pendingKey) === false) return false;
105
+ originX = event.clientX;
106
+ originY = event.clientY;
107
+ return true;
108
+ },
109
+ onMove: (event) => {
110
+ if (mode.value === null) {
111
+ if (Math.hypot(event.clientX - originX, event.clientY - originY) < (options.threshold?.() ?? 4)) return;
112
+ source.value = pendingKey;
113
+ mode.value = "pointer";
114
+ window.addEventListener("keydown", onWindowKeydown, true);
115
+ startScrolling();
116
+ notify();
117
+ }
118
+ event.preventDefault();
119
+ lastPoint = {
120
+ x: event.clientX,
121
+ y: event.clientY
122
+ };
123
+ updateTarget(lastPoint);
124
+ },
125
+ onEnd: () => {
126
+ if (mode.value === "pointer") commit();
127
+ else reset();
128
+ },
129
+ onCancel: reset
130
+ });
131
+ function isVertical() {
132
+ return (options.orientation?.() ?? "vertical") === "vertical";
133
+ }
134
+ function spanOf(rect) {
135
+ return isVertical() ? {
136
+ start: rect.top,
137
+ end: rect.bottom
138
+ } : {
139
+ start: rect.left,
140
+ end: rect.right
141
+ };
142
+ }
143
+ function notify() {
144
+ options.onUpdate?.(source.value, target.value, mode.value);
145
+ }
146
+ function updateTarget(point) {
147
+ const current = source.value;
148
+ if (current === null) return;
149
+ const rendered = [];
150
+ options.items().forEach((key, index) => {
151
+ const el = options.elementFor(key);
152
+ if (el) rendered.push({
153
+ key,
154
+ index,
155
+ span: spanOf(el.getBoundingClientRect())
156
+ });
157
+ });
158
+ const hit = hitTest(rendered.map((entry) => entry.span), isVertical() ? point.y : point.x);
159
+ const entry = hit === null ? null : rendered[hit.index];
160
+ const next = entry === null || hit === null ? null : options.resolveTarget({
161
+ key: entry.key,
162
+ index: entry.index,
163
+ fraction: hit.fraction
164
+ }, current, target.value);
165
+ if (next === target.value) return;
166
+ target.value = next;
167
+ notify();
168
+ }
169
+ /**
170
+ * Автопрокрутка живёт в своём кадре, а не в обработчике движения: у края
171
+ * указатель обычно стоит неподвижно, событий больше нет — а список должен
172
+ * продолжать ехать.
173
+ */
174
+ function tickScroll() {
175
+ scrollFrame = null;
176
+ const scroller = options.scroller?.();
177
+ const point = lastPoint;
178
+ if (!scroller || !point || mode.value !== "pointer") return;
179
+ const delta = autoScrollDelta(spanOf(scroller.getBoundingClientRect()), isVertical() ? point.y : point.x);
180
+ if (delta !== 0) {
181
+ if (isVertical()) scroller.scrollTop += delta;
182
+ else scroller.scrollLeft += delta;
183
+ updateTarget(point);
184
+ }
185
+ scrollFrame = requestAnimationFrame(tickScroll);
186
+ }
187
+ function startScrolling() {
188
+ if (scrollFrame !== null || !options.scroller?.()) return;
189
+ scrollFrame = requestAnimationFrame(tickScroll);
190
+ }
191
+ function stopScrolling() {
192
+ if (scrollFrame === null) return;
193
+ cancelAnimationFrame(scrollFrame);
194
+ scrollFrame = null;
195
+ }
196
+ function onWindowKeydown(event) {
197
+ if (event.key !== "Escape") return;
198
+ event.preventDefault();
199
+ gesture.stop(false);
200
+ }
201
+ function reset() {
202
+ stopScrolling();
203
+ window.removeEventListener("keydown", onWindowKeydown, true);
204
+ pendingKey = null;
205
+ lastPoint = null;
206
+ source.value = null;
207
+ target.value = null;
208
+ mode.value = null;
209
+ notify();
210
+ }
211
+ function commit() {
212
+ const from = source.value;
213
+ const to = target.value;
214
+ if (from !== null && to !== null) options.onDrop(from, to);
215
+ reset();
216
+ }
217
+ function startFrom(key) {
218
+ return (event) => {
219
+ if (mode.value === "keyboard") return;
220
+ pendingKey = key;
221
+ gesture.start(event);
222
+ };
223
+ }
224
+ function grab(key) {
225
+ if (options.disabled?.() || options.canDrag?.(key) === false) return;
226
+ if (mode.value !== null) return;
227
+ source.value = key;
228
+ target.value = null;
229
+ mode.value = "keyboard";
230
+ notify();
231
+ }
232
+ function setTarget(next) {
233
+ if (mode.value !== "keyboard") return;
234
+ target.value = next;
235
+ notify();
236
+ }
237
+ function drop() {
238
+ if (mode.value !== "keyboard") return;
239
+ commit();
240
+ }
241
+ function cancel() {
242
+ if (mode.value === "pointer") {
243
+ gesture.stop(false);
244
+ return;
245
+ }
246
+ if (mode.value === "keyboard") reset();
247
+ }
248
+ onScopeDispose(() => {
249
+ stopScrolling();
250
+ window.removeEventListener("keydown", onWindowKeydown, true);
251
+ });
252
+ return {
253
+ source,
254
+ target,
255
+ mode,
256
+ isActive,
257
+ startFrom,
258
+ grab,
259
+ setTarget,
260
+ drop,
261
+ cancel
262
+ };
263
+ }
264
+ //#endregion
265
+ export { insertionIndex as n, moveItem as r, useDragSort as t };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDragSort-0l47h4yb.js","names":[],"sources":["../../src/composables/internal/dragSortGeometry.ts","../../src/composables/useDragSort.ts"],"sourcesContent":["/**\n * Геометрия перестановки: чистая арифметика без DOM и без реактивности.\n *\n * Вынесена отдельно, потому что ошибка здесь проявляется не исключением, а\n * «элемент встал не туда» — поймать это можно только тестом на числах.\n * Композабл `useDragSort` сводит `DOMRect` к отрезкам вдоль оси и зовёт эти\n * функции; какой смысл у попадания, решает уже компонент.\n */\n\n/** Отрезок, занимаемый элементом вдоль оси переноса. */\nexport interface DragSortSpan {\n start: number\n end: number\n}\n\nexport interface DragSortHitResult {\n /** Индекс элемента в переданном наборе. */\n index: number\n /** Доля вдоль элемента: 0 — его начало, 1 — конец. */\n fraction: number\n}\n\n/**\n * Какой элемент под указателем и в каком месте его.\n *\n * Промах не отбрасывается: между строками есть зазоры, а курсор уезжает за\n * край списка — и в обоих случаях перенос обязан продолжать показывать\n * ближайшую цель, иначе индикатор мигает на каждом пикселе между строками.\n *\n * Вырожденный отрезок (нулевая высота строки) даёт `fraction: 0` — цель\n * остаётся детерминированной, а не зависящей от направления подхода.\n */\nexport function hitTest(spans: readonly DragSortSpan[], position: number): DragSortHitResult | null {\n if (spans.length === 0) return null\n\n let nearestIndex = 0\n let nearestDistance = Number.POSITIVE_INFINITY\n\n for (let index = 0; index < spans.length; index += 1) {\n const span = spans[index]\n\n if (position >= span.start && position <= span.end)\n return { index, fraction: fractionIn(span, position) }\n\n const distance = position < span.start ? span.start - position : position - span.end\n if (distance < nearestDistance) {\n nearestDistance = distance\n nearestIndex = index\n }\n }\n\n const nearest = spans[nearestIndex]\n\n return { index: nearestIndex, fraction: fractionIn(nearest, position) }\n}\n\nfunction fractionIn(span: DragSortSpan, position: number): number {\n const size = span.end - span.start\n if (size <= 0) return 0\n\n return clamp((position - span.start) / size, 0, 1)\n}\n\n/**\n * Куда встанет элемент, если отпустить сейчас.\n *\n * Верхняя половина строки — «до неё», нижняя — «после». Индекс возвращается\n * **в терминах итогового массива**, то есть уже с поправкой на то, что\n * перетаскиваемый элемент из него временно вынут: без поправки перенос вниз\n * на одну позицию не двигал бы ничего.\n */\nexport function insertionIndex(hit: DragSortHitResult, from: number, count: number): number {\n if (count <= 0) return 0\n\n const base = hit.fraction < 0.5 ? hit.index : hit.index + 1\n const adjusted = from < base ? base - 1 : base\n\n return clamp(adjusted, 0, count - 1)\n}\n\nexport interface AutoScrollOptions {\n /** Ширина чувствительной полосы у каждого края, px. По умолчанию 32. */\n zone?: number\n /** Скорость на самом краю, px за кадр. По умолчанию 12. */\n speed?: number\n}\n\n/**\n * На сколько прокрутить контейнер за кадр: отрицательное — к началу.\n *\n * Скорость растёт линейно по мере приближения к краю, а не включается\n * ступенькой: ступенька на длинном списке ощущается как рывок и промахивается\n * мимо цели.\n */\nexport function autoScrollDelta(viewport: DragSortSpan, position: number, options: AutoScrollOptions = {}): number {\n const zone = options.zone ?? 32\n const speed = options.speed ?? 12\n\n if (zone <= 0 || speed <= 0) return 0\n // Вьюпорт короче двух зон: полосы наложились бы и тянули список в обе стороны.\n if (viewport.end - viewport.start <= zone * 2) return 0\n\n if (position < viewport.start || position > viewport.end) return 0\n\n const fromStart = position - viewport.start\n if (fromStart < zone)\n return -Math.ceil(((zone - fromStart) / zone) * speed)\n\n const fromEnd = viewport.end - position\n if (fromEnd < zone)\n return Math.ceil(((zone - fromEnd) / zone) * speed)\n\n return 0\n}\n\n/**\n * Перестановка без мутации входа: массив потребителя остаётся нетронутым,\n * наружу уходит новый. `v-model` с мутацией исходного набора ломает и\n * сравнение «было — стало», и историю в сторе потребителя.\n */\nexport function moveItem<T>(items: readonly T[], from: number, to: number): T[] {\n const next = items.slice()\n if (from === to || from < 0 || from >= items.length) return next\n\n const [moved] = next.splice(from, 1)\n next.splice(clamp(to, 0, next.length), 0, moved)\n\n return next\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n","import type { Ref } from 'vue'\nimport { computed, onScopeDispose, shallowRef } from 'vue'\n\nimport type { DragSortSpan } from './internal/dragSortGeometry'\nimport { autoScrollDelta, hitTest } from './internal/dragSortGeometry'\nimport { useDragGesture } from './useDragGesture'\n\n/**\n * Модель перестановки: перенос элемента указателем и с клавиатуры.\n *\n * Композабл владеет **механикой** — сессией жеста поверх `useDragGesture`,\n * порогом начала, попаданием в элемент, автопрокруткой у краёв и состоянием\n * переноса. **Смысл** остаётся у компонента: что считать целью (индекс вставки\n * у списка, «до / внутрь / после» у дерева), какими клавишами это делается и\n * что сказать в живой регион.\n *\n * Граница проведена так не из вкуса: у списка цель переноса — число, у дерева —\n * пара «узел и зона», а `Space` в дереве уже занят чекбоксом. Общая у них\n * механика, а не семантика; обобщать семантику значило бы завести опцию на\n * каждое расхождение.\n *\n * Данные композабл не меняет: он сообщает «источник и цель», перестановку\n * делает потребитель (`moveItem` из `internal/dragSortGeometry` — если она\n * обычная).\n *\n * ```ts\n * const sort = useDragSort<string, number>({\n * items: () => keys.value,\n * elementFor: key => rowEls.get(key),\n * scroller: () => scrollerEl.value,\n * resolveTarget: (hit, source) => insertionIndex(hit, indexOf(source), keys.value.length),\n * onDrop: (source, to) => emit('move', indexOf(source), to),\n * })\n * ```\n */\n\nexport type DragSortOrientation = 'vertical' | 'horizontal'\n\n/** Чем ведётся перенос: указателем или с клавиатуры. */\nexport type DragSortMode = 'pointer' | 'keyboard'\n\nexport interface DragSortHit<TKey> {\n key: TKey\n index: number\n /** Доля вдоль элемента: 0 — начало, 1 — конец. Зоны нарезает компонент. */\n fraction: number\n}\n\nexport interface UseDragSortOptions<TKey, TTarget> {\n items: () => readonly TKey[]\n elementFor: (key: TKey) => HTMLElement | null | undefined\n /** По умолчанию `vertical`. */\n orientation?: () => DragSortOrientation\n /** Скроллер для автопрокрутки у краёв. Не задан — автопрокрутки нет. */\n scroller?: () => HTMLElement | null | undefined\n disabled?: () => boolean\n canDrag?: (key: TKey) => boolean\n /** Сдвиг, после которого нажатие становится переносом. По умолчанию 4px. */\n threshold?: () => number\n /**\n * Попадание указателя → цель переноса. `null` — сюда положить нельзя.\n *\n * Третьим аргументом приходит прежняя цель: цели сравниваются по ссылке, и\n * объектной цели (как «узел плюс зона» у дерева) стоит вернуть **ту же**\n * ссылку, пока смысл не изменился, — иначе подсветка и объявления будут\n * дёргаться на каждом движении указателя.\n */\n resolveTarget: (hit: DragSortHit<TKey>, source: TKey, previous: TTarget | null) => TTarget | null\n /** Перенос состоялся. */\n onDrop: (source: TKey, target: TTarget) => void\n /** Состояние изменилось: подсветка строк и объявления — на этом. */\n onUpdate?: (source: TKey | null, target: TTarget | null, mode: DragSortMode | null) => void\n}\n\nexport interface UseDragSortReturn<TKey, TTarget> {\n source: Ref<TKey | null>\n target: Ref<TTarget | null>\n mode: Ref<DragSortMode | null>\n isActive: Ref<boolean>\n /** Вешается на `@pointerdown` ручки или строки. */\n startFrom: (key: TKey) => (event: PointerEvent) => void\n /** Клавиатурный перенос: взять элемент. */\n grab: (key: TKey) => void\n /** Клавиатурный перенос: назначить цель (двигать ведёт компонент). */\n setTarget: (target: TTarget | null) => void\n /** Положить: если цель есть — состоится `onDrop`. */\n drop: () => void\n /** Отменить: ничего не переносится. */\n cancel: () => void\n}\n\nexport function useDragSort<TKey, TTarget>(\n options: UseDragSortOptions<TKey, TTarget>,\n): UseDragSortReturn<TKey, TTarget> {\n const source = shallowRef<TKey | null>(null)\n const target = shallowRef<TTarget | null>(null)\n const mode = shallowRef<DragSortMode | null>(null)\n const isActive = computed(() => mode.value !== null)\n\n // Нажатая, но ещё не потянутая строка: между `pointerdown` и порогом это\n // остаётся обычным кликом, и объявлять перенос рано.\n let pendingKey: TKey | null = null\n let originX = 0\n let originY = 0\n let lastPoint: { x: number, y: number } | null = null\n let scrollFrame: number | null = null\n\n const gesture = useDragGesture({\n disabled: () => options.disabled?.() ?? false,\n onStart: (event) => {\n if (pendingKey === null) return false\n if (options.canDrag?.(pendingKey) === false) return false\n\n originX = event.clientX\n originY = event.clientY\n\n return true\n },\n onMove: (event) => {\n if (mode.value === null) {\n const moved = Math.hypot(event.clientX - originX, event.clientY - originY)\n if (moved < (options.threshold?.() ?? 4)) return\n\n source.value = pendingKey\n mode.value = 'pointer'\n // Esc отменяет перенос, но `pointercancel` браузер при этом не шлёт —\n // клавишу приходится слушать самим, в capture, до слоёв приложения.\n window.addEventListener('keydown', onWindowKeydown, true)\n startScrolling()\n notify()\n }\n\n // Против выделения текста под курсором во время переноса.\n event.preventDefault()\n\n lastPoint = { x: event.clientX, y: event.clientY }\n updateTarget(lastPoint)\n },\n onEnd: () => {\n if (mode.value === 'pointer') commit()\n else reset()\n },\n onCancel: reset,\n })\n\n function isVertical(): boolean {\n return (options.orientation?.() ?? 'vertical') === 'vertical'\n }\n\n function spanOf(rect: DOMRect): DragSortSpan {\n return isVertical() ? { start: rect.top, end: rect.bottom } : { start: rect.left, end: rect.right }\n }\n\n function notify(): void {\n options.onUpdate?.(source.value, target.value, mode.value)\n }\n\n function updateTarget(point: { x: number, y: number }): void {\n const current = source.value\n if (current === null) return\n\n // Отрисованы не все: список может быть под окном отсечения, ветка дерева —\n // свёрнута. Попадание считается по тому, что на экране есть.\n const rendered: { key: TKey, index: number, span: DragSortSpan }[] = []\n\n options.items().forEach((key, index) => {\n const el = options.elementFor(key)\n if (el) rendered.push({ key, index, span: spanOf(el.getBoundingClientRect()) })\n })\n\n const hit = hitTest(rendered.map(entry => entry.span), isVertical() ? point.y : point.x)\n const entry = hit === null ? null : rendered[hit.index]\n const next = entry === null || hit === null\n ? null\n : options.resolveTarget({ key: entry.key, index: entry.index, fraction: hit.fraction }, current, target.value)\n\n if (next === target.value) return\n\n target.value = next\n notify()\n }\n\n /**\n * Автопрокрутка живёт в своём кадре, а не в обработчике движения: у края\n * указатель обычно стоит неподвижно, событий больше нет — а список должен\n * продолжать ехать.\n */\n function tickScroll(): void {\n scrollFrame = null\n\n const scroller = options.scroller?.()\n const point = lastPoint\n if (!scroller || !point || mode.value !== 'pointer') return\n\n const delta = autoScrollDelta(spanOf(scroller.getBoundingClientRect()), isVertical() ? point.y : point.x)\n\n if (delta !== 0) {\n if (isVertical()) scroller.scrollTop += delta\n else scroller.scrollLeft += delta\n\n updateTarget(point)\n }\n\n scrollFrame = requestAnimationFrame(tickScroll)\n }\n\n function startScrolling(): void {\n if (scrollFrame !== null || !options.scroller?.()) return\n\n scrollFrame = requestAnimationFrame(tickScroll)\n }\n\n function stopScrolling(): void {\n if (scrollFrame === null) return\n\n cancelAnimationFrame(scrollFrame)\n scrollFrame = null\n }\n\n function onWindowKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape') return\n\n event.preventDefault()\n gesture.stop(false)\n }\n\n function reset(): void {\n stopScrolling()\n window.removeEventListener('keydown', onWindowKeydown, true)\n\n pendingKey = null\n lastPoint = null\n source.value = null\n target.value = null\n mode.value = null\n notify()\n }\n\n function commit(): void {\n const from = source.value\n const to = target.value\n\n if (from !== null && to !== null) options.onDrop(from, to)\n\n reset()\n }\n\n function startFrom(key: TKey) {\n return (event: PointerEvent): void => {\n // Клавиатурный перенос идёт своим чередом: указатель его не перебивает.\n if (mode.value === 'keyboard') return\n\n pendingKey = key\n gesture.start(event)\n }\n }\n\n function grab(key: TKey): void {\n if (options.disabled?.() || options.canDrag?.(key) === false) return\n if (mode.value !== null) return\n\n source.value = key\n target.value = null\n mode.value = 'keyboard'\n notify()\n }\n\n function setTarget(next: TTarget | null): void {\n if (mode.value !== 'keyboard') return\n\n target.value = next\n notify()\n }\n\n function drop(): void {\n if (mode.value !== 'keyboard') return\n\n commit()\n }\n\n function cancel(): void {\n if (mode.value === 'pointer') {\n gesture.stop(false)\n return\n }\n\n if (mode.value === 'keyboard') reset()\n }\n\n onScopeDispose(() => {\n stopScrolling()\n window.removeEventListener('keydown', onWindowKeydown, true)\n })\n\n return { source, target, mode, isActive, startFrom, grab, setTarget, drop, cancel }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { c as useGrThemeAttrs } from "./context-BSYR7RZs.js";
2
- import { t as usePortalTarget } from "./usePortalTarget-RyzEgNTb.js";
2
+ import { t as usePortalTarget } from "./usePortalTarget-Cn-Wiz5d.js";
3
3
  import { n as markInert, t as inertableOutside } from "./inert-DIHCCn_1.js";
4
- import { t as useOverlayLayer } from "./useOverlayLayer-BupuP_hs.js";
4
+ import { useOverlayLayer } from "../composables/useOverlayLayer.js";
5
5
  import { useFocusTrap } from "../composables/useFocusTrap.js";
6
6
  import { computed, onBeforeUnmount, onScopeDispose, ref, watch } from "vue";
7
7
  //#region src/composables/internal/useInertOthers.ts
@@ -1 +1 @@
1
- {"version":3,"file":"useModalOverlay-DCM4d3yA.js","names":[],"sources":["../../src/composables/internal/useInertOthers.ts","../../src/composables/internal/useOverlayPresence.ts","../../src/composables/internal/useScrollLock.ts","../../src/composables/internal/useModalOverlay.ts"],"sourcesContent":["import type { Ref } from 'vue'\nimport { onScopeDispose, watch } from 'vue'\n\nimport { inertableOutside, markInert } from './inert'\n\n/**\n * Пока слой активен, всё вне его ветки помечено `inert`: от корня оверлея вверх\n * до `<body>`, на каждом уровне — соседи.\n *\n * Это вторая половина модальности: `z-index` перекрывает страницу глазами,\n * `inert` — клавиатурой и скринридером.\n */\nexport function useInertOthers(root: Ref<HTMLElement | null>, active: () => boolean): void {\n let release: (() => void) | undefined\n\n function apply(): void {\n release?.()\n release = markInert(inertableOutside(root.value))\n }\n\n function clear(): void {\n release?.()\n release = undefined\n }\n\n watch(\n () => Boolean(active()) && Boolean(root.value),\n (isActive) => {\n if (isActive) apply()\n else clear()\n },\n { immediate: true, flush: 'post' },\n )\n\n onScopeDispose(clear)\n}\n","import type { Ref } from 'vue'\nimport { ref, watch } from 'vue'\n\nexport interface OverlayPresence {\n /** Смонтировано ли поддерево слоя: держится до конца leave-анимации. */\n mounted: Ref<boolean>\n /** Показан ли слой: этим переключаются `<Transition>` внутри. */\n visible: Ref<boolean>\n /** Вызывается из `@after-leave` панели. */\n onPanelAfterLeave: () => void\n}\n\n/**\n * Присутствие оверлея в DOM отдельно от его видимости.\n *\n * Штатный `<Transition>` анимирует свой элемент, но не координирует соседей:\n * убери обёртку по `v-if` — и подложка с панелью исчезнут мгновенно, без\n * leave-анимации. Поэтому присутствие поддерева и видимость слоя — два разных\n * флага: первый снимается только по концу leave-анимации панели.\n *\n * Видимость включается **в том же такте**, что и монтирование: содержимое окна\n * обязано появляться в DOM сразу после открытия — на этом стоит и код\n * потребителя, и внутренние компоненты, которые ищут в панели свои элементы.\n * Enter-анимацию это не отменяет: `<Transition appear>` проигрывает её и на\n * первом рендере ребёнка.\n */\nexport function useOverlayPresence(open: Ref<boolean>): OverlayPresence {\n const mounted = ref(open.value)\n const visible = ref(open.value)\n\n watch(open, (isOpen) => {\n if (isOpen) mounted.value = true\n visible.value = isOpen\n })\n\n function onPanelAfterLeave(): void {\n // Пока панель уезжала, слой могли открыть заново — тогда размонтировать\n // нечего и незачем.\n if (!open.value) mounted.value = false\n }\n\n return { mounted, visible, onPanelAfterLeave }\n}\n","/**\n * useScrollLock — общий **reference-counted** lock скролла `<body>` для оверлеев\n * (GrModal, GrDrawer, GrImageViewer).\n *\n * Зачем счётчик: состояние `<body>` принадлежит не оверлею, а их совокупности.\n * Держи каждый свой `savedBodyOverflow`, и закрытие A при ещё открытой B вернуло\n * бы странице прокрутку под открытым окном — закрывают не всегда в порядке\n * открытия. Поэтому состояние сохраняется один раз (переход счётчика 0→1) и\n * восстанавливается только когда закрыт последний оверлей (1→0).\n *\n * Плюс компенсация ширины скроллбара: при `overflow: hidden` скроллбар исчезает и\n * контент дёргается вправо — добавляем `padding-right` на его ширину.\n *\n * Отдельная ветка — iOS Safari: там `overflow: hidden` фон не держит, страница\n * продолжает «резинить» под открытым окном. Лечится тем же, чем лечил\n * HeadlessUI (`handleIOSLocking`): гасим `touchmove` вне скроллящихся областей\n * оверлея и возвращаем позицию прокрутки после снятия лока.\n */\n\ninterface SavedBodyStyle {\n overflow: string\n paddingRight: string\n overscrollBehavior: string\n}\n\nlet lockCount = 0\nlet saved: SavedBodyStyle | null = null\nlet savedScrollY = 0\nlet releaseTouchGuard: (() => void) | null = null\n\n/**\n * iOS определяем по платформе, а не по ширине экрана: лечение специфично для\n * движка, а не для размера. `maxTouchPoints` отличает iPadOS, который со времён\n * 13-й версии представляется десктопным Mac.\n */\nfunction isIOS(): boolean {\n if (typeof navigator === 'undefined') return false\n return /iP(?:hone|ad|od)/.test(navigator.platform)\n || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)\n}\n\n/** Прокручиваемая область оверлея — единственное, чему `touchmove` разрешён. */\nfunction isScrollableOverlayArea(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n\n for (let node: Element | null = target; node; node = node.parentElement) {\n if (!(node instanceof HTMLElement)) continue\n if (node.hasAttribute('data-gr-overlay-root')) return true\n }\n\n return false\n}\n\nfunction applyTouchGuard(): void {\n if (!isIOS()) return\n\n const onTouchMove = (event: TouchEvent): void => {\n // Внутри оверлея скролл законен (длинное окно, лента изображений), а всё\n // остальное — это и есть фон, который лок обязан держать.\n if (isScrollableOverlayArea(event.target)) return\n if (event.cancelable) event.preventDefault()\n }\n\n document.addEventListener('touchmove', onTouchMove, { passive: false })\n releaseTouchGuard = () => document.removeEventListener('touchmove', onTouchMove)\n}\n\nfunction getScrollbarWidth(): number {\n return window.innerWidth - document.documentElement.clientWidth\n}\n\nfunction applyLock(): void {\n const body = document.body\n saved = {\n overflow: body.style.overflow,\n paddingRight: body.style.paddingRight,\n overscrollBehavior: body.style.overscrollBehavior,\n }\n savedScrollY = window.scrollY\n\n // Ширину скроллбара считаем ДО `overflow: hidden` (после — `clientWidth` меняется).\n const scrollbarWidth = getScrollbarWidth()\n\n body.style.overflow = 'hidden'\n // Прокрутка, «доехавшая» до края внутри окна, не должна продолжаться фоном.\n body.style.overscrollBehavior = 'contain'\n if (scrollbarWidth > 0) {\n const currentPadding = Number.parseFloat(getComputedStyle(body).paddingRight) || 0\n body.style.paddingRight = `${currentPadding + scrollbarWidth}px`\n }\n\n applyTouchGuard()\n}\n\nfunction releaseLock(): void {\n releaseTouchGuard?.()\n releaseTouchGuard = null\n\n if (!saved) return\n document.body.style.overflow = saved.overflow\n document.body.style.paddingRight = saved.paddingRight\n document.body.style.overscrollBehavior = saved.overscrollBehavior\n saved = null\n\n // iOS сдвигает страницу, пока фон заблокирован; возвращаем её на место.\n if (isIOS() && window.scrollY !== savedScrollY) window.scrollTo(0, savedScrollY)\n}\n\n/**\n * Возвращает пару `lock`/`unlock`, идемпотентную **в пределах одного инстанса**\n * (повторный `lock()` без `unlock()` не увеличивает глобальный счётчик дважды).\n * Глобальный счётчик разделяется всеми оверлеями.\n */\nexport function useScrollLock() {\n const isClient = typeof window !== 'undefined'\n let held = false\n\n function lock(): void {\n if (!isClient || held) return\n held = true\n lockCount += 1\n if (lockCount === 1) applyLock()\n }\n\n function unlock(): void {\n if (!isClient || !held) return\n held = false\n lockCount = Math.max(0, lockCount - 1)\n if (lockCount === 0) releaseLock()\n }\n\n return { lock, unlock }\n}\n\n/** Тестовая/служебная очистка глобального состояния. */\nexport function resetScrollLock(): void {\n lockCount = 0\n if (typeof document !== 'undefined') releaseLock()\n saved = null\n}\n","import type { ComputedRef, Ref } from 'vue'\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\n\nimport { useGrThemeAttrs } from '../../components/GrConfigProvider/context'\nimport { useFocusTrap } from '../useFocusTrap'\nimport { useOverlayLayer } from '../useOverlayLayer'\nimport { usePortalTarget } from '../usePortalTarget'\nimport { useInertOthers } from './useInertOthers'\nimport { useOverlayPresence } from './useOverlayPresence'\nimport { useScrollLock } from './useScrollLock'\n\n/**\n * Модальный слой целиком: одна сборка на весь пакет.\n *\n * Модальность собиралась вручную в трёх компонентах (`GrModal`, `GrDrawer`,\n * `GrImageViewer`) из шести примитивов, и сборка повторялась дословно. Дело не\n * в объёме: в этой проводке сидят инварианты, каждый из которых при пропуске\n * даёт тихий баг доступности, а не заметную поломку. Здесь они собраны в одном\n * месте — забыть часть модального слоя больше нельзя.\n *\n * Что именно неочевидно:\n *\n * - **`restoreFocus: false` у ловушки.** Возврат фокуса принадлежит стеку\n * слоёв: у него правило строже — вернуть, только если на момент закрытия\n * фокус ещё был внутри слоя. Две системы возврата подрались бы за фокус.\n * - **`containers: layer.rootsAbove`.** Панель селекта, открытого ВНУТРИ окна,\n * телепортирована в `body` и лежит вне поддерева панели. Без этого списка\n * ловушка считала бы её «снаружи» и отбирала фокус.\n * - **Ловушка и `inert` работают, только пока слой верхний.** Иначе нижнее\n * окно ворует фокус у открытого поверх него, а `inert` гасит собственную\n * панель вместе со страницей.\n * - **Клик по подложке считается только тот, что на ней и начался.** Иначе\n * выделение текста мышью или перетаскивание, отпущенное за пределами панели,\n * закрывает слой.\n *\n * Чего композабл НЕ делает: не рисует разметку. Корневой `div`, подложка и\n * панель у трёх компонентов различаются по существу (обёртки скролла у окна,\n * сквозные клики у немодальной панели, слои хрома у просмотрщика), а классами\n * общий модуль владеть не вправе — пресет гранулярности подмешивает safelist\n * только по директориям выбранных компонентов.\n */\nexport interface UseModalOverlayOptions {\n /**\n * Модальный ли слой: блокирует страницу, держит фокус, гасит нижние окна.\n * Геттером — там, где модальность включается пропом (`GrDrawer`).\n */\n modal?: boolean | (() => boolean)\n /** Закрывать ли по Esc. Геттер — проп компонента реактивен. */\n closeOnEscape?: () => boolean\n /** Закрывать ли по клику в подложку; читается обработчиками `backdrop`. */\n closeOnBackdrop?: () => boolean\n /** Панель слоя: контейнер ловушки фокуса и её запасной приёмник. */\n panel: Ref<HTMLElement | null>\n /**\n * Свой корень слоя. У якорного оверлея (`GrPopover`) корень и панель — один\n * элемент: в портал уезжает сама панель, отдельной обёртки нет. Не задан —\n * композабл заводит корень сам, как у центрированных окон.\n */\n root?: Ref<HTMLElement | null>\n /**\n * Точечное переопределение точки монтирования (проп `teleportTo`). По\n * умолчанию — общий портал оверлеев.\n */\n teleportTo?: () => string | HTMLElement | undefined\n /**\n * Кому отдать фокус при открытии. Не задан — первому фокусируемому внутри\n * панели (правило `useFocusTrap`): просмотрщику нужна именно кнопка тулбара,\n * а окну — сама панель, и оно передаёт её явно.\n */\n initialFocus?: () => HTMLElement | null | undefined\n /**\n * Блокировать ли скролл страницы. По умолчанию — пока слой открыт и модален:\n * немодальная панель существует ровно ради работы со страницей под ней.\n */\n lockScroll?: () => boolean\n}\n\nexport interface ModalOverlay {\n /** Корень слоя: цель `ref` в шаблоне, хозяин `inert` и корень для стека слоёв. */\n rootEl: Ref<HTMLElement | null>\n /** Смонтировано ли поддерево: держится до конца leave-анимации панели. */\n isMounted: Ref<boolean>\n /** Показан ли слой: этим переключаются `<Transition>` внутри. */\n isVisible: Ref<boolean>\n /** Верхний ли это слой сейчас. */\n isTopmost: Ref<boolean>\n /** Значение атрибута `inert` для корня: слой перестал быть верхним. */\n inertAttr: ComputedRef<true | undefined>\n portalTarget: ComputedRef<string | HTMLElement>\n teleportEnabled: ComputedRef<boolean>\n /** `data-theme` на телепортированный корень: в DOM он вне обёртки провайдера. */\n themeAttrs: ComputedRef<{ 'data-theme'?: string }>\n /** Для `@after-leave` панели. Компонент оборачивает, если ему нужен эмит `closed`. */\n onPanelAfterLeave: () => void\n /**\n * Обработчики подложки вместе с guard'ом «клик начался на ней». Имена — под\n * `v-on=\"backdrop\"`: объектный синтаксис ждёт имена событий, а не пропов.\n */\n backdrop: {\n mousedown: (event: MouseEvent) => void\n click: (event: MouseEvent) => void\n }\n}\n\nexport function useModalOverlay(\n open: Ref<boolean>,\n onDismiss: () => void,\n options: UseModalOverlayOptions,\n): ModalOverlay {\n const isModal = (): boolean =>\n typeof options.modal === 'function' ? options.modal() : options.modal ?? true\n\n const rootEl = options.root ?? ref<HTMLElement | null>(null)\n const isTopmost = ref(true)\n\n const inertAttr = computed(() => (open.value && isModal() && !isTopmost.value ? true : undefined))\n\n // Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n // рендер не совпадёт с серверным и сломается гидрация.\n const { target: portalTarget, enabled: teleportEnabled } = usePortalTarget(options.teleportTo)\n\n // Панель уезжает в `body`, то есть вне обёртки провайдера, и `data-theme` с\n // неё не наследуется. В дереве компонентов слой остаётся внутри — `inject`\n // доходит, и тему он ставит себе сам.\n const themeAttrs = useGrThemeAttrs()\n\n const { mounted: isMounted, visible: isVisible, onPanelAfterLeave } = useOverlayPresence(open)\n\n const layer = useOverlayLayer(open, onDismiss, {\n modal: isModal,\n closeOnEscape: options.closeOnEscape,\n onTopmostChange: (value) => { isTopmost.value = value },\n root: rootEl,\n })\n\n useFocusTrap(options.panel, {\n active: () => open.value && isModal() && isTopmost.value,\n initialFocus: options.initialFocus,\n fallbackFocus: () => options.panel.value,\n containers: layer.rootsAbove,\n restoreFocus: false,\n })\n\n useInertOthers(rootEl, () => open.value && isModal() && isTopmost.value)\n\n // Общий reference-counted lock: несколько открытых оверлеев снимают блокировку\n // независимо от порядка закрытия, ширина скроллбара компенсируется.\n const { lock, unlock } = useScrollLock()\n\n watch(\n () => options.lockScroll?.() ?? (open.value && isModal()),\n (locked) => {\n if (locked) lock()\n else unlock()\n },\n { immediate: true },\n )\n\n onBeforeUnmount(unlock)\n\n let pointerDownOnBackdrop = false\n\n return {\n rootEl,\n isMounted,\n isVisible,\n isTopmost,\n inertAttr,\n portalTarget,\n teleportEnabled,\n themeAttrs,\n onPanelAfterLeave,\n backdrop: {\n mousedown(event: MouseEvent): void {\n pointerDownOnBackdrop = event.target === event.currentTarget\n },\n click(event: MouseEvent): void {\n const startedHere = pointerDownOnBackdrop\n pointerDownOnBackdrop = false\n\n if (!startedHere || event.target !== event.currentTarget) return\n if (options.closeOnBackdrop?.() === false) return\n\n onDismiss()\n },\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useModalOverlay-DrQVYwwA.js","names":[],"sources":["../../src/composables/internal/useInertOthers.ts","../../src/composables/internal/useOverlayPresence.ts","../../src/composables/internal/useScrollLock.ts","../../src/composables/internal/useModalOverlay.ts"],"sourcesContent":["import type { Ref } from 'vue'\nimport { onScopeDispose, watch } from 'vue'\n\nimport { inertableOutside, markInert } from './inert'\n\n/**\n * Пока слой активен, всё вне его ветки помечено `inert`: от корня оверлея вверх\n * до `<body>`, на каждом уровне — соседи.\n *\n * Это вторая половина модальности: `z-index` перекрывает страницу глазами,\n * `inert` — клавиатурой и скринридером.\n */\nexport function useInertOthers(root: Ref<HTMLElement | null>, active: () => boolean): void {\n let release: (() => void) | undefined\n\n function apply(): void {\n release?.()\n release = markInert(inertableOutside(root.value))\n }\n\n function clear(): void {\n release?.()\n release = undefined\n }\n\n watch(\n () => Boolean(active()) && Boolean(root.value),\n (isActive) => {\n if (isActive) apply()\n else clear()\n },\n { immediate: true, flush: 'post' },\n )\n\n onScopeDispose(clear)\n}\n","import type { Ref } from 'vue'\nimport { ref, watch } from 'vue'\n\nexport interface OverlayPresence {\n /** Смонтировано ли поддерево слоя: держится до конца leave-анимации. */\n mounted: Ref<boolean>\n /** Показан ли слой: этим переключаются `<Transition>` внутри. */\n visible: Ref<boolean>\n /** Вызывается из `@after-leave` панели. */\n onPanelAfterLeave: () => void\n}\n\n/**\n * Присутствие оверлея в DOM отдельно от его видимости.\n *\n * Штатный `<Transition>` анимирует свой элемент, но не координирует соседей:\n * убери обёртку по `v-if` — и подложка с панелью исчезнут мгновенно, без\n * leave-анимации. Поэтому присутствие поддерева и видимость слоя — два разных\n * флага: первый снимается только по концу leave-анимации панели.\n *\n * Видимость включается **в том же такте**, что и монтирование: содержимое окна\n * обязано появляться в DOM сразу после открытия — на этом стоит и код\n * потребителя, и внутренние компоненты, которые ищут в панели свои элементы.\n * Enter-анимацию это не отменяет: `<Transition appear>` проигрывает её и на\n * первом рендере ребёнка.\n */\nexport function useOverlayPresence(open: Ref<boolean>): OverlayPresence {\n const mounted = ref(open.value)\n const visible = ref(open.value)\n\n watch(open, (isOpen) => {\n if (isOpen) mounted.value = true\n visible.value = isOpen\n })\n\n function onPanelAfterLeave(): void {\n // Пока панель уезжала, слой могли открыть заново — тогда размонтировать\n // нечего и незачем.\n if (!open.value) mounted.value = false\n }\n\n return { mounted, visible, onPanelAfterLeave }\n}\n","/**\n * useScrollLock — общий **reference-counted** lock скролла `<body>` для оверлеев\n * (GrModal, GrDrawer, GrImageViewer).\n *\n * Зачем счётчик: состояние `<body>` принадлежит не оверлею, а их совокупности.\n * Держи каждый свой `savedBodyOverflow`, и закрытие A при ещё открытой B вернуло\n * бы странице прокрутку под открытым окном — закрывают не всегда в порядке\n * открытия. Поэтому состояние сохраняется один раз (переход счётчика 0→1) и\n * восстанавливается только когда закрыт последний оверлей (1→0).\n *\n * Плюс компенсация ширины скроллбара: при `overflow: hidden` скроллбар исчезает и\n * контент дёргается вправо — добавляем `padding-right` на его ширину.\n *\n * Отдельная ветка — iOS Safari: там `overflow: hidden` фон не держит, страница\n * продолжает «резинить» под открытым окном. Лечится тем же, чем лечил\n * HeadlessUI (`handleIOSLocking`): гасим `touchmove` вне скроллящихся областей\n * оверлея и возвращаем позицию прокрутки после снятия лока.\n */\n\ninterface SavedBodyStyle {\n overflow: string\n paddingRight: string\n overscrollBehavior: string\n}\n\nlet lockCount = 0\nlet saved: SavedBodyStyle | null = null\nlet savedScrollY = 0\nlet releaseTouchGuard: (() => void) | null = null\n\n/**\n * iOS определяем по платформе, а не по ширине экрана: лечение специфично для\n * движка, а не для размера. `maxTouchPoints` отличает iPadOS, который со времён\n * 13-й версии представляется десктопным Mac.\n */\nfunction isIOS(): boolean {\n if (typeof navigator === 'undefined') return false\n return /iP(?:hone|ad|od)/.test(navigator.platform)\n || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)\n}\n\n/** Прокручиваемая область оверлея — единственное, чему `touchmove` разрешён. */\nfunction isScrollableOverlayArea(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n\n for (let node: Element | null = target; node; node = node.parentElement) {\n if (!(node instanceof HTMLElement)) continue\n if (node.hasAttribute('data-gr-overlay-root')) return true\n }\n\n return false\n}\n\nfunction applyTouchGuard(): void {\n if (!isIOS()) return\n\n const onTouchMove = (event: TouchEvent): void => {\n // Внутри оверлея скролл законен (длинное окно, лента изображений), а всё\n // остальное — это и есть фон, который лок обязан держать.\n if (isScrollableOverlayArea(event.target)) return\n if (event.cancelable) event.preventDefault()\n }\n\n document.addEventListener('touchmove', onTouchMove, { passive: false })\n releaseTouchGuard = () => document.removeEventListener('touchmove', onTouchMove)\n}\n\nfunction getScrollbarWidth(): number {\n return window.innerWidth - document.documentElement.clientWidth\n}\n\nfunction applyLock(): void {\n const body = document.body\n saved = {\n overflow: body.style.overflow,\n paddingRight: body.style.paddingRight,\n overscrollBehavior: body.style.overscrollBehavior,\n }\n savedScrollY = window.scrollY\n\n // Ширину скроллбара считаем ДО `overflow: hidden` (после — `clientWidth` меняется).\n const scrollbarWidth = getScrollbarWidth()\n\n body.style.overflow = 'hidden'\n // Прокрутка, «доехавшая» до края внутри окна, не должна продолжаться фоном.\n body.style.overscrollBehavior = 'contain'\n if (scrollbarWidth > 0) {\n const currentPadding = Number.parseFloat(getComputedStyle(body).paddingRight) || 0\n body.style.paddingRight = `${currentPadding + scrollbarWidth}px`\n }\n\n applyTouchGuard()\n}\n\nfunction releaseLock(): void {\n releaseTouchGuard?.()\n releaseTouchGuard = null\n\n if (!saved) return\n document.body.style.overflow = saved.overflow\n document.body.style.paddingRight = saved.paddingRight\n document.body.style.overscrollBehavior = saved.overscrollBehavior\n saved = null\n\n // iOS сдвигает страницу, пока фон заблокирован; возвращаем её на место.\n if (isIOS() && window.scrollY !== savedScrollY) window.scrollTo(0, savedScrollY)\n}\n\n/**\n * Возвращает пару `lock`/`unlock`, идемпотентную **в пределах одного инстанса**\n * (повторный `lock()` без `unlock()` не увеличивает глобальный счётчик дважды).\n * Глобальный счётчик разделяется всеми оверлеями.\n */\nexport function useScrollLock() {\n const isClient = typeof window !== 'undefined'\n let held = false\n\n function lock(): void {\n if (!isClient || held) return\n held = true\n lockCount += 1\n if (lockCount === 1) applyLock()\n }\n\n function unlock(): void {\n if (!isClient || !held) return\n held = false\n lockCount = Math.max(0, lockCount - 1)\n if (lockCount === 0) releaseLock()\n }\n\n return { lock, unlock }\n}\n\n/** Тестовая/служебная очистка глобального состояния. */\nexport function resetScrollLock(): void {\n lockCount = 0\n if (typeof document !== 'undefined') releaseLock()\n saved = null\n}\n","import type { ComputedRef, Ref } from 'vue'\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\n\nimport { useGrThemeAttrs } from '../../components/GrConfigProvider/context'\nimport { useFocusTrap } from '../useFocusTrap'\nimport { useOverlayLayer } from '../useOverlayLayer'\nimport { usePortalTarget } from '../usePortalTarget'\nimport { useInertOthers } from './useInertOthers'\nimport { useOverlayPresence } from './useOverlayPresence'\nimport { useScrollLock } from './useScrollLock'\n\n/**\n * Модальный слой целиком: одна сборка на весь пакет.\n *\n * Модальность собиралась вручную в трёх компонентах (`GrModal`, `GrDrawer`,\n * `GrImageViewer`) из шести примитивов, и сборка повторялась дословно. Дело не\n * в объёме: в этой проводке сидят инварианты, каждый из которых при пропуске\n * даёт тихий баг доступности, а не заметную поломку. Здесь они собраны в одном\n * месте — забыть часть модального слоя больше нельзя.\n *\n * Что именно неочевидно:\n *\n * - **`restoreFocus: false` у ловушки.** Возврат фокуса принадлежит стеку\n * слоёв: у него правило строже — вернуть, только если на момент закрытия\n * фокус ещё был внутри слоя. Две системы возврата подрались бы за фокус.\n * - **`containers: layer.rootsAbove`.** Панель селекта, открытого ВНУТРИ окна,\n * телепортирована в `body` и лежит вне поддерева панели. Без этого списка\n * ловушка считала бы её «снаружи» и отбирала фокус.\n * - **Ловушка и `inert` работают, только пока слой верхний.** Иначе нижнее\n * окно ворует фокус у открытого поверх него, а `inert` гасит собственную\n * панель вместе со страницей.\n * - **Клик по подложке считается только тот, что на ней и начался.** Иначе\n * выделение текста мышью или перетаскивание, отпущенное за пределами панели,\n * закрывает слой.\n *\n * Чего композабл НЕ делает: не рисует разметку. Корневой `div`, подложка и\n * панель у трёх компонентов различаются по существу (обёртки скролла у окна,\n * сквозные клики у немодальной панели, слои хрома у просмотрщика), а классами\n * общий модуль владеть не вправе — пресет гранулярности подмешивает safelist\n * только по директориям выбранных компонентов.\n */\nexport interface UseModalOverlayOptions {\n /**\n * Модальный ли слой: блокирует страницу, держит фокус, гасит нижние окна.\n * Геттером — там, где модальность включается пропом (`GrDrawer`).\n */\n modal?: boolean | (() => boolean)\n /** Закрывать ли по Esc. Геттер — проп компонента реактивен. */\n closeOnEscape?: () => boolean\n /** Закрывать ли по клику в подложку; читается обработчиками `backdrop`. */\n closeOnBackdrop?: () => boolean\n /** Панель слоя: контейнер ловушки фокуса и её запасной приёмник. */\n panel: Ref<HTMLElement | null>\n /**\n * Свой корень слоя. У якорного оверлея (`GrPopover`) корень и панель — один\n * элемент: в портал уезжает сама панель, отдельной обёртки нет. Не задан —\n * композабл заводит корень сам, как у центрированных окон.\n */\n root?: Ref<HTMLElement | null>\n /**\n * Точечное переопределение точки монтирования (проп `teleportTo`). По\n * умолчанию — общий портал оверлеев.\n */\n teleportTo?: () => string | HTMLElement | undefined\n /**\n * Кому отдать фокус при открытии. Не задан — первому фокусируемому внутри\n * панели (правило `useFocusTrap`): просмотрщику нужна именно кнопка тулбара,\n * а окну — сама панель, и оно передаёт её явно.\n */\n initialFocus?: () => HTMLElement | null | undefined\n /**\n * Блокировать ли скролл страницы. По умолчанию — пока слой открыт и модален:\n * немодальная панель существует ровно ради работы со страницей под ней.\n */\n lockScroll?: () => boolean\n}\n\nexport interface ModalOverlay {\n /** Корень слоя: цель `ref` в шаблоне, хозяин `inert` и корень для стека слоёв. */\n rootEl: Ref<HTMLElement | null>\n /** Смонтировано ли поддерево: держится до конца leave-анимации панели. */\n isMounted: Ref<boolean>\n /** Показан ли слой: этим переключаются `<Transition>` внутри. */\n isVisible: Ref<boolean>\n /** Верхний ли это слой сейчас. */\n isTopmost: Ref<boolean>\n /** Значение атрибута `inert` для корня: слой перестал быть верхним. */\n inertAttr: ComputedRef<true | undefined>\n portalTarget: ComputedRef<string | HTMLElement>\n teleportEnabled: ComputedRef<boolean>\n /** `data-theme` на телепортированный корень: в DOM он вне обёртки провайдера. */\n themeAttrs: ComputedRef<{ 'data-theme'?: string }>\n /** Для `@after-leave` панели. Компонент оборачивает, если ему нужен эмит `closed`. */\n onPanelAfterLeave: () => void\n /**\n * Обработчики подложки вместе с guard'ом «клик начался на ней». Имена — под\n * `v-on=\"backdrop\"`: объектный синтаксис ждёт имена событий, а не пропов.\n */\n backdrop: {\n mousedown: (event: MouseEvent) => void\n click: (event: MouseEvent) => void\n }\n}\n\nexport function useModalOverlay(\n open: Ref<boolean>,\n onDismiss: () => void,\n options: UseModalOverlayOptions,\n): ModalOverlay {\n const isModal = (): boolean =>\n typeof options.modal === 'function' ? options.modal() : options.modal ?? true\n\n const rootEl = options.root ?? ref<HTMLElement | null>(null)\n const isTopmost = ref(true)\n\n const inertAttr = computed(() => (open.value && isModal() && !isTopmost.value ? true : undefined))\n\n // Телепорт включается только ПОСЛЕ монтирования: иначе первый клиентский\n // рендер не совпадёт с серверным и сломается гидрация.\n const { target: portalTarget, enabled: teleportEnabled } = usePortalTarget(options.teleportTo)\n\n // Панель уезжает в `body`, то есть вне обёртки провайдера, и `data-theme` с\n // неё не наследуется. В дереве компонентов слой остаётся внутри — `inject`\n // доходит, и тему он ставит себе сам.\n const themeAttrs = useGrThemeAttrs()\n\n const { mounted: isMounted, visible: isVisible, onPanelAfterLeave } = useOverlayPresence(open)\n\n const layer = useOverlayLayer(open, onDismiss, {\n modal: isModal,\n closeOnEscape: options.closeOnEscape,\n onTopmostChange: (value) => { isTopmost.value = value },\n root: rootEl,\n })\n\n useFocusTrap(options.panel, {\n active: () => open.value && isModal() && isTopmost.value,\n initialFocus: options.initialFocus,\n fallbackFocus: () => options.panel.value,\n containers: layer.rootsAbove,\n restoreFocus: false,\n })\n\n useInertOthers(rootEl, () => open.value && isModal() && isTopmost.value)\n\n // Общий reference-counted lock: несколько открытых оверлеев снимают блокировку\n // независимо от порядка закрытия, ширина скроллбара компенсируется.\n const { lock, unlock } = useScrollLock()\n\n watch(\n () => options.lockScroll?.() ?? (open.value && isModal()),\n (locked) => {\n if (locked) lock()\n else unlock()\n },\n { immediate: true },\n )\n\n onBeforeUnmount(unlock)\n\n let pointerDownOnBackdrop = false\n\n return {\n rootEl,\n isMounted,\n isVisible,\n isTopmost,\n inertAttr,\n portalTarget,\n teleportEnabled,\n themeAttrs,\n onPanelAfterLeave,\n backdrop: {\n mousedown(event: MouseEvent): void {\n pointerDownOnBackdrop = event.target === event.currentTarget\n },\n click(event: MouseEvent): void {\n const startedHere = pointerDownOnBackdrop\n pointerDownOnBackdrop = false\n\n if (!startedHere || event.target !== event.currentTarget) return\n if (options.closeOnBackdrop?.() === false) return\n\n onDismiss()\n },\n },\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { s as useGrConfig } from "./context-BSYR7RZs.js";
2
- import { t as ensurePortalRoot } from "./portalRoot-CWD4zHBm.js";
2
+ import { t as ensurePortalRoot } from "./portalRoot-CVhCgdG6.js";
3
3
  import { computed, onMounted, readonly, ref } from "vue";
4
4
  //#region src/composables/internal/useTeleportEnabled.ts
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"usePortalTarget-RyzEgNTb.js","names":[],"sources":["../../src/composables/internal/useTeleportEnabled.ts","../../src/composables/usePortalTarget.ts"],"sourcesContent":["import { onMounted, readonly, ref, type Ref } from 'vue'\n\n/**\n * Можно ли телепортировать содержимое прямо сейчас.\n *\n * Возвращает `false` на сервере И на первом клиентском рендере, `true` — сразу\n * после монтирования. Использовать так:\n *\n * ```vue\n * <teleport to=\"body\" :disabled=\"!teleportEnabled\">…</teleport>\n * ```\n *\n * ## Зачем, если есть `typeof window !== 'undefined'`\n *\n * Проверка окружения выглядит достаточной, но именно она и ломала гидрацию.\n * `isClient` вычисляется в `setup`, поэтому получалось так:\n *\n * - сервер: `window` нет → телепорт выключен → содержимое остаётся НА МЕСТЕ\n * (`:disabled` означает «рендерить на месте», а не «не рендерить»);\n * - клиент, первый рендер: `window` есть → телепорт включён → содержимое сразу\n * уезжает в `body`.\n *\n * Структуры не совпадают, Vue сообщает `Hydration node mismatch`. В лучшем\n * случае это лишний варнинг и осиротевшая копия панели в DOM; в худшем —\n * панель «прилипает» к контейнеру приложения и пишет в него свои стили\n * (`position: fixed` + `display: none` у закрытой панели), после чего страница\n * исчезает целиком.\n *\n * Флаг, поднимаемый в `onMounted`, снимает проблему по построению: **первый**\n * клиентский рендер повторяет серверный, а телепорт включается уже после\n * гидрации. Для компонентов без SSR это ничего не меняет: панели закрыты\n * (`v-show`/`v-if`), а `onMounted` срабатывает в том же тике.\n *\n * Стенд, на котором это воспроизводится и проверяется, — `apps/playground-ssr`.\n */\nexport function useTeleportEnabled(): Readonly<Ref<boolean>> {\n const enabled = ref(false)\n\n onMounted(() => {\n enabled.value = true\n })\n\n return readonly(enabled)\n}\n","import type { ComputedRef } from 'vue'\nimport { computed } from 'vue'\n\nimport { useGrConfig } from '../components/GrConfigProvider/context'\nimport { ensurePortalRoot } from './internal/portalRoot'\nimport { useTeleportEnabled } from './internal/useTeleportEnabled'\n\nexport interface PortalTarget {\n /** Куда телепортировать: элемент портала либо селектор из конфига. */\n target: ComputedRef<string | HTMLElement>\n /**\n * Можно ли телепортировать прямо сейчас. `false` на сервере и на первом\n * клиентском рендере — иначе разметка разъедется на гидрации.\n */\n enabled: ComputedRef<boolean>\n}\n\n/**\n * Куда монтируются оверлеи пакета.\n *\n * Приоритет: локальное переопределение компонента (`teleportTo`) →\n * `portalTarget` из `GrConfigProvider` → общий корень `#gr-portal`, который\n * создаётся при первом обращении.\n *\n * ```vue\n * const { target, enabled } = usePortalTarget(() => props.teleportTo)\n *\n * <teleport :to=\"target\" :disabled=\"!enabled\">…</teleport>\n * ```\n *\n * Композабл публичный: свой оверлей потребителя должен уезжать туда же, куда и\n * оверлеи библиотеки, — иначе он окажется вне ветки портала и будет помечен\n * `inert` вместе со страницей, как только откроется модалка.\n */\nexport function usePortalTarget(local?: () => string | HTMLElement | undefined): PortalTarget {\n const config = useGrConfig()\n const teleportEnabled = useTeleportEnabled()\n\n const target = computed<string | HTMLElement>(() => {\n const explicit = local?.() ?? config.portalTarget?.value\n if (explicit) return explicit\n\n // Корень создаётся лениво и только на клиенте; до монтирования телепорт\n // выключен, поэтому `body` здесь — недостижимый запасной вариант, а не\n // рабочая ветка.\n return ensurePortalRoot() ?? 'body'\n })\n\n return { target, enabled: computed(() => teleportEnabled.value) }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,qBAA6C;CAC3D,MAAM,UAAU,IAAI,KAAK;CAEzB,gBAAgB;EACd,QAAQ,QAAQ;CAClB,CAAC;CAED,OAAO,SAAS,OAAO;AACzB;;;;;;;;;;;;;;;;;;;;ACTA,SAAgB,gBAAgB,OAA8D;CAC5F,MAAM,SAAS,YAAY;CAC3B,MAAM,kBAAkB,mBAAmB;CAY3C,OAAO;EAAE,QAVM,eAAqC;GAClD,MAAM,WAAW,QAAQ,KAAK,OAAO,cAAc;GACnD,IAAI,UAAU,OAAO;GAKrB,OAAO,iBAAiB,KAAK;EAC/B,CAES;EAAQ,SAAS,eAAe,gBAAgB,KAAK;CAAE;AAClE"}
1
+ {"version":3,"file":"usePortalTarget-Cn-Wiz5d.js","names":[],"sources":["../../src/composables/internal/useTeleportEnabled.ts","../../src/composables/usePortalTarget.ts"],"sourcesContent":["import { onMounted, readonly, ref, type Ref } from 'vue'\n\n/**\n * Можно ли телепортировать содержимое прямо сейчас.\n *\n * Возвращает `false` на сервере И на первом клиентском рендере, `true` — сразу\n * после монтирования. Использовать так:\n *\n * ```vue\n * <teleport to=\"body\" :disabled=\"!teleportEnabled\">…</teleport>\n * ```\n *\n * ## Зачем, если есть `typeof window !== 'undefined'`\n *\n * Проверка окружения выглядит достаточной, но именно она и ломала гидрацию.\n * `isClient` вычисляется в `setup`, поэтому получалось так:\n *\n * - сервер: `window` нет → телепорт выключен → содержимое остаётся НА МЕСТЕ\n * (`:disabled` означает «рендерить на месте», а не «не рендерить»);\n * - клиент, первый рендер: `window` есть → телепорт включён → содержимое сразу\n * уезжает в `body`.\n *\n * Структуры не совпадают, Vue сообщает `Hydration node mismatch`. В лучшем\n * случае это лишний варнинг и осиротевшая копия панели в DOM; в худшем —\n * панель «прилипает» к контейнеру приложения и пишет в него свои стили\n * (`position: fixed` + `display: none` у закрытой панели), после чего страница\n * исчезает целиком.\n *\n * Флаг, поднимаемый в `onMounted`, снимает проблему по построению: **первый**\n * клиентский рендер повторяет серверный, а телепорт включается уже после\n * гидрации. Для компонентов без SSR это ничего не меняет: панели закрыты\n * (`v-show`/`v-if`), а `onMounted` срабатывает в том же тике.\n *\n * Стенд, на котором это воспроизводится и проверяется, — `apps/playground-ssr`.\n */\nexport function useTeleportEnabled(): Readonly<Ref<boolean>> {\n const enabled = ref(false)\n\n onMounted(() => {\n enabled.value = true\n })\n\n return readonly(enabled)\n}\n","import type { ComputedRef } from 'vue'\nimport { computed } from 'vue'\n\nimport { useGrConfig } from '../components/GrConfigProvider/context'\nimport { ensurePortalRoot } from './internal/portalRoot'\nimport { useTeleportEnabled } from './internal/useTeleportEnabled'\n\nexport interface PortalTarget {\n /** Куда телепортировать: элемент портала либо селектор из конфига. */\n target: ComputedRef<string | HTMLElement>\n /**\n * Можно ли телепортировать прямо сейчас. `false` на сервере и на первом\n * клиентском рендере — иначе разметка разъедется на гидрации.\n */\n enabled: ComputedRef<boolean>\n}\n\n/**\n * Куда монтируются оверлеи пакета.\n *\n * Приоритет: локальное переопределение компонента (`teleportTo`) →\n * `portalTarget` из `GrConfigProvider` → общий корень `#gr-portal`, который\n * создаётся при первом обращении.\n *\n * ```vue\n * const { target, enabled } = usePortalTarget(() => props.teleportTo)\n *\n * <teleport :to=\"target\" :disabled=\"!enabled\">…</teleport>\n * ```\n *\n * Композабл публичный: свой оверлей потребителя должен уезжать туда же, куда и\n * оверлеи библиотеки, — иначе он окажется вне ветки портала и будет помечен\n * `inert` вместе со страницей, как только откроется модалка.\n */\nexport function usePortalTarget(local?: () => string | HTMLElement | undefined): PortalTarget {\n const config = useGrConfig()\n const teleportEnabled = useTeleportEnabled()\n\n const target = computed<string | HTMLElement>(() => {\n const explicit = local?.() ?? config.portalTarget?.value\n if (explicit) return explicit\n\n // Корень создаётся лениво и только на клиенте; до монтирования телепорт\n // выключен, поэтому `body` здесь — недостижимый запасной вариант, а не\n // рабочая ветка.\n return ensurePortalRoot() ?? 'body'\n })\n\n return { target, enabled: computed(() => teleportEnabled.value) }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,qBAA6C;CAC3D,MAAM,UAAU,IAAI,KAAK;CAEzB,gBAAgB;EACd,QAAQ,QAAQ;CAClB,CAAC;CAED,OAAO,SAAS,OAAO;AACzB;;;;;;;;;;;;;;;;;;;;ACTA,SAAgB,gBAAgB,OAA8D;CAC5F,MAAM,SAAS,YAAY;CAC3B,MAAM,kBAAkB,mBAAmB;CAY3C,OAAO;EAAE,QAVM,eAAqC;GAClD,MAAM,WAAW,QAAQ,KAAK,OAAO,cAAc;GACnD,IAAI,UAAU,OAAO;GAKrB,OAAO,iBAAiB,KAAK;EAC/B,CAES;EAAQ,SAAS,eAAe,gBAAgB,KAAK;CAAE;AAClE"}