generated from Nodarx/template
@@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { MediaItem, ResolveSource } from '@/types'
|
||||
import { findViewer } from '@/services/viewer.ts'
|
||||
import MediaStage from './MediaStage.vue'
|
||||
|
||||
/**
|
||||
* Lightweight hover-preview surface. Mounted inside a host trigger element and
|
||||
* anchored to it via Vuetify's `activator="parent"`, so the host only needs to
|
||||
* drop this component into the element it wants previewed.
|
||||
*
|
||||
* The menu renders its content lazily, so the source (and any blob fetch) is
|
||||
* only resolved once the menu actually opens after `openDelay` — a quick
|
||||
* fly-over never triggers a fetch. Clicking the preview emits `expand` so the
|
||||
* host can open the fullscreen viewer.
|
||||
*/
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item: MediaItem | null
|
||||
resolveSource: ResolveSource
|
||||
/** Hover dwell before opening, in ms. */
|
||||
openDelay?: number
|
||||
}>(),
|
||||
{ openDelay: 400 },
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
expand: [item: MediaItem]
|
||||
}>()
|
||||
|
||||
// Only offer a hover preview when a matching viewer exists and opts in
|
||||
// (meta.popover !== false). Standalone viewers default to opting out.
|
||||
const enabled = computed(() => {
|
||||
const mime = props.item?.mime
|
||||
if (!mime) return false
|
||||
const viewer = findViewer(mime)
|
||||
return !!viewer && viewer.meta?.popover !== false
|
||||
})
|
||||
|
||||
function expand() {
|
||||
if (props.item) emit('expand', props.item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-menu
|
||||
v-if="enabled"
|
||||
activator="parent"
|
||||
open-on-hover
|
||||
:open-delay="openDelay"
|
||||
:close-delay="100"
|
||||
location="end"
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<v-card class="media-popover" @click="expand">
|
||||
<div class="media-popover__stage">
|
||||
<MediaStage :item="item" :resolve-source="resolveSource" presentation="popover" />
|
||||
</div>
|
||||
<div class="media-popover__hint text-caption text-medium-emphasis">
|
||||
Click to open
|
||||
</div>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.media-popover {
|
||||
width: 320px;
|
||||
max-width: 80vw;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.media-popover__stage {
|
||||
width: 100%;
|
||||
height: 220px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: rgb(var(--v-theme-surface-light, var(--v-theme-surface)));
|
||||
}
|
||||
|
||||
.media-popover__hint {
|
||||
text-align: center;
|
||||
padding: 4px 8px;
|
||||
border-top: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user