-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWord.vue
More file actions
138 lines (121 loc) · 3.53 KB
/
Copy pathWord.vue
File metadata and controls
138 lines (121 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
<template>
<span
:id="id"
@mouseenter="onMouseEnter"
@mouseleave.stop="onMouseLeave"
@click.stop="OpenWordDetail"
:class="{
selected: markerStore.checkedSelected(id),
pointer: markerStore.checkedSelected(id),
}"
>{{ modelValue }}</span
>
</template>
<script lang="ts" setup>
import { emitOpen } from "../../../common/services/console-crane-bridge";
import { useMarkerStore } from "../../../stores/marker";
import { ref, getCurrentInstance, onMounted, onUnmounted } from "vue";
import { analytic } from "../../../plugins/mixpanel";
const markerStore = useMarkerStore();
const props = defineProps<{
id: string;
modelValue: string;
}>();
const elRef = ref<HTMLElement | null>(null);
const mouseLeaveTimeout = ref<number | null>(null);
onMounted(() => {
elRef.value = getCurrentInstance()?.proxy?.$el;
});
onUnmounted(() => {
if (mouseLeaveTimeout.value) {
clearTimeout(mouseLeaveTimeout.value);
}
});
function onMouseEnter() {
const boundingRect = elRef.value?.getBoundingClientRect();
if (!boundingRect) {
return;
}
// If multiple words are already marked (via anchors), disable hover marking
// Selection should persist until cleared by the Clear button
if (markerStore.markedWords.length > 1) {
// Only update hoveredWordId if the word is part of the current selection
// This keeps the rectangle visible when hovering marked words
if (markerStore.checkedSelected(props.id)) {
markerStore.setHoveredWordId(props.id);
}
// Don't clear or mark new words when multiple words are selected
return;
}
// Normal hover behavior when 0 or 1 word is marked
// Set hovered word ID (for rectangle display)
markerStore.setHoveredWordId(props.id);
// Mark the word if not already marked
// This ensures rectangle appears around the hovered word
if (!markerStore.checkedSelected(props.id)) {
// Clear previous selection and mark this word
markerStore.clear();
markerStore.markWord(props.id, props.modelValue?.trim(), boundingRect);
}
}
function onMouseLeave(e: MouseEvent) {
// Add a small delay to prevent flickering when moving to anchors
if (mouseLeaveTimeout.value) {
clearTimeout(mouseLeaveTimeout.value);
}
mouseLeaveTimeout.value = window.setTimeout(() => {
// Check if we're hovering over an anchor or rectangle
const elementUnderCursor = document.elementFromPoint(e.clientX, e.clientY);
const isOverAnchor = elementUnderCursor?.closest(".selection-anchor");
const isOverRectangle = elementUnderCursor?.closest(
".word-selection-rectangle"
);
// Don't clear hover if moving to anchor or rectangle
if (
!isOverAnchor &&
!isOverRectangle &&
markerStore.hoveredWordId === props.id
) {
markerStore.setHoveredWordId(null);
}
mouseLeaveTimeout.value = null;
}, 150);
}
function OpenWordDetail() {
analytic.track("phrase_clicked", {
word:
markerStore.words.length > 1
? markerStore.selectedPhrase
: props.modelValue,
platform: window.location.hostname,
});
if (markerStore.words.length > 1) {
emitOpen({
page: "word-detail",
params: {
word: markerStore.selectedPhrase,
context: markerStore.context,
},
});
} else {
emitOpen({
page: "word-detail",
params: {
word: props.modelValue,
context: markerStore.context,
},
});
}
}
</script>
<style scoped>
span {
transition: all ease 200ms;
}
.selected {
color: yellow !important;
}
.pointer {
cursor: pointer;
}
</style>