feat(pwa-offline): Pinia store + IndexedDB; contexto para cache/eliminación; toasts; compatibilidad PWA offline
- Agrega @pinia/nuxt, idb y store central (stores/music.ts) - Cacheo manual desde menú contextual y borrado (TrackContextMenu) - Ícono verde para canciones cacheadas, sin auto-cache al reproducir - Toasts de feedback (stores/toast.ts, ToastContainer) - Fallback offline de listado a IndexedDB; fix MUSIC_DIR absoluto en preview/prod - Ajustes PWA: navigateFallback '/', devOptions, workbox condicional - Estilos y animación del context menu (tema light/dark, blur fuerte) - Correcciones de sintaxis y posicionamiento exacto al cursor
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
@click="handleClick"
|
||||
@mouseenter="isHovered = true"
|
||||
@mouseleave="isHovered = false"
|
||||
@contextmenu.prevent="handleContext"
|
||||
>
|
||||
<div class="track-info">
|
||||
<p class="track-name">{{ track.name }}</p>
|
||||
@@ -25,7 +26,7 @@
|
||||
<AlertCircle v-if="hasError" class="icon error" :size="18" />
|
||||
<Pause v-else-if="isActive && !isPlaying" class="icon paused" :size="18" />
|
||||
<Play v-else-if="isActive && isPlaying" class="icon playing" :size="18" />
|
||||
<Music v-else class="icon idle" :size="18" />
|
||||
<Music v-else :class="['icon', 'idle', { cached: isCached }]" :size="18" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,10 +61,14 @@ const props = defineProps({
|
||||
hasError: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isCached: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
const emit = defineEmits(['click', 'context'])
|
||||
|
||||
const isHovered = ref(false)
|
||||
|
||||
@@ -71,6 +76,10 @@ const handleClick = () => {
|
||||
emit('click', props.track)
|
||||
}
|
||||
|
||||
const handleContext = (event) => {
|
||||
emit('context', { track: props.track, x: event.clientX, y: event.clientY })
|
||||
}
|
||||
|
||||
const formatTime = (seconds) => {
|
||||
if (!seconds || isNaN(seconds)) return '0:00'
|
||||
const mins = Math.floor(seconds / 60)
|
||||
@@ -215,6 +224,10 @@ const formatTime = (seconds) => {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.icon.idle.cached {
|
||||
color: #22c55e; /* green-500 */
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@@ -284,4 +297,4 @@ const formatTime = (seconds) => {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user