refactor: move popover to separate button

This commit is contained in:
arabcoders 2026-01-12 17:14:29 +03:00
parent 044bad41e1
commit d37cc6120a
3 changed files with 117 additions and 98 deletions

View file

@ -98,32 +98,36 @@
</label>
</td>
<td class="is-text-overflow is-vcentered">
<div class="is-inline is-pulled-right"
v-if="item.extras?.duration || getPath(config.app.download_path, item)">
<div class="is-inline is-pulled-right" v-if="item.extras?.duration || show_popover">
<span class="tag is-info is-unselectable" v-if="item.extras?.duration">
{{ formatTime(item.extras.duration) }}
</span>
<span class="icon is-pointer" v-if="getPath(config.app.download_path, item)"
v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
<i class="fa-solid fa-folder-open" />
</span>
<Popover :showDelay="400" :maxWidth="450" v-if="show_popover">
<template #trigger>
<span class="icon is-pointer"><i class="fa-solid fa-info-circle" /></span>
</template>
<template #title>
<strong>
{{ item.title }}
<span class="tag is-info is-unselectable">{{ item.preset }}</span>
</strong>
</template>
<p v-if="getPath(config.app.download_path, item)">
<b>Path:</b> {{ getPath(config.app.download_path, item) }}
</p>
<hr
v-if="(showThumbnails && getImage(config.app.download_path, item, false)) || item.description" />
<img v-if="showThumbnails && getImage(config.app.download_path, item, false)"
:src="getImage(config.app.download_path, item, false)" class="card-image mt-2 mb-2" />
<p v-if="item.description">{{ item.description }}</p>
</Popover>
</div>
<div v-if="show_popover">
<div class="is-text-overflow">
<Popover :showDelay="400" :maxWidth="450">
<template #trigger>
<NuxtLink class="is-text-overflow" target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</template>
<template #title>
<strong>
{{ item.title }}
<span class="tag is-info is-unselectable">{{ item.preset }}</span>
</strong>
</template>
<img v-if="showThumbnails && getImage(config.app.download_path, item, false)"
:src="getImage(config.app.download_path, item, false)" class="mt-2 mb-2" />
<p v-if="item.description">{{ item.description }}</p>
</Popover>
<NuxtLink class="is-text-overflow" v-tooltip="`${item.preset}: ${item.title}`" target="_blank"
:href="item.url">
{{ item.title }}</NuxtLink>
</div>
</div>
<template v-else>
@ -241,16 +245,7 @@
:class="{ 'is-bordered-danger': item.status === 'error', 'is-bordered-info': item.live_in || item.is_live }">
<header class="card-header">
<div class="card-header-title is-text-overflow is-block">
<Popover :showDelay="400" :maxWidth="550" v-if="show_popover">
<template #trigger>
<NuxtLink class="is-text-overflow" target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</template>
<template #title><strong>{{ item.title }}</strong></template>
<p v-if="item.description">{{ item.description }}</p>
</Popover>
<template v-else>
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</template>
<NuxtLink target="_blank" v-tooltip="item.title" :href="item.url">{{ item.title }}</NuxtLink>
</div>
<div class="card-header-icon">
@ -261,10 +256,20 @@
</span>
</div>
<div class="control" v-if="getPath(config.app.download_path, item)">
<span class="icon" v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
<i class="fa-solid fa-folder-open" />
</span>
<div class="control" v-if="show_popover && getPath(config.app.download_path, item)">
<Popover :showDelay="400" :maxWidth="550" v-if="show_popover">
<template #trigger>
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
</template>
<template #title><strong>{{ item.title }}</strong></template>
<p v-if="getPath(config.app.download_path, item)">
<b>Path:</b> {{ getPath(config.app.download_path, item) }}
</p>
<template v-if="item.description">
<hr>
<p>{{ item.description }}</p>
</template>
</Popover>
</div>
<div class="control">

View file

