diff --git a/.gitignore b/.gitignore index 4a7f73a..0ece5a5 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ logs .env .env.* !.env.example +.bruno diff --git a/app/components/PublicationDetail.vue b/app/components/PublicationDetail.vue index eb5382e..50d724a 100644 --- a/app/components/PublicationDetail.vue +++ b/app/components/PublicationDetail.vue @@ -635,6 +635,12 @@ onMounted(async () => { if (props.paragraphs.length) { await applyTypesenseHighlights() } + document.addEventListener('selectionchange', onSelectionChange) +}) + +onUnmounted(() => { + document.removeEventListener('selectionchange', onSelectionChange) + if (selectionTimer) clearTimeout(selectionTimer) }) // ---- Utilerías de búsqueda local -------------------------------------------- @@ -755,16 +761,34 @@ function highlightTextNodes(root: HTMLElement, terms: string[]): number { return matches.length } const isPopOverOpen = ref(false) +const selectionTooltip = ref<{ x: number; y: number } | null>(null) +let selectionTimer: ReturnType | null = null function handleSelection(event: MouseEvent) { const selection = window.getSelection() - if (selection && selection.toString().length > 3) { - isPopOverOpen.value = true + if (selection && selection.toString().trim().length > 3) { + if (selectionTimer) clearTimeout(selectionTimer) + selectionTimer = setTimeout(() => { + const x = Math.min(Math.max(event.clientX, 96), window.innerWidth - 96) + selectionTooltip.value = { x, y: event.clientY } + isPopOverOpen.value = true + }, 320) } else { - isPopOverOpen.value = false + clearSelectionTooltip() } } +function clearSelectionTooltip() { + if (selectionTimer) { clearTimeout(selectionTimer); selectionTimer = null } + selectionTooltip.value = null + isPopOverOpen.value = false +} + +function onSelectionChange() { + const sel = window.getSelection() + if (!sel || sel.toString().trim().length === 0) clearSelectionTooltip() +} + const links = computed(() => { return [ { @@ -981,56 +1005,55 @@ const items = computed(() => {

- + + + +
+ +
+ + +
+ + + Copiar + +
+ +
+ @@ -1042,4 +1065,21 @@ const items = computed(() => { .paragraph-html :deep(p:last-child) { margin-bottom: 0; } + +/* Tooltip de selección de texto */ +.selection-tooltip { + transform: translateX(-50%) translateY(-100%); +} + +.tooltip-pop-enter-active { + transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.12s ease; +} +.tooltip-pop-leave-active { + transition: transform 0.12s ease-in, opacity 0.1s ease; +} +.tooltip-pop-enter-from, +.tooltip-pop-leave-to { + transform: translateX(-50%) translateY(calc(-100% + 8px)); + opacity: 0; +}