@@ -13,18 +13,18 @@ const ForwardIcon = {
13
13
const ForwardButton = {
14
14
components : { ForwardIcon } ,
15
15
template : `
16
- <button type="button"
17
- class="group relative rounded-full focus:outline-none"
18
- @click="player.seekBy(amount ?? 10)" :aria-label="'Fast-forward ' + (amount ?? 10) + ' seconds'">
19
- <div class="absolute -inset-4 -left-2 md:hidden" />
20
- <ForwardIcon class="h-6 w-6 stroke-slate-500 group-hover:stroke-slate-700" />
21
- </button>` ,
16
+ <button type="button"
17
+ class="group relative rounded-full focus:outline-none"
18
+ @click="player.seekBy(amount ?? 10)" :aria-label="'Fast-forward ' + (amount ?? 10) + ' seconds'">
19
+ <div class="absolute -inset-4 -left-2 md:hidden" />
20
+ <ForwardIcon class="h-6 w-6 stroke-slate-500 group-hover:stroke-slate-700" />
21
+ </button>` ,
22
22
props : { player :Object , amount :Number } ,
23
23
}
24
24
25
25
const MuteIcon = {
26
26
template :`
27
- <svg aria-hidden="true" viewBox="0 0 24 24" stroke-width="2" stroke-line-cap="round" stroke-line-join="round">
27
+ <svg aria-hidden="true" viewBox="0 0 24 24" stroke-width="2" stroke-line-cap="round" stroke-line-join="round">
28
28
<template v-if="muted">
29
29
<path d="M12 6L8 10H6C5.44772 10 5 10.4477 5 11V13C5 13.5523 5.44772 14 6 14H8L12 18V6Z" />
30
30
<path d="M16 10L19 13" fill="none" />
@@ -35,19 +35,19 @@ const MuteIcon = {
35
35
<path d="M17 7C17 7 19 9 19 12C19 15 17 17 17 17" fill="none" />
36
36
<path d="M15.5 10.5C15.5 10.5 16 10.9998 16 11.9999C16 13 15.5 13.5 15.5 13.5" fill="none" />
37
37
</template>
38
- </svg>` ,
38
+ </svg>` ,
39
39
props : { muted :Boolean }
40
40
}
41
41
const MuteButton = {
42
42
components : { MuteIcon } ,
43
43
template :`
44
- <button type="button" :aria-label="player.muted ? 'Unmute' : 'Mute'"
45
- class="group relative rounded-md hover:bg-slate-100 dark:hover:bg-slate-800 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 md:order-none "
44
+ <button type="button" :aria-label="player.muted ? 'Unmute' : 'Mute'"
45
+ class="group relative rounded-md hover:bg-slate-100 dark:hover:bg-slate-800 md:order-none focus:outline-none focus:ring-2 focus:ring-slate-400 dark: focus:ring-slate-700 focus:ring- offset-2 ring-offset-slate-400 dark:ring-offset-slate-700 "
46
46
@click="player.toggleMute()">
47
- <div class="absolute -inset-4 md:hidden" />
48
- <MuteIcon :muted="player.muted"
49
- class="h-6 w-6 fill-slate-500 stroke-slate-500 group-hover:fill-slate-700 group-hover:stroke-slate-700" />
50
- </button>
47
+ <div class="absolute -inset-4 md:hidden" />
48
+ <MuteIcon :muted="player.muted"
49
+ class="h-6 w-6 fill-slate-500 dark:fill-slate-400 stroke-slate-500 dark:stroke-slate-400 group-hover:fill-slate-700 dark: group-hover:fill-slate-300 group-hover: stroke-slate-700 dark:group-hover:stroke-slate-300 " />
50
+ </button>
51
51
` ,
52
52
props : { player :Object } ,
53
53
}
@@ -99,13 +99,13 @@ const playbackRates = [
99
99
]
100
100
const PlaybackRateButton = {
101
101
template :`
102
- <button type="button"
103
- class="relative flex h-6 w-6 items-center justify-center rounded-md text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-700 dark:hover:text-slate-200 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2"
104
- @click="nextPlaybackRate()"
105
- aria-label="Playback rate">
106
- <div class="absolute -inset-4 md:hidden"></div>
107
- <component :is="playbackRate.icon" class="h-4 w-4" />
108
- </button>
102
+ <button type="button"
103
+ class="relative flex h-6 w-6 items-center justify-center rounded-md text-slate-500 hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-700 dark:hover:text-slate-800 focus:outline-none focus:ring-2 focus:ring-slate-400 dark: focus:ring-slate-700 focus:ring- offset-2 ring-offset-slate-400 dark:ring-offset-slate-700 "
104
+ @click="nextPlaybackRate()"
105
+ aria-label="Playback rate">
106
+ <div class="absolute -inset-4 md:hidden"></div>
107
+ <component :is="playbackRate.icon" class="h-4 w-4" />
108
+ </button>
109
109
` ,
110
110
props : { player :Object } ,
111
111
setup ( props ) {
@@ -123,17 +123,17 @@ const PlaybackRateButton = {
123
123
124
124
const PlayButton = {
125
125
template :`
126
- <button type="button" :aria-label="player.isPlaying ? 'Pause' : 'Play'"
126
+ <button type="button" :aria-label="player.isPlaying ? 'Pause' : 'Play'"
127
127
class="group relative flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-slate-700 dark:bg-slate-200 hover:bg-slate-900 dark:hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-700 dark:focus:ring-slate-200 focus:ring-offset-2 dark:ring-offset-black md:h-14 md:w-14"
128
128
@click="player.toggle()">
129
- <div class="absolute -inset-3 md:hidden" />
130
- <svg v-if="player.isPlaying" viewBox="0 0 36 36" aria-hidden="true" class="h-5 w-5 fill-white dark:fill-black group-active:fill-white/80 dark:group-active:fill-black/80 md:h-7 md:w-7">
131
- <path d="M8.5 4C7.67157 4 7 4.67157 7 5.5V30.5C7 31.3284 7.67157 32 8.5 32H11.5C12.3284 32 13 31.3284 13 30.5V5.5C13 4.67157 12.3284 4 11.5 4H8.5ZM24.5 4C23.6716 4 23 4.67157 23 5.5V30.5C23 31.3284 23.6716 32 24.5 32H27.5C28.3284 32 29 31.3284 29 30.5V5.5C29 4.67157 28.3284 4 27.5 4H24.5Z" />
132
- </svg>
133
- <svg v-else viewBox="0 0 36 36" aria-hidden="true" class="h-5 w-5 fill-white dark:fill-black group-active:fill-white/80 dark:group-active:fill-black/80 md:h-7 md:w-7">
134
- <path d="M33.75 16.701C34.75 17.2783 34.75 18.7217 33.75 19.299L11.25 32.2894C10.25 32.8668 9 32.1451 9 30.9904L9 5.00962C9 3.85491 10.25 3.13323 11.25 3.71058L33.75 16.701Z" />
135
- </svg>
136
- </button>
129
+ <div class="absolute -inset-3 md:hidden" />
130
+ <svg v-if="player.isPlaying" viewBox="0 0 36 36" aria-hidden="true" class="h-5 w-5 fill-white dark:fill-black group-active:fill-white/80 dark:group-active:fill-black/80 md:h-7 md:w-7">
131
+ <path d="M8.5 4C7.67157 4 7 4.67157 7 5.5V30.5C7 31.3284 7.67157 32 8.5 32H11.5C12.3284 32 13 31.3284 13 30.5V5.5C13 4.67157 12.3284 4 11.5 4H8.5ZM24.5 4C23.6716 4 23 4.67157 23 5.5V30.5C23 31.3284 23.6716 32 24.5 32H27.5C28.3284 32 29 31.3284 29 30.5V5.5C29 4.67157 28.3284 4 27.5 4H24.5Z" />
132
+ </svg>
133
+ <svg v-else viewBox="0 0 36 36" aria-hidden="true" class="h-5 w-5 fill-white dark:fill-black group-active:fill-white/80 dark:group-active:fill-black/80 md:h-7 md:w-7">
134
+ <path d="M33.75 16.701C34.75 17.2783 34.75 18.7217 33.75 19.299L11.25 32.2894C10.25 32.8668 9 32.1451 9 30.9904L9 5.00962C9 3.85491 10.25 3.13323 11.25 3.71058L33.75 16.701Z" />
135
+ </svg>
136
+ </button>
137
137
` ,
138
138
props : { player :Object } ,
139
139
}
@@ -149,37 +149,37 @@ const RewindIcon = {
149
149
const RewindButton = {
150
150
components : { RewindIcon } ,
151
151
template :`
152
- <button type="button"
152
+ <button type="button"
153
153
class="group relative rounded-full focus:outline-none"
154
154
@click="player.seekBy(-(amount??10))"
155
155
:aria-label="'Rewind ' + (amount??10) + ' seconds'">
156
- <div class="absolute -inset-4 -right-2 md:hidden" />
157
- <RewindIcon class="h-6 w-6 stroke-slate-500 group-hover:stroke-slate-700" />
158
- </button>` ,
156
+ <div class="absolute -inset-4 -right-2 md:hidden" />
157
+ <RewindIcon class="h-6 w-6 stroke-slate-500 group-hover:stroke-slate-700" />
158
+ </button>` ,
159
159
props : { player :Object , amount :Number } ,
160
160
}
161
161
162
162
const Slider = {
163
163
template :`
164
164
<div class="w-full flex items-center">
165
165
<div ref="sliderRef"
166
- class="relative flex-grow h-2 bg-gray-200 dark:bg-gray-700 rounded-full cursor-pointer"
167
- @click="handleClick"
168
- @mousedown="startDrag"
169
- @mousemove="handleDrag"
170
- @mouseup="stopDrag"
171
- @mouseleave="stopDrag">
172
- <div class="absolute top-0 left-0 h-full bg-blue-500 rounded-full"
173
- :style="{ width: progress + '%' }"></div>
166
+ class="relative flex-grow h-2 bg-gray-200 dark:bg-gray-700 rounded-full cursor-pointer"
167
+ @click="handleClick"
168
+ @mousedown="startDrag"
169
+ @mousemove="handleDrag"
170
+ @mouseup="stopDrag"
171
+ @mouseleave="stopDrag">
172
+ <div class="absolute top-0 left-0 h-full bg-blue-500 rounded-full"
173
+ :style="{ width: progress + '%' }"></div>
174
174
<div class="absolute top-1/2 -mt-2 w-4 h-4 bg-white dark:bg-black border-2 border-blue-500 rounded-full shadow"
175
- :style="{ left: 'calc(' + progress + '% - 0.5rem)' }"></div>
175
+ :style="{ left: 'calc(' + progress + '% - 0.5rem)' }"></div>
176
176
</div>
177
177
<div class="hidden ml-2 items-center gap-2 md:flex whitespace-nowrap">
178
- <span class="hidden rounded-md px-1 py-0.5 font-mono text-sm leading-6 md:block text-slate-500 dark:text-slate-400">{{ formatTime(currentTime) }}</span>
179
- <span class="text-sm leading-6 text-slate-300 dark:text-slate-600" aria-hidden="true">/</span>
180
- <span class="hidden rounded-md px-1 py-0.5 font-mono text-sm leading-6 text-slate-500 dark:text-slate-400 md:block">{{ formatTime(duration) }}</span>
181
- </div>
182
- </div>
178
+ <span class="hidden rounded-md px-1 py-0.5 font-mono text-sm leading-6 md:block text-slate-500 dark:text-slate-400">{{ formatTime(currentTime) }}</span>
179
+ <span class="text-sm leading-6 text-slate-300 dark:text-slate-600" aria-hidden="true">/</span>
180
+ <span class="hidden rounded-md px-1 py-0.5 font-mono text-sm leading-6 text-slate-500 dark:text-slate-400 md:block">{{ formatTime(duration) }}</span>
181
+ </div>
182
+ </div>
183
183
` ,
184
184
emits : [ 'update:currentTime' ] ,
185
185
props : {
@@ -253,46 +253,50 @@ const AudioPlayer = {
253
253
Slider,
254
254
} ,
255
255
template :`
256
- <div v-if="src" :class="v.cls">
257
- <audio ref="refPlayer"
258
- @play="onPlay()" @pause="onPause()"
259
- @timeupdate="onTimeUpdate(Math.floor($event.currentTarget.currentTime))"
260
- @durationchange="onDurationChange(Math.floor($event.currentTarget.duration))"
261
- :muted="muted"
262
- />
263
- <div class="hidden md:block">
256
+ <div v-if="src" :class="v.cls">
257
+ <button v-if="showClose" type="button" @click="$emit('close')" title="Close Audio Player">
258
+ <svg class="size-4 absolute top-1 right-1 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z"/></svg>
259
+ </button>
260
+ <audio ref="refPlayer"
261
+ @play="onPlay()" @pause="onPause()"
262
+ @timeupdate="onTimeUpdate(Math.floor($event.currentTarget.currentTime))"
263
+ @durationchange="onDurationChange(Math.floor($event.currentTarget.duration))"
264
+ :muted="muted"
265
+ />
266
+ <div class="hidden md:block">
267
+ <PlayButton :player="player"/>
268
+ </div>
269
+ <div :class="v.innerCls">
270
+ <a :href="'/podcasts/' + id"
271
+ class="truncate text-center text-sm font-bold leading-6 md:text-left"
272
+ :title="title">
273
+ {{title}}
274
+ </a>
275
+ <div :class="v.controlsCls">
276
+ <div :class="v.startControlsCls">
277
+ <MuteButton :player="player"/>
278
+ </div>
279
+ <div :class="v.midControlsCls">
280
+ <RewindButton :player="player"/>
281
+ <div class="md:hidden">
264
282
<PlayButton :player="player"/>
283
+ </div>
284
+ <ForwardButton :player="player"/>
265
285
</div>
266
- <div :class="v.innerCls">
267
- <a :href="'/podcasts/' + id"
268
- class="truncate text-center text-sm font-bold leading-6 md:text-left"
269
- :title="title">
270
- {{title}}
271
- </a>
272
- <div :class="v.controlsCls">
273
- <div :class="v.startControlsCls">
274
- <MuteButton :player="player"/>
275
- </div>
276
- <div :class="v.midControlsCls">
277
- <RewindButton :player="player"/>
278
- <div class="md:hidden">
279
- <PlayButton :player="player"/>
280
- </div>
281
- <ForwardButton :player="player"/>
282
- </div>
283
- <Slider :currentTime="currentTime" :duration="duration" @update:currentTime="player.currentTime=$event" />
284
- <div :class="v.endControlsCls">
285
- <div class="flex items-center">
286
- <PlaybackRateButton :player="player"/>
287
- </div>
288
- <div class="hidden items-center md:flex">
289
- <MuteButton :player="player"/>
290
- </div>
291
- </div>
292
- </div>
286
+ <Slider :currentTime="currentTime" :duration="duration" @update:currentTime="player.currentTime=$event" />
287
+ <div :class="v.endControlsCls">
288
+ <div class="flex items-center">
289
+ <PlaybackRateButton :player="player"/>
290
+ </div>
291
+ <div class="hidden items-center md:flex">
292
+ <MuteButton :player="player"/>
293
+ </div>
293
294
</div>
294
- </div>
295
+ </div>
296
+ </div>
297
+ </div>
295
298
` ,
299
+ emits : [ 'close' ] ,
296
300
props : {
297
301
bus : Object ,
298
302
id : String ,
@@ -303,9 +307,10 @@ const AudioPlayer = {
303
307
autoPlay : Boolean ,
304
308
variant : String ,
305
309
beaconUrl : String ,
306
- cls : {
310
+ showClose : Boolean ,
311
+ cls : {
307
312
type : String ,
308
- default : 'flex items-center gap-6 bg-white/90 dark:bg-black/90 px-4 py-4 shadow shadow-slate-200/80 dark:shadow-slate-700/80 ring-1 ring-slate-900/5 dark:ring-slate-50/5 backdrop-blur-sm md:px-6'
313
+ default : 'flex items-center gap-6 bg-white/90 dark:bg-black/70 px-4 py-4 shadow shadow-slate-200/80 dark:shadow-slate-700/80 ring-1 ring-slate-900/5 dark:ring-slate-50/5 backdrop-blur-sm md:px-6'
309
314
} ,
310
315
innerCls : {
311
316
type : String ,
@@ -375,7 +380,7 @@ const AudioPlayer = {
375
380
function onDurationChange ( value ) {
376
381
duration . value = value
377
382
}
378
-
383
+
379
384
const player = globalThis . player = {
380
385
get url ( ) { return props . url } ,
381
386
get id ( ) { return props . id } ,
@@ -436,7 +441,7 @@ const AudioPlayer = {
436
441
} )
437
442
onUnmounted ( ( ) => sub ?. unsubscribe ( ) )
438
443
// console.log('AudioPlayer.mjs', globalThis.player)
439
-
444
+
440
445
return {
441
446
v,
442
447
refPlayer,
0 commit comments