@ryuzaki13/react-foundation-lib 2.25.0 → 2.27.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/analytical-table/index.js +1 -1
- package/dist/browser-notifications/browserNotificationTypes.d.ts +23 -0
- package/dist/browser-notifications/browserNotificationTypes.d.ts.map +1 -0
- package/dist/browser-notifications/createBrowserNotificationPresenter.d.ts +7 -0
- package/dist/browser-notifications/createBrowserNotificationPresenter.d.ts.map +1 -0
- package/dist/browser-notifications/index.d.ts +3 -0
- package/dist/browser-notifications/index.d.ts.map +1 -0
- package/dist/browser-notifications/index.js +64 -0
- package/dist/browser-notifications/index.js.map +1 -0
- package/dist/chunks/{dom-Dbq_09-K.js → dom-BK22UmgF.js} +72 -51
- package/dist/chunks/dom-BK22UmgF.js.map +1 -0
- package/dist/chunks/{formatters-DVqfmx3V.js → formatters-B_NcT2pe.js} +2 -2
- package/dist/chunks/{formatters-DVqfmx3V.js.map → formatters-B_NcT2pe.js.map} +1 -1
- package/dist/chunks/media-WK38KtMl.js +191 -0
- package/dist/chunks/media-WK38KtMl.js.map +1 -0
- package/dist/chunks/{query-client-Dj_zNE22.js → query-client-uQEbemFc.js} +2 -2
- package/dist/chunks/{query-client-Dj_zNE22.js.map → query-client-uQEbemFc.js.map} +1 -1
- package/dist/chunks/uuid-CUJPAbRY.js +13 -0
- package/dist/chunks/uuid-CUJPAbRY.js.map +1 -0
- package/dist/crypto/index.js +18 -27
- package/dist/crypto/index.js.map +1 -1
- package/dist/dom/index.d.ts +1 -0
- package/dist/dom/index.d.ts.map +1 -1
- package/dist/dom/index.js +2 -2
- package/dist/dom/installFaviconOverride.d.ts +6 -0
- package/dist/dom/installFaviconOverride.d.ts.map +1 -0
- package/dist/error-report/index.js +14 -13
- package/dist/error-report/index.js.map +1 -1
- package/dist/excel/index.js +1 -1
- package/dist/formatters/index.js +1 -1
- package/dist/indexed-records/createIndexedRecordsSchema.d.ts +11 -0
- package/dist/indexed-records/createIndexedRecordsSchema.d.ts.map +1 -0
- package/dist/indexed-records/indexedRecordsSchemaTypes.d.ts +7 -0
- package/dist/indexed-records/indexedRecordsSchemaTypes.d.ts.map +1 -0
- package/dist/indexed-records/schema.d.ts +3 -0
- package/dist/indexed-records/schema.d.ts.map +1 -0
- package/dist/indexed-records/schema.js +45 -0
- package/dist/indexed-records/schema.js.map +1 -0
- package/dist/media/createNotificationTonePlayer.d.ts +7 -0
- package/dist/media/createNotificationTonePlayer.d.ts.map +1 -0
- package/dist/media/index.d.ts +2 -0
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/index.js +2 -148
- package/dist/media/notificationToneTypes.d.ts +13 -0
- package/dist/media/notificationToneTypes.d.ts.map +1 -0
- package/dist/query-client/index.js +1 -1
- package/dist/session-storage/index.js +1 -1
- package/dist/three-scene/index.js +1 -1
- package/package.json +9 -1
- package/src/browser-notifications/README.md +21 -0
- package/src/indexed-records/README.md +34 -0
- package/dist/chunks/dom-Dbq_09-K.js.map +0 -1
- package/dist/media/index.js.map +0 -1
package/dist/excel/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _n as e, c as t, l as n, ln as r, s as i, u as a, vn as o } from "../chunks/odata-service-tp2U5yIr.js";
|
|
2
2
|
import { t as s, v as c } from "../chunks/validate-C8cQm_9m.js";
|
|
3
|
-
import {
|
|
3
|
+
import { m as l } from "../chunks/dom-BK22UmgF.js";
|
|
4
4
|
import { escapeXmlValue as u } from "../xml/index.js";
|
|
5
5
|
import d from "write-excel-file/browser";
|
|
6
6
|
//#region src/excel/excel.ts
|
package/dist/formatters/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as e, A as t, At as n, B as r, Bt as i, C as a, Ct as o, D as s, Dt as c, E as l, Et as u, F as d, Ft as f, G as p, Gt as m, H as h, Ht as g, I as _, It as v, J as y, Jt as b, K as x, Kt as S, L as C, Lt as w, M as T, Mt as E, N as D, Nt as O, O as k, Ot as A, P as j, Pt as M, Q as N, R as P, Rt as F, S as I, St as L, T as R, Tt as z, U as B, Ut as V, V as H, Vt as U, W, Wt as G, X as K, Y as q, Z as J, _ as Y, _t as X, a as Z, at as Q, b as $, bt as ee, c as te, ct as ne, d as re, dt as ie, et as ae, f as oe, ft as se, g as ce, gt as le, h as ue, ht as de, i as fe, it as pe, j as me, jt as he, k as ge, kt as _e, l as ve, lt as ye, m as be, mt as xe, n as Se, nt as Ce, o as we, ot as Te, p as Ee, pt as De, q as Oe, qt as ke, r as Ae, rt as je, s as Me, st as Ne, t as Pe, tt as Fe, u as Ie, ut as Le, v as Re, vt as ze, w as Be, wt as Ve, x as He, xt as Ue, y as We, yt as Ge, z as Ke, zt as qe } from "../chunks/formatters-
|
|
1
|
+
import { $ as e, A as t, At as n, B as r, Bt as i, C as a, Ct as o, D as s, Dt as c, E as l, Et as u, F as d, Ft as f, G as p, Gt as m, H as h, Ht as g, I as _, It as v, J as y, Jt as b, K as x, Kt as S, L as C, Lt as w, M as T, Mt as E, N as D, Nt as O, O as k, Ot as A, P as j, Pt as M, Q as N, R as P, Rt as F, S as I, St as L, T as R, Tt as z, U as B, Ut as V, V as H, Vt as U, W, Wt as G, X as K, Y as q, Z as J, _ as Y, _t as X, a as Z, at as Q, b as $, bt as ee, c as te, ct as ne, d as re, dt as ie, et as ae, f as oe, ft as se, g as ce, gt as le, h as ue, ht as de, i as fe, it as pe, j as me, jt as he, k as ge, kt as _e, l as ve, lt as ye, m as be, mt as xe, n as Se, nt as Ce, o as we, ot as Te, p as Ee, pt as De, q as Oe, qt as ke, r as Ae, rt as je, s as Me, st as Ne, t as Pe, tt as Fe, u as Ie, ut as Le, v as Re, vt as ze, w as Be, wt as Ve, x as He, xt as Ue, y as We, yt as Ge, z as Ke, zt as qe } from "../chunks/formatters-B_NcT2pe.js";
|
|
2
2
|
import { _ as Je, b as Ye, g as Xe, v as Ze, x as Qe, y as $e } from "../chunks/array-D-_yMMUK.js";
|
|
3
3
|
import { $t as et, An as tt, Bt as nt, Cn as rt, Dn as it, En as at, Fn as ot, Gt as st, Ht as ct, It as lt, Jt as ut, Kt as dt, Lt as ft, Mn as pt, Nn as mt, On as ht, Pn as gt, Qt as _t, Rt as vt, Sn as yt, Tn as bt, Ut as xt, Vt as St, Wt as Ct, Xt as wt, Yt as Tt, Zt as Et, _n as Dt, an as Ot, bn as kt, cn as At, dn as jt, en as Mt, fn as Nt, gn as Pt, hn as Ft, in as It, jn as Lt, kn as Rt, ln as zt, mn as Bt, nn as Vt, on as Ht, pn as Ut, qt as Wt, rn as Gt, sn as Kt, tn as qt, un as Jt, vn as Yt, wn as Xt, xn as Zt, yn as Qt, zt as $t } from "../chunks/odata-service-tp2U5yIr.js";
|
|
4
4
|
import { _ as en, a as tn, b as nn, c as rn, d as an, f as on, g as sn, h as cn, i as ln, l as un, m as dn, n as fn, o as pn, p as mn, r as hn, s as gn, t as _n, u as vn, v as yn, x as bn, y as xn } from "../chunks/validate-C8cQm_9m.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type IndexedRecordsSchemaOptions } from "./indexedRecordsSchemaTypes";
|
|
3
|
+
import { type IndexedRecords } from "./indexedRecordsTypes";
|
|
4
|
+
/**
|
|
5
|
+
* Проверяет доставленный позиционный индекс, не восстанавливая его по items.
|
|
6
|
+
* Предметная itemsSchema вызывается один раз и сохраняет свой порядок/ссылки;
|
|
7
|
+
* она же обеспечивает неизменность массива, записей и вложенных значений.
|
|
8
|
+
* Фабрика создаёт и замораживает только собственные оболочку и numeric index.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createIndexedRecordsSchema<T>({ itemsSchema, getId }: IndexedRecordsSchemaOptions<T>): z.ZodType<IndexedRecords<T>>;
|
|
11
|
+
//# sourceMappingURL=createIndexedRecordsSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createIndexedRecordsSchema.d.ts","sourceRoot":"","sources":["../../src/indexed-records/createIndexedRecordsSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAa5D;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,2BAA2B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CA4ClI"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type z } from "zod";
|
|
2
|
+
/** Предметная схема владеет записями, их неизменностью и канонической identity. */
|
|
3
|
+
export type IndexedRecordsSchemaOptions<T> = Readonly<{
|
|
4
|
+
itemsSchema: z.ZodType<readonly T[]>;
|
|
5
|
+
getId: (item: T) => string;
|
|
6
|
+
}>;
|
|
7
|
+
//# sourceMappingURL=indexedRecordsSchemaTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexedRecordsSchemaTypes.d.ts","sourceRoot":"","sources":["../../src/indexed-records/indexedRecordsSchemaTypes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,mFAAmF;AACnF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI,QAAQ,CAAC;IACrD,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACrC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;CAC3B,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/indexed-records/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { i as e, r as t } from "../chunks/isRecord-DXDTW-jA.js";
|
|
2
|
+
import { z as n } from "zod";
|
|
3
|
+
//#region src/indexed-records/createIndexedRecordsSchema.ts
|
|
4
|
+
function r(n) {
|
|
5
|
+
if (!e(n) || !t(n) || !Object.hasOwn(n, "items") || !Object.hasOwn(n, "indexById")) return !1;
|
|
6
|
+
for (let e of Reflect.ownKeys(n)) {
|
|
7
|
+
let t = Object.getOwnPropertyDescriptor(n, e);
|
|
8
|
+
if (typeof e != "string" || !t?.enumerable || !("value" in t)) return !1;
|
|
9
|
+
}
|
|
10
|
+
return !0;
|
|
11
|
+
}
|
|
12
|
+
function i({ itemsSchema: i, getId: a }) {
|
|
13
|
+
return n.custom(r, "Ожидалась структура индексированных записей без accessor и symbol.").pipe(n.strictObject({
|
|
14
|
+
items: i,
|
|
15
|
+
indexById: n.unknown()
|
|
16
|
+
})).transform((r, i) => {
|
|
17
|
+
if (!e(r.indexById) || !t(r.indexById)) return i.addIssue({
|
|
18
|
+
code: "custom",
|
|
19
|
+
path: ["indexById"],
|
|
20
|
+
message: "Ожидался позиционный словарь записей."
|
|
21
|
+
}), n.NEVER;
|
|
22
|
+
let o = Object.create(null), s = 0;
|
|
23
|
+
for (let e of Reflect.ownKeys(r.indexById)) {
|
|
24
|
+
let t = Object.getOwnPropertyDescriptor(r.indexById, e), c = t?.value;
|
|
25
|
+
if (typeof e != "string" || !t?.enumerable || !("value" in t) || typeof c != "number" || !Number.isSafeInteger(c) || c < 0 || c >= r.items.length || !Object.hasOwn(r.items, c) || a(r.items[c]) !== e) return i.addIssue({
|
|
26
|
+
code: "custom",
|
|
27
|
+
path: ["indexById"],
|
|
28
|
+
message: "Индекс не соответствует записям."
|
|
29
|
+
}), n.NEVER;
|
|
30
|
+
o[e] = c, s += 1;
|
|
31
|
+
}
|
|
32
|
+
return s === r.items.length ? Object.freeze({
|
|
33
|
+
items: r.items,
|
|
34
|
+
indexById: Object.freeze(o)
|
|
35
|
+
}) : (i.addIssue({
|
|
36
|
+
code: "custom",
|
|
37
|
+
path: ["indexById"],
|
|
38
|
+
message: "Позиционный индекс неполон."
|
|
39
|
+
}), n.NEVER);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { i as createIndexedRecordsSchema };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","names":[],"sources":["../../src/indexed-records/createIndexedRecordsSchema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { isPlainObject, isRecord } from \"../validators/isRecord\";\n\nimport { type IndexedRecordsSchemaOptions } from \"./indexedRecordsSchemaTypes\";\nimport { type IndexedRecords } from \"./indexedRecordsTypes\";\n\n/** Не позволяет Zod прочитать accessor или проигнорировать symbol на wire-оболочке. */\nfunction hasIndexedRecordsWireDataProperties(value: unknown): value is Record<string, unknown> {\n\tif (!isRecord(value) || !isPlainObject(value)) return false;\n\tif (!Object.hasOwn(value, \"items\") || !Object.hasOwn(value, \"indexById\")) return false;\n\tfor (const key of Reflect.ownKeys(value)) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, key);\n\t\tif (typeof key !== \"string\" || !descriptor?.enumerable || !(\"value\" in descriptor)) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Проверяет доставленный позиционный индекс, не восстанавливая его по items.\n * Предметная itemsSchema вызывается один раз и сохраняет свой порядок/ссылки;\n * она же обеспечивает неизменность массива, записей и вложенных значений.\n * Фабрика создаёт и замораживает только собственные оболочку и numeric index.\n */\nexport function createIndexedRecordsSchema<T>({ itemsSchema, getId }: IndexedRecordsSchemaOptions<T>): z.ZodType<IndexedRecords<T>> {\n\treturn z\n\t\t.custom<Record<string, unknown>>(\n\t\t\thasIndexedRecordsWireDataProperties,\n\t\t\t\"Ожидалась структура индексированных записей без accessor и symbol.\"\n\t\t)\n\t\t.pipe(z.strictObject({ items: itemsSchema, indexById: z.unknown() }))\n\t\t.transform((wire, context): IndexedRecords<T> => {\n\t\t\tif (!isRecord(wire.indexById) || !isPlainObject(wire.indexById)) {\n\t\t\t\tcontext.addIssue({ code: \"custom\", path: [\"indexById\"], message: \"Ожидался позиционный словарь записей.\" });\n\t\t\t\treturn z.NEVER;\n\t\t\t}\n\n\t\t\t// Проверка и копирование доставленных чисел происходят одним проходом.\n\t\t\t// z.record здесь не применяется: он удаляет собственный __proto__.\n\t\t\tconst indexById = Object.create(null) as Record<string, number>;\n\t\t\tlet entries = 0;\n\t\t\tfor (const id of Reflect.ownKeys(wire.indexById)) {\n\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(wire.indexById, id);\n\t\t\t\tconst position: unknown = descriptor?.value;\n\t\t\t\tif (\n\t\t\t\t\ttypeof id !== \"string\" ||\n\t\t\t\t\t!descriptor?.enumerable ||\n\t\t\t\t\t!(\"value\" in descriptor) ||\n\t\t\t\t\ttypeof position !== \"number\" ||\n\t\t\t\t\t!Number.isSafeInteger(position) ||\n\t\t\t\t\tposition < 0 ||\n\t\t\t\t\tposition >= wire.items.length ||\n\t\t\t\t\t!Object.hasOwn(wire.items, position) ||\n\t\t\t\t\tgetId(wire.items[position]) !== id\n\t\t\t\t) {\n\t\t\t\t\tcontext.addIssue({ code: \"custom\", path: [\"indexById\"], message: \"Индекс не соответствует записям.\" });\n\t\t\t\t\treturn z.NEVER;\n\t\t\t\t}\n\t\t\t\tindexById[id] = position;\n\t\t\t\tentries += 1;\n\t\t\t}\n\t\t\tif (entries !== wire.items.length) {\n\t\t\t\tcontext.addIssue({ code: \"custom\", path: [\"indexById\"], message: \"Позиционный индекс неполон.\" });\n\t\t\t\treturn z.NEVER;\n\t\t\t}\n\n\t\t\treturn Object.freeze({ items: wire.items, indexById: Object.freeze(indexById) });\n\t\t});\n}\n"],"mappings":";;;AAQA,SAAS,EAAoC,GAAkD;CAE9F,IADI,CAAC,EAAS,CAAK,KAAK,CAAC,EAAc,CAAK,KACxC,CAAC,OAAO,OAAO,GAAO,OAAO,KAAK,CAAC,OAAO,OAAO,GAAO,WAAW,GAAG,OAAO;CACjF,KAAK,IAAM,KAAO,QAAQ,QAAQ,CAAK,GAAG;EACzC,IAAM,IAAa,OAAO,yBAAyB,GAAO,CAAG;EAC7D,IAAI,OAAO,KAAQ,YAAY,CAAC,GAAY,cAAc,EAAE,WAAW,IAAa,OAAO;CAC5F;CACA,OAAO;AACR;AAQA,SAAgB,EAA8B,EAAE,gBAAa,YAAuE;CACnI,OAAO,EACL,OACA,GACA,oEACD,CAAC,CACA,KAAK,EAAE,aAAa;EAAE,OAAO;EAAa,WAAW,EAAE,QAAQ;CAAE,CAAC,CAAC,CAAC,CACpE,WAAW,GAAM,MAA+B;EAChD,IAAI,CAAC,EAAS,EAAK,SAAS,KAAK,CAAC,EAAc,EAAK,SAAS,GAE7D,OADA,EAAQ,SAAS;GAAE,MAAM;GAAU,MAAM,CAAC,WAAW;GAAG,SAAS;EAAwC,CAAC,GACnG,EAAE;EAKV,IAAM,IAAY,OAAO,OAAO,IAAI,GAChC,IAAU;EACd,KAAK,IAAM,KAAM,QAAQ,QAAQ,EAAK,SAAS,GAAG;GACjD,IAAM,IAAa,OAAO,yBAAyB,EAAK,WAAW,CAAE,GAC/D,IAAoB,GAAY;GACtC,IACC,OAAO,KAAO,YACd,CAAC,GAAY,cACb,EAAE,WAAW,MACb,OAAO,KAAa,YACpB,CAAC,OAAO,cAAc,CAAQ,KAC9B,IAAW,KACX,KAAY,EAAK,MAAM,UACvB,CAAC,OAAO,OAAO,EAAK,OAAO,CAAQ,KACnC,EAAM,EAAK,MAAM,EAAS,MAAM,GAGhC,OADA,EAAQ,SAAS;IAAE,MAAM;IAAU,MAAM,CAAC,WAAW;IAAG,SAAS;GAAmC,CAAC,GAC9F,EAAE;GAGV,AADA,EAAU,KAAM,GAChB,KAAW;EACZ;EAMA,OALI,MAAY,EAAK,MAAM,SAKpB,OAAO,OAAO;GAAE,OAAO,EAAK;GAAO,WAAW,OAAO,OAAO,CAAS;EAAE,CAAC,KAJ9E,EAAQ,SAAS;GAAE,MAAM;GAAU,MAAM,CAAC,WAAW;GAAG,SAAS;EAA8B,CAAC,GACzF,EAAE;CAIX,CAAC;AACH"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type NotificationToneNote, type NotificationTonePlayer } from "./notificationToneTypes";
|
|
2
|
+
/**
|
|
3
|
+
* Один AudioContext и ограниченный набор осцилляторов. activate вызывается из
|
|
4
|
+
* жеста пользователя; доставка сама не пытается обойти autoplay policy.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createNotificationTonePlayer(notes: readonly NotificationToneNote[]): NotificationTonePlayer;
|
|
7
|
+
//# sourceMappingURL=createNotificationTonePlayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNotificationTonePlayer.d.ts","sourceRoot":"","sources":["../../src/media/createNotificationTonePlayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEjG;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,GAAG,sBAAsB,CAmE3G"}
|
package/dist/media/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from "./breakpoints";
|
|
2
|
+
export { createNotificationTonePlayer } from "./createNotificationTonePlayer";
|
|
3
|
+
export { type NotificationToneNote, type NotificationTonePlayer } from "./notificationToneTypes";
|
|
2
4
|
export * from "./getCurrentFontSize";
|
|
3
5
|
export * from "./pxToEm";
|
|
4
6
|
export * from "./responsive";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/media/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/media/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjG,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC"}
|
package/dist/media/index.js
CHANGED
|
@@ -1,148 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
var n = {
|
|
4
|
-
mobileMax: 479.98 / 16,
|
|
5
|
-
tabletMin: 480 / 16,
|
|
6
|
-
tabletMax: 1151.98 / 16,
|
|
7
|
-
laptopMin: 1152 / 16
|
|
8
|
-
}, r = 16, i = 1.2;
|
|
9
|
-
function a(e) {
|
|
10
|
-
if (typeof window > "u" || typeof document > "u") return r;
|
|
11
|
-
let t = e ?? document.documentElement, n = window.getComputedStyle(t).fontSize, i = Number.parseFloat(n);
|
|
12
|
-
return !Number.isFinite(i) || i <= 0 ? r : i;
|
|
13
|
-
}
|
|
14
|
-
function o(e) {
|
|
15
|
-
if (typeof window > "u" || typeof document > "u") return i;
|
|
16
|
-
let t = e ?? document.documentElement, n = window.getComputedStyle(t).lineHeight, a = Number.parseFloat(n);
|
|
17
|
-
return !Number.isFinite(a) || a <= 0 ? i : a / r;
|
|
18
|
-
}
|
|
19
|
-
function s() {
|
|
20
|
-
return a() + o() * 10;
|
|
21
|
-
}
|
|
22
|
-
//#endregion
|
|
23
|
-
//#region src/media/pxToEm.ts
|
|
24
|
-
function c(e, t) {
|
|
25
|
-
return (e / (t ?? a())).toString();
|
|
26
|
-
}
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/media/responsive.ts
|
|
29
|
-
function l(e) {
|
|
30
|
-
return typeof e != "object" || !e ? !1 : "mobile" in e || "tablet" in e || "laptop" in e || "desktop" in e;
|
|
31
|
-
}
|
|
32
|
-
function u(e, t) {
|
|
33
|
-
if (e !== void 0) return l(e) ? t === "mobile" ? e.mobile ?? e.tablet ?? e.laptop : t === "tablet" ? e.tablet ?? e.mobile ?? e.laptop : e.laptop ?? e.tablet ?? e.mobile : e;
|
|
34
|
-
}
|
|
35
|
-
function d(e, t, n) {
|
|
36
|
-
let r = { ...e };
|
|
37
|
-
for (let e of n) r[e] = u(r[e], t);
|
|
38
|
-
return r;
|
|
39
|
-
}
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/media/useMediaQuery.ts
|
|
42
|
-
var f = () => {};
|
|
43
|
-
function p(e, t) {
|
|
44
|
-
return typeof window > "u" || typeof window.matchMedia != "function" ? t : window.matchMedia(e).matches;
|
|
45
|
-
}
|
|
46
|
-
function m(e, t) {
|
|
47
|
-
if (typeof window > "u" || typeof window.matchMedia != "function") return f;
|
|
48
|
-
let n = window.matchMedia(e);
|
|
49
|
-
return typeof n.addEventListener == "function" ? (n.addEventListener("change", t), () => n.removeEventListener("change", t)) : (n.addListener(t), () => n.removeListener(t));
|
|
50
|
-
}
|
|
51
|
-
function h(n, { ssrFallback: r = !1 } = {}) {
|
|
52
|
-
let i = e((e) => m(n, e), [n]), a = e(() => p(n, r), [n, r]), o = e(() => r, [r]);
|
|
53
|
-
return t(i, a, o);
|
|
54
|
-
}
|
|
55
|
-
//#endregion
|
|
56
|
-
//#region src/media/useMatchMedia.ts
|
|
57
|
-
function g(e, t) {
|
|
58
|
-
return "addEventListener" in e ? (e.addEventListener("change", t), () => e.removeEventListener("change", t)) : (e.addListener(t), () => e.removeListener(t));
|
|
59
|
-
}
|
|
60
|
-
function _(e) {
|
|
61
|
-
let t = (t) => t * e, r = t(n.mobileMax), i = t(n.tabletMin), a = t(n.tabletMax), o = t(n.laptopMin);
|
|
62
|
-
return {
|
|
63
|
-
mobile: `(width <= ${r}px)`,
|
|
64
|
-
tablet: `(width >= ${i}px) and (width <= ${a}px)`,
|
|
65
|
-
laptop: `(width >= ${o}px)`
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
var v = {
|
|
69
|
-
matches: {
|
|
70
|
-
mobile: !1,
|
|
71
|
-
tablet: !1,
|
|
72
|
-
laptop: !1
|
|
73
|
-
},
|
|
74
|
-
activeBreakpoint: "laptop"
|
|
75
|
-
}, y = .25, b = 80, x = v, S = null, C = [], w = 0, T = !1, E = null, D = null, O = null, k = null, A = /* @__PURE__ */ new Set();
|
|
76
|
-
function j(e) {
|
|
77
|
-
return e.mobile ? "mobile" : e.tablet ? "tablet" : "laptop";
|
|
78
|
-
}
|
|
79
|
-
function M(e, t) {
|
|
80
|
-
return e.mobile === t.mobile && e.tablet === t.tablet && e.laptop === t.laptop;
|
|
81
|
-
}
|
|
82
|
-
function N(e) {
|
|
83
|
-
M(x.matches, e) || (x = {
|
|
84
|
-
matches: e,
|
|
85
|
-
activeBreakpoint: j(e)
|
|
86
|
-
}, A.forEach((e) => e()));
|
|
87
|
-
}
|
|
88
|
-
function P() {
|
|
89
|
-
S && N({
|
|
90
|
-
mobile: S.mobile.matches,
|
|
91
|
-
tablet: S.tablet.matches,
|
|
92
|
-
laptop: S.laptop.matches
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
function F() {
|
|
96
|
-
C.forEach((e) => e()), C = [];
|
|
97
|
-
}
|
|
98
|
-
function I(e = !1) {
|
|
99
|
-
if (typeof window > "u" || typeof document > "u") return;
|
|
100
|
-
let t = a();
|
|
101
|
-
if (!e && S && Math.abs(t - w) < y) return;
|
|
102
|
-
w = t, F();
|
|
103
|
-
let n = _(t);
|
|
104
|
-
S = {
|
|
105
|
-
mobile: window.matchMedia(n.mobile),
|
|
106
|
-
tablet: window.matchMedia(n.tablet),
|
|
107
|
-
laptop: window.matchMedia(n.laptop)
|
|
108
|
-
}, C = Object.keys(S).map((e) => g(S[e], P)), P();
|
|
109
|
-
}
|
|
110
|
-
function L() {
|
|
111
|
-
k && clearTimeout(k), k = setTimeout(() => {
|
|
112
|
-
k = null, I();
|
|
113
|
-
}, b);
|
|
114
|
-
}
|
|
115
|
-
function R() {
|
|
116
|
-
typeof window > "u" || typeof document > "u" || (E = new MutationObserver(L), E.observe(document.documentElement, {
|
|
117
|
-
attributes: !0,
|
|
118
|
-
attributeFilter: [
|
|
119
|
-
"style",
|
|
120
|
-
"class",
|
|
121
|
-
"data-font-size"
|
|
122
|
-
]
|
|
123
|
-
}), !(typeof ResizeObserver > "u") && (O = document.createElement("div"), O.style.position = "absolute", O.style.visibility = "hidden", O.style.pointerEvents = "none", O.style.width = "1em", O.style.height = "1px", O.style.overflow = "hidden", O.setAttribute("aria-hidden", "true"), (document.body ?? document.documentElement).appendChild(O), D = new ResizeObserver(L), D.observe(O)));
|
|
124
|
-
}
|
|
125
|
-
function z() {
|
|
126
|
-
F(), S = null, k &&= (clearTimeout(k), null), E &&= (E.disconnect(), null), D &&= (D.disconnect(), null), O?.parentNode && O.parentNode.removeChild(O), O = null, T = !1;
|
|
127
|
-
}
|
|
128
|
-
function B() {
|
|
129
|
-
T || typeof window > "u" || typeof document > "u" || (T = !0, I(!0), R());
|
|
130
|
-
}
|
|
131
|
-
function V(e) {
|
|
132
|
-
return typeof window > "u" || typeof document > "u" ? () => {} : (A.add(e), B(), () => {
|
|
133
|
-
A.delete(e), A.size === 0 && z();
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
function H() {
|
|
137
|
-
return x;
|
|
138
|
-
}
|
|
139
|
-
function U() {
|
|
140
|
-
return v;
|
|
141
|
-
}
|
|
142
|
-
function W() {
|
|
143
|
-
return t(V, H, U);
|
|
144
|
-
}
|
|
145
|
-
//#endregion
|
|
146
|
-
export { n as BREAKPOINTS_EM, s as getControlHeight, a as getCurrentFontSize, o as getCurrentLineHeight, l as isResponsiveMap, c as pxToEm, d as resolveProps, u as resolveResponsiveValue, W as useMatchMedia, h as useMediaQuery };
|
|
147
|
-
|
|
148
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "../chunks/media-WK38KtMl.js";
|
|
2
|
+
export { n as BREAKPOINTS_EM, u as createNotificationTonePlayer, c as getControlHeight, t as getCurrentFontSize, i as getCurrentLineHeight, s as isResponsiveMap, o as pxToEm, r as resolveProps, e as resolveResponsiveValue, l as useMatchMedia, a as useMediaQuery };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Ноты синтезированного сигнала; временные значения заданы в секундах. */
|
|
2
|
+
export type NotificationToneNote = Readonly<{
|
|
3
|
+
frequency: number;
|
|
4
|
+
offset: number;
|
|
5
|
+
duration: number;
|
|
6
|
+
volume: number;
|
|
7
|
+
}>;
|
|
8
|
+
export type NotificationTonePlayer = Readonly<{
|
|
9
|
+
activate: () => Promise<boolean>;
|
|
10
|
+
play: () => boolean;
|
|
11
|
+
dispose: () => void;
|
|
12
|
+
}>;
|
|
13
|
+
//# sourceMappingURL=notificationToneTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notificationToneTypes.d.ts","sourceRoot":"","sources":["../../src/media/notificationToneTypes.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC7C,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as e, D as t, E as n, S as r, T as i, _ as a, a as o, b as s, c, d as l, f as u, g as d, h as f, i as p, l as m, m as h, n as g, o as _, p as v, r as y, s as b, t as x, u as S, v as C, w, x as T, y as E } from "../chunks/query-client-
|
|
1
|
+
import { C as e, D as t, E as n, S as r, T as i, _ as a, a as o, b as s, c, d as l, f as u, g as d, h as f, i as p, l as m, m as h, n as g, o as _, p as v, r as y, s as b, t as x, u as S, v as C, w, x as T, y as E } from "../chunks/query-client-uQEbemFc.js";
|
|
2
2
|
export { s as QUERY_RESTORED_DATA_PARSER, S as REACT_QUERY_PERSISTENCE_BUSTER, l as REACT_QUERY_PERSISTENCE_MAX_AGE, i as broadcastCacheEvent, u as clearSessionQueryPersistence, C as configureQueryPersistenceProjectAdapter, v as createIndexedDbQueryStorage, o as createQueryClient, h as createReactQueryPersister, T as createSessionQueryPersistenceMeta, E as getQueryPersistenceProjectAdapter, f as getSessionQueryPersistenceScope, w as installQueryInvalidationBroadcast, n as installReactQueryBroadcast, x as installSessionScopedQueryReset, p as installSessionScopedQueryResetLifecycle, g as isSessionScopedQuery, _ as onErrorOptimistic, b as onMutateOptimistic, c as onSettledOptimistic, m as onSuccessOptimistic, r as persistedQueryMeta, y as resetSessionScopedQueries, e as sessionScopedQueryMeta, t as setBroadcastFn, d as setSessionQueryPersistenceScope, a as shouldPersistQuery };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuzaki13/react-foundation-lib",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.27.0",
|
|
4
4
|
"description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryuzaki13",
|
|
@@ -49,6 +49,10 @@
|
|
|
49
49
|
"types": "./dist/binary/index.d.ts",
|
|
50
50
|
"import": "./dist/binary/index.js"
|
|
51
51
|
},
|
|
52
|
+
"./browser-notifications": {
|
|
53
|
+
"types": "./dist/browser-notifications/index.d.ts",
|
|
54
|
+
"import": "./dist/browser-notifications/index.js"
|
|
55
|
+
},
|
|
52
56
|
"./bounded-copy-stack": {
|
|
53
57
|
"types": "./dist/bounded-copy-stack/index.d.ts",
|
|
54
58
|
"import": "./dist/bounded-copy-stack/index.js"
|
|
@@ -133,6 +137,10 @@
|
|
|
133
137
|
"types": "./dist/indexed-records/index.d.ts",
|
|
134
138
|
"import": "./dist/indexed-records/index.js"
|
|
135
139
|
},
|
|
140
|
+
"./indexed-records/schema": {
|
|
141
|
+
"types": "./dist/indexed-records/schema.d.ts",
|
|
142
|
+
"import": "./dist/indexed-records/schema.js"
|
|
143
|
+
},
|
|
136
144
|
"./media": {
|
|
137
145
|
"types": "./dist/media/index.d.ts",
|
|
138
146
|
"import": "./dist/media/index.js"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Браузерные уведомления
|
|
2
|
+
|
|
3
|
+
`createBrowserNotificationPresenter` из `/browser-notifications` предоставляет
|
|
4
|
+
явный запрос разрешения и отменяемую публикацию системного уведомления.
|
|
5
|
+
Создание не обращается к браузеру; SSR не вызывает API. Метод `show` предпочитает
|
|
6
|
+
уже активный Service Worker, а без него использует desktop Notification API.
|
|
7
|
+
Приложение обрабатывает `notificationclick` своего worker, проверяя URL из data.
|
|
8
|
+
Серверная Web Push доставка и регистрация worker не входят в контракт.
|
|
9
|
+
|
|
10
|
+
`requestPermission` вызывается непосредственно из пользовательского жеста.
|
|
11
|
+
Отказ не запрашивается повторно. HTTPS/localhost и поддержка API обязательны;
|
|
12
|
+
системные ограничения остаются у браузера. `show` возвращает немедленный disposer,
|
|
13
|
+
который закрывает также позднюю SW-публикацию; `dispose` завершает все свои.
|
|
14
|
+
События клика desktop передаются в `onOpen`, ошибки доставки — в `onError`.
|
|
15
|
+
|
|
16
|
+
Связанные независимые primitives: `/media` экспортирует
|
|
17
|
+
`createNotificationTonePlayer` с плавным синусоидальным сигналом до 1,5 секунды
|
|
18
|
+
и явной `activate` из жеста пользователя; `/dom` предоставляет
|
|
19
|
+
`installFaviconOverride` с симметричным восстановлением favicon links.
|
|
20
|
+
Продуктовые правила, интервалы оповещения, тексты, настройки и дедупликация
|
|
21
|
+
остаются у вызывающего приложения.
|
|
@@ -30,3 +30,37 @@ identity вызывает ошибку. Порядок и ссылки исхо
|
|
|
30
30
|
Восстановленные недоверенные данные должны пройти проверку у принимающей
|
|
31
31
|
границы. Наличие полей `items` и `indexById` само по себе не подтверждает
|
|
32
32
|
корректность позиций, полноту или уникальность каталога.
|
|
33
|
+
|
|
34
|
+
## Проверка доставленного индекса
|
|
35
|
+
|
|
36
|
+
Optional entry со схемой требует Zod 4. Прежний `indexed-records` entry не
|
|
37
|
+
импортирует его и сохраняет работу без Zod.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { z } from "zod";
|
|
41
|
+
import { createIndexedRecordsSchema } from "@ryuzaki13/react-foundation-lib/indexed-records/schema";
|
|
42
|
+
|
|
43
|
+
const recordsSchema = createIndexedRecordsSchema({
|
|
44
|
+
itemsSchema: z.array(z.strictObject({ id: z.string(), name: z.string() }).readonly()).readonly(),
|
|
45
|
+
getId: (item) => item.id
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const records = recordsSchema.parse(deliveredRecords);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Схема принимает только строгую оболочку `{ items, indexById }`. `itemsSchema`
|
|
52
|
+
проверяется один раз; её порядок, массив и записи переиспользуются без
|
|
53
|
+
дополнительной копии. Доставленный индекс проверяется и копируется одним
|
|
54
|
+
проходом. Каждая собственная identity должна указывать на целую допустимую
|
|
55
|
+
позицию своей записи; все записи должны быть покрыты ровно один раз.
|
|
56
|
+
Отсутствующие/лишние ключи, повторяющиеся identities, переставленные позиции,
|
|
57
|
+
accessor, symbol, нестандартный prototype и нечисловые значения отклоняются.
|
|
58
|
+
Строковые IDs, включая пустую строку, `__proto__` и числовые строки, не
|
|
59
|
+
нормализуются; числовой ID предмета можно преобразовать в `getId` через `String`.
|
|
60
|
+
|
|
61
|
+
Фабрика не строит индекс из массива, не сортирует записи и не знает их
|
|
62
|
+
предметной семантики. `getId` должен быть чистым и возвращать устойчивую
|
|
63
|
+
каноническую identity. Выходная оболочка и собственный null-prototype индекс
|
|
64
|
+
заморожены. Freeze массива, записей и вложенных объектов, а также межстрочные
|
|
65
|
+
предметные ограничения задаёт `itemsSchema` владельца. Generic recursive
|
|
66
|
+
freeze, второй parse записей, сертификаты, revision и cache сюда не входят.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dom-Dbq_09-K.js","names":[],"sources":["../../src/dom/downloadFile.ts","../../src/dom/getOrCreatePortalRoot.ts","../../src/dom/observeElementResize.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/canConsumeVerticalTouchScroll.ts","../../src/dom/document-scroll/isNativeTouchInteraction.ts","../../src/dom/document-scroll/attachDocumentTouchScrollGuard.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","/**\n * Наблюдает изменение одного DOM-контейнера без React state. Callback вызывается\n * в batched delivery ResizeObserver, поэтому дополнительный animation frame не нужен.\n * Возвращённый cleanup отключает observer и игнорирует уже поставленную delivery.\n */\nexport function observeElementResize(element: HTMLElement, onResize: () => void): () => void {\n\tif (typeof ResizeObserver === \"undefined\") return () => undefined;\n\tlet active = true;\n\tconst observer = new ResizeObserver((entries) => {\n\t\tif (active && entries.some((entry) => entry.target === element)) onResize();\n\t});\n\tobserver.observe(element);\n\treturn () => {\n\t\tif (!active) return;\n\t\tactive = false;\n\t\tobserver.disconnect();\n\t};\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","/** Проверяет обычную вложенную scroll-цепочку; корневой документ не является разрешённым host. */\nexport function canConsumeVerticalTouchScroll(documentTarget: Document, path: readonly EventTarget[], deltaY: number): boolean {\n\tconst view = documentTarget.defaultView;\n\tif (!view) return true;\n\tfor (const entry of path) {\n\t\tif (entry === documentTarget.body || entry === documentTarget.documentElement) break;\n\t\tif (!(entry instanceof view.Element) || !entry.isConnected) continue;\n\t\tconst style = view.getComputedStyle(entry);\n\t\tif (!/^(auto|scroll|overlay)$/.test(style.overflowY)) continue;\n\t\t// Reverse/writing-mode layout имеет другую систему координат. В этом patch\n\t\t// не подменяем её предположением 0..max и не реализуем собственную scroll physics.\n\t\tif (style.flexDirection === \"column-reverse\" || (style.writingMode && style.writingMode !== \"horizontal-tb\")) return true;\n\t\tconst maximum = Math.max(0, entry.scrollHeight - entry.clientHeight);\n\t\tconst top = Math.max(0, Math.min(maximum, entry.scrollTop));\n\t\t// scrollTop дробный, а scrollHeight/clientHeight округлены: 1 CSS px\n\t\t// допуска перекрывает ложный остаток на физически достигнутом краю.\n\t\tif (deltaY > 0 ? maximum - top > 1 : top > 1) return true;\n\t\t// На краю contain/none движение не должно переходить к следующему ancestor.\n\t\tif (/^(contain|none)$/.test(style.overscrollBehaviorY)) return false;\n\t}\n\treturn false;\n}\n","/** Редактирование/выделение остаются у браузера: перехват move способен сорвать drag каретки. */\nexport function isNativeTouchInteraction(documentTarget: Document, path: readonly EventTarget[]): boolean {\n\tconst view = documentTarget.defaultView;\n\tif (!view) return true;\n\tlet editableBoundarySeen = false;\n\tlet target: Element | undefined;\n\tfor (const entry of path) {\n\t\tif (!(entry instanceof view.Element)) continue;\n\t\ttarget ??= entry;\n\t\tif (entry.matches(\"input:not(:disabled), textarea:not(:disabled), select:not(:disabled), audio[controls], video[controls]\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (editableBoundarySeen) continue;\n\t\tconst editable = entry.getAttribute(\"contenteditable\")?.toLowerCase();\n\t\tif (editable === \"\" || editable === \"true\" || editable === \"plaintext-only\") return true;\n\t\tif (editable === \"false\") editableBoundarySeen = true;\n\t}\n\tconst selection = documentTarget.getSelection();\n\t// Чужое выделение не отключает защиту заголовка/фона на всей странице.\n\tif (!target || !selection || selection.isCollapsed) return false;\n\tfor (let index = 0; index < selection.rangeCount; index += 1) {\n\t\tif (selection.getRangeAt(index).intersectsNode(target)) return true;\n\t}\n\treturn false;\n}\n","import { canConsumeVerticalTouchScroll } from \"./canConsumeVerticalTouchScroll\";\nimport { isNativeTouchInteraction } from \"./isNativeTouchInteraction\";\n\ntype TouchScrollObservation = Readonly<{\n\tidentifier: number;\n\tx: number;\n\ty: number;\n\tpath: readonly EventTarget[];\n}>;\n\n/**\n * Safari может передать вертикальный pan корню при открытой клавиатуре даже\n * с fixed body. Страхуем пустую область/достигнутый край, не имитируя скроллинг.\n * Один listener set живёт ровно столько же, сколько общий document lock.\n */\nexport function attachDocumentTouchScrollGuard(documentTarget: Document): () => void {\n\tconst view = documentTarget.defaultView;\n\tlet observation: TouchScrollObservation | null = null;\n\tlet bypassGesture = false;\n\n\tconst onStart = (event: TouchEvent): void => {\n\t\t// После pinch даже оставшийся один палец принадлежит тому же native gesture.\n\t\tif (bypassGesture) return;\n\t\tconst touch = event.touches[0];\n\t\tconst path = event.composedPath();\n\t\tif (\n\t\t\tevent.touches.length !== 1 ||\n\t\t\t!touch ||\n\t\t\t(view?.visualViewport?.scale ?? 1) > 1 ||\n\t\t\tisNativeTouchInteraction(documentTarget, path)\n\t\t) {\n\t\t\tbypassGesture = true;\n\t\t\tobservation = null;\n\t\t\treturn;\n\t\t}\n\t\tobservation = { identifier: touch.identifier, x: touch.clientX, y: touch.clientY, path };\n\t};\n\n\tconst onMove = (event: TouchEvent): void => {\n\t\tif (bypassGesture || !observation) return;\n\t\tconst touch = event.touches[0];\n\t\tif (event.touches.length !== 1 || !touch || touch.identifier !== observation.identifier || (view?.visualViewport?.scale ?? 1) > 1) {\n\t\t\tbypassGesture = true;\n\t\t\tobservation = null;\n\t\t\treturn;\n\t\t}\n\t\tconst { x, y, path } = observation;\n\t\tconst deltaX = x - touch.clientX;\n\t\tconst deltaY = y - touch.clientY;\n\t\tobservation = { identifier: touch.identifier, x: touch.clientX, y: touch.clientY, path };\n\t\tif (isNativeTouchInteraction(documentTarget, path)) {\n\t\t\tbypassGesture = true;\n\t\t\treturn;\n\t\t}\n\t\t// Горизонтальные жесты, wheel и touchstart остаются без изменений.\n\t\tif (!event.cancelable || event.defaultPrevented || !deltaY || Math.abs(deltaX) >= Math.abs(deltaY)) return;\n\t\tif (!canConsumeVerticalTouchScroll(documentTarget, path, deltaY)) event.preventDefault();\n\t};\n\n\tconst onEnd = (event: TouchEvent): void => {\n\t\tif (event.touches.length > 0) return;\n\t\tobservation = null;\n\t\tbypassGesture = false;\n\t};\n\n\tdocumentTarget.addEventListener(\"touchstart\", onStart, { capture: true, passive: true });\n\tdocumentTarget.addEventListener(\"touchmove\", onMove, { capture: true, passive: false });\n\tdocumentTarget.addEventListener(\"touchend\", onEnd, { capture: true, passive: true });\n\tdocumentTarget.addEventListener(\"touchcancel\", onEnd, { capture: true, passive: true });\n\treturn () => {\n\t\tdocumentTarget.removeEventListener(\"touchstart\", onStart, true);\n\t\tdocumentTarget.removeEventListener(\"touchmove\", onMove, true);\n\t\tdocumentTarget.removeEventListener(\"touchend\", onEnd, true);\n\t\tdocumentTarget.removeEventListener(\"touchcancel\", onEnd, true);\n\t\tobservation = null;\n\t};\n}\n","import { attachDocumentTouchScrollGuard } from \"./attachDocumentTouchScrollGuard\";\n\ntype 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\t\tconst releaseTouchGuard = attachDocumentTouchScrollGuard(documentTarget);\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\treleaseTouchGuard();\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":";;AAAA,SAAgB,EAA0B,GAAkB,GAAmB;CAC9E,IAAM,IAAO,SAAS,cAAc,GAAG;CAMvC,AAJA,EAAK,OAAO,GACZ,EAAK,WAAW,GAChB,EAAK,MAAM,GAEX,OAAO,iBAAiB;EACvB,OAAO,IAAI,gBAAgB,CAAS;CACrC,GAAG,CAAC;AACL;AAEA,SAAgB,EAAqB,GAAkB,GAAY;CAElE,EAA0B,GADR,OAAO,IAAI,gBAAgB,CACT,CAAS;AAC9C;AAEA,SAAgB,EAAqB,GAAkB,GAAkB;CAExE,EAAqB,GAAU,IADd,KAAK,CAAC,KAAK,UAAU,GAAS,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,iCAAiC,CACpE,CAAI;AACpC;;;ACpBA,SAAgB,EAAsB,GAAgC;CACrE,IAAI,OAAO,WAAa,KACvB,OAAO;CAGR,IAAM,IAAkB,SAAS,eAAe,CAAE;CAClD,IAAI,aAA2B,aAC9B,OAAO;CAGR,IAAM,IAAa,SAAS,cAAc,KAAK;CAI/C,OAHA,EAAW,KAAK,GAChB,SAAS,KAAK,OAAO,CAAU,GAExB;AACR;;;ACVA,SAAgB,EAAqB,GAAsB,GAAkC;CAC5F,IAAI,OAAO,iBAAmB,KAAa,aAAa,KAAA;CACxD,IAAI,IAAS,IACP,IAAW,IAAI,gBAAgB,MAAY;EAChD,AAAI,KAAU,EAAQ,MAAM,MAAU,EAAM,WAAW,CAAO,KAAG,EAAS;CAC3E,CAAC;CAED,OADA,EAAS,QAAQ,CAAO,SACX;EACP,MACL,IAAS,IACT,EAAS,WAAW;CACrB;AACD;;;ACdA,IAAM,oBAAgB,IAAI,QAA2C;AAOrE,SAAgB,EAAoB,GAA8B,GAAyD;CAC1H,IAAI,CAAC,GAAU,eAAe,CAAC,GAAQ,eAAe,MAAa,KAAU,CAAC,EAAS,SAAS,CAAM,GAAG,OAAO;CAEhH,EAAc,IAAI,CAAQ,CAAC,EAAE,QAAQ;CACrC,IAAM,IACL,EAAO,sBAAsB,CAAC,CAAC,MAAM,EAAS,sBAAsB,CAAC,CAAC,MAAM,EAAS,YAAY,EAAS,WACrG,IAAe,EAAS,MAAM,iBAAiB,iBAAiB,GAChE,IAAkB,EAAS,MAAM,oBAAoB,iBAAiB,GACxE,IAAS;CAGb,EAAS,MAAM,YAAY,mBAAmB,QAAQ,WAAW;CAEjE,IAAM,IAAa,EAAS,MAAM,iBAAiB,iBAAiB,GAC9D,IAAgB,EAAS,MAAM,oBAAoB,iBAAiB,GACpE,UACL,EAAS,MAAM,iBAAiB,iBAAiB,MAAM,KACvD,EAAS,MAAM,oBAAoB,iBAAiB,MAAM,GAErD,IAAkC;EACvC,UAAU;GACJ,MACL,IAAS,IACT,EAAc,OAAO,CAAQ,GAExB,EAAU,MACX,IAAc,EAAS,MAAM,YAAY,mBAAmB,GAAc,CAAe,IACxF,EAAS,MAAM,eAAe,iBAAiB;EACrD;EACA,UAAU;GACT,IAAI,CAAC,GAAQ,OAAO;GACpB,IAAI;IACH,IAAI,CAAC,EAAS,eAAe,CAAC,EAAO,eAAe,CAAC,EAAS,SAAS,CAAM,KAAK,CAAC,EAAU,GAAG,OAAO;IAGvG,IAAM,IADL,EAAO,sBAAsB,CAAC,CAAC,MAAM,EAAS,sBAAsB,CAAC,CAAC,MAAM,EAAS,YAAY,EAAS,YAC7E;IAI9B,OADI,MAAU,KAAG,EAAS,SAAS;KAAE,KAAK;KAAO,UAAU;IAAU,CAAC,GAC/D;GACR,UAAU;IACT,EAAU,QAAQ;GACnB;EACD;CACD;CAEA,OADA,EAAc,IAAI,GAAU,CAAS,GAC9B;AACR;;;ACvDA,SAAgB,EAAgB,GAAsE,GAAqB;CAC1H,QAAgB;EACf,IAAM,KAAsB,MAAsB;GACjD,IAAI,MAAM,QAAQ,CAAG,GAAG;IACvB,IAAI,IAAY;IAChB,KAAK,IAAM,KAAK,GACf,AAAI,EAAE,WAAW,EAAE,QAAQ,SAAS,EAAM,MAAc,MACvD,IAAY;IAGd,AAAI,KACH,EAAQ;GAEV,OAAO,AAAI,EAAI,WAAW,CAAC,EAAI,QAAQ,SAAS,EAAM,MAAc,KACnE,EAAQ;EAEV;EAGA,OADA,SAAS,iBAAiB,aAAa,CAAkB,SAC5C,SAAS,oBAAoB,aAAa,CAAkB;CAC1E,GAAG,CAAC,GAAK,CAAO,CAAC;AAClB;;;ACrBA,SAAgB,EAAuD,GAAwC;CAC9G,IAAM,CAAC,GAAQ,KAAa,EAAS,CAAC;CAkBtC,OAhBA,QAAsB;EACrB,IAAM,IAAU,EAAW;EAC3B,IAAI,CAAC,GACJ;EAGD,IAAM,IAAW,IAAI,gBAAgB,MAAY;GAChD,IAAM,IAAa,EAAQ,EAAE,EAAE,YAAY,UAAU,EAAQ,sBAAsB,CAAC,CAAC;GACrF,EAAU,CAAU;EACrB,CAAC;EAGD,OAFA,EAAS,QAAQ,CAAO,SAEX,EAAS,WAAW;CAElC,GAAG,CAAC,EAAW,OAAO,CAAC,GAEhB;AACR;;;ACTA,SAAgB,EAAiB,EAAE,WAAQ,cAAW,aAAU,IAAM,mBAAgB,mBAAyC;CAC9H,IAAM,IAAU,QAAqB,EAAU,CAAC;CAEhD,QAAgB;EACf,IAAI,CAAC,KAAU,CAAC,GACf;EAGD,IAAM,IAAiB,MAAmB,OAAO,WAAa,MAAc,OAAO;EACnF,IAAI,CAAC,GACJ;EAGD,IAAM,KAAiB,MAAyB;GAC/C,IAAI,EAAM,QAAQ,UACjB;GAGD,IAAM,IAAgB,EAAe,eAC/B,IAAY,GAAc;GAC5B,KAAa,aAAyB,QAAQ,MAAkB,KAAa,CAAC,EAAU,SAAS,CAAa,MAIlH,EAAM,eAAe,GACrB,EAAQ;EACT;EAGA,OADA,EAAe,iBAAiB,WAAW,GAAe,EAAI,SACjD,EAAe,oBAAoB,WAAW,GAAe,EAAI;CAC/E,GAAG;EAAC;EAAQ;EAAS;EAAgB;CAAY,CAAC;AACnD;;;AC1CA,IAAM,IACL;AAiBD,SAAgB,EAAuC,EACtD,WACA,eAAY,IACZ,kBAAe,IACf,kBAAe,QACf,uBACA,mBAC6B;CAC7B,IAAM,IAAqB,EAAiB,IAAI,GAC1C,IAAa,KAAgB,GAC7B,IAA2B,EAA2B,IAAI,GAK1D,IAAuB,GAAgB,MAC5C,MAAM,KAAK,EAAU,iBAA8B,CAAyB,CAAC,CAAC,CAAC,QAC7E,MAAY,CAAC,EAAQ,aAAa,aAAa,CACjD,CACD,GAKM,IAAsB,GAAgB,MAAiB;EAC5D,IAAI,MAAiB,aACpB,OAAO;EAGR,IAAI,OAAO,KAAiB,YAC3B,OAAO,EAAa,CAAS,KAAK;EAGnC,IAAM,IAAoB,EAAqB,CAAS;EACxD,OACC,EAAkB,MAAM,MAAY,EAAQ,aAAa,WAAW,KAAK,EAAQ,aAAa,WAAW,CAAC,KAC1G,EAAkB,MAClB;CAEF,CAAC,GAKK,IAA4B,QAAqB,IAAqB,KAAK,EAAyB,OAAO;CAmFjH,OAjFA,QAAgB;EACf,IAAI,CAAC,GACJ;EAGD,EAAyB,UAAU,SAAS,yBAAyB,cAAc,SAAS,gBAAgB;EAC5G,IAAI,IAA2B,MAC3B,IAAQ,GAKN,UAAsB;GAE3B,IADA,IAAiB,EAAW,SACxB,CAAC,GAAgB;IACpB,IAAQ,OAAO,sBAAsB,CAAa;IAClD;GACD;GAEA,IAAM,IAAc,EAAoB,CAAc;GAKtD,AAJI,aAAuB,eAAe,EAAY,eACrD,EAAY,MAAM,EAAE,eAAe,GAAK,CAAC,GAGtC,KACH,EAAe,iBAAiB,WAAW,CAAY;EAEzD,GAKM,KAAgB,MAAyB;GAC9C,IAAI,CAAC,KAAa,EAAM,QAAQ,SAAS,CAAC,GACzC;GAGD,IAAM,IAAoB,EAAqB,CAAc;GAC7D,IAAI,EAAkB,WAAW,GAAG;IAEnC,AADA,EAAM,eAAe,GACrB,EAAe,MAAM;IACrB;GACD;GAEA,IAAM,IAAe,EAAkB,IACjC,IAAc,EAAkB,EAAkB,SAAS,IAC3D,IAAgB,SAAS;GAE/B,IAAI,EAAM,UAAU;IACnB,CAAI,MAAkB,KAAgB,MAAkB,OACvD,EAAM,eAAe,GACrB,EAAY,MAAM;IAEnB;GACD;GAEA,AAAI,MAAkB,MACrB,EAAM,eAAe,GACrB,EAAa,MAAM;EAErB;EAIA,OAFA,EAAc,SAED;GAMZ,IALA,OAAO,qBAAqB,CAAK,GAC7B,KAAa,KAChB,EAAe,oBAAoB,WAAW,CAAY,GAGvD,CAAC,GACJ;GAGD,IAAM,IAAgB,EAA0B;GAChD,AAAI,GAAe,eAClB,EAAc,MAAM,EAAE,eAAe,GAAK,CAAC;EAE7C;CACD,GAAG;EAAC;EAAQ;EAAW;EAAc;CAAU,CAAC,GAEzC;AACR;;;AC/IA,IAAa,KAAgB,MACrB,EAAgC;CAAE,QAAQ;CAAQ,WAAW;CAAM,cAAc;CAAM,cAAc;AAAO,CAAC;;;ACJrH,SAAgB,EAAwB,GAAsC,GAAoC;CACjH,IAAM,CAAC,GAAgB,KAAqB,EAAS,EAAK;CAoB1D,OAlBA,QAAgB;EACf,IAAM,IAAK,EAAI;EACf,IAAI,CAAC,GAAI;EAET,IAAM,IAAW,IAAI,sBAAsB,CAAC,OAAW;GACtD,EAAkB,EAAM,cAAc;EACvC,GAAG,CAAO;EAIV,OAFA,EAAS,QAAQ,CAAE,SAEN;GAIZ,AAHI,KACH,EAAS,UAAU,CAAE,GAEtB,EAAS,WAAW;EACrB;CACD,GAAG,CAAC,GAAS,CAAG,CAAC,GAEV,EAAE,kBAAe;AACzB;;;ACtBA,IAAa,UAAyB;CACrC,IAAM,CAAC,GAAe,KAAoB,EAAS,EAAK;CAkBxD,OAhBA,QAAgB;EACf,IAAM,UAAyB;GAC9B,IAAM,IAAU,OAAO,WAAW,mBAAmB,CAAC,CAAC,WAAW,kBAAkB,UAAU,UAAU,iBAAiB;GACzH,EAAiB,CAAO;EACzB;EAOA,OALA,EAAiB,GAGjB,OAAO,iBAAiB,UAAU,CAAgB,SAErC;GACZ,OAAO,oBAAoB,UAAU,CAAgB;EACtD;CACD,GAAG,CAAC,CAAC,GAEE;AACR;;;ACrBA,SAAgB,EAA8B,GAA0B,GAA8B,GAAyB;CAC9H,IAAM,IAAO,EAAe;CAC5B,IAAI,CAAC,GAAM,OAAO;CAClB,KAAK,IAAM,KAAS,GAAM;EACzB,IAAI,MAAU,EAAe,QAAQ,MAAU,EAAe,iBAAiB;EAC/E,IAAI,EAAE,aAAiB,EAAK,YAAY,CAAC,EAAM,aAAa;EAC5D,IAAM,IAAQ,EAAK,iBAAiB,CAAK;EACzC,IAAI,CAAC,0BAA0B,KAAK,EAAM,SAAS,GAAG;EAGtD,IAAI,EAAM,kBAAkB,oBAAqB,EAAM,eAAe,EAAM,gBAAgB,iBAAkB,OAAO;EACrH,IAAM,IAAU,KAAK,IAAI,GAAG,EAAM,eAAe,EAAM,YAAY,GAC7D,IAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAS,EAAM,SAAS,CAAC;EAG1D,IAAI,IAAS,IAAI,IAAU,IAAM,IAAI,IAAM,GAAG,OAAO;EAErD,IAAI,mBAAmB,KAAK,EAAM,mBAAmB,GAAG,OAAO;CAChE;CACA,OAAO;AACR;;;ACpBA,SAAgB,EAAyB,GAA0B,GAAuC;CACzG,IAAM,IAAO,EAAe;CAC5B,IAAI,CAAC,GAAM,OAAO;CAClB,IAAI,IAAuB,IACvB;CACJ,KAAK,IAAM,KAAS,GAAM;EACzB,IAAI,EAAE,aAAiB,EAAK,UAAU;EAEtC,IADA,MAAW,GACP,EAAM,QAAQ,wGAAwG,GACzH,OAAO;EAER,IAAI,GAAsB;EAC1B,IAAM,IAAW,EAAM,aAAa,iBAAiB,CAAC,EAAE,YAAY;EACpE,IAAI,MAAa,MAAM,MAAa,UAAU,MAAa,kBAAkB,OAAO;EACpF,AAAI,MAAa,YAAS,IAAuB;CAClD;CACA,IAAM,IAAY,EAAe,aAAa;CAE9C,IAAI,CAAC,KAAU,CAAC,KAAa,EAAU,aAAa,OAAO;CAC3D,KAAK,IAAI,IAAQ,GAAG,IAAQ,EAAU,YAAY,KAAS,GAC1D,IAAI,EAAU,WAAW,CAAK,CAAC,CAAC,eAAe,CAAM,GAAG,OAAO;CAEhE,OAAO;AACR;;;ACTA,SAAgB,EAA+B,GAAsC;CACpF,IAAM,IAAO,EAAe,aACxB,IAA6C,MAC7C,IAAgB,IAEd,KAAW,MAA4B;EAE5C,IAAI,GAAe;EACnB,IAAM,IAAQ,EAAM,QAAQ,IACtB,IAAO,EAAM,aAAa;EAChC,IACC,EAAM,QAAQ,WAAW,KACzB,CAAC,MACA,GAAM,gBAAgB,SAAS,KAAK,KACrC,EAAyB,GAAgB,CAAI,GAC5C;GAED,AADA,IAAgB,IAChB,IAAc;GACd;EACD;EACA,IAAc;GAAE,YAAY,EAAM;GAAY,GAAG,EAAM;GAAS,GAAG,EAAM;GAAS;EAAK;CACxF,GAEM,KAAU,MAA4B;EAC3C,IAAI,KAAiB,CAAC,GAAa;EACnC,IAAM,IAAQ,EAAM,QAAQ;EAC5B,IAAI,EAAM,QAAQ,WAAW,KAAK,CAAC,KAAS,EAAM,eAAe,EAAY,eAAe,GAAM,gBAAgB,SAAS,KAAK,GAAG;GAElI,AADA,IAAgB,IAChB,IAAc;GACd;EACD;EACA,IAAM,EAAE,MAAG,MAAG,YAAS,GACjB,IAAS,IAAI,EAAM,SACnB,IAAS,IAAI,EAAM;EAEzB,IADA,IAAc;GAAE,YAAY,EAAM;GAAY,GAAG,EAAM;GAAS,GAAG,EAAM;GAAS;EAAK,GACnF,EAAyB,GAAgB,CAAI,GAAG;GACnD,IAAgB;GAChB;EACD;EAEI,CAAC,EAAM,cAAc,EAAM,oBAAoB,CAAC,KAAU,KAAK,IAAI,CAAM,KAAK,KAAK,IAAI,CAAM,KAC5F,EAA8B,GAAgB,GAAM,CAAM,KAAG,EAAM,eAAe;CACxF,GAEM,KAAS,MAA4B;EACtC,EAAM,QAAQ,SAAS,MAC3B,IAAc,MACd,IAAgB;CACjB;CAMA,OAJA,EAAe,iBAAiB,cAAc,GAAS;EAAE,SAAS;EAAM,SAAS;CAAK,CAAC,GACvF,EAAe,iBAAiB,aAAa,GAAQ;EAAE,SAAS;EAAM,SAAS;CAAM,CAAC,GACtF,EAAe,iBAAiB,YAAY,GAAO;EAAE,SAAS;EAAM,SAAS;CAAK,CAAC,GACnF,EAAe,iBAAiB,eAAe,GAAO;EAAE,SAAS;EAAM,SAAS;CAAK,CAAC,SACzE;EAKZ,AAJA,EAAe,oBAAoB,cAAc,GAAS,EAAI,GAC9D,EAAe,oBAAoB,aAAa,GAAQ,EAAI,GAC5D,EAAe,oBAAoB,YAAY,GAAO,EAAI,GAC1D,EAAe,oBAAoB,eAAe,GAAO,EAAI,GAC7D,IAAc;CACf;AACD;;;AClEA,IAAM,oBAAQ,IAAI,QAAsC;AAGxD,SAAS,EAAkB,GAA4B,GAAqD;CAC3G,OAAO,EAAM,KAAK,OAAU;EAAE;EAAM,OAAO,EAAM,iBAAiB,CAAI;EAAG,UAAU,EAAM,oBAAoB,CAAI;CAAE,EAAE;AACtH;AAEA,SAAS,EAAkB,GAA4B,GAA6C;CAGnG,KAAK,IAAM,EAAE,aAAU,GAAY,EAAM,eAAe,CAAI;CAC5D,KAAK,IAAM,EAAE,SAAM,UAAO,iBAAc,GACvC,AAAI,KAAO,EAAM,YAAY,GAAM,GAAO,CAAQ;AAEpD;AAGA,SAAgB,EAA0B,GAA0B,GAA0C;CAC7G,IAAM,IAAO,EAAe,aACtB,IAAO,EAAe;CAC5B,IAAI,CAAC,KAAQ,CAAC,GAAM,aAAa,KAAA;CAEjC,IAAI,IAAO,EAAM,IAAI,CAAc;CACnC,IAAI,CAAC,GAAM;EACV,IAAM,IAAY,EAAe,gBAAgB,OAC3C,IAAY,EAAK,OACjB,IAAiB,EAAkB,GAAW;GAAC;GAAY;GAAc;EAAY,CAAC,GACtF,IAAiB,EAAkB,GAAW;GACnD;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACD,CAAC,GACK,IAAkB,EAAkB,GAAW,CAAC,eAAe,CAAC,GAChE,IAAU,EAAK,SACf,IAAU,EAAK,SACf,IAAiB,KAAK,IAAI,GAAG,EAAK,aAAa,EAAe,gBAAgB,WAAW,GACzF,IAAkB,OAAO,WAAW,EAAK,iBAAiB,CAAI,CAAC,CAAC,YAAY,KAAK,GACjF,oBAAS,IAAI,IAAqB;EASxC,AANA,EAAU,YAAY,YAAY,QAAQ,GAC1C,EAAU,YAAY,YAAY,OAAO,GACzC,EAAU,YAAY,OAAO,GAAG,CAAC,EAAQ,GAAG,GAC5C,EAAU,YAAY,QAAQ,GAAG,CAAC,EAAQ,GAAG,GAC7C,EAAU,YAAY,SAAS,MAAM,GACrC,EAAU,YAAY,YAAY,QAAQ,GAC1C,EAAU,YAAY,cAAc,YAAY;EAChD,IAAM,IAAoB,EAA+B,CAAc;EAgBvE,AAdA,IAAO;GACN;GACA,0BAA0B;IACzB,AAAI,IAAiB,KAAK,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,IAC1D,EAAU,YAAY,iBAAiB,GAAG,IAAkB,EAAe,GAAG,IACxE,EAAkB,GAAW,CAAe;GACpD;GACA,eAAe;IAId,AAHA,EAAkB,GAClB,EAAkB,GAAW,CAAc,GAC3C,EAAkB,GAAW,CAAc,GAC3C,EAAK,SAAS;KAAE,MAAM;KAAS,KAAK;KAAS,UAAU;IAAU,CAAC;GACnE;EACD,GACA,EAAM,IAAI,GAAgB,CAAI;CAC/B;CAEA,IAAM,IAAQ,OAAO;CAGrB,OAFA,EAAK,OAAO,IAAI,GAAO,CAAmB,GAC1C,EAAK,mBAAmB,SACX;EACP,EAAK,OAAO,OAAO,CAAK,MACzB,EAAK,OAAO,OAAO,IAAG,EAAK,mBAAmB,KAEjD,EAAM,OAAO,CAAc,GAC3B,EAAK,QAAQ;CAEf;AACD;;;ACvFA,SAAgB,EAAsB,EAAE,WAAQ,mBAAgB,yBAAsB,MAA6C;CAClI,QAAgB;EACf,IAAI,CAAC,GAAQ;EACb,IAAM,IAAS,MAAmB,KAAA,IAAa,OAAO,WAAa,MAAc,OAAO,WAAY;EAC/F,OACL,OAAO,EAA0B,GAAQ,CAAmB;CAC7D,GAAG;EAAC;EAAQ;EAAgB;CAAmB,CAAC;AACjD;;;ACbA,IAAM,IAAa;CAAC;CAAyB;CAA0B;CAA2B;AAA0B,GAEtH,oBAAc,IAAI,QAAkE;AAG1F,SAAS,EAA0B,GAAoC;CACtE,IAAM,IAAO,EAAU,cAAc;CACrC,IAAI,CAAC,GAAM,aAAa,KAAA;CACxB,IAAM,IAAW,EAAK,gBAChB,IAAQ,EAAU,OAClB,IAAW,EAAW,KAAK,OAAU;EAAE;EAAM,OAAO,EAAM,iBAAiB,CAAI;EAAG,UAAU,EAAM,oBAAoB,CAAI;CAAE,EAAE,GAC9H,oBAAc,IAAI,IAAoB,GACxC,IAAQ,GAEN,UAAqB;EAC1B,IAAQ;EACR,IAAM,IAAS;GACd,GAAU,aAAa;GACvB,GAAU,cAAc;GACxB,GAAU,SAAS,EAAK;GACxB,GAAU,UAAU,EAAK;EAC1B,GACM,IAAW;GAAC;GAAG;GAAG,EAAK;GAAY,EAAK;EAAW;EACzD,EAAW,SAAS,GAAM,MAAU;GACnC,IAAM,IAAQ,GAAG,OAAO,SAAS,EAAO,EAAM,IAAI,EAAO,KAAS,EAAS,GAAO;GAC9E,EAAY,IAAI,CAAI,MAAM,MAC9B,EAAM,YAAY,GAAM,CAAK,GAC7B,EAAY,IAAI,GAAM,CAAK;EAC5B,CAAC;CACF,GAEM,UAAuB;EAC5B,AAAY,MAAQ,EAAK,sBAAsB,CAAM;CACtD;CAMA,OAJA,EAAO,GACP,GAAU,iBAAiB,UAAU,CAAQ,GAC7C,GAAU,iBAAiB,UAAU,CAAQ,GAC7C,EAAK,iBAAiB,UAAU,CAAQ,SAC3B;EAIZ,AAHA,EAAK,qBAAqB,CAAK,GAC/B,GAAU,oBAAoB,UAAU,CAAQ,GAChD,GAAU,oBAAoB,UAAU,CAAQ,GAChD,EAAK,oBAAoB,UAAU,CAAQ;EAC3C,KAAK,IAAM,EAAE,SAAM,UAAO,iBAAc,GAEnC,EAAM,iBAAiB,CAAI,MAAM,EAAY,IAAI,CAAI,KAAK,EAAM,oBAAoB,CAAI,MACxF,IAAO,EAAM,YAAY,GAAM,GAAO,CAAQ,IAC7C,EAAM,eAAe,CAAI;CAEhC;AACD;AAGA,SAAgB,EAA0B,GAAoC;CAC7E,IAAI,IAAa,EAAY,IAAI,CAAS;CAC1C,AAAK,MACJ,IAAa;EAAE,wBAAQ,IAAI,IAAI;EAAG,QAAQ,EAA0B,CAAS;CAAE,GAC/E,EAAY,IAAI,GAAW,CAAU;CAEtC,IAAM,IAAQ,OAAO;CAErB,OADA,EAAW,OAAO,IAAI,CAAK,SACd;EACR,CAAC,EAAW,OAAO,OAAO,CAAK,KAAK,EAAW,OAAO,OAAO,MACjE,EAAY,OAAO,CAAS,GAC5B,EAAW,OAAO;CACnB;AACD;;;AC7DA,SAAgB,EAAuB,EAAE,WAAQ,mBAAqD;CACrG,QAAgB;EACf,IAAI,CAAC,KAAU,OAAO,SAAW,KAAa;EAC9C,IAAI,IAAQ,GACR,GAEE,UAAe;GACpB,IAAM,IAAY,EAAa;GAC/B,AAAI,IAAW,IAAS,EAA0B,CAAS,IACtD,IAAQ,OAAO,sBAAsB,CAAM;EACjD;EAEA,OADA,EAAO,SACM;GAEZ,AADA,OAAO,qBAAqB,CAAK,GACjC,IAAS;EACV;CACD,GAAG,CAAC,GAAQ,CAAY,CAAC;AAC1B"}
|
package/dist/media/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/media/breakpoints.ts","../../src/media/getCurrentFontSize.ts","../../src/media/pxToEm.ts","../../src/media/responsive.ts","../../src/media/useMediaQuery.ts","../../src/media/useMatchMedia.ts"],"sourcesContent":["export const BREAKPOINTS_EM = {\n\tmobileMax: 479.98 / 16,\n\ttabletMin: 480 / 16,\n\ttabletMax: 1151.98 / 16,\n\tlaptopMin: 1152 / 16\n} as const;\n\nexport type Breakpoint = \"mobile\" | \"tablet\" | \"laptop\";\nexport type MediaMatches = Record<Breakpoint, boolean>;\n","const DEFAULT_FONT_SIZE = 16;\nconst DEFAULT_LINE_HEIGHT = 1.2;\n\nexport function getCurrentFontSize(element?: HTMLElement | null): number {\n\tif (typeof window === \"undefined\" || typeof document === \"undefined\") {\n\t\treturn DEFAULT_FONT_SIZE;\n\t}\n\n\tconst target = element ?? document.documentElement;\n\tconst rootFontSize = window.getComputedStyle(target).fontSize;\n\tconst fontSize = Number.parseFloat(rootFontSize);\n\n\tif (!Number.isFinite(fontSize) || fontSize <= 0) {\n\t\treturn DEFAULT_FONT_SIZE;\n\t}\n\n\treturn fontSize;\n}\n\nexport function getCurrentLineHeight(element?: HTMLElement | null): number {\n\tif (typeof window === \"undefined\" || typeof document === \"undefined\") {\n\t\treturn DEFAULT_LINE_HEIGHT;\n\t}\n\n\tconst target = element ?? document.documentElement;\n\tconst computedStyles = window.getComputedStyle(target);\n\tconst rootLineHeight = computedStyles.lineHeight;\n\tconst lineHeight = Number.parseFloat(rootLineHeight);\n\n\tif (!Number.isFinite(lineHeight) || lineHeight <= 0) {\n\t\treturn DEFAULT_LINE_HEIGHT;\n\t}\n\n\treturn lineHeight / DEFAULT_FONT_SIZE;\n}\n\nexport function getControlHeight() {\n\tconst fontSize = getCurrentFontSize();\n\tconst lineHeight = getCurrentLineHeight();\n\n\t// Правильно расчета --control-height\n\treturn fontSize + lineHeight * 10;\n}\n","import { getCurrentFontSize } from \"./getCurrentFontSize\";\n\nexport function pxToEm(px: number, baseEm?: number): string {\n\tconst emSize = baseEm ?? getCurrentFontSize(); // динамическая привязка\n\treturn (px / emSize).toString();\n}\n","import { Breakpoint } from \"./breakpoints\";\n\nexport type ResponsiveMap<T> = {\n\tmobile?: T;\n\ttablet?: T;\n\tlaptop?: T;\n};\nexport type ResponsiveValue<T> = T | ResponsiveMap<T>;\nexport type UnwrapResponsive<T> = T extends ResponsiveValue<infer U> ? U : T;\n\nexport function isResponsiveMap<T>(v: ResponsiveValue<T>): v is ResponsiveMap<T> {\n\tif (typeof v !== \"object\" || v === null) return false;\n\n\t// ключевой момент: отличаем “responsive-map” от произвольного объекта T\n\treturn \"mobile\" in v || \"tablet\" in v || \"laptop\" in v || \"desktop\" in v;\n}\n\n/**\n * Превращает объект пропсов, где некоторые поля ResponsiveValue<...>,\n * в объект, где эти поля уже \"плоские\" (U).\n */\nexport type ResolvedProps<T extends object> = {\n\t[K in keyof T]: UnwrapResponsive<T[K]>;\n};\n\n/**\n * Резолвит responsive значение под текущий брейкпоинт.\n * Приоритеты:\n * - exact match (mobile/tablet/laptop)\n * - fallback на \"первое определённое\" в предсказуемом порядке\n */\nexport function resolveResponsiveValue<T>(value: ResponsiveValue<T> | undefined, bp: Breakpoint): T | undefined {\n\tif (value === undefined) return undefined;\n\tif (!isResponsiveMap(value)) {\n\t\t// здесь value точно T (в т.ч. может быть объектом — и это ок)\n\t\treturn value;\n\t}\n\n\tif (bp === \"mobile\") return value.mobile ?? value.tablet ?? value.laptop;\n\tif (bp === \"tablet\") return value.tablet ?? value.mobile ?? value.laptop;\n\n\treturn value.laptop ?? value.tablet ?? value.mobile;\n}\n\n/**\n * Типобезопасно резолвит все поля объекта по списку ключей, которые responsive.\n * (TS-магия: на выходе будет ResolvedProps<T>.)\n */\nexport function resolveProps<T extends object>(props: T, bp: Breakpoint, responsiveKeys: readonly (keyof T)[]): ResolvedProps<T> {\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tconst out: any = { ...props };\n\n\tfor (const key of responsiveKeys) {\n\t\tout[key] = resolveResponsiveValue(out[key], bp);\n\t}\n\n\treturn out as ResolvedProps<T>;\n}\n","import { useCallback, useSyncExternalStore } from \"react\";\n\ntype UseMediaQueryOptions = {\n\treadonly ssrFallback?: boolean;\n};\n\nconst noop = () => {};\n\nfunction readMediaQuery(query: string, fallback: boolean): boolean {\n\tif (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return fallback;\n\n\treturn window.matchMedia(query).matches;\n}\n\nfunction subscribeMediaQuery(query: string, listener: () => void): () => void {\n\tif (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return noop;\n\n\tconst mql = window.matchMedia(query);\n\n\tif (typeof mql.addEventListener === \"function\") {\n\t\tmql.addEventListener(\"change\", listener);\n\t\treturn () => mql.removeEventListener(\"change\", listener);\n\t}\n\n\tmql.addListener(listener);\n\treturn () => mql.removeListener(listener);\n}\n\n/**\n * SSR-безопасная подписка на произвольный media query.\n * Используется для клиентских предпочтений, где значение должно совпадать с SSR fallback на гидрации.\n */\nexport function useMediaQuery(query: string, { ssrFallback = false }: UseMediaQueryOptions = {}) {\n\tconst subscribe = useCallback((listener: () => void) => subscribeMediaQuery(query, listener), [query]);\n\tconst getSnapshot = useCallback(() => readMediaQuery(query, ssrFallback), [query, ssrFallback]);\n\tconst getServerSnapshot = useCallback(() => ssrFallback, [ssrFallback]);\n\n\treturn useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","import { useSyncExternalStore } from \"react\";\n\nimport { BREAKPOINTS_EM, type Breakpoint, type MediaMatches } from \"./breakpoints\";\nimport { getCurrentFontSize } from \"./getCurrentFontSize\";\n\n/**\n * Публичный снимок состояния, который возвращает хук/store.\n * Содержит сырые совпадения media query и вычисленный активный брейкпоинт.\n */\ntype MatchMediaSnapshot = {\n\tmatches: MediaMatches;\n\tactiveBreakpoint: Breakpoint;\n};\n\nfunction subscribeMql(mql: MediaQueryList, cb: () => void) {\n\tif (\"addEventListener\" in mql) {\n\t\tmql.addEventListener(\"change\", cb);\n\t\treturn () => mql.removeEventListener(\"change\", cb);\n\t}\n\t// Safari legacy\n\t// @ts-expect-error legacy api\n\tmql.addListener(cb);\n\t// @ts-expect-error legacy api\n\treturn () => mql.removeListener(cb);\n}\n\nfunction buildQueriesPx(emSize: number) {\n\tconst toPx = (em: number) => em * emSize;\n\n\tconst mobileMax = toPx(BREAKPOINTS_EM.mobileMax);\n\tconst tabletMin = toPx(BREAKPOINTS_EM.tabletMin);\n\tconst tabletMax = toPx(BREAKPOINTS_EM.tabletMax);\n\tconst laptopMin = toPx(BREAKPOINTS_EM.laptopMin);\n\n\treturn {\n\t\tmobile: `(width <= ${mobileMax}px)`,\n\t\ttablet: `(width >= ${tabletMin}px) and (width <= ${tabletMax}px)`,\n\t\tlaptop: `(width >= ${laptopMin}px)`\n\t} satisfies Record<Breakpoint, string>;\n}\n\nconst DEFAULT_MATCHES: MediaMatches = {\n\tmobile: false,\n\ttablet: false,\n\tlaptop: false\n};\n\nconst DEFAULT_SNAPSHOT: MatchMediaSnapshot = {\n\tmatches: DEFAULT_MATCHES,\n\tactiveBreakpoint: \"laptop\"\n};\n\n/**\n * Игнорируем микроколебания `em`, чтобы не перестраивать media query без необходимости.\n */\nconst EM_CHANGE_THRESHOLD = 0.25;\n/**\n * Склеиваем частые события observer-ов в одно перестроение.\n */\nconst EM_REBUILD_DEBOUNCE_MS = 80;\n\nlet snapshot: MatchMediaSnapshot = DEFAULT_SNAPSHOT;\nlet currentMql: Record<Breakpoint, MediaQueryList> | null = null;\nlet mqlUnsubs: Array<() => void> = [];\nlet emSizeRef = 0;\nlet initialized = false;\nlet mutationObserver: MutationObserver | null = null;\nlet resizeObserver: ResizeObserver | null = null;\nlet emProbeElement: HTMLDivElement | null = null;\nlet rebuildTimer: ReturnType<typeof setTimeout> | null = null;\n\nconst listeners = new Set<() => void>();\n\n/**\n * Выбирает один активный брейкпоинт по приоритету: mobile -> tablet -> laptop.\n */\nfunction getActiveBreakpoint(matches: MediaMatches): Breakpoint {\n\tif (matches.mobile) return \"mobile\";\n\tif (matches.tablet) return \"tablet\";\n\treturn \"laptop\";\n}\n\n/**\n * Поверхностное сравнение media-состояния, чтобы пропускать пустые уведомления.\n */\nfunction matchesEqual(a: MediaMatches, b: MediaMatches) {\n\treturn a.mobile === b.mobile && a.tablet === b.tablet && a.laptop === b.laptop;\n}\n\n/**\n * Обновляет глобальный snapshot и уведомляет подписчиков только при реальных изменениях.\n */\nfunction emitIfChanged(nextMatches: MediaMatches) {\n\tif (matchesEqual(snapshot.matches, nextMatches)) return;\n\tsnapshot = {\n\t\tmatches: nextMatches,\n\t\tactiveBreakpoint: getActiveBreakpoint(nextMatches)\n\t};\n\tlisteners.forEach((listener) => listener());\n}\n\n/**\n * Читает текущие значения `MediaQueryList` и публикует новый snapshot.\n */\nfunction recomputeFromMql() {\n\tif (!currentMql) return;\n\n\temitIfChanged({\n\t\tmobile: currentMql.mobile.matches,\n\t\ttablet: currentMql.tablet.matches,\n\t\tlaptop: currentMql.laptop.matches\n\t});\n}\n\n/**\n * Снимает активные mql-подписки перед перестроением/очисткой.\n */\nfunction clearMqlSubscriptions() {\n\tmqlUnsubs.forEach((unsubscribe) => unsubscribe());\n\tmqlUnsubs = [];\n}\n\n/**\n * Перестраивает media query, когда корневой `em` изменился достаточно сильно (или принудительно).\n * Это дорогой путь, поэтому он специально ограничен проверками.\n */\nfunction rebuildIfEmChanged(force = false) {\n\tif (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n\tconst em = getCurrentFontSize();\n\tif (!force && currentMql && Math.abs(em - emSizeRef) < EM_CHANGE_THRESHOLD) return;\n\n\temSizeRef = em;\n\tclearMqlSubscriptions();\n\n\tconst queries = buildQueriesPx(em);\n\tcurrentMql = {\n\t\tmobile: window.matchMedia(queries.mobile),\n\t\ttablet: window.matchMedia(queries.tablet),\n\t\tlaptop: window.matchMedia(queries.laptop)\n\t};\n\n\tmqlUnsubs = (Object.keys(currentMql) as Breakpoint[]).map((key) => subscribeMql(currentMql![key], recomputeFromMql));\n\trecomputeFromMql();\n}\n\n/**\n * Дебаунс-обертка для перестроения query по событиям observer-ов.\n */\nfunction scheduleRebuild() {\n\tif (rebuildTimer) clearTimeout(rebuildTimer);\n\trebuildTimer = setTimeout(() => {\n\t\trebuildTimer = null;\n\t\trebuildIfEmChanged();\n\t}, EM_REBUILD_DEBOUNCE_MS);\n}\n\n/**\n * Наблюдает за изменением style/class у root и размером probe-элемента `1em`.\n * Это позволяет ловить изменения font-size/zoom без слушателя window resize.\n */\nfunction setupEmObservers() {\n\tif (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n\tmutationObserver = new MutationObserver(scheduleRebuild);\n\tmutationObserver.observe(document.documentElement, {\n\t\tattributes: true,\n\t\tattributeFilter: [\"style\", \"class\", \"data-font-size\"]\n\t});\n\n\tif (typeof ResizeObserver === \"undefined\") return;\n\n\temProbeElement = document.createElement(\"div\");\n\temProbeElement.style.position = \"absolute\";\n\temProbeElement.style.visibility = \"hidden\";\n\temProbeElement.style.pointerEvents = \"none\";\n\temProbeElement.style.width = \"1em\";\n\temProbeElement.style.height = \"1px\";\n\temProbeElement.style.overflow = \"hidden\";\n\temProbeElement.setAttribute(\"aria-hidden\", \"true\");\n\n\t(document.body ?? document.documentElement).appendChild(emProbeElement);\n\n\tresizeObserver = new ResizeObserver(scheduleRebuild);\n\tresizeObserver.observe(emProbeElement);\n}\n\n/**\n * Полностью освобождает глобальные ресурсы, когда не осталось подписчиков.\n */\nfunction cleanupStore() {\n\tclearMqlSubscriptions();\n\tcurrentMql = null;\n\n\tif (rebuildTimer) {\n\t\tclearTimeout(rebuildTimer);\n\t\trebuildTimer = null;\n\t}\n\n\tif (mutationObserver) {\n\t\tmutationObserver.disconnect();\n\t\tmutationObserver = null;\n\t}\n\n\tif (resizeObserver) {\n\t\tresizeObserver.disconnect();\n\t\tresizeObserver = null;\n\t}\n\n\tif (emProbeElement?.parentNode) {\n\t\temProbeElement.parentNode.removeChild(emProbeElement);\n\t}\n\temProbeElement = null;\n\n\tinitialized = false;\n}\n\n/**\n * Лениво инициализирует singleton-store при первом подписчике.\n */\nfunction initializeStore() {\n\tif (initialized) return;\n\tif (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n\tinitialized = true;\n\trebuildIfEmChanged(true);\n\tsetupEmObservers();\n}\n\n/**\n * API подписки для `useSyncExternalStore`.\n * Держит ровно один глобальный набор observer-ов и mql на всё приложение.\n */\nfunction subscribeStore(listener: () => void) {\n\tif (typeof window === \"undefined\" || typeof document === \"undefined\") {\n\t\treturn () => {};\n\t}\n\n\tlisteners.add(listener);\n\tinitializeStore();\n\n\treturn () => {\n\t\tlisteners.delete(listener);\n\t\tif (listeners.size === 0) {\n\t\t\tcleanupStore();\n\t\t}\n\t};\n}\n\n/**\n * Getter клиентского snapshot для `useSyncExternalStore`.\n */\nfunction getSnapshot() {\n\treturn snapshot;\n}\n\n/**\n * SSR-безопасный getter snapshot.\n */\nfunction getServerSnapshot() {\n\treturn DEFAULT_SNAPSHOT;\n}\n\n/**\n * Общий хук брейкпоинтов, работающий через singleton external store.\n */\nexport function useMatchMedia() {\n\treturn useSyncExternalStore(subscribeStore, getSnapshot, getServerSnapshot);\n}\n"],"mappings":"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"}
|