@feugene/granularity 0.32.0 → 0.34.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 (157) hide show
  1. package/dist/chunks/{config-Dv_r92GL.js → config-2vrOFn2f.js} +2 -2
  2. package/dist/chunks/{config-Dv_r92GL.js.map → config-2vrOFn2f.js.map} +1 -1
  3. package/dist/chunks/{config-g8-JGZ_Y.js → config-BscVxZR3.js} +1 -1
  4. package/dist/chunks/{config-g8-JGZ_Y.js.map → config-BscVxZR3.js.map} +1 -1
  5. package/dist/chunks/{config-vYMJ3Cr1.js → config-CYthTVK2.js} +2 -2
  6. package/dist/chunks/config-CYthTVK2.js.map +1 -0
  7. package/dist/chunks/{config-CPSxM7XE.js → config-CsCUuukB.js} +1 -0
  8. package/dist/chunks/config-CsCUuukB.js.map +1 -0
  9. package/dist/chunks/{config-BTwYMQaz2.js → config-DBXCIb6r2.js} +4 -3
  10. package/dist/chunks/config-DBXCIb6r2.js.map +1 -0
  11. package/dist/chunks/{config-uzUuosv7.js → config-DGGaKf4R.js} +6 -5
  12. package/dist/chunks/config-DGGaKf4R.js.map +1 -0
  13. package/dist/chunks/{config-DnF472Ox.js → config-DOYz_SVK.js} +2 -2
  14. package/dist/chunks/{config-DnF472Ox.js.map → config-DOYz_SVK.js.map} +1 -1
  15. package/dist/chunks/{config-T3-DMXYm.js → config-DUNH02Fg.js} +2 -2
  16. package/dist/chunks/config-DUNH02Fg.js.map +1 -0
  17. package/dist/chunks/{generated-CSyPsWtM.js → generated-Buw6W7Te.js} +7 -0
  18. package/dist/chunks/{generated-CSyPsWtM.js.map → generated-Buw6W7Te.js.map} +1 -1
  19. package/dist/chunks/{grBadgeStyles-D8Mz73B5.js → grBadgeStyles-DV0DrAew.js} +17 -1
  20. package/dist/chunks/grBadgeStyles-DV0DrAew.js.map +1 -0
  21. package/dist/chunks/{grChipStyles-3JZApw03.js → grChipStyles-DcDyxAL0.js} +1 -1
  22. package/dist/chunks/{grChipStyles-3JZApw03.js.map → grChipStyles-DcDyxAL0.js.map} +1 -1
  23. package/dist/chunks/grDropdownStyles-DLToHWMX.js +5 -0
  24. package/dist/chunks/grDropdownStyles-DLToHWMX.js.map +1 -0
  25. package/dist/chunks/grPopoverStyles-DKwigt7b.js +91 -0
  26. package/dist/chunks/grPopoverStyles-DKwigt7b.js.map +1 -0
  27. package/dist/chunks/{grSelectStyles-Cdtd10eY.js → grSelectStyles-D9EQ-pgK.js} +2 -1
  28. package/dist/chunks/grSelectStyles-D9EQ-pgK.js.map +1 -0
  29. package/dist/chunks/{granular-provider-Da2CJO6j.js → granular-provider-3LUawDPh.js} +8 -8
  30. package/dist/chunks/{granular-provider-Da2CJO6j.js.map → granular-provider-3LUawDPh.js.map} +1 -1
  31. package/dist/chunks/{overlayTransition-0Fj5tQbq.js → overlayPanelSurface-BKeMhRx4.js} +24 -1
  32. package/dist/chunks/overlayPanelSurface-BKeMhRx4.js.map +1 -0
  33. package/dist/chunks/{safelist-CtiHPgrj.js → safelist-x8USecK8.js} +3 -1
  34. package/dist/chunks/safelist-x8USecK8.js.map +1 -0
  35. package/dist/chunks/{focusables-BkzK42SQ.js → useFocusTrap-D9bIPNF6.js} +123 -1
  36. package/dist/chunks/useFocusTrap-D9bIPNF6.js.map +1 -0
  37. package/dist/chunks/{useMenuItemsFocus-B6TldmGf.js → useMenuItemsFocus-B7Bl_aFA.js} +1 -1
  38. package/dist/chunks/{useMenuItemsFocus-B6TldmGf.js.map → useMenuItemsFocus-B7Bl_aFA.js.map} +1 -1
  39. package/dist/chunks/{useModalOverlay-D__CqLGn.js → useModalOverlay-BnAGZpcI.js} +1 -1
  40. package/dist/chunks/{useModalOverlay-D__CqLGn.js.map → useModalOverlay-BnAGZpcI.js.map} +1 -1
  41. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-rYEY15UI.js → GrAutocomplete-BQsSE7sj.js} +4 -4
  42. package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-rYEY15UI.js.map → GrAutocomplete-BQsSE7sj.js.map} +1 -1
  43. package/dist/components/GrAutocomplete/index.js +2 -2
  44. package/dist/components/GrBadge/chunks/{GrBadge-D2MfThPz.js → GrBadge-DpUDXKbq.js} +9 -4
  45. package/dist/components/GrBadge/chunks/GrBadge-DpUDXKbq.js.map +1 -0
  46. package/dist/components/GrBadge/index.js +3 -3
  47. package/dist/components/GrBadge/styles.css +1 -1
  48. package/dist/components/GrChip/chunks/{GrChip-C0vU69tf.js → GrChip-BTivtwff.js} +1 -1
  49. package/dist/components/GrChip/chunks/{GrChip-C0vU69tf.js.map → GrChip-BTivtwff.js.map} +1 -1
  50. package/dist/components/GrChip/index.js +2 -2
  51. package/dist/components/GrColorPicker/chunks/{GrColorPicker-p2XH_8cE.js → GrColorPicker-D1Iis7jf.js} +2 -1
  52. package/dist/components/GrColorPicker/chunks/GrColorPicker-D1Iis7jf.js.map +1 -0
  53. package/dist/components/GrColorPicker/index.js +1 -1
  54. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BXbh3q7O.js → GrCommandPalette-BZHUKlux.js} +1 -1
  55. package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BXbh3q7O.js.map → GrCommandPalette-BZHUKlux.js.map} +1 -1
  56. package/dist/components/GrCommandPalette/index.js +1 -1
  57. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CUP43q__.js → GrConfirmDialog-jRDs_E6C.js} +1 -1
  58. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CUP43q__.js.map → GrConfirmDialog-jRDs_E6C.js.map} +1 -1
  59. package/dist/components/GrConfirmDialog/index.js +1 -1
  60. package/dist/components/GrContextMenu/chunks/{GrContextMenu-ClkVjdcP.js → GrContextMenu-CWmgrzpi.js} +17 -3
  61. package/dist/components/GrContextMenu/chunks/GrContextMenu-CWmgrzpi.js.map +1 -0
  62. package/dist/components/GrContextMenu/index.js +2 -2
  63. package/dist/components/GrDataTable/chunks/{GrDataTable-CAGgRk27.js → GrDataTable-4JAgOQ-7.js} +1 -1
  64. package/dist/components/GrDataTable/chunks/{GrDataTable-CAGgRk27.js.map → GrDataTable-4JAgOQ-7.js.map} +1 -1
  65. package/dist/components/GrDataTable/index.js +1 -1
  66. package/dist/components/GrDialog/chunks/{GrDialog-BiMJ9_T4.js → GrDialog-C01Hhk6G.js} +1 -1
  67. package/dist/components/GrDialog/chunks/{GrDialog-BiMJ9_T4.js.map → GrDialog-C01Hhk6G.js.map} +1 -1
  68. package/dist/components/GrDialog/index.js +1 -1
  69. package/dist/components/GrDialogService/chunks/{GrDialogService-BxNK6crD.js → GrDialogService-CmsJNUmM.js} +7 -2
  70. package/dist/components/GrDialogService/chunks/{GrDialogService-BxNK6crD.js.map → GrDialogService-CmsJNUmM.js.map} +1 -1
  71. package/dist/components/GrDialogService/index.js +1 -1
  72. package/dist/components/GrDrawer/chunks/{GrDrawer-Ce2l3zaG.js → GrDrawer-xfoy-MCj.js} +1 -1
  73. package/dist/components/GrDrawer/chunks/{GrDrawer-Ce2l3zaG.js.map → GrDrawer-xfoy-MCj.js.map} +1 -1
  74. package/dist/components/GrDrawer/index.js +1 -1
  75. package/dist/components/GrDropdown/chunks/GrDropdown-CDkPkvIJ.js +180 -0
  76. package/dist/components/GrDropdown/chunks/GrDropdown-CDkPkvIJ.js.map +1 -0
  77. package/dist/components/GrDropdown/index.js +2 -2
  78. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-GOzuAXsi.js → GrDropdownMenu-DQrZ_yPV.js} +1 -1
  79. package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-GOzuAXsi.js.map → GrDropdownMenu-DQrZ_yPV.js.map} +1 -1
  80. package/dist/components/GrDropdownMenu/index.js +1 -1
  81. package/dist/components/GrFileUpload/chunks/{GrFileUpload-NsHkRRIa.js → GrFileUpload-LL8RZvC0.js} +10 -0
  82. package/dist/components/GrFileUpload/chunks/GrFileUpload-LL8RZvC0.js.map +1 -0
  83. package/dist/components/GrFileUpload/index.js +1 -1
  84. package/dist/components/GrImageViewer/chunks/{GrImageViewer-CG4vUBg_.js → GrImageViewer--BU8yrfx.js} +1 -1
  85. package/dist/components/GrImageViewer/chunks/{GrImageViewer-CG4vUBg_.js.map → GrImageViewer--BU8yrfx.js.map} +1 -1
  86. package/dist/components/GrImageViewer/index.js +1 -1
  87. package/dist/components/GrInputTag/chunks/{GrInputTag-Cj4A9Xgs.js → GrInputTag-ByD9NugQ.js} +2 -2
  88. package/dist/components/GrInputTag/chunks/{GrInputTag-Cj4A9Xgs.js.map → GrInputTag-ByD9NugQ.js.map} +1 -1
  89. package/dist/components/GrInputTag/index.js +1 -1
  90. package/dist/components/GrModal/chunks/{GrModal-D0mf1SAx.js → GrModal-_JrDhon-.js} +1 -1
  91. package/dist/components/GrModal/chunks/{GrModal-D0mf1SAx.js.map → GrModal-_JrDhon-.js.map} +1 -1
  92. package/dist/components/GrModal/index.js +1 -1
  93. package/dist/components/GrPagination/chunks/{GrPagination-B_kAhoAV.js → GrPagination-gBoWVHk6.js} +1 -1
  94. package/dist/components/GrPagination/chunks/{GrPagination-B_kAhoAV.js.map → GrPagination-gBoWVHk6.js.map} +1 -1
  95. package/dist/components/GrPagination/index.js +1 -1
  96. package/dist/components/GrPopover/chunks/{GrPopover-faJanmGs.js → GrPopover-CzeXMcvh.js} +87 -13
  97. package/dist/components/GrPopover/chunks/GrPopover-CzeXMcvh.js.map +1 -0
  98. package/dist/components/GrPopover/index.js +2 -2
  99. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-C6QGC7QJ.js → GrPromptDialog-CailXSqW.js} +1 -1
  100. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-C6QGC7QJ.js.map → GrPromptDialog-CailXSqW.js.map} +1 -1
  101. package/dist/components/GrPromptDialog/index.js +1 -1
  102. package/dist/components/GrSelect/chunks/{GrSelect-gCeRFgQo.js → GrSelect-CZqV1Eg4.js} +4 -4
  103. package/dist/components/GrSelect/chunks/{GrSelect-gCeRFgQo.js.map → GrSelect-CZqV1Eg4.js.map} +1 -1
  104. package/dist/components/GrSelect/index.js +2 -2
  105. package/dist/components/GrTable/chunks/{GrTable-Zcdz2St_.js → GrTable-jhUTczjv.js} +12 -1
  106. package/dist/components/GrTable/chunks/GrTable-jhUTczjv.js.map +1 -0
  107. package/dist/components/GrTable/index.js +1 -1
  108. package/dist/components/GrTabs/chunks/{GrTabs-ugDnkk-2.js → GrTabs-UsO4j8zv.js} +5 -0
  109. package/dist/components/GrTabs/chunks/GrTabs-UsO4j8zv.js.map +1 -0
  110. package/dist/components/GrTabs/index.js +1 -1
  111. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-BHwgGJcT.js → GrTreeSelect-DOXcpWq2.js} +2 -2
  112. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-BHwgGJcT.js.map → GrTreeSelect-DOXcpWq2.js.map} +1 -1
  113. package/dist/components/GrTreeSelect/index.js +2 -2
  114. package/dist/composables/useFocusTrap.js +1 -123
  115. package/dist/granular-provider-node.js +1 -1
  116. package/dist/granular-provider.js +1 -1
  117. package/dist/index.js +34 -34
  118. package/dist/theme.js +1 -1
  119. package/dist/tokens.js +1 -1
  120. package/dist/types/components/GrAutocomplete/grAutocompleteStyles.d.ts +1 -1
  121. package/dist/types/components/GrBadge/GrBadge.vue.d.ts +11 -0
  122. package/dist/types/components/GrBadge/grBadgeStyles.d.ts +11 -0
  123. package/dist/types/components/GrDropdown/GrDropdown.vue.d.ts +5 -5
  124. package/dist/types/components/GrDropdown/grDropdownStyles.d.ts +8 -2
  125. package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +2 -2
  126. package/dist/types/components/GrPopover/GrPopover.vue.d.ts +54 -4
  127. package/dist/types/components/GrPopover/grPopoverStyles.d.ts +19 -11
  128. package/dist/types/components/GrSelect/grSelectStyles.d.ts +1 -1
  129. package/dist/types/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
  130. package/dist/types/components/shared/overlayOrigin.d.ts +22 -0
  131. package/dist/types/components/shared/overlayPanelSurface.d.ts +21 -0
  132. package/dist/web-types.json +44 -4
  133. package/package.json +3 -3
  134. package/dist/chunks/config-BTwYMQaz2.js.map +0 -1
  135. package/dist/chunks/config-CPSxM7XE.js.map +0 -1
  136. package/dist/chunks/config-T3-DMXYm.js.map +0 -1
  137. package/dist/chunks/config-uzUuosv7.js.map +0 -1
  138. package/dist/chunks/config-vYMJ3Cr1.js.map +0 -1
  139. package/dist/chunks/focusables-BkzK42SQ.js.map +0 -1
  140. package/dist/chunks/grBadgeStyles-D8Mz73B5.js.map +0 -1
  141. package/dist/chunks/grDropdownStyles-V7TRiCXn.js +0 -24
  142. package/dist/chunks/grDropdownStyles-V7TRiCXn.js.map +0 -1
  143. package/dist/chunks/grPopoverStyles-Djh5RNhn.js +0 -59
  144. package/dist/chunks/grPopoverStyles-Djh5RNhn.js.map +0 -1
  145. package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +0 -1
  146. package/dist/chunks/overlayTransition-0Fj5tQbq.js.map +0 -1
  147. package/dist/chunks/safelist-CtiHPgrj.js.map +0 -1
  148. package/dist/components/GrBadge/chunks/GrBadge-D2MfThPz.js.map +0 -1
  149. package/dist/components/GrColorPicker/chunks/GrColorPicker-p2XH_8cE.js.map +0 -1
  150. package/dist/components/GrContextMenu/chunks/GrContextMenu-ClkVjdcP.js.map +0 -1
  151. package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js +0 -239
  152. package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js.map +0 -1
  153. package/dist/components/GrFileUpload/chunks/GrFileUpload-NsHkRRIa.js.map +0 -1
  154. package/dist/components/GrPopover/chunks/GrPopover-faJanmGs.js.map +0 -1
  155. package/dist/components/GrTable/chunks/GrTable-Zcdz2St_.js.map +0 -1
  156. package/dist/components/GrTabs/chunks/GrTabs-ugDnkk-2.js.map +0 -1
  157. package/dist/composables/useFocusTrap.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { nextTick, onScopeDispose, watch } from "vue";
