sse-hooks 1.2.0 → 2.0.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/index.mjs DELETED
@@ -1,2410 +0,0 @@
1
- import React, { useState, useRef, useEffect, useMemo, useCallback, useLayoutEffect } from 'react';
2
- import debounce from 'lodash.debounce';
3
-
4
- const extend = Object.assign;
5
- const isArray = Array.isArray;
6
- const isMap = (val) => val instanceof Map;
7
- const isFunction = (val) => typeof val === "function";
8
- const isSymbol = (val) => typeof val === "symbol";
9
- const isObject = (val) => val !== null && typeof val === "object";
10
- const isIntegerKey = (key) => typeof key === "string" && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key;
11
- const __DEV__ = process.env.NODE_ENV !== "production";
12
-
13
- let activeSub;
14
- let shouldTrack = true;
15
- function getActiveSub() {
16
- return activeSub;
17
- }
18
- function setActiveSub(sub) {
19
- activeSub = sub;
20
- }
21
- function startBatch() {
22
- }
23
- function endBatch() {
24
- }
25
- function batch(sub, isComputed = false) {
26
- }
27
-
28
- var EffectFlags = /* @__PURE__ */ ((EffectFlags2) => {
29
- EffectFlags2[EffectFlags2["ACTIVE"] = 1] = "ACTIVE";
30
- EffectFlags2[EffectFlags2["RUNNING"] = 2] = "RUNNING";
31
- EffectFlags2[EffectFlags2["TRACKING"] = 4] = "TRACKING";
32
- EffectFlags2[EffectFlags2["NOTIFIED"] = 8] = "NOTIFIED";
33
- EffectFlags2[EffectFlags2["DIRTY"] = 16] = "DIRTY";
34
- EffectFlags2[EffectFlags2["ALLOW_RECURSE"] = 32] = "ALLOW_RECURSE";
35
- EffectFlags2[EffectFlags2["PAUSED"] = 64] = "PAUSED";
36
- return EffectFlags2;
37
- })(EffectFlags || {});
38
- var TrackOpTypes = /* @__PURE__ */ ((TrackOpTypes2) => {
39
- TrackOpTypes2["GET"] = "get";
40
- TrackOpTypes2["HAS"] = "has";
41
- TrackOpTypes2["ITERATE"] = "iterate";
42
- return TrackOpTypes2;
43
- })(TrackOpTypes || {});
44
- var TriggerOpTypes = /* @__PURE__ */ ((TriggerOpTypes2) => {
45
- TriggerOpTypes2["SET"] = "set";
46
- TriggerOpTypes2["ADD"] = "add";
47
- TriggerOpTypes2["DELETE"] = "delete";
48
- TriggerOpTypes2["CLEAR"] = "clear";
49
- return TriggerOpTypes2;
50
- })(TriggerOpTypes || {});
51
- const ReactiveFlags = {
52
- IS_REF: "__v_isRef",
53
- IS_READONLY: "__v_isReadonly",
54
- SKIP: "__v_skip"
55
- };
56
-
57
- let globalVersion = 0;
58
- class Link {
59
- constructor(sub, dep) {
60
- this.sub = sub;
61
- this.dep = dep;
62
- this.version = dep.version;
63
- }
64
- version;
65
- nextDep;
66
- prevDep;
67
- nextSub;
68
- prevSub;
69
- prevActiveLink;
70
- }
71
- class Dep {
72
- constructor(computed) {
73
- this.computed = computed;
74
- if (__DEV__) {
75
- this.subsHead = void 0;
76
- }
77
- }
78
- version = 0;
79
- activeLink = void 0;
80
- subs = void 0;
81
- subsHead;
82
- map = void 0;
83
- key = void 0;
84
- sc = 0;
85
- track(debugInfo) {
86
- const activeSub = getActiveSub();
87
- if (!activeSub || !shouldTrack || activeSub === this.computed) {
88
- return;
89
- }
90
- let link = this.activeLink;
91
- if (link === void 0 || link.sub !== activeSub) {
92
- link = this.activeLink = new Link(activeSub, this);
93
- if (!activeSub.deps) {
94
- activeSub.deps = activeSub.depsTail = link;
95
- } else {
96
- link.prevDep = activeSub.depsTail;
97
- activeSub.depsTail.nextDep = link;
98
- activeSub.depsTail = link;
99
- }
100
- addSub(link);
101
- } else if (link.version === -1) {
102
- link.version = this.version;
103
- if (link.nextDep) {
104
- const next = link.nextDep;
105
- next.prevDep = link.prevDep;
106
- if (link.prevDep) link.prevDep.nextDep = next;
107
- link.prevDep = activeSub.depsTail;
108
- link.nextDep = void 0;
109
- activeSub.depsTail.nextDep = link;
110
- activeSub.depsTail = link;
111
- if (activeSub.deps === link) activeSub.deps = next;
112
- }
113
- }
114
- if (__DEV__ && activeSub.onTrack) {
115
- activeSub.onTrack(extend({ effect: activeSub }, debugInfo));
116
- }
117
- return link;
118
- }
119
- trigger(debugInfo) {
120
- this.version++;
121
- globalVersion++;
122
- this.notify(debugInfo);
123
- }
124
- notify(debugInfo) {
125
- try {
126
- for (let link = this.subs; link; link = link.prevSub) {
127
- if (link.sub.notify()) {
128
- link.sub.dep.notify();
129
- }
130
- }
131
- } finally {
132
- }
133
- }
134
- }
135
- function addSub(link) {
136
- link.dep.sc++;
137
- if (link.sub.flags & EffectFlags.TRACKING) {
138
- const computed = link.dep.computed;
139
- if (computed && !link.dep.subs) {
140
- computed.flags |= EffectFlags.TRACKING | EffectFlags.DIRTY;
141
- for (let l = computed.deps; l; l = l.nextDep) {
142
- addSub(l);
143
- }
144
- }
145
- const currentTail = link.dep.subs;
146
- if (currentTail !== link) {
147
- link.prevSub = currentTail;
148
- if (currentTail) currentTail.nextSub = link;
149
- }
150
- link.dep.subs = link;
151
- }
152
- }
153
- const targetMap = /* @__PURE__ */ new WeakMap();
154
- function track(target, type, key) {
155
- const activeSub = getActiveSub();
156
- if (activeSub) {
157
- let depsMap = targetMap.get(target);
158
- if (!depsMap) {
159
- targetMap.set(target, depsMap = /* @__PURE__ */ new Map());
160
- }
161
- let dep = depsMap.get(key);
162
- if (!dep) {
163
- depsMap.set(key, dep = new Dep());
164
- dep.map = depsMap;
165
- dep.key = key;
166
- }
167
- dep.track({ target, type, key });
168
- }
169
- }
170
-
171
- function refreshComputed(computed2) {
172
- if (computed2.flags & EffectFlags.DIRTY) {
173
- const prevSub = getActiveSub();
174
- setActiveSub(computed2);
175
- try {
176
- for (let link = computed2.deps; link; link = link.nextDep) {
177
- link.version = -1;
178
- link.prevActiveLink = link.prevDep;
179
- }
180
- const value = computed2.fn(computed2._value);
181
- computed2._value = value;
182
- computed2.flags &= ~EffectFlags.DIRTY;
183
- } finally {
184
- setActiveSub(prevSub);
185
- }
186
- }
187
- }
188
- class ComputedRefImpl {
189
- constructor(fn, setter, isSSR) {
190
- this.fn = fn;
191
- this.setter = setter;
192
- this.isSSR = isSSR;
193
- }
194
- _value = void 0;
195
- dep = new Dep(this);
196
- __v_isRef = true;
197
- deps = void 0;
198
- depsTail = void 0;
199
- flags = EffectFlags.DIRTY;
200
- globalVersion = globalVersion - 1;
201
- isSSR;
202
- effect = this;
203
- onTrack;
204
- onTrigger;
205
- notify() {
206
- this.flags |= EffectFlags.DIRTY;
207
- return true;
208
- }
209
- get value() {
210
- const link = __DEV__ ? this.dep.track({ target: this, type: TrackOpTypes.GET, key: "value" }) : this.dep.track();
211
- refreshComputed(this);
212
- if (link) link.version = this.dep.version;
213
- return this._value;
214
- }
215
- set value(newValue) {
216
- if (this.setter) {
217
- this.setter(newValue);
218
- } else {
219
- console.warn("Write operation failed: computed value is readonly");
220
- }
221
- }
222
- }
223
- function computed(getterOrOptions, debugOptions, isSSR = false) {
224
- let getter;
225
- let setter;
226
- if (isFunction(getterOrOptions)) {
227
- getter = getterOrOptions;
228
- } else {
229
- getter = getterOrOptions.get;
230
- setter = getterOrOptions.set;
231
- }
232
- const cRef = new ComputedRefImpl(getter, setter, isSSR);
233
- return cRef;
234
- }
235
-
236
- class ReactSubscriber {
237
- constructor(triggerUpdate) {
238
- this.triggerUpdate = triggerUpdate;
239
- }
240
- deps;
241
- depsTail;
242
- flags = EffectFlags.TRACKING;
243
- notify() {
244
- this.triggerUpdate();
245
- }
246
- }
247
- function useReactive(refObj) {
248
- const [, setTick] = useState(0);
249
- const forceUpdate = () => setTick((t) => t + 1);
250
- const subscriberRef = useRef(null);
251
- if (!subscriberRef.current) {
252
- subscriberRef.current = new ReactSubscriber(forceUpdate);
253
- }
254
- const sub = subscriberRef.current;
255
- const prevSub = getActiveSub();
256
- setActiveSub(sub);
257
- const value = refObj.value;
258
- setActiveSub(prevSub);
259
- useEffect(() => {
260
- return () => {
261
- };
262
- }, []);
263
- return value;
264
- }
265
- function useComputed(getter, deps = []) {
266
- const cRef = useMemo(() => computed(getter), deps);
267
- return useReactive(cRef);
268
- }
269
-
270
- class RefImpl {
271
- _value;
272
- dep;
273
- __v_isRef = true;
274
- constructor(value) {
275
- this._value = value;
276
- this.dep = new Dep();
277
- }
278
- get value() {
279
- this.dep.track({ target: this, type: TrackOpTypes.GET, key: "value" });
280
- return this._value;
281
- }
282
- set value(newVal) {
283
- if (newVal !== this._value) {
284
- this._value = newVal;
285
- this.dep.trigger({
286
- target: this,
287
- type: TriggerOpTypes.SET,
288
- key: "value",
289
- newValue: newVal
290
- });
291
- }
292
- }
293
- }
294
- function ref(value) {
295
- return new RefImpl(value);
296
- }
297
-
298
- function withDefaults(props, defaults) {
299
- const merged = useMemo(() => {
300
- const result = { ...props };
301
- for (const key in defaults) {
302
- if (!(key in result) || result[key] === void 0) {
303
- const def = defaults[key];
304
- if (typeof def === "function") {
305
- result[key] = def(result);
306
- } else {
307
- result[key] = def;
308
- }
309
- }
310
- }
311
- return result;
312
- }, [props, defaults]);
313
- return merged;
314
- }
315
-
316
- const useAudioRecorder = (options = {}) => {
317
- const { audioBitsPerSecond, mimeType, timeslice, enableAnalysis, fftSize } = withDefaults(options, {
318
- audioBitsPerSecond: 128e3,
319
- mimeType: "audio/webm",
320
- enableAnalysis: false,
321
- fftSize: 2048
322
- });
323
- const [isRecording, setIsRecording] = useState(false);
324
- const [isPaused, setIsPaused] = useState(false);
325
- const [stream, setStream] = useState(null);
326
- const [mediaRecorder, setMediaRecorder] = useState(
327
- null
328
- );
329
- const [audioBlob, setAudioBlob] = useState(null);
330
- const [audioUrl, setAudioUrl] = useState(null);
331
- const [duration, setDuration] = useState(0);
332
- const [error, setError] = useState(null);
333
- const [analysisData, setAnalysisData] = useState(
334
- null
335
- );
336
- const chunksRef = useRef([]);
337
- const startTimeRef = useRef(0);
338
- const pausedTimeRef = useRef(0);
339
- const intervalRef = useRef(null);
340
- const audioContextRef = useRef(null);
341
- const analyserRef = useRef(null);
342
- const sourceRef = useRef(null);
343
- const animationFrameRef = useRef(null);
344
- const isSupported = typeof navigator !== "undefined" && !!navigator.mediaDevices && !!navigator.mediaDevices.getUserMedia && !!window.MediaRecorder;
345
- const updateDuration = useCallback(() => {
346
- if (startTimeRef.current) {
347
- const elapsed = Date.now() - startTimeRef.current - pausedTimeRef.current;
348
- setDuration(Math.floor(elapsed / 1e3));
349
- }
350
- }, []);
351
- const analyzeAudio = useCallback(() => {
352
- if (!analyserRef.current || !enableAnalysis) return;
353
- const frequencyData = new Uint8Array(analyserRef.current.frequencyBinCount);
354
- const timeData = new Uint8Array(analyserRef.current.fftSize);
355
- analyserRef.current.getByteFrequencyData(frequencyData);
356
- analyserRef.current.getByteTimeDomainData(timeData);
357
- let sum = 0;
358
- for (let i = 0; i < timeData.length; i++) {
359
- const sample = ((timeData[i] ?? 0) - 128) / 128;
360
- sum += sample * sample;
361
- }
362
- const volume = Math.sqrt(sum / timeData.length);
363
- setAnalysisData({
364
- frequencyData: frequencyData.slice(),
365
- timeData: timeData.slice(),
366
- volume
367
- });
368
- if (isRecording && !isPaused) {
369
- animationFrameRef.current = requestAnimationFrame(analyzeAudio);
370
- }
371
- }, [isRecording, isPaused, enableAnalysis]);
372
- const setupAudioAnalysis = useCallback(
373
- (mediaStream) => {
374
- if (!enableAnalysis) return;
375
- try {
376
- audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
377
- analyserRef.current = audioContextRef.current.createAnalyser();
378
- sourceRef.current = audioContextRef.current.createMediaStreamSource(mediaStream);
379
- analyserRef.current.fftSize = fftSize;
380
- analyserRef.current.smoothingTimeConstant = 0.8;
381
- sourceRef.current.connect(analyserRef.current);
382
- analyzeAudio();
383
- } catch (err) {
384
- console.warn("Failed to setup audio analysis:", err);
385
- }
386
- },
387
- [enableAnalysis, fftSize, analyzeAudio]
388
- );
389
- const startRecording = useCallback(async () => {
390
- if (!isSupported) {
391
- setError("Audio recording is not supported in this browser");
392
- return;
393
- }
394
- try {
395
- setError(null);
396
- const mediaStream = await navigator.mediaDevices.getUserMedia({
397
- audio: {
398
- echoCancellation: true,
399
- noiseSuppression: true,
400
- autoGainControl: true
401
- }
402
- });
403
- setStream(mediaStream);
404
- setupAudioAnalysis(mediaStream);
405
- const recorder = new MediaRecorder(mediaStream, {
406
- audioBitsPerSecond,
407
- mimeType: MediaRecorder.isTypeSupported(mimeType) ? mimeType : "audio/webm"
408
- });
409
- chunksRef.current = [];
410
- recorder.ondataavailable = (event) => {
411
- if (event.data.size > 0) {
412
- chunksRef.current.push(event.data);
413
- }
414
- };
415
- recorder.onstop = () => {
416
- const blob = new Blob(chunksRef.current, { type: recorder.mimeType });
417
- setAudioBlob(blob);
418
- setAudioUrl(URL.createObjectURL(blob));
419
- setIsRecording(false);
420
- setIsPaused(false);
421
- if (intervalRef.current) {
422
- clearInterval(intervalRef.current);
423
- intervalRef.current = null;
424
- }
425
- if (animationFrameRef.current) {
426
- cancelAnimationFrame(animationFrameRef.current);
427
- animationFrameRef.current = null;
428
- }
429
- };
430
- recorder.onpause = () => {
431
- setIsPaused(true);
432
- pausedTimeRef.current += Date.now() - startTimeRef.current;
433
- if (animationFrameRef.current) {
434
- cancelAnimationFrame(animationFrameRef.current);
435
- animationFrameRef.current = null;
436
- }
437
- };
438
- recorder.onresume = () => {
439
- setIsPaused(false);
440
- startTimeRef.current = Date.now();
441
- if (enableAnalysis) {
442
- analyzeAudio();
443
- }
444
- };
445
- recorder.onerror = (event) => {
446
- setError(`Recording error: ${event.error?.message || "Unknown error"}`);
447
- setIsRecording(false);
448
- setIsPaused(false);
449
- };
450
- setMediaRecorder(recorder);
451
- recorder.start(timeslice);
452
- setIsRecording(true);
453
- startTimeRef.current = Date.now();
454
- pausedTimeRef.current = 0;
455
- setDuration(0);
456
- intervalRef.current = setInterval(updateDuration, 1e3);
457
- } catch (err) {
458
- const errorMessage = err instanceof Error ? err.message : "Failed to start recording";
459
- setError(errorMessage);
460
- }
461
- }, [
462
- isSupported,
463
- audioBitsPerSecond,
464
- mimeType,
465
- timeslice,
466
- setupAudioAnalysis,
467
- updateDuration,
468
- enableAnalysis,
469
- analyzeAudio
470
- ]);
471
- const stopRecording = useCallback(() => {
472
- if (mediaRecorder && mediaRecorder.state !== "inactive") {
473
- mediaRecorder.stop();
474
- }
475
- if (stream) {
476
- stream.getTracks().forEach((track) => track.stop());
477
- setStream(null);
478
- }
479
- if (audioContextRef.current) {
480
- audioContextRef.current.close();
481
- audioContextRef.current = null;
482
- }
483
- }, [mediaRecorder, stream]);
484
- const pauseRecording = useCallback(() => {
485
- if (mediaRecorder && mediaRecorder.state === "recording") {
486
- mediaRecorder.pause();
487
- }
488
- }, [mediaRecorder]);
489
- const resumeRecording = useCallback(() => {
490
- if (mediaRecorder && mediaRecorder.state === "paused") {
491
- mediaRecorder.resume();
492
- }
493
- }, [mediaRecorder]);
494
- const clearRecording = useCallback(() => {
495
- if (audioUrl) {
496
- URL.revokeObjectURL(audioUrl);
497
- }
498
- setAudioBlob(null);
499
- setAudioUrl(null);
500
- setDuration(0);
501
- setAnalysisData(null);
502
- setError(null);
503
- }, [audioUrl]);
504
- const downloadRecording = useCallback(
505
- (filename = "recording.webm") => {
506
- if (!audioUrl) return;
507
- const link = document.createElement("a");
508
- link.href = audioUrl;
509
- link.download = filename;
510
- document.body.appendChild(link);
511
- link.click();
512
- document.body.removeChild(link);
513
- },
514
- [audioUrl]
515
- );
516
- useEffect(() => {
517
- return () => {
518
- if (intervalRef.current) {
519
- clearInterval(intervalRef.current);
520
- }
521
- if (animationFrameRef.current) {
522
- cancelAnimationFrame(animationFrameRef.current);
523
- }
524
- if (stream) {
525
- stream.getTracks().forEach((track) => track.stop());
526
- }
527
- if (audioContextRef.current) {
528
- audioContextRef.current.close();
529
- }
530
- if (audioUrl) {
531
- URL.revokeObjectURL(audioUrl);
532
- }
533
- };
534
- }, [stream, audioUrl]);
535
- return {
536
- isSupported,
537
- isRecording,
538
- isPaused,
539
- stream,
540
- mediaRecorder,
541
- audioBlob,
542
- audioUrl,
543
- duration,
544
- error,
545
- analysisData,
546
- startRecording,
547
- stopRecording,
548
- pauseRecording,
549
- resumeRecording,
550
- clearRecording,
551
- downloadRecording
552
- };
553
- };
554
-
555
- function on(obj, ...args) {
556
- if (obj && obj.addEventListener) {
557
- obj.addEventListener(
558
- ...args
559
- );
560
- }
561
- }
562
- function off(obj, ...args) {
563
- if (obj && obj.removeEventListener) {
564
- obj.removeEventListener(
565
- ...args
566
- );
567
- }
568
- }
569
- const isNavigator = typeof navigator !== "undefined";
570
-
571
- const { valueOf, toString } = Object.prototype;
572
- const isEqual = (a, b) => {
573
- const visited = /* @__PURE__ */ new WeakMap();
574
- const inner = (a2, b2) => {
575
- if (a2 === b2) {
576
- return true;
577
- }
578
- if (typeof a2 !== "object" || typeof b2 !== "object" || !a2 || !b2) {
579
- return a2 !== a2 && b2 !== b2;
580
- }
581
- if (Object.getPrototypeOf(a2) !== Object.getPrototypeOf(b2)) {
582
- return false;
583
- }
584
- const { constructor } = a2;
585
- if (constructor === Date) {
586
- return a2.getTime() === b2.getTime();
587
- }
588
- if (constructor === RegExp) {
589
- return a2.source === b2.source && a2.flags === b2.flags;
590
- }
591
- if (constructor === Set) {
592
- if (a2.size !== b2.size) {
593
- return false;
594
- }
595
- for (const value of a2) {
596
- if (!b2.has(value)) {
597
- return false;
598
- }
599
- }
600
- return true;
601
- }
602
- if (constructor === ArrayBuffer) {
603
- a2 = new DataView(a2);
604
- b2 = new DataView(b2);
605
- }
606
- if (constructor === DataView || ArrayBuffer.isView(a2)) {
607
- if (constructor !== DataView) {
608
- a2 = new DataView(a2.buffer);
609
- b2 = new DataView(b2.buffer);
610
- }
611
- if (a2.byteLength !== b2.byteLength) return false;
612
- for (let i = a2.byteLength; i-- !== 0; ) {
613
- if (a2.getUint8(i) !== b2.getUint8(i)) {
614
- return false;
615
- }
616
- }
617
- return true;
618
- }
619
- if (visited.has(a2) && visited.get(a2) === b2) {
620
- return true;
621
- }
622
- visited.set(a2, b2);
623
- if (constructor === Array) {
624
- if (a2.length !== b2.length) {
625
- return false;
626
- }
627
- for (let i = a2.length; i-- !== 0; ) {
628
- if (!inner(a2[i], b2[i])) {
629
- return false;
630
- }
631
- }
632
- return true;
633
- }
634
- if (constructor === Map) {
635
- if (a2.size !== b2.size) {
636
- return false;
637
- }
638
- for (const entry of a2) {
639
- if (!b2.has(entry[0]) || !inner(entry[1], b2.get(entry[0]))) {
640
- return false;
641
- }
642
- }
643
- return true;
644
- }
645
- if (a2.valueOf !== valueOf && typeof a2.valueOf === "function" && typeof b2.valueOf === "function") {
646
- return a2.valueOf() === b2.valueOf();
647
- }
648
- if (a2.toString !== toString && typeof a2.toString === "function" && typeof b2.toString === "function") {
649
- return a2.toString() === b2.toString();
650
- }
651
- const aKeys = Object.keys(a2);
652
- let key;
653
- for (let l = aKeys.length; l-- !== 0; ) {
654
- key = aKeys[l];
655
- if (!Object.hasOwn(b2, key) || !inner(a2[key], b2[key])) {
656
- return false;
657
- }
658
- }
659
- return Object.keys(b2).length === aKeys.length;
660
- };
661
- return inner(a, b);
662
- };
663
-
664
- const nav = isNavigator ? navigator : void 0;
665
- const isBatteryApiSupported = nav && typeof nav.getBattery === "function";
666
- function useBatteryMock() {
667
- return { isSupported: false };
668
- }
669
- function useBattery() {
670
- const [state, setState] = useState({
671
- isSupported: true,
672
- fetched: false
673
- });
674
- useEffect(() => {
675
- let isMounted = true;
676
- let battery = null;
677
- const handleChange = () => {
678
- if (!isMounted || !battery) {
679
- return;
680
- }
681
- const newState = {
682
- isSupported: true,
683
- fetched: true,
684
- level: battery.level,
685
- charging: battery.charging,
686
- dischargingTime: battery.dischargingTime,
687
- chargingTime: battery.chargingTime
688
- };
689
- !isEqual(state, newState) && setState(newState);
690
- };
691
- nav.getBattery().then((bat) => {
692
- if (!isMounted) {
693
- return;
694
- }
695
- battery = bat;
696
- on(battery, "chargingchange", handleChange);
697
- on(battery, "chargingtimechange", handleChange);
698
- on(battery, "dischargingtimechange", handleChange);
699
- on(battery, "levelchange", handleChange);
700
- handleChange();
701
- });
702
- return () => {
703
- isMounted = false;
704
- if (battery) {
705
- off(battery, "chargingchange", handleChange);
706
- off(battery, "chargingtimechange", handleChange);
707
- off(battery, "dischargingtimechange", handleChange);
708
- off(battery, "levelchange", handleChange);
709
- }
710
- };
711
- }, []);
712
- return state;
713
- }
714
- const useBatteryHook = isBatteryApiSupported ? useBattery : useBatteryMock;
715
-
716
- function useBoolean(defaultValue = false) {
717
- if (typeof defaultValue !== "boolean") {
718
- throw new Error("defaultValue must be `true` or `false`");
719
- }
720
- const [value, setValue] = useState(defaultValue);
721
- const setTrue = useCallback(() => {
722
- setValue(true);
723
- }, []);
724
- const setFalse = useCallback(() => {
725
- setValue(false);
726
- }, []);
727
- const toggle = useCallback(() => {
728
- setValue((x) => !x);
729
- }, []);
730
- return { value, setValue, setTrue, setFalse, toggle };
731
- }
732
-
733
- const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
734
-
735
- function useEventListener(eventName, handler, element, options) {
736
- const savedHandler = useRef(handler);
737
- useIsomorphicLayoutEffect(() => {
738
- savedHandler.current = handler;
739
- }, [handler]);
740
- useEffect(() => {
741
- const targetElement = element?.current ?? window;
742
- if (!(targetElement && targetElement.addEventListener)) return;
743
- const listener = (event) => {
744
- savedHandler.current(event);
745
- };
746
- targetElement.addEventListener(eventName, listener, options);
747
- return () => {
748
- targetElement.removeEventListener(eventName, listener, options);
749
- };
750
- }, [eventName, element, options]);
751
- }
752
-
753
- function useClickAnyWhere(handler) {
754
- useEventListener("click", (event) => {
755
- handler(event);
756
- });
757
- }
758
-
759
- function useForkRef(...refs) {
760
- const cleanupRef = React.useRef(void 0);
761
- const refEffect = React.useCallback((instance) => {
762
- const cleanups = refs.map((ref) => {
763
- if (ref == null) {
764
- return null;
765
- }
766
- if (typeof ref === "function") {
767
- const refCallback = ref;
768
- const refCleanup = refCallback(instance);
769
- return typeof refCleanup === "function" ? refCleanup : () => {
770
- refCallback(null);
771
- };
772
- }
773
- ref.current = instance;
774
- return () => {
775
- ref.current = null;
776
- };
777
- });
778
- return () => {
779
- cleanups.forEach((refCleanup) => refCleanup?.());
780
- };
781
- }, refs);
782
- return React.useMemo(() => {
783
- if (refs.every((ref) => ref == null)) {
784
- return null;
785
- }
786
- return (value) => {
787
- if (cleanupRef.current) {
788
- cleanupRef.current();
789
- cleanupRef.current = void 0;
790
- }
791
- if (value != null) {
792
- cleanupRef.current = refEffect(value);
793
- }
794
- };
795
- }, refs);
796
- }
797
-
798
- function ownerDocument(node) {
799
- return node && node.ownerDocument || document;
800
- }
801
-
802
- function useEventCallback(fn) {
803
- const ref = useRef(() => {
804
- throw new Error("Cannot call an event handler while rendering.");
805
- });
806
- useIsomorphicLayoutEffect(() => {
807
- ref.current = fn;
808
- }, [fn]);
809
- return useCallback((...args) => ref.current?.(...args), [ref]);
810
- }
811
-
812
- function mapEventPropToEvent(eventProp) {
813
- return eventProp.substring(2).toLowerCase();
814
- }
815
- function clickedRootScrollbar(event, doc) {
816
- return doc.documentElement.clientWidth < event.clientX || doc.documentElement.clientHeight < event.clientY;
817
- }
818
- function useClickAway(onClickAway, options = {}) {
819
- const {
820
- disableReactTree = false,
821
- mouseEvent = "onClick",
822
- touchEvent = "onTouchEnd",
823
- ref: externalRef
824
- } = options;
825
- const internalRef = React.useRef(null);
826
- const handleRef = useForkRef(externalRef, internalRef);
827
- const movedRef = React.useRef(false);
828
- const activatedRef = React.useRef(false);
829
- const syntheticEventRef = React.useRef(false);
830
- React.useEffect(() => {
831
- const timer = setTimeout(() => {
832
- activatedRef.current = true;
833
- }, 0);
834
- return () => {
835
- activatedRef.current = false;
836
- clearTimeout(timer);
837
- };
838
- }, []);
839
- const handleClickAway = useEventCallback((event) => {
840
- const insideReactTree = syntheticEventRef.current;
841
- syntheticEventRef.current = false;
842
- const doc = ownerDocument(internalRef.current);
843
- if (!activatedRef.current || !internalRef.current || "clientX" in event && clickedRootScrollbar(event, doc)) {
844
- return;
845
- }
846
- if (movedRef.current) {
847
- movedRef.current = false;
848
- return;
849
- }
850
- let insideDOM;
851
- if (event.composedPath) {
852
- insideDOM = event.composedPath().includes(internalRef.current);
853
- } else {
854
- insideDOM = !doc.documentElement.contains(
855
- // @ts-expect-error returns `false` as intended when not dispatched from a Node
856
- event.target
857
- ) || internalRef.current.contains(
858
- // @ts-expect-error returns `false` as intended when not dispatched from a Node
859
- event.target
860
- );
861
- }
862
- if (!insideDOM && (disableReactTree || !insideReactTree)) {
863
- onClickAway(event);
864
- }
865
- });
866
- const createHandleSynthetic = (event) => {
867
- syntheticEventRef.current = true;
868
- };
869
- React.useEffect(() => {
870
- if (touchEvent !== false) {
871
- const mappedTouchEvent = mapEventPropToEvent(touchEvent);
872
- const doc = ownerDocument(internalRef.current);
873
- const handleTouchMove = () => {
874
- movedRef.current = true;
875
- };
876
- doc.addEventListener(mappedTouchEvent, handleClickAway);
877
- doc.addEventListener("touchmove", handleTouchMove);
878
- return () => {
879
- doc.removeEventListener(mappedTouchEvent, handleClickAway);
880
- doc.removeEventListener("touchmove", handleTouchMove);
881
- };
882
- }
883
- return void 0;
884
- }, [handleClickAway, touchEvent]);
885
- React.useEffect(() => {
886
- if (mouseEvent !== false) {
887
- const mappedMouseEvent = mapEventPropToEvent(mouseEvent);
888
- const doc = ownerDocument(internalRef.current);
889
- doc.addEventListener(mappedMouseEvent, handleClickAway);
890
- return () => {
891
- doc.removeEventListener(mappedMouseEvent, handleClickAway);
892
- };
893
- }
894
- return void 0;
895
- }, [handleClickAway, mouseEvent]);
896
- const listenerProps = {};
897
- if (mouseEvent !== false) {
898
- listenerProps[mouseEvent] = createHandleSynthetic;
899
- }
900
- if (touchEvent !== false) {
901
- listenerProps[touchEvent] = createHandleSynthetic;
902
- }
903
- return {
904
- ref: handleRef,
905
- listenerProps
906
- };
907
- }
908
-
909
- const IS_SERVER$7 = typeof document === "undefined";
910
- function parseCookies() {
911
- if (IS_SERVER$7) return {};
912
- return document.cookie.split("; ").reduce(
913
- (acc, part) => {
914
- const [k, ...v] = part.split("=");
915
- if (k && v) {
916
- acc[decodeURIComponent(k.trim())] = decodeURIComponent(v.join("="));
917
- }
918
- return acc;
919
- },
920
- {}
921
- );
922
- }
923
- function buildCookie(key, value, options) {
924
- let cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
925
- if (options.path) cookie += `; path=${options.path}`;
926
- if (options.domain) cookie += `; domain=${options.domain}`;
927
- if (options.expires) cookie += `; expires=${options.expires.toUTCString()}`;
928
- if (options.maxAge) cookie += `; max-age=${options.maxAge}`;
929
- if (options.secure) cookie += `; secure`;
930
- if (options.sameSite) cookie += `; samesite=${options.sameSite}`;
931
- return cookie;
932
- }
933
- function useCookie(key, initialValue, options = {}) {
934
- const { initializeWithValue = true, prefix = "" } = options;
935
- const cookieKey = prefix + key;
936
- const serializer = useCallback(
937
- (value) => {
938
- if (options.serializer) return options.serializer(value);
939
- return JSON.stringify(value);
940
- },
941
- [options]
942
- );
943
- const deserializer = useCallback(
944
- (value) => {
945
- if (options.deserializer) return options.deserializer(value);
946
- if (value === "undefined") return void 0;
947
- const fallback = initialValue instanceof Function ? initialValue() : initialValue;
948
- try {
949
- return JSON.parse(value);
950
- } catch {
951
- return fallback;
952
- }
953
- },
954
- [options, initialValue]
955
- );
956
- const readValue = useCallback(() => {
957
- const fallback = initialValue instanceof Function ? initialValue() : initialValue;
958
- if (IS_SERVER$7) return fallback;
959
- const cookies = parseCookies();
960
- if (!(cookieKey in cookies)) return fallback;
961
- return deserializer(cookies[cookieKey]);
962
- }, [cookieKey, deserializer, initialValue]);
963
- const [storedValue, setStoredValue] = useState(() => {
964
- if (initializeWithValue) return readValue();
965
- return initialValue instanceof Function ? initialValue() : initialValue;
966
- });
967
- const setValue = useEventCallback((value) => {
968
- if (IS_SERVER$7) return;
969
- try {
970
- const newValue = value instanceof Function ? value(readValue()) : value;
971
- const serialized = serializer(newValue);
972
- document.cookie = buildCookie(cookieKey, serialized, {
973
- path: "/",
974
- ...options
975
- });
976
- setStoredValue(newValue);
977
- window.dispatchEvent(
978
- new CustomEvent("cookie-change", { detail: { key: cookieKey } })
979
- );
980
- } catch (error) {
981
- console.warn(`Error setting cookie "${cookieKey}":`, error);
982
- }
983
- });
984
- const removeValue = useEventCallback(() => {
985
- if (IS_SERVER$7) return;
986
- const fallback = initialValue instanceof Function ? initialValue() : initialValue;
987
- document.cookie = buildCookie(cookieKey, "", {
988
- path: "/",
989
- ...options,
990
- expires: /* @__PURE__ */ new Date(0)
991
- });
992
- setStoredValue(fallback);
993
- window.dispatchEvent(
994
- new CustomEvent("cookie-change", { detail: { key: cookieKey } })
995
- );
996
- });
997
- useEffect(() => {
998
- setStoredValue(readValue());
999
- }, [cookieKey]);
1000
- const handleChange = useCallback(
1001
- (event) => {
1002
- if ("detail" in event && event.detail?.key !== cookieKey) return;
1003
- if ("key" in event && event.key !== cookieKey) return;
1004
- setStoredValue(readValue());
1005
- },
1006
- [cookieKey, readValue]
1007
- );
1008
- useEventListener("cookie-change", handleChange);
1009
- useEventListener("visibilitychange", () => {
1010
- if (document.visibilityState === "visible") {
1011
- setStoredValue(readValue());
1012
- }
1013
- });
1014
- return [storedValue, setValue, removeValue];
1015
- }
1016
-
1017
- function useCopyToClipboard() {
1018
- const [copiedText, setCopiedText] = useState(null);
1019
- const copy = useCallback(async (text) => {
1020
- if (!navigator?.clipboard) {
1021
- console.warn("Clipboard not supported");
1022
- return false;
1023
- }
1024
- try {
1025
- await navigator.clipboard.writeText(text);
1026
- setCopiedText(text);
1027
- return true;
1028
- } catch (error) {
1029
- console.warn("Copy failed", error);
1030
- setCopiedText(null);
1031
- return false;
1032
- }
1033
- }, []);
1034
- return [copiedText, copy];
1035
- }
1036
-
1037
- function useCounter(initialValue) {
1038
- const [count, setCount] = useState(initialValue ?? 0);
1039
- const increment = useCallback(() => {
1040
- setCount((x) => x + 1);
1041
- }, []);
1042
- const decrement = useCallback(() => {
1043
- setCount((x) => x - 1);
1044
- }, []);
1045
- const reset = useCallback(() => {
1046
- setCount(initialValue ?? 0);
1047
- }, [initialValue]);
1048
- return {
1049
- count,
1050
- increment,
1051
- decrement,
1052
- reset,
1053
- setCount
1054
- };
1055
- }
1056
-
1057
- function useInterval(callback, delay) {
1058
- const savedCallback = useRef(callback);
1059
- useIsomorphicLayoutEffect(() => {
1060
- savedCallback.current = callback;
1061
- }, [callback]);
1062
- useEffect(() => {
1063
- if (delay === null) {
1064
- return;
1065
- }
1066
- const id = setInterval(() => {
1067
- savedCallback.current();
1068
- }, delay);
1069
- return () => {
1070
- clearInterval(id);
1071
- };
1072
- }, [delay]);
1073
- }
1074
-
1075
- function useCountdown({
1076
- countStart,
1077
- countStop = 0,
1078
- intervalMs = 1e3,
1079
- isIncrement = false
1080
- }) {
1081
- const {
1082
- count,
1083
- increment,
1084
- decrement,
1085
- reset: resetCounter
1086
- } = useCounter(countStart);
1087
- const {
1088
- value: isCountdownRunning,
1089
- setTrue: startCountdown,
1090
- setFalse: stopCountdown
1091
- } = useBoolean(false);
1092
- const resetCountdown = useCallback(() => {
1093
- stopCountdown();
1094
- resetCounter();
1095
- }, [stopCountdown, resetCounter]);
1096
- const countdownCallback = useCallback(() => {
1097
- if (count === countStop) {
1098
- stopCountdown();
1099
- return;
1100
- }
1101
- if (isIncrement) {
1102
- increment();
1103
- } else {
1104
- decrement();
1105
- }
1106
- }, [count, countStop, decrement, increment, isIncrement, stopCountdown]);
1107
- useInterval(countdownCallback, isCountdownRunning ? intervalMs : null);
1108
- return [count, { startCountdown, stopCountdown, resetCountdown }];
1109
- }
1110
-
1111
- const IS_SERVER$6 = typeof window === "undefined";
1112
- function useLocalStorage(key, initialValue, options = {}) {
1113
- const { initializeWithValue = true } = options;
1114
- const serializer = useCallback(
1115
- (value) => {
1116
- if (options.serializer) {
1117
- return options.serializer(value);
1118
- }
1119
- return JSON.stringify(value);
1120
- },
1121
- [options]
1122
- );
1123
- const deserializer = useCallback(
1124
- (value) => {
1125
- if (options.deserializer) {
1126
- return options.deserializer(value);
1127
- }
1128
- if (value === "undefined") {
1129
- return void 0;
1130
- }
1131
- const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
1132
- let parsed;
1133
- try {
1134
- parsed = JSON.parse(value);
1135
- } catch (error) {
1136
- console.error("Error parsing JSON:", error);
1137
- return defaultValue;
1138
- }
1139
- return parsed;
1140
- },
1141
- [options, initialValue]
1142
- );
1143
- const readValue = useCallback(() => {
1144
- const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue;
1145
- if (IS_SERVER$6) {
1146
- return initialValueToUse;
1147
- }
1148
- try {
1149
- const raw = window.localStorage.getItem(key);
1150
- return raw ? deserializer(raw) : initialValueToUse;
1151
- } catch (error) {
1152
- console.warn(`Error reading localStorage key \u201C${key}\u201D:`, error);
1153
- return initialValueToUse;
1154
- }
1155
- }, [initialValue, key, deserializer]);
1156
- const [storedValue, setStoredValue] = useState(() => {
1157
- if (initializeWithValue) {
1158
- return readValue();
1159
- }
1160
- return initialValue instanceof Function ? initialValue() : initialValue;
1161
- });
1162
- const setValue = useEventCallback((value) => {
1163
- if (IS_SERVER$6) {
1164
- console.warn(
1165
- `Tried setting localStorage key \u201C${key}\u201D even though environment is not a client`
1166
- );
1167
- }
1168
- try {
1169
- const newValue = value instanceof Function ? value(readValue()) : value;
1170
- window.localStorage.setItem(key, serializer(newValue));
1171
- setStoredValue(newValue);
1172
- window.dispatchEvent(new StorageEvent("local-storage", { key }));
1173
- } catch (error) {
1174
- console.warn(`Error setting localStorage key \u201C${key}\u201D:`, error);
1175
- }
1176
- });
1177
- const removeValue = useEventCallback(() => {
1178
- if (IS_SERVER$6) {
1179
- console.warn(
1180
- `Tried removing localStorage key \u201C${key}\u201D even though environment is not a client`
1181
- );
1182
- }
1183
- const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
1184
- window.localStorage.removeItem(key);
1185
- setStoredValue(defaultValue);
1186
- window.dispatchEvent(new StorageEvent("local-storage", { key }));
1187
- });
1188
- useEffect(() => {
1189
- setStoredValue(readValue());
1190
- }, [key]);
1191
- const handleStorageChange = useCallback(
1192
- (event) => {
1193
- if (event.key && event.key !== key) {
1194
- return;
1195
- }
1196
- setStoredValue(readValue());
1197
- },
1198
- [key, readValue]
1199
- );
1200
- useEventListener("storage", handleStorageChange);
1201
- useEventListener("local-storage", handleStorageChange);
1202
- return [storedValue, setValue, removeValue];
1203
- }
1204
-
1205
- const IS_SERVER$5 = typeof window === "undefined";
1206
- function useMediaQuery(query, {
1207
- defaultValue = false,
1208
- initializeWithValue = true
1209
- } = {}) {
1210
- const getMatches = (query2) => {
1211
- if (IS_SERVER$5) {
1212
- return defaultValue;
1213
- }
1214
- return window.matchMedia(query2).matches;
1215
- };
1216
- const [matches, setMatches] = useState(() => {
1217
- if (initializeWithValue) {
1218
- return getMatches(query);
1219
- }
1220
- return defaultValue;
1221
- });
1222
- function handleChange() {
1223
- setMatches(getMatches(query));
1224
- }
1225
- useIsomorphicLayoutEffect(() => {
1226
- const matchMedia = window.matchMedia(query);
1227
- handleChange();
1228
- if (matchMedia.addListener) {
1229
- matchMedia.addListener(handleChange);
1230
- } else {
1231
- matchMedia.addEventListener("change", handleChange);
1232
- }
1233
- return () => {
1234
- if (matchMedia.removeListener) {
1235
- matchMedia.removeListener(handleChange);
1236
- } else {
1237
- matchMedia.removeEventListener("change", handleChange);
1238
- }
1239
- };
1240
- }, [query]);
1241
- return matches;
1242
- }
1243
-
1244
- const COLOR_SCHEME_QUERY$1 = "(prefers-color-scheme: dark)";
1245
- const LOCAL_STORAGE_KEY$1 = "sse-hooks-dark-mode";
1246
- function useDarkMode(options = {}) {
1247
- const {
1248
- defaultValue,
1249
- localStorageKey = LOCAL_STORAGE_KEY$1,
1250
- initializeWithValue = true
1251
- } = options;
1252
- const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY$1, {
1253
- initializeWithValue,
1254
- defaultValue
1255
- });
1256
- const [isDarkMode, setDarkMode] = useLocalStorage(
1257
- localStorageKey,
1258
- defaultValue ?? isDarkOS ?? false,
1259
- { initializeWithValue }
1260
- );
1261
- useIsomorphicLayoutEffect(() => {
1262
- if (isDarkOS !== isDarkMode) {
1263
- setDarkMode(isDarkOS);
1264
- }
1265
- }, [isDarkOS]);
1266
- return {
1267
- isDarkMode,
1268
- toggle: () => {
1269
- setDarkMode((prev) => !prev);
1270
- },
1271
- enable: () => {
1272
- setDarkMode(true);
1273
- },
1274
- disable: () => {
1275
- setDarkMode(false);
1276
- },
1277
- set: (value) => {
1278
- setDarkMode(value);
1279
- }
1280
- };
1281
- }
1282
-
1283
- function useUnmount(func) {
1284
- const funcRef = useRef(func);
1285
- funcRef.current = func;
1286
- useEffect(
1287
- () => () => {
1288
- funcRef.current();
1289
- },
1290
- []
1291
- );
1292
- }
1293
-
1294
- function useDebounceCallback(func, delay = 500, options) {
1295
- const debouncedFunc = useRef();
1296
- useUnmount(() => {
1297
- if (debouncedFunc.current) {
1298
- debouncedFunc.current.cancel();
1299
- }
1300
- });
1301
- const debounced = useMemo(() => {
1302
- const debouncedFuncInstance = debounce(func, delay, options);
1303
- const wrappedFunc = (...args) => {
1304
- return debouncedFuncInstance(...args);
1305
- };
1306
- wrappedFunc.cancel = () => {
1307
- debouncedFuncInstance.cancel();
1308
- };
1309
- wrappedFunc.isPending = () => {
1310
- return !!debouncedFunc.current;
1311
- };
1312
- wrappedFunc.flush = () => {
1313
- return debouncedFuncInstance.flush();
1314
- };
1315
- return wrappedFunc;
1316
- }, [func, delay, options]);
1317
- useEffect(() => {
1318
- debouncedFunc.current = debounce(func, delay, options);
1319
- }, [func, delay, options]);
1320
- return debounced;
1321
- }
1322
-
1323
- function useDebounceValue(initialValue, delay, options) {
1324
- const eq = options?.equalityFn ?? ((left, right) => left === right);
1325
- const unwrappedInitialValue = initialValue instanceof Function ? initialValue() : initialValue;
1326
- const [debouncedValue, setDebouncedValue] = useState(
1327
- unwrappedInitialValue
1328
- );
1329
- const previousValueRef = useRef(unwrappedInitialValue);
1330
- const updateDebouncedValue = useDebounceCallback(
1331
- setDebouncedValue,
1332
- delay,
1333
- options
1334
- );
1335
- if (!eq(previousValueRef.current, unwrappedInitialValue)) {
1336
- updateDebouncedValue(unwrappedInitialValue);
1337
- previousValueRef.current = unwrappedInitialValue;
1338
- }
1339
- return [debouncedValue, updateDebouncedValue];
1340
- }
1341
-
1342
- function useDocumentTitle(title, options = {}) {
1343
- const { preserveTitleOnUnmount = true } = options;
1344
- const defaultTitle = useRef(null);
1345
- useIsomorphicLayoutEffect(() => {
1346
- defaultTitle.current = window.document.title;
1347
- }, []);
1348
- useIsomorphicLayoutEffect(() => {
1349
- window.document.title = title;
1350
- }, [title]);
1351
- useUnmount(() => {
1352
- if (!preserveTitleOnUnmount && defaultTitle.current) {
1353
- window.document.title = defaultTitle.current;
1354
- }
1355
- });
1356
- }
1357
-
1358
- function useFetch(url, options = {}) {
1359
- const [state, setState] = useState({
1360
- data: null,
1361
- loading: false,
1362
- error: null
1363
- });
1364
- const abortControllerRef = useRef(null);
1365
- const optionsRef = useRef(options);
1366
- useEffect(() => {
1367
- optionsRef.current = options;
1368
- }, [options]);
1369
- const execute = useCallback(
1370
- async (executeUrl, executeOptions) => {
1371
- const targetUrl = executeUrl || url;
1372
- if (!targetUrl) {
1373
- const error = new Error("No URL provided");
1374
- setState((prev) => ({ ...prev, error, loading: false }));
1375
- optionsRef.current.onError?.(error);
1376
- throw error;
1377
- }
1378
- if (abortControllerRef.current) {
1379
- abortControllerRef.current.abort();
1380
- }
1381
- abortControllerRef.current = new AbortController();
1382
- setState((prev) => ({ ...prev, loading: true, error: null }));
1383
- try {
1384
- const { immediate, onSuccess, onError, ...fetchOptions } = optionsRef.current;
1385
- const response = await fetch(targetUrl, {
1386
- ...fetchOptions,
1387
- ...executeOptions,
1388
- signal: abortControllerRef.current.signal
1389
- });
1390
- if (!response.ok) {
1391
- throw new Error(`HTTP error! status: ${response.status}`);
1392
- }
1393
- let data;
1394
- const contentType = response.headers.get("content-type");
1395
- if (contentType && contentType.includes("application/json")) {
1396
- data = await response.json();
1397
- } else {
1398
- data = await response.text();
1399
- }
1400
- setState({ data, loading: false, error: null });
1401
- onSuccess?.(data);
1402
- return data;
1403
- } catch (error) {
1404
- const fetchError = error;
1405
- if (fetchError.name !== "AbortError") {
1406
- setState((prev) => ({ ...prev, loading: false, error: fetchError }));
1407
- optionsRef.current.onError?.(fetchError);
1408
- }
1409
- throw fetchError;
1410
- }
1411
- },
1412
- [url]
1413
- );
1414
- const abort = useCallback(() => {
1415
- if (abortControllerRef.current) {
1416
- abortControllerRef.current.abort();
1417
- abortControllerRef.current = null;
1418
- }
1419
- }, []);
1420
- const reset = useCallback(() => {
1421
- abort();
1422
- setState({ data: null, loading: false, error: null });
1423
- }, [abort]);
1424
- useEffect(() => {
1425
- if (options.immediate && url) {
1426
- execute();
1427
- }
1428
- }, [url, options.immediate, execute]);
1429
- useEffect(() => {
1430
- return () => {
1431
- abort();
1432
- };
1433
- }, [abort]);
1434
- return {
1435
- ...state,
1436
- execute,
1437
- abort,
1438
- reset
1439
- };
1440
- }
1441
- function useGet(url, options = {}) {
1442
- return useFetch(url, { ...options, method: "GET" });
1443
- }
1444
- function usePost(url, options = {}) {
1445
- return useFetch(url, { ...options, method: "POST" });
1446
- }
1447
- function usePut(url, options = {}) {
1448
- return useFetch(url, { ...options, method: "PUT" });
1449
- }
1450
- function useDelete(url, options = {}) {
1451
- return useFetch(url, { ...options, method: "DELETE" });
1452
- }
1453
-
1454
- function useHover(elementRef) {
1455
- const [value, setValue] = useState(false);
1456
- const handleMouseEnter = () => {
1457
- setValue(true);
1458
- };
1459
- const handleMouseLeave = () => {
1460
- setValue(false);
1461
- };
1462
- useEventListener("mouseenter", handleMouseEnter, elementRef);
1463
- useEventListener("mouseleave", handleMouseLeave, elementRef);
1464
- return value;
1465
- }
1466
-
1467
- function useIndexedDB(databaseName, storeName, options = {}) {
1468
- const [data, setData] = useState(null);
1469
- const [error, setError] = useState(null);
1470
- const [loading, setLoading] = useState(false);
1471
- const [db, setDb] = useState(null);
1472
- const { version = 1, onUpgradeNeeded } = options;
1473
- useEffect(() => {
1474
- if (typeof window === "undefined") return;
1475
- const initDB = async () => {
1476
- try {
1477
- setLoading(true);
1478
- setError(null);
1479
- const request = indexedDB.open(databaseName, version);
1480
- request.onerror = () => {
1481
- setError(`Failed to open database: ${request.error?.message}`);
1482
- setLoading(false);
1483
- };
1484
- request.onsuccess = () => {
1485
- setDb(request.result);
1486
- setLoading(false);
1487
- };
1488
- request.onupgradeneeded = (event) => {
1489
- const database = request.result;
1490
- const oldVersion = event.oldVersion;
1491
- const newVersion = event.newVersion || version;
1492
- if (!database.objectStoreNames.contains(storeName)) {
1493
- database.createObjectStore(storeName);
1494
- }
1495
- if (onUpgradeNeeded) {
1496
- onUpgradeNeeded(database, oldVersion, newVersion);
1497
- }
1498
- };
1499
- } catch (err) {
1500
- setError(`IndexedDB initialization error: ${err}`);
1501
- setLoading(false);
1502
- }
1503
- };
1504
- initDB();
1505
- return () => {
1506
- if (db) {
1507
- db.close();
1508
- }
1509
- };
1510
- }, [databaseName, storeName, version, onUpgradeNeeded]);
1511
- const setItem = useCallback(
1512
- async (key, value) => {
1513
- if (!db) {
1514
- throw new Error("Database not initialized");
1515
- }
1516
- return new Promise((resolve, reject) => {
1517
- const transaction = db.transaction([storeName], "readwrite");
1518
- const store = transaction.objectStore(storeName);
1519
- const request = store.put(value, key);
1520
- request.onsuccess = () => {
1521
- setData(value);
1522
- resolve();
1523
- };
1524
- request.onerror = () => {
1525
- const errorMsg = `Failed to set item: ${request.error?.message}`;
1526
- setError(errorMsg);
1527
- reject(new Error(errorMsg));
1528
- };
1529
- });
1530
- },
1531
- [db, storeName]
1532
- );
1533
- const getItem = useCallback(
1534
- async (key) => {
1535
- if (!db) {
1536
- throw new Error("Database not initialized");
1537
- }
1538
- return new Promise((resolve, reject) => {
1539
- const transaction = db.transaction([storeName], "readonly");
1540
- const store = transaction.objectStore(storeName);
1541
- const request = store.get(key);
1542
- request.onsuccess = () => {
1543
- const result = request.result || null;
1544
- setData(result);
1545
- resolve(result);
1546
- };
1547
- request.onerror = () => {
1548
- const errorMsg = `Failed to get item: ${request.error?.message}`;
1549
- setError(errorMsg);
1550
- reject(new Error(errorMsg));
1551
- };
1552
- });
1553
- },
1554
- [db, storeName]
1555
- );
1556
- const removeItem = useCallback(
1557
- async (key) => {
1558
- if (!db) {
1559
- throw new Error("Database not initialized");
1560
- }
1561
- return new Promise((resolve, reject) => {
1562
- const transaction = db.transaction([storeName], "readwrite");
1563
- const store = transaction.objectStore(storeName);
1564
- const request = store.delete(key);
1565
- request.onsuccess = () => {
1566
- setData(null);
1567
- resolve();
1568
- };
1569
- request.onerror = () => {
1570
- const errorMsg = `Failed to remove item: ${request.error?.message}`;
1571
- setError(errorMsg);
1572
- reject(new Error(errorMsg));
1573
- };
1574
- });
1575
- },
1576
- [db, storeName]
1577
- );
1578
- const clear = useCallback(async () => {
1579
- if (!db) {
1580
- throw new Error("Database not initialized");
1581
- }
1582
- return new Promise((resolve, reject) => {
1583
- const transaction = db.transaction([storeName], "readwrite");
1584
- const store = transaction.objectStore(storeName);
1585
- const request = store.clear();
1586
- request.onsuccess = () => {
1587
- setData(null);
1588
- resolve();
1589
- };
1590
- request.onerror = () => {
1591
- const errorMsg = `Failed to clear store: ${request.error?.message}`;
1592
- setError(errorMsg);
1593
- reject(new Error(errorMsg));
1594
- };
1595
- });
1596
- }, [db, storeName]);
1597
- const getAllKeys = useCallback(async () => {
1598
- if (!db) {
1599
- throw new Error("Database not initialized");
1600
- }
1601
- return new Promise((resolve, reject) => {
1602
- const transaction = db.transaction([storeName], "readonly");
1603
- const store = transaction.objectStore(storeName);
1604
- const request = store.getAllKeys();
1605
- request.onsuccess = () => {
1606
- resolve(request.result);
1607
- };
1608
- request.onerror = () => {
1609
- const errorMsg = `Failed to get keys: ${request.error?.message}`;
1610
- setError(errorMsg);
1611
- reject(new Error(errorMsg));
1612
- };
1613
- });
1614
- }, [db, storeName]);
1615
- return {
1616
- data,
1617
- error,
1618
- loading,
1619
- setItem,
1620
- getItem,
1621
- removeItem,
1622
- clear,
1623
- getAllKeys
1624
- };
1625
- }
1626
-
1627
- function useIntersectionObserver({
1628
- threshold = 0,
1629
- root = null,
1630
- rootMargin = "0%",
1631
- freezeOnceVisible = false,
1632
- initialIsIntersecting = false,
1633
- onChange
1634
- } = {}) {
1635
- const [ref, setRef] = useState(null);
1636
- const [state, setState] = useState(() => ({
1637
- isIntersecting: initialIsIntersecting,
1638
- entry: void 0
1639
- }));
1640
- const callbackRef = useRef();
1641
- callbackRef.current = onChange;
1642
- const frozen = state.entry?.isIntersecting && freezeOnceVisible;
1643
- useEffect(() => {
1644
- if (!ref) return;
1645
- if (!("IntersectionObserver" in window)) return;
1646
- if (frozen) return;
1647
- const observer = new IntersectionObserver(
1648
- (entries) => {
1649
- const thresholds = Array.isArray(observer.thresholds) ? observer.thresholds : [observer.thresholds];
1650
- entries.forEach((entry) => {
1651
- const isIntersecting = entry.isIntersecting && thresholds.some(
1652
- (threshold2) => entry.intersectionRatio >= threshold2
1653
- );
1654
- setState({ isIntersecting, entry });
1655
- if (callbackRef.current) {
1656
- callbackRef.current(isIntersecting, entry);
1657
- }
1658
- });
1659
- },
1660
- { threshold, root, rootMargin }
1661
- );
1662
- observer.observe(ref);
1663
- return () => {
1664
- observer.disconnect();
1665
- };
1666
- }, [
1667
- ref,
1668
- // eslint-disable-next-line react-hooks/exhaustive-deps
1669
- JSON.stringify(threshold),
1670
- root,
1671
- rootMargin,
1672
- frozen,
1673
- freezeOnceVisible
1674
- ]);
1675
- const prevRef = useRef(null);
1676
- useEffect(() => {
1677
- if (!ref && state.entry?.target && !freezeOnceVisible && !frozen && prevRef.current !== state.entry.target) {
1678
- prevRef.current = state.entry.target;
1679
- setState({ isIntersecting: initialIsIntersecting, entry: void 0 });
1680
- }
1681
- }, [ref, state.entry, freezeOnceVisible, frozen, initialIsIntersecting]);
1682
- const result = [
1683
- setRef,
1684
- !!state.isIntersecting,
1685
- state.entry
1686
- ];
1687
- result.ref = result[0];
1688
- result.isIntersecting = result[1];
1689
- result.entry = result[2];
1690
- return result;
1691
- }
1692
-
1693
- function useIsClient() {
1694
- const [isClient, setClient] = useState(false);
1695
- useEffect(() => {
1696
- setClient(true);
1697
- }, []);
1698
- return isClient;
1699
- }
1700
-
1701
- function useIsMounted() {
1702
- const isMounted = useRef(false);
1703
- useEffect(() => {
1704
- isMounted.current = true;
1705
- return () => {
1706
- isMounted.current = false;
1707
- };
1708
- }, []);
1709
- return useCallback(() => isMounted.current, []);
1710
- }
1711
-
1712
- function useMap(initialState = /* @__PURE__ */ new Map()) {
1713
- const [map, setMap] = useState(new Map(initialState));
1714
- const actions = {
1715
- set: useCallback((key, value) => {
1716
- setMap((prev) => {
1717
- const copy = new Map(prev);
1718
- copy.set(key, value);
1719
- return copy;
1720
- });
1721
- }, []),
1722
- setAll: useCallback((entries) => {
1723
- setMap(() => new Map(entries));
1724
- }, []),
1725
- remove: useCallback((key) => {
1726
- setMap((prev) => {
1727
- const copy = new Map(prev);
1728
- copy.delete(key);
1729
- return copy;
1730
- });
1731
- }, []),
1732
- reset: useCallback(() => {
1733
- setMap(() => /* @__PURE__ */ new Map());
1734
- }, [])
1735
- };
1736
- return [map, actions];
1737
- }
1738
-
1739
- const useMediaSession = (options) => {
1740
- const actionHandlersRef = useRef(/* @__PURE__ */ new Set());
1741
- const isSupported = typeof navigator !== "undefined" && "mediaSession" in navigator;
1742
- const setMetadata = useCallback(
1743
- (metadata) => {
1744
- if (!isSupported) return;
1745
- try {
1746
- navigator.mediaSession.metadata = new MediaMetadata(metadata);
1747
- } catch (error) {
1748
- console.warn("Failed to set media metadata:", error);
1749
- }
1750
- },
1751
- [isSupported]
1752
- );
1753
- const setPlaybackState = useCallback(
1754
- (state) => {
1755
- if (!isSupported) return;
1756
- try {
1757
- navigator.mediaSession.playbackState = state;
1758
- } catch (error) {
1759
- console.warn("Failed to set playback state:", error);
1760
- }
1761
- },
1762
- [isSupported]
1763
- );
1764
- const setActionHandler = useCallback(
1765
- (action, handler) => {
1766
- if (!isSupported) return;
1767
- try {
1768
- navigator.mediaSession.setActionHandler(action, handler);
1769
- if (handler) {
1770
- actionHandlersRef.current.add(action);
1771
- } else {
1772
- actionHandlersRef.current.delete(action);
1773
- }
1774
- } catch (error) {
1775
- console.warn(`Failed to set action handler for ${action}:`, error);
1776
- }
1777
- },
1778
- [isSupported]
1779
- );
1780
- const clearActionHandlers = useCallback(() => {
1781
- if (!isSupported) return;
1782
- actionHandlersRef.current.forEach((action) => {
1783
- try {
1784
- navigator.mediaSession.setActionHandler(action, null);
1785
- } catch (error) {
1786
- console.warn(`Failed to clear action handler for ${action}:`, error);
1787
- }
1788
- });
1789
- actionHandlersRef.current.clear();
1790
- }, [isSupported]);
1791
- useEffect(() => {
1792
- if (options?.metadata) {
1793
- setMetadata(options.metadata);
1794
- }
1795
- }, [setMetadata, options?.metadata]);
1796
- useEffect(() => {
1797
- if (options?.playbackState) {
1798
- setPlaybackState(options.playbackState);
1799
- }
1800
- }, [setPlaybackState, options?.playbackState]);
1801
- useEffect(() => {
1802
- if (options?.actionHandlers) {
1803
- Object.entries(options.actionHandlers).forEach(([action, handler]) => {
1804
- if (handler) {
1805
- setActionHandler(action, handler);
1806
- }
1807
- });
1808
- }
1809
- return () => {
1810
- clearActionHandlers();
1811
- };
1812
- }, [setActionHandler, clearActionHandlers, options?.actionHandlers]);
1813
- return {
1814
- isSupported,
1815
- setMetadata,
1816
- setPlaybackState,
1817
- setActionHandler,
1818
- clearActionHandlers
1819
- };
1820
- };
1821
-
1822
- const IS_SERVER$4 = typeof window === "undefined";
1823
- function useReadLocalStorage(key, options = {}) {
1824
- let { initializeWithValue = true } = options;
1825
- if (IS_SERVER$4) {
1826
- initializeWithValue = false;
1827
- }
1828
- const deserializer = useCallback(
1829
- (value) => {
1830
- if (options.deserializer) {
1831
- return options.deserializer(value);
1832
- }
1833
- if (value === "undefined") {
1834
- return void 0;
1835
- }
1836
- let parsed;
1837
- try {
1838
- parsed = JSON.parse(value);
1839
- } catch (error) {
1840
- console.error("Error parsing JSON:", error);
1841
- return null;
1842
- }
1843
- return parsed;
1844
- },
1845
- [options]
1846
- );
1847
- const readValue = useCallback(() => {
1848
- if (IS_SERVER$4) {
1849
- return null;
1850
- }
1851
- try {
1852
- const raw = window.localStorage.getItem(key);
1853
- return raw ? deserializer(raw) : null;
1854
- } catch (error) {
1855
- console.warn(`Error reading localStorage key \u201C${key}\u201D:`, error);
1856
- return null;
1857
- }
1858
- }, [key, deserializer]);
1859
- const [storedValue, setStoredValue] = useState(() => {
1860
- if (initializeWithValue) {
1861
- return readValue();
1862
- }
1863
- return void 0;
1864
- });
1865
- useEffect(() => {
1866
- setStoredValue(readValue());
1867
- }, [key]);
1868
- const handleStorageChange = useCallback(
1869
- (event) => {
1870
- if (event.key && event.key !== key) {
1871
- return;
1872
- }
1873
- setStoredValue(readValue());
1874
- },
1875
- [key, readValue]
1876
- );
1877
- useEventListener("storage", handleStorageChange);
1878
- useEventListener("local-storage", handleStorageChange);
1879
- return storedValue;
1880
- }
1881
-
1882
- const initialSize = {
1883
- width: void 0,
1884
- height: void 0
1885
- };
1886
- function useResizeObserver(options) {
1887
- const { ref, box = "content-box" } = options;
1888
- const [{ width, height }, setSize] = useState(initialSize);
1889
- const isMounted = useIsMounted();
1890
- const previousSize = useRef({ ...initialSize });
1891
- const onResize = useRef(void 0);
1892
- onResize.current = options.onResize;
1893
- useEffect(() => {
1894
- if (!ref.current) return;
1895
- if (typeof window === "undefined" || !("ResizeObserver" in window)) return;
1896
- const observer = new ResizeObserver(([entry]) => {
1897
- const boxProp = box === "border-box" ? "borderBoxSize" : box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize";
1898
- const newWidth = extractSize(entry, boxProp, "inlineSize");
1899
- const newHeight = extractSize(entry, boxProp, "blockSize");
1900
- const hasChanged = previousSize.current.width !== newWidth || previousSize.current.height !== newHeight;
1901
- if (hasChanged) {
1902
- const newSize = { width: newWidth, height: newHeight };
1903
- previousSize.current.width = newWidth;
1904
- previousSize.current.height = newHeight;
1905
- if (onResize.current) {
1906
- onResize.current(newSize);
1907
- } else {
1908
- if (isMounted()) {
1909
- setSize(newSize);
1910
- }
1911
- }
1912
- }
1913
- });
1914
- observer.observe(ref.current, { box });
1915
- return () => {
1916
- observer.disconnect();
1917
- };
1918
- }, [box, ref, isMounted]);
1919
- return { width, height };
1920
- }
1921
- function extractSize(entry, box, sizeType) {
1922
- if (!entry[box]) {
1923
- if (box === "contentBoxSize") {
1924
- return entry.contentRect[sizeType === "inlineSize" ? "width" : "height"];
1925
- }
1926
- return void 0;
1927
- }
1928
- return Array.isArray(entry[box]) ? entry[box][0][sizeType] : (
1929
- // @ts-ignore Support Firefox's non-standard behavior
1930
- entry[box][sizeType]
1931
- );
1932
- }
1933
-
1934
- const IS_SERVER$3 = typeof window === "undefined";
1935
- function useScreen(options = {}) {
1936
- let { initializeWithValue = true } = options;
1937
- if (IS_SERVER$3) {
1938
- initializeWithValue = false;
1939
- }
1940
- const readScreen = () => {
1941
- if (IS_SERVER$3) {
1942
- return void 0;
1943
- }
1944
- return window.screen;
1945
- };
1946
- const [screen, setScreen] = useState(() => {
1947
- if (initializeWithValue) {
1948
- return readScreen();
1949
- }
1950
- return void 0;
1951
- });
1952
- const debouncedSetScreen = useDebounceCallback(
1953
- setScreen,
1954
- options.debounceDelay
1955
- );
1956
- function handleSize() {
1957
- const newScreen = readScreen();
1958
- const setSize = options.debounceDelay ? debouncedSetScreen : setScreen;
1959
- if (newScreen) {
1960
- const {
1961
- width,
1962
- height,
1963
- availHeight,
1964
- availWidth,
1965
- colorDepth,
1966
- orientation,
1967
- pixelDepth
1968
- } = newScreen;
1969
- setSize({
1970
- width,
1971
- height,
1972
- availHeight,
1973
- availWidth,
1974
- colorDepth,
1975
- orientation,
1976
- pixelDepth
1977
- });
1978
- }
1979
- }
1980
- useEventListener("resize", handleSize);
1981
- useIsomorphicLayoutEffect(() => {
1982
- handleSize();
1983
- }, []);
1984
- return screen;
1985
- }
1986
-
1987
- const cachedScriptStatuses = /* @__PURE__ */ new Map();
1988
- function getScriptNode(src) {
1989
- const node = document.querySelector(
1990
- `script[src="${src}"]`
1991
- );
1992
- const status = node?.getAttribute("data-status");
1993
- return {
1994
- node,
1995
- status
1996
- };
1997
- }
1998
- function useScript(src, options) {
1999
- const [status, setStatus] = useState(() => {
2000
- if (!src || options?.shouldPreventLoad) {
2001
- return "idle";
2002
- }
2003
- if (typeof window === "undefined") {
2004
- return "loading";
2005
- }
2006
- return cachedScriptStatuses.get(src) ?? "loading";
2007
- });
2008
- useEffect(() => {
2009
- if (!src || options?.shouldPreventLoad) {
2010
- return;
2011
- }
2012
- const cachedScriptStatus = cachedScriptStatuses.get(src);
2013
- if (cachedScriptStatus === "ready" || cachedScriptStatus === "error") {
2014
- setStatus(cachedScriptStatus);
2015
- return;
2016
- }
2017
- const script = getScriptNode(src);
2018
- let scriptNode = script.node;
2019
- if (!scriptNode) {
2020
- scriptNode = document.createElement("script");
2021
- scriptNode.src = src;
2022
- scriptNode.async = true;
2023
- if (options?.id) {
2024
- scriptNode.id = options.id;
2025
- }
2026
- scriptNode.setAttribute("data-status", "loading");
2027
- document.body.appendChild(scriptNode);
2028
- const setAttributeFromEvent = (event) => {
2029
- const scriptStatus = event.type === "load" ? "ready" : "error";
2030
- scriptNode?.setAttribute("data-status", scriptStatus);
2031
- };
2032
- scriptNode.addEventListener("load", setAttributeFromEvent);
2033
- scriptNode.addEventListener("error", setAttributeFromEvent);
2034
- } else {
2035
- setStatus(script.status ?? cachedScriptStatus ?? "loading");
2036
- }
2037
- const setStateFromEvent = (event) => {
2038
- const newStatus = event.type === "load" ? "ready" : "error";
2039
- setStatus(newStatus);
2040
- cachedScriptStatuses.set(src, newStatus);
2041
- };
2042
- scriptNode.addEventListener("load", setStateFromEvent);
2043
- scriptNode.addEventListener("error", setStateFromEvent);
2044
- return () => {
2045
- if (scriptNode) {
2046
- scriptNode.removeEventListener("load", setStateFromEvent);
2047
- scriptNode.removeEventListener("error", setStateFromEvent);
2048
- }
2049
- if (scriptNode && options?.removeOnUnmount) {
2050
- scriptNode.remove();
2051
- cachedScriptStatuses.delete(src);
2052
- }
2053
- };
2054
- }, [src, options?.shouldPreventLoad, options?.removeOnUnmount, options?.id]);
2055
- return status;
2056
- }
2057
-
2058
- const IS_SERVER$2 = typeof window === "undefined";
2059
- function useScrollLock(options = {}) {
2060
- const { autoLock = true, lockTarget, widthReflow = true } = options;
2061
- const [isLocked, setIsLocked] = useState(false);
2062
- const target = useRef(null);
2063
- const originalStyle = useRef(null);
2064
- const lock = () => {
2065
- if (target.current) {
2066
- const { overflow, paddingRight } = target.current.style;
2067
- originalStyle.current = { overflow, paddingRight };
2068
- if (widthReflow) {
2069
- const offsetWidth = target.current === document.body ? window.innerWidth : target.current.offsetWidth;
2070
- const currentPaddingRight = parseInt(window.getComputedStyle(target.current).paddingRight, 10) || 0;
2071
- const scrollbarWidth = offsetWidth - target.current.scrollWidth;
2072
- target.current.style.paddingRight = `${scrollbarWidth + currentPaddingRight}px`;
2073
- }
2074
- target.current.style.overflow = "hidden";
2075
- setIsLocked(true);
2076
- }
2077
- };
2078
- const unlock = () => {
2079
- if (target.current && originalStyle.current) {
2080
- target.current.style.overflow = originalStyle.current.overflow;
2081
- if (widthReflow) {
2082
- target.current.style.paddingRight = originalStyle.current.paddingRight;
2083
- }
2084
- }
2085
- setIsLocked(false);
2086
- };
2087
- useIsomorphicLayoutEffect(() => {
2088
- if (IS_SERVER$2) return;
2089
- if (lockTarget) {
2090
- target.current = typeof lockTarget === "string" ? document.querySelector(lockTarget) : lockTarget;
2091
- }
2092
- if (!target.current) {
2093
- target.current = document.body;
2094
- }
2095
- if (autoLock) {
2096
- lock();
2097
- }
2098
- return () => {
2099
- unlock();
2100
- };
2101
- }, [autoLock, lockTarget, widthReflow]);
2102
- return { isLocked, lock, unlock };
2103
- }
2104
-
2105
- const IS_SERVER$1 = typeof window === "undefined";
2106
- function useSessionStorage(key, initialValue, options = {}) {
2107
- const { initializeWithValue = true } = options;
2108
- const serializer = useCallback(
2109
- (value) => {
2110
- if (options.serializer) {
2111
- return options.serializer(value);
2112
- }
2113
- return JSON.stringify(value);
2114
- },
2115
- [options]
2116
- );
2117
- const deserializer = useCallback(
2118
- (value) => {
2119
- if (options.deserializer) {
2120
- return options.deserializer(value);
2121
- }
2122
- if (value === "undefined") {
2123
- return void 0;
2124
- }
2125
- const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
2126
- let parsed;
2127
- try {
2128
- parsed = JSON.parse(value);
2129
- } catch (error) {
2130
- console.error("Error parsing JSON:", error);
2131
- return defaultValue;
2132
- }
2133
- return parsed;
2134
- },
2135
- [options, initialValue]
2136
- );
2137
- const readValue = useCallback(() => {
2138
- const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue;
2139
- if (IS_SERVER$1) {
2140
- return initialValueToUse;
2141
- }
2142
- try {
2143
- const raw = window.sessionStorage.getItem(key);
2144
- return raw ? deserializer(raw) : initialValueToUse;
2145
- } catch (error) {
2146
- console.warn(`Error reading sessionStorage key \u201C${key}\u201D:`, error);
2147
- return initialValueToUse;
2148
- }
2149
- }, [initialValue, key, deserializer]);
2150
- const [storedValue, setStoredValue] = useState(() => {
2151
- if (initializeWithValue) {
2152
- return readValue();
2153
- }
2154
- return initialValue instanceof Function ? initialValue() : initialValue;
2155
- });
2156
- const setValue = useEventCallback((value) => {
2157
- if (IS_SERVER$1) {
2158
- console.warn(
2159
- `Tried setting sessionStorage key \u201C${key}\u201D even though environment is not a client`
2160
- );
2161
- }
2162
- try {
2163
- const newValue = value instanceof Function ? value(readValue()) : value;
2164
- window.sessionStorage.setItem(key, serializer(newValue));
2165
- setStoredValue(newValue);
2166
- window.dispatchEvent(new StorageEvent("session-storage", { key }));
2167
- } catch (error) {
2168
- console.warn(`Error setting sessionStorage key \u201C${key}\u201D:`, error);
2169
- }
2170
- });
2171
- const removeValue = useEventCallback(() => {
2172
- if (IS_SERVER$1) {
2173
- console.warn(
2174
- `Tried removing sessionStorage key \u201C${key}\u201D even though environment is not a client`
2175
- );
2176
- }
2177
- const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
2178
- window.sessionStorage.removeItem(key);
2179
- setStoredValue(defaultValue);
2180
- window.dispatchEvent(new StorageEvent("session-storage", { key }));
2181
- });
2182
- useEffect(() => {
2183
- setStoredValue(readValue());
2184
- }, [key]);
2185
- const handleStorageChange = useCallback(
2186
- (event) => {
2187
- if (event.key && event.key !== key) {
2188
- return;
2189
- }
2190
- setStoredValue(readValue());
2191
- },
2192
- [key, readValue]
2193
- );
2194
- useEventListener("storage", handleStorageChange);
2195
- useEventListener("session-storage", handleStorageChange);
2196
- return [storedValue, setValue, removeValue];
2197
- }
2198
-
2199
- function useStep(maxStep) {
2200
- const [currentStep, setCurrentStep] = useState(1);
2201
- const canGoToNextStep = currentStep + 1 <= maxStep;
2202
- const canGoToPrevStep = currentStep - 1 > 0;
2203
- const setStep = useCallback(
2204
- (step) => {
2205
- const newStep = step instanceof Function ? step(currentStep) : step;
2206
- if (newStep >= 1 && newStep <= maxStep) {
2207
- setCurrentStep(newStep);
2208
- return;
2209
- }
2210
- throw new Error("Step not valid");
2211
- },
2212
- [maxStep, currentStep]
2213
- );
2214
- const goToNextStep = useCallback(() => {
2215
- if (canGoToNextStep) {
2216
- setCurrentStep((step) => step + 1);
2217
- }
2218
- }, [canGoToNextStep]);
2219
- const goToPrevStep = useCallback(() => {
2220
- if (canGoToPrevStep) {
2221
- setCurrentStep((step) => step - 1);
2222
- }
2223
- }, [canGoToPrevStep]);
2224
- const reset = useCallback(() => {
2225
- setCurrentStep(1);
2226
- }, []);
2227
- return [
2228
- currentStep,
2229
- {
2230
- goToNextStep,
2231
- goToPrevStep,
2232
- canGoToNextStep,
2233
- canGoToPrevStep,
2234
- setStep,
2235
- reset
2236
- }
2237
- ];
2238
- }
2239
-
2240
- const COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
2241
- const LOCAL_STORAGE_KEY = "sse-hooks-ternary-dark-mode";
2242
- function useTernaryDarkMode({
2243
- defaultValue = "system",
2244
- localStorageKey = LOCAL_STORAGE_KEY,
2245
- initializeWithValue = true
2246
- } = {}) {
2247
- const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY, { initializeWithValue });
2248
- const [mode, setMode] = useLocalStorage(localStorageKey, defaultValue, {
2249
- initializeWithValue
2250
- });
2251
- const isDarkMode = mode === "dark" || mode === "system" && isDarkOS;
2252
- const toggleTernaryDarkMode = () => {
2253
- const modes = ["light", "system", "dark"];
2254
- setMode((prevMode) => {
2255
- const nextIndex = (modes.indexOf(prevMode) + 1) % modes.length;
2256
- return modes[nextIndex];
2257
- });
2258
- };
2259
- return {
2260
- isDarkMode,
2261
- ternaryDarkMode: mode,
2262
- setTernaryDarkMode: setMode,
2263
- toggleTernaryDarkMode
2264
- };
2265
- }
2266
-
2267
- function useTimeout(callback, delay) {
2268
- const savedCallback = useRef(callback);
2269
- useIsomorphicLayoutEffect(() => {
2270
- savedCallback.current = callback;
2271
- }, [callback]);
2272
- useEffect(() => {
2273
- if (!delay && delay !== 0) {
2274
- return;
2275
- }
2276
- const id = setTimeout(() => {
2277
- savedCallback.current();
2278
- }, delay);
2279
- return () => {
2280
- clearTimeout(id);
2281
- };
2282
- }, [delay]);
2283
- }
2284
-
2285
- function useToggle(defaultValue) {
2286
- const [value, setValue] = useState(!!defaultValue);
2287
- const toggle = useCallback(() => {
2288
- setValue((x) => !x);
2289
- }, []);
2290
- return [value, toggle, setValue];
2291
- }
2292
-
2293
- const DEFAULT_CONSTRAINTS = {
2294
- video: true,
2295
- audio: true
2296
- };
2297
- const useUserMedia = (initialConstraints = DEFAULT_CONSTRAINTS) => {
2298
- const [stream, setStream] = useState(null);
2299
- const [error, setError] = useState(null);
2300
- const [isLoading, setIsLoading] = useState(false);
2301
- const streamRef = useRef(null);
2302
- const isSupported = typeof navigator !== "undefined" && "mediaDevices" in navigator && "getUserMedia" in navigator.mediaDevices;
2303
- const stopCapture = useCallback(() => {
2304
- if (streamRef.current) {
2305
- streamRef.current.getTracks().forEach((track) => {
2306
- track.stop();
2307
- });
2308
- streamRef.current = null;
2309
- setStream(null);
2310
- }
2311
- setError(null);
2312
- }, []);
2313
- const startCapture = useCallback(
2314
- async (constraints = initialConstraints) => {
2315
- if (!isSupported) {
2316
- setError("getUserMedia is not supported in this browser");
2317
- return;
2318
- }
2319
- stopCapture();
2320
- setIsLoading(true);
2321
- setError(null);
2322
- try {
2323
- const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
2324
- streamRef.current = mediaStream;
2325
- setStream(mediaStream);
2326
- } catch (err) {
2327
- let errorMessage = "Failed to access media devices";
2328
- if (err instanceof Error) {
2329
- switch (err.name) {
2330
- case "NotAllowedError":
2331
- errorMessage = "Permission denied. Please allow camera/microphone access.";
2332
- break;
2333
- case "NotFoundError":
2334
- errorMessage = "No camera or microphone found.";
2335
- break;
2336
- case "NotReadableError":
2337
- errorMessage = "Camera or microphone is already in use.";
2338
- break;
2339
- case "OverconstrainedError":
2340
- errorMessage = "Camera or microphone constraints cannot be satisfied.";
2341
- break;
2342
- case "SecurityError":
2343
- errorMessage = "Security error. Make sure you're using HTTPS.";
2344
- break;
2345
- case "AbortError":
2346
- errorMessage = "Media access was aborted.";
2347
- break;
2348
- default:
2349
- errorMessage = err.message || errorMessage;
2350
- }
2351
- }
2352
- setError(errorMessage);
2353
- } finally {
2354
- setIsLoading(false);
2355
- }
2356
- },
2357
- [isSupported, initialConstraints, stopCapture]
2358
- );
2359
- useEffect(() => {
2360
- return () => {
2361
- stopCapture();
2362
- };
2363
- }, [stopCapture]);
2364
- return {
2365
- stream,
2366
- error,
2367
- isLoading,
2368
- isSupported,
2369
- startCapture,
2370
- stopCapture
2371
- };
2372
- };
2373
-
2374
- const IS_SERVER = typeof window === "undefined";
2375
- function useWindowSize(options = {}) {
2376
- let { initializeWithValue = true } = options;
2377
- if (IS_SERVER) {
2378
- initializeWithValue = false;
2379
- }
2380
- const [windowSize, setWindowSize] = useState(() => {
2381
- if (initializeWithValue) {
2382
- return {
2383
- width: window.innerWidth,
2384
- height: window.innerHeight
2385
- };
2386
- }
2387
- return {
2388
- width: void 0,
2389
- height: void 0
2390
- };
2391
- });
2392
- const debouncedSetWindowSize = useDebounceCallback(
2393
- setWindowSize,
2394
- options.debounceDelay
2395
- );
2396
- function handleSize() {
2397
- const setSize = options.debounceDelay ? debouncedSetWindowSize : setWindowSize;
2398
- setSize({
2399
- width: window.innerWidth,
2400
- height: window.innerHeight
2401
- });
2402
- }
2403
- useEventListener("resize", handleSize);
2404
- useIsomorphicLayoutEffect(() => {
2405
- handleSize();
2406
- }, []);
2407
- return windowSize;
2408
- }
2409
-
2410
- export { ComputedRefImpl, Dep, EffectFlags, Link, ReactiveFlags, RefImpl, TrackOpTypes, TriggerOpTypes, __DEV__, batch, computed, endBatch, extend, getActiveSub, globalVersion, isArray, isFunction, isIntegerKey, isMap, isObject, isSymbol, ref, refreshComputed, setActiveSub, shouldTrack, startBatch, targetMap, track, useAudioRecorder, useBatteryHook as useBattery, useBoolean, useClickAnyWhere, useClickAway, useComputed, useCookie, useCopyToClipboard, useCountdown, useCounter, useDarkMode, useDebounceCallback, useDebounceValue, useDelete, useDocumentTitle, useEventCallback, useEventListener, useFetch, useForkRef, useGet, useHover, useIndexedDB, useIntersectionObserver, useInterval, useIsClient, useIsMounted, useIsomorphicLayoutEffect, useLocalStorage, useMap, useMediaQuery, useMediaSession, usePost, usePut, useReactive, useReadLocalStorage, useResizeObserver, useScreen, useScript, useScrollLock, useSessionStorage, useStep, useTernaryDarkMode, useTimeout, useToggle, useUnmount, useUserMedia, useWindowSize, withDefaults };