diff --git a/components/PromptPreviewTip.vue b/components/PromptPreviewTip.vue index 8046189..31f48ef 100644 --- a/components/PromptPreviewTip.vue +++ b/components/PromptPreviewTip.vue @@ -1,5 +1,13 @@ @@ -33,11 +46,75 @@ const emit = defineEmits<{ copy: [text: string] }>() +const root = ref(null) const panel = ref(null) +const show = ref(false) +const panelStyle = ref>({}) +let closeTimer: ReturnType | null = null + +function place() { + const el = root.value + if (!el) return + const rect = el.getBoundingClientRect() + const width = Math.min(448, window.innerWidth - 24) + const maxHeight = Math.min(320, window.innerHeight * 0.5) + let left = rect.right - width + left = Math.min(Math.max(12, left), window.innerWidth - width - 12) + const below = rect.bottom + 8 + const above = rect.top - 8 - maxHeight + const top = below + maxHeight <= window.innerHeight - 12 ? below : Math.max(12, above) + panelStyle.value = { + top: `${Math.round(top)}px`, + left: `${Math.round(left)}px`, + width: `${Math.round(width)}px` + } +} + +function open() { + if (closeTimer) { + clearTimeout(closeTimer) + closeTimer = null + } + show.value = true + nextTick(place) +} + +function close() { + show.value = false +} + +function scheduleClose() { + if (closeTimer) clearTimeout(closeTimer) + closeTimer = setTimeout(close, 120) +} + +function onFocusOut(event: FocusEvent) { + const next = event.relatedTarget as Node | null + if (root.value?.contains(next) || panel.value?.contains(next)) return + scheduleClose() +} function onIconWheel(event: WheelEvent) { const el = panel.value if (!el) return el.scrollTop += event.deltaY } + +watch(show, (value) => { + if (typeof window === 'undefined') return + if (value) { + window.addEventListener('resize', place) + window.addEventListener('scroll', place, true) + return + } + window.removeEventListener('resize', place) + window.removeEventListener('scroll', place, true) +}) + +onBeforeUnmount(() => { + if (closeTimer) clearTimeout(closeTimer) + if (typeof window === 'undefined') return + window.removeEventListener('resize', place) + window.removeEventListener('scroll', place, true) +})