@ -15,6 +15,11 @@
<div v-if="isOpen" class="popover-portal" ref="portal">
<div ref="popover" class="popover-card box" :class="placementClass" role="tooltip" :style="portalStyle"
@mouseenter="handleHoverEnter" @mouseleave="handleHoverLeave">
<button type="button" class="button is-bold is-fullwidth is-hidden-tablet" @click="closePopover">
<span class="icon"><i class="fas fa-times" /></span>
<span>Close</span>
</button>
<header v-if="hasTitleContent" class="popover-title mb-2">
<slot name="title">
<p class="title is-6 mb-1">{{ title }}</p>
@ -206,23 +211,47 @@ const updatePosition = () => {
const triggerRect = triggerRef.value.getBoundingClientRect()
const popoverRect = popover.value.getBoundingClientRect()
const offset = props.offset
const viewportWidth = window.innerWidth
const viewportHeight = window.innerHeight
let effectivePlacement = props.placement
if ('top' === props.placement) {
const spaceAbove = triggerRect.top - offset
if (spaceAbove < popoverRect.height) {
effectivePlacement = 'bottom'
}
} else if ('bottom' === props.placement) {
const spaceBelow = viewportHeight - triggerRect.bottom - offset
if (spaceBelow < popoverRect.height) {
effectivePlacement = 'top'
}
} else if ('left' === props.placement) {
const spaceLeft = triggerRect.left - offset
if (spaceLeft < popoverRect.width) {
effectivePlacement = 'right'
}
} else if ('right' === props.placement) {
const spaceRight = viewportWidth - triggerRect.right - offset
if (spaceRight < popoverRect.width) {
effectivePlacement = 'left'
}
}
let top = triggerRect.bottom + offset
let left = triggerRect.left + (triggerRect.width - popoverRect.width) / 2
if ('top' == props.placement) {
if ('top' === effectivePlacement) {
top = triggerRect.top - popoverRect.height - offset
} else if ('left' == props.placement) {
} else if ('left' === effectivePlacement) {
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
left = triggerRect.left - popoverRect.width - offset
} else if ('right' == props.placement) {
} else if ('right' === effectivePlacement) {
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
left = triggerRect.right + offset
}
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max)
const viewportWidth = window.innerWidth
const viewportHeight = window.innerHeight
const maxLeft = viewportWidth - popoverRect.width - 8
const maxTop = viewportHeight - popoverRect.height - 8
@ -284,7 +313,7 @@ onBeforeUnmount(() => {
min-width: 0;
}
.popover-trigger > * {
.popover-trigger>* {
max-width: 100%;
min-width: 0;
}

View file

@ -68,39 +68,34 @@
</label>
</td>
<td class="is-text-overflow is-vcentered">
<div class="is-inline is-pulled-right" v-if="item.downloaded_bytes || item.extras?.duration">
<div class="is-inline is-pulled-right" v-if="item.downloaded_bytes || show_popover">
<span class="tag" v-if="item.downloaded_bytes">{{ formatBytes(item.downloaded_bytes) }}</span>
<span class="tag is-info" v-if="item.extras?.duration">
{{ formatTime(item.extras.duration) }}
</span>
<span class="icon is-pointer" v-if="item.download_dir"
v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
<i class="fa-solid fa-folder-open" />
</span>
<Popover :showDelay="400" :maxWidth="450" v-if="show_popover">
<template #trigger>
<span class="icon is-pointer"><i class="fa-solid fa-info-circle" /></span>
</template>
<template #title>
<strong>
{{ item.title }}
<span class="tag is-info is-unselectable">{{ item.preset }}</span>
</strong>
</template>
<p v-if="item.extras?.duration">
<b>Duration</b>: {{ formatTime(item.extras.duration) }}
</p>
<p v-if="getPath(config.app.download_path, item)">
<b>Path:</b> {{ getPath(config.app.download_path, item) }}
</p>
<hr
v-if="(showThumbnails && getImage(config.app.download_path, item, false)) || item.description" />
<img v-if="showThumbnails && getImage(config.app.download_path, item, false)"
:src="getImage(config.app.download_path, item, false)" class="card-image mt-2 mb-2" />
<p v-if="item.description">{{ item.description }}</p>
</Popover>
</div>
<div v-if="show_popover">
<div class="is-text-overflow">
<Popover :showDelay="400" :maxWidth="450">
<template #trigger>
<NuxtLink class="is-text-overflow" target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</template>
<template #title>
<strong>
{{ item.title }}
<span class="tag is-info is-unselectable">{{ item.preset }}</span>
</strong>
</template>
<img v-if="showThumbnails && getImage(config.app.download_path, item)"
:src="getImage(config.app.download_path, item)" class="mt-2 mb-2" />
<p v-if="item.description">{{ item.description }}</p>
</Popover>
</div>
<div class="is-text-overflow" v-tooltip="`[${item.preset}] - ${item.title}`">
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</div>
<template v-else>
<div class="is-text-overflow" v-tooltip="`[${item.preset}] - ${item.title}`">
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</div>
</template>
</td>
<td class="has-text-centered is-text-overflow is-unselectable">
<span class="icon" :class="setIconColor(item)">
@ -176,16 +171,7 @@
<div class="card">
<header class="card-header">
<div class="card-header-title is-text-overflow is-block">
<Popover :showDelay="400" :maxWidth="550" v-if="show_popover">
<template #trigger>
<NuxtLink class="is-text-overflow" target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</template>
<template #title><strong>{{ item.title }}</strong></template>
<p v-if="item.description">{{ item.description }}</p>
</Popover>
<template v-else>
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
</template>
<NuxtLink target="_blank" v-tooltip="item.title" :href="item.url">{{ item.title }}</NuxtLink>
</div>
<div class="card-header-icon">
<div class="field is-grouped">
@ -194,11 +180,22 @@
{{ formatTime(item.extras.duration) }}
</span>
</div>
<div class="control" v-if="item.download_dir">
<span class="icon" v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
<i class="fa-solid fa-folder-open" />
</span>
</div>
<Popover :showDelay="400" :maxWidth="450" v-if="show_popover">
<template #trigger>
<span class="icon is-pointer"><i class="fa-solid fa-info-circle" /></span>
</template>
<template #title>
<strong>
{{ item.title }}
<span class="tag is-info is-unselectable">{{ item.preset }}</span>
</strong>
</template>
<p v-if="getPath(config.app.download_path, item)">
<b>Path:</b> {{ getPath(config.app.download_path, item) }}
</p>
<hr v-if="item.description" />
<p v-if="item.description">{{ item.description }}</p>
</Popover>
<div class="control">
<button @click="hideThumbnail = !hideThumbnail" v-if="thumbnails">
<span class="icon"><i class="fa-solid"
@ -252,8 +249,7 @@
<span v-tooltip="moment(item.datetime).format('MMMM Do YYYY, h:mm:ss a')" :data-datetime="item.datetime"
v-rtime="item.datetime" />
</div>
<div class="column is-half-mobile has-text-centered is-text-overflow is-unselectable"
v-if="item.downloaded_bytes" v-tooltip="`Saving to: ${makePath(item)}`">
<div class="column is-half-mobile has-text-centered is-unselectable" v-if="item.downloaded_bytes">
{{ formatBytes(item.downloaded_bytes) }}
</div>
@ -632,15 +628,4 @@ watch(embed_url, v => {
}
document.querySelector('body')?.setAttribute('style', `opacity: ${v ? 1 : bg_opacity.value}`)
})
const makePath = (item: StoreItem) => {
const parts = [
eTrim(item.download_dir, '/').replace(config.app.download_path, ''),
]
if (item?.filename) {
parts.push(eTrim(item.filename, '/'))
}
return '/' + sTrim(parts.filter(p => !!p).map(p => p.replace(/\\/g, '/').replace(/\/+/g, '/')).join('/'), '/')
}
</script>