@arraypress/waveform-player-react 0.1.1 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +11 -2
- package/dist/index.cjs +40 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -168
- package/dist/index.d.ts +43 -168
- package/dist/index.js +41 -10
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to `@arraypress/waveform-player-react` are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Public types are now adopted from the core
|
|
13
|
+
`@arraypress/waveform-player` (v1.8.0+), which ships a hand-authored
|
|
14
|
+
`index.d.ts`. The shared option surface (`WaveformStyle`,
|
|
15
|
+
`ColorPreset`, `AudioMode`, `AudioPreload`, `ButtonAlign`,
|
|
16
|
+
`WaveformMarker`, `WaveformPeaks`) is re-exported from the core, and
|
|
17
|
+
`WaveformPlayerProps` now `extends` the core's `WaveformPlayerOptions`
|
|
18
|
+
instead of re-declaring every field. The core is the single source of
|
|
19
|
+
truth, so the wrapper's types can no longer drift out of sync. Bumped
|
|
20
|
+
the `@arraypress/waveform-player` peer (and dev) dependency to
|
|
21
|
+
`^1.8.0`.
|
|
22
|
+
- Callback props (`onLoad`, `onPlay`, `onPause`, `onEnd`,
|
|
23
|
+
`onTimeUpdate`, `onError`) and the `WaveformPlayerHandle.instance`
|
|
24
|
+
accessor are now typed with the core's `WaveformPlayer` class instead
|
|
25
|
+
of `unknown`.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `accessibleSeek`, `seekLabel`, `barRadius`, and gradient-array
|
|
30
|
+
waveform / progress colours (`string[]`) are now exposed as typed
|
|
31
|
+
props — picked up for free by extending the core options and wired
|
|
32
|
+
through to the underlying player.
|
|
33
|
+
|
|
34
|
+
### Removed
|
|
35
|
+
|
|
36
|
+
- Deleted `src/core-module-shim.d.ts`, the loose ambient
|
|
37
|
+
`declare module '@arraypress/waveform-player'` shim that only existed
|
|
38
|
+
while the core had no shipped types.
|
|
39
|
+
|
|
8
40
|
## [0.1.1] — 2026-06-27
|
|
9
41
|
|
|
10
42
|
### Changed
|
package/README.md
CHANGED
|
@@ -35,9 +35,14 @@ The wrapper does **not** import the CSS for you — your bundler should own that
|
|
|
35
35
|
### Basic
|
|
36
36
|
|
|
37
37
|
```tsx
|
|
38
|
-
<WaveformPlayer
|
|
38
|
+
<WaveformPlayer src="/audio/track.mp3" />
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
> **Naming note.** `src` is shorthand for `url`. The visual style is `waveformStyle`
|
|
42
|
+
> — **not** `style`, which (as in any React component) is the host element's CSS
|
|
43
|
+
> object. So `style={{ minHeight: 64 }}` styles the container; `waveformStyle="bars"`
|
|
44
|
+
> picks the waveform look.
|
|
45
|
+
|
|
41
46
|
### With metadata + chosen style
|
|
42
47
|
|
|
43
48
|
```tsx
|
|
@@ -145,7 +150,8 @@ Every library option surfaces as a typed prop. See the full table in [`src/types
|
|
|
145
150
|
|
|
146
151
|
| Prop | Type | Default |
|
|
147
152
|
| ----------- | ------------------------------------- | ------------ |
|
|
148
|
-
| `url` | `string`
|
|
153
|
+
| `url` | `string` | — |
|
|
154
|
+
| `src` | `string` — shorthand alias for `url` (`url` wins if both set) | — |
|
|
149
155
|
| `audioMode` | `'self' \| 'external'` | `'self'` |
|
|
150
156
|
| `preload` | `'auto' \| 'metadata' \| 'none'` | `'metadata'` |
|
|
151
157
|
|
|
@@ -190,6 +196,9 @@ All optional. `colorPreset` controls the auto theme; any individual colour wins
|
|
|
190
196
|
| `singlePlay` | `boolean` | `true` |
|
|
191
197
|
| `playOnSeek` | `boolean` | `true` |
|
|
192
198
|
| `enableMediaSession`| `boolean` | `true` |
|
|
199
|
+
| `accessibleSeek` | `boolean` | `true` |
|
|
200
|
+
| `seekLabel` | `string` | — |
|
|
201
|
+
| `errorText` | `string` | `'Unable to load audio'` |
|
|
193
202
|
|
|
194
203
|
### Markers + metadata
|
|
195
204
|
|
package/dist/index.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
9
9
|
function buildLibraryOptions(props) {
|
|
10
10
|
const opts = {};
|
|
11
11
|
if (props.url !== void 0) opts.url = props.url;
|
|
12
|
+
else if (props.src !== void 0) opts.url = props.src;
|
|
12
13
|
if (props.audioMode !== void 0) opts.audioMode = props.audioMode;
|
|
13
14
|
if (props.preload !== void 0) opts.preload = props.preload;
|
|
14
15
|
if (props.waveformStyle !== void 0) opts.waveformStyle = props.waveformStyle;
|
|
@@ -16,6 +17,7 @@ function buildLibraryOptions(props) {
|
|
|
16
17
|
if (props.samples !== void 0) opts.samples = props.samples;
|
|
17
18
|
if (props.barWidth !== void 0) opts.barWidth = props.barWidth;
|
|
18
19
|
if (props.barSpacing !== void 0) opts.barSpacing = props.barSpacing;
|
|
20
|
+
if (props.barRadius !== void 0) opts.barRadius = props.barRadius;
|
|
19
21
|
if (props.waveform !== void 0 && props.waveform !== null) {
|
|
20
22
|
opts.waveform = props.waveform;
|
|
21
23
|
}
|
|
@@ -36,7 +38,13 @@ function buildLibraryOptions(props) {
|
|
|
36
38
|
if (props.showTime !== void 0) opts.showTime = props.showTime;
|
|
37
39
|
if (props.showHoverTime !== void 0) opts.showHoverTime = props.showHoverTime;
|
|
38
40
|
if (props.showBPM !== void 0) opts.showBPM = props.showBPM;
|
|
41
|
+
if (props.bpm !== void 0) opts.bpm = props.bpm;
|
|
39
42
|
if (props.buttonAlign !== void 0) opts.buttonAlign = props.buttonAlign;
|
|
43
|
+
if (props.layout !== void 0) opts.layout = props.layout;
|
|
44
|
+
if (props.buttonStyle !== void 0) opts.buttonStyle = props.buttonStyle;
|
|
45
|
+
if (props.accessibleSeek !== void 0) opts.accessibleSeek = props.accessibleSeek;
|
|
46
|
+
if (props.seekLabel !== void 0) opts.seekLabel = props.seekLabel;
|
|
47
|
+
if (props.errorText !== void 0) opts.errorText = props.errorText;
|
|
40
48
|
if (props.markers !== void 0) opts.markers = props.markers;
|
|
41
49
|
if (props.showMarkers !== void 0) opts.showMarkers = props.showMarkers;
|
|
42
50
|
if (props.title !== void 0) opts.title = props.title;
|
|
@@ -49,18 +57,30 @@ function buildLibraryOptions(props) {
|
|
|
49
57
|
if (props.enableMediaSession !== void 0) opts.enableMediaSession = props.enableMediaSession;
|
|
50
58
|
if (props.playIcon !== void 0) opts.playIcon = props.playIcon;
|
|
51
59
|
if (props.pauseIcon !== void 0) opts.pauseIcon = props.pauseIcon;
|
|
52
|
-
if (props.onLoad) opts.onLoad = props.onLoad;
|
|
53
|
-
if (props.onPlay) opts.onPlay = props.onPlay;
|
|
54
|
-
if (props.onPause) opts.onPause = props.onPause;
|
|
55
|
-
if (props.onEnd) opts.onEnd = props.onEnd;
|
|
56
|
-
if (props.onTimeUpdate) opts.onTimeUpdate = props.onTimeUpdate;
|
|
57
|
-
if (props.onError) opts.onError = props.onError;
|
|
58
60
|
return opts;
|
|
59
61
|
}
|
|
60
62
|
var WaveformPlayer = react.forwardRef(
|
|
61
63
|
function WaveformPlayer2(props, ref) {
|
|
62
64
|
const containerRef = react.useRef(null);
|
|
63
65
|
const instanceRef = react.useRef(null);
|
|
66
|
+
const callbacksRef = react.useRef({
|
|
67
|
+
onLoad: props.onLoad,
|
|
68
|
+
onPlay: props.onPlay,
|
|
69
|
+
onPause: props.onPause,
|
|
70
|
+
onEnd: props.onEnd,
|
|
71
|
+
onTimeUpdate: props.onTimeUpdate,
|
|
72
|
+
onError: props.onError
|
|
73
|
+
});
|
|
74
|
+
react.useLayoutEffect(() => {
|
|
75
|
+
callbacksRef.current = {
|
|
76
|
+
onLoad: props.onLoad,
|
|
77
|
+
onPlay: props.onPlay,
|
|
78
|
+
onPause: props.onPause,
|
|
79
|
+
onEnd: props.onEnd,
|
|
80
|
+
onTimeUpdate: props.onTimeUpdate,
|
|
81
|
+
onError: props.onError
|
|
82
|
+
};
|
|
83
|
+
});
|
|
64
84
|
react.useEffect(() => {
|
|
65
85
|
let cancelled = false;
|
|
66
86
|
let localInstance = null;
|
|
@@ -71,15 +91,21 @@ var WaveformPlayer = react.forwardRef(
|
|
|
71
91
|
const WaveformPlayerClass = mod.default ?? mod.WaveformPlayer;
|
|
72
92
|
if (typeof WaveformPlayerClass !== "function") {
|
|
73
93
|
console.error(
|
|
74
|
-
"[
|
|
94
|
+
"[WaveformPlayerReact] Failed to resolve WaveformPlayer constructor from module."
|
|
75
95
|
);
|
|
76
96
|
return;
|
|
77
97
|
}
|
|
78
98
|
const opts = buildLibraryOptions(props);
|
|
99
|
+
opts.onLoad = (instance) => callbacksRef.current.onLoad?.(instance);
|
|
100
|
+
opts.onPlay = (instance) => callbacksRef.current.onPlay?.(instance);
|
|
101
|
+
opts.onPause = (instance) => callbacksRef.current.onPause?.(instance);
|
|
102
|
+
opts.onEnd = (instance) => callbacksRef.current.onEnd?.(instance);
|
|
103
|
+
opts.onTimeUpdate = (currentTime, duration, instance) => callbacksRef.current.onTimeUpdate?.(currentTime, duration, instance);
|
|
104
|
+
opts.onError = (error, instance) => callbacksRef.current.onError?.(error, instance);
|
|
79
105
|
localInstance = new WaveformPlayerClass(container, opts);
|
|
80
106
|
instanceRef.current = localInstance;
|
|
81
107
|
}).catch((err) => {
|
|
82
|
-
console.error("[
|
|
108
|
+
console.error("[WaveformPlayerReact] Failed to load library:", err);
|
|
83
109
|
});
|
|
84
110
|
return () => {
|
|
85
111
|
cancelled = true;
|
|
@@ -88,13 +114,14 @@ var WaveformPlayer = react.forwardRef(
|
|
|
88
114
|
try {
|
|
89
115
|
current.destroy();
|
|
90
116
|
} catch (err) {
|
|
91
|
-
console.warn("[
|
|
117
|
+
console.warn("[WaveformPlayerReact] destroy() threw:", err);
|
|
92
118
|
}
|
|
93
119
|
}
|
|
94
120
|
instanceRef.current = null;
|
|
95
121
|
};
|
|
96
122
|
}, [
|
|
97
123
|
props.url,
|
|
124
|
+
props.src,
|
|
98
125
|
props.audioMode,
|
|
99
126
|
props.preload,
|
|
100
127
|
props.waveformStyle,
|
|
@@ -102,6 +129,7 @@ var WaveformPlayer = react.forwardRef(
|
|
|
102
129
|
props.samples,
|
|
103
130
|
props.barWidth,
|
|
104
131
|
props.barSpacing,
|
|
132
|
+
props.barRadius,
|
|
105
133
|
props.waveform,
|
|
106
134
|
props.colorPreset,
|
|
107
135
|
props.waveformColor,
|
|
@@ -121,6 +149,9 @@ var WaveformPlayer = react.forwardRef(
|
|
|
121
149
|
props.showHoverTime,
|
|
122
150
|
props.showBPM,
|
|
123
151
|
props.buttonAlign,
|
|
152
|
+
props.accessibleSeek,
|
|
153
|
+
props.seekLabel,
|
|
154
|
+
props.errorText,
|
|
124
155
|
props.markers,
|
|
125
156
|
props.showMarkers,
|
|
126
157
|
props.title,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/WaveformPlayer.tsx"],"names":["forwardRef","WaveformPlayer","useRef","useEffect","useImperativeHandle","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * WaveformPlayer.tsx\n * ------------------\n *\n * React wrapper around `@arraypress/waveform-player`. Mounts a player\n * instance into a `<div>` on first render, tears it down on unmount,\n * and re-mounts when any \"identity\" prop changes (the props whose\n * change requires the library to start over from scratch — `url`,\n * `audioMode`).\n *\n * For non-identity props, this component currently re-creates the\n * instance as well, which is simpler than diffing every option and\n * calling the right granular updater. The trade-off is acceptable\n * because:\n *\n * - The library re-uses any cached waveform data keyed by URL, so\n * re-mounts on the same URL are cheap.\n * - Per-render churn on a player widget is rare in practice.\n *\n * If you need finer control — imperative `loadTrack()`, `seekTo()`,\n * `setVolume()`, etc. — grab the instance through a `ref`:\n *\n * ```tsx\n * import { useRef, useEffect } from 'react';\n * import { WaveformPlayer, type WaveformPlayerHandle } from '@arraypress/waveform-player-react';\n *\n * function MyPlayer() {\n * const ref = useRef<WaveformPlayerHandle>(null);\n * return (\n * <>\n * <WaveformPlayer ref={ref} url=\"/audio/track.mp3\" />\n * <button onClick={() => ref.current?.seekTo(60)}>Jump to 1:00</button>\n * </>\n * );\n * }\n * ```\n *\n * ## Library setup\n *\n * This component does **not** load the core library's CSS for you.\n * Import it once at your app entry:\n *\n * ```ts\n * import '@arraypress/waveform-player/dist/waveform-player.css';\n * ```\n *\n * The library's JS is imported dynamically inside `useEffect` so it\n * only loads on the client (SSR-safe).\n *\n * @module WaveformPlayer\n */\nimport {\n\tforwardRef,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\ttype ForwardedRef,\n} from 'react';\nimport type { WaveformPlayerHandle, WaveformPlayerProps } from './types';\n\n/**\n * Convert a `WaveformPlayerProps` object into the option shape the\n * core library accepts. Most fields pass straight through; this\n * helper exists so the option-building logic is testable on its own\n * and the component body stays focused on lifecycle.\n *\n * @param props - The component's resolved props.\n * @returns An options object to pass into `new WaveformPlayer(el, …)`.\n */\nfunction buildLibraryOptions(props: WaveformPlayerProps): Record<string, unknown> {\n\tconst opts: Record<string, unknown> = {};\n\n\t/* Audio source */\n\tif (props.url !== undefined) opts.url = props.url;\n\tif (props.audioMode !== undefined) opts.audioMode = props.audioMode;\n\tif (props.preload !== undefined) opts.preload = props.preload;\n\n\t/* Waveform visualisation */\n\tif (props.waveformStyle !== undefined) opts.waveformStyle = props.waveformStyle;\n\tif (props.height !== undefined) opts.height = props.height;\n\tif (props.samples !== undefined) opts.samples = props.samples;\n\tif (props.barWidth !== undefined) opts.barWidth = props.barWidth;\n\tif (props.barSpacing !== undefined) opts.barSpacing = props.barSpacing;\n\tif (props.waveform !== undefined && props.waveform !== null) {\n\t\topts.waveform = props.waveform;\n\t}\n\n\t/* Colours */\n\tif (props.colorPreset !== undefined) opts.colorPreset = props.colorPreset;\n\tif (props.waveformColor !== undefined) opts.waveformColor = props.waveformColor;\n\tif (props.progressColor !== undefined) opts.progressColor = props.progressColor;\n\tif (props.buttonColor !== undefined) opts.buttonColor = props.buttonColor;\n\tif (props.buttonHoverColor !== undefined) opts.buttonHoverColor = props.buttonHoverColor;\n\tif (props.textColor !== undefined) opts.textColor = props.textColor;\n\tif (props.textSecondaryColor !== undefined) opts.textSecondaryColor = props.textSecondaryColor;\n\tif (props.backgroundColor !== undefined) opts.backgroundColor = props.backgroundColor;\n\tif (props.borderColor !== undefined) opts.borderColor = props.borderColor;\n\n\t/* Playback controls */\n\tif (props.playbackRate !== undefined) opts.playbackRate = props.playbackRate;\n\tif (props.showPlaybackSpeed !== undefined) opts.showPlaybackSpeed = props.showPlaybackSpeed;\n\tif (props.playbackRates !== undefined) opts.playbackRates = props.playbackRates;\n\n\t/* UI toggles */\n\tif (props.showControls !== undefined) opts.showControls = props.showControls;\n\tif (props.showInfo !== undefined) opts.showInfo = props.showInfo;\n\tif (props.showTime !== undefined) opts.showTime = props.showTime;\n\tif (props.showHoverTime !== undefined) opts.showHoverTime = props.showHoverTime;\n\tif (props.showBPM !== undefined) opts.showBPM = props.showBPM;\n\tif (props.buttonAlign !== undefined) opts.buttonAlign = props.buttonAlign;\n\n\t/* Markers */\n\tif (props.markers !== undefined) opts.markers = props.markers;\n\tif (props.showMarkers !== undefined) opts.showMarkers = props.showMarkers;\n\n\t/* Content metadata */\n\tif (props.title !== undefined) opts.title = props.title;\n\tif (props.subtitle !== undefined) opts.subtitle = props.subtitle;\n\tif (props.artwork !== undefined) opts.artwork = props.artwork;\n\tif (props.album !== undefined) opts.album = props.album;\n\n\t/* Behaviour */\n\tif (props.autoplay !== undefined) opts.autoplay = props.autoplay;\n\tif (props.singlePlay !== undefined) opts.singlePlay = props.singlePlay;\n\tif (props.playOnSeek !== undefined) opts.playOnSeek = props.playOnSeek;\n\tif (props.enableMediaSession !== undefined) opts.enableMediaSession = props.enableMediaSession;\n\n\t/* Icons */\n\tif (props.playIcon !== undefined) opts.playIcon = props.playIcon;\n\tif (props.pauseIcon !== undefined) opts.pauseIcon = props.pauseIcon;\n\n\t/* Callbacks — wired into the library's option-level callbacks\n\t * rather than custom-event listeners. The library invokes\n\t * these synchronously on the respective state change. */\n\tif (props.onLoad) opts.onLoad = props.onLoad;\n\tif (props.onPlay) opts.onPlay = props.onPlay;\n\tif (props.onPause) opts.onPause = props.onPause;\n\tif (props.onEnd) opts.onEnd = props.onEnd;\n\tif (props.onTimeUpdate) opts.onTimeUpdate = props.onTimeUpdate;\n\tif (props.onError) opts.onError = props.onError;\n\n\treturn opts;\n}\n\n/**\n * `WaveformPlayer` — React component wrapping\n * `@arraypress/waveform-player`.\n *\n * Render at the spot you want a waveform-driven audio player to\n * appear. The container `<div>` is rendered immediately for layout;\n * the actual player UI hydrates in once the library loads\n * client-side.\n *\n * @example Basic\n * <WaveformPlayer url=\"/audio/track.mp3\" title=\"My Track\" />\n *\n * @example With ref for imperative control\n * const ref = useRef<WaveformPlayerHandle>(null);\n * <WaveformPlayer ref={ref} url={url} />\n * <button onClick={() => ref.current?.togglePlay()}>Play/Pause</button>\n */\nexport const WaveformPlayer = forwardRef<WaveformPlayerHandle, WaveformPlayerProps>(\n\tfunction WaveformPlayer(props, ref: ForwardedRef<WaveformPlayerHandle>) {\n\t\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\t\tconst instanceRef = useRef<unknown>(null);\n\n\t\t/**\n\t\t * Mount / re-mount lifecycle.\n\t\t *\n\t\t * The dep array intentionally contains EVERY prop the library\n\t\t * uses at construction time. When any of them change, this\n\t\t * effect tears down the old instance and creates a new one\n\t\t * with the updated options. That's simpler and more correct\n\t\t * than trying to partial-update the live instance, and the\n\t\t * library has built-in caches (waveform peaks keyed by URL)\n\t\t * that make same-URL re-mounts cheap.\n\t\t */\n\t\tuseEffect(() => {\n\t\t\tlet cancelled = false;\n\t\t\tlet localInstance: { destroy?: () => void } | null = null;\n\n\t\t\t/* The library is browser-only. Defer the import until we're\n\t\t\t * actually mounting client-side so SSR / RSC don't try to\n\t\t\t * evaluate the audio + canvas + fetch surface on the server.\n\t\t\t */\n\t\t\tvoid import('@arraypress/waveform-player')\n\t\t\t\t.then((mod) => {\n\t\t\t\t\tif (cancelled) return;\n\t\t\t\t\tconst container = containerRef.current;\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst WaveformPlayerClass = (mod.default ?? (mod as { WaveformPlayer?: unknown }).WaveformPlayer) as {\n\t\t\t\t\t\tnew (el: HTMLElement, opts: Record<string, unknown>): { destroy?: () => void };\n\t\t\t\t\t};\n\n\t\t\t\t\tif (typeof WaveformPlayerClass !== 'function') {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t'[waveform-player-react] Failed to resolve WaveformPlayer constructor from module.'\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst opts = buildLibraryOptions(props);\n\t\t\t\t\tlocalInstance = new WaveformPlayerClass(container, opts);\n\t\t\t\t\tinstanceRef.current = localInstance;\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error('[waveform-player-react] Failed to load library:', err);\n\t\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tcancelled = true;\n\t\t\t\tconst current = localInstance ?? (instanceRef.current as { destroy?: () => void } | null);\n\t\t\t\tif (current && typeof current.destroy === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcurrent.destroy();\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconsole.warn('[waveform-player-react] destroy() threw:', err);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinstanceRef.current = null;\n\t\t\t};\n\t\t\t/* Re-mount on any prop change. Listed exhaustively rather\n\t\t\t * than spread to make the intent explicit and to keep the\n\t\t\t * lint rule happy. Callbacks intentionally NOT in deps:\n\t\t\t * a parent re-rendering with a fresh inline function\n\t\t\t * shouldn't tear the player down. */\n\t\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t\t}, [\n\t\t\tprops.url,\n\t\t\tprops.audioMode,\n\t\t\tprops.preload,\n\t\t\tprops.waveformStyle,\n\t\t\tprops.height,\n\t\t\tprops.samples,\n\t\t\tprops.barWidth,\n\t\t\tprops.barSpacing,\n\t\t\tprops.waveform,\n\t\t\tprops.colorPreset,\n\t\t\tprops.waveformColor,\n\t\t\tprops.progressColor,\n\t\t\tprops.buttonColor,\n\t\t\tprops.buttonHoverColor,\n\t\t\tprops.textColor,\n\t\t\tprops.textSecondaryColor,\n\t\t\tprops.backgroundColor,\n\t\t\tprops.borderColor,\n\t\t\tprops.playbackRate,\n\t\t\tprops.showPlaybackSpeed,\n\t\t\tprops.playbackRates,\n\t\t\tprops.showControls,\n\t\t\tprops.showInfo,\n\t\t\tprops.showTime,\n\t\t\tprops.showHoverTime,\n\t\t\tprops.showBPM,\n\t\t\tprops.buttonAlign,\n\t\t\tprops.markers,\n\t\t\tprops.showMarkers,\n\t\t\tprops.title,\n\t\t\tprops.subtitle,\n\t\t\tprops.artwork,\n\t\t\tprops.album,\n\t\t\tprops.autoplay,\n\t\t\tprops.singlePlay,\n\t\t\tprops.playOnSeek,\n\t\t\tprops.enableMediaSession,\n\t\t\tprops.playIcon,\n\t\t\tprops.pauseIcon,\n\t\t]);\n\n\t\t/**\n\t\t * Expose an imperative handle on the forwarded ref. Each\n\t\t * method is a thin pass-through to the live instance — if the\n\t\t * instance hasn't mounted yet (still loading async), calls are\n\t\t * no-ops (`pause`, `seekTo`, etc. return `undefined`).\n\t\t */\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tplay() {\n\t\t\t\t\tconst inst = instanceRef.current as { play?: () => Promise<void> | undefined } | null;\n\t\t\t\t\treturn inst?.play?.();\n\t\t\t\t},\n\t\t\t\tpause() {\n\t\t\t\t\tconst inst = instanceRef.current as { pause?: () => void } | null;\n\t\t\t\t\tinst?.pause?.();\n\t\t\t\t},\n\t\t\t\ttogglePlay() {\n\t\t\t\t\tconst inst = instanceRef.current as { togglePlay?: () => void } | null;\n\t\t\t\t\tinst?.togglePlay?.();\n\t\t\t\t},\n\t\t\t\tseekTo(seconds) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekTo?: (s: number) => void } | null;\n\t\t\t\t\tinst?.seekTo?.(seconds);\n\t\t\t\t},\n\t\t\t\tseekToPercent(percent) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekToPercent?: (p: number) => void } | null;\n\t\t\t\t\tinst?.seekToPercent?.(percent);\n\t\t\t\t},\n\t\t\t\tsetVolume(volume) {\n\t\t\t\t\tconst inst = instanceRef.current as { setVolume?: (v: number) => void } | null;\n\t\t\t\t\tinst?.setVolume?.(volume);\n\t\t\t\t},\n\t\t\t\tsetPlaybackRate(rate) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlaybackRate?: (r: number) => void } | null;\n\t\t\t\t\tinst?.setPlaybackRate?.(rate);\n\t\t\t\t},\n\t\t\t\tsetPlayingState(playing) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlayingState?: (p: boolean) => void } | null;\n\t\t\t\t\tinst?.setPlayingState?.(playing);\n\t\t\t\t},\n\t\t\t\tsetProgress(currentTime, duration) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tsetProgress?: (c: number, d: number) => void;\n\t\t\t\t\t} | null;\n\t\t\t\t\tinst?.setProgress?.(currentTime, duration);\n\t\t\t\t},\n\t\t\t\tasync loadTrack(url, title, subtitle, options) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tloadTrack?: (\n\t\t\t\t\t\t\tu: string,\n\t\t\t\t\t\t\tt?: string,\n\t\t\t\t\t\t\ts?: string,\n\t\t\t\t\t\t\to?: Record<string, unknown>\n\t\t\t\t\t\t) => Promise<void>;\n\t\t\t\t\t} | null;\n\t\t\t\t\tif (!inst?.loadTrack) return;\n\t\t\t\t\tawait inst.loadTrack(url, title, subtitle, options);\n\t\t\t\t},\n\t\t\t\tget instance() {\n\t\t\t\t\treturn instanceRef.current;\n\t\t\t\t},\n\t\t\t}),\n\t\t\t[]\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tid={props.id}\n\t\t\t\tclassName={['wfp-host', props.className].filter(Boolean).join(' ')}\n\t\t\t\tstyle={props.style}\n\t\t\t/>\n\t\t);\n\t}\n);\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/WaveformPlayer.tsx"],"names":["forwardRef","WaveformPlayer","useRef","useLayoutEffect","useEffect","useImperativeHandle","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * WaveformPlayer.tsx\n * ------------------\n *\n * React wrapper around `@arraypress/waveform-player`. Mounts a player\n * instance into a `<div>` on first render, tears it down on unmount,\n * and re-mounts when any \"identity\" prop changes (the props whose\n * change requires the library to start over from scratch — `url`,\n * `audioMode`).\n *\n * For non-identity props, this component currently re-creates the\n * instance as well, which is simpler than diffing every option and\n * calling the right granular updater. The trade-off is acceptable\n * because:\n *\n * - The library re-uses any cached waveform data keyed by URL, so\n * re-mounts on the same URL are cheap.\n * - Per-render churn on a player widget is rare in practice.\n *\n * If you need finer control — imperative `loadTrack()`, `seekTo()`,\n * `setVolume()`, etc. — grab the instance through a `ref`:\n *\n * ```tsx\n * import { useRef, useEffect } from 'react';\n * import { WaveformPlayer, type WaveformPlayerHandle } from '@arraypress/waveform-player-react';\n *\n * function MyPlayer() {\n * const ref = useRef<WaveformPlayerHandle>(null);\n * return (\n * <>\n * <WaveformPlayer ref={ref} url=\"/audio/track.mp3\" />\n * <button onClick={() => ref.current?.seekTo(60)}>Jump to 1:00</button>\n * </>\n * );\n * }\n * ```\n *\n * ## Library setup\n *\n * This component does **not** load the core library's CSS for you.\n * Import it once at your app entry:\n *\n * ```ts\n * import '@arraypress/waveform-player/dist/waveform-player.css';\n * ```\n *\n * The library's JS is imported dynamically inside `useEffect` so it\n * only loads on the client (SSR-safe).\n *\n * @module WaveformPlayer\n */\nimport {\n\tforwardRef,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseLayoutEffect,\n\tuseRef,\n\ttype ForwardedRef,\n} from 'react';\n// Aliased to avoid colliding with this file's own `WaveformPlayer`\n// component export. This is the core library's player class type.\nimport type { WaveformPlayer as WaveformPlayerInstance } from '@arraypress/waveform-player';\nimport type { WaveformPlayerHandle, WaveformPlayerProps } from './types';\n\n/**\n * Convert a `WaveformPlayerProps` object into the option shape the\n * core library accepts. Most fields pass straight through; this\n * helper exists so the option-building logic is testable on its own\n * and the component body stays focused on lifecycle.\n *\n * @param props - The component's resolved props.\n * @returns An options object to pass into `new WaveformPlayer(el, …)`.\n */\nfunction buildLibraryOptions(props: WaveformPlayerProps): Record<string, unknown> {\n\tconst opts: Record<string, unknown> = {};\n\n\t/* Audio source — `src` is the core's shorthand alias for `url`. */\n\tif (props.url !== undefined) opts.url = props.url;\n\telse if (props.src !== undefined) opts.url = props.src;\n\tif (props.audioMode !== undefined) opts.audioMode = props.audioMode;\n\tif (props.preload !== undefined) opts.preload = props.preload;\n\n\t/* Waveform visualisation */\n\tif (props.waveformStyle !== undefined) opts.waveformStyle = props.waveformStyle;\n\tif (props.height !== undefined) opts.height = props.height;\n\tif (props.samples !== undefined) opts.samples = props.samples;\n\tif (props.barWidth !== undefined) opts.barWidth = props.barWidth;\n\tif (props.barSpacing !== undefined) opts.barSpacing = props.barSpacing;\n\tif (props.barRadius !== undefined) opts.barRadius = props.barRadius;\n\tif (props.waveform !== undefined && props.waveform !== null) {\n\t\topts.waveform = props.waveform;\n\t}\n\n\t/* Colours */\n\tif (props.colorPreset !== undefined) opts.colorPreset = props.colorPreset;\n\tif (props.waveformColor !== undefined) opts.waveformColor = props.waveformColor;\n\tif (props.progressColor !== undefined) opts.progressColor = props.progressColor;\n\tif (props.buttonColor !== undefined) opts.buttonColor = props.buttonColor;\n\tif (props.buttonHoverColor !== undefined) opts.buttonHoverColor = props.buttonHoverColor;\n\tif (props.textColor !== undefined) opts.textColor = props.textColor;\n\tif (props.textSecondaryColor !== undefined) opts.textSecondaryColor = props.textSecondaryColor;\n\tif (props.backgroundColor !== undefined) opts.backgroundColor = props.backgroundColor;\n\tif (props.borderColor !== undefined) opts.borderColor = props.borderColor;\n\n\t/* Playback controls */\n\tif (props.playbackRate !== undefined) opts.playbackRate = props.playbackRate;\n\tif (props.showPlaybackSpeed !== undefined) opts.showPlaybackSpeed = props.showPlaybackSpeed;\n\tif (props.playbackRates !== undefined) opts.playbackRates = props.playbackRates;\n\n\t/* UI toggles */\n\tif (props.showControls !== undefined) opts.showControls = props.showControls;\n\tif (props.showInfo !== undefined) opts.showInfo = props.showInfo;\n\tif (props.showTime !== undefined) opts.showTime = props.showTime;\n\tif (props.showHoverTime !== undefined) opts.showHoverTime = props.showHoverTime;\n\tif (props.showBPM !== undefined) opts.showBPM = props.showBPM;\n\tif (props.bpm !== undefined) opts.bpm = props.bpm;\n\tif (props.buttonAlign !== undefined) opts.buttonAlign = props.buttonAlign;\n\tif (props.layout !== undefined) opts.layout = props.layout;\n\tif (props.buttonStyle !== undefined) opts.buttonStyle = props.buttonStyle;\n\n\t/* Accessibility */\n\tif (props.accessibleSeek !== undefined) opts.accessibleSeek = props.accessibleSeek;\n\tif (props.seekLabel !== undefined) opts.seekLabel = props.seekLabel;\n\n\t/* Error UI */\n\tif (props.errorText !== undefined) opts.errorText = props.errorText;\n\n\t/* Markers */\n\tif (props.markers !== undefined) opts.markers = props.markers;\n\tif (props.showMarkers !== undefined) opts.showMarkers = props.showMarkers;\n\n\t/* Content metadata */\n\tif (props.title !== undefined) opts.title = props.title;\n\tif (props.subtitle !== undefined) opts.subtitle = props.subtitle;\n\tif (props.artwork !== undefined) opts.artwork = props.artwork;\n\tif (props.album !== undefined) opts.album = props.album;\n\n\t/* Behaviour */\n\tif (props.autoplay !== undefined) opts.autoplay = props.autoplay;\n\tif (props.singlePlay !== undefined) opts.singlePlay = props.singlePlay;\n\tif (props.playOnSeek !== undefined) opts.playOnSeek = props.playOnSeek;\n\tif (props.enableMediaSession !== undefined) opts.enableMediaSession = props.enableMediaSession;\n\n\t/* Icons */\n\tif (props.playIcon !== undefined) opts.playIcon = props.playIcon;\n\tif (props.pauseIcon !== undefined) opts.pauseIcon = props.pauseIcon;\n\n\t/* Callbacks are intentionally NOT mapped here. They are wired in\n\t * the mount effect as *stable* wrapper functions that read the\n\t * latest handlers from `callbacksRef` at call time. That way a\n\t * parent re-render passing fresh inline callbacks is always seen\n\t * by the core without re-creating the player — and the callbacks\n\t * never capture stale first-mount state (stale-closure bug). */\n\n\treturn opts;\n}\n\n/**\n * `WaveformPlayer` — React component wrapping\n * `@arraypress/waveform-player`.\n *\n * Render at the spot you want a waveform-driven audio player to\n * appear. The container `<div>` is rendered immediately for layout;\n * the actual player UI hydrates in once the library loads\n * client-side.\n *\n * @example Basic\n * <WaveformPlayer url=\"/audio/track.mp3\" title=\"My Track\" />\n *\n * @example With ref for imperative control\n * const ref = useRef<WaveformPlayerHandle>(null);\n * <WaveformPlayer ref={ref} url={url} />\n * <button onClick={() => ref.current?.togglePlay()}>Play/Pause</button>\n */\nexport const WaveformPlayer = forwardRef<WaveformPlayerHandle, WaveformPlayerProps>(\n\tfunction WaveformPlayer(props, ref: ForwardedRef<WaveformPlayerHandle>) {\n\t\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\t\tconst instanceRef = useRef<unknown>(null);\n\n\t\t/**\n\t\t * Latest user callbacks, kept in a ref so the wrappers handed to\n\t\t * the core (built once at mount, below) always invoke the most\n\t\t * recent handler instead of the ones captured on first mount.\n\t\t *\n\t\t * Initialised from this render's props and refreshed every render\n\t\t * by the layout effect underneath — never listed in the mount\n\t\t * effect's deps, so updating a callback does NOT tear the player\n\t\t * down.\n\t\t */\n\t\tconst callbacksRef = useRef<\n\t\t\tPick<\n\t\t\t\tWaveformPlayerProps,\n\t\t\t\t'onLoad' | 'onPlay' | 'onPause' | 'onEnd' | 'onTimeUpdate' | 'onError'\n\t\t\t>\n\t\t>({\n\t\t\tonLoad: props.onLoad,\n\t\t\tonPlay: props.onPlay,\n\t\t\tonPause: props.onPause,\n\t\t\tonEnd: props.onEnd,\n\t\t\tonTimeUpdate: props.onTimeUpdate,\n\t\t\tonError: props.onError,\n\t\t});\n\n\t\t/* Refresh the ref on every render (before paint) so the stable\n\t\t * wrappers below always read the latest handlers. */\n\t\tuseLayoutEffect(() => {\n\t\t\tcallbacksRef.current = {\n\t\t\t\tonLoad: props.onLoad,\n\t\t\t\tonPlay: props.onPlay,\n\t\t\t\tonPause: props.onPause,\n\t\t\t\tonEnd: props.onEnd,\n\t\t\t\tonTimeUpdate: props.onTimeUpdate,\n\t\t\t\tonError: props.onError,\n\t\t\t};\n\t\t});\n\n\t\t/**\n\t\t * Mount / re-mount lifecycle.\n\t\t *\n\t\t * The dep array intentionally contains EVERY prop the library\n\t\t * uses at construction time. When any of them change, this\n\t\t * effect tears down the old instance and creates a new one\n\t\t * with the updated options. That's simpler and more correct\n\t\t * than trying to partial-update the live instance, and the\n\t\t * library has built-in caches (waveform peaks keyed by URL)\n\t\t * that make same-URL re-mounts cheap.\n\t\t */\n\t\tuseEffect(() => {\n\t\t\tlet cancelled = false;\n\t\t\tlet localInstance: { destroy?: () => void } | null = null;\n\n\t\t\t/* The library is browser-only. Defer the import until we're\n\t\t\t * actually mounting client-side so SSR / RSC don't try to\n\t\t\t * evaluate the audio + canvas + fetch surface on the server.\n\t\t\t */\n\t\t\tvoid import('@arraypress/waveform-player')\n\t\t\t\t.then((mod) => {\n\t\t\t\t\tif (cancelled) return;\n\t\t\t\t\tconst container = containerRef.current;\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst WaveformPlayerClass = (mod.default ?? (mod as { WaveformPlayer?: unknown }).WaveformPlayer) as {\n\t\t\t\t\t\tnew (el: HTMLElement, opts: Record<string, unknown>): { destroy?: () => void };\n\t\t\t\t\t};\n\n\t\t\t\t\tif (typeof WaveformPlayerClass !== 'function') {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t'[WaveformPlayerReact] Failed to resolve WaveformPlayer constructor from module.'\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst opts = buildLibraryOptions(props);\n\n\t\t\t\t\t/* Stable callback wrappers. Created once per player\n\t\t\t\t\t * instance and never change identity, so the core holds a\n\t\t\t\t\t * fixed reference, yet each call reads the *current*\n\t\t\t\t\t * handler from `callbacksRef` — fixing the stale-closure\n\t\t\t\t\t * bug without re-mounting on callback changes. Signatures\n\t\t\t\t\t * mirror the core's option callbacks exactly. */\n\t\t\t\t\topts.onLoad = (instance: WaveformPlayerInstance) => callbacksRef.current.onLoad?.(instance);\n\t\t\t\t\topts.onPlay = (instance: WaveformPlayerInstance) => callbacksRef.current.onPlay?.(instance);\n\t\t\t\t\topts.onPause = (instance: WaveformPlayerInstance) => callbacksRef.current.onPause?.(instance);\n\t\t\t\t\topts.onEnd = (instance: WaveformPlayerInstance) => callbacksRef.current.onEnd?.(instance);\n\t\t\t\t\topts.onTimeUpdate = (currentTime: number, duration: number, instance: WaveformPlayerInstance) =>\n\t\t\t\t\t\tcallbacksRef.current.onTimeUpdate?.(currentTime, duration, instance);\n\t\t\t\t\topts.onError = (error: Error, instance: WaveformPlayerInstance) =>\n\t\t\t\t\t\tcallbacksRef.current.onError?.(error, instance);\n\n\t\t\t\t\tlocalInstance = new WaveformPlayerClass(container, opts);\n\t\t\t\t\tinstanceRef.current = localInstance;\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error('[WaveformPlayerReact] Failed to load library:', err);\n\t\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tcancelled = true;\n\t\t\t\tconst current = localInstance ?? (instanceRef.current as { destroy?: () => void } | null);\n\t\t\t\tif (current && typeof current.destroy === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcurrent.destroy();\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconsole.warn('[WaveformPlayerReact] destroy() threw:', err);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinstanceRef.current = null;\n\t\t\t};\n\t\t\t/* Re-mount on any prop change. Listed exhaustively rather\n\t\t\t * than spread to make the intent explicit and to keep the\n\t\t\t * lint rule happy. Callbacks intentionally NOT in deps:\n\t\t\t * a parent re-rendering with a fresh inline function\n\t\t\t * shouldn't tear the player down. */\n\t\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t\t}, [\n\t\t\tprops.url,\n\t\t\tprops.src,\n\t\t\tprops.audioMode,\n\t\t\tprops.preload,\n\t\t\tprops.waveformStyle,\n\t\t\tprops.height,\n\t\t\tprops.samples,\n\t\t\tprops.barWidth,\n\t\t\tprops.barSpacing,\n\t\t\tprops.barRadius,\n\t\t\tprops.waveform,\n\t\t\tprops.colorPreset,\n\t\t\tprops.waveformColor,\n\t\t\tprops.progressColor,\n\t\t\tprops.buttonColor,\n\t\t\tprops.buttonHoverColor,\n\t\t\tprops.textColor,\n\t\t\tprops.textSecondaryColor,\n\t\t\tprops.backgroundColor,\n\t\t\tprops.borderColor,\n\t\t\tprops.playbackRate,\n\t\t\tprops.showPlaybackSpeed,\n\t\t\tprops.playbackRates,\n\t\t\tprops.showControls,\n\t\t\tprops.showInfo,\n\t\t\tprops.showTime,\n\t\t\tprops.showHoverTime,\n\t\t\tprops.showBPM,\n\t\t\tprops.buttonAlign,\n\t\t\tprops.accessibleSeek,\n\t\t\tprops.seekLabel,\n\t\t\tprops.errorText,\n\t\t\tprops.markers,\n\t\t\tprops.showMarkers,\n\t\t\tprops.title,\n\t\t\tprops.subtitle,\n\t\t\tprops.artwork,\n\t\t\tprops.album,\n\t\t\tprops.autoplay,\n\t\t\tprops.singlePlay,\n\t\t\tprops.playOnSeek,\n\t\t\tprops.enableMediaSession,\n\t\t\tprops.playIcon,\n\t\t\tprops.pauseIcon,\n\t\t]);\n\n\t\t/**\n\t\t * Expose an imperative handle on the forwarded ref. Each\n\t\t * method is a thin pass-through to the live instance — if the\n\t\t * instance hasn't mounted yet (still loading async), calls are\n\t\t * no-ops (`pause`, `seekTo`, etc. return `undefined`).\n\t\t */\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tplay() {\n\t\t\t\t\tconst inst = instanceRef.current as { play?: () => Promise<void> | undefined } | null;\n\t\t\t\t\treturn inst?.play?.();\n\t\t\t\t},\n\t\t\t\tpause() {\n\t\t\t\t\tconst inst = instanceRef.current as { pause?: () => void } | null;\n\t\t\t\t\tinst?.pause?.();\n\t\t\t\t},\n\t\t\t\ttogglePlay() {\n\t\t\t\t\tconst inst = instanceRef.current as { togglePlay?: () => void } | null;\n\t\t\t\t\tinst?.togglePlay?.();\n\t\t\t\t},\n\t\t\t\tseekTo(seconds) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekTo?: (s: number) => void } | null;\n\t\t\t\t\tinst?.seekTo?.(seconds);\n\t\t\t\t},\n\t\t\t\tseekToPercent(percent) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekToPercent?: (p: number) => void } | null;\n\t\t\t\t\tinst?.seekToPercent?.(percent);\n\t\t\t\t},\n\t\t\t\tsetVolume(volume) {\n\t\t\t\t\tconst inst = instanceRef.current as { setVolume?: (v: number) => void } | null;\n\t\t\t\t\tinst?.setVolume?.(volume);\n\t\t\t\t},\n\t\t\t\tsetPlaybackRate(rate) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlaybackRate?: (r: number) => void } | null;\n\t\t\t\t\tinst?.setPlaybackRate?.(rate);\n\t\t\t\t},\n\t\t\t\tsetPlayingState(playing) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlayingState?: (p: boolean) => void } | null;\n\t\t\t\t\tinst?.setPlayingState?.(playing);\n\t\t\t\t},\n\t\t\t\tsetProgress(currentTime, duration) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tsetProgress?: (c: number, d: number) => void;\n\t\t\t\t\t} | null;\n\t\t\t\t\tinst?.setProgress?.(currentTime, duration);\n\t\t\t\t},\n\t\t\t\tasync loadTrack(url, title, subtitle, options) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tloadTrack?: (\n\t\t\t\t\t\t\tu: string,\n\t\t\t\t\t\t\tt?: string,\n\t\t\t\t\t\t\ts?: string,\n\t\t\t\t\t\t\to?: Record<string, unknown>\n\t\t\t\t\t\t) => Promise<void>;\n\t\t\t\t\t} | null;\n\t\t\t\t\tif (!inst?.loadTrack) return;\n\t\t\t\t\tawait inst.loadTrack(url, title, subtitle, options);\n\t\t\t\t},\n\t\t\t\tget instance() {\n\t\t\t\t\treturn instanceRef.current as WaveformPlayerInstance;\n\t\t\t\t},\n\t\t\t}),\n\t\t\t[]\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tid={props.id}\n\t\t\t\tclassName={['wfp-host', props.className].filter(Boolean).join(' ')}\n\t\t\t\tstyle={props.style}\n\t\t\t/>\n\t\t);\n\t}\n);\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,75 +1,32 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { WaveformPlayerOptions, WaveformPlayer as WaveformPlayer$1 } from '@arraypress/waveform-player';
|
|
3
|
+
export { AudioMode, AudioPreload, ButtonAlign, ColorPreset, WaveformMarker, WaveformPeaks, WaveformStyle } from '@arraypress/waveform-player';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* @module types
|
|
5
7
|
* @description
|
|
6
8
|
* Public TypeScript types for `@arraypress/waveform-player-react`.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* The shared option surface — `WaveformStyle`, `ColorPreset`,
|
|
11
|
+
* `AudioMode`, `AudioPreload`, `ButtonAlign`, `WaveformMarker`,
|
|
12
|
+
* `WaveformPeaks`, and the full per-option list behind
|
|
13
|
+
* `WaveformPlayerProps` — is now owned by the core library and
|
|
14
|
+
* re-exported / extended here rather than re-declared. The core's
|
|
15
|
+
* hand-authored `index.d.ts` is the single source of truth, so this
|
|
16
|
+
* wrapper can never drift out of sync with it.
|
|
17
|
+
*
|
|
18
|
+
* This module only adds the React-specific surface:
|
|
10
19
|
*
|
|
11
20
|
* - Callback props (`onLoad`, `onPlay`, `onPause`, `onTimeUpdate`,
|
|
12
|
-
* `onEnd`, `onError`) that map
|
|
13
|
-
*
|
|
21
|
+
* `onEnd`, `onError`) that map to the library's same-named option
|
|
22
|
+
* fields but receive the typed `WaveformPlayer` instance.
|
|
14
23
|
* - DOM pass-through (`className`, `style`, `id`).
|
|
15
24
|
* - A `WaveformPlayerHandle` exposed via `ref` for imperative
|
|
16
25
|
* control (`loadTrack`, `seekTo`, `setVolume`, etc.).
|
|
17
26
|
*
|
|
18
27
|
* @see {@link https://github.com/arraypress/waveform-player} — core library
|
|
19
28
|
*/
|
|
20
|
-
|
|
21
|
-
* Visual style of the waveform.
|
|
22
|
-
*
|
|
23
|
-
* - `bars` — vertical bars from the baseline up
|
|
24
|
-
* - `mirror` — symmetrical bars mirrored around the centre line
|
|
25
|
-
* - `line` — connected line graph
|
|
26
|
-
* - `blocks` — chunky square blocks
|
|
27
|
-
* - `dots` — dotted plot
|
|
28
|
-
* - `seekbar` — minimal seek bar with no peak detail
|
|
29
|
-
*/
|
|
30
|
-
type WaveformStyle = 'bars' | 'mirror' | 'line' | 'blocks' | 'dots' | 'seekbar';
|
|
31
|
-
/**
|
|
32
|
-
* Forced colour scheme. `null` (the default) auto-detects from the
|
|
33
|
-
* page theme and `prefers-color-scheme`.
|
|
34
|
-
*/
|
|
35
|
-
type ColorPreset = 'dark' | 'light' | null;
|
|
36
|
-
/**
|
|
37
|
-
* How the player handles audio.
|
|
38
|
-
*
|
|
39
|
-
* - `self` — the player owns an `<audio>` element and plays the
|
|
40
|
-
* URL itself. Default.
|
|
41
|
-
* - `external` — the player renders waveform visualisation only and
|
|
42
|
-
* dispatches `waveformplayer:request-play|pause|seek` events for
|
|
43
|
-
* an external controller to handle. Drive the visualisation by
|
|
44
|
-
* calling `setProgress()` and `setPlayingState()` on the instance
|
|
45
|
-
* via the forwarded ref.
|
|
46
|
-
*/
|
|
47
|
-
type AudioMode = 'self' | 'external';
|
|
48
|
-
/** Browser preload hint for the underlying `<audio>` element. */
|
|
49
|
-
type AudioPreload = 'auto' | 'metadata' | 'none';
|
|
50
|
-
/** Vertical alignment of the play button relative to the waveform. */
|
|
51
|
-
type ButtonAlign = 'auto' | 'top' | 'center' | 'bottom';
|
|
52
|
-
/**
|
|
53
|
-
* A clickable chapter marker rendered on top of the waveform.
|
|
54
|
-
*/
|
|
55
|
-
interface WaveformMarker {
|
|
56
|
-
/** Time in seconds at which the marker appears. */
|
|
57
|
-
time: number;
|
|
58
|
-
/** Short label shown as a tooltip. */
|
|
59
|
-
label: string;
|
|
60
|
-
/** Optional override colour (CSS colour string). */
|
|
61
|
-
color?: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Pre-computed waveform peaks, OR a string pointer to them.
|
|
65
|
-
*
|
|
66
|
-
* - `number[]` — inline array of peak amplitudes (0..1)
|
|
67
|
-
* - `string` (.json URL) — JSON file URL the library will `fetch()`
|
|
68
|
-
* - `string` (JSON array) — inline JSON string the library will parse
|
|
69
|
-
* - `null` / omitted — the library decodes the audio file with
|
|
70
|
-
* the Web Audio API at load time
|
|
71
|
-
*/
|
|
72
|
-
type WaveformPeaks = number[] | string | null;
|
|
29
|
+
|
|
73
30
|
/**
|
|
74
31
|
* Imperative handle exposed through `ref`. Lets consumers drive the
|
|
75
32
|
* player directly — useful for "play this track when X happens"
|
|
@@ -114,132 +71,50 @@ interface WaveformPlayerHandle {
|
|
|
114
71
|
*/
|
|
115
72
|
loadTrack(url: string, title?: string, subtitle?: string, options?: Record<string, unknown>): Promise<void>;
|
|
116
73
|
/**
|
|
117
|
-
* Underlying instance.
|
|
118
|
-
*
|
|
119
|
-
*
|
|
74
|
+
* Underlying `WaveformPlayer` instance. Exposes the full core API
|
|
75
|
+
* (static helpers, `options`, `load`, `setWaveformData`, …) for the
|
|
76
|
+
* rare cases the handle methods above don't cover.
|
|
120
77
|
*/
|
|
121
|
-
readonly instance:
|
|
78
|
+
readonly instance: WaveformPlayer$1;
|
|
122
79
|
}
|
|
123
80
|
/**
|
|
124
81
|
* Props accepted by `<WaveformPlayer>`.
|
|
125
82
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* 10. React-specific extras (callbacks, className, style, id)
|
|
83
|
+
* Extends the core library's `WaveformPlayerOptions` so every library
|
|
84
|
+
* option is accepted as a typed prop automatically — including
|
|
85
|
+
* `accessibleSeek`, `seekLabel`, `barRadius`, and gradient-array
|
|
86
|
+
* colours — and stays in sync as the core evolves. The core's `url`
|
|
87
|
+
* and callback options are omitted and re-declared below with the
|
|
88
|
+
* React-specific shapes (`url` is required; callbacks receive the
|
|
89
|
+
* typed instance).
|
|
90
|
+
*
|
|
91
|
+
* React-specific extras layered on top: the callback props, plus the
|
|
92
|
+
* DOM pass-throughs `id`, `className`, and `style`.
|
|
137
93
|
*/
|
|
138
|
-
interface WaveformPlayerProps {
|
|
139
|
-
/** Audio file URL. Required. */
|
|
140
|
-
url: string;
|
|
94
|
+
interface WaveformPlayerProps extends Omit<WaveformPlayerOptions, 'url' | 'style' | 'onLoad' | 'onPlay' | 'onPause' | 'onEnd' | 'onError' | 'onTimeUpdate'> {
|
|
141
95
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* Changing this prop **re-mounts** the player instance — it's
|
|
146
|
-
* part of the "identity" dep array.
|
|
147
|
-
*
|
|
148
|
-
* @default 'self'
|
|
96
|
+
* Audio file URL. Optional only because the core's `src` shorthand is
|
|
97
|
+
* an accepted alias — provide one of `url` or `src`.
|
|
149
98
|
*/
|
|
150
|
-
|
|
151
|
-
/** Browser preload hint. @default 'metadata' */
|
|
152
|
-
preload?: AudioPreload;
|
|
153
|
-
/** Visual style. @default 'mirror' */
|
|
154
|
-
waveformStyle?: WaveformStyle;
|
|
155
|
-
/** Canvas height in pixels. @default 60 */
|
|
156
|
-
height?: number;
|
|
157
|
-
/** Number of peak samples. @default 200 */
|
|
158
|
-
samples?: number;
|
|
159
|
-
/** Bar/block width in pixels. */
|
|
160
|
-
barWidth?: number;
|
|
161
|
-
/** Gap between bars in pixels. */
|
|
162
|
-
barSpacing?: number;
|
|
163
|
-
/** Pre-computed peaks data. See {@link WaveformPeaks}. */
|
|
164
|
-
waveform?: WaveformPeaks;
|
|
165
|
-
/** Forced colour preset; `null` auto-detects. @default null */
|
|
166
|
-
colorPreset?: ColorPreset;
|
|
167
|
-
/** Unplayed peak colour (CSS colour string). */
|
|
168
|
-
waveformColor?: string;
|
|
169
|
-
/** Progress/played-through colour. */
|
|
170
|
-
progressColor?: string;
|
|
171
|
-
/** Play button border/text colour. */
|
|
172
|
-
buttonColor?: string;
|
|
173
|
-
/** Play button hover colour. */
|
|
174
|
-
buttonHoverColor?: string;
|
|
175
|
-
/** Primary text (title) colour. */
|
|
176
|
-
textColor?: string;
|
|
177
|
-
/** Secondary text (subtitle, time) colour. */
|
|
178
|
-
textSecondaryColor?: string;
|
|
179
|
-
/** Reserved. */
|
|
180
|
-
backgroundColor?: string;
|
|
181
|
-
/** Reserved. */
|
|
182
|
-
borderColor?: string;
|
|
183
|
-
/** Initial playback rate (0.5..2). @default 1 */
|
|
184
|
-
playbackRate?: number;
|
|
185
|
-
/** Show the playback-speed control menu. @default false */
|
|
186
|
-
showPlaybackSpeed?: boolean;
|
|
187
|
-
/** Speeds offered in the menu. */
|
|
188
|
-
playbackRates?: number[];
|
|
189
|
-
/** Show play/pause button. @default true */
|
|
190
|
-
showControls?: boolean;
|
|
191
|
-
/** Show info bar (title, subtitle, time, BPM, speed). @default true */
|
|
192
|
-
showInfo?: boolean;
|
|
193
|
-
/** Show current/total time. @default true */
|
|
194
|
-
showTime?: boolean;
|
|
195
|
-
/** Show hover-time indicator. Reserved. @default false */
|
|
196
|
-
showHoverTime?: boolean;
|
|
197
|
-
/** Detect and display BPM. @default false */
|
|
198
|
-
showBPM?: boolean;
|
|
199
|
-
/** Vertical alignment of play button. @default 'auto' */
|
|
200
|
-
buttonAlign?: ButtonAlign;
|
|
201
|
-
/** Chapter markers. */
|
|
202
|
-
markers?: WaveformMarker[];
|
|
203
|
-
/** Whether to render markers. @default true */
|
|
204
|
-
showMarkers?: boolean;
|
|
205
|
-
/** Track title (defaults to prettified filename). */
|
|
206
|
-
title?: string;
|
|
207
|
-
/** Subtitle / artist. */
|
|
208
|
-
subtitle?: string;
|
|
209
|
-
/** Cover image URL. */
|
|
210
|
-
artwork?: string;
|
|
211
|
-
/** Album name (Media Session API). */
|
|
212
|
-
album?: string;
|
|
213
|
-
/** Start as soon as metadata loads. @default false */
|
|
214
|
-
autoplay?: boolean;
|
|
215
|
-
/** Pause other players on the page when this one starts. @default true */
|
|
216
|
-
singlePlay?: boolean;
|
|
217
|
-
/** Resume on seek if paused. @default true */
|
|
218
|
-
playOnSeek?: boolean;
|
|
219
|
-
/** Wire up the browser's Media Session API. @default true */
|
|
220
|
-
enableMediaSession?: boolean;
|
|
221
|
-
/** Inline HTML/SVG for the play button. */
|
|
222
|
-
playIcon?: string;
|
|
223
|
-
/** Inline HTML/SVG for the pause button. */
|
|
224
|
-
pauseIcon?: string;
|
|
99
|
+
url?: string;
|
|
225
100
|
/**
|
|
226
|
-
* Called once on mount after the player's `onLoad` fires.
|
|
227
|
-
*
|
|
101
|
+
* Called once on mount after the player's `onLoad` fires. Receives
|
|
102
|
+
* the live `WaveformPlayer` instance.
|
|
228
103
|
*/
|
|
229
|
-
onLoad?: (instance:
|
|
104
|
+
onLoad?: (instance: WaveformPlayer$1) => void;
|
|
230
105
|
/** Called when playback starts. */
|
|
231
|
-
onPlay?: (instance:
|
|
106
|
+
onPlay?: (instance: WaveformPlayer$1) => void;
|
|
232
107
|
/** Called when playback pauses. */
|
|
233
|
-
onPause?: (instance:
|
|
108
|
+
onPause?: (instance: WaveformPlayer$1) => void;
|
|
234
109
|
/** Called when the track ends. */
|
|
235
|
-
onEnd?: (instance:
|
|
110
|
+
onEnd?: (instance: WaveformPlayer$1) => void;
|
|
236
111
|
/**
|
|
237
|
-
* Called on each progress frame
|
|
238
|
-
*
|
|
112
|
+
* Called on each progress frame. Fires with the same
|
|
113
|
+
* `(currentTime, duration, instance)` order in both audio modes.
|
|
239
114
|
*/
|
|
240
|
-
onTimeUpdate?: (currentTime: number, duration: number, instance:
|
|
115
|
+
onTimeUpdate?: (currentTime: number, duration: number, instance: WaveformPlayer$1) => void;
|
|
241
116
|
/** Called on audio load / playback error. */
|
|
242
|
-
onError?: (error: Error, instance:
|
|
117
|
+
onError?: (error: Error, instance: WaveformPlayer$1) => void;
|
|
243
118
|
/**
|
|
244
119
|
* DOM id forwarded to the container `<div>`. Useful for targeting
|
|
245
120
|
* the player from external scripts.
|
|
@@ -277,4 +152,4 @@ interface WaveformPlayerProps {
|
|
|
277
152
|
*/
|
|
278
153
|
declare const WaveformPlayer: react.ForwardRefExoticComponent<WaveformPlayerProps & react.RefAttributes<WaveformPlayerHandle>>;
|
|
279
154
|
|
|
280
|
-
export {
|
|
155
|
+
export { WaveformPlayer, type WaveformPlayerHandle, type WaveformPlayerProps, WaveformPlayer as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,75 +1,32 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { WaveformPlayerOptions, WaveformPlayer as WaveformPlayer$1 } from '@arraypress/waveform-player';
|
|
3
|
+
export { AudioMode, AudioPreload, ButtonAlign, ColorPreset, WaveformMarker, WaveformPeaks, WaveformStyle } from '@arraypress/waveform-player';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* @module types
|
|
5
7
|
* @description
|
|
6
8
|
* Public TypeScript types for `@arraypress/waveform-player-react`.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* The shared option surface — `WaveformStyle`, `ColorPreset`,
|
|
11
|
+
* `AudioMode`, `AudioPreload`, `ButtonAlign`, `WaveformMarker`,
|
|
12
|
+
* `WaveformPeaks`, and the full per-option list behind
|
|
13
|
+
* `WaveformPlayerProps` — is now owned by the core library and
|
|
14
|
+
* re-exported / extended here rather than re-declared. The core's
|
|
15
|
+
* hand-authored `index.d.ts` is the single source of truth, so this
|
|
16
|
+
* wrapper can never drift out of sync with it.
|
|
17
|
+
*
|
|
18
|
+
* This module only adds the React-specific surface:
|
|
10
19
|
*
|
|
11
20
|
* - Callback props (`onLoad`, `onPlay`, `onPause`, `onTimeUpdate`,
|
|
12
|
-
* `onEnd`, `onError`) that map
|
|
13
|
-
*
|
|
21
|
+
* `onEnd`, `onError`) that map to the library's same-named option
|
|
22
|
+
* fields but receive the typed `WaveformPlayer` instance.
|
|
14
23
|
* - DOM pass-through (`className`, `style`, `id`).
|
|
15
24
|
* - A `WaveformPlayerHandle` exposed via `ref` for imperative
|
|
16
25
|
* control (`loadTrack`, `seekTo`, `setVolume`, etc.).
|
|
17
26
|
*
|
|
18
27
|
* @see {@link https://github.com/arraypress/waveform-player} — core library
|
|
19
28
|
*/
|
|
20
|
-
|
|
21
|
-
* Visual style of the waveform.
|
|
22
|
-
*
|
|
23
|
-
* - `bars` — vertical bars from the baseline up
|
|
24
|
-
* - `mirror` — symmetrical bars mirrored around the centre line
|
|
25
|
-
* - `line` — connected line graph
|
|
26
|
-
* - `blocks` — chunky square blocks
|
|
27
|
-
* - `dots` — dotted plot
|
|
28
|
-
* - `seekbar` — minimal seek bar with no peak detail
|
|
29
|
-
*/
|
|
30
|
-
type WaveformStyle = 'bars' | 'mirror' | 'line' | 'blocks' | 'dots' | 'seekbar';
|
|
31
|
-
/**
|
|
32
|
-
* Forced colour scheme. `null` (the default) auto-detects from the
|
|
33
|
-
* page theme and `prefers-color-scheme`.
|
|
34
|
-
*/
|
|
35
|
-
type ColorPreset = 'dark' | 'light' | null;
|
|
36
|
-
/**
|
|
37
|
-
* How the player handles audio.
|
|
38
|
-
*
|
|
39
|
-
* - `self` — the player owns an `<audio>` element and plays the
|
|
40
|
-
* URL itself. Default.
|
|
41
|
-
* - `external` — the player renders waveform visualisation only and
|
|
42
|
-
* dispatches `waveformplayer:request-play|pause|seek` events for
|
|
43
|
-
* an external controller to handle. Drive the visualisation by
|
|
44
|
-
* calling `setProgress()` and `setPlayingState()` on the instance
|
|
45
|
-
* via the forwarded ref.
|
|
46
|
-
*/
|
|
47
|
-
type AudioMode = 'self' | 'external';
|
|
48
|
-
/** Browser preload hint for the underlying `<audio>` element. */
|
|
49
|
-
type AudioPreload = 'auto' | 'metadata' | 'none';
|
|
50
|
-
/** Vertical alignment of the play button relative to the waveform. */
|
|
51
|
-
type ButtonAlign = 'auto' | 'top' | 'center' | 'bottom';
|
|
52
|
-
/**
|
|
53
|
-
* A clickable chapter marker rendered on top of the waveform.
|
|
54
|
-
*/
|
|
55
|
-
interface WaveformMarker {
|
|
56
|
-
/** Time in seconds at which the marker appears. */
|
|
57
|
-
time: number;
|
|
58
|
-
/** Short label shown as a tooltip. */
|
|
59
|
-
label: string;
|
|
60
|
-
/** Optional override colour (CSS colour string). */
|
|
61
|
-
color?: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Pre-computed waveform peaks, OR a string pointer to them.
|
|
65
|
-
*
|
|
66
|
-
* - `number[]` — inline array of peak amplitudes (0..1)
|
|
67
|
-
* - `string` (.json URL) — JSON file URL the library will `fetch()`
|
|
68
|
-
* - `string` (JSON array) — inline JSON string the library will parse
|
|
69
|
-
* - `null` / omitted — the library decodes the audio file with
|
|
70
|
-
* the Web Audio API at load time
|
|
71
|
-
*/
|
|
72
|
-
type WaveformPeaks = number[] | string | null;
|
|
29
|
+
|
|
73
30
|
/**
|
|
74
31
|
* Imperative handle exposed through `ref`. Lets consumers drive the
|
|
75
32
|
* player directly — useful for "play this track when X happens"
|
|
@@ -114,132 +71,50 @@ interface WaveformPlayerHandle {
|
|
|
114
71
|
*/
|
|
115
72
|
loadTrack(url: string, title?: string, subtitle?: string, options?: Record<string, unknown>): Promise<void>;
|
|
116
73
|
/**
|
|
117
|
-
* Underlying instance.
|
|
118
|
-
*
|
|
119
|
-
*
|
|
74
|
+
* Underlying `WaveformPlayer` instance. Exposes the full core API
|
|
75
|
+
* (static helpers, `options`, `load`, `setWaveformData`, …) for the
|
|
76
|
+
* rare cases the handle methods above don't cover.
|
|
120
77
|
*/
|
|
121
|
-
readonly instance:
|
|
78
|
+
readonly instance: WaveformPlayer$1;
|
|
122
79
|
}
|
|
123
80
|
/**
|
|
124
81
|
* Props accepted by `<WaveformPlayer>`.
|
|
125
82
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* 10. React-specific extras (callbacks, className, style, id)
|
|
83
|
+
* Extends the core library's `WaveformPlayerOptions` so every library
|
|
84
|
+
* option is accepted as a typed prop automatically — including
|
|
85
|
+
* `accessibleSeek`, `seekLabel`, `barRadius`, and gradient-array
|
|
86
|
+
* colours — and stays in sync as the core evolves. The core's `url`
|
|
87
|
+
* and callback options are omitted and re-declared below with the
|
|
88
|
+
* React-specific shapes (`url` is required; callbacks receive the
|
|
89
|
+
* typed instance).
|
|
90
|
+
*
|
|
91
|
+
* React-specific extras layered on top: the callback props, plus the
|
|
92
|
+
* DOM pass-throughs `id`, `className`, and `style`.
|
|
137
93
|
*/
|
|
138
|
-
interface WaveformPlayerProps {
|
|
139
|
-
/** Audio file URL. Required. */
|
|
140
|
-
url: string;
|
|
94
|
+
interface WaveformPlayerProps extends Omit<WaveformPlayerOptions, 'url' | 'style' | 'onLoad' | 'onPlay' | 'onPause' | 'onEnd' | 'onError' | 'onTimeUpdate'> {
|
|
141
95
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* Changing this prop **re-mounts** the player instance — it's
|
|
146
|
-
* part of the "identity" dep array.
|
|
147
|
-
*
|
|
148
|
-
* @default 'self'
|
|
96
|
+
* Audio file URL. Optional only because the core's `src` shorthand is
|
|
97
|
+
* an accepted alias — provide one of `url` or `src`.
|
|
149
98
|
*/
|
|
150
|
-
|
|
151
|
-
/** Browser preload hint. @default 'metadata' */
|
|
152
|
-
preload?: AudioPreload;
|
|
153
|
-
/** Visual style. @default 'mirror' */
|
|
154
|
-
waveformStyle?: WaveformStyle;
|
|
155
|
-
/** Canvas height in pixels. @default 60 */
|
|
156
|
-
height?: number;
|
|
157
|
-
/** Number of peak samples. @default 200 */
|
|
158
|
-
samples?: number;
|
|
159
|
-
/** Bar/block width in pixels. */
|
|
160
|
-
barWidth?: number;
|
|
161
|
-
/** Gap between bars in pixels. */
|
|
162
|
-
barSpacing?: number;
|
|
163
|
-
/** Pre-computed peaks data. See {@link WaveformPeaks}. */
|
|
164
|
-
waveform?: WaveformPeaks;
|
|
165
|
-
/** Forced colour preset; `null` auto-detects. @default null */
|
|
166
|
-
colorPreset?: ColorPreset;
|
|
167
|
-
/** Unplayed peak colour (CSS colour string). */
|
|
168
|
-
waveformColor?: string;
|
|
169
|
-
/** Progress/played-through colour. */
|
|
170
|
-
progressColor?: string;
|
|
171
|
-
/** Play button border/text colour. */
|
|
172
|
-
buttonColor?: string;
|
|
173
|
-
/** Play button hover colour. */
|
|
174
|
-
buttonHoverColor?: string;
|
|
175
|
-
/** Primary text (title) colour. */
|
|
176
|
-
textColor?: string;
|
|
177
|
-
/** Secondary text (subtitle, time) colour. */
|
|
178
|
-
textSecondaryColor?: string;
|
|
179
|
-
/** Reserved. */
|
|
180
|
-
backgroundColor?: string;
|
|
181
|
-
/** Reserved. */
|
|
182
|
-
borderColor?: string;
|
|
183
|
-
/** Initial playback rate (0.5..2). @default 1 */
|
|
184
|
-
playbackRate?: number;
|
|
185
|
-
/** Show the playback-speed control menu. @default false */
|
|
186
|
-
showPlaybackSpeed?: boolean;
|
|
187
|
-
/** Speeds offered in the menu. */
|
|
188
|
-
playbackRates?: number[];
|
|
189
|
-
/** Show play/pause button. @default true */
|
|
190
|
-
showControls?: boolean;
|
|
191
|
-
/** Show info bar (title, subtitle, time, BPM, speed). @default true */
|
|
192
|
-
showInfo?: boolean;
|
|
193
|
-
/** Show current/total time. @default true */
|
|
194
|
-
showTime?: boolean;
|
|
195
|
-
/** Show hover-time indicator. Reserved. @default false */
|
|
196
|
-
showHoverTime?: boolean;
|
|
197
|
-
/** Detect and display BPM. @default false */
|
|
198
|
-
showBPM?: boolean;
|
|
199
|
-
/** Vertical alignment of play button. @default 'auto' */
|
|
200
|
-
buttonAlign?: ButtonAlign;
|
|
201
|
-
/** Chapter markers. */
|
|
202
|
-
markers?: WaveformMarker[];
|
|
203
|
-
/** Whether to render markers. @default true */
|
|
204
|
-
showMarkers?: boolean;
|
|
205
|
-
/** Track title (defaults to prettified filename). */
|
|
206
|
-
title?: string;
|
|
207
|
-
/** Subtitle / artist. */
|
|
208
|
-
subtitle?: string;
|
|
209
|
-
/** Cover image URL. */
|
|
210
|
-
artwork?: string;
|
|
211
|
-
/** Album name (Media Session API). */
|
|
212
|
-
album?: string;
|
|
213
|
-
/** Start as soon as metadata loads. @default false */
|
|
214
|
-
autoplay?: boolean;
|
|
215
|
-
/** Pause other players on the page when this one starts. @default true */
|
|
216
|
-
singlePlay?: boolean;
|
|
217
|
-
/** Resume on seek if paused. @default true */
|
|
218
|
-
playOnSeek?: boolean;
|
|
219
|
-
/** Wire up the browser's Media Session API. @default true */
|
|
220
|
-
enableMediaSession?: boolean;
|
|
221
|
-
/** Inline HTML/SVG for the play button. */
|
|
222
|
-
playIcon?: string;
|
|
223
|
-
/** Inline HTML/SVG for the pause button. */
|
|
224
|
-
pauseIcon?: string;
|
|
99
|
+
url?: string;
|
|
225
100
|
/**
|
|
226
|
-
* Called once on mount after the player's `onLoad` fires.
|
|
227
|
-
*
|
|
101
|
+
* Called once on mount after the player's `onLoad` fires. Receives
|
|
102
|
+
* the live `WaveformPlayer` instance.
|
|
228
103
|
*/
|
|
229
|
-
onLoad?: (instance:
|
|
104
|
+
onLoad?: (instance: WaveformPlayer$1) => void;
|
|
230
105
|
/** Called when playback starts. */
|
|
231
|
-
onPlay?: (instance:
|
|
106
|
+
onPlay?: (instance: WaveformPlayer$1) => void;
|
|
232
107
|
/** Called when playback pauses. */
|
|
233
|
-
onPause?: (instance:
|
|
108
|
+
onPause?: (instance: WaveformPlayer$1) => void;
|
|
234
109
|
/** Called when the track ends. */
|
|
235
|
-
onEnd?: (instance:
|
|
110
|
+
onEnd?: (instance: WaveformPlayer$1) => void;
|
|
236
111
|
/**
|
|
237
|
-
* Called on each progress frame
|
|
238
|
-
*
|
|
112
|
+
* Called on each progress frame. Fires with the same
|
|
113
|
+
* `(currentTime, duration, instance)` order in both audio modes.
|
|
239
114
|
*/
|
|
240
|
-
onTimeUpdate?: (currentTime: number, duration: number, instance:
|
|
115
|
+
onTimeUpdate?: (currentTime: number, duration: number, instance: WaveformPlayer$1) => void;
|
|
241
116
|
/** Called on audio load / playback error. */
|
|
242
|
-
onError?: (error: Error, instance:
|
|
117
|
+
onError?: (error: Error, instance: WaveformPlayer$1) => void;
|
|
243
118
|
/**
|
|
244
119
|
* DOM id forwarded to the container `<div>`. Useful for targeting
|
|
245
120
|
* the player from external scripts.
|
|
@@ -277,4 +152,4 @@ interface WaveformPlayerProps {
|
|
|
277
152
|
*/
|
|
278
153
|
declare const WaveformPlayer: react.ForwardRefExoticComponent<WaveformPlayerProps & react.RefAttributes<WaveformPlayerHandle>>;
|
|
279
154
|
|
|
280
|
-
export {
|
|
155
|
+
export { WaveformPlayer, type WaveformPlayerHandle, type WaveformPlayerProps, WaveformPlayer as default };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { forwardRef, useRef, useEffect, useImperativeHandle } from 'react';
|
|
1
|
+
import { forwardRef, useRef, useLayoutEffect, useEffect, useImperativeHandle } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/WaveformPlayer.tsx
|
|
5
5
|
function buildLibraryOptions(props) {
|
|
6
6
|
const opts = {};
|
|
7
7
|
if (props.url !== void 0) opts.url = props.url;
|
|
8
|
+
else if (props.src !== void 0) opts.url = props.src;
|
|
8
9
|
if (props.audioMode !== void 0) opts.audioMode = props.audioMode;
|
|
9
10
|
if (props.preload !== void 0) opts.preload = props.preload;
|
|
10
11
|
if (props.waveformStyle !== void 0) opts.waveformStyle = props.waveformStyle;
|
|
@@ -12,6 +13,7 @@ function buildLibraryOptions(props) {
|
|
|
12
13
|
if (props.samples !== void 0) opts.samples = props.samples;
|
|
13
14
|
if (props.barWidth !== void 0) opts.barWidth = props.barWidth;
|
|
14
15
|
if (props.barSpacing !== void 0) opts.barSpacing = props.barSpacing;
|
|
16
|
+
if (props.barRadius !== void 0) opts.barRadius = props.barRadius;
|
|
15
17
|
if (props.waveform !== void 0 && props.waveform !== null) {
|
|
16
18
|
opts.waveform = props.waveform;
|
|
17
19
|
}
|
|
@@ -32,7 +34,13 @@ function buildLibraryOptions(props) {
|
|
|
32
34
|
if (props.showTime !== void 0) opts.showTime = props.showTime;
|
|
33
35
|
if (props.showHoverTime !== void 0) opts.showHoverTime = props.showHoverTime;
|
|
34
36
|
if (props.showBPM !== void 0) opts.showBPM = props.showBPM;
|
|
37
|
+
if (props.bpm !== void 0) opts.bpm = props.bpm;
|
|
35
38
|
if (props.buttonAlign !== void 0) opts.buttonAlign = props.buttonAlign;
|
|
39
|
+
if (props.layout !== void 0) opts.layout = props.layout;
|
|
40
|
+
if (props.buttonStyle !== void 0) opts.buttonStyle = props.buttonStyle;
|
|
41
|
+
if (props.accessibleSeek !== void 0) opts.accessibleSeek = props.accessibleSeek;
|
|
42
|
+
if (props.seekLabel !== void 0) opts.seekLabel = props.seekLabel;
|
|
43
|
+
if (props.errorText !== void 0) opts.errorText = props.errorText;
|
|
36
44
|
if (props.markers !== void 0) opts.markers = props.markers;
|
|
37
45
|
if (props.showMarkers !== void 0) opts.showMarkers = props.showMarkers;
|
|
38
46
|
if (props.title !== void 0) opts.title = props.title;
|
|
@@ -45,18 +53,30 @@ function buildLibraryOptions(props) {
|
|
|
45
53
|
if (props.enableMediaSession !== void 0) opts.enableMediaSession = props.enableMediaSession;
|
|
46
54
|
if (props.playIcon !== void 0) opts.playIcon = props.playIcon;
|
|
47
55
|
if (props.pauseIcon !== void 0) opts.pauseIcon = props.pauseIcon;
|
|
48
|
-
if (props.onLoad) opts.onLoad = props.onLoad;
|
|
49
|
-
if (props.onPlay) opts.onPlay = props.onPlay;
|
|
50
|
-
if (props.onPause) opts.onPause = props.onPause;
|
|
51
|
-
if (props.onEnd) opts.onEnd = props.onEnd;
|
|
52
|
-
if (props.onTimeUpdate) opts.onTimeUpdate = props.onTimeUpdate;
|
|
53
|
-
if (props.onError) opts.onError = props.onError;
|
|
54
56
|
return opts;
|
|
55
57
|
}
|
|
56
58
|
var WaveformPlayer = forwardRef(
|
|
57
59
|
function WaveformPlayer2(props, ref) {
|
|
58
60
|
const containerRef = useRef(null);
|
|
59
61
|
const instanceRef = useRef(null);
|
|
62
|
+
const callbacksRef = useRef({
|
|
63
|
+
onLoad: props.onLoad,
|
|
64
|
+
onPlay: props.onPlay,
|
|
65
|
+
onPause: props.onPause,
|
|
66
|
+
onEnd: props.onEnd,
|
|
67
|
+
onTimeUpdate: props.onTimeUpdate,
|
|
68
|
+
onError: props.onError
|
|
69
|
+
});
|
|
70
|
+
useLayoutEffect(() => {
|
|
71
|
+
callbacksRef.current = {
|
|
72
|
+
onLoad: props.onLoad,
|
|
73
|
+
onPlay: props.onPlay,
|
|
74
|
+
onPause: props.onPause,
|
|
75
|
+
onEnd: props.onEnd,
|
|
76
|
+
onTimeUpdate: props.onTimeUpdate,
|
|
77
|
+
onError: props.onError
|
|
78
|
+
};
|
|
79
|
+
});
|
|
60
80
|
useEffect(() => {
|
|
61
81
|
let cancelled = false;
|
|
62
82
|
let localInstance = null;
|
|
@@ -67,15 +87,21 @@ var WaveformPlayer = forwardRef(
|
|
|
67
87
|
const WaveformPlayerClass = mod.default ?? mod.WaveformPlayer;
|
|
68
88
|
if (typeof WaveformPlayerClass !== "function") {
|
|
69
89
|
console.error(
|
|
70
|
-
"[
|
|
90
|
+
"[WaveformPlayerReact] Failed to resolve WaveformPlayer constructor from module."
|
|
71
91
|
);
|
|
72
92
|
return;
|
|
73
93
|
}
|
|
74
94
|
const opts = buildLibraryOptions(props);
|
|
95
|
+
opts.onLoad = (instance) => callbacksRef.current.onLoad?.(instance);
|
|
96
|
+
opts.onPlay = (instance) => callbacksRef.current.onPlay?.(instance);
|
|
97
|
+
opts.onPause = (instance) => callbacksRef.current.onPause?.(instance);
|
|
98
|
+
opts.onEnd = (instance) => callbacksRef.current.onEnd?.(instance);
|
|
99
|
+
opts.onTimeUpdate = (currentTime, duration, instance) => callbacksRef.current.onTimeUpdate?.(currentTime, duration, instance);
|
|
100
|
+
opts.onError = (error, instance) => callbacksRef.current.onError?.(error, instance);
|
|
75
101
|
localInstance = new WaveformPlayerClass(container, opts);
|
|
76
102
|
instanceRef.current = localInstance;
|
|
77
103
|
}).catch((err) => {
|
|
78
|
-
console.error("[
|
|
104
|
+
console.error("[WaveformPlayerReact] Failed to load library:", err);
|
|
79
105
|
});
|
|
80
106
|
return () => {
|
|
81
107
|
cancelled = true;
|
|
@@ -84,13 +110,14 @@ var WaveformPlayer = forwardRef(
|
|
|
84
110
|
try {
|
|
85
111
|
current.destroy();
|
|
86
112
|
} catch (err) {
|
|
87
|
-
console.warn("[
|
|
113
|
+
console.warn("[WaveformPlayerReact] destroy() threw:", err);
|
|
88
114
|
}
|
|
89
115
|
}
|
|
90
116
|
instanceRef.current = null;
|
|
91
117
|
};
|
|
92
118
|
}, [
|
|
93
119
|
props.url,
|
|
120
|
+
props.src,
|
|
94
121
|
props.audioMode,
|
|
95
122
|
props.preload,
|
|
96
123
|
props.waveformStyle,
|
|
@@ -98,6 +125,7 @@ var WaveformPlayer = forwardRef(
|
|
|
98
125
|
props.samples,
|
|
99
126
|
props.barWidth,
|
|
100
127
|
props.barSpacing,
|
|
128
|
+
props.barRadius,
|
|
101
129
|
props.waveform,
|
|
102
130
|
props.colorPreset,
|
|
103
131
|
props.waveformColor,
|
|
@@ -117,6 +145,9 @@ var WaveformPlayer = forwardRef(
|
|
|
117
145
|
props.showHoverTime,
|
|
118
146
|
props.showBPM,
|
|
119
147
|
props.buttonAlign,
|
|
148
|
+
props.accessibleSeek,
|
|
149
|
+
props.seekLabel,
|
|
150
|
+
props.errorText,
|
|
120
151
|
props.markers,
|
|
121
152
|
props.showMarkers,
|
|
122
153
|
props.title,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/WaveformPlayer.tsx"],"names":["WaveformPlayer"],"mappings":";;;;AAqEA,SAAS,oBAAoB,KAAA,EAAqD;AACjF,EAAA,MAAM,OAAgC,EAAC;AAGvC,EAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,MAAA,EAAW,IAAA,CAAK,MAAM,KAAA,CAAM,GAAA;AAC9C,EAAA,IAAI,KAAA,CAAM,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,YAAY,KAAA,CAAM,SAAA;AAC1D,EAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AAGtD,EAAA,IAAI,KAAA,CAAM,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,aAAA;AAClE,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,MAAA,EAAW,IAAA,CAAK,SAAS,KAAA,CAAM,MAAA;AACpD,EAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AACtD,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AACxD,EAAA,IAAI,KAAA,CAAM,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,aAAa,KAAA,CAAM,UAAA;AAC5D,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,IAAa,KAAA,CAAM,aAAa,IAAA,EAAM;AAC5D,IAAA,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AAAA,EACvB;AAGA,EAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,IAAA,CAAK,cAAc,KAAA,CAAM,WAAA;AAC9D,EAAA,IAAI,KAAA,CAAM,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,aAAA;AAClE,EAAA,IAAI,KAAA,CAAM,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,aAAA;AAClE,EAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,IAAA,CAAK,cAAc,KAAA,CAAM,WAAA;AAC9D,EAAA,IAAI,KAAA,CAAM,gBAAA,KAAqB,MAAA,EAAW,IAAA,CAAK,mBAAmB,KAAA,CAAM,gBAAA;AACxE,EAAA,IAAI,KAAA,CAAM,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,YAAY,KAAA,CAAM,SAAA;AAC1D,EAAA,IAAI,KAAA,CAAM,kBAAA,KAAuB,MAAA,EAAW,IAAA,CAAK,qBAAqB,KAAA,CAAM,kBAAA;AAC5E,EAAA,IAAI,KAAA,CAAM,eAAA,KAAoB,MAAA,EAAW,IAAA,CAAK,kBAAkB,KAAA,CAAM,eAAA;AACtE,EAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,IAAA,CAAK,cAAc,KAAA,CAAM,WAAA;AAG9D,EAAA,IAAI,KAAA,CAAM,YAAA,KAAiB,MAAA,EAAW,IAAA,CAAK,eAAe,KAAA,CAAM,YAAA;AAChE,EAAA,IAAI,KAAA,CAAM,iBAAA,KAAsB,MAAA,EAAW,IAAA,CAAK,oBAAoB,KAAA,CAAM,iBAAA;AAC1E,EAAA,IAAI,KAAA,CAAM,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,aAAA;AAGlE,EAAA,IAAI,KAAA,CAAM,YAAA,KAAiB,MAAA,EAAW,IAAA,CAAK,eAAe,KAAA,CAAM,YAAA;AAChE,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AACxD,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AACxD,EAAA,IAAI,KAAA,CAAM,aAAA,KAAkB,MAAA,EAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,aAAA;AAClE,EAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AACtD,EAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,IAAA,CAAK,cAAc,KAAA,CAAM,WAAA;AAG9D,EAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AACtD,EAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,IAAA,CAAK,cAAc,KAAA,CAAM,WAAA;AAG9D,EAAA,IAAI,KAAA,CAAM,KAAA,KAAU,MAAA,EAAW,IAAA,CAAK,QAAQ,KAAA,CAAM,KAAA;AAClD,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AACxD,EAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AACtD,EAAA,IAAI,KAAA,CAAM,KAAA,KAAU,MAAA,EAAW,IAAA,CAAK,QAAQ,KAAA,CAAM,KAAA;AAGlD,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AACxD,EAAA,IAAI,KAAA,CAAM,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,aAAa,KAAA,CAAM,UAAA;AAC5D,EAAA,IAAI,KAAA,CAAM,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,aAAa,KAAA,CAAM,UAAA;AAC5D,EAAA,IAAI,KAAA,CAAM,kBAAA,KAAuB,MAAA,EAAW,IAAA,CAAK,qBAAqB,KAAA,CAAM,kBAAA;AAG5E,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AACxD,EAAA,IAAI,KAAA,CAAM,SAAA,KAAc,MAAA,EAAW,IAAA,CAAK,YAAY,KAAA,CAAM,SAAA;AAK1D,EAAA,IAAI,KAAA,CAAM,MAAA,EAAQ,IAAA,CAAK,MAAA,GAAS,KAAA,CAAM,MAAA;AACtC,EAAA,IAAI,KAAA,CAAM,MAAA,EAAQ,IAAA,CAAK,MAAA,GAAS,KAAA,CAAM,MAAA;AACtC,EAAA,IAAI,KAAA,CAAM,OAAA,EAAS,IAAA,CAAK,OAAA,GAAU,KAAA,CAAM,OAAA;AACxC,EAAA,IAAI,KAAA,CAAM,KAAA,EAAO,IAAA,CAAK,KAAA,GAAQ,KAAA,CAAM,KAAA;AACpC,EAAA,IAAI,KAAA,CAAM,YAAA,EAAc,IAAA,CAAK,YAAA,GAAe,KAAA,CAAM,YAAA;AAClD,EAAA,IAAI,KAAA,CAAM,OAAA,EAAS,IAAA,CAAK,OAAA,GAAU,KAAA,CAAM,OAAA;AAExC,EAAA,OAAO,IAAA;AACR;AAmBO,IAAM,cAAA,GAAiB,UAAA;AAAA,EAC7B,SAASA,eAAAA,CAAe,KAAA,EAAO,GAAA,EAAyC;AACvE,IAAA,MAAM,YAAA,GAAe,OAA8B,IAAI,CAAA;AACvD,IAAA,MAAM,WAAA,GAAc,OAAgB,IAAI,CAAA;AAaxC,IAAA,SAAA,CAAU,MAAM;AACf,MAAA,IAAI,SAAA,GAAY,KAAA;AAChB,MAAA,IAAI,aAAA,GAAiD,IAAA;AAMrD,MAAA,KAAK,OAAO,6BAA6B,CAAA,CACvC,IAAA,CAAK,CAAC,GAAA,KAAQ;AACd,QAAA,IAAI,SAAA,EAAW;AACf,QAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,QAAA,IAAI,CAAC,SAAA,EAAW;AAEhB,QAAA,MAAM,mBAAA,GAAuB,GAAA,CAAI,OAAA,IAAY,GAAA,CAAqC,cAAA;AAIlF,QAAA,IAAI,OAAO,wBAAwB,UAAA,EAAY;AAC9C,UAAA,OAAA,CAAQ,KAAA;AAAA,YACP;AAAA,WACD;AACA,UAAA;AAAA,QACD;AAEA,QAAA,MAAM,IAAA,GAAO,oBAAoB,KAAK,CAAA;AACtC,QAAA,aAAA,GAAgB,IAAI,mBAAA,CAAoB,SAAA,EAAW,IAAI,CAAA;AACvD,QAAA,WAAA,CAAY,OAAA,GAAU,aAAA;AAAA,MACvB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACf,QAAA,OAAA,CAAQ,KAAA,CAAM,mDAAmD,GAAG,CAAA;AAAA,MACrE,CAAC,CAAA;AAEF,MAAA,OAAO,MAAM;AACZ,QAAA,SAAA,GAAY,IAAA;AACZ,QAAA,MAAM,OAAA,GAAU,iBAAkB,WAAA,CAAY,OAAA;AAC9C,QAAA,IAAI,OAAA,IAAW,OAAO,OAAA,CAAQ,OAAA,KAAY,UAAA,EAAY;AACrD,UAAA,IAAI;AACH,YAAA,OAAA,CAAQ,OAAA,EAAQ;AAAA,UACjB,SAAS,GAAA,EAAK;AACb,YAAA,OAAA,CAAQ,IAAA,CAAK,4CAA4C,GAAG,CAAA;AAAA,UAC7D;AAAA,QACD;AACA,QAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AAAA,MACvB,CAAA;AAAA,IAOD,CAAA,EAAG;AAAA,MACF,KAAA,CAAM,GAAA;AAAA,MACN,KAAA,CAAM,SAAA;AAAA,MACN,KAAA,CAAM,OAAA;AAAA,MACN,KAAA,CAAM,aAAA;AAAA,MACN,KAAA,CAAM,MAAA;AAAA,MACN,KAAA,CAAM,OAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM,UAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM,WAAA;AAAA,MACN,KAAA,CAAM,aAAA;AAAA,MACN,KAAA,CAAM,aAAA;AAAA,MACN,KAAA,CAAM,WAAA;AAAA,MACN,KAAA,CAAM,gBAAA;AAAA,MACN,KAAA,CAAM,SAAA;AAAA,MACN,KAAA,CAAM,kBAAA;AAAA,MACN,KAAA,CAAM,eAAA;AAAA,MACN,KAAA,CAAM,WAAA;AAAA,MACN,KAAA,CAAM,YAAA;AAAA,MACN,KAAA,CAAM,iBAAA;AAAA,MACN,KAAA,CAAM,aAAA;AAAA,MACN,KAAA,CAAM,YAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM,aAAA;AAAA,MACN,KAAA,CAAM,OAAA;AAAA,MACN,KAAA,CAAM,WAAA;AAAA,MACN,KAAA,CAAM,OAAA;AAAA,MACN,KAAA,CAAM,WAAA;AAAA,MACN,KAAA,CAAM,KAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM,OAAA;AAAA,MACN,KAAA,CAAM,KAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM,UAAA;AAAA,MACN,KAAA,CAAM,UAAA;AAAA,MACN,KAAA,CAAM,kBAAA;AAAA,MACN,KAAA,CAAM,QAAA;AAAA,MACN,KAAA,CAAM;AAAA,KACN,CAAA;AAQD,IAAA,mBAAA;AAAA,MACC,GAAA;AAAA,MACA,OAAO;AAAA,QACN,IAAA,GAAO;AACN,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,OAAO,MAAM,IAAA,IAAO;AAAA,QACrB,CAAA;AAAA,QACA,KAAA,GAAQ;AACP,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,KAAA,IAAQ;AAAA,QACf,CAAA;AAAA,QACA,UAAA,GAAa;AACZ,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,UAAA,IAAa;AAAA,QACpB,CAAA;AAAA,QACA,OAAO,OAAA,EAAS;AACf,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,SAAS,OAAO,CAAA;AAAA,QACvB,CAAA;AAAA,QACA,cAAc,OAAA,EAAS;AACtB,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,gBAAgB,OAAO,CAAA;AAAA,QAC9B,CAAA;AAAA,QACA,UAAU,MAAA,EAAQ;AACjB,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,YAAY,MAAM,CAAA;AAAA,QACzB,CAAA;AAAA,QACA,gBAAgB,IAAA,EAAM;AACrB,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,kBAAkB,IAAI,CAAA;AAAA,QAC7B,CAAA;AAAA,QACA,gBAAgB,OAAA,EAAS;AACxB,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AACzB,UAAA,IAAA,EAAM,kBAAkB,OAAO,CAAA;AAAA,QAChC,CAAA;AAAA,QACA,WAAA,CAAY,aAAa,QAAA,EAAU;AAClC,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AAGzB,UAAA,IAAA,EAAM,WAAA,GAAc,aAAa,QAAQ,CAAA;AAAA,QAC1C,CAAA;AAAA,QACA,MAAM,SAAA,CAAU,GAAA,EAAK,KAAA,EAAO,UAAU,OAAA,EAAS;AAC9C,UAAA,MAAM,OAAO,WAAA,CAAY,OAAA;AAQzB,UAAA,IAAI,CAAC,MAAM,SAAA,EAAW;AACtB,UAAA,MAAM,IAAA,CAAK,SAAA,CAAU,GAAA,EAAK,KAAA,EAAO,UAAU,OAAO,CAAA;AAAA,QACnD,CAAA;AAAA,QACA,IAAI,QAAA,GAAW;AACd,UAAA,OAAO,WAAA,CAAY,OAAA;AAAA,QACpB;AAAA,OACD,CAAA;AAAA,MACA;AAAC,KACF;AAEA,IAAA,uBACC,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACA,GAAA,EAAK,YAAA;AAAA,QACL,IAAI,KAAA,CAAM,EAAA;AAAA,QACV,SAAA,EAAW,CAAC,UAAA,EAAY,KAAA,CAAM,SAAS,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAAA,QACjE,OAAO,KAAA,CAAM;AAAA;AAAA,KACd;AAAA,EAEF;AACD","file":"index.js","sourcesContent":["/**\n * WaveformPlayer.tsx\n * ------------------\n *\n * React wrapper around `@arraypress/waveform-player`. Mounts a player\n * instance into a `<div>` on first render, tears it down on unmount,\n * and re-mounts when any \"identity\" prop changes (the props whose\n * change requires the library to start over from scratch — `url`,\n * `audioMode`).\n *\n * For non-identity props, this component currently re-creates the\n * instance as well, which is simpler than diffing every option and\n * calling the right granular updater. The trade-off is acceptable\n * because:\n *\n * - The library re-uses any cached waveform data keyed by URL, so\n * re-mounts on the same URL are cheap.\n * - Per-render churn on a player widget is rare in practice.\n *\n * If you need finer control — imperative `loadTrack()`, `seekTo()`,\n * `setVolume()`, etc. — grab the instance through a `ref`:\n *\n * ```tsx\n * import { useRef, useEffect } from 'react';\n * import { WaveformPlayer, type WaveformPlayerHandle } from '@arraypress/waveform-player-react';\n *\n * function MyPlayer() {\n * const ref = useRef<WaveformPlayerHandle>(null);\n * return (\n * <>\n * <WaveformPlayer ref={ref} url=\"/audio/track.mp3\" />\n * <button onClick={() => ref.current?.seekTo(60)}>Jump to 1:00</button>\n * </>\n * );\n * }\n * ```\n *\n * ## Library setup\n *\n * This component does **not** load the core library's CSS for you.\n * Import it once at your app entry:\n *\n * ```ts\n * import '@arraypress/waveform-player/dist/waveform-player.css';\n * ```\n *\n * The library's JS is imported dynamically inside `useEffect` so it\n * only loads on the client (SSR-safe).\n *\n * @module WaveformPlayer\n */\nimport {\n\tforwardRef,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\ttype ForwardedRef,\n} from 'react';\nimport type { WaveformPlayerHandle, WaveformPlayerProps } from './types';\n\n/**\n * Convert a `WaveformPlayerProps` object into the option shape the\n * core library accepts. Most fields pass straight through; this\n * helper exists so the option-building logic is testable on its own\n * and the component body stays focused on lifecycle.\n *\n * @param props - The component's resolved props.\n * @returns An options object to pass into `new WaveformPlayer(el, …)`.\n */\nfunction buildLibraryOptions(props: WaveformPlayerProps): Record<string, unknown> {\n\tconst opts: Record<string, unknown> = {};\n\n\t/* Audio source */\n\tif (props.url !== undefined) opts.url = props.url;\n\tif (props.audioMode !== undefined) opts.audioMode = props.audioMode;\n\tif (props.preload !== undefined) opts.preload = props.preload;\n\n\t/* Waveform visualisation */\n\tif (props.waveformStyle !== undefined) opts.waveformStyle = props.waveformStyle;\n\tif (props.height !== undefined) opts.height = props.height;\n\tif (props.samples !== undefined) opts.samples = props.samples;\n\tif (props.barWidth !== undefined) opts.barWidth = props.barWidth;\n\tif (props.barSpacing !== undefined) opts.barSpacing = props.barSpacing;\n\tif (props.waveform !== undefined && props.waveform !== null) {\n\t\topts.waveform = props.waveform;\n\t}\n\n\t/* Colours */\n\tif (props.colorPreset !== undefined) opts.colorPreset = props.colorPreset;\n\tif (props.waveformColor !== undefined) opts.waveformColor = props.waveformColor;\n\tif (props.progressColor !== undefined) opts.progressColor = props.progressColor;\n\tif (props.buttonColor !== undefined) opts.buttonColor = props.buttonColor;\n\tif (props.buttonHoverColor !== undefined) opts.buttonHoverColor = props.buttonHoverColor;\n\tif (props.textColor !== undefined) opts.textColor = props.textColor;\n\tif (props.textSecondaryColor !== undefined) opts.textSecondaryColor = props.textSecondaryColor;\n\tif (props.backgroundColor !== undefined) opts.backgroundColor = props.backgroundColor;\n\tif (props.borderColor !== undefined) opts.borderColor = props.borderColor;\n\n\t/* Playback controls */\n\tif (props.playbackRate !== undefined) opts.playbackRate = props.playbackRate;\n\tif (props.showPlaybackSpeed !== undefined) opts.showPlaybackSpeed = props.showPlaybackSpeed;\n\tif (props.playbackRates !== undefined) opts.playbackRates = props.playbackRates;\n\n\t/* UI toggles */\n\tif (props.showControls !== undefined) opts.showControls = props.showControls;\n\tif (props.showInfo !== undefined) opts.showInfo = props.showInfo;\n\tif (props.showTime !== undefined) opts.showTime = props.showTime;\n\tif (props.showHoverTime !== undefined) opts.showHoverTime = props.showHoverTime;\n\tif (props.showBPM !== undefined) opts.showBPM = props.showBPM;\n\tif (props.buttonAlign !== undefined) opts.buttonAlign = props.buttonAlign;\n\n\t/* Markers */\n\tif (props.markers !== undefined) opts.markers = props.markers;\n\tif (props.showMarkers !== undefined) opts.showMarkers = props.showMarkers;\n\n\t/* Content metadata */\n\tif (props.title !== undefined) opts.title = props.title;\n\tif (props.subtitle !== undefined) opts.subtitle = props.subtitle;\n\tif (props.artwork !== undefined) opts.artwork = props.artwork;\n\tif (props.album !== undefined) opts.album = props.album;\n\n\t/* Behaviour */\n\tif (props.autoplay !== undefined) opts.autoplay = props.autoplay;\n\tif (props.singlePlay !== undefined) opts.singlePlay = props.singlePlay;\n\tif (props.playOnSeek !== undefined) opts.playOnSeek = props.playOnSeek;\n\tif (props.enableMediaSession !== undefined) opts.enableMediaSession = props.enableMediaSession;\n\n\t/* Icons */\n\tif (props.playIcon !== undefined) opts.playIcon = props.playIcon;\n\tif (props.pauseIcon !== undefined) opts.pauseIcon = props.pauseIcon;\n\n\t/* Callbacks — wired into the library's option-level callbacks\n\t * rather than custom-event listeners. The library invokes\n\t * these synchronously on the respective state change. */\n\tif (props.onLoad) opts.onLoad = props.onLoad;\n\tif (props.onPlay) opts.onPlay = props.onPlay;\n\tif (props.onPause) opts.onPause = props.onPause;\n\tif (props.onEnd) opts.onEnd = props.onEnd;\n\tif (props.onTimeUpdate) opts.onTimeUpdate = props.onTimeUpdate;\n\tif (props.onError) opts.onError = props.onError;\n\n\treturn opts;\n}\n\n/**\n * `WaveformPlayer` — React component wrapping\n * `@arraypress/waveform-player`.\n *\n * Render at the spot you want a waveform-driven audio player to\n * appear. The container `<div>` is rendered immediately for layout;\n * the actual player UI hydrates in once the library loads\n * client-side.\n *\n * @example Basic\n * <WaveformPlayer url=\"/audio/track.mp3\" title=\"My Track\" />\n *\n * @example With ref for imperative control\n * const ref = useRef<WaveformPlayerHandle>(null);\n * <WaveformPlayer ref={ref} url={url} />\n * <button onClick={() => ref.current?.togglePlay()}>Play/Pause</button>\n */\nexport const WaveformPlayer = forwardRef<WaveformPlayerHandle, WaveformPlayerProps>(\n\tfunction WaveformPlayer(props, ref: ForwardedRef<WaveformPlayerHandle>) {\n\t\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\t\tconst instanceRef = useRef<unknown>(null);\n\n\t\t/**\n\t\t * Mount / re-mount lifecycle.\n\t\t *\n\t\t * The dep array intentionally contains EVERY prop the library\n\t\t * uses at construction time. When any of them change, this\n\t\t * effect tears down the old instance and creates a new one\n\t\t * with the updated options. That's simpler and more correct\n\t\t * than trying to partial-update the live instance, and the\n\t\t * library has built-in caches (waveform peaks keyed by URL)\n\t\t * that make same-URL re-mounts cheap.\n\t\t */\n\t\tuseEffect(() => {\n\t\t\tlet cancelled = false;\n\t\t\tlet localInstance: { destroy?: () => void } | null = null;\n\n\t\t\t/* The library is browser-only. Defer the import until we're\n\t\t\t * actually mounting client-side so SSR / RSC don't try to\n\t\t\t * evaluate the audio + canvas + fetch surface on the server.\n\t\t\t */\n\t\t\tvoid import('@arraypress/waveform-player')\n\t\t\t\t.then((mod) => {\n\t\t\t\t\tif (cancelled) return;\n\t\t\t\t\tconst container = containerRef.current;\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst WaveformPlayerClass = (mod.default ?? (mod as { WaveformPlayer?: unknown }).WaveformPlayer) as {\n\t\t\t\t\t\tnew (el: HTMLElement, opts: Record<string, unknown>): { destroy?: () => void };\n\t\t\t\t\t};\n\n\t\t\t\t\tif (typeof WaveformPlayerClass !== 'function') {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t'[waveform-player-react] Failed to resolve WaveformPlayer constructor from module.'\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst opts = buildLibraryOptions(props);\n\t\t\t\t\tlocalInstance = new WaveformPlayerClass(container, opts);\n\t\t\t\t\tinstanceRef.current = localInstance;\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error('[waveform-player-react] Failed to load library:', err);\n\t\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tcancelled = true;\n\t\t\t\tconst current = localInstance ?? (instanceRef.current as { destroy?: () => void } | null);\n\t\t\t\tif (current && typeof current.destroy === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcurrent.destroy();\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconsole.warn('[waveform-player-react] destroy() threw:', err);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinstanceRef.current = null;\n\t\t\t};\n\t\t\t/* Re-mount on any prop change. Listed exhaustively rather\n\t\t\t * than spread to make the intent explicit and to keep the\n\t\t\t * lint rule happy. Callbacks intentionally NOT in deps:\n\t\t\t * a parent re-rendering with a fresh inline function\n\t\t\t * shouldn't tear the player down. */\n\t\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t\t}, [\n\t\t\tprops.url,\n\t\t\tprops.audioMode,\n\t\t\tprops.preload,\n\t\t\tprops.waveformStyle,\n\t\t\tprops.height,\n\t\t\tprops.samples,\n\t\t\tprops.barWidth,\n\t\t\tprops.barSpacing,\n\t\t\tprops.waveform,\n\t\t\tprops.colorPreset,\n\t\t\tprops.waveformColor,\n\t\t\tprops.progressColor,\n\t\t\tprops.buttonColor,\n\t\t\tprops.buttonHoverColor,\n\t\t\tprops.textColor,\n\t\t\tprops.textSecondaryColor,\n\t\t\tprops.backgroundColor,\n\t\t\tprops.borderColor,\n\t\t\tprops.playbackRate,\n\t\t\tprops.showPlaybackSpeed,\n\t\t\tprops.playbackRates,\n\t\t\tprops.showControls,\n\t\t\tprops.showInfo,\n\t\t\tprops.showTime,\n\t\t\tprops.showHoverTime,\n\t\t\tprops.showBPM,\n\t\t\tprops.buttonAlign,\n\t\t\tprops.markers,\n\t\t\tprops.showMarkers,\n\t\t\tprops.title,\n\t\t\tprops.subtitle,\n\t\t\tprops.artwork,\n\t\t\tprops.album,\n\t\t\tprops.autoplay,\n\t\t\tprops.singlePlay,\n\t\t\tprops.playOnSeek,\n\t\t\tprops.enableMediaSession,\n\t\t\tprops.playIcon,\n\t\t\tprops.pauseIcon,\n\t\t]);\n\n\t\t/**\n\t\t * Expose an imperative handle on the forwarded ref. Each\n\t\t * method is a thin pass-through to the live instance — if the\n\t\t * instance hasn't mounted yet (still loading async), calls are\n\t\t * no-ops (`pause`, `seekTo`, etc. return `undefined`).\n\t\t */\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tplay() {\n\t\t\t\t\tconst inst = instanceRef.current as { play?: () => Promise<void> | undefined } | null;\n\t\t\t\t\treturn inst?.play?.();\n\t\t\t\t},\n\t\t\t\tpause() {\n\t\t\t\t\tconst inst = instanceRef.current as { pause?: () => void } | null;\n\t\t\t\t\tinst?.pause?.();\n\t\t\t\t},\n\t\t\t\ttogglePlay() {\n\t\t\t\t\tconst inst = instanceRef.current as { togglePlay?: () => void } | null;\n\t\t\t\t\tinst?.togglePlay?.();\n\t\t\t\t},\n\t\t\t\tseekTo(seconds) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekTo?: (s: number) => void } | null;\n\t\t\t\t\tinst?.seekTo?.(seconds);\n\t\t\t\t},\n\t\t\t\tseekToPercent(percent) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekToPercent?: (p: number) => void } | null;\n\t\t\t\t\tinst?.seekToPercent?.(percent);\n\t\t\t\t},\n\t\t\t\tsetVolume(volume) {\n\t\t\t\t\tconst inst = instanceRef.current as { setVolume?: (v: number) => void } | null;\n\t\t\t\t\tinst?.setVolume?.(volume);\n\t\t\t\t},\n\t\t\t\tsetPlaybackRate(rate) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlaybackRate?: (r: number) => void } | null;\n\t\t\t\t\tinst?.setPlaybackRate?.(rate);\n\t\t\t\t},\n\t\t\t\tsetPlayingState(playing) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlayingState?: (p: boolean) => void } | null;\n\t\t\t\t\tinst?.setPlayingState?.(playing);\n\t\t\t\t},\n\t\t\t\tsetProgress(currentTime, duration) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tsetProgress?: (c: number, d: number) => void;\n\t\t\t\t\t} | null;\n\t\t\t\t\tinst?.setProgress?.(currentTime, duration);\n\t\t\t\t},\n\t\t\t\tasync loadTrack(url, title, subtitle, options) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tloadTrack?: (\n\t\t\t\t\t\t\tu: string,\n\t\t\t\t\t\t\tt?: string,\n\t\t\t\t\t\t\ts?: string,\n\t\t\t\t\t\t\to?: Record<string, unknown>\n\t\t\t\t\t\t) => Promise<void>;\n\t\t\t\t\t} | null;\n\t\t\t\t\tif (!inst?.loadTrack) return;\n\t\t\t\t\tawait inst.loadTrack(url, title, subtitle, options);\n\t\t\t\t},\n\t\t\t\tget instance() {\n\t\t\t\t\treturn instanceRef.current;\n\t\t\t\t},\n\t\t\t}),\n\t\t\t[]\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tid={props.id}\n\t\t\t\tclassName={['wfp-host', props.className].filter(Boolean).join(' ')}\n\t\t\t\tstyle={props.style}\n\t\t\t/>\n\t\t);\n\t}\n);\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/WaveformPlayer.tsx"],"names":["WaveformPlayer"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * WaveformPlayer.tsx\n * ------------------\n *\n * React wrapper around `@arraypress/waveform-player`. Mounts a player\n * instance into a `<div>` on first render, tears it down on unmount,\n * and re-mounts when any \"identity\" prop changes (the props whose\n * change requires the library to start over from scratch — `url`,\n * `audioMode`).\n *\n * For non-identity props, this component currently re-creates the\n * instance as well, which is simpler than diffing every option and\n * calling the right granular updater. The trade-off is acceptable\n * because:\n *\n * - The library re-uses any cached waveform data keyed by URL, so\n * re-mounts on the same URL are cheap.\n * - Per-render churn on a player widget is rare in practice.\n *\n * If you need finer control — imperative `loadTrack()`, `seekTo()`,\n * `setVolume()`, etc. — grab the instance through a `ref`:\n *\n * ```tsx\n * import { useRef, useEffect } from 'react';\n * import { WaveformPlayer, type WaveformPlayerHandle } from '@arraypress/waveform-player-react';\n *\n * function MyPlayer() {\n * const ref = useRef<WaveformPlayerHandle>(null);\n * return (\n * <>\n * <WaveformPlayer ref={ref} url=\"/audio/track.mp3\" />\n * <button onClick={() => ref.current?.seekTo(60)}>Jump to 1:00</button>\n * </>\n * );\n * }\n * ```\n *\n * ## Library setup\n *\n * This component does **not** load the core library's CSS for you.\n * Import it once at your app entry:\n *\n * ```ts\n * import '@arraypress/waveform-player/dist/waveform-player.css';\n * ```\n *\n * The library's JS is imported dynamically inside `useEffect` so it\n * only loads on the client (SSR-safe).\n *\n * @module WaveformPlayer\n */\nimport {\n\tforwardRef,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseLayoutEffect,\n\tuseRef,\n\ttype ForwardedRef,\n} from 'react';\n// Aliased to avoid colliding with this file's own `WaveformPlayer`\n// component export. This is the core library's player class type.\nimport type { WaveformPlayer as WaveformPlayerInstance } from '@arraypress/waveform-player';\nimport type { WaveformPlayerHandle, WaveformPlayerProps } from './types';\n\n/**\n * Convert a `WaveformPlayerProps` object into the option shape the\n * core library accepts. Most fields pass straight through; this\n * helper exists so the option-building logic is testable on its own\n * and the component body stays focused on lifecycle.\n *\n * @param props - The component's resolved props.\n * @returns An options object to pass into `new WaveformPlayer(el, …)`.\n */\nfunction buildLibraryOptions(props: WaveformPlayerProps): Record<string, unknown> {\n\tconst opts: Record<string, unknown> = {};\n\n\t/* Audio source — `src` is the core's shorthand alias for `url`. */\n\tif (props.url !== undefined) opts.url = props.url;\n\telse if (props.src !== undefined) opts.url = props.src;\n\tif (props.audioMode !== undefined) opts.audioMode = props.audioMode;\n\tif (props.preload !== undefined) opts.preload = props.preload;\n\n\t/* Waveform visualisation */\n\tif (props.waveformStyle !== undefined) opts.waveformStyle = props.waveformStyle;\n\tif (props.height !== undefined) opts.height = props.height;\n\tif (props.samples !== undefined) opts.samples = props.samples;\n\tif (props.barWidth !== undefined) opts.barWidth = props.barWidth;\n\tif (props.barSpacing !== undefined) opts.barSpacing = props.barSpacing;\n\tif (props.barRadius !== undefined) opts.barRadius = props.barRadius;\n\tif (props.waveform !== undefined && props.waveform !== null) {\n\t\topts.waveform = props.waveform;\n\t}\n\n\t/* Colours */\n\tif (props.colorPreset !== undefined) opts.colorPreset = props.colorPreset;\n\tif (props.waveformColor !== undefined) opts.waveformColor = props.waveformColor;\n\tif (props.progressColor !== undefined) opts.progressColor = props.progressColor;\n\tif (props.buttonColor !== undefined) opts.buttonColor = props.buttonColor;\n\tif (props.buttonHoverColor !== undefined) opts.buttonHoverColor = props.buttonHoverColor;\n\tif (props.textColor !== undefined) opts.textColor = props.textColor;\n\tif (props.textSecondaryColor !== undefined) opts.textSecondaryColor = props.textSecondaryColor;\n\tif (props.backgroundColor !== undefined) opts.backgroundColor = props.backgroundColor;\n\tif (props.borderColor !== undefined) opts.borderColor = props.borderColor;\n\n\t/* Playback controls */\n\tif (props.playbackRate !== undefined) opts.playbackRate = props.playbackRate;\n\tif (props.showPlaybackSpeed !== undefined) opts.showPlaybackSpeed = props.showPlaybackSpeed;\n\tif (props.playbackRates !== undefined) opts.playbackRates = props.playbackRates;\n\n\t/* UI toggles */\n\tif (props.showControls !== undefined) opts.showControls = props.showControls;\n\tif (props.showInfo !== undefined) opts.showInfo = props.showInfo;\n\tif (props.showTime !== undefined) opts.showTime = props.showTime;\n\tif (props.showHoverTime !== undefined) opts.showHoverTime = props.showHoverTime;\n\tif (props.showBPM !== undefined) opts.showBPM = props.showBPM;\n\tif (props.bpm !== undefined) opts.bpm = props.bpm;\n\tif (props.buttonAlign !== undefined) opts.buttonAlign = props.buttonAlign;\n\tif (props.layout !== undefined) opts.layout = props.layout;\n\tif (props.buttonStyle !== undefined) opts.buttonStyle = props.buttonStyle;\n\n\t/* Accessibility */\n\tif (props.accessibleSeek !== undefined) opts.accessibleSeek = props.accessibleSeek;\n\tif (props.seekLabel !== undefined) opts.seekLabel = props.seekLabel;\n\n\t/* Error UI */\n\tif (props.errorText !== undefined) opts.errorText = props.errorText;\n\n\t/* Markers */\n\tif (props.markers !== undefined) opts.markers = props.markers;\n\tif (props.showMarkers !== undefined) opts.showMarkers = props.showMarkers;\n\n\t/* Content metadata */\n\tif (props.title !== undefined) opts.title = props.title;\n\tif (props.subtitle !== undefined) opts.subtitle = props.subtitle;\n\tif (props.artwork !== undefined) opts.artwork = props.artwork;\n\tif (props.album !== undefined) opts.album = props.album;\n\n\t/* Behaviour */\n\tif (props.autoplay !== undefined) opts.autoplay = props.autoplay;\n\tif (props.singlePlay !== undefined) opts.singlePlay = props.singlePlay;\n\tif (props.playOnSeek !== undefined) opts.playOnSeek = props.playOnSeek;\n\tif (props.enableMediaSession !== undefined) opts.enableMediaSession = props.enableMediaSession;\n\n\t/* Icons */\n\tif (props.playIcon !== undefined) opts.playIcon = props.playIcon;\n\tif (props.pauseIcon !== undefined) opts.pauseIcon = props.pauseIcon;\n\n\t/* Callbacks are intentionally NOT mapped here. They are wired in\n\t * the mount effect as *stable* wrapper functions that read the\n\t * latest handlers from `callbacksRef` at call time. That way a\n\t * parent re-render passing fresh inline callbacks is always seen\n\t * by the core without re-creating the player — and the callbacks\n\t * never capture stale first-mount state (stale-closure bug). */\n\n\treturn opts;\n}\n\n/**\n * `WaveformPlayer` — React component wrapping\n * `@arraypress/waveform-player`.\n *\n * Render at the spot you want a waveform-driven audio player to\n * appear. The container `<div>` is rendered immediately for layout;\n * the actual player UI hydrates in once the library loads\n * client-side.\n *\n * @example Basic\n * <WaveformPlayer url=\"/audio/track.mp3\" title=\"My Track\" />\n *\n * @example With ref for imperative control\n * const ref = useRef<WaveformPlayerHandle>(null);\n * <WaveformPlayer ref={ref} url={url} />\n * <button onClick={() => ref.current?.togglePlay()}>Play/Pause</button>\n */\nexport const WaveformPlayer = forwardRef<WaveformPlayerHandle, WaveformPlayerProps>(\n\tfunction WaveformPlayer(props, ref: ForwardedRef<WaveformPlayerHandle>) {\n\t\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\t\tconst instanceRef = useRef<unknown>(null);\n\n\t\t/**\n\t\t * Latest user callbacks, kept in a ref so the wrappers handed to\n\t\t * the core (built once at mount, below) always invoke the most\n\t\t * recent handler instead of the ones captured on first mount.\n\t\t *\n\t\t * Initialised from this render's props and refreshed every render\n\t\t * by the layout effect underneath — never listed in the mount\n\t\t * effect's deps, so updating a callback does NOT tear the player\n\t\t * down.\n\t\t */\n\t\tconst callbacksRef = useRef<\n\t\t\tPick<\n\t\t\t\tWaveformPlayerProps,\n\t\t\t\t'onLoad' | 'onPlay' | 'onPause' | 'onEnd' | 'onTimeUpdate' | 'onError'\n\t\t\t>\n\t\t>({\n\t\t\tonLoad: props.onLoad,\n\t\t\tonPlay: props.onPlay,\n\t\t\tonPause: props.onPause,\n\t\t\tonEnd: props.onEnd,\n\t\t\tonTimeUpdate: props.onTimeUpdate,\n\t\t\tonError: props.onError,\n\t\t});\n\n\t\t/* Refresh the ref on every render (before paint) so the stable\n\t\t * wrappers below always read the latest handlers. */\n\t\tuseLayoutEffect(() => {\n\t\t\tcallbacksRef.current = {\n\t\t\t\tonLoad: props.onLoad,\n\t\t\t\tonPlay: props.onPlay,\n\t\t\t\tonPause: props.onPause,\n\t\t\t\tonEnd: props.onEnd,\n\t\t\t\tonTimeUpdate: props.onTimeUpdate,\n\t\t\t\tonError: props.onError,\n\t\t\t};\n\t\t});\n\n\t\t/**\n\t\t * Mount / re-mount lifecycle.\n\t\t *\n\t\t * The dep array intentionally contains EVERY prop the library\n\t\t * uses at construction time. When any of them change, this\n\t\t * effect tears down the old instance and creates a new one\n\t\t * with the updated options. That's simpler and more correct\n\t\t * than trying to partial-update the live instance, and the\n\t\t * library has built-in caches (waveform peaks keyed by URL)\n\t\t * that make same-URL re-mounts cheap.\n\t\t */\n\t\tuseEffect(() => {\n\t\t\tlet cancelled = false;\n\t\t\tlet localInstance: { destroy?: () => void } | null = null;\n\n\t\t\t/* The library is browser-only. Defer the import until we're\n\t\t\t * actually mounting client-side so SSR / RSC don't try to\n\t\t\t * evaluate the audio + canvas + fetch surface on the server.\n\t\t\t */\n\t\t\tvoid import('@arraypress/waveform-player')\n\t\t\t\t.then((mod) => {\n\t\t\t\t\tif (cancelled) return;\n\t\t\t\t\tconst container = containerRef.current;\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst WaveformPlayerClass = (mod.default ?? (mod as { WaveformPlayer?: unknown }).WaveformPlayer) as {\n\t\t\t\t\t\tnew (el: HTMLElement, opts: Record<string, unknown>): { destroy?: () => void };\n\t\t\t\t\t};\n\n\t\t\t\t\tif (typeof WaveformPlayerClass !== 'function') {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t'[WaveformPlayerReact] Failed to resolve WaveformPlayer constructor from module.'\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst opts = buildLibraryOptions(props);\n\n\t\t\t\t\t/* Stable callback wrappers. Created once per player\n\t\t\t\t\t * instance and never change identity, so the core holds a\n\t\t\t\t\t * fixed reference, yet each call reads the *current*\n\t\t\t\t\t * handler from `callbacksRef` — fixing the stale-closure\n\t\t\t\t\t * bug without re-mounting on callback changes. Signatures\n\t\t\t\t\t * mirror the core's option callbacks exactly. */\n\t\t\t\t\topts.onLoad = (instance: WaveformPlayerInstance) => callbacksRef.current.onLoad?.(instance);\n\t\t\t\t\topts.onPlay = (instance: WaveformPlayerInstance) => callbacksRef.current.onPlay?.(instance);\n\t\t\t\t\topts.onPause = (instance: WaveformPlayerInstance) => callbacksRef.current.onPause?.(instance);\n\t\t\t\t\topts.onEnd = (instance: WaveformPlayerInstance) => callbacksRef.current.onEnd?.(instance);\n\t\t\t\t\topts.onTimeUpdate = (currentTime: number, duration: number, instance: WaveformPlayerInstance) =>\n\t\t\t\t\t\tcallbacksRef.current.onTimeUpdate?.(currentTime, duration, instance);\n\t\t\t\t\topts.onError = (error: Error, instance: WaveformPlayerInstance) =>\n\t\t\t\t\t\tcallbacksRef.current.onError?.(error, instance);\n\n\t\t\t\t\tlocalInstance = new WaveformPlayerClass(container, opts);\n\t\t\t\t\tinstanceRef.current = localInstance;\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error('[WaveformPlayerReact] Failed to load library:', err);\n\t\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tcancelled = true;\n\t\t\t\tconst current = localInstance ?? (instanceRef.current as { destroy?: () => void } | null);\n\t\t\t\tif (current && typeof current.destroy === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcurrent.destroy();\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconsole.warn('[WaveformPlayerReact] destroy() threw:', err);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinstanceRef.current = null;\n\t\t\t};\n\t\t\t/* Re-mount on any prop change. Listed exhaustively rather\n\t\t\t * than spread to make the intent explicit and to keep the\n\t\t\t * lint rule happy. Callbacks intentionally NOT in deps:\n\t\t\t * a parent re-rendering with a fresh inline function\n\t\t\t * shouldn't tear the player down. */\n\t\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t\t}, [\n\t\t\tprops.url,\n\t\t\tprops.src,\n\t\t\tprops.audioMode,\n\t\t\tprops.preload,\n\t\t\tprops.waveformStyle,\n\t\t\tprops.height,\n\t\t\tprops.samples,\n\t\t\tprops.barWidth,\n\t\t\tprops.barSpacing,\n\t\t\tprops.barRadius,\n\t\t\tprops.waveform,\n\t\t\tprops.colorPreset,\n\t\t\tprops.waveformColor,\n\t\t\tprops.progressColor,\n\t\t\tprops.buttonColor,\n\t\t\tprops.buttonHoverColor,\n\t\t\tprops.textColor,\n\t\t\tprops.textSecondaryColor,\n\t\t\tprops.backgroundColor,\n\t\t\tprops.borderColor,\n\t\t\tprops.playbackRate,\n\t\t\tprops.showPlaybackSpeed,\n\t\t\tprops.playbackRates,\n\t\t\tprops.showControls,\n\t\t\tprops.showInfo,\n\t\t\tprops.showTime,\n\t\t\tprops.showHoverTime,\n\t\t\tprops.showBPM,\n\t\t\tprops.buttonAlign,\n\t\t\tprops.accessibleSeek,\n\t\t\tprops.seekLabel,\n\t\t\tprops.errorText,\n\t\t\tprops.markers,\n\t\t\tprops.showMarkers,\n\t\t\tprops.title,\n\t\t\tprops.subtitle,\n\t\t\tprops.artwork,\n\t\t\tprops.album,\n\t\t\tprops.autoplay,\n\t\t\tprops.singlePlay,\n\t\t\tprops.playOnSeek,\n\t\t\tprops.enableMediaSession,\n\t\t\tprops.playIcon,\n\t\t\tprops.pauseIcon,\n\t\t]);\n\n\t\t/**\n\t\t * Expose an imperative handle on the forwarded ref. Each\n\t\t * method is a thin pass-through to the live instance — if the\n\t\t * instance hasn't mounted yet (still loading async), calls are\n\t\t * no-ops (`pause`, `seekTo`, etc. return `undefined`).\n\t\t */\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tplay() {\n\t\t\t\t\tconst inst = instanceRef.current as { play?: () => Promise<void> | undefined } | null;\n\t\t\t\t\treturn inst?.play?.();\n\t\t\t\t},\n\t\t\t\tpause() {\n\t\t\t\t\tconst inst = instanceRef.current as { pause?: () => void } | null;\n\t\t\t\t\tinst?.pause?.();\n\t\t\t\t},\n\t\t\t\ttogglePlay() {\n\t\t\t\t\tconst inst = instanceRef.current as { togglePlay?: () => void } | null;\n\t\t\t\t\tinst?.togglePlay?.();\n\t\t\t\t},\n\t\t\t\tseekTo(seconds) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekTo?: (s: number) => void } | null;\n\t\t\t\t\tinst?.seekTo?.(seconds);\n\t\t\t\t},\n\t\t\t\tseekToPercent(percent) {\n\t\t\t\t\tconst inst = instanceRef.current as { seekToPercent?: (p: number) => void } | null;\n\t\t\t\t\tinst?.seekToPercent?.(percent);\n\t\t\t\t},\n\t\t\t\tsetVolume(volume) {\n\t\t\t\t\tconst inst = instanceRef.current as { setVolume?: (v: number) => void } | null;\n\t\t\t\t\tinst?.setVolume?.(volume);\n\t\t\t\t},\n\t\t\t\tsetPlaybackRate(rate) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlaybackRate?: (r: number) => void } | null;\n\t\t\t\t\tinst?.setPlaybackRate?.(rate);\n\t\t\t\t},\n\t\t\t\tsetPlayingState(playing) {\n\t\t\t\t\tconst inst = instanceRef.current as { setPlayingState?: (p: boolean) => void } | null;\n\t\t\t\t\tinst?.setPlayingState?.(playing);\n\t\t\t\t},\n\t\t\t\tsetProgress(currentTime, duration) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tsetProgress?: (c: number, d: number) => void;\n\t\t\t\t\t} | null;\n\t\t\t\t\tinst?.setProgress?.(currentTime, duration);\n\t\t\t\t},\n\t\t\t\tasync loadTrack(url, title, subtitle, options) {\n\t\t\t\t\tconst inst = instanceRef.current as {\n\t\t\t\t\t\tloadTrack?: (\n\t\t\t\t\t\t\tu: string,\n\t\t\t\t\t\t\tt?: string,\n\t\t\t\t\t\t\ts?: string,\n\t\t\t\t\t\t\to?: Record<string, unknown>\n\t\t\t\t\t\t) => Promise<void>;\n\t\t\t\t\t} | null;\n\t\t\t\t\tif (!inst?.loadTrack) return;\n\t\t\t\t\tawait inst.loadTrack(url, title, subtitle, options);\n\t\t\t\t},\n\t\t\t\tget instance() {\n\t\t\t\t\treturn instanceRef.current as WaveformPlayerInstance;\n\t\t\t\t},\n\t\t\t}),\n\t\t\t[]\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tid={props.id}\n\t\t\t\tclassName={['wfp-host', props.className].filter(Boolean).join(' ')}\n\t\t\t\tstyle={props.style}\n\t\t\t/>\n\t\t);\n\t}\n);\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arraypress/waveform-player-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "React component wrapper for @arraypress/waveform-player — forwardRef-friendly, useEffect lifecycle, typed props for every library option.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"sideEffects": false,
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@arraypress/waveform-player": "^1.
|
|
62
|
+
"@arraypress/waveform-player": "^1.8.0",
|
|
63
63
|
"react": "^18.0.0 || ^19.0.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
@@ -71,16 +71,16 @@
|
|
|
71
71
|
"prepublishOnly": "npm run build"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@arraypress/waveform-player": "^1.
|
|
74
|
+
"@arraypress/waveform-player": "^1.12.0",
|
|
75
75
|
"@testing-library/jest-dom": "^6.9.1",
|
|
76
76
|
"@testing-library/react": "^16.3.2",
|
|
77
|
-
"@types/react": "^19.2.
|
|
77
|
+
"@types/react": "^19.2.17",
|
|
78
78
|
"@types/react-dom": "^19.2.3",
|
|
79
79
|
"jsdom": "^29.1.1",
|
|
80
|
-
"react": "^19.2.
|
|
81
|
-
"react-dom": "^19.2.
|
|
80
|
+
"react": "^19.2.7",
|
|
81
|
+
"react-dom": "^19.2.7",
|
|
82
82
|
"tsup": "^8.5.1",
|
|
83
83
|
"typescript": "^6.0.3",
|
|
84
|
-
"vitest": "^4.1.
|
|
84
|
+
"vitest": "^4.1.9"
|
|
85
85
|
}
|
|
86
86
|
}
|