1
2
  //#region src/composables/internal/focusables.ts
2
3
  /**
3
4
  * Что пакет считает фокусируемым.
@@ -84,4 +85,125 @@ function getFocusableElementsIn(roots) {
84
85
  });
85
86
  }
86
87
  //#endregion
87
- export { getFocusableElementsIn as n, isFocusable as r, getFocusableElements as t };
88
+ //#region src/composables/useFocusTrap.ts
89
+ /**
90
+ * Ловушка фокуса: пока активна, Tab ходит по кругу внутри слоя, а фокус,
91
+ * утёкший наружу, возвращается обратно.
92
+ *
93
+ * Реализована на `keydown` и `focusin`, **без sentinel-узлов**: распространённый
94
+ * подход с двумя focus-guard кнопками по краям кладёт в разметку интерактивные
95
+ * элементы, а внутрь контейнера с ARIA-ролью их класть нельзя — роль объявляет
96
+ * потомков презентационными (axe: `nested-interactive`).
97
+ *
98
+ * Слой из ловушки не выводится: порядок окон, Esc и `inert` — забота
99
+ * `useOverlayLayer`. Композиция такая:
100
+ *
101
+ * ```ts
102
+ * const layer = useOverlayLayer(open, close, { modal: true, root: panelRef })
103
+ * useFocusTrap(panelRef, {
104
+ * active: () => open.value && isTopmost.value,
105
+ * containers: layer.rootsAbove,
106
+ * })
107
+ * ```
108
+ */
109
+ function useFocusTrap(container, options = {}) {
110
+ let previouslyFocused = null;
111
+ let listening = false;
112
+ let lastFocusedInside = null;
113
+ function roots() {
114
+ const extra = options.containers?.() ?? [];
115
+ return [container.value, ...extra].filter((root) => Boolean(root));
116
+ }
117
+ function contains(target) {
118
+ if (!target) return false;
119
+ return roots().some((root) => root === target || root.contains(target));
120
+ }
121
+ function focusables() {
122
+ return getFocusableElementsIn(roots());
123
+ }
124
+ function focusInitial() {
125
+ if (contains(document.activeElement)) return;
126
+ const requested = options.initialFocus?.();
127
+ if (requested && isFocusable(requested)) {
128
+ requested.focus();
129
+ return;
130
+ }
131
+ const [first] = focusables();
132
+ if (first) {
133
+ first.focus();
134
+ return;
135
+ }
136
+ (options.fallbackFocus?.() ?? container.value)?.focus();
137
+ }
138
+ function onKeydown(event) {
139
+ if (event.key !== "Tab") return;
140
+ if (!contains(document.activeElement)) return;
141
+ const items = focusables();
142
+ if (items.length === 0) {
143
+ event.preventDefault();
144
+ (options.fallbackFocus?.() ?? container.value)?.focus();
145
+ return;
146
+ }
147
+ const first = items[0];
148
+ const last = items[items.length - 1];
149
+ const active = document.activeElement;
150
+ if ((active ? items.indexOf(active) : -1) === -1) {
151
+ event.preventDefault();
152
+ (event.shiftKey ? last : first).focus();
153
+ return;
154
+ }
155
+ if (event.shiftKey && active === first) {
156
+ event.preventDefault();
157
+ last.focus();
158
+ return;
159
+ }
160
+ if (!event.shiftKey && active === last) {
161
+ event.preventDefault();
162
+ first.focus();
163
+ }
164
+ }
165
+ function onFocusIn(event) {
166
+ const target = event.target;
167
+ if (contains(target)) {
168
+ lastFocusedInside = target;
169
+ return;
170
+ }
171
+ if (lastFocusedInside && contains(lastFocusedInside) && isFocusable(lastFocusedInside)) {
172
+ lastFocusedInside.focus();
173
+ return;
174
+ }
175
+ focusInitial();
176
+ }
177
+ function activate() {
178
+ if (listening) return;
179
+ if (typeof document === "undefined") return;
180
+ listening = true;
181
+ previouslyFocused = document.activeElement ?? null;
182
+ lastFocusedInside = null;
183
+ document.addEventListener("keydown", onKeydown, true);
184
+ document.addEventListener("focusin", onFocusIn, true);
185
+ nextTick(() => {
186
+ if (!listening) return;
187
+ focusInitial();
188
+ });
189
+ }
190
+ function deactivate() {
191
+ if (!listening) return;
192
+ listening = false;
193
+ document.removeEventListener("keydown", onKeydown, true);
194
+ document.removeEventListener("focusin", onFocusIn, true);
195
+ const target = previouslyFocused;
196
+ previouslyFocused = null;
197
+ lastFocusedInside = null;
198
+ if (options.restoreFocus === false || !target) return;
199
+ if (!target.isConnected) return;
200
+ target.focus?.();
201
+ }
202
+ watch(() => Boolean(options.active?.() ?? true) && Boolean(container.value), (isActive) => {
203
+ if (isActive) activate();
204
+ else deactivate();
205
+ }, { immediate: true });
206
+ onScopeDispose(deactivate);
207
+ }
208
+ //#endregion
209
+ export { getFocusableElements as n, useFocusTrap as t };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusTrap-D9bIPNF6.js","names":[],"sources":["../../src/composables/internal/focusables.ts","../../src/composables/useFocusTrap.ts"],"sourcesContent":["/**\n * Что пакет считает фокусируемым.\n *\n * Один источник правды на ловушку фокуса, roving-навигацию и «первый элемент\n * панели». До него каждый компонент собирал свой селектор: у `GrDropdown` в\n * список не попадали `select` и `textarea`, а фильтр видимости стоял на\n * `offsetParent !== null` — он врёт на `position: fixed` (у таких элементов\n * `offsetParent` всегда `null`), то есть на панелях самих оверлеев.\n */\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n '[tabindex]',\n].join(',')\n\n/**\n * Видимость.\n *\n * В браузере спрашиваем `checkVisibility()` — он единственный учитывает скрытых\n * предков, `content-visibility` и `display: contents`. В jsdom его нет и\n * раскладки тоже нет, поэтому там остаётся проверка собственных стилей: этого\n * достаточно, чтобы тесты видели ту же логику, а не пустой список.\n */\nfunction isVisible(element: HTMLElement): boolean {\n if (element.hidden)\n return false\n\n if (typeof element.checkVisibility === 'function')\n return element.checkVisibility({ contentVisibilityAuto: true, visibilityProperty: true })\n\n const style = element.ownerDocument.defaultView?.getComputedStyle(element)\n if (!style)\n return true\n return style.display !== 'none' && style.visibility !== 'hidden'\n}\n\n/** Элемент внутри `inert`-поддерева не фокусируем — браузер его не отдаёт. */\nfunction isInert(element: HTMLElement): boolean {\n return element.closest('[inert]') !== null\n}\n\nexport function isFocusable(element: HTMLElement): boolean {\n if (!element.matches(FOCUSABLE_SELECTOR))\n return false\n if (element.hasAttribute('disabled'))\n return false\n if (element.getAttribute('aria-hidden') === 'true')\n return false\n return !isInert(element) && isVisible(element)\n}\n\n/** Участвует ли элемент в таб-порядке (`tabindex=\"-1\"` — нет). */\nexport function isTabbable(element: HTMLElement): boolean {\n if (!isFocusable(element))\n return false\n return Number.parseInt(element.getAttribute('tabindex') ?? '0', 10) >= 0\n}\n\n/**\n * Фокусируемые внутри корня, в порядке документа.\n *\n * `tabbableOnly` (по умолчанию) оставляет только участников таб-порядка — это\n * то, что нужно циклу Tab. Полный список нужен «первому фокусируемому» при\n * открытии: панель с `tabindex=\"-1\"` фокусируется программно.\n */\nexport function getFocusableElements(\n root: HTMLElement | null | undefined,\n options: { tabbableOnly?: boolean } = {},\n): HTMLElement[] {\n if (!root)\n return []\n\n const tabbableOnly = options.tabbableOnly ?? true\n const matches = Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))\n\n return matches.filter(element => (tabbableOnly ? isTabbable(element) : isFocusable(element)))\n}\n\n/**\n * Фокусируемые сразу в нескольких корнях, в порядке документа.\n *\n * Нужен ловушке: панель селекта, открытого внутри модалки, телепортирована в\n * `body` и лежит вне DOM-поддерева окна, но для пользователя это продолжение\n * того же слоя.\n */\nexport function getFocusableElementsIn(roots: (HTMLElement | null | undefined)[]): HTMLElement[] {\n const unique = new Set<HTMLElement>()\n\n for (const root of roots) {\n for (const element of getFocusableElements(root)) unique.add(element)\n }\n\n return Array.from(unique).sort((left, right) => {\n const position = left.compareDocumentPosition(right)\n if (position & Node.DOCUMENT_POSITION_FOLLOWING)\n return -1\n if (position & Node.DOCUMENT_POSITION_PRECEDING)\n return 1\n return 0\n })\n}\n","import type { Ref } from 'vue'\nimport { nextTick, onScopeDispose, watch } from 'vue'\n\nimport { getFocusableElementsIn, isFocusable } from './internal/focusables'\n\nexport interface UseFocusTrapOptions {\n /**\n * Работает ли ловушка сейчас. Геттер, а не значение: модальное окно держит\n * фокус, только пока оно верхнее в стеке слоёв, — иначе нижнее окно отбирало\n * бы фокус у открытого поверх него.\n */\n active?: () => boolean\n /** Кому отдать фокус при активации. Не задан или не фокусируем — первый в контейнере. */\n initialFocus?: () => HTMLElement | null | undefined\n /**\n * Запасной приёмник фокуса, когда фокусировать внутри нечего: обычно сама\n * панель с `tabindex=\"-1\"`. Без него фокус остался бы снаружи, и Tab увёл бы\n * пользователя на страницу под окном.\n */\n fallbackFocus?: () => HTMLElement | null | undefined\n /**\n * Дополнительные корни, которые считаются частью слоя. Панель селекта,\n * открытого внутри окна, телепортирована в `body` и лежит вне поддерева\n * контейнера — без этого списка ловушка отбирала бы у неё фокус.\n */\n containers?: () => (HTMLElement | null | undefined)[]\n /**\n * Возвращать фокус туда, где он был до активации. По умолчанию — да.\n * Оверлеи пакета выключают: возврат делает стек слоёв по более строгому\n * правилу (только если фокус на момент закрытия ещё внутри слоя).\n */\n restoreFocus?: boolean\n}\n\n/**\n * Ловушка фокуса: пока активна, Tab ходит по кругу внутри слоя, а фокус,\n * утёкший наружу, возвращается обратно.\n *\n * Реализована на `keydown` и `focusin`, **без sentinel-узлов**: распространённый\n * подход с двумя focus-guard кнопками по краям кладёт в разметку интерактивные\n * элементы, а внутрь контейнера с ARIA-ролью их класть нельзя — роль объявляет\n * потомков презентационными (axe: `nested-interactive`).\n *\n * Слой из ловушки не выводится: порядок окон, Esc и `inert` — забота\n * `useOverlayLayer`. Композиция такая:\n *\n * ```ts\n * const layer = useOverlayLayer(open, close, { modal: true, root: panelRef })\n * useFocusTrap(panelRef, {\n * active: () => open.value && isTopmost.value,\n * containers: layer.rootsAbove,\n * })\n * ```\n */\nexport function useFocusTrap(\n container: Ref<HTMLElement | null>,\n options: UseFocusTrapOptions = {},\n): void {\n let previouslyFocused: HTMLElement | null = null\n let listening = false\n // Последний законный держатель фокуса: с него начинается возврат, когда\n // фокус ушёл в никуда (например, кликом по неинтерактивному фону).\n let lastFocusedInside: HTMLElement | null = null\n\n function roots(): HTMLElement[] {\n const extra = options.containers?.() ?? []\n return [container.value, ...extra].filter((root): root is HTMLElement => Boolean(root))\n }\n\n function contains(target: Node | null): boolean {\n if (!target)\n return false\n return roots().some(root => root === target || root.contains(target))\n }\n\n function focusables(): HTMLElement[] {\n return getFocusableElementsIn(roots())\n }\n\n function focusInitial(): void {\n // Фокус уже внутри слоя — его поставили осознанно: диалог наводит на свою\n // кнопку, окно ввода — на поле, и делают это в тот же такт, когда панель\n // появилась. `initialFocus` — дефолт слоя, а не приказ, поэтому такую\n // установку он не перебивает.\n if (contains(document.activeElement))\n return\n\n const requested = options.initialFocus?.()\n if (requested && isFocusable(requested)) {\n requested.focus()\n return\n }\n\n const [first] = focusables()\n if (first) {\n first.focus()\n return\n }\n\n // Фокусировать нечего — забираем фокус на сам слой, иначе он останется\n // снаружи и Tab уведёт пользователя под окно.\n const fallback = options.fallbackFocus?.() ?? container.value\n fallback?.focus()\n }\n\n function onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Tab')\n return\n if (!contains(document.activeElement))\n return\n\n const items = focusables()\n if (items.length === 0) {\n // Внутри некуда идти — держим фокус на слое, а не отпускаем на страницу.\n event.preventDefault()\n ;(options.fallbackFocus?.() ?? container.value)?.focus()\n return\n }\n\n const first = items[0]\n const last = items[items.length - 1]\n const active = document.activeElement as HTMLElement | null\n const index = active ? items.indexOf(active) : -1\n\n // Фокус на самой панели (`tabindex=\"-1\"`, в списке её нет): Tab обязан\n // войти внутрь, а не выпрыгнуть за слой.\n if (index === -1) {\n event.preventDefault()\n ;(event.shiftKey ? last : first).focus()\n return\n }\n\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last.focus()\n return\n }\n\n if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n function onFocusIn(event: FocusEvent): void {\n const target = event.target as HTMLElement | null\n\n if (contains(target)) {\n lastFocusedInside = target\n return\n }\n\n // Фокус утёк наружу — программным `focus()`, кликом по фону или обходом\n // через адресную строку. Возвращаем его на последнее законное место.\n if (lastFocusedInside && contains(lastFocusedInside) && isFocusable(lastFocusedInside)) {\n lastFocusedInside.focus()\n return\n }\n\n focusInitial()\n }\n\n function activate(): void {\n if (listening)\n return\n if (typeof document === 'undefined')\n return\n listening = true\n\n previouslyFocused = (document.activeElement as HTMLElement) ?? null\n lastFocusedInside = null\n\n document.addEventListener('keydown', onKeydown, true)\n document.addEventListener('focusin', onFocusIn, true)\n\n // Ждём, пока Vue домонтирует содержимое слоя: до этого фокусировать нечего.\n void nextTick(() => {\n if (!listening)\n return\n focusInitial()\n })\n }\n\n function deactivate(): void {\n if (!listening)\n return\n listening = false\n\n document.removeEventListener('keydown', onKeydown, true)\n document.removeEventListener('focusin', onFocusIn, true)\n\n const target = previouslyFocused\n previouslyFocused = null\n lastFocusedInside = null\n\n if (options.restoreFocus === false || !target)\n return\n if (!target.isConnected)\n return\n target.focus?.()\n }\n\n watch(\n () => Boolean(options.active?.() ?? true) && Boolean(container.value),\n (isActive) => {\n if (isActive)\n activate()\n else deactivate()\n },\n { immediate: true },\n )\n\n onScopeDispose(deactivate)\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { i as isEditableTarget } from "./keyboard-DE2yhz_b.js";
2
- import { t as getFocusableElements } from "./focusables-BkzK42SQ.js";
2
+ import { n as getFocusableElements } from "./useFocusTrap-D9bIPNF6.js";
3
3
  import { t as useTypeahead } from "./useTypeahead-mxPtPBf2.js";
4
4
  //#region src/composables/internal/useMenuItemsFocus.ts
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"useMenuItemsFocus-B6TldmGf.js","names":[],"sources":["../../src/composables/internal/useMenuItemsFocus.ts"],"sourcesContent":["import { isEditableTarget } from '../../internal/keyboard'\n\nimport { getFocusableElements } from './focusables'\nimport { useTypeahead } from './useTypeahead'\n\nexport interface UseMenuItemsFocusOptions {\n /** Корень, внутри которого живут пункты. */\n container: () => HTMLElement | null\n /** Закрыть меню — `Tab` уводит фокус наружу, панель при этом закрывается. */\n close: () => void\n}\n\nexport interface UseMenuItemsFocusReturn {\n /** Пункты в порядке документа. */\n items: () => HTMLElement[]\n /** Сфокусировать по индексу; отрицательный и превышающий заворачиваются. */\n focusAt: (index: number) => void\n /** Стрелки, `Home`/`End`, `Tab`, typeahead. `true` — клавиша обработана. */\n onKeydown: (event: KeyboardEvent) => boolean\n reset: () => void\n}\n\n/**\n * Клавиатура панели-меню: кольцо фокуса плюс поиск по первым буквам.\n *\n * Пункты ищутся в DOM, а не в модели, и это осознанно: у панели бывает\n * произвольное содержимое из слота, в котором ключей нет вовсе. По той же\n * причине здесь не `useRovingFocus` — он адресуется ключами и ведёт\n * `tabindex=\"0\"` на активном элементе, а пункты меню обязаны быть `-1`.\n */\nexport function useMenuItemsFocus(options: UseMenuItemsFocusOptions): UseMenuItemsFocusReturn {\n /**\n * `tabbableOnly: false` — не оплошность: пункты меню намеренно `tabindex=\"-1\"`,\n * и список участников таб-порядка был бы пуст.\n */\n function items(): HTMLElement[] {\n return getFocusableElements(options.container(), { tabbableOnly: false })\n }\n\n function focusAt(index: number): void {\n const list = items()\n if (list.length === 0)\n return\n\n // `preventScroll` обязателен: панель уже приведена во вьюпорт позиционером,\n // поэтому прокрутка ради фокуса внутри неё паразитная. У контекстного меню\n // она к тому же порождала `scroll`, по которому меню закрывало само себя.\n list[(index + list.length) % list.length]?.focus({ preventScroll: true })\n }\n\n const typeahead = useTypeahead<HTMLElement>({\n items,\n textOf: element => element.textContent ?? '',\n currentIndex: list => list.indexOf(document.activeElement as HTMLElement),\n onMatch: element => element.focus({ preventScroll: true }),\n })\n\n function onKeydown(event: KeyboardEvent): boolean {\n const list = items()\n const currentIndex = list.indexOf(document.activeElement as HTMLElement)\n\n // Панель бывает не только меню из кнопок: с `closeOnContentClick={false}` в\n // ней живут поля и чекбоксы. Клавиши, которые сфокусированный контрол умеет\n // сам, остаются ему — иначе в поле не напечатать, а чекбокс не переключить.\n const editable = isEditableTarget(event.target)\n\n switch (event.key) {\n // Стрелки — исключение: они за меню даже в поле. `Tab` панель закрывает, и\n // без них из поля внутри панели не было бы выхода вовсе.\n case 'ArrowDown':\n event.preventDefault()\n focusAt(currentIndex + 1)\n return true\n case 'ArrowUp':\n event.preventDefault()\n focusAt(currentIndex - 1)\n return true\n case 'Home':\n if (editable)\n return false\n event.preventDefault()\n focusAt(0)\n return true\n case 'End':\n if (editable)\n return false\n event.preventDefault()\n focusAt(-1)\n return true\n case 'Tab':\n options.close()\n return true\n }\n\n if (editable)\n return false\n\n // Пробел при пустом буфере — активация сфокусированного пункта, а не поиск:\n // пункты меню это кнопки, и пробел для них родная клавиша. В буфер он входит,\n // только когда поиск уже идёт (правило typeahead из WAI-ARIA APG).\n if (event.key === ' ' && typeahead.isEmpty())\n return false\n\n // Печатный символ без модификаторов — поиск по пунктам, а не команда.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n event.preventDefault()\n typeahead.type(event.key)\n return true\n }\n\n return false\n }\n\n return { items, focusAt, onKeydown, reset: typeahead.reset }\n}\n"],"mappings":";;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,SAA4D;;;;;CAK5F,SAAS,QAAuB;EAC9B,OAAO,qBAAqB,QAAQ,UAAU,GAAG,EAAE,cAAc,MAAM,CAAC;CAC1E;CAEA,SAAS,QAAQ,OAAqB;EACpC,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,WAAW,GAClB;EAKF,MAAM,QAAQ,KAAK,UAAU,KAAK,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,CAAC;CAC1E;CAEA,MAAM,YAAY,aAA0B;EAC1C;EACA,SAAQ,YAAW,QAAQ,eAAe;EAC1C,eAAc,SAAQ,KAAK,QAAQ,SAAS,aAA4B;EACxE,UAAS,YAAW,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CAC3D,CAAC;CAED,SAAS,UAAU,OAA+B;EAEhD,MAAM,eADO,MACQ,CAAA,CAAK,QAAQ,SAAS,aAA4B;EAKvE,MAAM,WAAW,iBAAiB,MAAM,MAAM;EAE9C,QAAQ,MAAM,KAAd;GAGE,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,CAAC;IACT,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,EAAE;IACV,OAAO;GACT,KAAK;IACH,QAAQ,MAAM;IACd,OAAO;EACX;EAEA,IAAI,UACF,OAAO;EAKT,IAAI,MAAM,QAAQ,OAAO,UAAU,QAAQ,GACzC,OAAO;EAGT,IAAI,MAAM,IAAI,WAAW,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,QAAQ;GAC/E,MAAM,eAAe;GACrB,UAAU,KAAK,MAAM,GAAG;GACxB,OAAO;EACT;EAEA,OAAO;CACT;CAEA,OAAO;EAAE;EAAO;EAAS;EAAW,OAAO,UAAU;CAAM;AAC7D"}
1
+ {"version":3,"file":"useMenuItemsFocus-B7Bl_aFA.js","names":[],"sources":["../../src/composables/internal/useMenuItemsFocus.ts"],"sourcesContent":["import { isEditableTarget } from '../../internal/keyboard'\n\nimport { getFocusableElements } from './focusables'\nimport { useTypeahead } from './useTypeahead'\n\nexport interface UseMenuItemsFocusOptions {\n /** Корень, внутри которого живут пункты. */\n container: () => HTMLElement | null\n /** Закрыть меню — `Tab` уводит фокус наружу, панель при этом закрывается. */\n close: () => void\n}\n\nexport interface UseMenuItemsFocusReturn {\n /** Пункты в порядке документа. */\n items: () => HTMLElement[]\n /** Сфокусировать по индексу; отрицательный и превышающий заворачиваются. */\n focusAt: (index: number) => void\n /** Стрелки, `Home`/`End`, `Tab`, typeahead. `true` — клавиша обработана. */\n onKeydown: (event: KeyboardEvent) => boolean\n reset: () => void\n}\n\n/**\n * Клавиатура панели-меню: кольцо фокуса плюс поиск по первым буквам.\n *\n * Пункты ищутся в DOM, а не в модели, и это осознанно: у панели бывает\n * произвольное содержимое из слота, в котором ключей нет вовсе. По той же\n * причине здесь не `useRovingFocus` — он адресуется ключами и ведёт\n * `tabindex=\"0\"` на активном элементе, а пункты меню обязаны быть `-1`.\n */\nexport function useMenuItemsFocus(options: UseMenuItemsFocusOptions): UseMenuItemsFocusReturn {\n /**\n * `tabbableOnly: false` — не оплошность: пункты меню намеренно `tabindex=\"-1\"`,\n * и список участников таб-порядка был бы пуст.\n */\n function items(): HTMLElement[] {\n return getFocusableElements(options.container(), { tabbableOnly: false })\n }\n\n function focusAt(index: number): void {\n const list = items()\n if (list.length === 0)\n return\n\n // `preventScroll` обязателен: панель уже приведена во вьюпорт позиционером,\n // поэтому прокрутка ради фокуса внутри неё паразитная. У контекстного меню\n // она к тому же порождала `scroll`, по которому меню закрывало само себя.\n list[(index + list.length) % list.length]?.focus({ preventScroll: true })\n }\n\n const typeahead = useTypeahead<HTMLElement>({\n items,\n textOf: element => element.textContent ?? '',\n currentIndex: list => list.indexOf(document.activeElement as HTMLElement),\n onMatch: element => element.focus({ preventScroll: true }),\n })\n\n function onKeydown(event: KeyboardEvent): boolean {\n const list = items()\n const currentIndex = list.indexOf(document.activeElement as HTMLElement)\n\n // Панель бывает не только меню из кнопок: с `closeOnContentClick={false}` в\n // ней живут поля и чекбоксы. Клавиши, которые сфокусированный контрол умеет\n // сам, остаются ему — иначе в поле не напечатать, а чекбокс не переключить.\n const editable = isEditableTarget(event.target)\n\n switch (event.key) {\n // Стрелки — исключение: они за меню даже в поле. `Tab` панель закрывает, и\n // без них из поля внутри панели не было бы выхода вовсе.\n case 'ArrowDown':\n event.preventDefault()\n focusAt(currentIndex + 1)\n return true\n case 'ArrowUp':\n event.preventDefault()\n focusAt(currentIndex - 1)\n return true\n case 'Home':\n if (editable)\n return false\n event.preventDefault()\n focusAt(0)\n return true\n case 'End':\n if (editable)\n return false\n event.preventDefault()\n focusAt(-1)\n return true\n case 'Tab':\n options.close()\n return true\n }\n\n if (editable)\n return false\n\n // Пробел при пустом буфере — активация сфокусированного пункта, а не поиск:\n // пункты меню это кнопки, и пробел для них родная клавиша. В буфер он входит,\n // только когда поиск уже идёт (правило typeahead из WAI-ARIA APG).\n if (event.key === ' ' && typeahead.isEmpty())\n return false\n\n // Печатный символ без модификаторов — поиск по пунктам, а не команда.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n event.preventDefault()\n typeahead.type(event.key)\n return true\n }\n\n return false\n }\n\n return { items, focusAt, onKeydown, reset: typeahead.reset }\n}\n"],"mappings":";;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,SAA4D;;;;;CAK5F,SAAS,QAAuB;EAC9B,OAAO,qBAAqB,QAAQ,UAAU,GAAG,EAAE,cAAc,MAAM,CAAC;CAC1E;CAEA,SAAS,QAAQ,OAAqB;EACpC,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,WAAW,GAClB;EAKF,MAAM,QAAQ,KAAK,UAAU,KAAK,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,CAAC;CAC1E;CAEA,MAAM,YAAY,aAA0B;EAC1C;EACA,SAAQ,YAAW,QAAQ,eAAe;EAC1C,eAAc,SAAQ,KAAK,QAAQ,SAAS,aAA4B;EACxE,UAAS,YAAW,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CAC3D,CAAC;CAED,SAAS,UAAU,OAA+B;EAEhD,MAAM,eADO,MACQ,CAAA,CAAK,QAAQ,SAAS,aAA4B;EAKvE,MAAM,WAAW,iBAAiB,MAAM,MAAM;EAE9C,QAAQ,MAAM,KAAd;GAGE,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,CAAC;IACT,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,EAAE;IACV,OAAO;GACT,KAAK;IACH,QAAQ,MAAM;IACd,OAAO;EACX;EAEA,IAAI,UACF,OAAO;EAKT,IAAI,MAAM,QAAQ,OAAO,UAAU,QAAQ,GACzC,OAAO;EAGT,IAAI,MAAM,IAAI,WAAW,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,QAAQ;GAC/E,MAAM,eAAe;GACrB,UAAU,KAAK,MAAM,GAAG;GACxB,OAAO;EACT;EAEA,OAAO;CACT;CAEA,OAAO;EAAE;EAAO;EAAS;EAAW,OAAO,UAAU;CAAM;AAC7D"}
@@ -3,7 +3,7 @@ import { t as usePortalTarget } from "./usePortalTarget-C0p-mhaM.js";
3
3
  import { n as markInert, t as inertableOutside } from "./inert-DIHCCn_1.js";
