refactor: move popover to separate button
This commit is contained in:
parent
044bad41e1
commit
d37cc6120a
3 changed files with 117 additions and 98 deletions
|
|
@ -98,32 +98,36 @@
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td class="is-text-overflow is-vcentered">
|
<td class="is-text-overflow is-vcentered">
|
||||||
<div class="is-inline is-pulled-right"
|
<div class="is-inline is-pulled-right" v-if="item.extras?.duration || show_popover">
|
||||||
v-if="item.extras?.duration || getPath(config.app.download_path, item)">
|
|
||||||
<span class="tag is-info is-unselectable" v-if="item.extras?.duration">
|
<span class="tag is-info is-unselectable" v-if="item.extras?.duration">
|
||||||
{{ formatTime(item.extras.duration) }}
|
{{ formatTime(item.extras.duration) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="icon is-pointer" v-if="getPath(config.app.download_path, item)"
|
|
||||||
v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
|
<Popover :showDelay="400" :maxWidth="450" v-if="show_popover">
|
||||||
<i class="fa-solid fa-folder-open" />
|
<template #trigger>
|
||||||
</span>
|
<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>
|
||||||
<div v-if="show_popover">
|
<div v-if="show_popover">
|
||||||
<div class="is-text-overflow">
|
<div class="is-text-overflow">
|
||||||
<Popover :showDelay="400" :maxWidth="450">
|
<NuxtLink class="is-text-overflow" v-tooltip="`${item.preset}: ${item.title}`" target="_blank"
|
||||||
<template #trigger>
|
:href="item.url">
|
||||||
<NuxtLink class="is-text-overflow" target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
|
{{ 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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
@ -241,16 +245,7 @@
|
||||||
:class="{ 'is-bordered-danger': item.status === 'error', 'is-bordered-info': item.live_in || item.is_live }">
|
:class="{ 'is-bordered-danger': item.status === 'error', 'is-bordered-info': item.live_in || item.is_live }">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
<div class="card-header-title is-text-overflow is-block">
|
<div class="card-header-title is-text-overflow is-block">
|
||||||
<Popover :showDelay="400" :maxWidth="550" v-if="show_popover">
|
<NuxtLink target="_blank" v-tooltip="item.title" :href="item.url">{{ item.title }}</NuxtLink>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-header-icon">
|
<div class="card-header-icon">
|
||||||
|
|
@ -261,10 +256,20 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control" v-if="getPath(config.app.download_path, item)">
|
<div class="control" v-if="show_popover && getPath(config.app.download_path, item)">
|
||||||
<span class="icon" v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
|
<Popover :showDelay="400" :maxWidth="550" v-if="show_popover">
|
||||||
<i class="fa-solid fa-folder-open" />
|
<template #trigger>
|
||||||
</span>
|
<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>
|
||||||
|
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@
|
||||||
<div v-if="isOpen" class="popover-portal" ref="portal">
|
<div v-if="isOpen" class="popover-portal" ref="portal">
|
||||||
<div ref="popover" class="popover-card box" :class="placementClass" role="tooltip" :style="portalStyle"
|
<div ref="popover" class="popover-card box" :class="placementClass" role="tooltip" :style="portalStyle"
|
||||||
@mouseenter="handleHoverEnter" @mouseleave="handleHoverLeave">
|
@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">
|
<header v-if="hasTitleContent" class="popover-title mb-2">
|
||||||
<slot name="title">
|
<slot name="title">
|
||||||
<p class="title is-6 mb-1">{{ title }}</p>
|
<p class="title is-6 mb-1">{{ title }}</p>
|
||||||
|
|
@ -206,23 +211,47 @@ const updatePosition = () => {
|
||||||
const triggerRect = triggerRef.value.getBoundingClientRect()
|
const triggerRect = triggerRef.value.getBoundingClientRect()
|
||||||
const popoverRect = popover.value.getBoundingClientRect()
|
const popoverRect = popover.value.getBoundingClientRect()
|
||||||
const offset = props.offset
|
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 top = triggerRect.bottom + offset
|
||||||
let left = triggerRect.left + (triggerRect.width - popoverRect.width) / 2
|
let left = triggerRect.left + (triggerRect.width - popoverRect.width) / 2
|
||||||
|
|
||||||
if ('top' == props.placement) {
|
if ('top' === effectivePlacement) {
|
||||||
top = triggerRect.top - popoverRect.height - offset
|
top = triggerRect.top - popoverRect.height - offset
|
||||||
} else if ('left' == props.placement) {
|
} else if ('left' === effectivePlacement) {
|
||||||
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
|
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
|
||||||
left = triggerRect.left - popoverRect.width - offset
|
left = triggerRect.left - popoverRect.width - offset
|
||||||
} else if ('right' == props.placement) {
|
} else if ('right' === effectivePlacement) {
|
||||||
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
|
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
|
||||||
left = triggerRect.right + offset
|
left = triggerRect.right + offset
|
||||||
}
|
}
|
||||||
|
|
||||||
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max)
|
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 maxLeft = viewportWidth - popoverRect.width - 8
|
||||||
const maxTop = viewportHeight - popoverRect.height - 8
|
const maxTop = viewportHeight - popoverRect.height - 8
|
||||||
|
|
||||||
|
|
@ -284,7 +313,7 @@ onBeforeUnmount(() => {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-trigger > * {
|
.popover-trigger>* {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,39 +68,34 @@
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td class="is-text-overflow is-vcentered">
|
<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" v-if="item.downloaded_bytes">{{ formatBytes(item.downloaded_bytes) }}</span>
|
||||||
<span class="tag is-info" v-if="item.extras?.duration">
|
<Popover :showDelay="400" :maxWidth="450" v-if="show_popover">
|
||||||
{{ formatTime(item.extras.duration) }}
|
<template #trigger>
|
||||||
</span>
|
<span class="icon is-pointer"><i class="fa-solid fa-info-circle" /></span>
|
||||||
<span class="icon is-pointer" v-if="item.download_dir"
|
</template>
|
||||||
v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
|
<template #title>
|
||||||
<i class="fa-solid fa-folder-open" />
|
<strong>
|
||||||
</span>
|
{{ 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>
|
||||||
<div v-if="show_popover">
|
<div class="is-text-overflow" v-tooltip="`[${item.preset}] - ${item.title}`">
|
||||||
<div class="is-text-overflow">
|
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
|
||||||
<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>
|
</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>
|
||||||
<td class="has-text-centered is-text-overflow is-unselectable">
|
<td class="has-text-centered is-text-overflow is-unselectable">
|
||||||
<span class="icon" :class="setIconColor(item)">
|
<span class="icon" :class="setIconColor(item)">
|
||||||
|
|
@ -176,16 +171,7 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
<div class="card-header-title is-text-overflow is-block">
|
<div class="card-header-title is-text-overflow is-block">
|
||||||
<Popover :showDelay="400" :maxWidth="550" v-if="show_popover">
|
<NuxtLink target="_blank" v-tooltip="item.title" :href="item.url">{{ item.title }}</NuxtLink>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-header-icon">
|
<div class="card-header-icon">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
|
|
@ -194,11 +180,22 @@
|
||||||
{{ formatTime(item.extras.duration) }}
|
{{ formatTime(item.extras.duration) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="control" v-if="item.download_dir">
|
<Popover :showDelay="400" :maxWidth="450" v-if="show_popover">
|
||||||
<span class="icon" v-tooltip="`Path: ${getPath(config.app.download_path, item)}`">
|
<template #trigger>
|
||||||
<i class="fa-solid fa-folder-open" />
|
<span class="icon is-pointer"><i class="fa-solid fa-info-circle" /></span>
|
||||||
</span>
|
</template>
|
||||||
</div>
|
<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">
|
<div class="control">
|
||||||
<button @click="hideThumbnail = !hideThumbnail" v-if="thumbnails">
|
<button @click="hideThumbnail = !hideThumbnail" v-if="thumbnails">
|
||||||
<span class="icon"><i class="fa-solid"
|
<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"
|
<span v-tooltip="moment(item.datetime).format('MMMM Do YYYY, h:mm:ss a')" :data-datetime="item.datetime"
|
||||||
v-rtime="item.datetime" />
|
v-rtime="item.datetime" />
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-half-mobile has-text-centered is-text-overflow is-unselectable"
|
<div class="column is-half-mobile has-text-centered is-unselectable" v-if="item.downloaded_bytes">
|
||||||
v-if="item.downloaded_bytes" v-tooltip="`Saving to: ${makePath(item)}`">
|
|
||||||
{{ formatBytes(item.downloaded_bytes) }}
|
{{ formatBytes(item.downloaded_bytes) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -632,15 +628,4 @@ watch(embed_url, v => {
|
||||||
}
|
}
|
||||||
document.querySelector('body')?.setAttribute('style', `opacity: ${v ? 1 : bg_opacity.value}`)
|
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>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue