@@ -3,6 +3,7 @@ import { clsx } from "clsx";
33
44import { ReactComponent as IconPlay } from "../assets/play.svg" ;
55import { ReactComponent as IconPause } from "../assets/pause.svg" ;
6+ import { ReactComponent as IconRight } from "../assets/right.svg" ;
67import type { ArtistInfo , AudioInfo } from "../types" ;
78import { Playlist } from "./list" ;
89import { PlaybackControls } from "./controller" ;
@@ -31,6 +32,11 @@ type APlayerProps = {
3132 */
3233 volume ?: number ;
3334
35+ /**
36+ * @default "normal"
37+ */
38+ appearance ?: "normal" | "fixed" ;
39+
3440 /**
3541 * @default "all"
3642 */
@@ -52,6 +58,7 @@ type APlayerProps = {
5258export function APlayer ( {
5359 theme = defaultThemeColor ,
5460 audio,
61+ appearance = "normal" ,
5562 volume = 0.7 ,
5663 initialLoop,
5764 initialOrder,
@@ -160,17 +167,42 @@ export function APlayer({
160167 ) ;
161168 } , [ ] ) ;
162169
170+ const [ mini , setMini ] = useState ( false ) ;
171+
172+ const [ displayLyrics , setDisplayLyrics ] = useState ( true ) ;
173+
174+ const bodyRef = useRef < HTMLDivElement > ( null ) ;
175+
176+ useEffect ( ( ) => {
177+ if ( appearance === "fixed" ) {
178+ if ( bodyRef . current ) {
179+ const bodyElement = bodyRef . current ;
180+ // Explicitly set width on the body element
181+ // to ensure the width transition works
182+ bodyElement . style . width = bodyElement . offsetWidth - 18 + "px" ;
183+
184+ return ( ) => {
185+ bodyElement . removeAttribute ( "style" ) ;
186+ } ;
187+ }
188+ }
189+ } , [ appearance ] ) ;
190+
163191 return (
164192 < div
165193 className = { clsx ( "aplayer" , {
194+ "aplayer-fixed" : appearance === "fixed" ,
166195 "aplayer-loading" : audioControl . isLoading ,
167196 "aplayer-withlist" : hasPlaylist ,
168- "aplayer-withlrc" : Boolean ( playlist . currentSong . lrc ) ,
197+ "aplayer-withlrc" :
198+ Boolean ( playlist . currentSong . lrc ) && appearance !== "fixed" ,
199+ "aplayer-narrow" : mini ,
169200 } ) }
170201 >
171- < div className = "aplayer-body" >
202+ < div ref = { bodyRef } className = "aplayer-body" >
172203 < div
173204 className = "aplayer-pic"
205+ onClick = { handlePlayButtonClick }
174206 style = { {
175207 backgroundImage : `url("${ playlist . currentSong ?. cover } ")` ,
176208 } }
@@ -180,7 +212,6 @@ export function APlayer({
180212 "aplayer-button" ,
181213 audioControl . isPlaying ? "aplayer-pause" : "aplayer-play"
182214 ) }
183- onClick = { handlePlayButtonClick }
184215 >
185216 { audioControl . isPlaying ? < IconPause /> : < IconPlay /> }
186217 </ div >
@@ -195,10 +226,13 @@ export function APlayer({
195226 - { renderArtist ( playlist . currentSong ?. artist ) }
196227 </ span >
197228 </ div >
198- < Lyrics
199- lrcText = { playlist . currentSong . lrc }
200- currentTime = { audioControl . currentTime ?? 0 }
201- />
229+ { appearance === "fixed" ? null : (
230+ < Lyrics
231+ show = { displayLyrics }
232+ lrcText = { playlist . currentSong . lrc }
233+ currentTime = { audioControl . currentTime ?? 0 }
234+ />
235+ ) }
202236 < PlaybackControls
203237 volume = { audioControl . volume ?? volume }
204238 onChangeVolume = { audioControl . setVolume }
@@ -214,12 +248,33 @@ export function APlayer({
214248 onOrderChange = { playlist . setOrder }
215249 loop = { playlist . loop }
216250 onLoopChange = { playlist . setLoop }
251+ isPlaying = { audioControl . isPlaying ?? false }
252+ onTogglePlay = { handlePlayButtonClick }
253+ onSkipForward = { ( ) => {
254+ if ( playlist . hasNextSong ) {
255+ playlist . next ( ) ;
256+ }
257+ } }
258+ onSkipBack = { ( ) => {
259+ playlist . previous ( ) ;
260+ } }
261+ showLyrics = { displayLyrics }
262+ onToggleLyrics = { ( ) => {
263+ setDisplayLyrics ( ( prev ) => ! prev ) ;
264+ } }
217265 />
218266 </ div >
219267 < div className = "aplayer-notice" style = { notice . style } >
220268 { notice . text }
221269 </ div >
222- < div className = "aplayer-miniswitcher" > </ div >
270+ < div
271+ className = "aplayer-miniswitcher"
272+ onClick = { ( ) => setMini ( ( prev ) => ! prev ) }
273+ >
274+ < button className = "aplayer-icon" >
275+ < IconRight />
276+ </ button >
277+ </ div >
223278 </ div >
224279 { hasPlaylist ? (
225280 < Playlist
@@ -231,6 +286,13 @@ export function APlayer({
231286 listMaxHeight = { listMaxHeight }
232287 />
233288 ) : null }
289+ { appearance === "fixed" && (
290+ < Lyrics
291+ show = { displayLyrics }
292+ lrcText = { playlist . currentSong . lrc }
293+ currentTime = { audioControl . currentTime ?? 0 }
294+ />
295+ ) }
234296 </ div >
235297 ) ;
236298}
0 commit comments