4
4
  import { r as modalLayerZIndex } from "./overlayStack-0CZS99Da.js";
5
5
  import { useOverlayLayer } from "../composables/useOverlayLayer.js";
6
- import { useFocusTrap } from "../composables/useFocusTrap.js";
6
+ import { t as useFocusTrap } from "./useFocusTrap-D9bIPNF6.js";
7
7
  import { computed, onBeforeUnmount, onScopeDispose, ref, watch } from "vue";
8
8
  //#region src/composables/internal/useInertOthers.ts
9
9
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"useModalOverlay-D__CqLGn.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)\n 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)\n mounted.value = true\n visible.value = isOpen\n })\n\n function onPanelAfterLeave(): void {\n // Пока панель уезжала, слой могли открыть заново — тогда размонтировать\n // нечего и незачем.\n if (!open.value)\n 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')\n 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))\n return false\n\n for (let node: Element | null = target; node; node = node.parentElement) {\n if (!(node instanceof HTMLElement))\n continue\n if (node.hasAttribute('data-gr-overlay-root'))\n return true\n }\n\n return false\n}\n\nfunction applyTouchGuard(): void {\n if (!isIOS())\n return\n\n const onTouchMove = (event: TouchEvent): void => {\n // Внутри оверлея скролл законен (длинное окно, лента изображений), а всё\n // остальное — это и есть фон, который лок обязан держать.\n if (isScrollableOverlayArea(event.target))\n return\n if (event.cancelable)\n 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)\n 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)\n 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)\n return\n held = true\n lockCount += 1\n if (lockCount === 1)\n applyLock()\n }\n\n function unlock(): void {\n if (!isClient || !held)\n return\n held = false\n lockCount = Math.max(0, lockCount - 1)\n if (lockCount === 0)\n releaseLock()\n }\n\n return { lock, unlock }\n}\n\n/** Тестовая/служебная очистка глобального состояния. */\nexport function resetScrollLock(): void {\n lockCount = 0\n if (typeof document !== 'undefined')\n 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 { modalLayerZIndex } from './overlayStack'\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 * Имя CSS-переменной слоя. Escape-hatch мимо `--gr-z-modal` — им пользуется\n * просмотрщик. Глубина слоя прибавляется и к подменённой переменной.\n */\n zIndexVar?: () => string | undefined\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 /**\n * Высота слоя: базовый токен плюс позиция в стеке.\n *\n * Один токен на все модальные слои означал бы, что порядок отрисовки решает\n * порядок узлов в портале, а он задан **созданием** телепорта, а не\n * открытием: статически объявленный диалог вставал бы под окном, открытым\n * позже, и оказывался невидимым — при том что стек считает верхним его.\n */\n layerZIndex: ComputedRef<string>\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 const layerZIndex = computed(() => modalLayerZIndex(\n layer.depth.value,\n options.zIndexVar?.() ?? '--gr-z-modal',\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)\n 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 layerZIndex,\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)\n return\n if (options.closeOnBackdrop?.() === false)\n return\n\n onDismiss()\n },\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useModalOverlay-BnAGZpcI.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)\n 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)\n mounted.value = true\n visible.value = isOpen\n })\n\n function onPanelAfterLeave(): void {\n // Пока панель уезжала, слой могли открыть заново — тогда размонтировать\n // нечего и незачем.\n if (!open.value)\n 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')\n 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))\n return false\n\n for (let node: Element | null = target; node; node = node.parentElement) {\n if (!(node instanceof HTMLElement))\n continue\n if (node.hasAttribute('data-gr-overlay-root'))\n return true\n }\n\n return false\n}\n\nfunction applyTouchGuard(): void {\n if (!isIOS())\n return\n\n const onTouchMove = (event: TouchEvent): void => {\n // Внутри оверлея скролл законен (длинное окно, лента изображений), а всё\n // остальное — это и есть фон, который лок обязан держать.\n if (isScrollableOverlayArea(event.target))\n return\n if (event.cancelable)\n 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)\n 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)\n 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)\n return\n held = true\n lockCount += 1\n if (lockCount === 1)\n applyLock()\n }\n\n function unlock(): void {\n if (!isClient || !held)\n return\n held = false\n lockCount = Math.max(0, lockCount - 1)\n if (lockCount === 0)\n releaseLock()\n }\n\n return { lock, unlock }\n}\n\n/** Тестовая/служебная очистка глобального состояния. */\nexport function resetScrollLock(): void {\n lockCount = 0\n if (typeof document !== 'undefined')\n 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 { modalLayerZIndex } from './overlayStack'\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 * Имя CSS-переменной слоя. Escape-hatch мимо `--gr-z-modal` — им пользуется\n * просмотрщик. Глубина слоя прибавляется и к подменённой переменной.\n */\n zIndexVar?: () => string | undefined\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 /**\n * Высота слоя: базовый токен плюс позиция в стеке.\n *\n * Один токен на все модальные слои означал бы, что порядок отрисовки решает\n * порядок узлов в портале, а он задан **созданием** телепорта, а не\n * открытием: статически объявленный диалог вставал бы под окном, открытым\n * позже, и оказывался невидимым — при том что стек считает верхним его.\n */\n layerZIndex: ComputedRef<string>\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 const layerZIndex = computed(() => modalLayerZIndex(\n layer.depth.value,\n options.zIndexVar?.() ?? '--gr-z-modal',\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)\n 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 layerZIndex,\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)\n return\n if (options.closeOnBackdrop?.() === false)\n return\n\n onDismiss()\n },\n },\n }\n}\n"],"mappings":"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"}
@@ -16,10 +16,10 @@ import { useComboboxNavigation } from "../../../composables/useComboboxNavigatio
16
16
  import { useRovingFocus } from "../../../composables/useRovingFocus.js";
