@ryuzaki13/react-foundation-lib 1.1.9 → 1.1.10
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/hooks/index.js
CHANGED
|
@@ -61,10 +61,11 @@ function A(e = 6) {
|
|
|
61
61
|
}
|
|
62
62
|
//#endregion
|
|
63
63
|
//#region src/hooks/useFloatingListbox.ts
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
var j = /* @__PURE__ */ new WeakMap();
|
|
65
|
+
function M({ options: e, selectedIndex: a, open: s, onOpenChange: u, getOptionDisabled: d, onSelect: f, disabled: p, placement: m = "bottom-start", closeOnSelect: w = !0, focusFloatingOnOpen: T = !0, allowOpenWithoutOptions: E = !1, restoreFocusOnClose: D = !0, resolveFloatingSize: O }) {
|
|
66
|
+
let k = c([]), [A, M] = l(!1), N = s ?? A, P = u ?? M, [F, I] = l(a >= 0 ? a : t(e, d)), L = (n) => e.length === 0 ? -1 : n >= 0 && n < e.length && !d?.(e[n]) ? n : a >= 0 && a < e.length && !d?.(e[a]) ? a : t(e, d), R = L(F), { refs: z, floatingStyles: B, context: V } = x({
|
|
67
|
+
open: N,
|
|
68
|
+
onOpenChange: P,
|
|
68
69
|
placement: m,
|
|
69
70
|
transform: !1,
|
|
70
71
|
strategy: "fixed",
|
|
@@ -74,144 +75,171 @@ function j({ options: e, selectedIndex: a, open: s, onOpenChange: u, getOptionDi
|
|
|
74
75
|
v({ padding: 8 }),
|
|
75
76
|
y({
|
|
76
77
|
padding: 8,
|
|
77
|
-
apply({
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
apply({ availableWidth: e, availableHeight: t, rects: n, elements: r }) {
|
|
79
|
+
let i = r.floating.ownerDocument.documentElement, a = r.floating.style, o = O?.({
|
|
80
|
+
availableWidth: e,
|
|
81
|
+
availableHeight: t,
|
|
82
|
+
viewportWidth: i.clientWidth,
|
|
83
|
+
viewportHeight: i.clientHeight,
|
|
84
|
+
referenceWidth: n.reference.width,
|
|
85
|
+
referenceHeight: n.reference.height
|
|
86
|
+
}), s = j.get(r.floating);
|
|
87
|
+
for (let e of s?.customProperties ?? []) a.removeProperty(e);
|
|
88
|
+
for (let [e, t] of s?.standardProperties ?? []) a[e] = t;
|
|
89
|
+
a.minWidth = `${n.reference.width}px`, a.maxHeight = `${Math.max(t, 120)}px`;
|
|
90
|
+
let c = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Map();
|
|
91
|
+
for (let [e, t] of Object.entries(o ?? {})) {
|
|
92
|
+
if (e.startsWith("--")) {
|
|
93
|
+
let n = e;
|
|
94
|
+
t !== void 0 && (a.setProperty(n, t), c.add(n));
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (t !== void 0) {
|
|
98
|
+
let n = e;
|
|
99
|
+
l.set(n, a[n]), a[n] = t;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
j.set(r.floating, {
|
|
103
|
+
customProperties: c,
|
|
104
|
+
standardProperties: l
|
|
81
105
|
});
|
|
82
106
|
}
|
|
83
107
|
})
|
|
84
108
|
],
|
|
85
109
|
whileElementsMounted: h
|
|
86
|
-
}), { getFloatingProps:
|
|
110
|
+
}), { getFloatingProps: H } = S([b(V), C(V, { role: "listbox" })]), U = E || e.length > 0;
|
|
87
111
|
o(() => {
|
|
88
|
-
!
|
|
89
|
-
}, [
|
|
90
|
-
!
|
|
91
|
-
|
|
112
|
+
!N || R < 0 || k.current[R]?.scrollIntoView({ block: "nearest" });
|
|
113
|
+
}, [R, N]), o(() => {
|
|
114
|
+
!N || !T || requestAnimationFrame(() => {
|
|
115
|
+
z.floating.current?.focus();
|
|
92
116
|
});
|
|
93
117
|
}, [
|
|
94
118
|
T,
|
|
95
|
-
|
|
96
|
-
|
|
119
|
+
N,
|
|
120
|
+
z
|
|
97
121
|
]);
|
|
98
|
-
let
|
|
99
|
-
if (
|
|
100
|
-
let e =
|
|
122
|
+
let W = () => {
|
|
123
|
+
if (P(!1), D) {
|
|
124
|
+
let e = z.domReference.current;
|
|
101
125
|
e instanceof HTMLElement && e.focus();
|
|
102
126
|
}
|
|
103
|
-
},
|
|
104
|
-
p || !
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
},
|
|
108
|
-
if (
|
|
109
|
-
|
|
127
|
+
}, G = (t) => {
|
|
128
|
+
p || !U || (e.length > 0 && I(L(t)), P(!0));
|
|
129
|
+
}, K = () => {
|
|
130
|
+
G(a >= 0 ? a : t(e, d));
|
|
131
|
+
}, q = () => {
|
|
132
|
+
if (N) {
|
|
133
|
+
W();
|
|
110
134
|
return;
|
|
111
135
|
}
|
|
112
|
-
|
|
113
|
-
},
|
|
114
|
-
d?.(e) || (f?.(e), w &&
|
|
115
|
-
}, q = () => {
|
|
116
|
-
if (I < 0) return;
|
|
117
|
-
let t = e[I];
|
|
118
|
-
t !== void 0 && K(t);
|
|
119
|
-
}, J = () => {
|
|
120
|
-
P(t(e, d));
|
|
136
|
+
K();
|
|
137
|
+
}, J = (e) => {
|
|
138
|
+
d?.(e) || (f?.(e), w && W());
|
|
121
139
|
}, Y = () => {
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
if (R < 0) return;
|
|
141
|
+
let t = e[R];
|
|
142
|
+
t !== void 0 && J(t);
|
|
143
|
+
}, X = () => {
|
|
144
|
+
I(t(e, d));
|
|
145
|
+
}, Z = () => {
|
|
146
|
+
I(n(e, d));
|
|
147
|
+
}, Q = (i) => {
|
|
124
148
|
if (!p) {
|
|
125
149
|
if (i.key === "ArrowDown") {
|
|
126
|
-
if (i.preventDefault(), !
|
|
127
|
-
|
|
150
|
+
if (i.preventDefault(), !N) {
|
|
151
|
+
G(a >= 0 ? a : t(e, d));
|
|
128
152
|
return;
|
|
129
153
|
}
|
|
130
|
-
|
|
154
|
+
I((t) => r(e, L(t), 1, d));
|
|
131
155
|
return;
|
|
132
156
|
}
|
|
133
157
|
if (i.key === "ArrowUp") {
|
|
134
|
-
if (i.preventDefault(), !
|
|
135
|
-
|
|
158
|
+
if (i.preventDefault(), !N) {
|
|
159
|
+
G(a >= 0 ? a : n(e, d));
|
|
136
160
|
return;
|
|
137
161
|
}
|
|
138
|
-
|
|
162
|
+
I((t) => r(e, L(t), -1, d));
|
|
139
163
|
return;
|
|
140
164
|
}
|
|
141
|
-
if (i.key === "Home" &&
|
|
142
|
-
i.preventDefault(),
|
|
165
|
+
if (i.key === "Home" && N) {
|
|
166
|
+
i.preventDefault(), X();
|
|
143
167
|
return;
|
|
144
168
|
}
|
|
145
|
-
if (i.key === "End" &&
|
|
146
|
-
i.preventDefault(),
|
|
169
|
+
if (i.key === "End" && N) {
|
|
170
|
+
i.preventDefault(), Z();
|
|
147
171
|
return;
|
|
148
172
|
}
|
|
149
|
-
if (i.key === "Escape" &&
|
|
150
|
-
i.preventDefault(),
|
|
173
|
+
if (i.key === "Escape" && N) {
|
|
174
|
+
i.preventDefault(), W();
|
|
151
175
|
return;
|
|
152
176
|
}
|
|
153
|
-
i.key === "Tab" &&
|
|
177
|
+
i.key === "Tab" && N && W();
|
|
178
|
+
}
|
|
179
|
+
}, ee = (t) => {
|
|
180
|
+
if (t.target !== t.currentTarget) {
|
|
181
|
+
t.key === "Escape" && (t.preventDefault(), W());
|
|
182
|
+
return;
|
|
154
183
|
}
|
|
155
|
-
}, Z = (t) => {
|
|
156
184
|
if (t.key === "ArrowDown") {
|
|
157
|
-
t.preventDefault(),
|
|
185
|
+
t.preventDefault(), I((t) => r(e, L(t), 1, d));
|
|
158
186
|
return;
|
|
159
187
|
}
|
|
160
188
|
if (t.key === "ArrowUp") {
|
|
161
|
-
t.preventDefault(),
|
|
189
|
+
t.preventDefault(), I((t) => r(e, L(t), -1, d));
|
|
162
190
|
return;
|
|
163
191
|
}
|
|
164
192
|
if (t.key === "Home") {
|
|
165
|
-
t.preventDefault(),
|
|
193
|
+
t.preventDefault(), X();
|
|
166
194
|
return;
|
|
167
195
|
}
|
|
168
196
|
if (t.key === "End") {
|
|
169
|
-
t.preventDefault(),
|
|
197
|
+
t.preventDefault(), Z();
|
|
170
198
|
return;
|
|
171
199
|
}
|
|
172
|
-
if (!i(t,
|
|
200
|
+
if (!i(t, Y)) {
|
|
173
201
|
if (t.key === "Escape") {
|
|
174
|
-
t.preventDefault(),
|
|
202
|
+
t.preventDefault(), W();
|
|
175
203
|
return;
|
|
176
204
|
}
|
|
177
|
-
t.key === "Tab" &&
|
|
205
|
+
t.key === "Tab" && W();
|
|
178
206
|
}
|
|
179
|
-
},
|
|
207
|
+
}, $ = (e, t) => `${e}-option-${t}`;
|
|
180
208
|
return {
|
|
181
|
-
open:
|
|
182
|
-
setOpen:
|
|
183
|
-
activeIndex:
|
|
184
|
-
refs:
|
|
185
|
-
context:
|
|
186
|
-
floatingStyles:
|
|
187
|
-
getFloatingProps:
|
|
188
|
-
setReference:
|
|
189
|
-
setFloating:
|
|
209
|
+
open: N,
|
|
210
|
+
setOpen: P,
|
|
211
|
+
activeIndex: R,
|
|
212
|
+
refs: z,
|
|
213
|
+
context: V,
|
|
214
|
+
floatingStyles: B,
|
|
215
|
+
getFloatingProps: H,
|
|
216
|
+
setReference: z.setReference,
|
|
217
|
+
setFloating: z.setFloating,
|
|
190
218
|
setOptionRef: (e, t) => {
|
|
191
|
-
|
|
219
|
+
k.current[e] = t;
|
|
192
220
|
},
|
|
193
|
-
close:
|
|
194
|
-
openList:
|
|
195
|
-
openWithIndex:
|
|
196
|
-
toggleOpen:
|
|
197
|
-
selectOption:
|
|
198
|
-
selectActiveOption:
|
|
199
|
-
handleReferenceKeyDown:
|
|
200
|
-
handleFloatingKeyDown:
|
|
201
|
-
getOptionId:
|
|
202
|
-
getActiveOptionId: (e) =>
|
|
221
|
+
close: W,
|
|
222
|
+
openList: K,
|
|
223
|
+
openWithIndex: G,
|
|
224
|
+
toggleOpen: q,
|
|
225
|
+
selectOption: J,
|
|
226
|
+
selectActiveOption: Y,
|
|
227
|
+
handleReferenceKeyDown: Q,
|
|
228
|
+
handleFloatingKeyDown: ee,
|
|
229
|
+
getOptionId: $,
|
|
230
|
+
getActiveOptionId: (e) => R >= 0 ? $(e, R) : void 0
|
|
203
231
|
};
|
|
204
232
|
}
|
|
205
233
|
//#endregion
|
|
206
234
|
//#region src/hooks/useLazyComponent.ts
|
|
207
|
-
var
|
|
235
|
+
var N = (t, n, r) => s(() => e(t, n, r), [
|
|
208
236
|
r,
|
|
209
237
|
t,
|
|
210
238
|
n
|
|
211
239
|
]);
|
|
212
240
|
//#endregion
|
|
213
241
|
//#region src/hooks/useReferenceTrace.ts
|
|
214
|
-
function
|
|
242
|
+
function P(e, t) {
|
|
215
243
|
let n = c(t);
|
|
216
244
|
o(() => {
|
|
217
245
|
if (!__DEV__) return;
|
|
@@ -225,7 +253,7 @@ function N(e, t) {
|
|
|
225
253
|
}
|
|
226
254
|
//#endregion
|
|
227
255
|
//#region src/hooks/useSearchLink.ts
|
|
228
|
-
function
|
|
256
|
+
function F() {
|
|
229
257
|
let e = w();
|
|
230
258
|
return { buildLink: (t) => {
|
|
231
259
|
let n = new URLSearchParams(e.searchStr);
|
|
@@ -240,7 +268,7 @@ function P() {
|
|
|
240
268
|
}
|
|
241
269
|
//#endregion
|
|
242
270
|
//#region src/hooks/useSearchParams.ts
|
|
243
|
-
function
|
|
271
|
+
function I(e) {
|
|
244
272
|
let t = E({ strict: !1 }), n = T();
|
|
245
273
|
return {
|
|
246
274
|
params: t,
|
|
@@ -257,7 +285,7 @@ function F(e) {
|
|
|
257
285
|
}
|
|
258
286
|
//#endregion
|
|
259
287
|
//#region src/hooks/useThrottledCallback.ts
|
|
260
|
-
function
|
|
288
|
+
function L(e, t, n) {
|
|
261
289
|
let r = c(e);
|
|
262
290
|
o(() => {
|
|
263
291
|
r.current = e;
|
|
@@ -295,6 +323,6 @@ function I(e, t, n) {
|
|
|
295
323
|
};
|
|
296
324
|
}
|
|
297
325
|
//#endregion
|
|
298
|
-
export { D as useBlockBrowserZoom, O as useDebounce, k as useDebouncedCallback, A as useDndSortableSensors,
|
|
326
|
+
export { D as useBlockBrowserZoom, O as useDebounce, k as useDebouncedCallback, A as useDndSortableSensors, M as useFloatingListbox, N as useLazyComponent, P as useReferenceTrace, F as useSearchLink, I as useSearchParams, L as useThrottledCallback };
|
|
299
327
|
|
|
300
328
|
//# sourceMappingURL=index.js.map
|
package/dist/hooks/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/hooks/useBlockBrowserZoom.ts","../../src/hooks/useDebounce.ts","../../src/hooks/useDebounceCallback.ts","../../src/hooks/useDndSortableSensors.ts","../../src/hooks/useFloatingListbox.ts","../../src/hooks/useLazyComponent.ts","../../src/hooks/useReferenceTrace.ts","../../src/hooks/useSearchLink.ts","../../src/hooks/useSearchParams.ts","../../src/hooks/useThrottledCallback.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\n/**\n * Глобально отключает браузерный zoom (ctrl+wheel).\n *\n * @param enabled если true — блокируем нативный зум страницы\n */\nexport function useBlockBrowserZoom(enabled: boolean) {\n\tuseEffect(() => {\n\t\tif (!enabled) return;\n\n\t\tconst handler = (e: WheelEvent) => {\n\t\t\tif (e.ctrlKey) {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"wheel\", handler, { passive: false });\n\t\treturn () => window.removeEventListener(\"wheel\", handler);\n\t}, [enabled]);\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useDebounce<T>(value: T, delay: number): T {\n\tconst [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n\tuseEffect(() => {\n\t\tconst timeoutId = setTimeout(() => {\n\t\t\tsetDebouncedValue(value);\n\t\t}, delay);\n\n\t\treturn () => clearTimeout(timeoutId);\n\t}, [value, delay]);\n\n\treturn debouncedValue;\n}\n","import { useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Дебаунсит вызов колбэка: частые вызовы `call(value)` не вызывают `cb` сразу,\n * а откладывают его на `delayMs` после последнего вызова.\n *\n * Важно:\n * - Всегда сохраняется *последнее* значение (last-write-wins).\n * - `flush()` немедленно вызывает cb с последним сохранённым значением (если оно есть).\n * - `cancel()` отменяет таймер и забывает последнее значение.\n * - На unmount автоматически вызывается `cancel()` чтобы не было setState после размонтирования.\n *\n * Типовой кейс:\n * - onNodesChange -> call(snapshot)\n * - pointerup / blur / unmount -> flush() (или cancel(), если не надо сохранять)\n */\nexport function useDebouncedCallback<T>(cb: (arg: T) => void, delayMs: number, options?: { flushOnUnmount?: boolean }) {\n\t/**\n\t * Храним актуальную версию cb в ref, чтобы:\n\t * - не пересоздавать дебаунсер при каждом рендере,\n\t * - не ловить устаревший замыканием cb (stale closure),\n\t * - при этом всегда вызывать самую свежую функцию.\n\t */\n\tconst cbRef = useRef<(arg: T) => void>(cb);\n\n\t/**\n\t * Обновляем актуальную версию cb после commit'а\n\t */\n\tuseEffect(() => {\n\t\tcbRef.current = cb;\n\t}, [cb]);\n\n\t/**\n\t * id таймера. null означает что таймер не активен.\n\t */\n\tconst tRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t/**\n\t * Последнее значение, которое мы хотим \"закоммитить\" в cb.\n\t * Используем стратегию last-write-wins.\n\t */\n\tconst lastRef = useRef<T | null>(null);\n\n\t/**\n\t * cancel():\n\t * - останавливаем таймер, если он был,\n\t * - очищаем последнее значение (ничего не будет вызвано).\n\t */\n\tconst cancel = useCallback(() => {\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\t\tlastRef.current = null;\n\t}, []);\n\n\t/**\n\t * flush():\n\t * - если есть отложенное значение, вызываем cb немедленно,\n\t * - сбрасываем таймер и lastRef.\n\t *\n\t * Используется, когда нужно гарантированно сохранить \"последнее\" прямо сейчас:\n\t * - unmount\n\t * - visibilitychange/blur\n\t * - pointerup/mouseup (конец взаимодействия)\n\t */\n\tconst flush = useCallback(() => {\n\t\tconst v = lastRef.current;\n\t\tif (v === null) return;\n\n\t\t// Сбрасываем before-call, чтобы если cb вызовет call() рекурсивно,\n\t\t// мы не держали старое значение.\n\t\tlastRef.current = null;\n\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\n\t\tcbRef.current(v);\n\t}, []);\n\n\t/**\n\t * call(arg):\n\t * - сохраняем arg как \"последнее значение\"\n\t * - перезапускаем таймер\n\t * - по истечении delayMs вызываем flush()\n\t */\n\tconst call = useCallback(\n\t\t(arg: T) => {\n\t\t\tlastRef.current = arg;\n\n\t\t\t// Перезапуск таймера -> классический debounce\n\t\t\tif (tRef.current) clearTimeout(tRef.current);\n\n\t\t\ttRef.current = setTimeout(() => {\n\t\t\t\ttRef.current = null;\n\t\t\t\tflush();\n\t\t\t}, delayMs);\n\t\t},\n\t\t[delayMs, flush]\n\t);\n\n\t/**\n\t * Авто-cancel на unmount:\n\t * - убирает pending timeout\n\t * - предотвращает вызовы cb после размонтирования компонента\n\t */\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (options?.flushOnUnmount) {\n\t\t\t\tflush();\n\t\t\t} else {\n\t\t\t\tcancel();\n\t\t\t}\n\t\t};\n\t}, [cancel, flush, options?.flushOnUnmount]);\n\n\treturn { call, flush, cancel };\n}\n","import { KeyboardSensor, PointerSensor, useSensor, useSensors } from \"@dnd-kit/core\";\nimport { sortableKeyboardCoordinates } from \"@dnd-kit/sortable\";\n\nexport interface UseDndSortableSensorsOptions {\n\tactivationDistance?: number;\n}\n\n/**\n * Стандартная конфигурация DnD-сенсоров для сортируемых списков.\n *\n * Зачем: исключить дублирование одинаковой настройки PointerSensor + KeyboardSensor\n * в каждом хуке персонализации (колонки, сортировки, группировки).\n *\n * @param distance — минимальное смещение указателя (px) для начала перетаскивания (по умолчанию 6).\n */\nexport function useDndSortableSensors(distanceOrOptions: number | UseDndSortableSensorsOptions = 6) {\n\tconst distance = typeof distanceOrOptions === \"number\" ? distanceOrOptions : (distanceOrOptions.activationDistance ?? 6);\n\n\treturn useSensors(\n\t\tuseSensor(PointerSensor, { activationConstraint: { distance } }),\n\t\tuseSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })\n\t);\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nimport {\n\tautoUpdate,\n\tflip,\n\tsize as floatingSize,\n\toffset,\n\tPlacement,\n\tshift,\n\tuseDismiss,\n\tuseFloating,\n\tuseInteractions,\n\tuseRole\n} from \"@floating-ui/react\";\n\nimport { findFirstEnabledIndex, findLastEnabledIndex, findNextEnabledIndex, handleKeyboardActivation } from \"../utils\";\n\ninterface UseFloatingListboxParams<T> {\n\toptions: readonly T[];\n\tselectedIndex: number;\n\topen?: boolean;\n\tonOpenChange?: (open: boolean) => void;\n\tgetOptionDisabled?: (option: T) => boolean;\n\tonSelect?: (option: T) => void;\n\tdisabled?: boolean;\n\tplacement?: Placement;\n\tcloseOnSelect?: boolean;\n\tfocusFloatingOnOpen?: boolean;\n\tallowOpenWithoutOptions?: boolean;\n\trestoreFocusOnClose?: boolean;\n}\n\nexport function useFloatingListbox<T>({\n\toptions,\n\tselectedIndex,\n\topen,\n\tonOpenChange,\n\tgetOptionDisabled,\n\tonSelect,\n\tdisabled,\n\tplacement = \"bottom-start\",\n\tcloseOnSelect = true,\n\tfocusFloatingOnOpen = true,\n\tallowOpenWithoutOptions = false,\n\trestoreFocusOnClose = true\n}: UseFloatingListboxParams<T>) {\n\tconst optionRefs = useRef<Array<HTMLElement | null>>([]);\n\tconst [internalOpen, setInternalOpen] = useState(false);\n\tconst resolvedOpen = open ?? internalOpen;\n\tconst setResolvedOpen = onOpenChange ?? setInternalOpen;\n\tconst [currentActiveIndex, setActiveIndex] = useState(\n\t\tselectedIndex >= 0 ? selectedIndex : findFirstEnabledIndex(options, getOptionDisabled)\n\t);\n\n\t/**\n\t * Поддерживает активный индекс в валидном состоянии при изменении набора опций\n\t * или выбранного элемента, чтобы `aria-activedescendant` всегда ссылался на\n\t * существующую и доступную опцию.\n\t */\n\tconst resolveActiveIndex = (activeIndex: number) => {\n\t\tif (options.length === 0) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (activeIndex >= 0 && activeIndex < options.length && !getOptionDisabled?.(options[activeIndex])) {\n\t\t\treturn activeIndex;\n\t\t}\n\n\t\tif (selectedIndex >= 0 && selectedIndex < options.length && !getOptionDisabled?.(options[selectedIndex])) {\n\t\t\treturn selectedIndex;\n\t\t}\n\n\t\treturn findFirstEnabledIndex(options, getOptionDisabled);\n\t};\n\tconst activeIndex = resolveActiveIndex(currentActiveIndex);\n\n\tconst { refs, floatingStyles, context } = useFloating({\n\t\topen: resolvedOpen,\n\t\tonOpenChange: setResolvedOpen,\n\t\tplacement,\n\t\ttransform: false,\n\t\tstrategy: \"fixed\",\n\t\tmiddleware: [\n\t\t\toffset(4),\n\t\t\tflip({ padding: 8 }),\n\t\t\tshift({ padding: 8 }),\n\t\t\tfloatingSize({\n\t\t\t\tpadding: 8,\n\t\t\t\tapply({ availableHeight, rects, elements }) {\n\t\t\t\t\tObject.assign(elements.floating.style, {\n\t\t\t\t\t\tminWidth: `${rects.reference.width}px`,\n\t\t\t\t\t\tmaxHeight: `${Math.max(availableHeight, 120)}px`\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t})\n\t\t],\n\t\twhileElementsMounted: autoUpdate\n\t});\n\n\tconst dismiss = useDismiss(context);\n\tconst role = useRole(context, { role: \"listbox\" });\n\tconst { getFloatingProps } = useInteractions([dismiss, role]);\n\n\tconst canOpen = allowOpenWithoutOptions || options.length > 0;\n\n\tuseEffect(() => {\n\t\tif (!resolvedOpen || activeIndex < 0) {\n\t\t\treturn;\n\t\t}\n\n\t\toptionRefs.current[activeIndex]?.scrollIntoView({ block: \"nearest\" });\n\t}, [activeIndex, resolvedOpen]);\n\n\tuseEffect(() => {\n\t\tif (!resolvedOpen || !focusFloatingOnOpen) {\n\t\t\treturn;\n\t\t}\n\n\t\trequestAnimationFrame(() => {\n\t\t\trefs.floating.current?.focus();\n\t\t});\n\t}, [focusFloatingOnOpen, resolvedOpen, refs]);\n\n\tconst close = () => {\n\t\tsetResolvedOpen(false);\n\n\t\tif (restoreFocusOnClose) {\n\t\t\tconst referenceElement = refs.domReference.current;\n\t\t\tif (referenceElement instanceof HTMLElement) {\n\t\t\t\treferenceElement.focus();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst openWithIndex = (nextIndex: number) => {\n\t\tif (disabled || !canOpen) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (options.length > 0) {\n\t\t\tsetActiveIndex(resolveActiveIndex(nextIndex));\n\t\t}\n\n\t\tsetResolvedOpen(true);\n\t};\n\n\tconst openList = () => {\n\t\topenWithIndex(selectedIndex >= 0 ? selectedIndex : findFirstEnabledIndex(options, getOptionDisabled));\n\t};\n\n\tconst toggleOpen = () => {\n\t\tif (resolvedOpen) {\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\n\t\topenList();\n\t};\n\n\tconst selectOption = (option: T) => {\n\t\tif (getOptionDisabled?.(option)) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSelect?.(option);\n\n\t\tif (closeOnSelect) {\n\t\t\tclose();\n\t\t}\n\t};\n\n\tconst selectActiveOption = () => {\n\t\tif (activeIndex < 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst activeOption = options[activeIndex];\n\t\tif (activeOption !== undefined) {\n\t\t\tselectOption(activeOption);\n\t\t}\n\t};\n\n\tconst moveActiveToFirst = () => {\n\t\tsetActiveIndex(findFirstEnabledIndex(options, getOptionDisabled));\n\t};\n\n\tconst moveActiveToLast = () => {\n\t\tsetActiveIndex(findLastEnabledIndex(options, getOptionDisabled));\n\t};\n\n\tconst handleReferenceKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n\t\tif (disabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowDown\") {\n\t\t\tevent.preventDefault();\n\n\t\t\tif (!resolvedOpen) {\n\t\t\t\topenWithIndex(selectedIndex >= 0 ? selectedIndex : findFirstEnabledIndex(options, getOptionDisabled));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), 1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowUp\") {\n\t\t\tevent.preventDefault();\n\n\t\t\tif (!resolvedOpen) {\n\t\t\t\topenWithIndex(selectedIndex >= 0 ? selectedIndex : findLastEnabledIndex(options, getOptionDisabled));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), -1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Home\" && resolvedOpen) {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToFirst();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"End\" && resolvedOpen) {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToLast();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Escape\" && resolvedOpen) {\n\t\t\tevent.preventDefault();\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Tab\" && resolvedOpen) {\n\t\t\tclose();\n\t\t}\n\t};\n\n\tconst handleFloatingKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n\t\tif (event.key === \"ArrowDown\") {\n\t\t\tevent.preventDefault();\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), 1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowUp\") {\n\t\t\tevent.preventDefault();\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), -1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Home\") {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToFirst();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"End\") {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToLast();\n\t\t\treturn;\n\t\t}\n\n\t\tif (handleKeyboardActivation(event, selectActiveOption)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Escape\") {\n\t\t\tevent.preventDefault();\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Tab\") {\n\t\t\tclose();\n\t\t}\n\t};\n\n\tconst getOptionId = (listId: string, index: number) => `${listId}-option-${index}`;\n\tconst getActiveOptionId = (listId: string) => (activeIndex >= 0 ? getOptionId(listId, activeIndex) : undefined);\n\n\treturn {\n\t\topen: resolvedOpen,\n\t\tsetOpen: setResolvedOpen,\n\t\tactiveIndex,\n\t\trefs,\n\t\tcontext,\n\t\tfloatingStyles,\n\t\tgetFloatingProps,\n\t\tsetReference: refs.setReference,\n\t\tsetFloating: refs.setFloating,\n\t\tsetOptionRef: (index: number, node: HTMLElement | null) => {\n\t\t\toptionRefs.current[index] = node;\n\t\t},\n\t\tclose,\n\t\topenList,\n\t\topenWithIndex,\n\t\ttoggleOpen,\n\t\tselectOption,\n\t\tselectActiveOption,\n\t\thandleReferenceKeyDown,\n\t\thandleFloatingKeyDown,\n\t\tgetOptionId,\n\t\tgetActiveOptionId\n\t};\n}\n","import { type ComponentType, useMemo } from \"react\";\n\nimport { createLazyComponent } from \"../utils/createLazyComponent\";\n\n/**\n * Хук для создания ленивой компоненты, которая будет загружена только при первом рендере.\n *\n * @example\n *\n * ```tsx\n * const MyComponent = () => {\n * const TextEditor = useLazyComponent(\n * () => import(\"@components/content/article/editor/TextEditor\"),\n * \"TextEditor\"\n * );\n *\n * return (\n * <Suspense fallback={<div>Загрузка...</div>}>\n * <TextEditor />\n * </Suspense>\n * );\n * };\n * ```\n *\n * @param importFn - функция, которая возвращает промис, который резолвится в объект с компонентами\n * @param componentName - имя компонента, который нужно загрузить\n * @returns\n */\nconst useLazyComponent = <TProps extends object>(\n\timportFn: () => Promise<Record<string, ComponentType<TProps>> | null>,\n\tcomponentName: string,\n\tcacheKey?: string\n) => {\n\treturn useMemo(() => createLazyComponent(importFn, componentName, cacheKey), [cacheKey, importFn, componentName]);\n};\n\nexport { useLazyComponent };\n","import { useEffect, useRef } from \"react\";\n\nexport function useReferenceTrace<T>(name: string, value: T) {\n\tconst previousRef = useRef(value);\n\n\tuseEffect(() => {\n\t\tif (!__DEV__) return;\n\n\t\tconst referenceChanged = !Object.is(previousRef.current, value);\n\n\t\tconsole.debug(`[render trace] ${name}`, { referenceChanged, previous: previousRef.current, current: value });\n\n\t\tpreviousRef.current = value;\n\t});\n}\n","import { useLocation } from \"@tanstack/react-router\";\n\nimport { SearchParams } from \"./types\";\n\nexport function useSearchLink<K extends PropertyKey>() {\n\tconst location = useLocation();\n\n\tconst buildLink = (params: SearchParams<K>) => {\n\t\tconst next = new URLSearchParams(location.searchStr);\n\n\t\tparams = params || [];\n\n\t\tfor (const param in params) {\n\t\t\tconst value = params[param];\n\n\t\t\tif (value) {\n\t\t\t\tnext.set(param, String(value));\n\t\t\t} else {\n\t\t\t\tnext.delete(param);\n\t\t\t}\n\t\t}\n\n\t\tconst searchString = next.toString();\n\t\treturn `${location.pathname}${searchString ? `?${searchString}` : \"\"}${location.hash}`;\n\t};\n\n\treturn { buildLink };\n}\n","import { useMemo } from \"react\";\n\nimport { AnyRouter, useNavigate, useSearch } from \"@tanstack/react-router\";\n\nimport { SearchParams, SearchParamsUpdater } from \"./types\";\n\nexport function useSearchParams<K extends PropertyKey>(allowedKeys?: (keyof K)[]) {\n\tconst params = useSearch<AnyRouter, string, false, true, SearchParams<K>>({ strict: false });\n\tconst navigate = useNavigate();\n\n\t// Фильтруем параметры, оставляя только разрешенные ключи\n\tconst filteredParams = useMemo(() => {\n\t\tif (!allowedKeys) return params;\n\n\t\treturn Object.fromEntries(Object.entries(params).filter(([key]) => allowedKeys.includes(key as keyof K))) as SearchParams<K>;\n\t}, [params, allowedKeys]);\n\n\tconst setParams = (next: SearchParamsUpdater<K>) =>\n\t\tnavigate({\n\t\t\tto: \".\",\n\t\t\tsearch: (prev: SearchParams<K>) => ({\n\t\t\t\t...prev,\n\t\t\t\t...next\n\t\t\t}),\n\t\t\treplace: true\n\t\t});\n\n\treturn { params, setParams, filteredParams };\n}\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport type ThrottledCallback<T> = {\n\t/**\n\t * Основной вход: вызывай на каждом частом событии (например, onNodesChange).\n\t * Колбэк `cb` будет вызываться не чаще, чем 1 раз в `intervalMs`.\n\t *\n\t * Стратегия: last-write-wins — если за интервал пришло много значений,\n\t * по окончании интервала будет применено последнее.\n\t */\n\tcall: (arg: T) => void;\n\n\t/**\n\t * Немедленно применить последнее накопленное значение (если оно есть),\n\t * отменив запланированный вызов (если он был).\n\t * Полезно на:\n\t * - pointerup/mouseup (конец перетаскивания)\n\t * - blur/visibilitychange/beforeunload\n\t * - unmount (если хочешь гарантированно сохранить финальное состояние)\n\t */\n\tflush: () => void;\n\n\t/**\n\t * Отменить запланированный вызов и забыть накопленное значение.\n\t * Полезно, если ты переключаешь редактор/граф и не хочешь “долетов” старых данных.\n\t */\n\tcancel: () => void;\n};\n\n/**\n * useThrottledCallback (React 19 safe)\n *\n * Проблема:\n * - В xyflow onNodesChange/onEdgesChange может сыпаться очень часто.\n * - Если каждый раз писать “сериализуемый снапшот” в zustand → лишние ререндеры/CPU/GC.\n *\n * Решение:\n * - throttle: вызывать cb не чаще, чем 1 раз в intervalMs.\n * - last-write-wins: если за интервал пришло 50 обновлений — применим последнее.\n *\n * React 19 нюанс:\n * - нельзя мутировать ref.current во время render (линтер/правило).\n * - поэтому cbRef обновляем в useEffect.\n */\nexport function useThrottledCallback<T>(\n\tcb: (arg: T) => void,\n\tintervalMs: number,\n\toptions?: { flushOnUnmount?: boolean }\n): ThrottledCallback<T> {\n\t/**\n\t * Актуальная версия cb (обновляется только после commit’а).\n\t * Это решает \"stale closure\": call/flush/cancel стабильны,\n\t * но всегда дергают самый свежий cb.\n\t */\n\tconst cbRef = useRef<(arg: T) => void>(cb);\n\n\tuseEffect(() => {\n\t\tcbRef.current = cb;\n\t}, [cb]);\n\n\t/**\n\t * Таймер отложенного выполнения.\n\t * Он нужен, когда call() пришёл раньше, чем завершился текущий throttle-интервал:\n\t * мы планируем “выполнить в конце окна”.\n\t */\n\tconst tRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t/**\n\t * Накопленное “последнее значение”.\n\t * Всегда хранится последний arg, пришедший через call().\n\t */\n\tconst lastRef = useRef<T | null>(null);\n\n\t/**\n\t * Время (timestamp) последнего РЕАЛЬНОГО вызова cb.\n\t * Нужно, чтобы вычислять: можно ли выполнить сейчас или нужно подождать.\n\t */\n\tconst lastInvokeTsRef = useRef<number>(0);\n\n\t/**\n\t * cancel():\n\t * - отменяем отложенный таймер,\n\t * - забываем накопленное значение.\n\t * Важно: это не вызывает cb.\n\t */\n\tconst cancel = useCallback(() => {\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\t\tlastRef.current = null;\n\t}, []);\n\n\t/**\n\t * flush():\n\t * - если есть накопленное значение, вызываем cb немедленно,\n\t * - чистим таймер,\n\t * - фиксируем lastInvokeTsRef (чтобы окно throttle считалось от flush тоже).\n\t */\n\tconst flush = useCallback(() => {\n\t\tconst v = lastRef.current;\n\t\tif (v === null) return;\n\n\t\t// Сбрасываем накопленное ДО вызова cb на случай,\n\t\t// если cb внутри синхронно вызовет call() снова.\n\t\tlastRef.current = null;\n\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\n\t\tlastInvokeTsRef.current = Date.now();\n\t\tcbRef.current(v);\n\t}, []);\n\n\t/**\n\t * call(arg):\n\t * 1) сохраняем arg как последнее значение (last-write-wins)\n\t * 2) если с момента последнего вызова прошло достаточно времени → вызываем сразу\n\t * 3) иначе — планируем flush ровно в момент, когда throttle-окно закончится\n\t *\n\t * При этом мы гарантируем:\n\t * - не более одного cb вызова за intervalMs\n\t * - и что “последнее значение за интервал” будет применено в конце.\n\t */\n\tconst call = useCallback(\n\t\t(arg: T) => {\n\t\t\tlastRef.current = arg;\n\n\t\t\tconst now = Date.now();\n\t\t\tconst elapsed = now - lastInvokeTsRef.current;\n\n\t\t\t// Если окно уже прошло — можно выполнить немедленно.\n\t\t\tif (elapsed >= intervalMs) {\n\t\t\t\t// Немедленный вызов = “leading edge”.\n\t\t\t\t// Берём последнее значение, очищаем lastRef и вызываем cb.\n\t\t\t\tconst v = lastRef.current;\n\t\t\t\tlastRef.current = null;\n\n\t\t\t\t// На всякий случай прибираем таймер, если он был.\n\t\t\t\tif (tRef.current) {\n\t\t\t\t\tclearTimeout(tRef.current);\n\t\t\t\t\ttRef.current = null;\n\t\t\t\t}\n\n\t\t\t\tlastInvokeTsRef.current = now;\n\t\t\t\tif (v !== null) cbRef.current(v);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Иначе — “trailing edge”: планируем выполнение в конце окна.\n\t\t\t// Важно: если таймер уже запланирован — НЕ пересоздаём,\n\t\t\t// потому что trailing-вызов должен случиться в конкретный момент (конец окна),\n\t\t\t// а значение мы и так обновляем через lastRef.current.\n\t\t\tif (!tRef.current) {\n\t\t\t\tconst wait = intervalMs - elapsed;\n\n\t\t\t\ttRef.current = setTimeout(() => {\n\t\t\t\t\ttRef.current = null;\n\t\t\t\t\t// В конце окна применяем последнее накопленное значение\n\t\t\t\t\tflush();\n\t\t\t\t}, wait);\n\t\t\t}\n\t\t},\n\t\t[intervalMs, flush]\n\t);\n\n\t/**\n\t * Cleanup на unmount:\n\t * - по умолчанию cancel (не делаем побочных эффектов при размонтировании)\n\t * - при flushOnUnmount=true — пытаемся сохранить последнее значение.\n\t *\n\t * Для твоего кейса (сериализация финального snapshot) чаще полезно flushOnUnmount=true.\n\t */\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (options?.flushOnUnmount) flush();\n\t\t\telse cancel();\n\t\t};\n\t}, [cancel, flush, options?.flushOnUnmount]);\n\n\treturn { call, flush, cancel };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/hooks/useBlockBrowserZoom.ts","../../src/hooks/useDebounce.ts","../../src/hooks/useDebounceCallback.ts","../../src/hooks/useDndSortableSensors.ts","../../src/hooks/useFloatingListbox.ts","../../src/hooks/useLazyComponent.ts","../../src/hooks/useReferenceTrace.ts","../../src/hooks/useSearchLink.ts","../../src/hooks/useSearchParams.ts","../../src/hooks/useThrottledCallback.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\n/**\n * Глобально отключает браузерный zoom (ctrl+wheel).\n *\n * @param enabled если true — блокируем нативный зум страницы\n */\nexport function useBlockBrowserZoom(enabled: boolean) {\n\tuseEffect(() => {\n\t\tif (!enabled) return;\n\n\t\tconst handler = (e: WheelEvent) => {\n\t\t\tif (e.ctrlKey) {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"wheel\", handler, { passive: false });\n\t\treturn () => window.removeEventListener(\"wheel\", handler);\n\t}, [enabled]);\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useDebounce<T>(value: T, delay: number): T {\n\tconst [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n\tuseEffect(() => {\n\t\tconst timeoutId = setTimeout(() => {\n\t\t\tsetDebouncedValue(value);\n\t\t}, delay);\n\n\t\treturn () => clearTimeout(timeoutId);\n\t}, [value, delay]);\n\n\treturn debouncedValue;\n}\n","import { useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Дебаунсит вызов колбэка: частые вызовы `call(value)` не вызывают `cb` сразу,\n * а откладывают его на `delayMs` после последнего вызова.\n *\n * Важно:\n * - Всегда сохраняется *последнее* значение (last-write-wins).\n * - `flush()` немедленно вызывает cb с последним сохранённым значением (если оно есть).\n * - `cancel()` отменяет таймер и забывает последнее значение.\n * - На unmount автоматически вызывается `cancel()` чтобы не было setState после размонтирования.\n *\n * Типовой кейс:\n * - onNodesChange -> call(snapshot)\n * - pointerup / blur / unmount -> flush() (или cancel(), если не надо сохранять)\n */\nexport function useDebouncedCallback<T>(cb: (arg: T) => void, delayMs: number, options?: { flushOnUnmount?: boolean }) {\n\t/**\n\t * Храним актуальную версию cb в ref, чтобы:\n\t * - не пересоздавать дебаунсер при каждом рендере,\n\t * - не ловить устаревший замыканием cb (stale closure),\n\t * - при этом всегда вызывать самую свежую функцию.\n\t */\n\tconst cbRef = useRef<(arg: T) => void>(cb);\n\n\t/**\n\t * Обновляем актуальную версию cb после commit'а\n\t */\n\tuseEffect(() => {\n\t\tcbRef.current = cb;\n\t}, [cb]);\n\n\t/**\n\t * id таймера. null означает что таймер не активен.\n\t */\n\tconst tRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t/**\n\t * Последнее значение, которое мы хотим \"закоммитить\" в cb.\n\t * Используем стратегию last-write-wins.\n\t */\n\tconst lastRef = useRef<T | null>(null);\n\n\t/**\n\t * cancel():\n\t * - останавливаем таймер, если он был,\n\t * - очищаем последнее значение (ничего не будет вызвано).\n\t */\n\tconst cancel = useCallback(() => {\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\t\tlastRef.current = null;\n\t}, []);\n\n\t/**\n\t * flush():\n\t * - если есть отложенное значение, вызываем cb немедленно,\n\t * - сбрасываем таймер и lastRef.\n\t *\n\t * Используется, когда нужно гарантированно сохранить \"последнее\" прямо сейчас:\n\t * - unmount\n\t * - visibilitychange/blur\n\t * - pointerup/mouseup (конец взаимодействия)\n\t */\n\tconst flush = useCallback(() => {\n\t\tconst v = lastRef.current;\n\t\tif (v === null) return;\n\n\t\t// Сбрасываем before-call, чтобы если cb вызовет call() рекурсивно,\n\t\t// мы не держали старое значение.\n\t\tlastRef.current = null;\n\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\n\t\tcbRef.current(v);\n\t}, []);\n\n\t/**\n\t * call(arg):\n\t * - сохраняем arg как \"последнее значение\"\n\t * - перезапускаем таймер\n\t * - по истечении delayMs вызываем flush()\n\t */\n\tconst call = useCallback(\n\t\t(arg: T) => {\n\t\t\tlastRef.current = arg;\n\n\t\t\t// Перезапуск таймера -> классический debounce\n\t\t\tif (tRef.current) clearTimeout(tRef.current);\n\n\t\t\ttRef.current = setTimeout(() => {\n\t\t\t\ttRef.current = null;\n\t\t\t\tflush();\n\t\t\t}, delayMs);\n\t\t},\n\t\t[delayMs, flush]\n\t);\n\n\t/**\n\t * Авто-cancel на unmount:\n\t * - убирает pending timeout\n\t * - предотвращает вызовы cb после размонтирования компонента\n\t */\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (options?.flushOnUnmount) {\n\t\t\t\tflush();\n\t\t\t} else {\n\t\t\t\tcancel();\n\t\t\t}\n\t\t};\n\t}, [cancel, flush, options?.flushOnUnmount]);\n\n\treturn { call, flush, cancel };\n}\n","import { KeyboardSensor, PointerSensor, useSensor, useSensors } from \"@dnd-kit/core\";\nimport { sortableKeyboardCoordinates } from \"@dnd-kit/sortable\";\n\nexport interface UseDndSortableSensorsOptions {\n\tactivationDistance?: number;\n}\n\n/**\n * Стандартная конфигурация DnD-сенсоров для сортируемых списков.\n *\n * Зачем: исключить дублирование одинаковой настройки PointerSensor + KeyboardSensor\n * в каждом хуке персонализации (колонки, сортировки, группировки).\n *\n * @param distance — минимальное смещение указателя (px) для начала перетаскивания (по умолчанию 6).\n */\nexport function useDndSortableSensors(distanceOrOptions: number | UseDndSortableSensorsOptions = 6) {\n\tconst distance = typeof distanceOrOptions === \"number\" ? distanceOrOptions : (distanceOrOptions.activationDistance ?? 6);\n\n\treturn useSensors(\n\t\tuseSensor(PointerSensor, { activationConstraint: { distance } }),\n\t\tuseSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })\n\t);\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nimport {\n\tautoUpdate,\n\tflip,\n\tsize as floatingSize,\n\toffset,\n\tPlacement,\n\tshift,\n\tuseDismiss,\n\tuseFloating,\n\tuseInteractions,\n\tuseRole\n} from \"@floating-ui/react\";\n\nimport { findFirstEnabledIndex, findLastEnabledIndex, findNextEnabledIndex, handleKeyboardActivation } from \"../utils\";\n\n/** Геометрия, которую Floating UI вычислил для доступной стороны listbox. */\nexport type FloatingListboxSizeContext = {\n\tavailableWidth: number;\n\tavailableHeight: number;\n\tviewportWidth: number;\n\tviewportHeight: number;\n\treferenceWidth: number;\n\treferenceHeight: number;\n};\n\n/**\n * Размеры floating-элемента и CSS custom properties, которые специализированный\n * picker может вычислить из доступного viewport без собственного middleware.\n */\nexport type FloatingListboxSizeStyle = {\n\twidth?: string;\n\tminWidth?: string;\n\tmaxWidth?: string;\n\tmaxHeight?: string;\n} & {\n\t[customProperty: `--${string}`]: string | undefined;\n};\n\nexport type FloatingListboxSizeResolver = (context: FloatingListboxSizeContext) => FloatingListboxSizeStyle;\n\ntype FloatingListboxStandardSizeProperty = \"width\" | \"minWidth\" | \"maxWidth\" | \"maxHeight\";\n\ntype FloatingListboxAppliedSizeState = {\n\tcustomProperties: Set<`--${string}`>;\n\tstandardProperties: Map<FloatingListboxStandardSizeProperty, string>;\n};\n\n/** Хранит свойства и их исходные значения до императивного применения resolver. */\nconst floatingListboxAppliedSizeState = new WeakMap<HTMLElement, FloatingListboxAppliedSizeState>();\n\ninterface UseFloatingListboxParams<T> {\n\toptions: readonly T[];\n\tselectedIndex: number;\n\topen?: boolean;\n\tonOpenChange?: (open: boolean) => void;\n\tgetOptionDisabled?: (option: T) => boolean;\n\tonSelect?: (option: T) => void;\n\tdisabled?: boolean;\n\tplacement?: Placement;\n\tcloseOnSelect?: boolean;\n\tfocusFloatingOnOpen?: boolean;\n\tallowOpenWithoutOptions?: boolean;\n\trestoreFocusOnClose?: boolean;\n\tresolveFloatingSize?: FloatingListboxSizeResolver;\n}\n\nexport function useFloatingListbox<T>({\n\toptions,\n\tselectedIndex,\n\topen,\n\tonOpenChange,\n\tgetOptionDisabled,\n\tonSelect,\n\tdisabled,\n\tplacement = \"bottom-start\",\n\tcloseOnSelect = true,\n\tfocusFloatingOnOpen = true,\n\tallowOpenWithoutOptions = false,\n\trestoreFocusOnClose = true,\n\tresolveFloatingSize\n}: UseFloatingListboxParams<T>) {\n\tconst optionRefs = useRef<Array<HTMLElement | null>>([]);\n\tconst [internalOpen, setInternalOpen] = useState(false);\n\tconst resolvedOpen = open ?? internalOpen;\n\tconst setResolvedOpen = onOpenChange ?? setInternalOpen;\n\tconst [currentActiveIndex, setActiveIndex] = useState(\n\t\tselectedIndex >= 0 ? selectedIndex : findFirstEnabledIndex(options, getOptionDisabled)\n\t);\n\n\t/**\n\t * Поддерживает активный индекс в валидном состоянии при изменении набора опций\n\t * или выбранного элемента, чтобы `aria-activedescendant` всегда ссылался на\n\t * существующую и доступную опцию.\n\t */\n\tconst resolveActiveIndex = (activeIndex: number) => {\n\t\tif (options.length === 0) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (activeIndex >= 0 && activeIndex < options.length && !getOptionDisabled?.(options[activeIndex])) {\n\t\t\treturn activeIndex;\n\t\t}\n\n\t\tif (selectedIndex >= 0 && selectedIndex < options.length && !getOptionDisabled?.(options[selectedIndex])) {\n\t\t\treturn selectedIndex;\n\t\t}\n\n\t\treturn findFirstEnabledIndex(options, getOptionDisabled);\n\t};\n\tconst activeIndex = resolveActiveIndex(currentActiveIndex);\n\n\tconst { refs, floatingStyles, context } = useFloating({\n\t\topen: resolvedOpen,\n\t\tonOpenChange: setResolvedOpen,\n\t\tplacement,\n\t\ttransform: false,\n\t\tstrategy: \"fixed\",\n\t\tmiddleware: [\n\t\t\toffset(4),\n\t\t\tflip({ padding: 8 }),\n\t\t\tshift({ padding: 8 }),\n\t\t\tfloatingSize({\n\t\t\t\tpadding: 8,\n\t\t\t\tapply({ availableWidth, availableHeight, rects, elements }) {\n\t\t\t\t\tconst viewport = elements.floating.ownerDocument.documentElement;\n\t\t\t\t\tconst floatingStyle = elements.floating.style;\n\t\t\t\t\tconst resolvedSize = resolveFloatingSize?.({\n\t\t\t\t\t\tavailableWidth,\n\t\t\t\t\t\tavailableHeight,\n\t\t\t\t\t\tviewportWidth: viewport.clientWidth,\n\t\t\t\t\t\tviewportHeight: viewport.clientHeight,\n\t\t\t\t\t\treferenceWidth: rects.reference.width,\n\t\t\t\t\t\treferenceHeight: rects.reference.height\n\t\t\t\t\t});\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Сначала сбрасываем размеры предыдущего resolver: Floating UI сохраняет\n\t\t\t\t\t * один DOM-узел между режимами, поэтому старые width и CSS-переменные\n\t\t\t\t\t * иначе продолжат влиять на следующий вариант отображения.\n\t\t\t\t\t */\n\t\t\t\t\tconst previousAppliedState = floatingListboxAppliedSizeState.get(elements.floating);\n\t\t\t\t\tfor (const property of previousAppliedState?.customProperties ?? []) {\n\t\t\t\t\t\tfloatingStyle.removeProperty(property);\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [property, previousValue] of previousAppliedState?.standardProperties ?? []) {\n\t\t\t\t\t\tfloatingStyle[property] = previousValue;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloatingStyle.minWidth = `${rects.reference.width}px`;\n\t\t\t\t\tfloatingStyle.maxHeight = `${Math.max(availableHeight, 120)}px`;\n\n\t\t\t\t\tconst nextCustomProperties = new Set<`--${string}`>();\n\t\t\t\t\tconst nextStandardProperties = new Map<FloatingListboxStandardSizeProperty, string>();\n\t\t\t\t\tfor (const [property, value] of Object.entries(resolvedSize ?? {})) {\n\t\t\t\t\t\tif (property.startsWith(\"--\")) {\n\t\t\t\t\t\t\tconst customProperty = property as `--${string}`;\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tfloatingStyle.setProperty(customProperty, value);\n\t\t\t\t\t\t\t\tnextCustomProperties.add(customProperty);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\tconst standardProperty = property as FloatingListboxStandardSizeProperty;\n\t\t\t\t\t\t\tnextStandardProperties.set(standardProperty, floatingStyle[standardProperty]);\n\t\t\t\t\t\t\tfloatingStyle[standardProperty] = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfloatingListboxAppliedSizeState.set(elements.floating, {\n\t\t\t\t\t\tcustomProperties: nextCustomProperties,\n\t\t\t\t\t\tstandardProperties: nextStandardProperties\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t})\n\t\t],\n\t\twhileElementsMounted: autoUpdate\n\t});\n\n\tconst dismiss = useDismiss(context);\n\tconst role = useRole(context, { role: \"listbox\" });\n\tconst { getFloatingProps } = useInteractions([dismiss, role]);\n\n\tconst canOpen = allowOpenWithoutOptions || options.length > 0;\n\n\tuseEffect(() => {\n\t\tif (!resolvedOpen || activeIndex < 0) {\n\t\t\treturn;\n\t\t}\n\n\t\toptionRefs.current[activeIndex]?.scrollIntoView({ block: \"nearest\" });\n\t}, [activeIndex, resolvedOpen]);\n\n\tuseEffect(() => {\n\t\tif (!resolvedOpen || !focusFloatingOnOpen) {\n\t\t\treturn;\n\t\t}\n\n\t\trequestAnimationFrame(() => {\n\t\t\trefs.floating.current?.focus();\n\t\t});\n\t}, [focusFloatingOnOpen, resolvedOpen, refs]);\n\n\tconst close = () => {\n\t\tsetResolvedOpen(false);\n\n\t\tif (restoreFocusOnClose) {\n\t\t\tconst referenceElement = refs.domReference.current;\n\t\t\tif (referenceElement instanceof HTMLElement) {\n\t\t\t\treferenceElement.focus();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst openWithIndex = (nextIndex: number) => {\n\t\tif (disabled || !canOpen) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (options.length > 0) {\n\t\t\tsetActiveIndex(resolveActiveIndex(nextIndex));\n\t\t}\n\n\t\tsetResolvedOpen(true);\n\t};\n\n\tconst openList = () => {\n\t\topenWithIndex(selectedIndex >= 0 ? selectedIndex : findFirstEnabledIndex(options, getOptionDisabled));\n\t};\n\n\tconst toggleOpen = () => {\n\t\tif (resolvedOpen) {\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\n\t\topenList();\n\t};\n\n\tconst selectOption = (option: T) => {\n\t\tif (getOptionDisabled?.(option)) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSelect?.(option);\n\n\t\tif (closeOnSelect) {\n\t\t\tclose();\n\t\t}\n\t};\n\n\tconst selectActiveOption = () => {\n\t\tif (activeIndex < 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst activeOption = options[activeIndex];\n\t\tif (activeOption !== undefined) {\n\t\t\tselectOption(activeOption);\n\t\t}\n\t};\n\n\tconst moveActiveToFirst = () => {\n\t\tsetActiveIndex(findFirstEnabledIndex(options, getOptionDisabled));\n\t};\n\n\tconst moveActiveToLast = () => {\n\t\tsetActiveIndex(findLastEnabledIndex(options, getOptionDisabled));\n\t};\n\n\tconst handleReferenceKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n\t\tif (disabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowDown\") {\n\t\t\tevent.preventDefault();\n\n\t\t\tif (!resolvedOpen) {\n\t\t\t\topenWithIndex(selectedIndex >= 0 ? selectedIndex : findFirstEnabledIndex(options, getOptionDisabled));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), 1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowUp\") {\n\t\t\tevent.preventDefault();\n\n\t\t\tif (!resolvedOpen) {\n\t\t\t\topenWithIndex(selectedIndex >= 0 ? selectedIndex : findLastEnabledIndex(options, getOptionDisabled));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), -1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Home\" && resolvedOpen) {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToFirst();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"End\" && resolvedOpen) {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToLast();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Escape\" && resolvedOpen) {\n\t\t\tevent.preventDefault();\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Tab\" && resolvedOpen) {\n\t\t\tclose();\n\t\t}\n\t};\n\n\tconst handleFloatingKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n\t\tif (event.target !== event.currentTarget) {\n\t\t\tif (event.key === \"Escape\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tclose();\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowDown\") {\n\t\t\tevent.preventDefault();\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), 1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"ArrowUp\") {\n\t\t\tevent.preventDefault();\n\t\t\tsetActiveIndex((currentIndex) => findNextEnabledIndex(options, resolveActiveIndex(currentIndex), -1, getOptionDisabled));\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Home\") {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToFirst();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"End\") {\n\t\t\tevent.preventDefault();\n\t\t\tmoveActiveToLast();\n\t\t\treturn;\n\t\t}\n\n\t\tif (handleKeyboardActivation(event, selectActiveOption)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Escape\") {\n\t\t\tevent.preventDefault();\n\t\t\tclose();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === \"Tab\") {\n\t\t\tclose();\n\t\t}\n\t};\n\n\tconst getOptionId = (listId: string, index: number) => `${listId}-option-${index}`;\n\tconst getActiveOptionId = (listId: string) => (activeIndex >= 0 ? getOptionId(listId, activeIndex) : undefined);\n\n\treturn {\n\t\topen: resolvedOpen,\n\t\tsetOpen: setResolvedOpen,\n\t\tactiveIndex,\n\t\trefs,\n\t\tcontext,\n\t\tfloatingStyles,\n\t\tgetFloatingProps,\n\t\tsetReference: refs.setReference,\n\t\tsetFloating: refs.setFloating,\n\t\tsetOptionRef: (index: number, node: HTMLElement | null) => {\n\t\t\toptionRefs.current[index] = node;\n\t\t},\n\t\tclose,\n\t\topenList,\n\t\topenWithIndex,\n\t\ttoggleOpen,\n\t\tselectOption,\n\t\tselectActiveOption,\n\t\thandleReferenceKeyDown,\n\t\thandleFloatingKeyDown,\n\t\tgetOptionId,\n\t\tgetActiveOptionId\n\t};\n}\n","import { type ComponentType, useMemo } from \"react\";\n\nimport { createLazyComponent } from \"../utils/createLazyComponent\";\n\n/**\n * Хук для создания ленивой компоненты, которая будет загружена только при первом рендере.\n *\n * @example\n *\n * ```tsx\n * const MyComponent = () => {\n * const TextEditor = useLazyComponent(\n * () => import(\"@components/content/article/editor/TextEditor\"),\n * \"TextEditor\"\n * );\n *\n * return (\n * <Suspense fallback={<div>Загрузка...</div>}>\n * <TextEditor />\n * </Suspense>\n * );\n * };\n * ```\n *\n * @param importFn - функция, которая возвращает промис, который резолвится в объект с компонентами\n * @param componentName - имя компонента, который нужно загрузить\n * @returns\n */\nconst useLazyComponent = <TProps extends object>(\n\timportFn: () => Promise<Record<string, ComponentType<TProps>> | null>,\n\tcomponentName: string,\n\tcacheKey?: string\n) => {\n\treturn useMemo(() => createLazyComponent(importFn, componentName, cacheKey), [cacheKey, importFn, componentName]);\n};\n\nexport { useLazyComponent };\n","import { useEffect, useRef } from \"react\";\n\nexport function useReferenceTrace<T>(name: string, value: T) {\n\tconst previousRef = useRef(value);\n\n\tuseEffect(() => {\n\t\tif (!__DEV__) return;\n\n\t\tconst referenceChanged = !Object.is(previousRef.current, value);\n\n\t\tconsole.debug(`[render trace] ${name}`, { referenceChanged, previous: previousRef.current, current: value });\n\n\t\tpreviousRef.current = value;\n\t});\n}\n","import { useLocation } from \"@tanstack/react-router\";\n\nimport { SearchParams } from \"./types\";\n\nexport function useSearchLink<K extends PropertyKey>() {\n\tconst location = useLocation();\n\n\tconst buildLink = (params: SearchParams<K>) => {\n\t\tconst next = new URLSearchParams(location.searchStr);\n\n\t\tparams = params || [];\n\n\t\tfor (const param in params) {\n\t\t\tconst value = params[param];\n\n\t\t\tif (value) {\n\t\t\t\tnext.set(param, String(value));\n\t\t\t} else {\n\t\t\t\tnext.delete(param);\n\t\t\t}\n\t\t}\n\n\t\tconst searchString = next.toString();\n\t\treturn `${location.pathname}${searchString ? `?${searchString}` : \"\"}${location.hash}`;\n\t};\n\n\treturn { buildLink };\n}\n","import { useMemo } from \"react\";\n\nimport { AnyRouter, useNavigate, useSearch } from \"@tanstack/react-router\";\n\nimport { SearchParams, SearchParamsUpdater } from \"./types\";\n\nexport function useSearchParams<K extends PropertyKey>(allowedKeys?: (keyof K)[]) {\n\tconst params = useSearch<AnyRouter, string, false, true, SearchParams<K>>({ strict: false });\n\tconst navigate = useNavigate();\n\n\t// Фильтруем параметры, оставляя только разрешенные ключи\n\tconst filteredParams = useMemo(() => {\n\t\tif (!allowedKeys) return params;\n\n\t\treturn Object.fromEntries(Object.entries(params).filter(([key]) => allowedKeys.includes(key as keyof K))) as SearchParams<K>;\n\t}, [params, allowedKeys]);\n\n\tconst setParams = (next: SearchParamsUpdater<K>) =>\n\t\tnavigate({\n\t\t\tto: \".\",\n\t\t\tsearch: (prev: SearchParams<K>) => ({\n\t\t\t\t...prev,\n\t\t\t\t...next\n\t\t\t}),\n\t\t\treplace: true\n\t\t});\n\n\treturn { params, setParams, filteredParams };\n}\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport type ThrottledCallback<T> = {\n\t/**\n\t * Основной вход: вызывай на каждом частом событии (например, onNodesChange).\n\t * Колбэк `cb` будет вызываться не чаще, чем 1 раз в `intervalMs`.\n\t *\n\t * Стратегия: last-write-wins — если за интервал пришло много значений,\n\t * по окончании интервала будет применено последнее.\n\t */\n\tcall: (arg: T) => void;\n\n\t/**\n\t * Немедленно применить последнее накопленное значение (если оно есть),\n\t * отменив запланированный вызов (если он был).\n\t * Полезно на:\n\t * - pointerup/mouseup (конец перетаскивания)\n\t * - blur/visibilitychange/beforeunload\n\t * - unmount (если хочешь гарантированно сохранить финальное состояние)\n\t */\n\tflush: () => void;\n\n\t/**\n\t * Отменить запланированный вызов и забыть накопленное значение.\n\t * Полезно, если ты переключаешь редактор/граф и не хочешь “долетов” старых данных.\n\t */\n\tcancel: () => void;\n};\n\n/**\n * useThrottledCallback (React 19 safe)\n *\n * Проблема:\n * - В xyflow onNodesChange/onEdgesChange может сыпаться очень часто.\n * - Если каждый раз писать “сериализуемый снапшот” в zustand → лишние ререндеры/CPU/GC.\n *\n * Решение:\n * - throttle: вызывать cb не чаще, чем 1 раз в intervalMs.\n * - last-write-wins: если за интервал пришло 50 обновлений — применим последнее.\n *\n * React 19 нюанс:\n * - нельзя мутировать ref.current во время render (линтер/правило).\n * - поэтому cbRef обновляем в useEffect.\n */\nexport function useThrottledCallback<T>(\n\tcb: (arg: T) => void,\n\tintervalMs: number,\n\toptions?: { flushOnUnmount?: boolean }\n): ThrottledCallback<T> {\n\t/**\n\t * Актуальная версия cb (обновляется только после commit’а).\n\t * Это решает \"stale closure\": call/flush/cancel стабильны,\n\t * но всегда дергают самый свежий cb.\n\t */\n\tconst cbRef = useRef<(arg: T) => void>(cb);\n\n\tuseEffect(() => {\n\t\tcbRef.current = cb;\n\t}, [cb]);\n\n\t/**\n\t * Таймер отложенного выполнения.\n\t * Он нужен, когда call() пришёл раньше, чем завершился текущий throttle-интервал:\n\t * мы планируем “выполнить в конце окна”.\n\t */\n\tconst tRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t/**\n\t * Накопленное “последнее значение”.\n\t * Всегда хранится последний arg, пришедший через call().\n\t */\n\tconst lastRef = useRef<T | null>(null);\n\n\t/**\n\t * Время (timestamp) последнего РЕАЛЬНОГО вызова cb.\n\t * Нужно, чтобы вычислять: можно ли выполнить сейчас или нужно подождать.\n\t */\n\tconst lastInvokeTsRef = useRef<number>(0);\n\n\t/**\n\t * cancel():\n\t * - отменяем отложенный таймер,\n\t * - забываем накопленное значение.\n\t * Важно: это не вызывает cb.\n\t */\n\tconst cancel = useCallback(() => {\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\t\tlastRef.current = null;\n\t}, []);\n\n\t/**\n\t * flush():\n\t * - если есть накопленное значение, вызываем cb немедленно,\n\t * - чистим таймер,\n\t * - фиксируем lastInvokeTsRef (чтобы окно throttle считалось от flush тоже).\n\t */\n\tconst flush = useCallback(() => {\n\t\tconst v = lastRef.current;\n\t\tif (v === null) return;\n\n\t\t// Сбрасываем накопленное ДО вызова cb на случай,\n\t\t// если cb внутри синхронно вызовет call() снова.\n\t\tlastRef.current = null;\n\n\t\tif (tRef.current) {\n\t\t\tclearTimeout(tRef.current);\n\t\t\ttRef.current = null;\n\t\t}\n\n\t\tlastInvokeTsRef.current = Date.now();\n\t\tcbRef.current(v);\n\t}, []);\n\n\t/**\n\t * call(arg):\n\t * 1) сохраняем arg как последнее значение (last-write-wins)\n\t * 2) если с момента последнего вызова прошло достаточно времени → вызываем сразу\n\t * 3) иначе — планируем flush ровно в момент, когда throttle-окно закончится\n\t *\n\t * При этом мы гарантируем:\n\t * - не более одного cb вызова за intervalMs\n\t * - и что “последнее значение за интервал” будет применено в конце.\n\t */\n\tconst call = useCallback(\n\t\t(arg: T) => {\n\t\t\tlastRef.current = arg;\n\n\t\t\tconst now = Date.now();\n\t\t\tconst elapsed = now - lastInvokeTsRef.current;\n\n\t\t\t// Если окно уже прошло — можно выполнить немедленно.\n\t\t\tif (elapsed >= intervalMs) {\n\t\t\t\t// Немедленный вызов = “leading edge”.\n\t\t\t\t// Берём последнее значение, очищаем lastRef и вызываем cb.\n\t\t\t\tconst v = lastRef.current;\n\t\t\t\tlastRef.current = null;\n\n\t\t\t\t// На всякий случай прибираем таймер, если он был.\n\t\t\t\tif (tRef.current) {\n\t\t\t\t\tclearTimeout(tRef.current);\n\t\t\t\t\ttRef.current = null;\n\t\t\t\t}\n\n\t\t\t\tlastInvokeTsRef.current = now;\n\t\t\t\tif (v !== null) cbRef.current(v);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Иначе — “trailing edge”: планируем выполнение в конце окна.\n\t\t\t// Важно: если таймер уже запланирован — НЕ пересоздаём,\n\t\t\t// потому что trailing-вызов должен случиться в конкретный момент (конец окна),\n\t\t\t// а значение мы и так обновляем через lastRef.current.\n\t\t\tif (!tRef.current) {\n\t\t\t\tconst wait = intervalMs - elapsed;\n\n\t\t\t\ttRef.current = setTimeout(() => {\n\t\t\t\t\ttRef.current = null;\n\t\t\t\t\t// В конце окна применяем последнее накопленное значение\n\t\t\t\t\tflush();\n\t\t\t\t}, wait);\n\t\t\t}\n\t\t},\n\t\t[intervalMs, flush]\n\t);\n\n\t/**\n\t * Cleanup на unmount:\n\t * - по умолчанию cancel (не делаем побочных эффектов при размонтировании)\n\t * - при flushOnUnmount=true — пытаемся сохранить последнее значение.\n\t *\n\t * Для твоего кейса (сериализация финального snapshot) чаще полезно flushOnUnmount=true.\n\t */\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (options?.flushOnUnmount) flush();\n\t\t\telse cancel();\n\t\t};\n\t}, [cancel, flush, options?.flushOnUnmount]);\n\n\treturn { call, flush, cancel };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import { Placement } from "@floating-ui/react";
|
|
2
|
+
/** Геометрия, которую Floating UI вычислил для доступной стороны listbox. */
|
|
3
|
+
export type FloatingListboxSizeContext = {
|
|
4
|
+
availableWidth: number;
|
|
5
|
+
availableHeight: number;
|
|
6
|
+
viewportWidth: number;
|
|
7
|
+
viewportHeight: number;
|
|
8
|
+
referenceWidth: number;
|
|
9
|
+
referenceHeight: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Размеры floating-элемента и CSS custom properties, которые специализированный
|
|
13
|
+
* picker может вычислить из доступного viewport без собственного middleware.
|
|
14
|
+
*/
|
|
15
|
+
export type FloatingListboxSizeStyle = {
|
|
16
|
+
width?: string;
|
|
17
|
+
minWidth?: string;
|
|
18
|
+
maxWidth?: string;
|
|
19
|
+
maxHeight?: string;
|
|
20
|
+
} & {
|
|
21
|
+
[customProperty: `--${string}`]: string | undefined;
|
|
22
|
+
};
|
|
23
|
+
export type FloatingListboxSizeResolver = (context: FloatingListboxSizeContext) => FloatingListboxSizeStyle;
|
|
2
24
|
interface UseFloatingListboxParams<T> {
|
|
3
25
|
options: readonly T[];
|
|
4
26
|
selectedIndex: number;
|
|
@@ -12,8 +34,9 @@ interface UseFloatingListboxParams<T> {
|
|
|
12
34
|
focusFloatingOnOpen?: boolean;
|
|
13
35
|
allowOpenWithoutOptions?: boolean;
|
|
14
36
|
restoreFocusOnClose?: boolean;
|
|
37
|
+
resolveFloatingSize?: FloatingListboxSizeResolver;
|
|
15
38
|
}
|
|
16
|
-
export declare function useFloatingListbox<T>({ options, selectedIndex, open, onOpenChange, getOptionDisabled, onSelect, disabled, placement, closeOnSelect, focusFloatingOnOpen, allowOpenWithoutOptions, restoreFocusOnClose }: UseFloatingListboxParams<T>): {
|
|
39
|
+
export declare function useFloatingListbox<T>({ options, selectedIndex, open, onOpenChange, getOptionDisabled, onSelect, disabled, placement, closeOnSelect, focusFloatingOnOpen, allowOpenWithoutOptions, restoreFocusOnClose, resolveFloatingSize }: UseFloatingListboxParams<T>): {
|
|
17
40
|
open: boolean;
|
|
18
41
|
setOpen: (open: boolean) => void;
|
|
19
42
|
activeIndex: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFloatingListbox.d.ts","sourceRoot":"","sources":["../../src/hooks/useFloatingListbox.ts"],"names":[],"mappings":"AAEA,OAAO,EAKN,SAAS,EAMT,MAAM,oBAAoB,CAAC;AAI5B,UAAU,wBAAwB,CAAC,CAAC;IACnC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,mBAAmB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useFloatingListbox.d.ts","sourceRoot":"","sources":["../../src/hooks/useFloatingListbox.ts"],"names":[],"mappings":"AAEA,OAAO,EAKN,SAAS,EAMT,MAAM,oBAAoB,CAAC;AAI5B,6EAA6E;AAC7E,MAAM,MAAM,0BAA0B,GAAG;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG;IACH,CAAC,cAAc,EAAE,KAAK,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,EAAE,0BAA0B,KAAK,wBAAwB,CAAC;AAY5G,UAAU,wBAAwB,CAAC,CAAC;IACnC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,2BAA2B,CAAC;CAClD;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,EACrC,OAAO,EACP,aAAa,EACb,IAAI,EACJ,YAAY,EACZ,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,SAA0B,EAC1B,aAAoB,EACpB,mBAA0B,EAC1B,uBAA+B,EAC/B,mBAA0B,EAC1B,mBAAmB,EACnB,EAAE,wBAAwB,CAAC,CAAC,CAAC;;oBA1BP,OAAO,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA0Uf,MAAM,QAAQ,WAAW,GAAG,IAAI;;;+BA1KrB,MAAM;;2BAyBV,CAAC;;oCA+BQ,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC;mCAoDjC,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC;0BAiDzC,MAAM,SAAS,MAAM;gCACf,MAAM;EA0BzC"}
|