Fix flickering tooltip when hovering modified in FileExplorer. Use the new, improved tooltip also in gallery to show full name, modified and size of an item.
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-if="visible" class="cursor-tooltip" :style="tooltipStyle">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
// Global activation state - shared across all instances
|
||||||
|
let globalActive = false
|
||||||
|
let globalDeactivateTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
text: string
|
||||||
|
delay?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const mouseX = ref(0)
|
||||||
|
const mouseY = ref(0)
|
||||||
|
let hoverTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
const tooltipStyle = computed(() => ({
|
||||||
|
left: `${mouseX.value + 12}px`,
|
||||||
|
top: `${mouseY.value + 12}px`,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const startHover = (e: MouseEvent) => {
|
||||||
|
mouseX.value = e.clientX
|
||||||
|
mouseY.value = e.clientY
|
||||||
|
// Clear any pending deactivation
|
||||||
|
if (globalDeactivateTimer) {
|
||||||
|
clearTimeout(globalDeactivateTimer)
|
||||||
|
globalDeactivateTimer = null
|
||||||
|
}
|
||||||
|
const delay = globalActive ? 0 : (props.delay ?? 800)
|
||||||
|
hoverTimer = setTimeout(() => {
|
||||||
|
visible.value = true
|
||||||
|
globalActive = true
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePosition = (e: MouseEvent) => {
|
||||||
|
mouseX.value = e.clientX
|
||||||
|
mouseY.value = e.clientY
|
||||||
|
}
|
||||||
|
|
||||||
|
const endHover = () => {
|
||||||
|
if (hoverTimer) {
|
||||||
|
clearTimeout(hoverTimer)
|
||||||
|
hoverTimer = null
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
// Deactivate global state after a short delay if no new tooltip started
|
||||||
|
if (globalDeactivateTimer) clearTimeout(globalDeactivateTimer)
|
||||||
|
globalDeactivateTimer = setTimeout(() => {
|
||||||
|
globalActive = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
startHover,
|
||||||
|
updatePosition,
|
||||||
|
endHover,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.cursor-tooltip {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10000;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
border-radius: 3rem 0 3rem 0;
|
||||||
|
box-shadow: 0 0 1rem var(--accent-color);
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
pointer-events: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,15 +2,11 @@
|
|||||||
<td class="modified right">
|
<td class="modified right">
|
||||||
<time
|
<time
|
||||||
:datetime=datetime
|
:datetime=datetime
|
||||||
@mouseenter="startHover"
|
@mouseenter="tooltip?.startHover"
|
||||||
@mousemove="updatePosition"
|
@mousemove="tooltip?.updatePosition"
|
||||||
@mouseleave="endHover"
|
@mouseleave="tooltip?.endHover"
|
||||||
>{{ modified }}</time>
|
>{{ modified }}</time>
|
||||||
<Teleport to="body">
|
<CursorTooltip ref="tooltip" :text="tooltipText">{{ tooltipText }}</CursorTooltip>
|
||||||
<div v-if="showTooltip" class="cursor-tooltip" :style="tooltipStyle">
|
|
||||||
{{ tooltipText }}
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
</td>
|
</td>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -18,12 +14,15 @@
|
|||||||
import { Doc } from '@/repositories/Document'
|
import { Doc } from '@/repositories/Document'
|
||||||
import { formatUnixDate } from '@/utils'
|
import { formatUnixDate } from '@/utils'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import CursorTooltip from './CursorTooltip.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
doc: Doc
|
doc: Doc
|
||||||
now: number
|
now: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const tooltip = ref<InstanceType<typeof CursorTooltip> | null>(null)
|
||||||
|
|
||||||
// Reference props.now to trigger reactivity when time updates
|
// Reference props.now to trigger reactivity when time updates
|
||||||
const modified = computed(() => {
|
const modified = computed(() => {
|
||||||
props.now // trigger reactivity
|
props.now // trigger reactivity
|
||||||
@@ -37,50 +36,4 @@ const datetime = computed(() =>
|
|||||||
const tooltipText = computed(() =>
|
const tooltipText = computed(() =>
|
||||||
datetime.value.replace('T', ' ').replace('Z', ' UTC')
|
datetime.value.replace('T', ' ').replace('Z', ' UTC')
|
||||||
)
|
)
|
||||||
|
|
||||||
const showTooltip = ref(false)
|
|
||||||
const mouseX = ref(0)
|
|
||||||
const mouseY = ref(0)
|
|
||||||
let hoverTimer: ReturnType<typeof setTimeout> | null = null
|
|
||||||
|
|
||||||
const tooltipStyle = computed(() => ({
|
|
||||||
left: `${mouseX.value + 12}px`,
|
|
||||||
top: `${mouseY.value + 12}px`,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const startHover = (e: MouseEvent) => {
|
|
||||||
mouseX.value = e.clientX
|
|
||||||
mouseY.value = e.clientY
|
|
||||||
hoverTimer = setTimeout(() => {
|
|
||||||
showTooltip.value = true
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePosition = (e: MouseEvent) => {
|
|
||||||
mouseX.value = e.clientX
|
|
||||||
mouseY.value = e.clientY
|
|
||||||
}
|
|
||||||
|
|
||||||
const endHover = () => {
|
|
||||||
if (hoverTimer) {
|
|
||||||
clearTimeout(hoverTimer)
|
|
||||||
hoverTimer = null
|
|
||||||
}
|
|
||||||
showTooltip.value = false
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.cursor-tooltip {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10000;
|
|
||||||
padding: .5rem 1rem;
|
|
||||||
border-radius: 3rem 0 3rem 0;
|
|
||||||
box-shadow: 0 0 1rem var(--accent-color);
|
|
||||||
background-color: var(--accent-color);
|
|
||||||
color: var(--primary-color);
|
|
||||||
white-space: pre;
|
|
||||||
pointer-events: none;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
@contextmenu.stop
|
@contextmenu.stop
|
||||||
@focus.stop="store.cursor = doc.key"
|
@focus.stop="store.cursor = doc.key"
|
||||||
@click=onclick
|
@click=onclick
|
||||||
|
@mouseenter="tooltip?.startHover"
|
||||||
|
@mousemove="tooltip?.updatePosition"
|
||||||
|
@mouseleave="tooltip?.endHover"
|
||||||
>
|
>
|
||||||
<figure>
|
<figure>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
@@ -15,19 +18,24 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<SelectBox :doc=doc @click="store.cursor = doc.key"/>
|
<SelectBox :doc=doc @click="store.cursor = doc.key"/>
|
||||||
<span :title="doc.name + '\n' + doc.modified + '\n' + doc.sizedisp">{{ doc.name }}</span>
|
<span>{{ doc.name }}</span>
|
||||||
<div class=namespacer></div>
|
<div class=namespacer></div>
|
||||||
</template>
|
</template>
|
||||||
</figcaption>
|
</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
<CursorTooltip ref="tooltip" :text="tooltipText">
|
||||||
|
<div class="tooltip-name">{{ doc.name }}</div>
|
||||||
|
<div class="tooltip-details">{{ doc.modified }} — {{ doc.sizedisp }}</div>
|
||||||
|
</CursorTooltip>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang=ts>
|
<script setup lang=ts>
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useMainStore } from '@/stores/main'
|
import { useMainStore } from '@/stores/main'
|
||||||
import { Doc } from '@/repositories/Document'
|
import { Doc } from '@/repositories/Document'
|
||||||
import MediaPreview from '@/components/MediaPreview.vue'
|
import MediaPreview from '@/components/MediaPreview.vue'
|
||||||
|
import CursorTooltip from './CursorTooltip.vue'
|
||||||
|
|
||||||
const store = useMainStore()
|
const store = useMainStore()
|
||||||
type EditingProp = {
|
type EditingProp = {
|
||||||
@@ -40,6 +48,9 @@ const props = defineProps<{
|
|||||||
editing?: EditingProp,
|
editing?: EditingProp,
|
||||||
}>()
|
}>()
|
||||||
const m = ref<typeof MediaPreview | null>(null)
|
const m = ref<typeof MediaPreview | null>(null)
|
||||||
|
const tooltip = ref<InstanceType<typeof CursorTooltip> | null>(null)
|
||||||
|
|
||||||
|
const tooltipText = computed(() => props.doc.key)
|
||||||
|
|
||||||
const onclick = (ev: Event) => {
|
const onclick = (ev: Event) => {
|
||||||
if (m.value!.play()) ev.preventDefault()
|
if (m.value!.play()) ev.preventDefault()
|
||||||
@@ -48,6 +59,13 @@ const onclick = (ev: Event) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.tooltip-name {
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tooltip-details {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
figure {
|
figure {
|
||||||
max-height: 15em;
|
max-height: 15em;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
Reference in New Issue
Block a user