generated from Nodarx/template
4e302ea923
Signed-off-by: Sebastian <krupinski01@gmail.com>
90 lines
2.3 KiB
Vue
90 lines
2.3 KiB
Vue
<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>
|