@@ -3,6 +3,7 @@ import { clsx } from "clsx";
3
3
4
4
import { ReactComponent as IconPlay } from "../assets/play.svg" ;
5
5
import { ReactComponent as IconPause } from "../assets/pause.svg" ;
6
+ import { ReactComponent as IconRight } from "../assets/right.svg" ;
6
7
import type { ArtistInfo , AudioInfo } from "../types" ;
7
8
import { Playlist } from "./list" ;
8
9
import { PlaybackControls } from "./controller" ;
@@ -31,6 +32,11 @@ type APlayerProps = {
31
32
*/
32
33
volume ?: number ;
33
34
35
+ /**
36
+ * @default "normal"
37
+ */
38
+ appearance ?: "normal" | "fixed" ;
39
+
34
40
/**
35
41
* @default "all"
36
42
*/
@@ -52,6 +58,7 @@ type APlayerProps = {
52
58
export function APlayer ( {
53
59
theme = defaultThemeColor ,
54
60
audio,
61
+ appearance = "normal" ,
55
62
volume = 0.7 ,
56
63
initialLoop,
57
64
initialOrder,
@@ -160,17 +167,42 @@ export function APlayer({
160
167
) ;
161
168
} , [ ] ) ;
162
169
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
+
163
191
return (
164
192
< div
165
193
className = { clsx ( "aplayer" , {
194
+ "aplayer-fixed" : appearance === "fixed" ,
166
195
"aplayer-loading" : audioControl . isLoading ,
167
196
"aplayer-withlist" : hasPlaylist ,
168
- "aplayer-withlrc" : Boolean ( playlist . currentSong . lrc ) ,
197
+ "aplayer-withlrc" :
198
+ Boolean ( playlist . currentSong . lrc ) && appearance !== "fixed" ,
199
+ "aplayer-narrow" : mini ,
169
200
} ) }
170
201
>
171
- < div className = "aplayer-body" >
202
+ < div ref = { bodyRef } className = "aplayer-body" >
172
203
< div
173
204
className = "aplayer-pic"
205
+ onClick = { handlePlayButtonClick }
174
206
style = { {
175
207
backgroundImage : `url("${ playlist . currentSong ?. cover } ")` ,
176
208
} }
@@ -180,7 +212,6 @@ export function APlayer({
180
212
"aplayer-button" ,
181
213
audioControl . isPlaying ? "aplayer-pause" : "aplayer-play"
182
214
) }
183
- onClick = { handlePlayButtonClick }
184
215
>
185
216
{ audioControl . isPlaying ? < IconPause /> : < IconPlay /> }
186
217
</ div >
@@ -195,10 +226,13 @@ export function APlayer({
195
226
- { renderArtist ( playlist . currentSong ?. artist ) }
196
227
</ span >
197
228
</ 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
+ ) }
202
236
< PlaybackControls
203
237
volume = { audioControl . volume ?? volume }
204
238
onChangeVolume = { audioControl . setVolume }
@@ -214,12 +248,33 @@ export function APlayer({
214
248
onOrderChange = { playlist . setOrder }
215
249
loop = { playlist . loop }
216
250
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
+ } }
217
265
/>
218
266
</ div >
219
267
< div className = "aplayer-notice" style = { notice . style } >
220
268
{ notice . text }
221
269
</ 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 >
223
278
</ div >
224
279
{ hasPlaylist ? (
225
280
< Playlist
@@ -231,6 +286,13 @@ export function APlayer({
231
286
listMaxHeight = { listMaxHeight }
232
287
/>
233
288
) : null }
289
+ { appearance === "fixed" && (
290
+ < Lyrics
291
+ show = { displayLyrics }
292
+ lrcText = { playlist . currentSong . lrc }
293
+ currentTime = { audioControl . currentTime ?? 0 }
294
+ />
295
+ ) }
234
296
</ div >
235
297
) ;
236
298
}
0 commit comments