@jswork/howler-audio-manager 1.0.1 → 1.0.2

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/src/manager.ts CHANGED
@@ -287,6 +287,10 @@ export class AudioManager<K extends string | number = string> {
287
287
  }
288
288
  }
289
289
 
290
+ getGlobalVolume(): number {
291
+ return this.globalVolume;
292
+ }
293
+
290
294
  setGlobalVolume(volume: number): void {
291
295
  this.globalVolume = volume;
292
296
  this.HowlerInstance.volume(volume);
package/src/react.tsx ADDED
@@ -0,0 +1,98 @@
1
+ import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import { AudioManager } from './manager';
4
+ import type { AudioEventType, AudioEventPayload } from './types';
5
+
6
+ const AudioManagerContext = createContext<AudioManager<any> | null>(null);
7
+
8
+ export interface AudioManagerProviderProps<K extends string | number = string> {
9
+ manager: AudioManager<K>;
10
+ children?: ReactNode;
11
+ }
12
+
13
+ export function AudioManagerProvider<K extends string | number = string>({
14
+ manager,
15
+ children
16
+ }: AudioManagerProviderProps<K>) {
17
+ return (
18
+ <AudioManagerContext.Provider value={manager}>
19
+ {children}
20
+ </AudioManagerContext.Provider>
21
+ );
22
+ }
23
+
24
+ export function useAudio<K extends string | number = string>(): AudioManager<K> {
25
+ const manager = useContext(AudioManagerContext);
26
+ if (!manager) {
27
+ throw new Error('useAudio must be used within an <AudioManagerProvider>.');
28
+ }
29
+ return manager as unknown as AudioManager<K>;
30
+ }
31
+
32
+ export function useAudioEvent<K extends string | number = string>(
33
+ event: AudioEventType,
34
+ handler: (payload: AudioEventPayload<K>) => void
35
+ ): void {
36
+ const manager = useAudio<K>();
37
+ const handlerRef = useRef(handler);
38
+ handlerRef.current = handler;
39
+
40
+ useEffect(() => {
41
+ const subscription = manager.on(event, (payload) => handlerRef.current(payload));
42
+ return () => subscription.destroy();
43
+ }, [manager, event]);
44
+ }
45
+
46
+ export function useGlobalVolume(): [number, (volume: number) => void] {
47
+ const manager = useAudio();
48
+ const [volume, setVolumeState] = useState(() => manager.getGlobalVolume());
49
+
50
+ const setVolume = useCallback(
51
+ (next: number) => {
52
+ manager.setGlobalVolume(next);
53
+ setVolumeState(next);
54
+ },
55
+ [manager]
56
+ );
57
+
58
+ return [volume, setVolume];
59
+ }
60
+
61
+ export interface PreloadState {
62
+ status: 'idle' | 'loading' | 'loaded' | 'error';
63
+ error: unknown;
64
+ }
65
+
66
+ export function usePreload(urls: string[]): PreloadState {
67
+ const manager = useAudio();
68
+ const urlsKey = JSON.stringify(urls);
69
+ const [state, setState] = useState<PreloadState>({ status: 'idle', error: null });
70
+
71
+ useEffect(() => {
72
+ const list: string[] = JSON.parse(urlsKey);
73
+ if (list.length === 0) {
74
+ setState({ status: 'idle', error: null });
75
+ return;
76
+ }
77
+
78
+ let cancelled = false;
79
+ setState({ status: 'loading', error: null });
80
+
81
+ manager.preloadUrls(list).then(
82
+ () => {
83
+ if (!cancelled) setState({ status: 'loaded', error: null });
84
+ },
85
+ (error) => {
86
+ if (!cancelled) setState({ status: 'error', error });
87
+ }
88
+ );
89
+
90
+ return () => {
91
+ cancelled = true;
92
+ };
93
+ }, [manager, urlsKey]);
94
+
95
+ return state;
96
+ }
97
+
98
+