17
17
  import { a as resolveSelectedOptions, t as filterOptions } from "../../../chunks/optionFilter-DYw9XPPx.js";
18
18
  import { t as useControlAddons } from "../../../chunks/useControlAddons-asUr10Pf.js";
19
- import { d as chipSizeForBadgeScale } from "../../../chunks/grChipStyles-3JZApw03.js";
20
- import { t as GrChip_default } from "../../GrChip/chunks/GrChip-C0vU69tf.js";
21
- import { t as panelPopTransition } from "../../../chunks/overlayTransition-0Fj5tQbq.js";
22
- import { a as autocompleteShellClass, i as autocompletePanelClasses, o as autocompleteStateClass, r as autocompleteOptionClass } from "../../../chunks/config-vYMJ3Cr1.js";
19
+ import { d as chipSizeForBadgeScale } from "../../../chunks/grChipStyles-DcDyxAL0.js";
20
+ import { t as GrChip_default } from "../../GrChip/chunks/GrChip-BTivtwff.js";
21
+ import { n as panelPopTransition } from "../../../chunks/overlayPanelSurface-BKeMhRx4.js";
22
+ import { a as autocompleteShellClass, i as autocompletePanelClasses, o as autocompleteStateClass, r as autocompleteOptionClass } from "../../../chunks/config-CYthTVK2.js";
23
23
  import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
24
24
  import { t as chevron_down_default } from "../../../chunks/chevron-down-CQS2OG_s.js";
25
25
  import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BOaGB7Aw.js";