react-modern-audio-player 2.1.0 → 2.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/README.md +138 -3
- package/dist/index.es.js +1288 -1163
- package/dist/types/components/AudioPlayer/Context/StateContext/index.d.ts +1 -0
- package/dist/types/components/AudioPlayer/Context/UIContext.d.ts +1 -0
- package/dist/types/{hooks/context → components/AudioPlayer/Context/hooks}/index.d.ts +0 -1
- package/dist/types/components/AudioPlayer/Context/hooks/useAudioAttrsContext.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Context/hooks/usePlaybackContext.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Context/hooks/useResourceContext.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Context/hooks/useTimeContext.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Context/hooks/useTrackContext.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Context/hooks/useUIContext.d.ts +1 -0
- package/dist/types/components/AudioPlayer/Context/index.d.ts +1 -0
- package/dist/types/components/AudioPlayer/Interface/Controller/Button/RepeatTypeBtn.d.ts +3 -1
- package/dist/types/components/AudioPlayer/Interface/Controller/Button/TransportControls/index.d.ts +4 -0
- package/dist/types/components/AudioPlayer/Interface/Controller/Button/index.d.ts +4 -3
- package/dist/types/components/AudioPlayer/Interface/Controller/Drawer/SortablePlayList/index.d.ts +5 -1
- package/dist/types/components/AudioPlayer/Interface/Controller/Input/Progress/index.d.ts +12 -1
- package/dist/types/components/AudioPlayer/Interface/Controller/Tooltip/Volume/index.d.ts +3 -1
- package/dist/types/components/AudioPlayer/Interface/Information/Artwork.d.ts +3 -1
- package/dist/types/components/AudioPlayer/Interface/Information/TrackInfo.d.ts +3 -1
- package/dist/types/components/AudioPlayer/Interface/Information/TrackTime/index.d.ts +3 -1
- package/dist/types/components/AudioPlayer/Interface/PlayListEmpty/index.d.ts +6 -0
- package/dist/types/components/AudioPlayer/Interface/compound/index.d.ts +3 -0
- package/dist/types/components/AudioPlayer/Interface/compound/slotMetaMap.d.ts +10 -0
- package/dist/types/components/AudioPlayer/Interface/compound/useCompoundSlots.d.ts +6 -0
- package/dist/types/components/AudioPlayer/Interface/compound/useDuplicateSlotWarning.d.ts +7 -0
- package/dist/types/components/AudioPlayer/Interface/contexts/index.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Interface/contexts/playListEmptyContext.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Interface/contexts/playListPortalContext.d.ts +1 -0
- package/dist/types/components/AudioPlayer/Interface/hooks/index.d.ts +2 -0
- package/dist/types/components/AudioPlayer/Interface/hooks/useGridTemplate.d.ts +3 -0
- package/dist/types/components/AudioPlayer/Interface/hooks/useResolvedGridArea.d.ts +2 -0
- package/dist/types/components/AudioPlayer/index.d.ts +23 -2
- package/dist/types/components/Drawer/Drawer.d.ts +1 -0
- package/dist/types/components/Grid/Item.d.ts +3 -2
- package/dist/types/components/Grid/index.d.ts +1 -0
- package/dist/types/hooks/index.d.ts +1 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/utils/ssr.d.ts +0 -2
- package/llms.txt +61 -0
- package/package.json +4 -2
- package/dist/types/hooks/context/useAudioAttrsContext.d.ts +0 -2
- package/dist/types/hooks/context/usePlaybackContext.d.ts +0 -2
- package/dist/types/hooks/context/useResourceContext.d.ts +0 -2
- package/dist/types/hooks/context/useTimeContext.d.ts +0 -2
- package/dist/types/hooks/context/useTrackContext.d.ts +0 -2
- package/dist/types/hooks/context/useUIContext.d.ts +0 -1
- package/dist/types/hooks/useGridTemplate.d.ts +0 -2
- /package/dist/types/components/AudioPlayer/Interface/Controller/Button/{NextBtn.d.ts → TransportControls/NextBtn.d.ts} +0 -0
- /package/dist/types/components/AudioPlayer/Interface/Controller/Button/{PlayBtn.d.ts → TransportControls/PlayBtn.d.ts} +0 -0
- /package/dist/types/components/AudioPlayer/Interface/Controller/Button/{PrevBtn.d.ts → TransportControls/PrevBtn.d.ts} +0 -0
- /package/dist/types/{utils → components/AudioPlayer/utils}/clampVolume.d.ts +0 -0
- /package/dist/types/hooks/{context/useNonNullableContext.d.ts → useNonNullableContext.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
<a href="https://www.npmjs.com/package/react-modern-audio-player">
|
|
16
16
|
<img src="https://img.shields.io/npm/dt/react-modern-audio-player" alt="Download">
|
|
17
17
|
</a>
|
|
18
|
-
<a href="https://bundlephobia.com/package/react-modern-audio-player
|
|
18
|
+
<a href="https://bundlephobia.com/package/react-modern-audio-player">
|
|
19
19
|
<img src="https://img.shields.io/bundlephobia/minzip/react-modern-audio-player" alt="BundleSize">
|
|
20
20
|
</a>
|
|
21
21
|
<a href="https://github.com/slash9494/react-modern-audio-player/actions/workflows/integration.yml">
|
|
22
|
-
<img src="https://
|
|
22
|
+
<img src="https://img.shields.io/github/actions/workflow/status/slash9494/react-modern-audio-player/integration.yml?branch=main&label=CI" alt="CI">
|
|
23
23
|
</a>
|
|
24
24
|
<img src="https://img.shields.io/badge/TypeScript-first-3178C6?logo=typescript&logoColor=white" alt="TypeScript">
|
|
25
25
|
</p>
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
- **Waveform** progress bar powered by `wavesurfer.js`
|
|
30
30
|
- **Playlist** with drag-and-drop reorder, repeat, shuffle
|
|
31
31
|
- **Fully customizable** — swap any sub-component, CSS variable theming, light & dark themes
|
|
32
|
+
- **Compound slots** — `AudioPlayer.Volume`, `AudioPlayer.Progress`, `AudioPlayer.PlayList`, etc. for partial customization without losing the preset
|
|
33
|
+
- **Multi-instance playlist** — render multiple players on the same page with isolated playlist drawers and fully independent audio state
|
|
32
34
|
- **Accessible** — WAI-ARIA patterns, full keyboard navigation, axe-tested
|
|
33
35
|
- **TypeScript-first** — typed props and hooks (`useAudioPlayer`, sub-hooks)
|
|
34
36
|
- **SSR-friendly** — works with Next.js App Router / Server Components
|
|
@@ -154,8 +156,9 @@ export default function PlayerPage() {
|
|
|
154
156
|
| **Props** | [PlayList](#playlist) · [InitialStates](#initialstates) · [ActiveUI](#activeui) · [Placement](#placement) · [RootContainerProps](#rootcontainerprops) |
|
|
155
157
|
| **Override & Style** | [CustomIcons](#customicons) · [CoverImgsCss](#coverimgscss) · [Theme mode](#theme-mode-dark-mode) · [ID & Classnames](#id--classnames) |
|
|
156
158
|
| **Player Hook API** | [useAudioPlayer](#useaudioplayer) · [AudioPlayerControls](#audioplayercontrols) · [Sub-Hooks](#sub-hooks) |
|
|
157
|
-
| **Custom Component** | [Custom Component](#custom-component)
|
|
159
|
+
| **Custom Component** | [Custom Component](#custom-component) · [Compound Slots](#compound-slots) |
|
|
158
160
|
| **Accessibility** | [Keyboard support](#keyboard-support) |
|
|
161
|
+
| **Gotchas** | [Gotchas](#gotchas) |
|
|
159
162
|
| **Example** | [Example](#example) |
|
|
160
163
|
|
|
161
164
|
# Props
|
|
@@ -238,9 +241,12 @@ type InitialStates = Omit<
|
|
|
238
241
|
currentTime?: number;
|
|
239
242
|
duration?: number;
|
|
240
243
|
curPlayId: number;
|
|
244
|
+
playListExpanded?: boolean;
|
|
241
245
|
};
|
|
242
246
|
```
|
|
243
247
|
|
|
248
|
+
> `playListExpanded: true` opens the playlist drawer on mount. Consistent with the other fields on `audioInitialState`, this is read once at mount and is not tracked in reducer state.
|
|
249
|
+
|
|
244
250
|
## ActiveUI
|
|
245
251
|
|
|
246
252
|
```tsx
|
|
@@ -379,6 +385,12 @@ const defaultInterfacePlacement = {
|
|
|
379
385
|
|
|
380
386
|
- rm-audio-player
|
|
381
387
|
|
|
388
|
+
> **Multi-instance note**: when multiple `<AudioPlayer>` instances share a
|
|
389
|
+
> page the root `id` is duplicated across them. Playlist and audio state are
|
|
390
|
+
> still isolated per instance (each player has its own React provider tree
|
|
391
|
+
> and its own `<audio>` DOM node). If you need per-instance selectors, target
|
|
392
|
+
> via the class names below rather than the id.
|
|
393
|
+
|
|
382
394
|
### root ClassName
|
|
383
395
|
|
|
384
396
|
- rmap-player-provider
|
|
@@ -590,6 +602,116 @@ const CustomComponent = () => {
|
|
|
590
602
|
</AudioPlayer>;
|
|
591
603
|
```
|
|
592
604
|
|
|
605
|
+
# Compound Slots
|
|
606
|
+
|
|
607
|
+
`AudioPlayer` exposes its built-in controls as static members so you can re-place or augment individual pieces without rebuilding the whole layout.
|
|
608
|
+
|
|
609
|
+
| Member | Renders |
|
|
610
|
+
| --- | --- |
|
|
611
|
+
| `AudioPlayer.Progress` | progress bar / waveform |
|
|
612
|
+
| `AudioPlayer.Volume` | volume trigger + slider |
|
|
613
|
+
| `AudioPlayer.PlayList` | sortable playlist drawer (accepts `initialExpanded?`) |
|
|
614
|
+
| `AudioPlayer.PlayListEmpty` | fallback rendered inside the playlist drawer when `playList` is empty |
|
|
615
|
+
| [`AudioPlayer.PlayButton`](#audioplayerplaybutton-and-activeuiprevnnext) | Play + Prev + Next group (Prev/Next visibility follows `activeUI.prevNnext`) |
|
|
616
|
+
| `AudioPlayer.RepeatButton` | repeat-type button |
|
|
617
|
+
| `AudioPlayer.Artwork` | track artwork |
|
|
618
|
+
| `AudioPlayer.TrackInfo` | track title / writer |
|
|
619
|
+
| `AudioPlayer.TrackTime` | current + duration time |
|
|
620
|
+
| `AudioPlayer.CustomComponent` | user-defined slot |
|
|
621
|
+
|
|
622
|
+
Each slot accepts the full `GridItemLayoutProps` set — `gridArea?`, `visible?`, `width?`, `padding?`, `justifySelf?`, `UNSAFE_className?` — plus its own domain props. `AudioPlayer.TrackTime` is the exception: it only exposes `visible?` because the slot maps to two grid areas internally.
|
|
623
|
+
|
|
624
|
+
Native HTML attributes (`className`, `style`, `onClick`, `data-*`, etc.) are **not** forwarded by compound slots. Compose the underlying primitives (`PlayBtn`, `PrevBtn`, `NextBtn`, etc., still exported) when full DOM control is needed; headless support with native attribute pass-through is planned for v3.
|
|
625
|
+
|
|
626
|
+
## Mental model — `activeUI` vs compound children
|
|
627
|
+
|
|
628
|
+
- **`activeUI`** governs the **preset** (default layout) — which built-in controls are shown.
|
|
629
|
+
- **Compound children** are **explicit placements** that always render (`visible` defaults to `true`).
|
|
630
|
+
|
|
631
|
+
The two layers are orthogonal. Compound children render **additively** alongside the preset. To truly replace a preset control, disable it in `activeUI` and render the compound counterpart:
|
|
632
|
+
|
|
633
|
+
```tsx
|
|
634
|
+
// Remove the default volume, re-place it with a custom gridArea
|
|
635
|
+
<AudioPlayer
|
|
636
|
+
playList={playList}
|
|
637
|
+
activeUI={{ all: true, volume: false }}
|
|
638
|
+
>
|
|
639
|
+
<AudioPlayer.Volume gridArea="1 / 5 / 1 / 6" />
|
|
640
|
+
</AudioPlayer>
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
In development, a `console.warn` is emitted when a compound slot is rendered while its preset counterpart is still active, so silent duplication is easy to catch.
|
|
644
|
+
|
|
645
|
+
## `AudioPlayer.PlayButton` and `activeUI.prevNnext`
|
|
646
|
+
|
|
647
|
+
`AudioPlayer.PlayButton` is a single slot that renders the **Play + Prev + Next** group as one unit. There is no separate `AudioPlayer.PrevButton` / `AudioPlayer.NextButton` compound slot — Prev / Next are part of the `PlayButton` compound, and there are two ways to render them.
|
|
648
|
+
|
|
649
|
+
### Method 1 — `AudioPlayer.PlayButton` compound (group)
|
|
650
|
+
|
|
651
|
+
Place the whole group at a custom grid area; Prev / Next visibility inside the group follows `activeUI.prevNnext` (which defaults to `activeUI.all`):
|
|
652
|
+
|
|
653
|
+
```tsx
|
|
654
|
+
// Re-place the full Play + Prev + Next group at a custom area
|
|
655
|
+
<AudioPlayer
|
|
656
|
+
playList={playList}
|
|
657
|
+
activeUI={{ all: true, playButton: false }} // hide the preset transport
|
|
658
|
+
>
|
|
659
|
+
<AudioPlayer.PlayButton gridArea="row1-3" />
|
|
660
|
+
</AudioPlayer>
|
|
661
|
+
|
|
662
|
+
// Same compound, but render Play only — Prev / Next hidden by activeUI.prevNnext
|
|
663
|
+
<AudioPlayer
|
|
664
|
+
playList={playList}
|
|
665
|
+
activeUI={{ all: true, playButton: false, prevNnext: false }}
|
|
666
|
+
>
|
|
667
|
+
<AudioPlayer.PlayButton gridArea="row1-3" />
|
|
668
|
+
</AudioPlayer>
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### Method 2 — primitives via `AudioPlayer.CustomComponent`
|
|
672
|
+
|
|
673
|
+
When you need finer control — placing Prev / Play / Next in different grid cells, applying custom wrappers, or attaching native HTML attributes — drop down to the `PlayBtn`, `PrevBtn`, and `NextBtn` primitives (still exported) and render them inside `AudioPlayer.CustomComponent`:
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
import AudioPlayer, {
|
|
677
|
+
PrevBtn,
|
|
678
|
+
PlayBtn,
|
|
679
|
+
NextBtn,
|
|
680
|
+
} from "react-modern-audio-player";
|
|
681
|
+
|
|
682
|
+
<AudioPlayer
|
|
683
|
+
playList={playList}
|
|
684
|
+
activeUI={{ all: true, playButton: false }} // hide the preset transport
|
|
685
|
+
placement={{
|
|
686
|
+
interface: { customComponentsArea: { "custom-transport": "row1-3" } },
|
|
687
|
+
}}
|
|
688
|
+
>
|
|
689
|
+
<AudioPlayer.CustomComponent id="custom-transport">
|
|
690
|
+
<div className="my-transport">
|
|
691
|
+
<PrevBtn isVisible />
|
|
692
|
+
<PlayBtn />
|
|
693
|
+
<NextBtn isVisible />
|
|
694
|
+
</div>
|
|
695
|
+
</AudioPlayer.CustomComponent>
|
|
696
|
+
</AudioPlayer>;
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
`PrevBtn` / `NextBtn` accept an `isVisible` boolean for symmetry with the compound group's visibility logic; pass `false` to omit them. Use Method 2 when Method 1's group placement and `activeUI.prevNnext` toggle aren't enough.
|
|
700
|
+
|
|
701
|
+
## Custom empty-playlist UI
|
|
702
|
+
|
|
703
|
+
Pass children to `AudioPlayer.PlayListEmpty` to render a custom node inside the playlist drawer when `playList` is empty. Omit the slot to keep the default (drawer content renders nothing).
|
|
704
|
+
|
|
705
|
+
```tsx
|
|
706
|
+
<AudioPlayer playList={[]}>
|
|
707
|
+
<AudioPlayer.PlayListEmpty>
|
|
708
|
+
<div className="my-empty">Playlist is empty</div>
|
|
709
|
+
</AudioPlayer.PlayListEmpty>
|
|
710
|
+
</AudioPlayer>
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
`PlayListEmpty` is a marker slot; its children are consumed by the drawer via context and the component itself does not render in-place.
|
|
714
|
+
|
|
593
715
|
# **Accessibility**
|
|
594
716
|
|
|
595
717
|
The player follows WAI-ARIA patterns and is fully navigable by keyboard and screen readers.
|
|
@@ -608,6 +730,19 @@ All controls are reachable via `Tab` and respond to standard keyboard activation
|
|
|
608
730
|
|
|
609
731
|
Drag-and-drop reordering is preserved as an alternative — keyboard and mouse both call the same `onReorder` handler.
|
|
610
732
|
|
|
733
|
+
# **Gotchas**
|
|
734
|
+
|
|
735
|
+
Common integration mistakes to avoid:
|
|
736
|
+
|
|
737
|
+
- **Don't toggle the theme via `rootContainerProps.style.colorScheme`.** The native CSS `color-scheme` property does not switch the player's theme. Use the top-level [`colorScheme`](#theme-mode-dark-mode) prop, which drives the `[data-theme]` attribute and re-initializes the waveform colors.
|
|
738
|
+
- **Set the `InterfacePlacement` generic when placing `customComponentsArea` beyond row 9.** TypeScript rejects values past the default range, so use `InterfacePlacement<N>` where `N` is `(max row length + 1)` — e.g. `InterfacePlacement<11>` for `"row1-10"` (see [Custom Component](#custom-component)).
|
|
739
|
+
- **`AudioPlayer.CustomComponent` accepts a single React element child.** It uses `React.cloneElement` internally, so passing multiple children or a primitive (string, number) will throw.
|
|
740
|
+
- **Volume is `0..1`, not `0..100`.** `setVolume` clamps out-of-range values, so `setVolume(50)` silently becomes `setVolume(1)`.
|
|
741
|
+
- **Compound slots don't forward native HTML attributes.** `<AudioPlayer.Volume className="...">` is rejected by TypeScript — only `GridItemLayoutProps` (layout) pass through. Compose the underlying primitives (`PlayBtn`, `PrevBtn`, `NextBtn`, etc., still exported) when you need `className`, `style`, `onClick`, or `data-*`. Full headless support is planned for v3.
|
|
742
|
+
- **`id: 0` is a valid track id.** The reducer uses nullish checks, so tracks with `id: 0` are handled correctly — don't filter them out of `playList` on the assumption that zero is falsy.
|
|
743
|
+
- **Don't import the CSS manually.** Styles are auto-injected via `sideEffects: ["*.css"]`; `import "react-modern-audio-player/dist/index.css"` will 404 or double-load.
|
|
744
|
+
- **Multiple mounted `<AudioPlayer>` instances don't share React state, but they do share the user's speakers.** Each instance has its own provider and its own `<audio>` element, so the state is isolated — but if two instances both play, the user hears both tracks simultaneously. Coordinate playback yourself (e.g. pause the others when one `play()` fires).
|
|
745
|
+
|
|
611
746
|
# **Example**
|
|
612
747
|
|
|
613
748
|
```tsx
|