@ryuzaki13/react-foundation-lib 2.5.0 → 2.7.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.
- package/dist/chunks/dom-Bin1L9-q.js +315 -0
- package/dist/chunks/dom-Bin1L9-q.js.map +1 -0
- package/dist/dom/document-scroll/acquireDocumentScrollLock.d.ts +3 -0
- package/dist/dom/document-scroll/acquireDocumentScrollLock.d.ts.map +1 -0
- package/dist/dom/document-scroll/documentScrollLockTypes.d.ts +8 -0
- package/dist/dom/document-scroll/documentScrollLockTypes.d.ts.map +1 -0
- package/dist/dom/index.d.ts +6 -0
- package/dist/dom/index.d.ts.map +1 -1
- package/dist/dom/index.js +2 -159
- package/dist/dom/scroll-anchor/captureScrollAnchor.d.ts +8 -0
- package/dist/dom/scroll-anchor/captureScrollAnchor.d.ts.map +1 -0
- package/dist/dom/scroll-anchor/scrollAnchorTypes.d.ts +8 -0
- package/dist/dom/scroll-anchor/scrollAnchorTypes.d.ts.map +1 -0
- package/dist/dom/useDocumentScrollLock.d.ts +4 -0
- package/dist/dom/useDocumentScrollLock.d.ts.map +1 -0
- package/dist/dom/useVisualViewportFrame.d.ts +4 -0
- package/dist/dom/useVisualViewportFrame.d.ts.map +1 -0
- package/dist/dom/visual-viewport/attachVisualViewportFrame.d.ts +3 -0
- package/dist/dom/visual-viewport/attachVisualViewportFrame.d.ts.map +1 -0
- package/dist/dom/visual-viewport/visualViewportFrameTypes.d.ts +7 -0
- package/dist/dom/visual-viewport/visualViewportFrameTypes.d.ts.map +1 -0
- package/dist/excel/index.js +1 -1
- package/package.json +2 -2
- package/src/dom/README.mdx +142 -1
- package/dist/dom/index.js.map +0 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { useEffect as e, useEffectEvent as t, useLayoutEffect as n, useRef as r, useState as i } from "react";
|
|
2
|
+
//#region src/dom/downloadFile.ts
|
|
3
|
+
function a(e, t) {
|
|
4
|
+
let n = document.createElement("a");
|
|
5
|
+
n.href = t, n.download = e, n.click(), window.setTimeout(() => {
|
|
6
|
+
window.URL.revokeObjectURL(t);
|
|
7
|
+
}, 0);
|
|
8
|
+
}
|
|
9
|
+
function o(e, t) {
|
|
10
|
+
a(e, window.URL.createObjectURL(t));
|
|
11
|
+
}
|
|
12
|
+
function s(e, t) {
|
|
13
|
+
o(e, new Blob([JSON.stringify(t, null, 2)], { type: "application/json;charset=utf-8" }));
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/dom/getOrCreatePortalRoot.ts
|
|
17
|
+
function c(e) {
|
|
18
|
+
if (typeof document > "u") return null;
|
|
19
|
+
let t = document.getElementById(e);
|
|
20
|
+
if (t instanceof HTMLElement) return t;
|
|
21
|
+
let n = document.createElement("div");
|
|
22
|
+
return n.id = e, document.body.append(n), n;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/dom/scroll-anchor/captureScrollAnchor.ts
|
|
26
|
+
var l = /* @__PURE__ */ new WeakMap();
|
|
27
|
+
function u(e, t) {
|
|
28
|
+
if (!e?.isConnected || !t?.isConnected || e === t || !e.contains(t)) return null;
|
|
29
|
+
l.get(e)?.release();
|
|
30
|
+
let n = t.getBoundingClientRect().top - e.getBoundingClientRect().top - e.clientTop + e.scrollTop, r = e.style.getPropertyValue("overflow-anchor"), i = e.style.getPropertyPriority("overflow-anchor"), a = !0;
|
|
31
|
+
e.style.setProperty("overflow-anchor", "none", "important");
|
|
32
|
+
let o = e.style.getPropertyValue("overflow-anchor"), s = e.style.getPropertyPriority("overflow-anchor"), c = () => e.style.getPropertyValue("overflow-anchor") === o && e.style.getPropertyPriority("overflow-anchor") === s, u = {
|
|
33
|
+
release() {
|
|
34
|
+
a && (a = !1, l.delete(e), c() && (r ? e.style.setProperty("overflow-anchor", r, i) : e.style.removeProperty("overflow-anchor")));
|
|
35
|
+
},
|
|
36
|
+
restore() {
|
|
37
|
+
if (!a) return !1;
|
|
38
|
+
try {
|
|
39
|
+
if (!e.isConnected || !t.isConnected || !e.contains(t) || !c()) return !1;
|
|
40
|
+
let r = t.getBoundingClientRect().top - e.getBoundingClientRect().top - e.clientTop + e.scrollTop - n;
|
|
41
|
+
return r !== 0 && e.scrollBy({
|
|
42
|
+
top: r,
|
|
43
|
+
behavior: "instant"
|
|
44
|
+
}), !0;
|
|
45
|
+
} finally {
|
|
46
|
+
u.release();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return l.set(e, u), u;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/dom/useClickOutside.ts
|
|
54
|
+
function d(t, n) {
|
|
55
|
+
e(() => {
|
|
56
|
+
let e = (e) => {
|
|
57
|
+
if (Array.isArray(t)) {
|
|
58
|
+
let r = !0;
|
|
59
|
+
for (let n of t) n.current && n.current.contains(e.target) && (r = !1);
|
|
60
|
+
r && n();
|
|
61
|
+
} else t.current && !t.current.contains(e.target) && n();
|
|
62
|
+
};
|
|
63
|
+
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
64
|
+
}, [t, n]);
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/dom/useElementHeightObserver.ts
|
|
68
|
+
function f(e) {
|
|
69
|
+
let [t, r] = i(0);
|
|
70
|
+
return n(() => {
|
|
71
|
+
let t = e.current;
|
|
72
|
+
if (!t) return;
|
|
73
|
+
let n = new ResizeObserver((e) => {
|
|
74
|
+
let n = e[0]?.contentRect.height ?? t.getBoundingClientRect().height;
|
|
75
|
+
r(n);
|
|
76
|
+
});
|
|
77
|
+
return n.observe(t), () => n.disconnect();
|
|
78
|
+
}, [e.current]), t;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/dom/useEscapeDismiss.ts
|
|
82
|
+
function p({ active: n, onDismiss: r, enabled: i = !0, documentTarget: a, containerRef: o }) {
|
|
83
|
+
let s = t(() => r());
|
|
84
|
+
e(() => {
|
|
85
|
+
if (!n || !i) return;
|
|
86
|
+
let e = a ?? (typeof document > "u" ? null : document);
|
|
87
|
+
if (!e) return;
|
|
88
|
+
let t = (t) => {
|
|
89
|
+
if (t.key !== "Escape") return;
|
|
90
|
+
let n = e.activeElement, r = o?.current;
|
|
91
|
+
r && n instanceof Node && n !== r && !r.contains(n) || (t.preventDefault(), s());
|
|
92
|
+
};
|
|
93
|
+
return e.addEventListener("keydown", t, !0), () => e.removeEventListener("keydown", t, !0);
|
|
94
|
+
}, [
|
|
95
|
+
n,
|
|
96
|
+
i,
|
|
97
|
+
a,
|
|
98
|
+
o
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/dom/useOverlayFocus.ts
|
|
103
|
+
var m = "button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable=\"true\"]:not([tabindex=\"-1\"]), [contenteditable=\"\"]:not([tabindex=\"-1\"]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])";
|
|
104
|
+
function h({ active: n, trapFocus: i = !1, restoreFocus: a = !0, initialFocus: o = "auto", restoreFocusTarget: s, containerRef: c }) {
|
|
105
|
+
let l = r(null), u = c ?? l, d = r(null), f = t((e) => Array.from(e.querySelectorAll(m)).filter((e) => !e.hasAttribute("aria-hidden"))), p = t((e) => {
|
|
106
|
+
if (o === "container") return e;
|
|
107
|
+
if (typeof o == "function") return o(e) ?? e;
|
|
108
|
+
let t = f(e);
|
|
109
|
+
return t.find((e) => e.hasAttribute("autofocus") || e.hasAttribute("autoFocus")) ?? t[0] ?? e;
|
|
110
|
+
}), h = t(() => s?.() ?? d.current);
|
|
111
|
+
return e(() => {
|
|
112
|
+
if (!n) return;
|
|
113
|
+
d.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
114
|
+
let e = null, t = 0, r = () => {
|
|
115
|
+
if (e = u.current, !e) {
|
|
116
|
+
t = window.requestAnimationFrame(r);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
let n = p(e);
|
|
120
|
+
n instanceof HTMLElement && n.isConnected && n.focus({ preventScroll: !0 }), i && e.addEventListener("keydown", o);
|
|
121
|
+
}, o = (t) => {
|
|
122
|
+
if (!i || t.key !== "Tab" || !e) return;
|
|
123
|
+
let n = f(e);
|
|
124
|
+
if (n.length === 0) {
|
|
125
|
+
t.preventDefault(), e.focus();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let r = n[0], a = n[n.length - 1], o = document.activeElement;
|
|
129
|
+
if (t.shiftKey) {
|
|
130
|
+
(o === r || o === e) && (t.preventDefault(), a.focus());
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
o === a && (t.preventDefault(), r.focus());
|
|
134
|
+
};
|
|
135
|
+
return r(), () => {
|
|
136
|
+
if (window.cancelAnimationFrame(t), i && e && e.removeEventListener("keydown", o), !a) return;
|
|
137
|
+
let n = h();
|
|
138
|
+
n?.isConnected && n.focus({ preventScroll: !0 });
|
|
139
|
+
};
|
|
140
|
+
}, [
|
|
141
|
+
n,
|
|
142
|
+
i,
|
|
143
|
+
a,
|
|
144
|
+
u
|
|
145
|
+
]), u;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/dom/useFocusTrap.ts
|
|
149
|
+
var g = (e) => h({
|
|
150
|
+
active: e,
|
|
151
|
+
trapFocus: !0,
|
|
152
|
+
restoreFocus: !0,
|
|
153
|
+
initialFocus: "auto"
|
|
154
|
+
});
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/dom/useIntersectionObserver.ts
|
|
157
|
+
function _(t, n) {
|
|
158
|
+
let [r, a] = i(!1);
|
|
159
|
+
return e(() => {
|
|
160
|
+
let e = t.current;
|
|
161
|
+
if (!e) return;
|
|
162
|
+
let r = new IntersectionObserver(([e]) => {
|
|
163
|
+
a(e.isIntersecting);
|
|
164
|
+
}, n);
|
|
165
|
+
return r.observe(e), () => {
|
|
166
|
+
e && r.unobserve(e), r.disconnect();
|
|
167
|
+
};
|
|
168
|
+
}, [n, t]), { isIntersecting: r };
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/dom/useIsTouchDevice.ts
|
|
172
|
+
var v = () => {
|
|
173
|
+
let [t, n] = i(!1);
|
|
174
|
+
return e(() => {
|
|
175
|
+
let e = () => {
|
|
176
|
+
let e = window.matchMedia("(pointer: coarse)").matches || "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
177
|
+
n(e);
|
|
178
|
+
};
|
|
179
|
+
return e(), window.addEventListener("resize", e), () => {
|
|
180
|
+
window.removeEventListener("resize", e);
|
|
181
|
+
};
|
|
182
|
+
}, []), t;
|
|
183
|
+
}, y = /* @__PURE__ */ new WeakMap();
|
|
184
|
+
function b(e, t) {
|
|
185
|
+
return t.map((t) => ({
|
|
186
|
+
name: t,
|
|
187
|
+
value: e.getPropertyValue(t),
|
|
188
|
+
priority: e.getPropertyPriority(t)
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
function x(e, t) {
|
|
192
|
+
for (let { name: n } of t) e.removeProperty(n);
|
|
193
|
+
for (let { name: n, value: r, priority: i } of t) r && e.setProperty(n, r, i);
|
|
194
|
+
}
|
|
195
|
+
function S(e, t) {
|
|
196
|
+
let n = e.defaultView, r = e.body;
|
|
197
|
+
if (!n || !r) return () => void 0;
|
|
198
|
+
let i = y.get(e);
|
|
199
|
+
if (!i) {
|
|
200
|
+
let t = e.documentElement.style, a = r.style, o = b(t, [
|
|
201
|
+
"overflow",
|
|
202
|
+
"overflow-x",
|
|
203
|
+
"overflow-y"
|
|
204
|
+
]), s = b(a, [
|
|
205
|
+
"position",
|
|
206
|
+
"top",
|
|
207
|
+
"left",
|
|
208
|
+
"width",
|
|
209
|
+
"overflow",
|
|
210
|
+
"overflow-x",
|
|
211
|
+
"overflow-y",
|
|
212
|
+
"box-sizing",
|
|
213
|
+
"padding-right"
|
|
214
|
+
]), c = b(a, ["padding-right"]), l = n.scrollX, u = n.scrollY, d = Math.max(0, n.innerWidth - e.documentElement.clientWidth), f = Number.parseFloat(n.getComputedStyle(r).paddingRight) || 0, p = /* @__PURE__ */ new Map();
|
|
215
|
+
t.setProperty("overflow", "hidden"), a.setProperty("position", "fixed"), a.setProperty("top", `${-u}px`), a.setProperty("left", `${-l}px`), a.setProperty("width", "100%"), a.setProperty("overflow", "hidden"), a.setProperty("box-sizing", "border-box"), i = {
|
|
216
|
+
leases: p,
|
|
217
|
+
updateCompensation: () => {
|
|
218
|
+
d > 0 && [...p.values()].some(Boolean) ? a.setProperty("padding-right", `${f + d}px`) : x(a, c);
|
|
219
|
+
},
|
|
220
|
+
restore: () => {
|
|
221
|
+
x(t, o), x(a, s), n.scrollTo({
|
|
222
|
+
left: l,
|
|
223
|
+
top: u,
|
|
224
|
+
behavior: "instant"
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}, y.set(e, i);
|
|
228
|
+
}
|
|
229
|
+
let a = Symbol();
|
|
230
|
+
return i.leases.set(a, t), i.updateCompensation(), () => {
|
|
231
|
+
i.leases.delete(a) && (i.leases.size > 0 ? i.updateCompensation() : (y.delete(e), i.restore()));
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/dom/useDocumentScrollLock.ts
|
|
236
|
+
function C({ active: t, documentTarget: n, compensateScrollbar: r = !1 }) {
|
|
237
|
+
e(() => {
|
|
238
|
+
if (!t) return;
|
|
239
|
+
let e = n === void 0 ? typeof document > "u" ? null : document : n;
|
|
240
|
+
if (e) return S(e, r);
|
|
241
|
+
}, [
|
|
242
|
+
t,
|
|
243
|
+
n,
|
|
244
|
+
r
|
|
245
|
+
]);
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/dom/visual-viewport/attachVisualViewportFrame.ts
|
|
249
|
+
var w = [
|
|
250
|
+
"--visual-viewport-top",
|
|
251
|
+
"--visual-viewport-left",
|
|
252
|
+
"--visual-viewport-width",
|
|
253
|
+
"--visual-viewport-height"
|
|
254
|
+
], T = /* @__PURE__ */ new WeakMap();
|
|
255
|
+
function E(e) {
|
|
256
|
+
let t = e.ownerDocument.defaultView;
|
|
257
|
+
if (!t) return () => void 0;
|
|
258
|
+
let n = t.visualViewport, r = e.style, i = w.map((e) => ({
|
|
259
|
+
name: e,
|
|
260
|
+
value: r.getPropertyValue(e),
|
|
261
|
+
priority: r.getPropertyPriority(e)
|
|
262
|
+
})), a = /* @__PURE__ */ new Map(), o = 0, s = () => {
|
|
263
|
+
o = 0;
|
|
264
|
+
let e = [
|
|
265
|
+
n?.offsetTop ?? 0,
|
|
266
|
+
n?.offsetLeft ?? 0,
|
|
267
|
+
n?.width ?? t.innerWidth,
|
|
268
|
+
n?.height ?? t.innerHeight
|
|
269
|
+
], i = [
|
|
270
|
+
0,
|
|
271
|
+
0,
|
|
272
|
+
t.innerWidth,
|
|
273
|
+
t.innerHeight
|
|
274
|
+
];
|
|
275
|
+
w.forEach((t, n) => {
|
|
276
|
+
let o = `${Number.isFinite(e[n]) ? e[n] : i[n]}px`;
|
|
277
|
+
a.get(t) !== o && (r.setProperty(t, o), a.set(t, o));
|
|
278
|
+
});
|
|
279
|
+
}, c = () => {
|
|
280
|
+
o ||= t.requestAnimationFrame(s);
|
|
281
|
+
};
|
|
282
|
+
return s(), n?.addEventListener("resize", c), n?.addEventListener("scroll", c), t.addEventListener("resize", c), () => {
|
|
283
|
+
t.cancelAnimationFrame(o), n?.removeEventListener("resize", c), n?.removeEventListener("scroll", c), t.removeEventListener("resize", c);
|
|
284
|
+
for (let { name: e, value: t, priority: n } of i) r.getPropertyValue(e) !== a.get(e) || r.getPropertyPriority(e) || (t ? r.setProperty(e, t, n) : r.removeProperty(e));
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function D(e) {
|
|
288
|
+
let t = T.get(e);
|
|
289
|
+
t || (t = {
|
|
290
|
+
leases: /* @__PURE__ */ new Set(),
|
|
291
|
+
detach: E(e)
|
|
292
|
+
}, T.set(e, t));
|
|
293
|
+
let n = Symbol();
|
|
294
|
+
return t.leases.add(n), () => {
|
|
295
|
+
!t.leases.delete(n) || t.leases.size > 0 || (T.delete(e), t.detach());
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/dom/useVisualViewportFrame.ts
|
|
300
|
+
function O({ active: t, containerRef: n }) {
|
|
301
|
+
e(() => {
|
|
302
|
+
if (!t || typeof window > "u") return;
|
|
303
|
+
let e = 0, r, i = () => {
|
|
304
|
+
let t = n.current;
|
|
305
|
+
t ? r = D(t) : e = window.requestAnimationFrame(i);
|
|
306
|
+
};
|
|
307
|
+
return i(), () => {
|
|
308
|
+
window.cancelAnimationFrame(e), r?.();
|
|
309
|
+
};
|
|
310
|
+
}, [t, n]);
|
|
311
|
+
}
|
|
312
|
+
//#endregion
|
|
313
|
+
export { g as a, f as c, c as d, o as f, _ as i, d as l, a as m, C as n, h as o, s as p, v as r, p as s, O as t, u };
|
|
314
|
+
|
|
315
|
+
//# sourceMappingURL=dom-Bin1L9-q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-Bin1L9-q.js","names":[],"sources":["../../src/dom/downloadFile.ts","../../src/dom/getOrCreatePortalRoot.ts","../../src/dom/scroll-anchor/captureScrollAnchor.ts","../../src/dom/useClickOutside.ts","../../src/dom/useElementHeightObserver.ts","../../src/dom/useEscapeDismiss.ts","../../src/dom/useOverlayFocus.ts","../../src/dom/useFocusTrap.ts","../../src/dom/useIntersectionObserver.ts","../../src/dom/useIsTouchDevice.ts","../../src/dom/document-scroll/acquireDocumentScrollLock.ts","../../src/dom/useDocumentScrollLock.ts","../../src/dom/visual-viewport/attachVisualViewportFrame.ts","../../src/dom/useVisualViewportFrame.ts"],"sourcesContent":["export function downloadFileFromObjectURL(filename: string, objectUrl: string) {\n\tconst link = document.createElement(\"a\");\n\n\tlink.href = objectUrl;\n\tlink.download = filename;\n\tlink.click();\n\n\twindow.setTimeout(() => {\n\t\twindow.URL.revokeObjectURL(objectUrl);\n\t}, 0);\n}\n\nexport function downloadFileFromBlob(filename: string, blob: Blob) {\n\tconst objectUrl = window.URL.createObjectURL(blob);\n\tdownloadFileFromObjectURL(filename, objectUrl);\n}\n\nexport function downloadFileFromJson(filename: string, payload: unknown) {\n\tconst blob = new Blob([JSON.stringify(payload, null, 2)], { type: \"application/json;charset=utf-8\" });\n\tdownloadFileFromBlob(filename, blob);\n}\n","export function getOrCreatePortalRoot(id: string): HTMLElement | null {\n\tif (typeof document === \"undefined\") {\n\t\treturn null;\n\t}\n\n\tconst existingElement = document.getElementById(id);\n\tif (existingElement instanceof HTMLElement) {\n\t\treturn existingElement;\n\t}\n\n\tconst portalRoot = document.createElement(\"div\");\n\tportalRoot.id = id;\n\tdocument.body.append(portalRoot);\n\n\treturn portalRoot;\n}\n","import { type CapturedScrollAnchor } from \"./scrollAnchorTypes\";\n\n// Один scroll host не может одновременно компенсировать один и тот же prepend дважды.\nconst activeAnchors = new WeakMap<HTMLElement, CapturedScrollAnchor>();\n\n/**\n * Захватывает позицию сохраняемого элемента перед изменением обычного вертикального списка.\n * Вызывать только на browser event/effect boundary; restore — после DOM commit, release — при отмене.\n * Новый захват того же viewport завершает предыдущий без прокрутки.\n */\nexport function captureScrollAnchor(viewport: HTMLElement | null, anchor: HTMLElement | null): CapturedScrollAnchor | null {\n\tif (!viewport?.isConnected || !anchor?.isConnected || viewport === anchor || !viewport.contains(anchor)) return null;\n\n\tactiveAnchors.get(viewport)?.release();\n\tconst initialOffset =\n\t\tanchor.getBoundingClientRect().top - viewport.getBoundingClientRect().top - viewport.clientTop + viewport.scrollTop;\n\tconst initialStyle = viewport.style.getPropertyValue(\"overflow-anchor\");\n\tconst initialPriority = viewport.style.getPropertyPriority(\"overflow-anchor\");\n\tlet active = true;\n\n\t// Native anchoring иначе успеет компенсировать prepend до restore и приведёт к двойному сдвигу.\n\tviewport.style.setProperty(\"overflow-anchor\", \"none\", \"important\");\n\t// В браузере без этого CSS-свойства native anchoring тоже отсутствует: сравниваем фактически принятый стиль.\n\tconst ownedStyle = viewport.style.getPropertyValue(\"overflow-anchor\");\n\tconst ownedPriority = viewport.style.getPropertyPriority(\"overflow-anchor\");\n\tconst ownsStyle = () =>\n\t\tviewport.style.getPropertyValue(\"overflow-anchor\") === ownedStyle &&\n\t\tviewport.style.getPropertyPriority(\"overflow-anchor\") === ownedPriority;\n\n\tconst operation: CapturedScrollAnchor = {\n\t\trelease() {\n\t\t\tif (!active) return;\n\t\t\tactive = false;\n\t\t\tactiveAnchors.delete(viewport);\n\t\t\t// Не затираем новое решение host, если он сам изменил свойство во время ожидания.\n\t\t\tif (!ownsStyle()) return;\n\t\t\tif (initialStyle) viewport.style.setProperty(\"overflow-anchor\", initialStyle, initialPriority);\n\t\t\telse viewport.style.removeProperty(\"overflow-anchor\");\n\t\t},\n\t\trestore() {\n\t\t\tif (!active) return false;\n\t\t\ttry {\n\t\t\t\tif (!viewport.isConnected || !anchor.isConnected || !viewport.contains(anchor) || !ownsStyle()) return false;\n\t\t\t\tconst currentOffset =\n\t\t\t\t\tanchor.getBoundingClientRect().top - viewport.getBoundingClientRect().top - viewport.clientTop + viewport.scrollTop;\n\t\t\t\tconst delta = currentOffset - initialOffset;\n\t\t\t\t// Content-space delta не отменяет ручную прокрутку между запросом и commit.\n\t\t\t\t// Instant не наследует CSS smooth; горизонтальная позиция остаётся неизменной.\n\t\t\t\tif (delta !== 0) viewport.scrollBy({ top: delta, behavior: \"instant\" });\n\t\t\t\treturn true;\n\t\t\t} finally {\n\t\t\t\toperation.release();\n\t\t\t}\n\t\t}\n\t};\n\tactiveAnchors.set(viewport, operation);\n\treturn operation;\n}\n","import { RefObject, useEffect } from \"react\";\n\nexport function useClickOutside(ref: RefObject<HTMLElement | null> | RefObject<HTMLElement | null>[], handler: () => void) {\n\tuseEffect(() => {\n\t\tconst handleClickOutside = (event: MouseEvent) => {\n\t\t\tif (Array.isArray(ref)) {\n\t\t\t\tlet isOutside = true;\n\t\t\t\tfor (const r of ref) {\n\t\t\t\t\tif (r.current && r.current.contains(event.target as Node)) {\n\t\t\t\t\t\tisOutside = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isOutside) {\n\t\t\t\t\thandler();\n\t\t\t\t}\n\t\t\t} else if (ref.current && !ref.current.contains(event.target as Node)) {\n\t\t\t\thandler();\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\treturn () => document.removeEventListener(\"mousedown\", handleClickOutside);\n\t}, [ref, handler]);\n}\n","import { type RefObject, useLayoutEffect, useState } from \"react\";\n\nexport function useElementHeightObserver<TElement extends HTMLElement>(elementRef: RefObject<TElement | null>) {\n\tconst [height, setHeight] = useState(0);\n\n\tuseLayoutEffect(() => {\n\t\tconst element = elementRef.current;\n\t\tif (!element) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst observer = new ResizeObserver((entries) => {\n\t\t\tconst nextHeight = entries[0]?.contentRect.height ?? element.getBoundingClientRect().height;\n\t\t\tsetHeight(nextHeight);\n\t\t});\n\t\tobserver.observe(element);\n\n\t\treturn () => observer.disconnect();\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps, react-hooks/refs\n\t}, [elementRef.current]);\n\n\treturn height;\n}\n","import { RefObject, useEffect, useEffectEvent } from \"react\";\n\ninterface UseEscapeDismissOptions {\n\tactive: boolean;\n\tonDismiss: () => void;\n\tenabled?: boolean;\n\tdocumentTarget?: Document | null;\n\tcontainerRef?: RefObject<HTMLElement | null>;\n}\n\n/**\n * Подписывает overlay на закрытие по клавише Escape.\n */\nexport function useEscapeDismiss({ active, onDismiss, enabled = true, documentTarget, containerRef }: UseEscapeDismissOptions) {\n\tconst dismiss = useEffectEvent(() => onDismiss());\n\n\tuseEffect(() => {\n\t\tif (!active || !enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst targetDocument = documentTarget ?? (typeof document === \"undefined\" ? null : document);\n\t\tif (!targetDocument) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleKeyDown = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== \"Escape\" /*|| event.defaultPrevented*/) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst activeElement = targetDocument.activeElement;\n\t\t\tconst container = containerRef?.current;\n\t\t\tif (container && activeElement instanceof Node && activeElement !== container && !container.contains(activeElement)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tevent.preventDefault();\n\t\t\tdismiss();\n\t\t};\n\n\t\ttargetDocument.addEventListener(\"keydown\", handleKeyDown, true);\n\t\treturn () => targetDocument.removeEventListener(\"keydown\", handleKeyDown, true);\n\t}, [active, enabled, documentTarget, containerRef]);\n}\n","import { RefObject, useEffect, useEffectEvent, useRef } from \"react\";\n\nconst focusableElementsSelector =\n\t'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable=\"true\"]:not([tabindex=\"-1\"]), [contenteditable=\"\"]:not([tabindex=\"-1\"]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])';\n\nexport interface UseOverlayFocusOptions<T extends HTMLElement> {\n\tactive: boolean;\n\ttrapFocus?: boolean;\n\trestoreFocus?: boolean;\n\tinitialFocus?: \"auto\" | \"container\" | ((container: T) => HTMLElement | null | undefined);\n\trestoreFocusTarget?: () => HTMLElement | null | undefined;\n\tcontainerRef?: RefObject<T | null>;\n}\n\n/**\n * Возвращает ref overlay-контейнера и управляет начальными/возвратными переходами фокуса.\n * Открытие и закрытие не прокручивают документ: портал может получить фокус\n * до завершения позиционирования. Пользовательский Tab по-прежнему раскрывает\n * выбранный элемент в прокручиваемом содержимом overlay.\n */\nexport function useOverlayFocus<T extends HTMLElement>({\n\tactive,\n\ttrapFocus = false,\n\trestoreFocus = true,\n\tinitialFocus = \"auto\",\n\trestoreFocusTarget,\n\tcontainerRef\n}: UseOverlayFocusOptions<T>) {\n\tconst internalOverlayRef = useRef<T | null>(null);\n\tconst overlayRef = containerRef ?? internalOverlayRef;\n\tconst previousActiveElementRef = useRef<HTMLElement | null>(null);\n\n\t/**\n\t * Собирает фокусируемые элементы overlay в актуальном DOM-порядке.\n\t */\n\tconst getFocusableElements = useEffectEvent((container: T) =>\n\t\tArray.from(container.querySelectorAll<HTMLElement>(focusableElementsSelector)).filter(\n\t\t\t(element) => !element.hasAttribute(\"aria-hidden\")\n\t\t)\n\t);\n\n\t/**\n\t * Вычисляет стартовый фокус при открытии overlay.\n\t */\n\tconst resolveInitialFocus = useEffectEvent((container: T) => {\n\t\tif (initialFocus === \"container\") {\n\t\t\treturn container;\n\t\t}\n\n\t\tif (typeof initialFocus === \"function\") {\n\t\t\treturn initialFocus(container) ?? container;\n\t\t}\n\n\t\tconst focusableElements = getFocusableElements(container);\n\t\treturn (\n\t\t\tfocusableElements.find((element) => element.hasAttribute(\"autofocus\") || element.hasAttribute(\"autoFocus\")) ??\n\t\t\tfocusableElements[0] ??\n\t\t\tcontainer\n\t\t);\n\t});\n\n\t/**\n\t * Вычисляет элемент, которому нужно вернуть фокус после закрытия overlay.\n\t */\n\tconst resolveRestoreFocusTarget = useEffectEvent(() => restoreFocusTarget?.() ?? previousActiveElementRef.current);\n\n\tuseEffect(() => {\n\t\tif (!active) {\n\t\t\treturn;\n\t\t}\n\n\t\tpreviousActiveElementRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n\t\tlet overlayElement: T | null = null;\n\t\tlet rafId = 0;\n\n\t\t/**\n\t\t * Цикл откладывает инициализацию, пока ref контейнера не будет привязан к DOM.\n\t\t */\n\t\tconst attachOverlay = () => {\n\t\t\toverlayElement = overlayRef.current;\n\t\t\tif (!overlayElement) {\n\t\t\t\trafId = window.requestAnimationFrame(attachOverlay);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst focusTarget = resolveInitialFocus(overlayElement);\n\t\t\tif (focusTarget instanceof HTMLElement && focusTarget.isConnected) {\n\t\t\t\tfocusTarget.focus({ preventScroll: true });\n\t\t\t}\n\n\t\t\tif (trapFocus) {\n\t\t\t\toverlayElement.addEventListener(\"keydown\", handleTabKey);\n\t\t\t}\n\t\t};\n\n\t\t/**\n\t\t * Зацикливает Tab внутри overlay, пока он открыт как модальный слой.\n\t\t */\n\t\tconst handleTabKey = (event: KeyboardEvent) => {\n\t\t\tif (!trapFocus || event.key !== \"Tab\" || !overlayElement) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst focusableElements = getFocusableElements(overlayElement);\n\t\t\tif (focusableElements.length === 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\toverlayElement.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst firstElement = focusableElements[0];\n\t\t\tconst lastElement = focusableElements[focusableElements.length - 1];\n\t\t\tconst activeElement = document.activeElement;\n\n\t\t\tif (event.shiftKey) {\n\t\t\t\tif (activeElement === firstElement || activeElement === overlayElement) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tlastElement.focus();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (activeElement === lastElement) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfirstElement.focus();\n\t\t\t}\n\t\t};\n\n\t\tattachOverlay();\n\n\t\treturn () => {\n\t\t\twindow.cancelAnimationFrame(rafId);\n\t\t\tif (trapFocus && overlayElement) {\n\t\t\t\toverlayElement.removeEventListener(\"keydown\", handleTabKey);\n\t\t\t}\n\n\t\t\tif (!restoreFocus) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst restoreTarget = resolveRestoreFocusTarget();\n\t\t\tif (restoreTarget?.isConnected) {\n\t\t\t\trestoreTarget.focus({ preventScroll: true });\n\t\t\t}\n\t\t};\n\t}, [active, trapFocus, restoreFocus, overlayRef]);\n\n\treturn overlayRef;\n}\n","import { useOverlayFocus } from \"./useOverlayFocus\";\n\n/**\n * Совместимый wrapper для overlay с фокус-ловушкой.\n */\nexport const useFocusTrap = (isOpen: boolean) => {\n\treturn useOverlayFocus<HTMLDivElement>({ active: isOpen, trapFocus: true, restoreFocus: true, initialFocus: \"auto\" });\n};\n","import { useEffect, useState } from \"react\";\n\nexport function useIntersectionObserver(ref: React.RefObject<Element | null>, options?: IntersectionObserverInit) {\n\tconst [isIntersecting, setIsIntersecting] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tconst observer = new IntersectionObserver(([entry]) => {\n\t\t\tsetIsIntersecting(entry.isIntersecting);\n\t\t}, options);\n\n\t\tobserver.observe(el);\n\n\t\treturn () => {\n\t\t\tif (el) {\n\t\t\t\tobserver.unobserve(el);\n\t\t\t}\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [options, ref]); // не забыть, что ref.current может меняться\n\n\treturn { isIntersecting };\n}\n","import { useEffect, useState } from \"react\";\n\nexport const useIsTouchDevice = () => {\n\tconst [isTouchDevice, setIsTouchDevice] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst checkTouchDevice = () => {\n\t\t\tconst isTouch = window.matchMedia(\"(pointer: coarse)\").matches || \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n\t\t\tsetIsTouchDevice(isTouch);\n\t\t};\n\n\t\tcheckTouchDevice();\n\n\t\t// Обработчик для изменений в типе устройства\n\t\twindow.addEventListener(\"resize\", checkTouchDevice);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"resize\", checkTouchDevice);\n\t\t};\n\t}, []);\n\n\treturn isTouchDevice;\n};\n","type InlineProperty = Readonly<{ name: string; value: string; priority: string }>;\n\ntype DocumentScrollLock = Readonly<{\n\tleases: Map<symbol, boolean>;\n\tupdateCompensation: () => void;\n\trestore: () => void;\n}>;\n\nconst locks = new WeakMap<Document, DocumentScrollLock>();\n\n/** Снимок ограничен принадлежащими lock свойствами, остальные inline-правки не затрагиваются. */\nfunction captureProperties(style: CSSStyleDeclaration, names: readonly string[]): readonly InlineProperty[] {\n\treturn names.map((name) => ({ name, value: style.getPropertyValue(name), priority: style.getPropertyPriority(name) }));\n}\n\nfunction restoreProperties(style: CSSStyleDeclaration, properties: readonly InlineProperty[]): void {\n\t// Сначала снимаем весь принадлежащий нам shorthand/longhand набор: иначе позднее\n\t// удаление пустого longhand может отменить только что восстановленный shorthand.\n\tfor (const { name } of properties) style.removeProperty(name);\n\tfor (const { name, value, priority } of properties) {\n\t\tif (value) style.setProperty(name, value, priority);\n\t}\n}\n\n/** Один владелец body/html на документ предотвращает преждевременный unlock вложенных overlays. */\nexport function acquireDocumentScrollLock(documentTarget: Document, compensateScrollbar: boolean): () => void {\n\tconst view = documentTarget.defaultView;\n\tconst body = documentTarget.body;\n\tif (!view || !body) return () => undefined;\n\n\tlet lock = locks.get(documentTarget);\n\tif (!lock) {\n\t\tconst rootStyle = documentTarget.documentElement.style;\n\t\tconst bodyStyle = body.style;\n\t\tconst rootProperties = captureProperties(rootStyle, [\"overflow\", \"overflow-x\", \"overflow-y\"]);\n\t\tconst bodyProperties = captureProperties(bodyStyle, [\n\t\t\t\"position\",\n\t\t\t\"top\",\n\t\t\t\"left\",\n\t\t\t\"width\",\n\t\t\t\"overflow\",\n\t\t\t\"overflow-x\",\n\t\t\t\"overflow-y\",\n\t\t\t\"box-sizing\",\n\t\t\t\"padding-right\"\n\t\t]);\n\t\tconst originalPadding = captureProperties(bodyStyle, [\"padding-right\"]);\n\t\tconst scrollX = view.scrollX;\n\t\tconst scrollY = view.scrollY;\n\t\tconst scrollbarWidth = Math.max(0, view.innerWidth - documentTarget.documentElement.clientWidth);\n\t\tconst computedPadding = Number.parseFloat(view.getComputedStyle(body).paddingRight) || 0;\n\t\tconst leases = new Map<symbol, boolean>();\n\n\t\t// Fixed body нужен и при iOS visual-viewport pan: одного overflow hidden недостаточно.\n\t\trootStyle.setProperty(\"overflow\", \"hidden\");\n\t\tbodyStyle.setProperty(\"position\", \"fixed\");\n\t\tbodyStyle.setProperty(\"top\", `${-scrollY}px`);\n\t\tbodyStyle.setProperty(\"left\", `${-scrollX}px`);\n\t\tbodyStyle.setProperty(\"width\", \"100%\");\n\t\tbodyStyle.setProperty(\"overflow\", \"hidden\");\n\t\tbodyStyle.setProperty(\"box-sizing\", \"border-box\");\n\n\t\tlock = {\n\t\t\tleases,\n\t\t\tupdateCompensation: () => {\n\t\t\t\tif (scrollbarWidth > 0 && [...leases.values()].some(Boolean)) {\n\t\t\t\t\tbodyStyle.setProperty(\"padding-right\", `${computedPadding + scrollbarWidth}px`);\n\t\t\t\t} else restoreProperties(bodyStyle, originalPadding);\n\t\t\t},\n\t\t\trestore: () => {\n\t\t\t\trestoreProperties(rootStyle, rootProperties);\n\t\t\t\trestoreProperties(bodyStyle, bodyProperties);\n\t\t\t\tview.scrollTo({ left: scrollX, top: scrollY, behavior: \"instant\" });\n\t\t\t}\n\t\t};\n\t\tlocks.set(documentTarget, lock);\n\t}\n\n\tconst lease = Symbol();\n\tlock.leases.set(lease, compensateScrollbar);\n\tlock.updateCompensation();\n\treturn () => {\n\t\tif (!lock.leases.delete(lease)) return;\n\t\tif (lock.leases.size > 0) lock.updateCompensation();\n\t\telse {\n\t\t\tlocks.delete(documentTarget);\n\t\t\tlock.restore();\n\t\t}\n\t};\n}\n","import { useEffect } from \"react\";\n\nimport { acquireDocumentScrollLock } from \"./document-scroll/acquireDocumentScrollLock\";\nimport { type UseDocumentScrollLockOptions } from \"./document-scroll/documentScrollLockTypes\";\n\n/** Блокирует фоновую прокрутку до освобождения последнего потребителя; не управляет focus/inert. */\nexport function useDocumentScrollLock({ active, documentTarget, compensateScrollbar = false }: UseDocumentScrollLockOptions): void {\n\tuseEffect(() => {\n\t\tif (!active) return;\n\t\tconst target = documentTarget === undefined ? (typeof document === \"undefined\" ? null : document) : documentTarget;\n\t\tif (!target) return;\n\t\treturn acquireDocumentScrollLock(target, compensateScrollbar);\n\t}, [active, documentTarget, compensateScrollbar]);\n}\n","const properties = [\"--visual-viewport-top\", \"--visual-viewport-left\", \"--visual-viewport-width\", \"--visual-viewport-height\"] as const;\n\nconst attachments = new WeakMap<HTMLElement, { leases: Set<symbol>; detach: () => void }>();\n\n/** Подписка меняет CSS-переменные конкретной поверхности, не вызывает React render на каждом pan. */\nfunction createVisualViewportFrame(container: HTMLElement): () => void {\n\tconst view = container.ownerDocument.defaultView;\n\tif (!view) return () => undefined;\n\tconst viewport = view.visualViewport;\n\tconst style = container.style;\n\tconst previous = properties.map((name) => ({ name, value: style.getPropertyValue(name), priority: style.getPropertyPriority(name) }));\n\tconst ownedValues = new Map<string, string>();\n\tlet frame = 0;\n\n\tconst update = (): void => {\n\t\tframe = 0;\n\t\tconst bounds = [\n\t\t\tviewport?.offsetTop ?? 0,\n\t\t\tviewport?.offsetLeft ?? 0,\n\t\t\tviewport?.width ?? view.innerWidth,\n\t\t\tviewport?.height ?? view.innerHeight\n\t\t];\n\t\tconst fallback = [0, 0, view.innerWidth, view.innerHeight];\n\t\tproperties.forEach((name, index) => {\n\t\t\tconst value = `${Number.isFinite(bounds[index]) ? bounds[index] : fallback[index]}px`;\n\t\t\tif (ownedValues.get(name) === value) return;\n\t\t\tstyle.setProperty(name, value);\n\t\t\townedValues.set(name, value);\n\t\t});\n\t};\n\n\tconst schedule = (): void => {\n\t\tif (!frame) frame = view.requestAnimationFrame(update);\n\t};\n\n\tupdate();\n\tviewport?.addEventListener(\"resize\", schedule);\n\tviewport?.addEventListener(\"scroll\", schedule);\n\tview.addEventListener(\"resize\", schedule);\n\treturn () => {\n\t\tview.cancelAnimationFrame(frame);\n\t\tviewport?.removeEventListener(\"resize\", schedule);\n\t\tviewport?.removeEventListener(\"scroll\", schedule);\n\t\tview.removeEventListener(\"resize\", schedule);\n\t\tfor (const { name, value, priority } of previous) {\n\t\t\t// Не затираем новое значение, если внешний owner уже забрал это CSS-свойство.\n\t\t\tif (style.getPropertyValue(name) !== ownedValues.get(name) || style.getPropertyPriority(name)) continue;\n\t\t\tif (value) style.setProperty(name, value, priority);\n\t\t\telse style.removeProperty(name);\n\t\t}\n\t};\n}\n\n/** Несколько модалок могут разделять portal-root; его frame снимается только последним owner. */\nexport function attachVisualViewportFrame(container: HTMLElement): () => void {\n\tlet attachment = attachments.get(container);\n\tif (!attachment) {\n\t\tattachment = { leases: new Set(), detach: createVisualViewportFrame(container) };\n\t\tattachments.set(container, attachment);\n\t}\n\tconst lease = Symbol();\n\tattachment.leases.add(lease);\n\treturn () => {\n\t\tif (!attachment.leases.delete(lease) || attachment.leases.size > 0) return;\n\t\tattachments.delete(container);\n\t\tattachment.detach();\n\t};\n}\n","import { useEffect } from \"react\";\n\nimport { attachVisualViewportFrame } from \"./visual-viewport/attachVisualViewportFrame\";\nimport { type UseVisualViewportFrameOptions } from \"./visual-viewport/visualViewportFrameTypes\";\n\n/** CSS frame использует реальные visualViewport размеры без отмены pinch zoom; SSR оставляет CSS fallback. */\nexport function useVisualViewportFrame({ active, containerRef }: UseVisualViewportFrameOptions): void {\n\tuseEffect(() => {\n\t\tif (!active || typeof window === \"undefined\") return;\n\t\tlet frame = 0;\n\t\tlet detach: (() => void) | undefined;\n\t\t// Portal может установить ref позже родительского effect; ожидание завершается при cleanup.\n\t\tconst attach = () => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (container) detach = attachVisualViewportFrame(container);\n\t\t\telse frame = window.requestAnimationFrame(attach);\n\t\t};\n\t\tattach();\n\t\treturn () => {\n\t\t\twindow.cancelAnimationFrame(frame);\n\t\t\tdetach?.();\n\t\t};\n\t}, [active, containerRef]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Один владелец body/html на документ предотвращает преждевременный unlock вложенных overlays. */
|
|
2
|
+
export declare function acquireDocumentScrollLock(documentTarget: Document, compensateScrollbar: boolean): () => void;
|
|
3
|
+
//# sourceMappingURL=acquireDocumentScrollLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acquireDocumentScrollLock.d.ts","sourceRoot":"","sources":["../../../src/dom/document-scroll/acquireDocumentScrollLock.ts"],"names":[],"mappings":"AAwBA,mGAAmG;AACnG,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,GAAG,MAAM,IAAI,CAgE5G"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Общая аренда блокировки документа; вложенные потребители освобождают только свою долю. */
|
|
2
|
+
export type UseDocumentScrollLockOptions = Readonly<{
|
|
3
|
+
active: boolean;
|
|
4
|
+
/** undefined выбирает глобальный document, null явно отключает DOM-операцию. */
|
|
5
|
+
documentTarget?: Document | null;
|
|
6
|
+
compensateScrollbar?: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
//# sourceMappingURL=documentScrollLockTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentScrollLockTypes.d.ts","sourceRoot":"","sources":["../../../src/dom/document-scroll/documentScrollLockTypes.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IACnD,MAAM,EAAE,OAAO,CAAC;IAChB,gFAAgF;IAChF,cAAc,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC,CAAC"}
|
package/dist/dom/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from "./downloadFile";
|
|
2
2
|
export * from "./getOrCreatePortalRoot";
|
|
3
|
+
export { captureScrollAnchor } from "./scroll-anchor/captureScrollAnchor";
|
|
4
|
+
export { type CapturedScrollAnchor } from "./scroll-anchor/scrollAnchorTypes";
|
|
3
5
|
export * from "./useClickOutside";
|
|
4
6
|
export * from "./useElementHeightObserver";
|
|
5
7
|
export * from "./useEscapeDismiss";
|
|
@@ -7,4 +9,8 @@ export * from "./useFocusTrap";
|
|
|
7
9
|
export * from "./useIntersectionObserver";
|
|
8
10
|
export * from "./useIsTouchDevice";
|
|
9
11
|
export * from "./useOverlayFocus";
|
|
12
|
+
export { useDocumentScrollLock } from "./useDocumentScrollLock";
|
|
13
|
+
export { type UseDocumentScrollLockOptions } from "./document-scroll/documentScrollLockTypes";
|
|
14
|
+
export { useVisualViewportFrame } from "./useVisualViewportFrame";
|
|
15
|
+
export { type UseVisualViewportFrameOptions } from "./visual-viewport/visualViewportFrameTypes";
|
|
10
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/dom/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dom/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dom/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,2CAA2C,CAAC;AAC9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,4CAA4C,CAAC"}
|
package/dist/dom/index.js
CHANGED
|
@@ -1,159 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
function a(e, t) {
|
|
4
|
-
let n = document.createElement("a");
|
|
5
|
-
n.href = t, n.download = e, n.click(), window.setTimeout(() => {
|
|
6
|
-
window.URL.revokeObjectURL(t);
|
|
7
|
-
}, 0);
|
|
8
|
-
}
|
|
9
|
-
function o(e, t) {
|
|
10
|
-
a(e, window.URL.createObjectURL(t));
|
|
11
|
-
}
|
|
12
|
-
function s(e, t) {
|
|
13
|
-
o(e, new Blob([JSON.stringify(t, null, 2)], { type: "application/json;charset=utf-8" }));
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/dom/getOrCreatePortalRoot.ts
|
|
17
|
-
function c(e) {
|
|
18
|
-
if (typeof document > "u") return null;
|
|
19
|
-
let t = document.getElementById(e);
|
|
20
|
-
if (t instanceof HTMLElement) return t;
|
|
21
|
-
let n = document.createElement("div");
|
|
22
|
-
return n.id = e, document.body.append(n), n;
|
|
23
|
-
}
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/dom/useClickOutside.ts
|
|
26
|
-
function l(t, n) {
|
|
27
|
-
e(() => {
|
|
28
|
-
let e = (e) => {
|
|
29
|
-
if (Array.isArray(t)) {
|
|
30
|
-
let r = !0;
|
|
31
|
-
for (let n of t) n.current && n.current.contains(e.target) && (r = !1);
|
|
32
|
-
r && n();
|
|
33
|
-
} else t.current && !t.current.contains(e.target) && n();
|
|
34
|
-
};
|
|
35
|
-
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
36
|
-
}, [t, n]);
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/dom/useElementHeightObserver.ts
|
|
40
|
-
function u(e) {
|
|
41
|
-
let [t, r] = i(0);
|
|
42
|
-
return n(() => {
|
|
43
|
-
let t = e.current;
|
|
44
|
-
if (!t) return;
|
|
45
|
-
let n = new ResizeObserver((e) => {
|
|
46
|
-
let n = e[0]?.contentRect.height ?? t.getBoundingClientRect().height;
|
|
47
|
-
r(n);
|
|
48
|
-
});
|
|
49
|
-
return n.observe(t), () => n.disconnect();
|
|
50
|
-
}, [e.current]), t;
|
|
51
|
-
}
|
|
52
|
-
//#endregion
|
|
53
|
-
//#region src/dom/useEscapeDismiss.ts
|
|
54
|
-
function d({ active: n, onDismiss: r, enabled: i = !0, documentTarget: a, containerRef: o }) {
|
|
55
|
-
let s = t(() => r());
|
|
56
|
-
e(() => {
|
|
57
|
-
if (!n || !i) return;
|
|
58
|
-
let e = a ?? (typeof document > "u" ? null : document);
|
|
59
|
-
if (!e) return;
|
|
60
|
-
let t = (t) => {
|
|
61
|
-
if (t.key !== "Escape") return;
|
|
62
|
-
let n = e.activeElement, r = o?.current;
|
|
63
|
-
r && n instanceof Node && n !== r && !r.contains(n) || (t.preventDefault(), s());
|
|
64
|
-
};
|
|
65
|
-
return e.addEventListener("keydown", t, !0), () => e.removeEventListener("keydown", t, !0);
|
|
66
|
-
}, [
|
|
67
|
-
n,
|
|
68
|
-
i,
|
|
69
|
-
a,
|
|
70
|
-
o
|
|
71
|
-
]);
|
|
72
|
-
}
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/dom/useOverlayFocus.ts
|
|
75
|
-
var f = "button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])";
|
|
76
|
-
function p({ active: n, trapFocus: i = !1, restoreFocus: a = !0, initialFocus: o = "auto", restoreFocusTarget: s, containerRef: c }) {
|
|
77
|
-
let l = r(null), u = c ?? l, d = r(null), p = t((e) => Array.from(e.querySelectorAll(f)).filter((e) => !e.hasAttribute("aria-hidden"))), m = t((e) => {
|
|
78
|
-
if (o === "container") return e;
|
|
79
|
-
if (typeof o == "function") return o(e) ?? e;
|
|
80
|
-
let t = p(e);
|
|
81
|
-
return t.find((e) => e.hasAttribute("autofocus") || e.hasAttribute("autoFocus")) ?? t[0] ?? e;
|
|
82
|
-
}), h = t(() => s?.() ?? d.current);
|
|
83
|
-
return e(() => {
|
|
84
|
-
if (!n) return;
|
|
85
|
-
d.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
86
|
-
let e = null, t = 0, r = () => {
|
|
87
|
-
if (e = u.current, !e) {
|
|
88
|
-
t = window.requestAnimationFrame(r);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
let n = m(e);
|
|
92
|
-
n instanceof HTMLElement && n.isConnected && n.focus({ preventScroll: !0 }), i && e.addEventListener("keydown", o);
|
|
93
|
-
}, o = (t) => {
|
|
94
|
-
if (!i || t.key !== "Tab" || !e) return;
|
|
95
|
-
let n = p(e);
|
|
96
|
-
if (n.length === 0) {
|
|
97
|
-
t.preventDefault(), e.focus();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
let r = n[0], a = n[n.length - 1], o = document.activeElement;
|
|
101
|
-
if (t.shiftKey) {
|
|
102
|
-
(o === r || o === e) && (t.preventDefault(), a.focus());
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
o === a && (t.preventDefault(), r.focus());
|
|
106
|
-
};
|
|
107
|
-
return r(), () => {
|
|
108
|
-
if (window.cancelAnimationFrame(t), i && e && e.removeEventListener("keydown", o), !a) return;
|
|
109
|
-
let n = h();
|
|
110
|
-
n?.isConnected && n.focus({ preventScroll: !0 });
|
|
111
|
-
};
|
|
112
|
-
}, [
|
|
113
|
-
n,
|
|
114
|
-
i,
|
|
115
|
-
a,
|
|
116
|
-
u
|
|
117
|
-
]), u;
|
|
118
|
-
}
|
|
119
|
-
//#endregion
|
|
120
|
-
//#region src/dom/useFocusTrap.ts
|
|
121
|
-
var m = (e) => p({
|
|
122
|
-
active: e,
|
|
123
|
-
trapFocus: !0,
|
|
124
|
-
restoreFocus: !0,
|
|
125
|
-
initialFocus: "auto"
|
|
126
|
-
});
|
|
127
|
-
//#endregion
|
|
128
|
-
//#region src/dom/useIntersectionObserver.ts
|
|
129
|
-
function h(t, n) {
|
|
130
|
-
let [r, a] = i(!1);
|
|
131
|
-
return e(() => {
|
|
132
|
-
let e = t.current;
|
|
133
|
-
if (!e) return;
|
|
134
|
-
let r = new IntersectionObserver(([e]) => {
|
|
135
|
-
a(e.isIntersecting);
|
|
136
|
-
}, n);
|
|
137
|
-
return r.observe(e), () => {
|
|
138
|
-
e && r.unobserve(e), r.disconnect();
|
|
139
|
-
};
|
|
140
|
-
}, [n, t]), { isIntersecting: r };
|
|
141
|
-
}
|
|
142
|
-
//#endregion
|
|
143
|
-
//#region src/dom/useIsTouchDevice.ts
|
|
144
|
-
var g = () => {
|
|
145
|
-
let [t, n] = i(!1);
|
|
146
|
-
return e(() => {
|
|
147
|
-
let e = () => {
|
|
148
|
-
let e = window.matchMedia("(pointer: coarse)").matches || "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
149
|
-
n(e);
|
|
150
|
-
};
|
|
151
|
-
return e(), window.addEventListener("resize", e), () => {
|
|
152
|
-
window.removeEventListener("resize", e);
|
|
153
|
-
};
|
|
154
|
-
}, []), t;
|
|
155
|
-
};
|
|
156
|
-
//#endregion
|
|
157
|
-
export { o as downloadFileFromBlob, s as downloadFileFromJson, a as downloadFileFromObjectURL, c as getOrCreatePortalRoot, l as useClickOutside, u as useElementHeightObserver, d as useEscapeDismiss, m as useFocusTrap, h as useIntersectionObserver, g as useIsTouchDevice, p as useOverlayFocus };
|
|
158
|
-
|
|
159
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, m as o, n as s, o as c, p as l, r as u, s as d, t as f, u as p } from "../chunks/dom-Bin1L9-q.js";
|
|
2
|
+
export { p as captureScrollAnchor, r as downloadFileFromBlob, l as downloadFileFromJson, o as downloadFileFromObjectURL, n as getOrCreatePortalRoot, a as useClickOutside, s as useDocumentScrollLock, t as useElementHeightObserver, d as useEscapeDismiss, e as useFocusTrap, i as useIntersectionObserver, u as useIsTouchDevice, c as useOverlayFocus, f as useVisualViewportFrame };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CapturedScrollAnchor } from "./scrollAnchorTypes";
|
|
2
|
+
/**
|
|
3
|
+
* Захватывает позицию сохраняемого элемента перед изменением обычного вертикального списка.
|
|
4
|
+
* Вызывать только на browser event/effect boundary; restore — после DOM commit, release — при отмене.
|
|
5
|
+
* Новый захват того же viewport завершает предыдущий без прокрутки.
|
|
6
|
+
*/
|
|
7
|
+
export declare function captureScrollAnchor(viewport: HTMLElement | null, anchor: HTMLElement | null): CapturedScrollAnchor | null;
|
|
8
|
+
//# sourceMappingURL=captureScrollAnchor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captureScrollAnchor.d.ts","sourceRoot":"","sources":["../../../src/dom/scroll-anchor/captureScrollAnchor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAKhE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,oBAAoB,GAAG,IAAI,CA+CzH"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Одноразовая DOM-операция; host обязан завершить её после commit либо отменить при ошибке/unmount. */
|
|
2
|
+
export type CapturedScrollAnchor = Readonly<{
|
|
3
|
+
/** true — прежний anchor ещё допустим и компенсация применена, в том числе при нулевом смещении. */
|
|
4
|
+
restore: () => boolean;
|
|
5
|
+
/** Освобождает временный стиль без изменения позиции прокрутки. Повторный вызов безопасен. */
|
|
6
|
+
release: () => void;
|
|
7
|
+
}>;
|
|
8
|
+
//# sourceMappingURL=scrollAnchorTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrollAnchorTypes.d.ts","sourceRoot":"","sources":["../../../src/dom/scroll-anchor/scrollAnchorTypes.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC3C,oGAAoG;IACpG,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,8FAA8F;IAC9F,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type UseDocumentScrollLockOptions } from "./document-scroll/documentScrollLockTypes";
|
|
2
|
+
/** Блокирует фоновую прокрутку до освобождения последнего потребителя; не управляет focus/inert. */
|
|
3
|
+
export declare function useDocumentScrollLock({ active, documentTarget, compensateScrollbar }: UseDocumentScrollLockOptions): void;
|
|
4
|
+
//# sourceMappingURL=useDocumentScrollLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDocumentScrollLock.d.ts","sourceRoot":"","sources":["../../src/dom/useDocumentScrollLock.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,2CAA2C,CAAC;AAE9F,oGAAoG;AACpG,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,mBAA2B,EAAE,EAAE,4BAA4B,GAAG,IAAI,CAOjI"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type UseVisualViewportFrameOptions } from "./visual-viewport/visualViewportFrameTypes";
|
|
2
|
+
/** CSS frame использует реальные visualViewport размеры без отмены pinch zoom; SSR оставляет CSS fallback. */
|
|
3
|
+
export declare function useVisualViewportFrame({ active, containerRef }: UseVisualViewportFrameOptions): void;
|
|
4
|
+
//# sourceMappingURL=useVisualViewportFrame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVisualViewportFrame.d.ts","sourceRoot":"","sources":["../../src/dom/useVisualViewportFrame.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,4CAA4C,CAAC;AAEhG,8GAA8G;AAC9G,wBAAgB,sBAAsB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,6BAA6B,GAAG,IAAI,CAiBpG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachVisualViewportFrame.d.ts","sourceRoot":"","sources":["../../../src/dom/visual-viewport/attachVisualViewportFrame.ts"],"names":[],"mappings":"AAqDA,iGAAiG;AACjG,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,WAAW,GAAG,MAAM,IAAI,CAa5E"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
/** Визуальная область браузера для CSS fixed-поверхности, включая экранную клавиатуру. */
|
|
3
|
+
export type UseVisualViewportFrameOptions = Readonly<{
|
|
4
|
+
active: boolean;
|
|
5
|
+
containerRef: RefObject<HTMLElement | null>;
|
|
6
|
+
}>;
|
|
7
|
+
//# sourceMappingURL=visualViewportFrameTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualViewportFrameTypes.d.ts","sourceRoot":"","sources":["../../../src/dom/visual-viewport/visualViewportFrameTypes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,0FAA0F;AAC1F,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACpD,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC5C,CAAC,CAAC"}
|
package/dist/excel/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as e } from "../chunks/dom-Bin1L9-q.js";
|
|
2
2
|
import { Sn as t, c as n, l as r, pn as i, s as a, u as o, xn as s } from "../chunks/odata-service-CWkHASdw.js";
|
|
3
3
|
import { d as c, t as l } from "../chunks/validate-DyzojM0w.js";
|
|
4
4
|
import { escapeXmlValue as u } from "../xml/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuzaki13/react-foundation-lib",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryuzaki13",
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
},
|
|
202
202
|
"scripts": {
|
|
203
203
|
"build": "vite build && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
204
|
-
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
204
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.type-tests.json --noEmit",
|
|
205
205
|
"test": "vitest run",
|
|
206
206
|
"lint": "eslint .",
|
|
207
207
|
"lint:fix": "eslint . --fix",
|
package/src/dom/README.mdx
CHANGED
|
@@ -13,6 +13,9 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
13
13
|
- установить начальный фокус, зациклить `Tab` и восстановить прежний фокус;
|
|
14
14
|
- узнать высоту элемента через `ResizeObserver`;
|
|
15
15
|
- узнать, пересекается ли элемент с viewport или другим root;
|
|
16
|
+
- сохранить позицию чтения при вставке элементов выше текущего;
|
|
17
|
+
- согласованно блокировать прокрутку документа для вложенных overlays;
|
|
18
|
+
- привязать fixed-поверхность к visual viewport при экранной клавиатуре;
|
|
16
19
|
- определить наличие touch/coarse-pointer возможностей.
|
|
17
20
|
|
|
18
21
|
Модуль является низкоуровневым browser foundation-слоем. Он не создаёт готовые кнопки, dialog, modal, popover, tooltip или download UI. Визуальный компонент по-прежнему отвечает за семантический HTML, ARIA, стили, анимацию, стек overlay, сообщения об ошибках и пользовательский сценарий.
|
|
@@ -27,6 +30,8 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
27
30
|
- [Быстрый старт](#быстрый-старт)
|
|
28
31
|
- [Скачивание файлов](#скачивание-файлов)
|
|
29
32
|
- [`getOrCreatePortalRoot`](#getorcreateportalroot)
|
|
33
|
+
- [`captureScrollAnchor`](#capturescrollanchor)
|
|
34
|
+
- [Блокировка документа и visual viewport](#блокировка-документа-и-visual-viewport)
|
|
30
35
|
- [`useClickOutside`](#useclickoutside)
|
|
31
36
|
- [`useEscapeDismiss`](#useescapedismiss)
|
|
32
37
|
- [`useElementHeightObserver`](#useelementheightobserver)
|
|
@@ -67,7 +72,7 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
67
72
|
- не удаляет portal root после использования;
|
|
68
73
|
- не реализует полноценный dialog/modal/popover;
|
|
69
74
|
- не назначает `role`, `aria-modal`, `aria-labelledby` или `aria-describedby`;
|
|
70
|
-
- не
|
|
75
|
+
- не выбирает автоматически, какие поверхности должны блокировать прокрутку страницы;
|
|
71
76
|
- не делает фон `inert`;
|
|
72
77
|
- не управляет стеком вложенных overlay;
|
|
73
78
|
- не блокирует программный уход фокуса;
|
|
@@ -103,6 +108,7 @@ npm install react react-dom
|
|
|
103
108
|
|
|
104
109
|
```ts
|
|
105
110
|
import {
|
|
111
|
+
captureScrollAnchor,
|
|
106
112
|
downloadFileFromBlob,
|
|
107
113
|
downloadFileFromJson,
|
|
108
114
|
downloadFileFromObjectURL,
|
|
@@ -272,6 +278,7 @@ Focus определяет, какой элемент получает keyboard
|
|
|
272
278
|
| Скачать готовый `Blob` | `downloadFileFromBlob` |
|
|
273
279
|
| Сериализовать значение в readable JSON и скачать | `downloadFileFromJson` |
|
|
274
280
|
| Получить общий container для `createPortal` | `getOrCreatePortalRoot` |
|
|
281
|
+
| Сохранить позицию при вставке элементов сверху | `captureScrollAnchor` |
|
|
275
282
|
| Закрыть UI по `mousedown` вне одного элемента | `useClickOutside` |
|
|
276
283
|
| Считать trigger и portal content одной «внутренней» областью | `useClickOutside` с массивом refs |
|
|
277
284
|
| Закрыть активный overlay по `Escape` | `useEscapeDismiss` |
|
|
@@ -668,6 +675,81 @@ getOrCreatePortalRoot("dialog-root"); // null, если document отсутст
|
|
|
668
675
|
|
|
669
676
|
Не кешируйте этот `null` в server-loaded module и не ожидайте, что он автоматически станет HTMLElement на client. Portal target должен разрешаться на корректной browser lifecycle boundary.
|
|
670
677
|
|
|
678
|
+
## `captureScrollAnchor`
|
|
679
|
+
|
|
680
|
+
Одноразовая компенсация сдвига сохраняемого DOM-элемента в обычном
|
|
681
|
+
вертикальном scroll-контейнере. Например, при добавлении более ранних
|
|
682
|
+
элементов в начало списка. Не запускает запросы и не зависит от React,
|
|
683
|
+
Query, cursor или предметной модели.
|
|
684
|
+
|
|
685
|
+
```ts
|
|
686
|
+
declare function captureScrollAnchor(viewport: HTMLElement | null, anchor: HTMLElement | null): CapturedScrollAnchor | null;
|
|
687
|
+
|
|
688
|
+
type CapturedScrollAnchor = Readonly<{
|
|
689
|
+
restore: () => boolean;
|
|
690
|
+
release: () => void;
|
|
691
|
+
}>;
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Host выбирает сохраняемый элемент, захватывает его **до изменения DOM**,
|
|
695
|
+
вызывает `restore()` **после DOM commit**, а при отмене, ошибке или unmount —
|
|
696
|
+
`release()`. Разрешение Promise загрузки не гарантирует DOM commit: React-host
|
|
697
|
+
применяет восстановление в layout effect соответствующего обновления, а не
|
|
698
|
+
сразу после `await fetchNextPage()`.
|
|
699
|
+
|
|
700
|
+
```ts
|
|
701
|
+
const pendingAnchor = captureScrollAnchor(viewportElement, retainedElement);
|
|
702
|
+
try {
|
|
703
|
+
// Здесь пример синхронного DOM commit, не React render.
|
|
704
|
+
listElement.prepend(olderElementsFragment);
|
|
705
|
+
pendingAnchor?.restore();
|
|
706
|
+
} finally {
|
|
707
|
+
pendingAnchor?.release();
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Операция сохраняет положение anchor в координатах содержимого контейнера.
|
|
712
|
+
Разницу после commit прибавляет к **текущему** `scrollTop`: ручная прокрутка
|
|
713
|
+
во время ожидания не отменяется. Компенсация мгновенная даже при CSS
|
|
714
|
+
`scroll-behavior: smooth`; горизонтальная позиция не меняется. При нулевом
|
|
715
|
+
сдвиге запись прокрутки не выполняется. Допустимый диапазон ограничивает
|
|
716
|
+
сам браузер.
|
|
717
|
+
|
|
718
|
+
На время операции `overflow-anchor` контейнера устанавливается в
|
|
719
|
+
`none!important`, чтобы браузер не выполнил вторую компенсацию. При завершении
|
|
720
|
+
возвращаются исходные inline-значение и priority либо удаляется отсутствовавшее
|
|
721
|
+
свойство. Внешнее изменение этого свойства отменяет компенсацию и не
|
|
722
|
+
перезаписывается: native anchoring уже мог возобновиться. Браузер без
|
|
723
|
+
поддержки `overflow-anchor` не требует отключения native anchoring.
|
|
724
|
+
|
|
725
|
+
На контейнер допускается один активный захват: новый корректный вызов
|
|
726
|
+
отменяет предыдущий без прокрутки. Его старый handle становится неактивным
|
|
727
|
+
и не может освободить стиль нового захвата. Разные контейнеры независимы.
|
|
728
|
+
|
|
729
|
+
`null` возвращается для отсутствующих или отключённых от документа элементов,
|
|
730
|
+
самого viewport в качестве anchor и элемента вне viewport. `restore()`
|
|
731
|
+
возвращает `true`, если anchor сохранился и операция выполнена, включая нулевой
|
|
732
|
+
сдвиг; `false` — если операция уже завершена либо DOM больше не подходит.
|
|
733
|
+
`release()` и повторный `restore()` безопасны. Временный стиль освобождается
|
|
734
|
+
и при ошибке browser API; ошибка не подавляется.
|
|
735
|
+
После завершения host отбрасывает handle: пока он хранится, его closure
|
|
736
|
+
содержит ссылки на захваченные DOM-элементы.
|
|
737
|
+
|
|
738
|
+
Границы: anchor должен сохранять DOM identity и обычное положение в потоке
|
|
739
|
+
без промежуточного независимо прокручиваемого контейнера. Не использовать
|
|
740
|
+
для window-scroll, горизонтальной или reverse-flex ленты, sticky/fixed anchor,
|
|
741
|
+
CSS scroll-snap и преобразования масштаба/поворота. Изменения layout после
|
|
742
|
+
завершения операции, например поздние изображения, не отслеживаются. API не
|
|
743
|
+
создаёт listeners, observers, timers или React state. Его импорт SSR-safe;
|
|
744
|
+
во время render вызов с реальными DOM-элементами запрещён.
|
|
745
|
+
|
|
746
|
+
Реальная геометрия, native anchoring, CSS smooth и browser clamp проверяются
|
|
747
|
+
в [browser fixture](./scroll-anchor/scroll-anchor.browser.html). Для повторения
|
|
748
|
+
запустить `npx vite dev --host 127.0.0.1 --port 6011 --strictPort`, открыть
|
|
749
|
+
`http://127.0.0.1:6011/src/dom/scroll-anchor/scroll-anchor.browser.html` и нажать
|
|
750
|
+
«Запустить проверки». Результаты и измерения выводятся на странице; этот
|
|
751
|
+
dev-only сценарий дополняет unit-тесты, а не заменяет проверку React-host.
|
|
752
|
+
|
|
671
753
|
## `useClickOutside`
|
|
672
754
|
|
|
673
755
|
Вызывает handler на `mousedown`, если target находится вне одного ref или вне всех refs из массива.
|
|
@@ -2049,12 +2131,71 @@ Touch-capable laptop не обязательно mobile. Не связывайт
|
|
|
2049
2131
|
|
|
2050
2132
|
## Краткий справочник API
|
|
2051
2133
|
|
|
2134
|
+
### Блокировка документа и visual viewport
|
|
2135
|
+
|
|
2136
|
+
```tsx
|
|
2137
|
+
import { useRef } from "react";
|
|
2138
|
+
import { useDocumentScrollLock, useVisualViewportFrame } from "@ryuzaki13/react-foundation-lib/dom";
|
|
2139
|
+
|
|
2140
|
+
function Surface({ open }: { open: boolean }) {
|
|
2141
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
2142
|
+
useDocumentScrollLock({ active: open });
|
|
2143
|
+
useVisualViewportFrame({ active: open, containerRef });
|
|
2144
|
+
return open ? <div ref={containerRef} className="viewportSurface" /> : null;
|
|
2145
|
+
}
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
```css
|
|
2149
|
+
.viewportSurface {
|
|
2150
|
+
position: fixed;
|
|
2151
|
+
top: var(--visual-viewport-top, 0px);
|
|
2152
|
+
left: var(--visual-viewport-left, 0px);
|
|
2153
|
+
width: var(--visual-viewport-width, 100%);
|
|
2154
|
+
height: var(--visual-viewport-height, 100dvh);
|
|
2155
|
+
overflow: hidden;
|
|
2156
|
+
}
|
|
2157
|
+
```
|
|
2158
|
+
|
|
2159
|
+
`UseDocumentScrollLockOptions` содержит `active`, необязательные `documentTarget`
|
|
2160
|
+
и `compensateScrollbar` (по умолчанию `false`). `undefined` выбирает глобальный
|
|
2161
|
+
document, `null` явно отключает операцию. Потребители одного документа разделяют
|
|
2162
|
+
аренды: закрытие вложенного dialog не разблокирует открытый fullscreen. После
|
|
2163
|
+
последнего cleanup восстанавливаются исходные inline properties с priority и
|
|
2164
|
+
scroll X/Y; посторонние CSS-свойства не затрагиваются. Пока lock активен, другой
|
|
2165
|
+
код не должен параллельно изменять принадлежащие ему position/top/left/width,
|
|
2166
|
+
overflow, box-sizing и padding-right body или overflow html.
|
|
2167
|
+
|
|
2168
|
+
`UseVisualViewportFrameOptions` содержит `active` и `containerRef`. Hook пишет
|
|
2169
|
+
четыре показанные CSS-переменные только на переданный контейнер; готовый layout,
|
|
2170
|
+
scroll host, safe-area padding и z-index задаёт UI-владелец. `resize` и `scroll`
|
|
2171
|
+
visual viewport объединяются одним animation frame. Несколько потребителей
|
|
2172
|
+
одного portal root разделяют подписку. Отложенное появление portal ref
|
|
2173
|
+
поддерживается; после установки DOM-узел должен оставаться тем же до деактивации.
|
|
2174
|
+
При замене узла деактивируйте прежний lifecycle либо перемонтируйте его owner.
|
|
2175
|
+
|
|
2176
|
+
При отсутствии VisualViewport используются innerWidth/innerHeight окна и его
|
|
2177
|
+
resize. Нулевая размерность сохраняется как действительное состояние, NaN
|
|
2178
|
+
заменяется fallback. Pinch zoom не отключается: hook использует CSS-pixel bounds
|
|
2179
|
+
браузера и не добавляет обратный scale. Эти hooks не определяют «телефон» по UA,
|
|
2180
|
+
не перехватывают touchmove и не заставляют окно постоянно возвращаться в (0, 0).
|
|
2181
|
+
|
|
2182
|
+
Оба hook SSR-safe: browser работа начинается только после commit, первый HTML
|
|
2183
|
+
использует CSS fallback. Они не предоставляют focus trap, inert, Escape или
|
|
2184
|
+
порядок overlays — используйте UI primitive и `useOverlayFocus` по назначению.
|
|
2185
|
+
Прежние независимые body locks нужно заменить во всех совместно открываемых
|
|
2186
|
+
потребителях: общий ref-count не может координировать чужую реализацию.
|
|
2187
|
+
|
|
2188
|
+
JSDOM покрывает leases, lifecycle и события, но не поведение виртуальной
|
|
2189
|
+
клавиатуры. Мобильные fixed-поверхности дополнительно проверяйте на физическом
|
|
2190
|
+
устройстве: keyboard open/close, pan, orientation, pinch zoom и вложенный overlay.
|
|
2191
|
+
|
|
2052
2192
|
| API | Вход | Результат | Default/событие | Главное ограничение |
|
|
2053
2193
|
| --------------------------- | ----------------------------- | ----------------------------------- | ------------------------------------- | -------------------------------------------------------- |
|
|
2054
2194
|
| `downloadFileFromObjectURL` | filename, object URL | `void` | revoke через `setTimeout(0)` | Принимает ownership URL; нет подтверждения download. |
|
|
2055
2195
|
| `downloadFileFromBlob` | filename, `Blob` | `void` | Сам создаёт/revokes URL | Browser-only, ошибки синхронные. |
|
|
2056
2196
|
| `downloadFileFromJson` | filename, `unknown` | `void` | JSON с 2 spaces, UTF-8 MIME | Наследует ошибки и потери `JSON.stringify`. |
|
|
2057
2197
|
| `getOrCreatePortalRoot` | `id: string` | `HTMLElement \| null` | Создаёт `div` в body | DOM mutation; root не удаляется. |
|
|
2198
|
+
| `captureScrollAnchor` | viewport, сохраняемый элемент | `CapturedScrollAnchor \| null` | Один активный захват на viewport | Обычный вертикальный DOM flow; restore после commit. |
|
|
2058
2199
|
| `useClickOutside` | один ref/массив refs, handler | `void` | Global bubbling `mousedown` | Нет enabled/event arg/custom document. |
|
|
2059
2200
|
| `useEscapeDismiss` | options object | `void` | enabled=true, capture `keydown` | Не управляет stack/focus; defaultPrevented игнорируется. |
|
|
2060
2201
|
| `useElementHeightObserver` | element ref | `number` | Initial `0` | ContentRect, требует ResizeObserver. |
|
package/dist/dom/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/dom/downloadFile.ts","../../src/dom/getOrCreatePortalRoot.ts","../../src/dom/useClickOutside.ts","../../src/dom/useElementHeightObserver.ts","../../src/dom/useEscapeDismiss.ts","../../src/dom/useOverlayFocus.ts","../../src/dom/useFocusTrap.ts","../../src/dom/useIntersectionObserver.ts","../../src/dom/useIsTouchDevice.ts"],"sourcesContent":["export function downloadFileFromObjectURL(filename: string, objectUrl: string) {\n\tconst link = document.createElement(\"a\");\n\n\tlink.href = objectUrl;\n\tlink.download = filename;\n\tlink.click();\n\n\twindow.setTimeout(() => {\n\t\twindow.URL.revokeObjectURL(objectUrl);\n\t}, 0);\n}\n\nexport function downloadFileFromBlob(filename: string, blob: Blob) {\n\tconst objectUrl = window.URL.createObjectURL(blob);\n\tdownloadFileFromObjectURL(filename, objectUrl);\n}\n\nexport function downloadFileFromJson(filename: string, payload: unknown) {\n\tconst blob = new Blob([JSON.stringify(payload, null, 2)], { type: \"application/json;charset=utf-8\" });\n\tdownloadFileFromBlob(filename, blob);\n}\n","export function getOrCreatePortalRoot(id: string): HTMLElement | null {\n\tif (typeof document === \"undefined\") {\n\t\treturn null;\n\t}\n\n\tconst existingElement = document.getElementById(id);\n\tif (existingElement instanceof HTMLElement) {\n\t\treturn existingElement;\n\t}\n\n\tconst portalRoot = document.createElement(\"div\");\n\tportalRoot.id = id;\n\tdocument.body.append(portalRoot);\n\n\treturn portalRoot;\n}\n","import { RefObject, useEffect } from \"react\";\n\nexport function useClickOutside(ref: RefObject<HTMLElement | null> | RefObject<HTMLElement | null>[], handler: () => void) {\n\tuseEffect(() => {\n\t\tconst handleClickOutside = (event: MouseEvent) => {\n\t\t\tif (Array.isArray(ref)) {\n\t\t\t\tlet isOutside = true;\n\t\t\t\tfor (const r of ref) {\n\t\t\t\t\tif (r.current && r.current.contains(event.target as Node)) {\n\t\t\t\t\t\tisOutside = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isOutside) {\n\t\t\t\t\thandler();\n\t\t\t\t}\n\t\t\t} else if (ref.current && !ref.current.contains(event.target as Node)) {\n\t\t\t\thandler();\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\treturn () => document.removeEventListener(\"mousedown\", handleClickOutside);\n\t}, [ref, handler]);\n}\n","import { type RefObject, useLayoutEffect, useState } from \"react\";\n\nexport function useElementHeightObserver<TElement extends HTMLElement>(elementRef: RefObject<TElement | null>) {\n\tconst [height, setHeight] = useState(0);\n\n\tuseLayoutEffect(() => {\n\t\tconst element = elementRef.current;\n\t\tif (!element) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst observer = new ResizeObserver((entries) => {\n\t\t\tconst nextHeight = entries[0]?.contentRect.height ?? element.getBoundingClientRect().height;\n\t\t\tsetHeight(nextHeight);\n\t\t});\n\t\tobserver.observe(element);\n\n\t\treturn () => observer.disconnect();\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps, react-hooks/refs\n\t}, [elementRef.current]);\n\n\treturn height;\n}\n","import { RefObject, useEffect, useEffectEvent } from \"react\";\n\ninterface UseEscapeDismissOptions {\n\tactive: boolean;\n\tonDismiss: () => void;\n\tenabled?: boolean;\n\tdocumentTarget?: Document | null;\n\tcontainerRef?: RefObject<HTMLElement | null>;\n}\n\n/**\n * Подписывает overlay на закрытие по клавише Escape.\n */\nexport function useEscapeDismiss({ active, onDismiss, enabled = true, documentTarget, containerRef }: UseEscapeDismissOptions) {\n\tconst dismiss = useEffectEvent(() => onDismiss());\n\n\tuseEffect(() => {\n\t\tif (!active || !enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst targetDocument = documentTarget ?? (typeof document === \"undefined\" ? null : document);\n\t\tif (!targetDocument) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleKeyDown = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== \"Escape\" /*|| event.defaultPrevented*/) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst activeElement = targetDocument.activeElement;\n\t\t\tconst container = containerRef?.current;\n\t\t\tif (container && activeElement instanceof Node && activeElement !== container && !container.contains(activeElement)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tevent.preventDefault();\n\t\t\tdismiss();\n\t\t};\n\n\t\ttargetDocument.addEventListener(\"keydown\", handleKeyDown, true);\n\t\treturn () => targetDocument.removeEventListener(\"keydown\", handleKeyDown, true);\n\t}, [active, enabled, documentTarget, containerRef]);\n}\n","import { RefObject, useEffect, useEffectEvent, useRef } from \"react\";\n\nconst focusableElementsSelector =\n\t'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])';\n\nexport interface UseOverlayFocusOptions<T extends HTMLElement> {\n\tactive: boolean;\n\ttrapFocus?: boolean;\n\trestoreFocus?: boolean;\n\tinitialFocus?: \"auto\" | \"container\" | ((container: T) => HTMLElement | null | undefined);\n\trestoreFocusTarget?: () => HTMLElement | null | undefined;\n\tcontainerRef?: RefObject<T | null>;\n}\n\n/**\n * Возвращает ref overlay-контейнера и управляет начальными/возвратными переходами фокуса.\n * Открытие и закрытие не прокручивают документ: портал может получить фокус\n * до завершения позиционирования. Пользовательский Tab по-прежнему раскрывает\n * выбранный элемент в прокручиваемом содержимом overlay.\n */\nexport function useOverlayFocus<T extends HTMLElement>({\n\tactive,\n\ttrapFocus = false,\n\trestoreFocus = true,\n\tinitialFocus = \"auto\",\n\trestoreFocusTarget,\n\tcontainerRef\n}: UseOverlayFocusOptions<T>) {\n\tconst internalOverlayRef = useRef<T | null>(null);\n\tconst overlayRef = containerRef ?? internalOverlayRef;\n\tconst previousActiveElementRef = useRef<HTMLElement | null>(null);\n\n\t/**\n\t * Собирает фокусируемые элементы overlay в актуальном DOM-порядке.\n\t */\n\tconst getFocusableElements = useEffectEvent((container: T) =>\n\t\tArray.from(container.querySelectorAll<HTMLElement>(focusableElementsSelector)).filter(\n\t\t\t(element) => !element.hasAttribute(\"aria-hidden\")\n\t\t)\n\t);\n\n\t/**\n\t * Вычисляет стартовый фокус при открытии overlay.\n\t */\n\tconst resolveInitialFocus = useEffectEvent((container: T) => {\n\t\tif (initialFocus === \"container\") {\n\t\t\treturn container;\n\t\t}\n\n\t\tif (typeof initialFocus === \"function\") {\n\t\t\treturn initialFocus(container) ?? container;\n\t\t}\n\n\t\tconst focusableElements = getFocusableElements(container);\n\t\treturn (\n\t\t\tfocusableElements.find((element) => element.hasAttribute(\"autofocus\") || element.hasAttribute(\"autoFocus\")) ??\n\t\t\tfocusableElements[0] ??\n\t\t\tcontainer\n\t\t);\n\t});\n\n\t/**\n\t * Вычисляет элемент, которому нужно вернуть фокус после закрытия overlay.\n\t */\n\tconst resolveRestoreFocusTarget = useEffectEvent(() => restoreFocusTarget?.() ?? previousActiveElementRef.current);\n\n\tuseEffect(() => {\n\t\tif (!active) {\n\t\t\treturn;\n\t\t}\n\n\t\tpreviousActiveElementRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n\t\tlet overlayElement: T | null = null;\n\t\tlet rafId = 0;\n\n\t\t/**\n\t\t * Цикл откладывает инициализацию, пока ref контейнера не будет привязан к DOM.\n\t\t */\n\t\tconst attachOverlay = () => {\n\t\t\toverlayElement = overlayRef.current;\n\t\t\tif (!overlayElement) {\n\t\t\t\trafId = window.requestAnimationFrame(attachOverlay);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst focusTarget = resolveInitialFocus(overlayElement);\n\t\t\tif (focusTarget instanceof HTMLElement && focusTarget.isConnected) {\n\t\t\t\tfocusTarget.focus({ preventScroll: true });\n\t\t\t}\n\n\t\t\tif (trapFocus) {\n\t\t\t\toverlayElement.addEventListener(\"keydown\", handleTabKey);\n\t\t\t}\n\t\t};\n\n\t\t/**\n\t\t * Зацикливает Tab внутри overlay, пока он открыт как модальный слой.\n\t\t */\n\t\tconst handleTabKey = (event: KeyboardEvent) => {\n\t\t\tif (!trapFocus || event.key !== \"Tab\" || !overlayElement) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst focusableElements = getFocusableElements(overlayElement);\n\t\t\tif (focusableElements.length === 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\toverlayElement.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst firstElement = focusableElements[0];\n\t\t\tconst lastElement = focusableElements[focusableElements.length - 1];\n\t\t\tconst activeElement = document.activeElement;\n\n\t\t\tif (event.shiftKey) {\n\t\t\t\tif (activeElement === firstElement || activeElement === overlayElement) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tlastElement.focus();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (activeElement === lastElement) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfirstElement.focus();\n\t\t\t}\n\t\t};\n\n\t\tattachOverlay();\n\n\t\treturn () => {\n\t\t\twindow.cancelAnimationFrame(rafId);\n\t\t\tif (trapFocus && overlayElement) {\n\t\t\t\toverlayElement.removeEventListener(\"keydown\", handleTabKey);\n\t\t\t}\n\n\t\t\tif (!restoreFocus) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst restoreTarget = resolveRestoreFocusTarget();\n\t\t\tif (restoreTarget?.isConnected) {\n\t\t\t\trestoreTarget.focus({ preventScroll: true });\n\t\t\t}\n\t\t};\n\t}, [active, trapFocus, restoreFocus, overlayRef]);\n\n\treturn overlayRef;\n}\n","import { useOverlayFocus } from \"./useOverlayFocus\";\n\n/**\n * Совместимый wrapper для overlay с фокус-ловушкой.\n */\nexport const useFocusTrap = (isOpen: boolean) => {\n\treturn useOverlayFocus<HTMLDivElement>({ active: isOpen, trapFocus: true, restoreFocus: true, initialFocus: \"auto\" });\n};\n","import { useEffect, useState } from \"react\";\n\nexport function useIntersectionObserver(ref: React.RefObject<Element | null>, options?: IntersectionObserverInit) {\n\tconst [isIntersecting, setIsIntersecting] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tconst observer = new IntersectionObserver(([entry]) => {\n\t\t\tsetIsIntersecting(entry.isIntersecting);\n\t\t}, options);\n\n\t\tobserver.observe(el);\n\n\t\treturn () => {\n\t\t\tif (el) {\n\t\t\t\tobserver.unobserve(el);\n\t\t\t}\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [options, ref]); // не забыть, что ref.current может меняться\n\n\treturn { isIntersecting };\n}\n","import { useEffect, useState } from \"react\";\n\nexport const useIsTouchDevice = () => {\n\tconst [isTouchDevice, setIsTouchDevice] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst checkTouchDevice = () => {\n\t\t\tconst isTouch = window.matchMedia(\"(pointer: coarse)\").matches || \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n\t\t\tsetIsTouchDevice(isTouch);\n\t\t};\n\n\t\tcheckTouchDevice();\n\n\t\t// Обработчик для изменений в типе устройства\n\t\twindow.addEventListener(\"resize\", checkTouchDevice);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"resize\", checkTouchDevice);\n\t\t};\n\t}, []);\n\n\treturn isTouchDevice;\n};\n"],"mappings":"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"}
|